-
Notifications
You must be signed in to change notification settings - Fork 403
Description
PHP Version
8.2.6
Laravel Version
10.48
Package Version
13.6.8
Description
sometimes really rarely the prune method hangs as the delete takes really really long to finish (almost an hour).
our use case is using laravel-audits as an audit for contact changes, the audits table contains ~200million records of some million contacts.
slow query log excerpt:
SET timestamp=1745938126;
delete `audits` from `audits` left join (select `id` from `audits` where `audits`.`auditable_type` = 'App\\cfab9d1953' and `audits`.`auditable_id` = 3210790 and `audits`.`auditable_id` is not null order by `created_at` desc limit 30) as `audit_threshold` on `audits`.`id` = `audit_threshold`.`id` where `audits`.`auditable_type` = 'App\\cfab9d1953' and `audits`.`auditable_id` = 3210790 and `audits`.`auditable_id` is not null and `audit_threshold`.`id` is null;
# Time: 250429 10:48:50
# User@Host: mydb-live[mydb-live] @ [172.17.12.195]
# Thread_id: 5651235 Schema: mydb-live QC_hit: No
# Query_time: 2.003077 Lock_time: 0.000026 Rows_sent: 1 Rows_examined: 0
# Rows_affected: 0 Bytes_sent: 80
--
# User@Host: mydb-live[mydb-live] @ [172.17.12.195]
# Thread_id: 5667915 Schema: mydb-live QC_hit: No
# Query_time: 3103.636623 Lock_time: 0.000022 Rows_sent: 0 Rows_examined: 146805805
# Rows_affected: 1 Bytes_sent: 11
SET timestamp=1745942497;
delete `audits` from `audits` left join (select `id` from `audits` where `audits`.`auditable_type` = 'App\\cfab9d1953' and `audits`.`auditable_id` = 10254368 and `audits`.`auditable_id` is not null order by `created_at` desc limit 30) as `audit_threshold` on `audits`.`id` = `audit_threshold`.`id` where `audits`.`auditable_type` = 'App\\cfab9d1953' and `audits`.`auditable_id` = 10254368 and `audits`.`auditable_id` is not null and `audit_threshold`.`id` is null;
# Time: 250429 12:02:00
# User@Host: mydb-live[mydb-live] @ [172.17.12.195]
# Thread_id: 5651235 Schema: mydb-live QC_hit: No
# Query_time: 18.598989 Lock_time: 0.000036 Rows_sent: 0 Rows_examined: 11414403
# Rows_affected: 0 Bytes_sent: 8508
Steps To Reproduce
see also description with many audits and high load this locking behaviour does arise really rarely
Possible Solutions
rewriting the prune logic to not lock with a potential complex query.
This runs first a query to find out the ids of all matching audits and then optionally in a second step deletes the records, if need be by the id reference.
Note: Even though the operation is not atomic anymore compared to the original query this is still logically fine. Even when racing with simultanious pruning of more audit changes, then end result is that all to-be-wiped records are gone.
public function prune(Auditable $model): bool
{
if (($threshold = $model->getAuditThreshold()) > 0) {
$auditClass = get_class($model->audits()->getModel());
$auditModel = new $auditClass;
$auditIds = $model->audits()->pluck($auditModel->getKeyName())->toArray();
if(count($auditIds) <= $threshold) return false;
$oldIds = array_slice($auditIds, 0, count($auditIds) - $threshold);
$auditClass::whereIn('id', $oldIds)->delete();
return true;
}
return false;
}