From b77bb81343c4999ae51b1960f752805bfa132433 Mon Sep 17 00:00:00 2001 From: Digimer Date: Mon, 20 Jun 2022 20:52:07 -0400 Subject: [PATCH] * Found a bug where, if a record was deleted from the public schema but not from the history schema, and then later a resync was performed, the record would be added to the peer database's public schema (while still not existing locally). This condition should never occur as data in history should only exist to track the public record. This update checks for this condition and purges those records prior to resync'ng a database table. * Continued work on fixing issues with striker-purge-target (which led the the discovery of the above bug). Added expliit checks to purge file_location and storage_group data when purging an sub-anvil from the database. Signed-off-by: Digimer --- Anvil/Tools/Database.pm | 70 +++++++++++++++++++++++++++++++++++--- share/words.xml | 2 ++ tools/striker-purge-target | 67 +++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/Anvil/Tools/Database.pm b/Anvil/Tools/Database.pm index e160977a..16406532 100644 --- a/Anvil/Tools/Database.pm +++ b/Anvil/Tools/Database.pm @@ -16224,10 +16224,59 @@ sub resync_databases }}); } + ### TODO: This can be removed later. + # Look through the bridges, bonds, and network interfaces tables. Look for records in the + # history schema that don't exist in the public schema and purge them. + if ($schema eq "history") + { + foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }}); + + my $query = "SELECT DISTINCT ".$uuid_column." FROM history.".$table.";"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); + + my $results = $anvil->Database->query({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $column_uuid = $row->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { column_uuid => $column_uuid }}); + + my $query = "SELECT COUNT(*) FROM ".$table." WHERE ".$uuid_column." = '".$column_uuid."';"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0124", variables => { query => $query }}); + + my $count = $anvil->Database->query({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }}); + + if (not $count) + { + # Purge it from everywhere. + my $queries = []; + push @{$queries}, "DELETE FROM history.".$table." WHERE ".$uuid_column." = '".$column_uuid."';"; + push @{$queries}, "DELETE FROM ".$table." WHERE ".$uuid_column." = '".$column_uuid."';"; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "err", key => "error_0365", variables => { + table => $table, + uuid_column => $uuid_column, + column_uuid => $column_uuid, + }}); + # Delete across all DBs. + $anvil->Database->write({debug => $debug, query => $queries, source => $THIS_FILE, line => __LINE__}); + } + } + } + } + # Now read in the data from the different databases. foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { uuid => $uuid }}); + + # This will store queries. $anvil->data->{db_resync}{$uuid}{public}{sql} = []; $anvil->data->{db_resync}{$uuid}{history}{sql} = []; @@ -16266,7 +16315,7 @@ sub resync_databases { $query .= " ORDER BY utc_modified_date DESC;"; } - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0074", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0074", variables => { uuid => $anvil->Database->get_host_from_uuid({short => 1, host_uuid => $uuid}), query => $query, }}); @@ -16345,7 +16394,7 @@ sub resync_databases if (($last_record) && ($schema eq "history") && ($last_record eq "${modified_date}::${row_uuid}")) { # Duplicate! - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0363", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "err", key => "error_0363", variables => { table => $table, key => $last_record, query => $query, @@ -16354,7 +16403,7 @@ sub resync_databases # Delete this entry. my $query = "DELETE FROM history.".$table." WHERE history_id = ".$history_id.";"; - $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }}); $anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__}); next; @@ -18066,7 +18115,6 @@ ORDER BY } } - # Are being asked to trigger a resync? foreach my $uuid (keys %{$anvil->data->{cache}{database_handle}}) { @@ -18154,6 +18202,20 @@ ORDER BY last if $anvil->data->{sys}{database}{resync_needed}; } + # Force resync if requested by command line switch. + $anvil->data->{switches}{'resync-db'} = "" if not defined $anvil->data->{switches}{'resync-db'}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "switches::resync-db" => $anvil->data->{switches}{'resync-db'}, + }}); + if ($anvil->data->{switches}{'resync-db'}) + { + $anvil->data->{sys}{database}{resync_needed} = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + "sys::database::resync_needed" => $anvil->data->{sys}{database}{resync_needed}, + }}); + return(0); + } + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::resync_needed" => $anvil->data->{sys}{database}{resync_needed} }}); return(0); } diff --git a/share/words.xml b/share/words.xml index 73e7b71c..1ce8432e 100644 --- a/share/words.xml +++ b/share/words.xml @@ -510,6 +510,7 @@ The output, if any, was; ' to specify the alert level of the test message.]]> There are two or more entries on the host: [#!variable!host!#] in the history table: [#!variable!table!#]! The duplicate modidied_date and column UUID are: [#!variable!key!#] (time is UTC), and the query that exposed the dupplicate was: [#!variable!query!#]. This is likely caused by two database writes where the 'modified_date' wasn't updated between writes. [ Error ] - There was a problem purging records. The details of the problem should be in the logs. + The table: [#!variable!table!#] has an entry in the history schema that doesn't have a corresponding record in the public schema. This is likely a resync artifact of a deleted record. Purging the record: [#!variable!uuid_column!#:#!variable!column_uuid!#] from all databases. @@ -2125,6 +2126,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is: I was asked to update the timestamp, but the returned timestamp matches the last one. Will loop until a new timestamp is returned. The timestamp has been updated from: [#!variable!old_time!#] to: [#!variable!new_time!#]. read_state() was called but both the 'state_name' and 'state_uuid' parameters were not passed or both were empty.]]> + Forcing the dailing resync and checking to clear records in the history schema no longer in public schema. The host name: [#!variable!target!#] does not resolve to an IP address. diff --git a/tools/striker-purge-target b/tools/striker-purge-target index db2d0a05..2ff73cdf 100755 --- a/tools/striker-purge-target +++ b/tools/striker-purge-target @@ -280,7 +280,66 @@ WHERE push @{$queries}, $query; } } - + + # If deleting an Anvil!, we need to clear any Anvil! references from file_locations and storage groups. + if (($table eq "anvils") && ($anvil->data->{purge}{anvil_uuid})) + { + # + my $query = "DELETE FROM history.file_locations WHERE file_location_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + + $query = "DELETE FROM file_locations WHERE file_location_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + + # Storage groups + $query = " +SELECT + storage_group_member_uuid +FROM + storage_group_members +WHERE + storage_group_member_storage_group_uuid = + ( + SELECT + storage_group_uuid + FROM + storage_groups + WHERE + storage_group_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid})." + ) +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $storage_group_member_uuid = $row->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { storage_group_member_uuid => $storage_group_member_uuid }}); + + my $query = "DELETE FROM history.storage_group_members WHERE storage_group_member_uuid = ".$anvil->Database->quote($storage_group_member_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + + $query = "DELETE FROM storage_group_members WHERE storage_group_member_uuid = ".$anvil->Database->quote($storage_group_member_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + + $query = "DELETE FROM history.storage_groups WHERE storage_group_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + + $query = "DELETE FROM storage_groups WHERE storage_group_anvil_uuid = ".$anvil->Database->quote($anvil->data->{purge}{anvil_uuid}).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$queries}, $query; + } + # Just delete the record normally. if ($anvil->data->{sys}{database}{history_table}{$table}) { @@ -295,6 +354,12 @@ WHERE } # Commit. + print "===================\n"; + foreach my $query (@{$queries}) + { + print $query."\n"; + } + print "===================\n"; my $problem = $anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }}); if ($problem)