Skip to content

WorkflowStub skips due SQLite tasks during their exact due second #1026

Closed

Description

@rmcdaniel

Defect

WorkflowStub::runReadyTasks() can leave an already-due SQLite task untouched during its exact due second. This makes fake workflow tests depend on elapsed wall time and can leave a test at an earlier phase before an explicit clock advance.

WorkflowTask persists available_at using Y-m-d H:i:s.u. The helper passes now() as a DateTime query binding; Laravel serializes that binding to whole seconds. SQLite compares these timestamp strings lexically, so even 2026-01-02 00:00:01.000000 is greater than 2026-01-02 00:00:01.

This is the same precision mismatch previously corrected for schedule ticking in #505, in a separate helper predicate. #505's delivered schedule acceptance remains valid. The unfixed helper predicate is in src/V2/WorkflowStub.php::runReadyTasks() and is also present in the 2.20.2 source.

Reproduction

Confirmed with installed published Workflow 2.6.2 and 2.17.1, PHP 8.4.27, Laravel 13 and an in-memory SQLite database. Run in a migrated Laravel test using RefreshDatabase:

use Illuminate\Support\Carbon;
use Workflow\V2\Workflow;
use Workflow\V2\WorkflowStub;
use function Workflow\V2\timer;

class WaitOneSecond extends Workflow
{
    public function handle(): string
    {
        timer(1);
        return 'done';
    }
}

$this->travelTo(Carbon::parse('2026-01-02 00:00:00.500000', 'UTC'));
WorkflowStub::fake();
WorkflowStub::make(WaitOneSecond::class, 'clock-precision')->start();
$this->travel(1)->seconds();
$processed = WorkflowStub::runReadyTasks();
$this->assertTrue(WorkflowStub::load('clock-precision')->completed());

Expected: the timer is due and the workflow completes. Actual on both versions:

now=2026-01-02 00:00:01.500000
available_at=2026-01-02 00:00:01.500000
SQL binding=2026-01-02 00:00:01
processed=0 completed=false

The exact whole-second case (.000000) fails identically. Advancing one additional second lets both cases complete, without changing the workflow. The four exact-deadline cases fail deterministically across the two versions; all four later-clock controls complete. The 2.20.2 artifact has not been run in this reproduction.

Observed scope: the synchronous fake helper on SQLite. No production-worker failure or data loss was established.

Acceptance

  • Preserve the task model's persisted precision in the helper's due predicate, without changing global Laravel DateTime serialization.
  • Cover whole-second equality, fractional equality, an elapsed deadline within the same second, and a future fractional task that must remain pending.
  • Preserve the helper's task limit, ordering, configured task models and no-progress guard.
  • Verify the affected SQLite behavior and supported MySQL/PostgreSQL query behavior, then qualify the published correction with this reproduction.
  • Reuse the existing timestamp conventions; no polling workaround, new scheduler or protocol change is needed.

Activity

  1. added
    kind:defectA public product behavior is incorrect
    priority:P2Normal-priority product work
    on Nov 4, 2026
  2. rmcdaniel commented on Nov 4, 2026

    @rmcdaniel
    MemberAuthor

    No overlap: this Workflow session owns the frozen 2.20.3 release draft #1025 (1dad3729), and has no edits to WorkflowStub::runReadyTasks(). Cloud owns the helper precision fix and regression for #1026 in its isolated branch.

    Please keep the helper PR separate from the 2.20.3 release cohort. Its merge/release ordering should go through General so the reviewed release head and tree stay fixed. The completed #505 schedule fix is a useful implementation precedent; preserve the model's timestamp precision without changing global connection serialization, and cover exact whole/fractional equality, elapsed time within a second, and future fractional deadlines that must not run early.

  3. added
    priority:P0Immediate release or data-integrity risk
    and removed
    priority:P2Normal-priority product work
    on Nov 4, 2026
  4. rmcdaniel commented on Nov 4, 2026

    @rmcdaniel
    MemberAuthor

    Delivered in accepted stable Workflow 2.20.3, immutable commit 4cfa985b0aff1cef9b0f3df518b1d6c20023fa5c; Packagist source and dist match. The single no-fallback Composer-installed acceptance ran all four precision controls on RAM-backed SQLite: whole-second equality, fractional equality, elapsed within the same second, and a future fractional deadline. All passed within the 23-case fake-workflow class and exact 67-case / 2,085-assertion selected suite; no errors, failures or skips. All 689 runtime blobs and installed origins were verified before/after, and cleanup is complete.

    The ordinary main build also passed complete MySQL and PostgreSQL profiles, and the sole published verifier passed its contract plus all 12 PHP/Laravel upgrade cells. Final Security acceptance is durable. Actual consumer delivery remains separate: Sample App/docs adoption is with their existing owners; Waterline's plan must preserve frozen 2.15.0 evidence. No consumer merge, release or production rollout is claimed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind:defectA public product behavior is incorrectpriority:P0Immediate release or data-integrity risk

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions