perf: delete purged connections in batches of 5000

The purge ran one DELETE over the whole retention cutoff in a single
transaction, spiking undo/binlog and holding row locks on a table the
fetch cron writes to. Now it loops in batches of 5000 rows with a short
pause between batches, keeping each commit small.
This commit is contained in:
daprogs committed 2026-10-18 22:18:08 -04:00
1 parent 4ce1502831
commit b479f92a63
1 file changed
+23 -4
+23 -4
View File
@@ -258,15 +258,34 @@ function frequent_ips(int $min_connections, ?string $since = null): array {
/** /**
* Delete connections older than retention_days setting. * Delete connections older than retention_days setting.
* Rows are removed in batches so each transaction stays small — a single
* giant DELETE would lock a large part of the table while the fetch cron
* keeps inserting rows.
*
* Returns the number of rows deleted. * Returns the number of rows deleted.
*/ */
function purge_old_connections(): int { function purge_old_connections(): int {
$days = max(1, (int)get_setting('retention_days', '7')); $days = max(1, (int)get_setting('retention_days', '7'));
$cutoff = date('Y-m-d H:i:s', time() - $days * 86400);
$s = db()->prepare( $s = db()->prepare(
'DELETE FROM connections WHERE occurred_at < NOW() - INTERVAL ? DAY' 'DELETE FROM connections
WHERE occurred_at < ?
ORDER BY id
LIMIT 5000'
); );
$s->execute([$days]);
return $s->rowCount(); $total = 0;
do {
$s->execute([$cutoff]);
$deleted = $s->rowCount();
$total += $deleted;
if ($deleted > 0) {
usleep(100_000); // 100 ms between batches to ease lock pressure
}
} while ($deleted >= 5000);
return $total;
} }
// ── Upstream version check ──────────────────────────────────────────────────── // ── Upstream version check ────────────────────────────────────────────────────