Deduplicate residual-encryption purge dispatches with ShouldBeUnique

Reviewer finding (minor): share() runs on every non-api web GET, so a legacy
user with a stale salt re-enqueues PurgeResidualEncryptionArtifactsJob on every
page load until an async worker clears the salt, piling up redundant jobs rows.

Marks the job ShouldBeUnique keyed by user id so concurrent/repeat dispatches
for the same user collapse into a single pending job. The work was already
idempotent; this just removes the queue churn.
This commit is contained in:
Víctor Falcón 2026-07-04 20:34:28 +02:00
parent 9e90db293c
commit f803a4a9b0
1 changed files with 12 additions and 4 deletions

View File

@ -4,6 +4,7 @@ namespace App\Jobs;
use App\Console\Commands\Concerns\FindsUsersWithLegacyEncryption;
use App\Models\User;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Queue\Queueable;
@ -15,19 +16,26 @@ use Illuminate\Foundation\Queue\Queueable;
*
* It re-checks the condition on execution and is therefore idempotent: dispatching
* it more than once (or after the salt was already cleared elsewhere) is a no-op.
* It is also {@see ShouldBeUnique} keyed by user, so the repeated dispatches that
* share() issues on every page load collapse into a single queued job per user.
*
* Because the purge is destructive (it drops the only key material for any data
* still encrypted at rest), the residual check uses the same source of truth as
* {@see FindsUsersWithLegacyEncryption}: the
* per-row `*_iv` columns, never the stale `accounts.encrypted` flag. Keying off
* that flag could destroy the salt while an account name is still encrypted.
* {@see FindsUsersWithLegacyEncryption}: the per-row `*_iv` columns, never the
* stale `accounts.encrypted` flag. Keying off that flag could destroy the salt
* while an account name is still encrypted.
*/
class PurgeResidualEncryptionArtifactsJob implements ShouldQueue
class PurgeResidualEncryptionArtifactsJob implements ShouldBeUnique, ShouldQueue
{
use Queueable;
public function __construct(public User $user) {}
public function uniqueId(): string
{
return $this->user->id;
}
public function handle(): void
{
$user = $this->user->fresh();