Repository navigation
fix(store): preflight legacy graph prefixes #3330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3db1aa9
ac1dba2
70d03eb
a3cf146
d9ff79f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Store legacy GraphID preflight | ||
|
|
||
| Some older Store writes can have the reserved `FFFE` graph prefix without a | ||
| persisted graph-to-ID mapping. Allocating a new graph ID does not rewrite those | ||
| rows, so their physical presence does not imply that normal graph reads can see | ||
| them. This condition is specific to affected old write paths; it does not mean | ||
| that every 1.7 database needs migration. | ||
|
|
||
| Store checks existing graph-data column families before allocating a missing | ||
| graph ID, first opening a partition DB during metadata bootstrap, starting a | ||
| partition's Raft state machine, or activating a snapshot. | ||
| If it finds the reserved prefix, it rejects that operation and reports the | ||
| partition and column family. Metadata-only column families are excluded. The | ||
| checks use the configured column-family options from normal Store opens. They do | ||
| not assign an owner to legacy rows or change the persisted ID format. | ||
|
|
||
| The partition preflight resolves existing DB versions without creating a DB, | ||
| allocating a store location or pruning older versions. It runs before partition | ||
| task/FSM construction and Raft replay. The first native DB open also validates | ||
| every existing version not already queued for removal before constructor pruning | ||
| or cache publication. Cached sessions are reused without repeating these scans. | ||
| Bootstrap can update Store/Raft location metadata before this check; the refusal | ||
| preserves data DB versions, not every file touched by the startup process. | ||
|
|
||
| A rejected snapshot retains the active DB and its index, the incoming snapshot, | ||
| and the staged copy for diagnosis. Successful snapshots still use the existing | ||
| activation path. An incomplete selected DB version with an older valid DB is | ||
| rejected rather than silently skipping its preflight. | ||
|
|
||
| After rejection, preserve the original database, metadata and snapshot evidence | ||
| and investigate the affected write/upgrade history before restarting writes or | ||
| replay. Do not delete mappings or assign all reserved-prefix rows to one graph: | ||
| different graphs may share that prefix. This change provides detection and | ||
| refusal, not automatic migration, repair or proof of a complete 1.7 upgrade. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -101,6 +101,7 @@ | |
| import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest.Request; | ||
| import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest.ScanType; | ||
| import org.apache.hugegraph.store.grpc.query.DeDupOption; | ||
| import org.apache.hugegraph.store.meta.GraphIdManager; | ||
| import org.apache.hugegraph.store.meta.Partition; | ||
| import org.apache.hugegraph.store.meta.PartitionManager; | ||
| import org.apache.hugegraph.store.meta.asynctask.AsyncTaskState; | ||
|
|
@@ -1195,11 +1196,20 @@ public void saveSnapshot(String snapshotPath, String graph, int partId) throws | |
| public void loadSnapshot(String snapshotPath, String graph, int partId, long v1) throws | ||
| HgStoreException { | ||
| try (RocksDBSession dbSession = getSession(graph, partId)) { | ||
| dbSession.loadSnapshot(snapshotPath, v1); | ||
| dbSession.loadSnapshot(snapshotPath, v1, stagedPath -> { | ||
| try { | ||
| GraphIdManager.checkLegacyGraphIdData(stagedPath, partId); | ||
| } catch (HgStoreException e) { | ||
| throw new DBStoreException(e.getMessage(), e); | ||
| } | ||
| }); | ||
| keyCreator.clearCache(partId); | ||
| factory.destroyGraphDB(dbSession.getGraphName()); | ||
| } catch (DBStoreException e) { | ||
| throw new HgStoreException(HgStoreException.EC_RKDB_IMPORT_SNAPSHOT_FAIL, e.toString()); | ||
| HgStoreException failure = | ||
| new HgStoreException(HgStoreException.EC_RKDB_IMPORT_SNAPSHOT_FAIL, e.toString()); | ||
| failure.initCause(e); | ||
| throw failure; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1490,7 +1500,7 @@ public RocksDBSession getSession(int partId) throws HgStoreException { | |
| long version = HgStoreEngine.getInstance().getCommittedIndex(partId); | ||
| dbSession = | ||
| factory.createGraphDB(partitionManager.getDbDataPath(partId, dbName), dbName, | ||
| version); | ||
| version, path -> GraphIdManager.checkLegacyGraphIdData(path, partId)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Important: a refusal from this validator stops every partition on the Store, not only the affected one.
Catch the refusal per partition in |
||
| if (dbSession == null) { | ||
| log.info("failed to create a new graph db: {}", dbName); | ||
| throw new HgStoreException(HgStoreException.EC_RKDB_CREATE_FAIL, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <!-- | ||
| Licensed to the Apache Software Foundation (ASF) under one or more | ||
| contributor license agreements. See the NOTICE file distributed with | ||
| this work for additional information regarding copyright ownership. | ||
| The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| (the "License"); you may not use this file except in compliance with | ||
| the License. You may obtain a copy of the License at | ||
|
|
||
| http://www.apache.org/licenses/LICENSE-2.0 | ||
|
|
||
| Unless required by applicable law or agreed to in writing, software | ||
| distributed under the License is distributed on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| See the License for the specific language governing permissions and | ||
| limitations under the License. | ||
| --> | ||
|
|
||
| # RocksDB path and snapshot validation | ||
|
|
||
| `RocksDBSession.resolveExistingDBPath(dbDataPath, graphName, version)` uses the | ||
| same version-directory selection as the constructor, without opening, creating, | ||
| pruning or renaming databases. It returns `null` when the selected path has no | ||
| `CURRENT` and no existing database version is present. If an incomplete selected | ||
| version would hide another existing database, it fails without changing either | ||
| path. Directory listing or `CURRENT` inspection failures are rejected; unknown | ||
| access or I/O state is not treated as an absent database. Paths queued for removal retain the constructor's existing treatment. | ||
| The normal constructor continues to open read/write and prune old versions. | ||
|
|
||
| `RocksDBFactory.createGraphDB(path, name, version, Consumer<String> validator)` | ||
| validates existing DB versions before creating an uncached native session. It | ||
| uses the existing factory creation lock and cache: cached sessions skip validation. | ||
| Each existing candidate not queued for removal is passed to the validator through | ||
| the read-only resolver before constructor pruning or cache publication. Validation | ||
| failure leaves these DB paths intact. The original overloads keep their behavior; | ||
| this generic hook does not depend on Store graph-ID rules. | ||
|
|
||
| `loadSnapshot(snapshotPath, version, Consumer<String> validator)` first copies | ||
| and verifies the staging database with the session's configured column-family | ||
| options, then passes its path to the validator before | ||
| replacing the target version. The validator must inspect it read-only. A rejected | ||
| validation preserves active databases, target versions and staging evidence; | ||
| the exception cause is retained. The original two-argument overload remains | ||
| available and uses a no-op validator. | ||
|
|
||
| These APIs provide generic inspection hooks. They do not validate graph ownership, | ||
| migrate historical data, or replace deployment-specific recovery and upgrade checks. |
Uh oh!
There was an error while loading. Please reload this page.