Skip to content

feat: add bounded embedded queue continuations - #834

Merged
rmcdaniel merged 44 commits into
mainfrom
feature/embedded-execution-continuation
Oct 20, 2026
Merged

rmcdaniel merged 44 commits into
mainfrom
feature/embedded-execution-continuation

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Persistent embedded PHP workers replay the committed instruction prefix on every workflow task. This adds optional bounded live execution for asynchronous database-queue jobs, so an eligible workflow can resume its suspended Fiber after a committed outcome. Cold replay remains the default. Delivers the initial bounded profile for #748, with normal published Core and affected embedded consumers qualified.

Released behavior

An explicit workflow allowlist and immutable application build ID enable sequential remote activities and positive ordinary timers. A frame holds the workflow/Fiber, suspended call and sequence, without retaining a job, task, lease or transaction. The worker removes a candidate before advancing it and publishes it only after root commit. Rollback, uncertain acknowledgement, job exceptions and shutdown discard retained execution.

Before resuming, the runtime verifies backend/schema/table scope, physical PDO session, namespace/run/configured-model identities, definition/build/runtime, immutable run fields, the complete contiguous committed history prefix and the attempt generation captured in the claim transaction. Changed or missing authority selects cold replay. Weak PDO keys do not keep disconnected sessions alive.

Bounds are four entries, a nonrenewing 30-second monotonic age and a 128 MiB whole-worker RSS threshold checked at loop, lookup and publication. Missing RSS selects cold replay. Ordinary queues require no affinity wait or lease extension. Normal worker supervision remains required. Eviction preserves the existing #480 Fiber-disposal contract.

Object-capable entry signatures, object injection, explicit bindings, decorated workflows, unsupported history/features, fake/synchronous jobs and caller-owned transactions select cold replay. Excluded operations fall back before their durable handler. Configuration, diagnostics and limitations describe the initial profile. The optional CPU observer measures authored forward execution plus replay, rather than isolated completed-prefix CPU.

The release integrates 2.7.3 dda951d9, preserving pre-entry refusal, supported repair of the same original and genuine workflow-body failures. Inherited source 77132094 does not qualify 2.7.3. This change adds no migrations or portable protocol changes, and changes no native/shared/frozen/catalog fixtures.

Qualification and publication

Merged commit 2edaa6b1c6f45fcec4570813c2e741aadde92b3b is published as Core 2.8.0. The entire merge tree equals checked PR head 82acd9b1; product source, resources and Composer metadata equal functional source fa5d2300.

  • Full-source qualification and protected PR build pass. This includes 4,301 unit tests / 56,181 assertions, all database feature shards, five Laravel source-upgrade families, regression corpus, style/static analysis and combined coverage of 68,160 / 72,703 lines (93.75%), with the complete 620-PHP-file inventory and no exclusions.
  • The exact PR head passes dependency security, public-boundary checks and Sonar, with no ignored conditions or open findings at review.
  • A fresh normal Packagist installation matches source/dist references and all 955 product files to the immutable release. Product classes/functions load from vendor. Its 71 continuation/process/cache/profile controls pass 2,606 assertions. The publication receipt records provenance. The published-package verifier and all 16 supported PHP/Laravel upgrade cells pass across five Laravel families.
  • Counterfactual controls fail before the physical-reconnect, configured-model, captured-generation, post-commit broker-acknowledgement and broad-entry-type corrections, then pass after correction. Process tests cover memory-stop and SIGKILL recovery with original deadlines and no repeated recorded effects. The broader controls cover age/RSS/build/miss/eviction, duplicates, rollback, isolation, dependency lifetime, excluded features, cancellation/termination and redrive.

Measurements and limits

The source-specific report records the actual source for each comparison. It separates cold process startup, history/projection, dispatch/validation, authored PHP, timer/queue delay, cache hits/misses and worker memory. Timings from 5dd594e3 are not attributed to the later immutable release.

The 1,000-event single-worker study passes all three pairs / six originals with complete audits and zero errors, unfinished runs, task repairs, healthy redispatches, OOM kills or restarts. The 100-event four-worker/eight-run study passes six cohorts / 48 workflows, with frequent cache misses and no consistent worker-CPU saving. The functional-source 10-event study passes all three pairs. Retention saves dispatch/validation work in the measured hit-heavy case; authoritative history remains a substantial cost and history SELECT counts increase.

All failed originals remain failed. The large-study report retains six failed four-worker/one-run originals: three OOM kills and three ordinary worker memory exits, with SQLite lock errors and unfinished work. Both one-worker/eight-run and four-worker/eight-run large pilots miss their original observation deadlines; all eight workflows remain unfinished. The four-worker pilots record 57/62 failed jobs and 7/11 tasks with positive repair counts, without enough evidence to classify those increments. Remaining large concurrent repetitions were not admitted, so there is no completed 72-original grid or general capacity claim. The earlier one-hour pilot (642 events, 128 effects, 512/513 completed tasks, one unfinished original, no OOM) and observer originals remain preserved.

Consumer follow-through

Server #552 and Sample App #264 now pin normal published Core 2.8.0. Server's combined TLS/security source passes 69 focused controls / 885 assertions, all 955 installed Core file hashes and a fresh-volume candidate-image first-run. Sample App #264 is merged at 1ee51252, with its entire tree identical to the checked candidate. Its four PHP cells, all five Compose samples, polyglot smoke and candidate devcontainer checks pass. Normal two-architecture devcontainer publication and installed acceptance pass, with both public main channels promoted. Server #552 is merged at 987b07e1; its final combined-source checks and protected Server 2.9.11 / chart 0.2.1 publisher all pass. Exact published-image acceptance includes SQLite first-run, MySQL Compose, anonymous chart install, all 955 Core file hashes and 18 direct PostgreSQL/MySQL/MariaDB TLS probes. General's subsequent Sample #265 security delivery retains these Core pins and passes its own native two-architecture installed publication. Earlier unpublished source-alias rehearsals and their failed stable-version guards remain historical evidence, not published-package proof.

Waterline's Core dependency is development-only; its unchanged runtime is exercised in Sample App. SDK packages and the portable contract are unchanged. Native/shared/frozen/catalog adoption and Cloud rollout remain with their separate owners. #773 follows the agreed next-minor order against actual main and owns tracing-enabled cold fallback/context-toggle qualification. Service-mode warm execution remains PHP SDK #113.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Exact da2079db / tree 0d95059d passes the normal PR build and official Sonar App check. The separate full candidate is running; coverage, database and upgrade qualification remain pending.

Sonar feedback has changed the source: the dispatcher is split into task/call phases, parallel writes are explicit, the unused timer assignment is removed while preserving its call, and condition resolution/satisfaction/timeouts have focused handlers. The final local corpus/continuation/executor selection passes 129 tests / 5,061 assertions.

The unchanged gate passed before issue triage, with 1.7% new-code duplication. I reviewed and accepted twenty return-count warnings individually, recording each method's guard states in Sonar. These retain flat waiting/failure/replay exits. Eight cognitive-complexity findings remain open and visible for further decomposition alongside the related runtime work. No rule, threshold or source exclusion changed, and no zero-findings claim follows.

All task-owned local containers, dependencies, databases and raw output are removed. This remains experimental source, disabled by default; the original #748 acceptance is unfinished.

@codecov

codecov Bot commented Oct 17, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.00000% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.89%. Comparing base (6a21bd6) to head (da2079d).

Files with missing lines Patch % Lines
src/V2/Support/WorkflowContinuationSession.php 95.83% 5 Missing ⚠️
src/V2/Support/WorkflowContinuationCache.php 94.93% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #834      +/-   ##
==========================================
+ Coverage   93.77%   93.89%   +0.11%     
==========================================
  Files         530      535       +5     
  Lines       74795    74969     +174     
==========================================
+ Hits        70141    70389     +248     
+ Misses       4654     4580      -74     
Flag Coverage Δ
v2 93.89% <96.00%> (+0.11%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Pushed 00e6b7beaa65eeb38151c256833f20d161f14926 / whole tree bbe60cefbe7431826ac351eee58dd7532717e85c, after merging qualified current main at 857c421b.

The eight open Sonar complexity findings are addressed in source by separating activity/timer/signal/service/child replay resumption from waiting, scheduling and parallel outcomes. Frame references, original cursor advancement, exception identity, failure handling, replay clocks and catch boundaries are preserved. No profile, threshold, exclusion, configuration, schema or dependency change.

Final local source passes 163 existing replay tests / 1,274 assertions, style and full source/test PHPStan. The first local replay setup used the unavailable PostgreSQL driver; an in-memory SQLite setup then lost schema between cases. The fresh file-backed SQLite run passes unchanged assertions. A superseded local continuation selection was stopped before completion and supplies no completed-suite qualification.

Fresh normal checks and Sonar are running separately; analyzer resolution and final hosted continuation/process-loss acceptance remain pending. Both boundary checks pass.

The prototype stays disabled by default. All seven #748 acceptance boxes remain open; the earlier one-hour pilot remains failed. No new performance experiment, stable release, native/shared adoption or published-consumer claim follows.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Exact 00e6b7be Sonar analysis and the App check PASS. The API gate has 0.0% new duplication, all configured ratings A, 100% reviewed hotspots and no ignored conditions. All eight previously open S3776 executor findings are independently read back as CLOSED/FIXED.

The analyzer raised eight new S1481 findings on reference bindings in the activity/timer/signal/service/child/parallel-failure resume helpers. Each was reviewed against source and individually marked FALSE-POSITIVE with a method-specific explanation: $current = &$frame->current aliases the saved frame property; subsequent send/throw assignments perform an observable write through that reference. Removing the binding would change execution state. The rule stays enabled, with no threshold/profile/exclusion change. The API now reports zero OPEN/CONFIRMED PR issues; the earlier twenty accepted guard-return reports retain their separate explanations.

Hosted style/static quality and regression corpus now pass. MySQL and the exact continuation/process-loss controls remain running, so this is Sonar/source progress rather than final prototype acceptance. Local final replay controls pass 163 tests / 1,274 assertions. All local tooling, copied dependencies, cache, database and raw output are retired; the source stays clean and pushed. The feature remains disabled by default and all #748 acceptance boxes stay open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Exact 00e6b7be / tree bbe60cef now passes every applicable normal check, including MySQL features, database-queue recovery, retained execution/process-loss, all eight full-observer and four minimal-observer functional setup cohorts, canonical corpus and quality. Conditional full-candidate jobs remain skipped in this ordinary PR run; this is not a new all-database/coverage matrix.

Sonar and both boundary checks pass. All eight prior complexity findings are CLOSED/FIXED; the individually reviewed reference-alias false positives and unchanged rules/gates are explained in 6242640715. No OPEN/CONFIRMED PR finding remains at that read.

Local final replay checks pass 163 tests / 1,274 assertions, style and full source/test static analysis. Local tooling/dependencies/cache/database/raw are actually removed. This completes the scoped source-quality follow-through. The default-disabled prototype, all seven #748 acceptance boxes, incomplete larger/concurrent measurements and published consumers remain unchanged.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The current-main small growth report records all twelve fresh 0648f9f6 originals: 10/100 events, one worker/run, both modes and three alternating repetitions. All pass, with 660 events, 120 unique activity effects, 516 completed tasks and 132 timers; zero repairs/errors/unfinished work and all container exits zero/no OOM/restart. Exact loaded source and lock identities were audited. Each mode uses a fresh one-CPU/1-GiB/no-swap container.

At 100 events, within-host retained worker CPU is 7.33%, 9.86% and 6.42% lower, while wall differences are small and history SELECTs increase from 433 to 512. Ten-event CPU differences vary in sign. Host models differ between pairs, so no cross-host median or production saving is claimed. Whole-worker CPU remains distinct from isolated replay attribution. The old one-hour failure remains failed.

Runtime follow-up 9a4e0e9e adds physical database-session identity via weak PDO keys and includes table prefix/search path in logical identity. The new actual-queue reconnect control fails on the preceding implementation (one authored entry where a fresh session must select cold replay), then passes on the correction: one effect, unchanged commands/history/deadlines and cold recovery. Weak keys do not retain disconnected PDO objects. The existing generation fence still spans reconnect keys for the same logical run.

The rollback and post-commit acknowledgement controls now continue through supported repair/watchdog recovery to completion. They retain the original failed task record, committed prefix and deadlines, execute the activity once, and leave no new failed jobs or queued work. The first added observer incorrectly expected ordinary failed tasks to become completed and checked a never-dispatched timer before the existing stale-dispatch interval. Diagnostics identified those observer errors; no product repair interval or deadline changed. RepairRequested deliberately selects cold replay. Final focused controls pass 3 tests / 79 assertions, cache controls 8 / 24, style and focused PHPStan pass. The earlier 55 / 2,248 whole continuation selection remains attributed to its pre-follow-up source, not transferred.

Final tooling/report head ae29003f has runtime identical to 9a4e0e9e; its fresh normal CI/Sonar is pending. It also fixes the two shell-style findings without changing quality settings and allows one bounded pilot before three repetitions. Next is one new 1,000-event replay/retained pilot, preserving each original's 3,600-second bound. No failed original is resumed or rebudgeted.

The tracing owner confirmed that a retained Fiber must not inherit a prior job's ended processing span/task context. #748 will integrate #773 after its qualified minor and add an explicit tested cold fallback for tracing contexts until safe per-resume refresh is proven, including context toggles. Final qualification must cover real sequential jobs and caller-context restoration. This requirement and the remaining replay-phase, concurrency, lifetime, published-package and consumer acceptance keep #834 draft and #748 open.

@rmcdaniel

Copy link
Copy Markdown
Member Author

The claimed-attempt correction is pushed at 39314aec: the generation is captured inside the claim transaction and compared before a retained prefix can resume. Both different-owner and same-owner/advanced-attempt controls pass (2 tests, 22 assertions). This preserves the released 2.7.3 pre-entry refusal/repair behavior and genuine workflow-body failures.

70ed2c27 distinguishes ordinary transport redispatch from lease or error recovery in fresh growth audits. The actual held-reservation plus entry-repair controls pass 3 tests / 72 assertions; SIGKILL recovery classifications pass 2 / 1,642. Every persisted repair increment must reconcile with a healthy Ready-to-Ready redispatch before the first claim; first-attempt execution, original history/IDs/deadlines/effects and drained delivery remain required. The prior failed original stays failed. The two documentation-link preflight failures on 39314aec are corrected; its skipped dependent checks are not claimed as passes.

The 100-event concurrent report retains all eighteen original source-ae29003f cohorts, including the failed audit. Seventeen pass; miss-heavy cells show little or no worker-CPU saving and more history SELECTs.

6f087ee6 adds an explicit, disabled-by-default process observer around real executor/Fiber boundaries, with a test-only history/projection delegate. Suspended time is excluded from Fiber brackets; the collector is not stored in retained frames. Inclusive/exclusive CPU and wall buckets reconcile with queue-worker CPU. Actual authored PHP includes forward execution as well as replay and is not labelled isolated completed-prefix CPU. Controls pass 78 tests / 310 assertions, and real cold/retained 10-event observer-on and observer-off setup cohorts pass (2 / 806 and 2 / 978 respectively); these local setup timings are not comparative performance evidence. Style, static analysis and workflow lint pass. Fresh locked hosted observations and final source/conformance/publication/consumer gates remain open.

Release order is coordinated with #773: no tracing minor is qualified or reserved; an independently qualified embedded minor may publish first. The tracing owner reviewed the observer diff and confirmed no current bind/throw/finally conflict. Runtime/migration coordination is also recorded in #871. The original 1,000-event pilot 39452413954 remains within its original per-mode bounds; it is not restarted or counted as passed.

@rmcdaniel

Copy link
Copy Markdown
Member Author

9ee8d641 corrects another retained-frame identity gap: changing the configured history model without changing the build previously retained the old frame. The actual database-queue counterfactual fails its required cold-entry count (expected 2, got 1). The key now includes a sorted scalar fingerprint of configured *_model class settings. History, activity, timer and task model changes each select fresh replay; four controls pass 104 assertions with original effects/history/deadlines and drained tasks intact. Style and static analysis pass. This is a prototype finding, not a customer-impact claim.

The 1,000-event pilot report preserves both passing source-ae29003f originals and the older failed pilot. Current-source observer-enabled 100-event study39461324018 passes all six originals; phase attribution separates authored PHP from the dominant history/projection work. The separately dispatched observer-off study39461327140 was canceled with all three jobs still queued, zero steps/originals executed. A prospective same-host on/off/on (reversed in repetition2) observer/drift comparison is running at 39462885370. No old observation is reclassified.

The already-started source-e3f148bc 1,000-event repetitions39462444846 continue under their original bounds and will retain their own source attribution. Queued four-worker study39462447820 and full-source39462308528 were canceled before their original/check steps began, following the identity finding. Corrected-source full qualification is 39463825695; the four-worker/one-run three-pair study is 39463828599, with one/eight and four/eight 1,000-event pilots 39463831372 / 39463834650. All keep the original per-mode 3,600-second bounds. No final-source or release pass is assumed while checks run.

@rmcdaniel

Copy link
Copy Markdown
Member Author

48d664ff closes a separate broker-acknowledgement gap in the prototype. The earlier ack control injects a database after-commit exception; it does not exercise queue delivery deletion after the handler publishes its frame. A new real database-queue DELETE fault reaches that publication (one cached entry), then fails the required discard check on the previous source.

The worker now clears resolved retained state on JobExceptionOccurred, with evict_job_exception counting actual removed entries. The corrected control passes 1 test / 36 assertions: it clears the frame, cold-completes the same original with one effect and unchanged deadlines, then lets the original 90-second broker reservation expire after workflow completion. The obsolete delivery is refused under its unchanged tries=1 policy, raises the expected JobFailed(MaxAttemptsExceededException) event and leaves terminal history untouched. The direct-worker harness does not install Artisan's failed-job logger, so the event is observed explicitly rather than claiming a persisted failure record. No workflow deadline, lease or observation budget is increased. The separate SIGKILL/Artisan controls retain their failed-job record checks.

The full current continuation/process/cache/profile selection passes 65 tests / 2,408 assertions; focused style and static analysis pass. Earlier 9ee8d641 source selection passes 64 / 2,471, and its mirrored-source installed-package rehearsal passes 64 / 2,375 with all 955 source/resource files equal and product classes/functions loaded from the consumer's installed package. These mirrored-source results are not published-package evidence. The installed-package procedure is prepared for the normal release installation.

The new CPU/drift report preserves all eighteen passing source-92bb602f originals and reports per-host variation, not a universal observer correction factor. Existing large observations and the already-started 9ee8d641 full matrix keep their original source/outcomes. A new full-source qualification is dispatched on 48d664ff; no earlier source/matrix is treated as qualification of this correction. No merge, tag or minor-version reservation is pending.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Full source run39463825695 on 9ee8d641 records a PostgreSQL shard0 failure in V2TaskDispatchTest::testQueuedTimerAndLocalWritersUseConfiguredWorkflowTaskLease: its reflection assertion expected the old private claim result [true, null], while the claim-generation correction returns [true, null, 1]. The original failed result remains failed. This is a stale private-result expectation, not evidence of a PostgreSQL lease-policy defect.

64b306ac updates that one assertion to include the captured generation. The unchanged workflow/timer/local lease-expiry checks all pass in the focused control (1 test / 7 assertions); style passes. Runtime source is identical to 48d664ff, whose complete continuation selection passes 65 / 2,408 and mirrored-source installed-package rehearsal passes 65 / 2,445. The rehearsal checks all source/resource bytes and installed autoload paths but is not normal published-package evidence.

The 48d664ff full-source run39467828955 was canceled while preflight jobs had zero steps, avoiding a known obsolete expectation. The new full-source matrix is dispatched on 64b306ac. Already-running older matrices and large observations retain their exact source/outcomes. No tag, merge or released-artifact acceptance is claimed.

@rmcdaniel rmcdaniel changed the title feat: prototype bounded embedded queue continuations feat: add bounded embedded queue continuations Oct 20, 2026
@rmcdaniel
rmcdaniel marked this pull request as ready for review October 20, 2026 20:06
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel
rmcdaniel merged commit 2edaa6b into main Oct 20, 2026
19 checks passed
@rmcdaniel
rmcdaniel deleted the feature/embedded-execution-continuation branch October 20, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants