From e1cbc5c699391c87590cc4ea72564c5336cc7017 Mon Sep 17 00:00:00 2001 From: Robert Anderson <randerson@lbsd.net> Date: Wed, 14 Oct 2009 11:00:49 +0000 Subject: [PATCH] Added check for duplicate accounting records when updating --- .../modules/accounting/mod_accounting_sql.pm | 113 ++++++++++++++++++ smradiusd.conf | 21 ++++ 2 files changed, 134 insertions(+) diff --git a/smradius/modules/accounting/mod_accounting_sql.pm b/smradius/modules/accounting/mod_accounting_sql.pm index 28c1a15e..3cf07022 100644 --- a/smradius/modules/accounting/mod_accounting_sql.pm +++ b/smradius/modules/accounting/mod_accounting_sql.pm @@ -205,6 +205,29 @@ sub init AND PeriodKey = %{query.PeriodKey} '; + $config->{'accounting_select_duplicates_query'} = ' + SELECT + ID + FROM + @TP@accounting + WHERE + Username = %{request.User-Name} + AND AcctSessionID = %{request.Acct-Session-Id} + AND NASIPAddress = %{request.NAS-IP-Address} + AND PeriodKey = %{query.PeriodKey} + ORDER BY + ID + LIMIT 99 OFFSET 1 + '; + + $config->{'accounting_delete_duplicates_query'} = ' + DELETE FROM + @TP@accounting + WHERE + ID = %{query.DuplicateID} + AND PeriodKey = %{query.PeriodKey} + '; + # Setup SQL queries if (defined($scfg->{'mod_accounting_sql'})) { # Pull in queries @@ -262,6 +285,24 @@ sub init $config->{'accounting_usage_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_usage_query'}; } } + if (defined($scfg->{'mod_accounting_sql'}->{'accounting_select_duplicates_query'}) && + $scfg->{'mod_accounting_sql'}->{'accounting_select_duplicates_query'} ne "") { + if (ref($scfg->{'mod_accounting_sql'}->{'accounting_select_duplicates_query'}) eq "ARRAY") { + $config->{'accounting_select_duplicates_query'} = join(' ', + @{$scfg->{'mod_accounting_sql'}->{'accounting_select_duplicates_query'}}); + } else { + $config->{'accounting_select_duplicates_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_select_duplicates_query'}; + } + } + if (defined($scfg->{'mod_accounting_sql'}->{'accounting_delete_duplicates_query'}) && + $scfg->{'mod_accounting_sql'}->{'accounting_delete_duplicates_query'} ne "") { + if (ref($scfg->{'mod_accounting_sql'}->{'accounting_delete_duplicates_query'}) eq "ARRAY") { + $config->{'accounting_delete_duplicates_query'} = join(' ', + @{$scfg->{'mod_accounting_sql'}->{'accounting_delete_duplicates_query'}}); + } else { + $config->{'accounting_delete_duplicates_query'} = $scfg->{'mod_accounting_sql'}->{'accounting_delete_duplicates_query'}; + } + } } } @@ -451,6 +492,7 @@ sub acct_log $startNewPeriod = 1; } } + DBFreeRes($sth); # Re-calculate my ($inputGigawordsStr,$inputOctetsStr) = $totalInputBytes->bdiv(UINT_MAX); @@ -474,6 +516,12 @@ sub acct_log awitpt::db::dblayer::Error()); return MOD_RES_NACK; } + + # Check if we updated duplicates, if we did, fix them + if ($sth > 1) { + fixDuplicates($server, $template); + } + # Else do a start record to continue session } else { # Replace template entries @@ -486,6 +534,12 @@ sub acct_log awitpt::db::dblayer::Error()); return MOD_RES_NACK; } + + # Check if we updated duplicates, if we did, fix them + if ($sth > 1) { + fixDuplicates($server, $template); + } + $startNewPeriod = 0; } @@ -581,12 +635,71 @@ sub acct_log $server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Failed to update accounting STOP record: ".awitpt::db::dblayer::Error()); return MOD_RES_NACK; } + + # Check if we updated duplicates, if we did, fix them + if ($sth > 1) { + fixDuplicates($server, $template); + } } return MOD_RES_ACK; } +# Resolve duplicate records +sub fixDuplicates +{ + my ($server, $template) = @_; + + + # Replace template entries + my @dbDoParams = templateReplace($config->{'accounting_select_duplicates_query'},$template); + + # Select duplicates + my $sth = DBSelect(@dbDoParams); + if (!$sth) { + $server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Database query failed: ".awitpt::db::dblayer::Error()); + return; + } + + # Pull in duplicates + my @IDList; + while (my $duplicates = $sth->fetchrow_hashref()) { + $duplicates = hashifyLCtoMC( + $duplicates, + qw(ID) + ); + push(@IDList,$duplicates->{'ID'}); + } + DBFreeRes($sth); + + # Loop through IDs and delete + DBBegin(); + foreach my $duplicateID (@IDList) { + # Add ID list to the template + $template->{'query'}->{'DuplicateID'} = $duplicateID; + + # Replace template entries + @dbDoParams = templateReplace($config->{'accounting_delete_duplicates_query'},$template); + + # Delete duplicates + $sth = DBDo(@dbDoParams); + if (!$sth) { + $server->log(LOG_ERR,"[MOD_ACCOUNTING_SQL] Database query failed: ".awitpt::db::dblayer::Error()); + DBRollback(); + return; + } + } + + # Commit changes to the database + $server->log(LOG_DEBUG,"[MOD_ACCOUNTING_SQL] Duplicate accounting records deleted"); + DBCommit(); + + + return +} + + # Add up totals function sub cleanup { diff --git a/smradiusd.conf b/smradiusd.conf index 7d951de2..ef621414 100644 --- a/smradiusd.conf +++ b/smradiusd.conf @@ -345,7 +345,28 @@ accounting_usage_query=<<EOT AND PeriodKey = %{query.PeriodKey} EOT +accounting_select_duplicates_query=<<EOT + SELECT + ID + FROM + @TP@accounting + WHERE + Username = %{request.User-Name} + AND AcctSessionID = %{request.Acct-Session-Id} + AND NASIPAddress = %{request.NAS-IP-Address} + AND PeriodKey = %{query.PeriodKey} + ORDER BY + ID + LIMIT 99 OFFSET 1 +EOT +accounting_delete_duplicates_query=<<EOT + DELETE FROM + @TP@accounting + WHERE + ID = %{query.DuplicateID} + AND PeriodKey = %{query.PeriodKey} +EOT # MOD_USERDB_SQL -- GitLab