Repository navigation
Conversation
|
run buildall |
|
/review |
|
Codex automated review failed and did not complete. Error: Your access token could not be refreshed because your refresh token was already used. Please log out and sign in again. This credential version is excluded from future reviews. Replace its refresh token to restore eligibility. Please trigger /review again; another configured account may be available. |
|
run external |
### What problem does this PR solve? Problem Summary: Batch finalization constructs an empty deque inside a noexcept cleanup helper. Its allocator can reject the request and terminate the backend. Move the encoded metadata out and destroy the scratch state directly. Restrict the remaining swap-based cleanup to vectors. If registering a key fails after appending its storage, remove the unindexed entry before row rollback. ### Release note Prevent backend termination when memory limits are reached during Variant batch finalization. ### Check List (For Author) - Test: ASAN standalone deque allocation rejection probe and clang-format 16. Full BE unit validation started separately. - Behavior changed: Yes. Variant cleanup no longer allocates, and failed key registration rolls back the unindexed entry. - Does this need documentation: No
### What problem does this PR solve? Problem Summary: Reject allocations after the output has been encoded and check that finalization still returns the expected Variant. Reject key-buffer and key-map allocations and check that registration can be retried without leaving an unindexed key. ### Release note None ### Check List (For Author) - Test: clang-format 16 and git diff --check. Full BE unit validation started separately. - Behavior changed: No - Does this need documentation: No
### What problem does this PR solve? Problem Summary: Path append treated allocation failures as scalar conversion failures and retried through JSONB promotion. A failure after appending a nested value could send a partially appended nullable column into promotion and abort. Propagate memory errors and append the null map outside the conversion fallback handler. ### Release note Prevent VARIANT path writes from masking allocation failures or aborting during JSONB fallback. ### Check List (For Author) - Test: Adapted release-base ASAN fault injection reproduced masked success and abort before the fix. The adapted fix passed 61 related BE unit tests. Master clang-tidy and build hygiene passed. Master ASAN configuration is blocked by missing BSON and MongoDB C driver dependencies. - Behavior changed: Yes. Memory failures return their original error without type promotion. - Does this need documentation: No
### What problem does this PR solve? Problem Summary: Inject memory exceptions before appending the value and before appending the null map. Verify that both return the original memory error and perform no JSONB promotion. ### Release note None ### Check List (For Author) - Test: 61 related ASAN BE unit tests passed with the equivalent release-base adaptation. Master clang-format 16 and build hygiene passed. - Behavior changed: No - Does this need documentation: No
### What problem does this PR solve? Problem Summary: Verify at compile time that the vector used by noexcept batch cleanup has a noexcept default constructor and member swap. Future container or allocator changes that break either guarantee now fail compilation. ### Release note None ### Check List (For Author) - Test: The equivalent helper compiled in the existing release ASAN build and its scratch release fault-injection test passed. Master clang-format 16 and repository clang-tidy passed. Full master ASAN remains blocked by missing BSON and MongoDB C driver dependencies. - Behavior changed: No - Does this need documentation: No
…-safe ### Background VariantBatchBuilder collects one encoding unit in a VariantMetadataBuilder. The builder interns every object key once, maps its bytes to a temporary key id while rows are being collected, and seal() then sorts the keys, fixes the temporary-to-final id mapping and encodes the dictionary. Aborting a row removes the keys that row added. ### What problem does this PR solve? Issue Number: None Related PR: apache#70481 (fixes the cleanup path this change builds on) PR Type: Refactor Problem Summary: register_key() appended a PaddedPODArray key to a std::deque and then inserted its map entry inside a try/catch that popped the key again. The rollback was correct, but it coupled the two containers through exceptions, and the deque's default constructor can allocate in the noexcept cleanup path. Store each key in a DorisVector<char> owned by a DorisVector, and register in two phases: copy the bytes and reserve key-vector capacity first, publish the map entry second, and move the byte buffer into the vector last. That move cannot allocate or throw, so the catch block and the deque are both gone. The encoded metadata, dictionary ordering, temporary/final id mapping and row-abort semantics are unchanged. ### Details - **Moves vs. logic changes:** Only the in-memory key storage and the registration order change. The V2 encoding, unsigned-UTF-8 sort, duplicate handling, UTF-8 validation, id remapping and abort semantics are untouched. - **Behavior change:** None. - **Compatibility:** Existing segments and readers are unaffected because the encoded dictionary stays byte-for-byte identical. - **Performance impact:** The existing million-field ASAN unit test dropped from 22.9 s to 20.9 s on the same machine. The key vector grows geometrically; reserving size+1 on every registration would make std::vector reallocate per key. - **Rollback:** Revert this commit to restore the deque plus try/catch implementation. ### Testing - `./run-be-ut.sh --run --filter="VariantBatchBuilderTest.*:VariantMetadataBuilderTest.*"` (ASAN): 49 tests passed, including new empty-key, key-address-stability, abort and injected-OOM cases. - `./run-be-ut.sh --run --filter="VariantBatchBuilderTest.*:VariantBatchBuilderAddValueTest.*:VariantMetadataBuilderTest.*:VariantPathBuilderTest.*:VariantCanonicalTest.*:VariantGoldenVectorTest.*:VariantFieldTest.*:ColumnVariantV2Test.*"`: 175 tests passed. - `build-support/check-format.sh` and `build-support/check-build-hygiene.sh` passed. - Million-key registration A/B on the same ASAN build: 22.9 s before, 20.9 s after. - clang-tidy could not complete in this environment because its libstdc++ include path misses `stddef.h`; the diagnostics it did emit are on lines outside this diff. ### Release note None ### Check List (For Author) - Behavior changed: No - Does this need documentation: No - Pre-submission status: Ready
db34f22 to
de768c1
Compare
|
run buildall |
|
/review |
There was a problem hiding this comment.
Reviewed the full four-file aggregate diff at de768c1, including the stacked changes. No new actionable findings or existing blocking inline comments were identified. No additional user focus was supplied.
Critical checkpoint conclusions:
- Goal and evidence: tracked, two-phase metadata key registration and allocation-free scratch release preserve successful encoding; path allocation errors now bypass JSONB fallback. Added unit tests directly cover these mechanisms by inspection.
- Scope: changes are confined to the two Variant implementations and their unit tests; the aggregate also includes the stacked cleanup/error-propagation fixes.
- Concurrency and locks: these builders use staged, per-owner mutation with one active row. No new threads, production shared state, locks or lock-order changes.
- Lifecycle and initialization: key views survive nonthrowing buffer moves; map erasure precedes freeing key bytes. Terminal guards and finalized counters protect the reset metadata implementation. The empty-key sentinel is constexpr, with no dynamic initialization dependency.
- Configuration: no new configuration; test hooks restore existing debug-point settings.
- Compatibility: dictionary sorting, temporary/final IDs, offsets, version header and encoded value layout are preserved. No external symbol or persistent-format change requiring mixed-version handling was found.
- Thrift/protobuf: no schema, field-ID or enum changes.
- Parallel paths: direct registration, imported values, row abort and previous-object key reuse were traced; scalar, array, JSONB, ordinary/doc shredding and extracted-subcolumn writer error paths were covered.
- Conditional checks: geometric growth and empty-key branches have concrete rationale. Both memory error codes explicitly bypass scalar conversion fallback; null-map publication now occurs outside that catch.
- Test coverage: inspected empty keys, key-address stability, allocation rejection during byte copy/vector growth/map growth, abort rollback, scratch release and failed-seal terminal behavior, plus existing canonicalization, validation and large-dictionary cases. The new path injection covers MEM_ALLOC_FAILED at both sites; MEM_LIMIT_EXCEEDED was checked in source.
- Expected results: unit expectations match the inspected encoding/error flow; no regression result files changed.
- Observability: failures retain Exception/Status propagation through existing caller boundaries; no new operational metrics requirement was identified.
- Transactions and persistence: no edit-log, version-visibility, delete-bitmap, commit or failover protocol changes.
- Write atomicity and memory: owned buffers use tracked allocators. Map growth was checked against the pinned phmap v1.3.8 implementation: allocation precedes table replacement. Failed path appends can leave scratch partial, but the shredder latches failure and extracted writers propagate it to abort the enclosing write before publication.
- FE/BE variables: no new transmitted variables or sending paths.
- Performance: outer key storage grows geometrically and moves buffer owners without copying key bytes; no new per-row scan or avoidable allocation was identified. No benchmark claims were independently validated.
- Additional checks: nullable-column ownership, exact-size key-buffer consumers, stale row/scope handles, destruction order and test cleanup were rechecked without an additional issue. No new header-layer, PCH, explicit-instantiation, source-including test or libc API dependency was introduced.
Review converged after two rounds: every assigned reviewer returned NO_NEW_VALUABLE_FINDINGS, all four changed paths were covered, and all risk focuses were dismissed with concrete evidence. No unresolved candidate or static coverage gap remains. Validation was static inspection only: no builds, tests, benchmarks or executable/ABI checks were run.
Review converged after 2 round(s).
Local pipeline review — ✅ PASSschema: doris-repo-review/v1
status: PASS
pr: apache/doris#70487
commit: de768c17b5df176a2c49bd5af736e2636167ce36
base: 9e598d38789571a7fc30652aa054f035bd4f1184
reviewed_at: 2026-11-12T11:27+08:00
reviewer: eldenmoon
model: gpt-6-astra
effort: xhigh
findings: {blocker: 0, major: 0, minor: 0, nit: 0}
rounds: 1
converged: trueNotes for maintainers
Reviewed locally with the |
|
run p0 |
|
run cloud_p0 |
|
run vault_p0 |
|
run external |
|
run nonConcurrent |
|
run external |
|
run p0 |
|
run external |
|
run nonConcurrent |
|
run p0 |
|
run cloud_p0 |
|
run buildall |
|
run cloud_p0 |
Background
VariantBatchBuilder collects one encoding unit in a VariantMetadataBuilder. The builder interns
every object key once, maps its bytes to a temporary key id while rows are being collected, and
seal()then sorts the keys, fixes the temporary-to-final id mapping and encodes the dictionary.Aborting a row removes the keys that row added.
What problem does this PR solve?
Problem Summary:
register_key()appended aPaddedPODArraykey to astd::dequeand theninserted its map entry inside a
try/catchthat popped the key again. The rollback was correct,but it coupled the two containers through exceptions, and the deque's default constructor can
allocate in a
noexceptcleanup path. Store each key in aDorisVector<char>owned by aDorisVector, and register in two phases: copy the bytes and reserve key-vector capacity first,publish the map entry second, and move the byte buffer into the vector last. That move cannot
allocate or throw, so the catch block and the deque are both gone. The encoded metadata,
dictionary ordering, temporary/final id mapping and row-abort semantics are unchanged.
Details
The V2 encoding, unsigned-UTF-8 sort, duplicate handling, UTF-8 validation, id remapping and
abort semantics are untouched.
stays byte-for-byte identical.
on the same machine. The key vector grows geometrically; reserving
size + 1on everyregistration would make
std::vectorreallocate for every key.try/catchimplementation.Performance
Dedicated metadata microbenchmark, RELEASE UT build, same worktree, old = the #70481
deque implementation, new = this PR. Values below are ns per key for 16-byte keys, and
retainedis the tracked memory of one registered+sealed dictionary. 64-byte keys show thesame direction (16K keys: register 105 → 113 ns, retained 5.48 MB → 4.42 MB).
register_key()costs a few extra ns per key at batch scale (2–9% for 64–16K keys;the relative gap looks larger only at 4–16 keys, where the absolute cost is ~100 ns/key).
or better;
seal()is 25–35% faster because the key objects are contiguous.PaddedPODArraypadding or deque blocks).are the ones that matter for ingestion.
Testing
./run-be-ut.sh --run --filter="VariantBatchBuilderTest.*:VariantMetadataBuilderTest.*"(ASAN):49 tests passed, including new empty-key, key-address-stability, abort and injected-OOM cases.
./run-be-ut.sh --run --filter="VariantBatchBuilderTest.*:VariantBatchBuilderAddValueTest.*:VariantMetadataBuilderTest.*:VariantPathBuilderTest.*:VariantCanonicalTest.*:VariantGoldenVectorTest.*:VariantFieldTest.*:ColumnVariantV2Test.*":175 tests passed.
build-support/check-format.shandbuild-support/check-build-hygiene.shpassed.stddef.h; the diagnostics it did emit are on lines outside this diff.Notes for Reviewers
raw_hash_set::resize()allocates the replacementtable before it replaces
ctrl_/slots_/capacity_, andprepare_insert()only mutates thetable after that allocation succeeds. Entries move with a non-throwing transfer. The new
static_assert(std::is_nothrow_move_constructible_v<KeyMapValue>)keeps that assumptionchecked.
size + 1on every registration.std::vector::reserve()allocates exactlythe requested capacity, so that would reallocate and move every previous key on every insert.
The code grows geometrically only when the vector is full; the comment in
register_key()records why.
DorisVector<char>has a nulldata()for an empty key,while the map entry and
StringRefhashing/comparison still need a stable pointer. A singlesentinel byte represents every empty key, and
seal()skips the zero-lengthPaddedPODArray::insert.nested-column sizes before publishing) are intentionally not in this PR: [fix](variant) Preserve memory errors in batch and path builders #70481 already covers
memory-error propagation there, and those checks touch a different module.
Release note
None
Check List (For Author)