* 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 <digimer@alteeve.ca>
main
Digimer 3 years ago
parent 6ea8365067
commit b77bb81343
  1. 70
      Anvil/Tools/Database.pm
  2. 2
      share/words.xml
  3. 65
      tools/striker-purge-target

@ -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. # Now read in the data from the different databases.
foreach my $uuid (sort {$a cmp $b} keys %{$anvil->data->{cache}{database_handle}}) 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 }}); $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}{public}{sql} = [];
$anvil->data->{db_resync}{$uuid}{history}{sql} = []; $anvil->data->{db_resync}{$uuid}{history}{sql} = [];
@ -16266,7 +16315,7 @@ sub resync_databases
{ {
$query .= " ORDER BY utc_modified_date DESC;"; $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}), uuid => $anvil->Database->get_host_from_uuid({short => 1, host_uuid => $uuid}),
query => $query, query => $query,
}}); }});
@ -16345,7 +16394,7 @@ sub resync_databases
if (($last_record) && ($schema eq "history") && ($last_record eq "${modified_date}::${row_uuid}")) if (($last_record) && ($schema eq "history") && ($last_record eq "${modified_date}::${row_uuid}"))
{ {
# Duplicate! # 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, table => $table,
key => $last_record, key => $last_record,
query => $query, query => $query,
@ -16354,7 +16403,7 @@ sub resync_databases
# Delete this entry. # Delete this entry.
my $query = "DELETE FROM history.".$table." WHERE history_id = ".$history_id.";"; 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__}); $anvil->Database->write({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__});
next; next;
@ -18066,7 +18115,6 @@ ORDER BY
} }
} }
# Are being asked to trigger a resync? # Are being asked to trigger a resync?
foreach my $uuid (keys %{$anvil->data->{cache}{database_handle}}) foreach my $uuid (keys %{$anvil->data->{cache}{database_handle}})
{ {
@ -18154,6 +18202,20 @@ ORDER BY
last if $anvil->data->{sys}{database}{resync_needed}; 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} }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { "sys::database::resync_needed" => $anvil->data->{sys}{database}{resync_needed} }});
return(0); return(0);
} }

@ -510,6 +510,7 @@ The output, if any, was;
<key name="error_0362"><![CDATA[The level: [#!variable!level!#] is invalid. Please use '--level <critical,warning,notice,info>' to specify the alert level of the test message.]]></key> <key name="error_0362"><![CDATA[The level: [#!variable!level!#] is invalid. Please use '--level <critical,warning,notice,info>' to specify the alert level of the test message.]]></key>
<key name="error_0363">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.</key> <key name="error_0363">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.</key>
<key name="error_0364">[ Error ] - There was a problem purging records. The details of the problem should be in the logs.</key> <key name="error_0364">[ Error ] - There was a problem purging records. The details of the problem should be in the logs.</key>
<key name="error_0365">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.</key>
<!-- Files templates --> <!-- Files templates -->
<!-- NOTE: Translating these files requires an understanding of which lines are translatable --> <!-- NOTE: Translating these files requires an understanding of which lines are translatable -->
@ -2125,6 +2126,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0702">I was asked to update the timestamp, but the returned timestamp matches the last one. Will loop until a new timestamp is returned.</key> <key name="log_0702">I was asked to update the timestamp, but the returned timestamp matches the last one. Will loop until a new timestamp is returned.</key>
<key name="log_0703">The timestamp has been updated from: [#!variable!old_time!#] to: [#!variable!new_time!#].</key> <key name="log_0703">The timestamp has been updated from: [#!variable!old_time!#] to: [#!variable!new_time!#].</key>
<key name="log_0704"><![CDATA[[ Error ] - The method Database->read_state() was called but both the 'state_name' and 'state_uuid' parameters were not passed or both were empty.]]></key> <key name="log_0704"><![CDATA[[ Error ] - The method Database->read_state() was called but both the 'state_name' and 'state_uuid' parameters were not passed or both were empty.]]></key>
<key name="log_0705">Forcing the dailing resync and checking to clear records in the history schema no longer in public schema.</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. --> <!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key> <key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>

@ -281,6 +281,65 @@ WHERE
} }
} }
# 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. # Just delete the record normally.
if ($anvil->data->{sys}{database}{history_table}{$table}) if ($anvil->data->{sys}{database}{history_table}{$table})
{ {
@ -295,6 +354,12 @@ WHERE
} }
# Commit. # Commit.
print "===================\n";
foreach my $query (@{$queries})
{
print $query."\n";
}
print "===================\n";
my $problem = $anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); 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 }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ($problem) if ($problem)

Loading…
Cancel
Save