Skip to content

fix(store): stop ProjectionStage clearing dedup keys in place - #3408

Merged
imbajin merged 1 commit into
apache:masterfrom
vaidikgarg-bit:fix/projection-stage-column-mutation
Oct 26, 2026
Merged

imbajin merged 1 commit into
apache:masterfrom
vaidikgarg-bit:fix/projection-stage-column-mutation

Conversation

@vaidikgarg-bit

Copy link
Copy Markdown
Contributor

Purpose of the PR

Follow-up to #3404, from @bitflicker64's review there. MapLimitIterator keeps each emitted RocksDBSession.BackendColumn in its dedup set and passes the same object downstream. On an id-only PRIMARY_SCAN (null_property = true, no property list, condition, order-by or aggregation) with dedup_option = LIMIT_DEDUP, ProjectionStage.handle() set column.value = new byte[0] on that object. The key stored in the set changed after insertion, so it no longer equalled a later byte-equal row and the copy was emitted again. @bitflicker64's probe got 200 rows for 100 keys read twice; on master the new test fails with key 0 emitted twice.

Main Changes

  • ProjectionStage.handle(): in the BACKEND_COLUMN branch with removeAllProperty, replace the result's column with BackendColumn.of(column.name, new byte[0]) instead of clearing value on the shared object.
  • New ProjectionStageTest in hg-store-test, registered in ServerSuiteTest:
    • projection returns the same name with an empty value and leaves the input column unchanged;
    • MapLimitIterator followed by the projection on each emitted row returns each of 100 keys once.

Only a gRPC client that sets dedup_option reaches this path today, since StoreQueryParam fixes it to NONE.

Verifying these changes

  • Trivial rework / code cleanup without any test coverage. (No Need)
  • Already covered by existing tests, such as (please modify tests here).
  • Need tests and can be verified as follows:
    • mvn test -pl hugegraph-store/hg-store-test -am -P store-server-test -Dtest=ProjectionStageTest -Dsurefire.failIfNoSpecifiedTests=false: both tests fail on master (a key is emitted twice; the input column's value is cleared) and pass with this change.
    • mvn test -pl hugegraph-store/hg-store-test -am -P store-server-test: 182 tests, 0 failures.
    • mvn test -pl hugegraph-store/hg-store-test -am -P store-core-test: 141 tests, 0 failures (1 skipped, IpUtilTest, an environment assumption).
    • mvn editorconfig:check -pl hugegraph-store/hg-store-test -am passes.

Does this PR potentially affect the following parts?

Documentation Status

  • Doc - TODO: required documentation is pending; complete it before merging.
  • Doc - Done: documentation is included here or linked below.
  • Doc - No Need: no user-visible documentation is affected.

Documentation files in this PR or paired hugegraph-doc PR:

MapLimitIterator keeps each emitted RocksDBSession.BackendColumn as a key
in its dedup set and passes the same object downstream. On an id-only scan
with every property removed, ProjectionStage then set column.value to an
empty array in place, so the stored key no longer equalled a later
byte-equal row and the copy was emitted again: 200 rows for 100 keys read
twice.

- replace the projected column instead of clearing value on the shared object
- keep the dedup key MapLimitIterator holds unchanged
- cover both with ProjectionStageTest in ServerSuiteTest

Fixes apache#3407
@codecov

codecov Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 55.69%. Comparing base (c2ec570) to head (c2b6c92).
⚠️ Report is 21 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3408      +/-   ##
============================================
- Coverage     55.93%   55.69%   -0.25%     
- Complexity     2277     2423     +146     
============================================
  Files           774      775       +1     
  Lines         68534    69521     +987     
  Branches       9333     9618     +285     
============================================
+ Hits          38334    38717     +383     
- Misses        26634    27192     +558     
- Partials       3566     3612      +46     
Flag Coverage Δ *Carryforward flag
commons 96.37% <ø> (ø) Carriedforward from c2ec570
hstore 36.08% <ø> (+0.28%) ⬆️
pd 49.26% <ø> (+0.09%) ⬆️ Carriedforward from c2ec570
server_hbase 42.02% <ø> (+0.12%) ⬆️ Carriedforward from c2ec570
server_memory 54.46% <ø> (+0.13%) ⬆️ Carriedforward from c2ec570
server_rocksdb 42.45% <ø> (+0.11%) ⬆️ Carriedforward from c2ec570
store 33.08% <ø> (+1.14%) ⬆️

*This pull request uses carry forward flags. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@bitflicker64 bitflicker64 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: no. Summary: ProjectionStage.handle() replaces the result column through setColumn(BackendColumn.of(name, new byte[0])), so the key MapLimitIterator holds keeps its value and its hashCode, matching the replace pattern OlapStage uses. ProjectionStageTest is registered in ServerSuiteTest, and the pd_store / store job runs it at this head (182 tests, 0 failures).

@imbajin imbajin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: no. Summary: Replacing the projected column preserves the name/value identity retained by MapLimitIterator while keeping the serialized id-only result unchanged. The regression tests cover input preservation and interleaved projection of byte-equal duplicate rows. Evidence: Traced BackendColumn equality/hashCode, PipelineResult and AggregativeQueryObserver serialization; exact-head Store CI completed the registered server suite with 182 tests and zero failures, and HStore/cluster checks passed. The project coverage check is a non-blocking quality signal. Score: 8.7/10.

@imbajin
imbajin merged commit e497efd into apache:master Oct 26, 2026
15 of 16 checks passed
@vaidikgarg-bit
vaidikgarg-bit deleted the fix/projection-stage-column-mutation branch October 26, 2026 13:26
imbajin pushed a commit that referenced this pull request Oct 27, 2026
…#3410)

The existing tests drive ProjectionStage by hand, so they would miss a
change in how buildPlan wires the projection for a null_property request.
The new case checks that buildPlan returns a projection-only plan for that
request, feeds MapLimitIterator output through it, and checks each key
comes out once with an empty value. It fails with "key 0 emitted twice"
when the ProjectionStage fix from #3408 is reverted.
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.

[Bug] Store limit dedup emits duplicates on id-only scans because ProjectionStage mutates the stored column

3 participants