Repository navigation
fix(storage): cancel paginated value reads - #276
perfloop[bot] wants to merge 14 commits into
Conversation
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
|
| if readAller, ok := reader.(listPayloadReadAller); ok { | ||
| return payloadReader.readSynchronous(ctx, reader, readAller.ReadAll) |
There was a problem hiding this comment.
Cancellation Still Waits On Reads
The real raw-file and segment readers implement ReadAll, so they always take this synchronous branch. If a disk read stalls after entering the operating system, closing its regular-file descriptor does not reliably interrupt it on Linux or macOS. This branch cannot return until readAll finishes, so a canceled list request can still retain its descriptor and handler indefinitely. The asynchronous fallback test uses a reader without ReadAll, while the benchmark gate cancels before entering the underlying file read, so neither covers this production path.
Knowledge Base Used:
There was a problem hiding this comment.
This is valid and should be fixed. The synchronous ReadAll path is the production path for the raw-file and segment readers, and canceling before ReadAll does not prove cancellation once that read has entered the OS. I will change the production read boundary so cancellation is observed while the payload read is in progress, with cleanup that releases the private descriptor and cached read state, and add a test that blocks the real ReadAll path after entry and verifies the canceled request exits without publishing a partial page. I will keep the live-page ordering, continuation, omission, and error behavior unchanged.
|
Closing this pull request because the remaining cancellation path depends on synchronous regular-file reads that cannot be safely interrupted by closing the descriptor. The review correctly identified that raw-file and segment readers can retain their read capacity until the syscall returns; the attempted containment does not meet the requested guarantee. I’m not shipping a partial fix that only returns the handler while the underlying read remains active. |
Canceled cluster value-page requests could leave local and peer scans running until a full page finished. The list path now carries the caller context into storage and stops before returning a partial page, so canceled work can release its iterator and payload resources promptly.
Spilled segment and raw-file values use private interruptible descriptors for paginated reads. Closing a canceled read cannot close a descriptor still owned by the shared cache. Successful pages keep their sorted order, continuation tokens, value lengths, omission behavior, and ordinary read-error handling. The private descriptor open/close work on live spilled-value pages is covered by a continuation-page guard.
The change adds storage coverage for raw and segment values, pre-canceled scans, and a read that never returns. It also adds a cluster benchmark that cancels after local and peer payload reads begin and checks post-cancel rows and bytes along with handler, scan, and payload-reader lifetime. A live benchmark covers the same public operation across continuation pages.
Testing:
make test-servermake test-storagemake test-bench-cache-service-list-with-values-cancellationmake test-race-bench-cache-service-list-with-values-cancellationmake test-integration TESTRUN='^$'make lintmake license-checkWorkload:
cluster public ListWithValues cancellation after local and peer raw-value reads beginns/opB/opallocs/opcancel-to-exit-ns/oprows-after-cancel/opbytes-after-cancel/opactive-list-scans-after-cancel/opactive-list-handlers-after-cancel/opactive-payload-readers-after-cancel/ophandler-exit-before-gate/opGenerated by Perfloop. Measurements and checks.
Note
Medium Risk
Changes cancellation and I/O behavior on the hot ListWithValues path across storage, files, and segments; semantics are well tested but regressions could affect pagination or concurrent reads.
Overview
Canceled
ListWithValuesrequests now stop promptly instead of finishing local/peer scans and disk reads. The callercontextis threaded from RPC/operations intoListKeyValuesWithPagination, with checks throughout the scan and proto conversion so no partial page or continuation token is returned on cancel.Spilled raw-file and segment values for list pages use private, interruptible file descriptors (
ReadForList/ReadEntryForList,InterruptibleReadCloser) rather than the shared FD cache, so cancellation canInterrupt()blocked reads without affecting other readers. AlistPayloadReaderties context cancellation to descriptor interrupt (syncReadAllfast path plus worker fallback).Benchmark-only work under
ocache_benchmark: payload read blocking/stats inbenchio, cluster cancellation test/benchmark withNewBenchmarkCoordinator, Makefile targets, and storage tests for cursors, segments, and stuck reads.Reviewed by Cursor Bugbot for commit 4a9acf2. Bugbot is set up for automated code reviews on this repo. Configure here.