-
Notifications
You must be signed in to change notification settings - Fork 569
fix(server): prevent graph clear API from clearing meta table in MySQL backend storage #2888
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: release-1.5.0
Are you sure you want to change the base?
fix(server): prevent graph clear API from clearing meta table in MySQL backend storage #2888
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a bug where the graph clear API inadvertently clears the meta table in MySQL backend storage, causing backend store version inconsistencies. The fix ensures that after truncating tables, the meta table is properly reinitialized to maintain correct version information.
Key Changes:
- Added
init()call aftertruncateTables()in thetruncate()method to reinitialize the meta table
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
...erver/hugegraph-mysql/src/main/java/org/apache/hugegraph/backend/store/mysql/MysqlStore.java
Outdated
Show resolved
Hide resolved
...erver/hugegraph-mysql/src/main/java/org/apache/hugegraph/backend/store/mysql/MysqlStore.java
Outdated
Show resolved
Hide resolved
| this.checkOpened(); | ||
|
|
||
| this.truncateTables(); | ||
| this.init(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description mentions that "HugeGraph only supports hstore, rocksdb, hbase, and memory" but this fix is only applied to MySQL backend.
Questions to verify:
- Do RocksDB, HBase, and Memory backends have the same issue?
- From my quick check, HBase's
truncate()method doesn't callinit()after truncating tables - why is MySQL different? - Is there a fundamental difference in how MySQL handles the meta table vs other backends?
Recommendation:
- Document WHY MySQL requires this init() call while other backends don't
- Verify other supported backends (RocksDB, HBase) don't have similar issues
- Consider adding a comment explaining the MySQL-specific requirement:
this.truncateTables();
// MySQL-specific: Re-initialize tables to restore meta table
// Other backends handle meta table separately during truncation
this.init();There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Root Cause
The issue stems from the fact that backend stores such as MySQL, HBase, and RocksDB persist metadata (e.g., driverVersion) to the backend storage. When the truncate() operation is executed to clear all table data, this metadata is also deleted. As a result, subsequent operations fail due to missing metadata or state conflicts.
Analysis by Backend
- RocksDB:
Although affected by the same problem, itstruncate()method explicitly callsthis.init()after clearing data to reinitialize the metadata, thereby avoiding errors.
@Override
public synchronized void truncate() {
Lock writeLock = this.storeLock.writeLock();
writeLock.lock();
try {
this.checkOpened();
this.clear(false);
this.init();
// Clear write-batch
this.dbs.values().forEach(BackendSessionPool::forceResetSessions);
LOG.debug("Store truncated: {}", this.store);
} finally {
writeLock.unlock();
}
}
- HBase:
Confirmed to have the same issue as MySQL.
Action Required: A follow-up PR is needed to fix the HBase backend accordingly. - Memory / HStore:
These backends are not affected because they do not persist critical version information or similar metadata to the backend storage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Root Cause The issue stems from the fact that backend stores such as MySQL, HBase, and RocksDB persist metadata (e.g.,
driverVersion) to the backend storage. When thetruncate()operation is executed to clear all table data, this metadata is also deleted. As a result, subsequent operations fail due to missing metadata or state conflicts.Analysis by Backend
- RocksDB:
Although affected by the same problem, itstruncate()method explicitly callsthis.init()after clearing data to reinitialize the metadata, thereby avoiding errors.@Override public synchronized void truncate() { Lock writeLock = this.storeLock.writeLock(); writeLock.lock(); try { this.checkOpened(); this.clear(false); this.init(); // Clear write-batch this.dbs.values().forEach(BackendSessionPool::forceResetSessions); LOG.debug("Store truncated: {}", this.store); } finally { writeLock.unlock(); } }
- HBase:
Confirmed to have the same issue as MySQL.
Action Required: A follow-up PR is needed to fix the HBase backend accordingly.- Memory / HStore:
These backends are not affected because they do not persist critical version information or similar metadata to the backend storage.
@LYD031106 LGTM, we could add a basic test & comment for the above issue (and we could merge this PR soon)
BTW, this week we prepare to release the new version (1.7.0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have submitted a MySQL compatibility fix to the release-1.5.0 branch. Since this is a legacy compatibility patch, I will submit a separate PR to the master branch to address the HBase storage issues.
Regarding the addition of test cases: I’ve noticed that hugegraph-test currently lacks backend-specific tests for HBase and MySQL. I plan to add a minimal set of regression tests. However, I’m not very familiar with how to build such backend-specific test cases. Could you please provide some guidance or reference examples?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have submitted a MySQL compatibility fix to the
release-1.5.0branch. Since this is a legacy compatibility patch, I will submit a separate PR to the master branch to address the HBase storage issues. Regarding the addition of test cases: I’ve noticed thathugegraph-testcurrently lacks backend-specific tests forHBaseandMySQL. I plan to add a minimal set of regression tests. However, I’m not very familiar with how to build such backend-specific test cases. Could you please provide some guidance or reference examples?
refer ci steps: https://github.com/apache/incubator-hugegraph/actions/runs/18783126996/workflow?pr=2888#L49

| this.checkOpened(); | ||
|
|
||
| this.truncateTables(); | ||
| this.init(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Minor: Consider side effects of calling init()
The init() method does more than just initTables():
- It calls
checkClusterConnected() - Creates database if not exists (
sessions.createDatabase()) - Opens a new session
- Then calls
checkOpened()again
Observation:
Most of these operations are already done and may be redundant when called from truncate() (which already calls checkOpened() first).
Potential issue:
If there are any idempotency concerns with these operations being called multiple times, this could introduce subtle bugs.
Suggestion:
Review whether all operations in init() are safe to call multiple times during the store lifecycle, or extract just the table initialization logic into a separate method.
...h-server/hugegraph-test/src/main/java/org/apache/hugegraph/unit/mysql/BaseMysqlUnitTest.java
Show resolved
Hide resolved
| Session session = this.sessions.session(); | ||
| for (MysqlTable table : this.tables()) { | ||
| if (table instanceof MysqlTables.Meta || table instanceof MysqlTables.Counters){ | ||
| continue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current fix uses instanceof checks to skip specific table types during truncation. This approach has several issues:
- Tight coupling: Creates dependency on concrete implementation classes rather than using a more abstract marker (e.g., an interface like
MetaTableor a table property) - Not extensible: If new meta tables are added in the future, developers must remember to update this condition
- Violates Open/Closed Principle: The store layer shouldn't need to know about specific table implementations
| continue; | |
| // Consider adding a method to MysqlTable interface like: | |
| // public boolean isSystemTable() { return false; } | |
| // Then override in Meta and Counters tables | |
| if (table.isSystemTable()) { | |
| continue; | |
| } |
Alternatively, you could maintain a set of system tables or use table metadata to mark which tables should be preserved.
| protected void truncateTables() { | ||
| Session session = this.sessions.session(); | ||
| for (MysqlTable table : this.tables()) { | ||
| if (table instanceof MysqlTables.Meta || table instanceof MysqlTables.Counters){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR description mentions fixing Meta table truncation but doesn't explain why the Counters table should also be skipped. This needs clarification:
- Is this fixing an existing bug with Counters table? (not mentioned in issue [Bug] The backend store version is inconsistent #2209)
- What data does the Counters table hold that should survive truncation?
- Should Counters be reset during truncate but preserved during clear?
Please add a comment explaining the reasoning, or if this is a separate fix, consider splitting it into a separate PR for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, this was my misjudgment: I mistakenly thought the Counters table was a system table similar to the Meta table, so I included it in the truncation-skip logic.
| this.checkOpened(); | ||
|
|
||
| this.truncateTables(); | ||
| this.init(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this.init() after truncation appears to reinitialize the meta table. However:
- Does
init()have any side effects beyond restoring meta data? - Is this call idempotent and safe to call multiple times?
- Should this also be added to
clearTables()method for consistency?
Please verify this is the correct approach and consider adding a comment explaining why re-initialization is needed after truncate.
|
|
||
| private final BackendStoreProvider provider; | ||
|
|
||
| private final Map<HugeType, MysqlTable> tables; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Minor: Unnecessary whitespace change
This blank line removal is an unrelated formatting change. Please keep formatting changes separate from functional fixes to make code reviews easier.
|
|
||
| public class MysqlTest extends BaseMysqlUnitTest{ | ||
| @Before | ||
| public void setUp(){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The setUp() method in MysqlTest calls super.setup(), but the base class method setup() doesn't have @Before annotation. This means:
- The base class setup runs for every test in MysqlTest (due to @before here)
- But may not run correctly for other test classes extending BaseMysqlUnitTest
- The naming inconsistency (
setupvssetUp) is confusing
| public void setUp(){ | |
| // Either add @Before to the base class setup() method, or rename for consistency: | |
| @Before | |
| public void setup() { | |
| super.setup(); | |
| } |
| super.setup(); | ||
| } | ||
|
|
||
| @Test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test only verifies that the version is preserved, but doesn't verify:
- That data in non-meta tables IS actually cleared - The bug could still exist if truncate silently fails
- That the Counters table content is preserved (if that's intentional)
- That schema data is cleared - The fix might have side effects on other stores
Suggested additions:
@Test
public void testMysqlTruncateClearsDataButPreservesMeta() {
// 1. Insert some test data into graph store
// 2. Record meta version before truncate
// 3. Call truncate
// 4. Verify meta version unchanged
// 5. Verify test data was actually cleared
// 6. Verify Counters table state (if needed)
}
Purpose of the PR
Main Changes
The PR fixes the issue where the graph clear API mistakenly clears the meta table in MySQL backend storage. Since version 1.7.0, HugeGraph only supports "hstore, rocksdb, hbase, and memory" as backend storage options. Future checks will be performed to ensure that similar issues do not exist in these supported backends.
Verifying these changes
Does this PR potentially affect the following parts?
Documentation Status
Doc - TODODoc - DoneDoc - No Need