From 783f824f0b62f652831f28a1f2c6a488c3a0205b Mon Sep 17 00:00:00 2001 From: erikn69 Date: Wed, 12 Jun 2024 10:12:46 -0500 Subject: [PATCH 1/3] Fix database prune --- src/Drivers/Database.php | 8 ++++++-- tests/Functional/AuditingTest.php | 15 +++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/Drivers/Database.php b/src/Drivers/Database.php index a762ff66..d0cc1d9d 100644 --- a/src/Drivers/Database.php +++ b/src/Drivers/Database.php @@ -23,9 +23,13 @@ public function audit(Auditable $model): ?Audit public function prune(Auditable $model): bool { if (($threshold = $model->getAuditThreshold()) > 0) { + $class = get_class($model->audits()->getModel()); + $keyName = (new $class)->getKeyName(); + return $model->audits() - ->latest() - ->offset($threshold)->limit(PHP_INT_MAX) + ->whereNotIn( + $keyName, $model->audits()->select($keyName)->limit($threshold)->latest() + ) ->delete() > 0; } diff --git a/tests/Functional/AuditingTest.php b/tests/Functional/AuditingTest.php index ffef3721..6bc063d6 100644 --- a/tests/Functional/AuditingTest.php +++ b/tests/Functional/AuditingTest.php @@ -297,16 +297,23 @@ public function itWillRemoveOlderAuditsAboveTheThreshold() ]); $article = factory(Article::class)->create([ - 'reviewed' => 1, + 'title' => 'Title #0', ]); - foreach (range(0, 99) as $count) { + foreach (range(1, 20) as $count) { + if ($count === 11) { + sleep(1); + } + $article->update([ - 'reviewed' => ($count % 2), + 'title' => 'Title #' . $count, ]); } - $this->assertSame(10, $article->audits()->count()); + $audits = $article->audits()->get(); + $this->assertSame(10, $audits->count()); + $this->assertSame('Title #11', $audits->first()->new_values['title']); + $this->assertSame('Title #20', $audits->last()->new_values['title']); } /** From db5ed9cdd3370cb6325865abf524dc67de32ed7b Mon Sep 17 00:00:00 2001 From: erikn69 Date: Wed, 26 Jun 2024 09:03:15 -0500 Subject: [PATCH 2/3] Reverse prune query optimization --- src/Drivers/Database.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Drivers/Database.php b/src/Drivers/Database.php index d0cc1d9d..e0fc4c68 100644 --- a/src/Drivers/Database.php +++ b/src/Drivers/Database.php @@ -25,11 +25,15 @@ public function prune(Auditable $model): bool if (($threshold = $model->getAuditThreshold()) > 0) { $class = get_class($model->audits()->getModel()); $keyName = (new $class)->getKeyName(); - + + $idsToKeep = $model->audits() + ->select($keyName) + ->limit($threshold) + ->latest() + ->pluck($keyName); + return $model->audits() - ->whereNotIn( - $keyName, $model->audits()->select($keyName)->limit($threshold)->latest() - ) + ->whereNotIn($keyName, $idsToKeep) ->delete() > 0; } From c0ff649d3cd37e54aedcc9f52c1c26458c393a51 Mon Sep 17 00:00:00 2001 From: erikn69 Date: Wed, 26 Jun 2024 09:31:35 -0500 Subject: [PATCH 3/3] Use left join to optimize prune query --- src/Drivers/Database.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Drivers/Database.php b/src/Drivers/Database.php index e0fc4c68..71bfcf76 100644 --- a/src/Drivers/Database.php +++ b/src/Drivers/Database.php @@ -23,17 +23,22 @@ public function audit(Auditable $model): ?Audit public function prune(Auditable $model): bool { if (($threshold = $model->getAuditThreshold()) > 0) { - $class = get_class($model->audits()->getModel()); - $keyName = (new $class)->getKeyName(); - - $idsToKeep = $model->audits() - ->select($keyName) - ->limit($threshold) - ->latest() - ->pluck($keyName); - + $auditClass = get_class($model->audits()->getModel()); + $auditModel = new $auditClass; + return $model->audits() - ->whereNotIn($keyName, $idsToKeep) + ->leftJoinSub( + $model->audits()->select($auditModel->getKeyName())->limit($threshold)->latest(), + 'audit_threshold', + function ($join) use ($auditModel) { + $join->on( + $auditModel->gettable().'.'.$auditModel->getKeyName(), + '=', + 'audit_threshold.'.$auditModel->getKeyName() + ); + } + ) + ->whereNull('audit_threshold.'.$auditModel->getKeyName()) ->delete() > 0; }