<?php

declare(strict_types=1);

use Phinx\Db\Table\Index;
use Phinx\Migration\AbstractMigration;

/**
 * `sequence` is allocated by reading MAX(sequence)+1 and inserting — no lock,
 * no transaction. Two concurrent enqueues of the same handler read the same
 * number, and UNIQUE(sequence, handler) then decided what happened to the
 * loser. Under apexl/vault <=3 every insert was emitted as INSERT ... ON
 * DUPLICATE KEY UPDATE, so the second write silently overwrote the first job,
 * contents and all: jobs were lost with nothing in the log. Under vault >=4
 * the insert is strict, so it throws at enqueue instead — louder, still a lost
 * job.
 *
 * The constraint bought nothing it cost. A job is identified by `id`;
 * `sequence` is an ordering hint, and QueuedTaskFinderDb already orders by
 * (sequence, id) precisely because sequence is not unique — external services
 * supply their own. Dropping to a plain index makes the collision harmless on
 * both vault majors rather than reacting to it on one: two jobs share a
 * number, both survive, and `id` breaks the tie in arrival order.
 *
 * The index itself stays, on the same columns in the same order, so the
 * finder's ORDER BY is served exactly as before.
 *
 * down() re-adds a constraint that rows written since may already violate.
 */
final class RelaxJobQueueSequenceUniqueness extends AbstractMigration
{
    public function up(): void
    {
        $this->table('job_queue')->removeIndex(['sequence', 'handler'])->update();
        $this->table('job_queue')->addIndex(['sequence', 'handler'])->update();
    }

    public function down(): void
    {
        $this->table('job_queue')->removeIndex(['sequence', 'handler'])->update();
        $this->table('job_queue')->addIndex(['sequence', 'handler'], [Index::UNIQUE => true])->update();
    }
}
