Skip to content

fix(store): allocate graph id before batch writes - #3153

Merged
imbajin merged 4 commits into
apache:masterfrom
hugegraph:task/fix-store-batch-graph-id
Aug 25, 2026
Merged

fix(store): allocate graph id before batch writes#3153
imbajin merged 4 commits into
apache:masterfrom
hugegraph:task/fix-store-batch-graph-id

Conversation

@contrueCT

@contrueCT contrueCT commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Overview

Important

HStore uses a 2-byte GraphId as the graph-isolation boundary. Before this fix, a new graph whose first write used batch PUT or MERGE could encode data with the reserved missing GraphId 0xFFFE. Multiple graphs could then share the same physical RocksDB key range.

HStore GraphId isolation failure and fix

Closes #3095. This PR extracts the focused fix from hugegraph#163.

Physical key layout

┌──────────────────┬──────────────────────┬──────────────────┐
│ GraphId · 2 bytes │ Logical key          │ Key code · 2 bytes │
└──────────────────┴──────────────────────┴──────────────────┘

The GraphId prefix is what keeps data from different graphs in separate physical ranges:

Graph Expected physical key Buggy first batch write
Graph A 0001 │ shared-key │ code FFFE │ shared-key │ code
Graph B 0002 │ shared-key │ code FFFE │ shared-key │ code

65534 (0xFFFE) is reserved for a missing GraphId mapping. The old batch path called getKey(), which returned this sentinel instead of allocating an ID.

Trigger conditions

The collision requires all three conditions:

1. New graph 2. No mapping 3. First write is batched
The graph has not written data in this partition No GraphId exists for the graph in the target partition The first operation enters batch PUT or MERGE

Once multiple graphs meet these conditions in the same partition, they share the FFFE prefix.

Data impact

Operation Result when prefixes collide
PUT The same logical key can overwrite another graph's value
MERGE Counters from different graphs can be combined
TRUNCATE Deleting the shared physical range can remove another graph's data

Note

RocksDB accepts these keys as valid, so the corruption is silent: requests can succeed without exceptions, failed health checks, or Raft errors.

Why this can remain hidden in a long-running deployment
  • Normal single-write paths allocate a GraphId before encoding the key.
  • Long-running graphs usually already have a persisted mapping and never re-enter the first-write state.
  • The failure needs multiple new graphs in the same partition; PUT/MERGE collisions are most visible when their logical keys overlap.
  • Graph truncate is uncommon, and the storage layer cannot distinguish an unintended shared prefix from a valid key range.

Fix

Before After
Batch PUT/MERGE used getKey() Batch PUT/MERGE uses getKeyOrCreate()
Missing mapping encoded as FFFE A real GraphId is allocated before key encoding
Truncate could race with in-flight encoded keys A graph/partition lifecycle lock fences transaction commit or rollback from truncate
Batch failures could retain transaction resources All three batch call sites roll back and release the session and lifecycle lock

The first-allocation path also checks whether the vertex table exists before scanning it. This avoids upgrading the column-family read lock held by the prepared batch into a write path that creates the missing table.

Regression coverage

BatchGraphIsolationTest exercises the real BusinessHandler.doBatch() and RocksDB path:

  • distinct PUT values for the same logical key in two graphs;
  • target-graph deletion and neighboring-graph preservation after truncate;
  • isolated MERGE counters;
  • first batch write on an empty partition without creating the vertex table;
  • rollback of failed transfer batches;
  • truncate waiting for an in-flight batch transaction.
mvn test -pl hugegraph-store/hg-store-test -am \
    -P store-core-test -Djacoco.skip=true -ntp

Scope

This focused change keeps the physical-key format, public APIs, dependencies, configuration, and CI workflow unchanged. It does not repair already affected data or include broader GraphId lifecycle, ID-reuse, or historical-data migration work.

PR checklist

  • Regression coverage added and verified
  • No dependency or configuration changes
  • No public API changes
  • Documentation update not required

@contrueCT
contrueCT marked this pull request as ready for review August 25, 2026 08:38
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working store Store module tests Add or improve test cases labels Aug 25, 2026

@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: yes. Summary: The new batch graph-ID path can leave RocksDB transaction locks held on allocation errors, persist IDs for batches that never commit, and reuse an ID while an in-flight batch still owns encoded keys. The added regression test also exercises an invalid partition instead of the empty-table transaction path. Evidence: exact-head static review; mvn test -pl hugegraph-store/hg-store-test -am -P store-core-test -Djacoco.skip=true -ntp passed 3 tests locally; latest-head CI completed, with Codecov upload 429 treated as non-blocking.

@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 82 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.41%. Comparing base (c9a646d) to head (a0c34a7).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
.../hugegraph/store/business/BusinessHandlerImpl.java 0.00% 47 Missing ⚠️
...ache/hugegraph/store/business/DataManagerImpl.java 0.00% 12 Missing ⚠️
...che/hugegraph/store/business/DefaultDataMover.java 0.00% 12 Missing ⚠️
...ache/hugegraph/store/business/BusinessHandler.java 0.00% 6 Missing ⚠️
...rg/apache/hugegraph/store/meta/GraphIdManager.java 0.00% 5 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3153      +/-   ##
============================================
- Coverage     39.23%   37.41%   -1.82%     
- Complexity      264     6447    +6183     
============================================
  Files           771      800      +29     
  Lines         65938    68653    +2715     
  Branches       8759     9106     +347     
============================================
- Hits          25872    25688     -184     
- Misses        37310    39951    +2641     
- Partials       2756     3014     +258     

☔ 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.

- assert the truncated graph no longer returns data
- retain the neighboring graph isolation assertion
- prevent false-positive truncate regression coverage

@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.

TODO: check the diff carefully (inconsistent)

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Aug 25, 2026
@github-project-automation github-project-automation Bot moved this from In progress to In review in HugeGraph PD-Store Tasks Aug 25, 2026
@imbajin
imbajin merged commit 4186a57 into apache:master Aug 25, 2026
17 of 19 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in HugeGraph PD-Store Tasks Aug 25, 2026
@imbajin
imbajin deleted the task/fix-store-batch-graph-id branch August 26, 2026 06:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files. store Store module tests Add or improve test cases

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug] GraphSpace, graph and store isolation do not work with HStore in HugeGraph 1.7.0

2 participants