Skip to content

Add standalone DELETE operation with lossless dispatch lifecycle - #185

Merged
mikehorgan-dell merged 51 commits into
mainfrom
feat/delete-operation
Sep 4, 2026
Merged

Add standalone DELETE operation with lossless dispatch lifecycle#185
mikehorgan-dell merged 51 commits into
mainfrom
feat/delete-operation

Conversation

@mikehorgan-dell

@mikehorgan-dell mikehorgan-dell commented Sep 4, 2026

Copy link
Copy Markdown
Member

Summary

Standalone DELETE operation

  • add a standalone DELETE request engine with S3 support across the Netty, AWS SDK, and RDMA drivers
  • add DELETE target selection modes: explicit manifest, seeded count, guarded existing-prefix, and duration
  • enforce failed-object budgets as a sticky stop trigger with coordinated drain across distributed slices
  • add the DELETE metrics contract, persisted result artifacts, and inventory pre/post verification
  • add seeded DELETE cleanup and CLI support for DELETE preparation, topology, and metadata

Lossless dispatch lifecycle

  • track every operation's custody from generation through terminal outcome so that shutdown recovers unattempted work as accounting rather than converting it to failures
  • unify generator and driver custody registries and freeze the invariants under test

Dispatch-path remediation

The lifecycle accounting introduced a 4-5% WRITE throughput regression against 5.14.2 through dispatchLock contention and cross-thread hand-offs on the critical path. The remediation commits recover it:

  • decouple generator spin from lock admission and pre-check the full queue before acquiring the admission lock
  • park/unpark the dispatcher directly instead of awaiting a locked condition; signal it as soon as a permit is released
  • replace the generator's Thread.yield() spin with an adaptive back-off that holds on partial output acceptance and excludes throttle waits
  • clamp RateThrottle.tryAcquire(int) so an exhausted quota is treated as a refusal rather than a negative permit count

An 18-run balanced Williams crossover campaign confirmed throughput parity with the release while reducing client CPU utilization by 37% and voluntary context switches by 8%.

mikehorgan-dell and others added 21 commits September 4, 2026 08:46
signalDispatch() previously unparked the dispatch thread and then took
dispatchLock to signal dispatchReady. The unpark had no effect on a
thread inside Condition.await() (AQS re-parks on a spurious wake), so
every completion on a Netty event loop still acquired dispatchLock.

The dispatch task now parks its own thread at its three wait sites and
signalDispatch() only unparks it. The existing double-checks plus the
sticky unpark permit rule out lost wake-ups, and an interrupt still
surfaces as InterruptedException so stop() behaves as before. The
Condition constructor parameter is retained for extension compatibility
but is unused.

Also name the generator's no-progress back-off and correct the recycle
path comment that still described a yield.
Per-thread context-switch sampling of the write/10KiB/T4 arms showed the
generator's fixed 50 us park accounting for ~2.4 of the candidate's +2.5
voluntary switches per operation (~9.4K wake-ups per second per worker)
while the driver's input queue was full. The dispatcher and event loops
were unchanged against the release.

The no-progress output wait now starts at 50 us and doubles per
consecutive blocked iteration up to 1 ms, resetting on any progress. The
drain interval is far longer than the cap, so a refilled queue is still
picked up promptly. The recycle and retry-idle waits keep the fixed short
park: they resolve at request-latency cadence, where a growing park would
sit on the re-admission path.
With the input queue full the dispatcher frees one slot per completed
operation, so the generator's output attempts alternate between one-op
successes and refusals at request frequency. Resetting the back-off on
any progress restarted the 50 us wait each time, leaving the generator
at ~1.3 wake-ups per operation after the adaptive back-off landed.

Only a fully accepted output now resets the wait; a partial acceptance
holds it. At the 1 ms cap several slots accumulate per wake-up, so the
generator admits a small batch each time it runs instead of one op.
A rate throttle refusing permits produced the same "nothing accepted"
outcome as a full output queue, so the adaptive back-off grew to its
1 ms cap while waiting for permits that return every few hundred
microseconds. At an 8K ops/s limit the generator woke ~600 times per
second per worker and throughput collapsed to ~1.6K ops/s.

Decide the wait in one place: a throttle refusal parks the fixed short
interval and leaves the queue back-off untouched; only an output refusal
with permits in hand grows it; acceptance returns immediately.
RateThrottle.tryAcquire(int) returns the schedule deficit as a negative
count once its quota is exhausted, and its first call grants the whole
request eagerly, so the second call under a rate limit returns roughly
minus the batch size. Since 0d345d3 the generator asserts the permit
count is within [0, pending]; that assertion turned every rate-limited
run into a single burst followed by a stopped generator. The release
never asserted and simply saw n <= 0.

Clamp each throttle's answer to zero before the next throttle and the
range check. The regression test drives the recycle path with a throttle
returning the deficit and requires the generator to survive and resume.
Main now fails compilation on Error Prone warnings. Give the verification
ledger's Presence an explicit byte code instead of relying on ordinal,
raise the pending-phase sentinel on demand instead of caching a static
Throwable, and parenthesize mixed boolean operators.
@mikehorgan-dell
mikehorgan-dell merged commit a803677 into main Sep 4, 2026
6 checks passed
@mikehorgan-dell
mikehorgan-dell deleted the feat/delete-operation branch September 4, 2026 14:08
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