Repository navigation
[feature](cloud) Spill to object storage in cloud mode and report the traffic in SHOW DATA - #68032
Conversation
|
Thank you for your contribution to Apache Doris. Please clearly describe your PR:
|
|
/review |
There was a problem hiding this comment.
Static verdict: REQUEST_CHANGES. The S3-backed spill path and warehouse-level SHOW DATA plumbing are present, but 16 blocking correctness and operability issues remain. The highest risks are cross-BE deletion caused by a shared identity, non-durable and non-idempotent lifetime statistics, cleanup paths that can delete live data or leak billable storage, request counters that do not measure physical provider traffic, and unbounded recovery/metadata scans.
Critical checkpoints (Part 1.3):
- Goal and proof: The goal is remote S3 spill with capacity control, request/byte accounting, persisted warehouse totals, cleanup, and SHOW DATA exposure. Happy-path BE/cloud/FE/regression tests were added, but the goal is not correctly achieved because identity, durability, cleanup, retry, and accounting failures remain.
- Scope and focus: The 47-file change is cohesive around that feature, and the cross-layer breadth is necessary, but the accepted defects mean it is not yet review-ready.
- Concurrency: I traced the spill-GC thread, writer/upload pools, non-blocking close, upload-budget mutex/condition variable, atomic counters, heartbeat mutation, and FDB transactions. Reachable upload callbacks are balanced and I found no separate deadlock, but release builds mask an underflow invariant (MMF-10), and transaction serialization does not fence stale generations (MMF-9).
- Lifecycle and SIOF: No cross-TU static-initialization dependency was found. Process boot, query/file teardown, startup cleanup, graceful/abrupt shutdown, vault binding, and multipart ownership were traced; MMF-4, MMF-6, MMF-7, MMF-8, MMF-11, MMF-12, and MMF-13 show lifecycle failures.
- Configuration: New mutable storage, upload-budget, and recycler controls are observed online, but negative storage limits disable the cap (MMF-3), non-positive TTLs can select live objects (MMF-7), and runtime accepts/persists an upload budget that startup rejects (MMF-15).
- Compatibility: The protobuf additions are additive, SHOW DATA row width is consistent, non-cloud mode avoids the RPC, and the repository's supported latest-MS/older-FE direction remains compatible. The FE-first/older-MS hypothesis was dismissed under that explicit contract.
- Parallel paths: Local and S3 writer paths, PutObject/UploadPart, data/footer reads, synchronous/non-blocking close, and hooks with no stats sink were checked. Local behavior remains balanced, but both read and write request accounting miss physical retries/failures (MMF-5, MMF-16).
- Special conditions: Remote-only selection, default-vault lookup, positive-limit gates, age cutoffs, and release underflow handling were checked. The non-positive and defensive-continue conditions are not safe (MMF-3, MMF-7, MMF-10, MMF-15).
- Test coverage: Ordinary round trips, rotation, cleanup retries, capacity concurrency, budget blocking/cancellation, several upload failures, meta-service aggregation, and SHOW DATA shape are covered. Missing negative coverage includes two live BEs sharing an identity, reordered/stale reports, crash before report, live identity migration, real expiry/liveness, abort failure/restart residue, paginated cleanup, churn cardinality, invalid dynamic updates, and SDK/GET retries.
- Test results: The changed expected SHOW DATA output is internally consistent with the five-column metadata. Per the review-only contract, I did not run builds or tests; author/CI results were not independently verified here.
- Observability: Profiles, workload counters, BE metrics, logs, and meta-service bvars were added, but the report RPC omits its FDB read metrics (MMF-1), physical S3 attempts are undercounted (MMF-5/MMF-16), and the persisted total itself is not reliable across crashes or identity transitions.
- Transactions and persistence: The FDB read-modify-write is atomic per attempt, but unequal boot IDs are not an ordering fence (MMF-9), process counters lose unreported crash suffixes (MMF-6), and retired identity records are never compacted (MMF-14). No FE EditLog change is involved.
- Data writes and crash behavior: S3 multipart creation/upload/abort, visible-object deletion, capacity reservation, and stats writes were traced. Crashes can lose totals and multipart ownership, deletion failure releases live capacity, and cleanup can delete another live BE or a long-running query's data (MMF-4/MMF-6/MMF-7/MMF-8/MMF-11).
- FE/BE variable passing: No new scattered FE-to-BE session variable path was introduced; the new MS/FE protobuf and five-column mapping are consistent. The existing heartbeat cloud identity is nevertheless shared and mutable in ways incompatible with its new use as storage/stats identity (MMF-4/MMF-13).
- Performance: Startup recursively materializes the entire spill namespace outside its work budget (MMF-12), and SHOW DATA range-reads every historical backend identity without pagination or compaction (MMF-14).
- Other issues: Three capped normal/risk convergence rounds found no additional distinct issue after the 16 inline findings. Two hypotheses were dismissed with code evidence: dynamic default-vault rebinding is not an explicit contract, and FE-first upgrade is outside the documented MS-first rollout direction.
Focus response: no additional user-provided review focus was supplied; full-scope review was performed.
|
Thanks for the review. Pushed a follow-up commit addressing it; per finding: Fixed
Kept as designed (happy to discuss)
|
add6dfd to
02ba809
Compare
Local pipeline review — ✅ PASSschema: doris-repo-review/v1
status: PASS
pr: apache/doris#68032
commit: 5d93bb00ab769d313f5868a764aafc3aa77c9865
base: 3c489e297d95191c26e7259a9939c2fabb3510cb
reviewed_at: 2026-09-17T15:55+08:00
reviewer: mrhhsg
model: gpt-6-astra
effort: xhigh
findings: {blocker: 0, major: 0, minor: 3, nit: 1}
rounds: 1
converged: trueNotes for maintainers
Reviewed locally with the |
94124f3 to
71a21e2
Compare
|
/review-light |
There was a problem hiding this comment.
Request changes. Static review of exact head 71a21e2af8850b8467c997d9f0adef13cdf8d010 found nine distinct issues: five P1 correctness/scalability issues and four P2 observability/freshness gaps. Existing inline threads were treated as hard duplicate fences.
Critical-checkpoint conclusions:
- Goal and tests: the PR adds cloud S3 spill storage, lifecycle cleanup, accounting/billing, and remote-spill observability. It has substantial BE, cloud, FE, and regression coverage, but the goal is not safely complete because shared-vault ownership, deleted-instance cleanup, terminal write-error propagation, error-unwind accounting, and recycler scalability are not covered.
- Scope: the 62-file change is broad but cohesive. The new storage path itself is understandable; parallel consumer and producer paths were missed (MAIN-3, MAIN-5, MAIN-7, MAIN-9).
- Concurrency and lifecycle: I traced GC, recycler, scheduled FE polling, async S3 upload callbacks, upload-budget admission/release, and RuntimeState/QueryContext teardown. The ordinary gate/callback branches and lock ordering appear balanced with no new deadlock found, but MAIN-1, MAIN-2, MAIN-4, and MAIN-9 expose cross-instance, teardown, or terminal-close lifecycle failures.
- Configuration and compatibility: mutable limits, upload budget, heartbeat/TTL, poll interval, and max age are read on live paths; startup/dynamic parity already has an existing review thread. Appended optional Thrift/protobuf fields and name-mapped audit columns preserve rolling compatibility. MAIN-8 violates the configured freshness guarantee after a wall-clock rollback.
- Conditions and parallel paths: TTL filtering is the direct trigger for MAIN-2. The local-spill analogues exposed missing live-stat aggregation, Iceberg counter registration, HTTP-plan audit initialization, and Iceberg terminal-close handling.
- Tests/results: added tests cover core S3 writer/reader/quota behavior, cloud expiry, FE polling/audit aggregation, and SHOW DATA, and the checked-in audit schema output is consistent. Missing negative coverage is called out inline: shared instances/endpoints, default-TTL instance deletion, repartition error unwinding, multi-page shared-vault scans, Iceberg S3 close failures/counters, live consumers, HTTP-plan audit output, and clock rollback. No build or test was run for this review, as required by the task.
- Observability: the PR adds useful counters, metrics, audit fields, and logs, but MAIN-3, MAIN-5, and MAIN-7 leave supported paths invisible or report sentinel values.
- Persistence and data writes: no new FE EditLog path is involved. Remote multipart writes and cloud recycler state were traced through failure/cleanup boundaries; MAIN-2 loses the durable cleanup owner, MAIN-4 loses the accounting owner, and MAIN-9 suppresses a terminal write failure after intermediate inputs may already be deleted. Crash-before-report, failed delete, multipart residue, retry-attempt counting, and heartbeat deletion races are already covered by existing threads and were not duplicated.
- FE/BE contracts and performance: the new optional wire fields are compatible, but live consumers are incomplete (MAIN-3). MAIN-6 adds repeated unbounded
N * Mobject listings on shared vaults. The fixed 64-KiB footer probe is bounded and was not considered a separate high-value issue. - Other checkpoints: no new nullable-column, visible-version, MoW, static-initialization, or schema-format issue applies here. Error handling and ownership were rechecked; MAIN-4 and MAIN-9 are the remaining ownership/error-propagation defects.
The focus file contains only -light and names no subsystem; I kept the presentation concise but completed the full review. The final BE pass found MAIN-9 at the three-round cap, so this review is capped/incomplete rather than converged. Validation is static only.
|
/review-light |
|
Codex automated review failed and did not complete. Error: {"type":"error","status":400,"error":{"type":"invalid_request_error","message":"The 'gpt-6-sol' model is not supported when using Codex with a ChatGPT account."}} Please inspect the workflow logs and rerun the review after the underlying issue is resolved. |
…ceberg spill writers explicitly ### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: Follow-ups of the review of remote (object storage) spill. - A SpillFileWriter can outlive its SpillFile: the repartitioner keeps its output writers while the output files live in the caller, so an error after setup_output() destroys the files first. Closing the writer later force-charged a footer to no owner (leaking spill capacity) and, on object storage, could publish a part after the file's prefix was deleted. SpillFile::gc() now discards its active writer first: the unfinished part is dropped without footer or close(), in-flight uploads are drained, the budget is reconciled and the multipart upload is aborted. A writer destroyed after a failed close() unregisters itself so the file never reaches a dangling writer. - The Iceberg sort writer relied on the writer destructor to close its spill files, which only logs a failure. On object storage close() uploads the tail of the file, so a failure could silently lose rows, and the intermediate merge deleted its inputs before its output was complete. Both paths now close the writer and return its status. - The Iceberg spill sink registers the remote spill counters, so its profile shows remote requests, bytes and times like the other spilling operators. - The remote spill bytes are aggregated by ProfileManager and shown by the query profile REST API and show proc '/current_queries'; the HTTP query plan audit event sets them to 0 instead of the -1 default. - RemoteSpillStatsPoller measures the age of the polled value on the monotonic clock, so a wall clock moved backward cannot keep a stale value fresh. - Document that the remote request counters count logical requests: retries inside the object storage client are not included. ### Release note None ### Check List (For Author) - Test: Unit Test - BE: SpillFileS3Test (new SpillFileDestroyedBeforeWriterDiscardsPart, FailedCloseThenSpillFileDestroyed), SpillFileTest, SpillRepartitionerTest, partitioned and spill sort operators, SpillIcebergTableSinkOperatorTest, VIcebergPartitionWriterTest (213 passed) - FE: RemoteSpillStatsPollerTest, CurrentQueryStatisticsProcDirTest, AuditLoaderTest - Behavior changed: Yes. show proc '/current_queries' has two more columns, SpillWriteBytesToRemoteStorage and SpillReadBytesFromRemoteStorage. - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01B5f3dfnSMXhasgsShyagfR
|
/review-light |
There was a problem hiding this comment.
Static review complete on head bdd4fad. I request changes for six distinct issues: default-vault rotation (P1), billing of buffered bytes, footer read cap, mixed-version current-query rows, completed-query audit stats, and a fresh total that omits a BRPC-reachable BE (P2 each). Inline comments give the reproductions and suggested fixes. Existing threads were treated as duplicate fences. The requested focus was light; the complete changed-file review found no other focus-specific concern. No build or test was run in this review environment.
Critical checkpoint conclusions:
- Goal and proof: Cloud S3 spill and reporting are wired end to end and have BE unit, FE unit, and regression coverage. Those tests do not prove the six state transitions above, so the goal is not fully met.
- Scope: The BE/FE/protocol/test changes are connected to this feature; no unrelated source change was identified.
- Concurrency: Async upload callbacks, the spill GC thread, and FE polling introduce concurrency. Traced budget and pending-delete locks; heavy S3/RPC work is outside those locks. No separate lock-order deadlock was substantiated.
- Lifecycle: Writer close, abort, query teardown, runtime-state destruction, and GC were traced. No further lifecycle issue survived, but file-system selection must be pinned per file across default-vault rotation (M1). No cross-TU static initialization dependency was found.
- Configuration: New settings are validated or refreshed where intended. M1 ignores a changed default vault, and M3 exceeds the configured effective read cap.
- Compatibility: Optional BE resource/Thrift fields support older peers; an older BE cannot create this S3 spill. The all-FE HTTP response is incompatible during a rolling FE upgrade (M4).
- Parallel paths: Local and Iceberg spill paths were checked. Live statistics, completion audit, and warehouse totals need consistent remote data; M5 affects the final audit path.
- Conditions: Capacity, cancellation, readiness, and polling branches were traced. Skipping heartbeat-dead but BRPC-reachable BEs causes M6.
- Test coverage: Existing round-trip, failure, cleanup, limit, query-stat, schema, and SHOW DATA cases cover normal paths; add the six focused reproductions from the inline comments. Complex BE logic has unit tests, but these transitions lack them.
- Test results: The changed audit expected-output rows match the added schema columns on static inspection. No test result was independently generated or executed here.
- Observability: New remote byte/request counters, metrics, logs, and audit fields cover major paths. M5 leaves persisted audit fields misleading when the final report is late.
- Persistence: No new EditLog transaction path or meta-service spill-stat record is in this diff. The documented crash-residue limitation is already covered by prior review context; M5 affects persisted audit rows.
- Writes and crashes: S3 close/abort, GC retry, and budget reconciliation were traced without another substantiated issue. M2 reports in-memory reservations as stored object bytes.
- FE/BE values: New Thrift and resource response fields are propagated for live query stats and billing; final PQueryStatistics lacks the remote pair needed for M5.
- Performance: Upload admission and read coalescing bound normal work; M3 defeats the footer read bound under a small configured cap. No additional concrete performance issue survived.
- Other concerns: All 63 changed paths and six candidate findings were reswept. The second normal and focused review round found no new valuable findings; this review is complete.
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: When reading a remote spill part, the footer is fetched by one probe of the part's tail. The probe was always min(part size, 64 KiB), even when spill_s3_read_coalesce_bytes (or the query's spill buffer size) was smaller. So a 1 KiB setting still issued a 40-64 KiB GET and allocated a buffer of that size, which breaks the documented per-GET bound. The probe is now bounded by the effective coalesced read size as well, but it is never smaller than the 16-byte tail. If the offsets array does not fit, it is fetched separately, as before. Also document that an empty spill_s3_storage_vault is resolved to the default vault when the BE spills for the first time. A later SET DEFAULT STORAGE VAULT applies to spill after the BE restarts. Also document that the rest of an offset array that does not fit in the probe is read whole, in the same way as a single block that is larger than the read size. ### Release note None ### Check List (For Author) - Test: Unit Test - SpillFileS3Test.ReadCoalescesAdjacentBlocks now expects the probe to be capped by a 1 KiB and a 40 KiB window; new FooterLargerThanProbe covers a window smaller than the block offset array (probe, then one GET for the rest of the footer); SpillFile* (63 tests) passed - Behavior changed: No - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01B5f3dfnSMXhasgsShyagfR
|
run buildall |
…schema projection ### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: An earlier commit of this PR made the audit loader keep a failed batch and retry it until it loaded, sent it with max_filter_ratio 0, and appended a 32-character UUID to the stream load label. Three regressions followed. A single row filtered by BE (for example an over-long field) made every retry fail, so the FE stopped loading audit rows for good. After an ambiguous transport failure the batch kept its label, and a later "Label Already Exists"/FINISHED reply reset the buffer without checking the row count, dropping rows assembled after the first attempt. With a long FE identity (FQDN plus port) the label exceeded the 128-character limit and every load was rejected. Restore the master behavior: max_filter_ratio 1.0, the original label, and discarding the batch on failure. Keep only the send-time projection that drops the two remote spill columns while the audit table still has its old schema, which is what a follower-first upgrade needs. ### Release note None ### Check List (For Author) - Test: Unit Test (AuditLoaderTest, AuditStreamLoaderTest: 9 tests); FE checkstyle - Behavior changed: No (audit loading failure handling matches master again) - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
… correct stale spill comments ### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: Follow-ups from the local review of the spill-to-S3 changes. The audit projection test only checked the field count, the stmt position and that the query ids appeared, so dropping the wrong two columns would still pass; it now gives the neighbouring columns distinct values and checks every kept column against the full row by name. Three upload-queue tests declared their teardown Defer before the ScopedThreads, so the threads were joined before the Defer released the blocked uploads and a failing assertion hung the test binary; the Defer now follows the threads. Several comments described earlier revisions or were wrong: the lifecycle rule prefix (keys are under <vault prefix>/spill/), spill_s3_max_inflight_upload_bytes described as a memory bound instead of a back-pressure threshold, the SpillDataDir/writer/reader contract that all IO goes through fs() (each file uses the fs pinned in its SpillFile), the thread a synchronous close runs on, the persisted-bytes and store-view comments, spill_storage_limit not applying to the S3 store, and the destroy-order reason in ExecEnv. ### Release note None ### Check List (For Author) - Test: Unit Test (SpillFileS3Test + SpillFileTest: 73 tests; AuditLoaderTest + AuditStreamLoaderTest: 9 tests); clang-format 16, check-build-hygiene, FE checkstyle - Behavior changed: No - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: SpillFileWriter::_append_to_part sliced every serialized block into s3_write_buffer_size pieces and waited for the S3 upload queue before each piece. S3FileWriter already splits appended data into upload buffers, so the slicing only made the back-pressure check run once per buffer. The block is already in memory when it is appended, and what the back-pressure has to bound is the rate of later appends, so the queue is now checked once per append and the whole block is handed to S3FileWriter. A writer may queue the buffers of one block after the check; the config and class comments say so, and the upload-queue test allows the at most two buffers a test block can fill. ### Release note None ### Check List (For Author) - Test: Unit Test (run-be-ut.sh --filter='SpillFile*:SpillDataDir*', 73 tests passed) - Behavior changed: No - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: Without spill_s3_storage_vault_id, an instance with storage vaults spilled to the S3 vault with the smallest ID, so spill stayed on the oldest vault forever. It now uses the S3 vault with the largest ID, the newest one: changing the default vault still does not move spill, and an added S3 vault receives new spill files. Files already spilled keep the file system they were created on and are read and deleted there. ### Release note None ### Check List (For Author) - Test: Unit Test (run-be-ut.sh --filter='SpillFile*:SpillDataDir*', 73 tests passed, SelectVaultFileSystem covers the numeric comparison and a newly added vault) - Behavior changed: Yes (automatic vault selection picks the largest S3 vault ID) - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
… time ### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: A spill directory whose deletion failed is queued and retried by the spill GC. Every round retried the whole queue before spill_gc_work_time_ms was checked, and stop() and the destructor retried it again. With an unavailable object store each remote deletion waits for a request timeout, so one GC round, and BE shutdown, could take one timeout per queued directory. Retries now stop once the time slice is used up (at least one directory is tried per round); the directories not tried are queued again ahead of the ones that just failed, so one slow failing directory cannot starve the others. stop() and the destructor are bounded by spill_gc_work_time_ms the same way. Remote objects left at shutdown are removed by the lifecycle rule of the bucket, local directories are moved to the GC root after restart. ### Release note None ### Check List (For Author) - Test: Unit Test (run-be-ut.sh --filter='SpillFile*:SpillDataDir*:*Spill*Manager*', 74 tests passed; new PendingRemoteDeletesAreBoundedByGcWorkTime uses a slow mock object store where one directory keeps failing, and fails when the queue order is not rotated) - Behavior changed: No - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: stop() and the SpillFileManager destructor retried the queued failed spill deletions within spill_gc_work_time_ms, and their comments promised that bound. It did not hold for remote directories: at least one directory is always tried, and a failed remote deletion waits for the object store twice (the delete and the reconcile LIST), each with the client's retries, so an unavailable object store could still delay a graceful shutdown by minutes. Shutdown now retries only directories of local stores; remote ones stay queued for a later gc() and are otherwise removed by the lifecycle rule of the bucket, as before. The GC round keeps its time slice; its comment now says that a remote deletion can exceed it. The bounded-retry test also checks that stop() sends no delete request, and releases its spill files in order instead of relying on the element destruction order of vector::clear(). ### Release note None ### Check List (For Author) - Test: Unit Test (run-be-ut.sh --filter='SpillFile*:SpillDataDir*:*Spill*Manager*', 74 tests passed) - Behavior changed: No - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
…ctation ### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: BE UT CI failed on two tests that are unrelated to each other but both broke when run together with the rest of the suite. SpillFileS3Test.SelectVaultFileSystem enumerates every registered S3 storage resource to pick the one with the largest ID; TabletCooldownTest registers a mock S3 storage resource (id 10000) and a storage policy in SetUpTestSuite but never removes them in TearDownTestSuite, so they leak into the process-global registry for the rest of the gtest binary and get picked up as a spurious "largest ID" vault by any later test, including this one. TabletCooldownTest's teardown now also deletes its storage resource and policy. PartitionedAggregationSinkOperatorTest.SinkWithSpilError asserted that close() still returns OK after a fault-injected spill write failure; this PR's SpillFileWriter intentionally made close() sticky after a failed write_block() (see its header comment), so close() now correctly surfaces that earlier failure. The test still encoded the old swallow-the-error behavior; it is updated to expect close() to fail. ### Release note None ### Check List (For Author) - Test: Unit Test (run-be-ut.sh --filter reproduced both failures standalone and together with TabletCooldownTest before the fix, and verified green after; a broader run across all spill/partitioned-aggregation/hash-join/sort BE UT suites, 175 tests, also passed) - Behavior changed: No - Does this need documentation: No
…SHOW DATA only for the cloud warehouse listings
### What problem does this PR solve?
Issue Number: close #xxx
Related PR: #xxx
Problem Summary:
Two review findings on the S3 spill path.
1. `SpillFileManager::_retry_pending_spill_directories` put every directory it
did not get to (and every one that failed) back through
`_add_pending_directory`, which scans the queue twice per call. Remote spill
queues one prefix per spill file, so after an object store outage during a
partitioned spill a time-sliced GC pass that tries one slow directory
re-adds thousands of entries one by one, under the pending-directory mutex,
on every pass. The kept entries were already merged with each other when
they were queued; now they go back as one batch (untried ones first, then
the failed ones, as before) and only the directories queued while the pass
ran are merged through `_add_pending_directory`.
2. `ShowDataCommand.toRedirectStatus()` returned `FORWARD_NO_SYNC` for every
form of SHOW DATA, although only the two cloud listings that show the
`__remote_spill__` row (`entire_warehouse` without `db_names`, and
`SHOW DATA` with no database selected) read the remote spill size that the
BEs report to the master FE. `SHOW DATA FROM db.table`, the per-database
listing, the `db_names` form and every non-cloud deployment are served
locally again, as on master.
### Release note
None
### Check List (For Author)
- Test: Unit Test
- BE: `SpillFileS3Test.PendingRemoteDeleteBacklogIsRequeuedInBulk` (new) with
`SpillFileS3Test.*` and `SpillFileTest.*` (75 tests)
- FE: `ShowDataCommandTest` (12 tests)
- Behavior changed: No
- Does this need documentation: No
…er::abort() ### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: A spill part that fails or is discarded before it is published leaves the parts of its multipart upload in object storage. Deleting the spill prefix does not remove them (they are not listed as objects), so they stay billed until the bucket's lifecycle rule aborts the upload. S3FileWriter never aborts on its own: other writers' uploads may be completed elsewhere (the S3 committer completes them on FE), so destroying a writer must not change the provider state. The spill writer therefore aborted the upload itself, rebuilding the bucket, key and upload id from S3FileWriter's getters and calling the object storage client of the spill file system directly, and relied on destroying the writer first to make sure no part was uploaded after the abort. Add S3FileWriter::abort(), an explicit call for an owner that knows no one will complete the upload: it marks the writer failed so that queued buffers skip their upload, waits for the running uploads, closes the writer and aborts the multipart upload if one was started. It is allowed on an open writer or one whose close() failed, never runs on destruction, and does not change any existing caller. The spill writer calls it for a failed or discarded remote part, and its own MultipartUploadId and abort helper are removed. ### Release note None ### Check List (For Author) - Test: Unit Test - S3FileWriterTest.abort: abort after multipart uploads started (one abort, nothing completed, close() fails afterwards); abort while 10 uploads are blocked in the mock and 2 are queued (the abort request waits for the running uploads, the queued ones are skipped); abort below one upload buffer (no request); abort after a failed close() - Existing spill S3 tests that check the aborted uploads of failed and discarded parts - Behavior changed: No - Does this need documentation: No Claude-Session: https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: Failed or discarded remote spill parts do not need a new S3FileWriter abort interface just to reclaim incomplete multipart uploads sooner. Remove that interface and the spill-side abort logic. Keep waiting for in-flight uploads through writer destruction before prefix cleanup, and retain write failures instead of publishing partial spill files. Incomplete multipart uploads are reclaimed by the operator-configured bucket lifecycle rule. Update the tests and document this requirement. ### Release note Incomplete multipart uploads from failed or cancelled S3 spills are left to the bucket's AbortIncompleteMultipartUpload lifecycle rule rather than being actively aborted by Doris. ### Check List (For Author) - Test: ASAN unit tests via ./run-be-ut.sh --run --filter='SpillFileS3Test.*:S3FileWriterTest.*' -j24 (47 passed); scoped clang-format/check-format, build hygiene and git diff --check passed - Behavior changed: Yes. Incomplete spill MPU storage is reclaimed by lifecycle cleanup instead of an explicit abort RPC. - Does this need documentation: Yes. Configuration comments and the existing PR description explain the lifecycle prerequisite.
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: Spill upload statistics inferred request counts by inspecting S3FileWriter upload IDs and completed parts. Remove those write-request estimates while keeping successful upload bytes, upload time and queue wait. Add spill-layer lifecycle logs for logical file creation, close and cleanup, with query identity, local/remote storage, pinned filesystem/path, bytes, part count and status. Preserve per-file context when failed deletions merge in the manager, rate-limit retry warnings, and log eventual success. Add per-store logical-file creation and successful-cleanup counters; failed attempts and repeated gc/destruction must not count as another deletion. The generic S3 writer and incomplete-MPU lifecycle policy are unchanged. ### Release note Spill files gain query-correlated lifecycle logs and per-store spill_file_created_total / spill_file_deleted_total counters. Remote spill write-request estimates are no longer collected; byte/time statistics and read request counters remain. ### Check List (For Author) - Test: ASAN Unit Test via ./run-be-ut.sh --run --filter='SpillFileS3Test.*:SpillFileTest.*:S3FileWriterTest.*' -j32 (96 passed, including 7 new tests); clang-format/check-format v16, build hygiene, changed-line clang-tidy and git diff --check passed - Behavior changed: Yes. Add per-logical-file logs/metrics and remove inferred write-request counters; no query-result or spill-format change. - Does this need documentation: Yes. Code comments and the existing PR description document log fields, logical-file counter scope and lifecycle cleanup boundaries.
### What problem does this PR solve? Issue Number: None Related PR: apache#68032 Problem Summary: The upstream SHOW DATA column is now named ChangelogSize, while two spill regression-test comments still described the former BinlogSize column. Align those comments with the rebased command metadata without changing test behavior. ### Release note None ### Check List (For Author) - Test: Unit Test: ShowDataCommandTest 12/12 passed; FE build and Checkstyle passed; BE ASAN compilation reached final link but failed on duplicate rust_eh_personality in liblance_c.a and libpaimon_c.a - Behavior changed: No - Does this need documentation: No
6d43a17 to
2811649
Compare
|
run buildall |
Cloud UT Coverage ReportIncrement line coverage Increment coverage report
|
TPC-H: Total hot run time: 28373 ms |
TPC-DS: Total hot run time: 150686 ms |
ClickBench: Total hot run time: 25.34 s |
FE UT Coverage ReportIncrement line coverage |
FE Regression Coverage ReportIncrement line coverage |
BE Regression && UT Coverage ReportIncrement line coverage Increment coverage report
|
Local pipeline review — ✅ PASSschema: doris-repo-review/v1
status: PASS
pr: apache/doris#68032
commit: 2811649fa760e18cbcdd6e61e86e30ad607bdbba
base: 85adcce350bf4ece48955daa7ac426bef53a892f
reviewed_at: 2026-10-22T09:20+08:00
reviewer: mrhhsg
model: gpt-6-astra
effort: xhigh
findings: {blocker: 0, major: 0, minor: 5, nit: 13}
rounds: 1
converged: trueNotes for maintainers
Reviewed locally with the |
924060929
left a comment
There was a problem hiding this comment.
Reviewed the FE changes at 2811649. The cloud disk-report -> Backend -> warehouse SHOW DATA flow and the remote query-statistics -> audit/proc/REST flow are consistent with the documented best-effort contract. Checked scoped SHOW DATA forwarding, dispatched-backend retention through errors/retries/forwarded cancellation, old audit-table projection, mixed-FE row normalization, related tests, and optional Thrift field IDs against branch-3.1/4.0/4.1/4.2. No new blocking FE issue found. FE-only static review; no local build or tests run. BE implementation is outside this approval's scope.
|
PR approved by at least one committer and no changes requested. |
What problem does this PR solve?
Issue Number: None
Problem Summary:
Cloud-mode BEs could only spill to local disks. This PR lets query spill go to an S3 storage vault, selected in
be.conf(spill_storage_type = local | s3), while keeping the existing spill operators and part-file format. Each BE reports the bytes of spill objects it currently holds in its disk report, andSHOW DATAlists the sum. This is a best-effort, BE-reported value, not a durable source for exact billing; see Known limitations.BE: where spill goes
spill_storage_type = s3is accepted only in cloud mode;SpillFileManager::init()validates it and builds the spill stores itself (the remote store for query spill plus the local roots that external writers such as Paimon JNI still need). The store is split intoSpillDataDir(base),LocalSpillDataDirandRemoteSpillDataDir.RemoteSpillDataDir::select_vault_fs), without touching storage vault code:spill_s3_storage_vault_id(a vault ID, not a name) when configured;latest_fs(), set only when storage vaults are disabled);<vault prefix>/spill/{ip}_{port}/{query_id}/{spill file}/{part}(ip/port: the BE address and heartbeat port). A spill file deletes its prefix when the query releases it; failed deletions are retried by the GC thread within its time slice (spill_gc_work_time_ms), at least one per pass and rotating so that one slow failing prefix does not starve the others.stop()and the manager destructor (reached only when the BE exits throughExecEnv::destroy(), e.g. withenable_graceful_exit_check) retry local directories only; remote prefixes still pending at exit are left to the lifecycle rule. What a crashed BE leaves behind is left to a bucket lifecycle rule on<vault prefix>/spill/(plusAbortIncompleteMultipartUpload). No meta-service RPC or recycler change.BE: writing and reading
SpillFileWriter/SpillFileReaderuse the existingS3FileWriter/S3FileReader; all spill-specific logic is in the caller. This PR does not modifyS3FileWriteror add an abort interface.spill_storage_limit_bytes(new, mutable, 0 = no byte limit) caps every spill store separately: for each local path the limit is the smaller of it andspill_storage_limit(which still applies), and it is the only limit of the S3 store.S3FileWritersplits appended data into upload buffers and queues full ones without waiting, so before appending each serialized block the writer waits (checking query cancellation) while the BE's S3 upload queue holds at leastspill_s3_max_inflight_upload_bytesof buffers (get_queue_size() * s3_write_buffer_size). It is a back-pressure threshold, not a hard memory limit; it is not based on the S3 buffer tracker because that counts the partially filled buffers of every open writer and partitioned spill would deadlock on it.close()drops the current part instead of finishing the file. Failed or discarded parts drain in-flight uploads through the existing writer destructor before prefix cleanup; they are not closed just to publish a partial file. Doris does not actively abort their multipart uploads; these are reclaimed by the bucket lifecycle rule.spill_s3_read_coalesce_bytes(further capped by the query's spill buffer size).spill_remote_{read,write}_bytes/spill_remote_get_requestsremain (rates are left to Prometheus/Grafana);TQueryStatisticsgainsspill_write_bytes_to_remote_storage/spill_read_bytes_from_remote_storage.SpillFile, not per block or S3 UploadPart: successful create/close/cleanup at INFO and failures at WARNING, with query ID, local/remote storage, pinned filesystem ID, local path or fulls3://bucket/prefix/...URI, bytes and completed spill-part count. Logical creation does not imply close or physical publication. Failed deletion retains each file identity across manager queue merging; retry warnings are rate-limited, and eventual success is logged for each logical file.path-labelled countersspill_file_created_total/spill_file_deleted_totalcount logical files created / successfully cleaned up. Multiple parts still count as one logical file; an unwritten file has a successful no-op cleanup. Failed delete attempts are not successful deletions, and explicit GC plus destruction cannot count twice. These process-local totals exclude native external-writer files and pre-restart residue. Remote deletion means completed-object prefix cleanup, not incomplete MPU reclamation.RuntimeStatereleases the operator local states before its object pool, which owns the profiles whose counters those states hold (an unclosed spill writer used a freed timer).FE: SHOW DATA
TReportRequest.remote_spill_bytes.ReportHandlerkeeps it in the master's memory onBackend(no edit log, like disk capacity); it is reset when a heartbeat shows the BE restarted.entire_warehousewithoutdb_names, andSHOW DATAwith no database selected) are forwarded to the master (FORWARD_NO_SYNC, asSHOW DATA SKEW), which sums the value over alive BEs, and get a__remote_spill__row (counted in the total). Database and table views, thedb_namesform and non-cloud deployments are served locally as before.show data properties("entire_warehouse"="true"), before:after (cloud mode, no
db_names):show datawith no database selected, before:after (cloud mode):
FE: audit
fe.audit.logand__internal_schema.audit_log) gainsspill_write_bytes_to_remote_storage/spill_read_bytes_from_remote_storage. The FE keeps the dispatched BE IDs across post-dispatch errors and forwarded cancellation and waits for their finalTQueryStatisticsreports before logging, with the existing bounded fallback.Known limitations / follow-up items
AbortIncompleteMultipartUploadcovering<vault prefix>/spill/, in addition to object expiration for crash residue. Ordinary object expiration and prefix deletion do not reclaim incomplete MPU parts. These parts remain billed until lifecycle cleanup and are not included in the best-effort completed-object bytes reported bySHOW DATA.SHOW DATAcan report a low value until the lifecycle rule removes them. Review threadRelease note
Cloud mode: query spill can be written to an S3 storage vault (
spill_storage_type = s3,spill_s3_storage_vault_idin be.conf);spill_storage_limit_bytescaps every spill store;SHOW DATAlists the remote spill object bytes in a__remote_spill__row included in the total; the audit log records spill bytes written to and read from object storage per query.Spill files have query-correlated lifecycle logs and per-store
spill_file_created_total/spill_file_deleted_totallogical-file counters. Spill upload byte/time statistics remain; inferred S3 write-request counts are not collected.Check List (For Author)
Test
./run-be-ut.sh --run --filter='SpillFileS3Test.*:SpillFileTest.*:S3FileWriterTest.*' -j32passed (96 tests). Covers local/remote lifecycles, logical-vs-part counts, create/close failures, idempotent cleanup, both directory-merge directions, rate-limited retry warnings and eventual success, plus the existing read/write/cancellation/accounting cases. Real provider lifecycle timing and SQL regression were not exercised by this follow-up.85adcce350b:./run-fe-ut.sh --run org.apache.doris.nereids.trees.plans.commands.ShowDataCommandTestpassed (12/12);./build.sh --fepassed with Checkstyle; BE ASAN compilation reached the final link, but./build.sh --befailed there becauseliblance_c.aandlibpaimon_c.aboth definerust_eh_personality. BE UT and SQL regression have not been rerun on this rebased head.Behavior changed:
spill_storage_type,spill_s3_storage_vault_id,spill_storage_limit_bytes,spill_s3_max_inflight_upload_bytes,spill_s3_read_coalesce_bytes); the two warehouse-levelSHOW DATAlistings are forwarded to the master in cloud mode and list a__remote_spill__row; two new audit log columns.Does this need documentation?
Check List (For Reviewer who merge this PR)
https://claude.ai/code/session_01FvMyoceJUUqSBddwautKSM