Batch the audit log retention DELETE to bound lock hold time (#66)
deleteOldLogs() ran a single unbounded DELETE. created_at is indexed so row selection itself is cheap, but on a large qualifying set (first run after enabling/changing AUDIT_LOG_RETENTION_DAYS, or after the cron silently missed runs) an unbounded single-statement DELETE holds row locks for the full duration — risking contention with the frequent concurrent INSERTs the audit log receives from live traffic. Now deletes in batches of 1000 (parameterized), looping until nothing qualifies. Verified against a local MariaDB instance with a batch size of 10 forcing multiple loop iterations: deleted exactly the stale rows, left recent rows untouched, correct total count returned. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MGDKHiU5RJdo3dqQUDow3X
This commit is contained in:
@@ -309,17 +309,28 @@ class AuditLogModel
|
||||
* @param int $daysToKeep Number of days of logs to keep
|
||||
* @return int Number of deleted records
|
||||
*/
|
||||
public function deleteOldLogs($daysToKeep = 90)
|
||||
public function deleteOldLogs($daysToKeep = 90, $batchSize = 1000)
|
||||
{
|
||||
// Batched to bound how long each statement holds row locks — an
|
||||
// unbounded single DELETE on a large backlog (e.g. the first run after
|
||||
// enabling/changing retention, or after the cron silently missed runs)
|
||||
// would otherwise contend with the frequent concurrent INSERTs the
|
||||
// audit log receives from live traffic.
|
||||
$stmt = $this->conn->prepare(
|
||||
"DELETE FROM audit_log WHERE created_at < DATE_SUB(NOW(), INTERVAL ? DAY)"
|
||||
"DELETE FROM audit_log WHERE created_at < DATE_SUB(NOW(), INTERVAL ? DAY) ORDER BY audit_id LIMIT ?"
|
||||
);
|
||||
$stmt->bind_param("i", $daysToKeep);
|
||||
$stmt->execute();
|
||||
$affectedRows = $stmt->affected_rows;
|
||||
$stmt->bind_param("ii", $daysToKeep, $batchSize);
|
||||
|
||||
$totalDeleted = 0;
|
||||
do {
|
||||
$stmt->execute();
|
||||
$affected = $stmt->affected_rows;
|
||||
$totalDeleted += $affected;
|
||||
} while ($affected > 0);
|
||||
|
||||
$stmt->close();
|
||||
|
||||
return $affectedRows;
|
||||
return $totalDeleted;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user