Skip to content

Allow bounded leased completion uploads during storage draining - #147

Merged
rmcdaniel merged 5 commits into
mainfrom
fix/leased-external-payload-draining
Sep 8, 2026
Merged

Allow bounded leased completion uploads during storage draining#147
rmcdaniel merged 5 commits into
mainfrom
fix/leased-external-payload-draining

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Sep 8, 2026

Copy link
Copy Markdown
Member

Problem

Related to #146. Storage draining permits task acknowledgements but refuses the external uploads that SDKs need before they can submit those acknowledgements.

Implementation

  • Add a capability-negotiated, bounded completion-context header on the existing upload endpoint.
  • Reuse activity/workflow/query ownership and worker-registration fencing. Unbound uploads and new client input remain blocked while draining.
  • Persist an immutable payload-slot allowance per lease, shared across complete/fail requests. Namespace quotas and hard storage fences remain authoritative.
  • Reconcile known matching ready references without a write after lost upload/completion responses.
  • Acquire engine ownership locks before object locks and recheck before writes. Reuse existing bounded cleanup without resetting renewable leases.
  • Prepare Server 2.3.5 and chart 0.1.84.

Qualification

  • Activity, workflow, query, failure, budget, namespace, ownership, expiry, hard/stale fence and cleanup regression tests.
  • Concurrent fresh-process uploads and cold completion/readback on SQLite, MySQL 8.0.46 and PostgreSQL 16.4; native cases included in existing CI.
  • PHP, Python and Rust capability negotiation and retry regression coverage.
  • Nine real-HTTP source cases: each SDK completed an activity, workflow and query while draining; a fresh Server process verified all nine retained payloads and closed tasks.
  • Full Server and all three SDK CI passed at the implementation commits. Release-metadata checks rerunning at the final heads.
  • Merge and publish the exact patch tuple.
  • Qualify that published tuple under bounded native storage before closing Storage draining rejects payload uploads needed to finish already-leased work #146.

Companion PRs: durable-workflow/sdk-php#65, durable-workflow/sdk-python#59, durable-workflow/sdk-rust#41.

A per-lease allowance is not physical disk reservation. Source HTTP tests used an isolated PHP built-in server and SQLite, not a production capacity benchmark or rollout. No production change is included in this PR.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Updated source qualification at 9bff92d:

  • Fresh-process concurrency/persistence: 4 cases, 102 assertions passed across SQLite and MySQL 8.0.46, using PHP 8.3.33. Simultaneous distinct uploads cannot replace an existing slot; cold retries retain the same reference and allowance; exhaustion stays an unadmitted storage-pressure refusal; completion succeeds while draining; post-completion retries are read-only and cannot create a new slot.
  • The process tests boot a fresh application/database connection for every operation. JSON object member order is canonicalized only in the test assertion; reference IDs, hashes, sizes, and scalar types remain strict. SQLite may report its existing retryable backend-lock-pressure response during a competing write; the fresh retry still cannot replace the winning slot.
  • Added worker-role enforcement and registry lock-order coverage. Ownership checks now acquire engine locks before payload locks, including cleanup; budget updates use locking reads rather than stale MySQL snapshots.
  • Focused source suite passed 124 cases / 3,729 assertions before the two final role/lock-order cases; those final completion tests passed separately (20 cases / 272 assertions). Pint passed.

Native process command: DW_TEST_COMPLETION_MYSQL_HOST=<disposable-host> DW_TEST_COMPLETION_MYSQL_PASSWORD=<test-only-password> php vendor/bin/phpunit tests/Feature/RuntimePayloadCompletionProcessTest.php. The test creates and drops uniquely named disposable databases. No customer environment was used.

This is draft-source qualification, not published-artifact or native storage-reserve qualification. The remaining end-to-end SDK/bounded-storage run is still required.

@rmcdaniel

Copy link
Copy Markdown
Member Author

Source qualification is complete for the upload/completion contract.

Native persistence: SQLite + MySQL 8.0.46 passed 4 cases / 102 assertions. SQLite + PostgreSQL 16.4 passed 4 cases / 102 assertions, with the two MySQL cases correctly skipped in that invocation. Both native database cases now run in the existing feature job, which passed at 6fa39ce5. These tests launch new PHP application processes for contested upload, cold retry, draining completion and post-completion readback; they are not in-memory model comparisons.

Actual HTTP, 2026-09-08 17:33 UTC: Server 6fa39ce5, PHP 18b52e0, Python 452a9dc, Rust 7db1f87. Nine isolated namespaces, each with a current leased task and external storage enabled at 64 KiB, were moved to draining before SDK completion. All three SDKs completed activity, workflow and query tasks using their public client methods. A new Server process verified nine closed tasks, nine bounded upload records, retained references and exact decoded values. Encoded payloads ranged from 1,747,648 to 2,796,224 bytes. No manual completion header was supplied by the clients.

HTTP setup used PHP 8.3.33's built-in server, SQLite and local storage; this is interoperability evidence, not a production concurrency/capacity claim. Initial fixture permissions and role setup were corrected before the clean run. Test containers/network are removed.

The PHP, Python and Rust implementation CIs are green. Patch metadata is now prepared for Server 2.3.5, PHP 2.0.9, Python 2.0.4 and Rust 2.0.5. Published-artifact bounded-storage qualification remains required before #146 closes.

@rmcdaniel
rmcdaniel marked this pull request as ready for review September 8, 2026 17:37
@rmcdaniel
rmcdaniel merged commit 5980bfc into main Sep 8, 2026
14 checks passed
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