Skip to content

mysql AuditDriver - prune experienced slow query issue #1019

@zebastian

Description

@zebastian

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;
    }

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions