Skip to content

Conversation

@LYD031106
Copy link

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

  • 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:
    • xxx

Does this PR potentially affect the following parts?

Documentation Status

  • Doc - TODO
  • Doc - Done
  • Doc - No Need

@dosubot dosubot bot added size:XS This PR changes 0-9 lines, ignoring generated files. bug Something isn't working store Store module labels Oct 24, 2025
@imbajin imbajin requested a review from Copilot October 25, 2025 13:29
Copy link
Contributor

Copilot AI left a 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 after truncateTables() in the truncate() method to reinitialize the meta table

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

this.checkOpened();

this.truncateTables();
this.init();
Copy link
Member

Choose a reason for hiding this comment

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

⚠️ Important: Check consistency with other backend stores

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:

  1. Do RocksDB, HBase, and Memory backends have the same issue?
  2. From my quick check, HBase's truncate() method doesn't call init() after truncating tables - why is MySQL different?
  3. 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();

Copy link
Author

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, its truncate() method explicitly calls this.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.

Copy link
Member

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, its truncate() method explicitly calls this.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)

Copy link
Author

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?

Copy link
Member

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?

refer ci steps: https://github.com/apache/incubator-hugegraph/actions/runs/18783126996/workflow?pr=2888#L49
image

this.checkOpened();

this.truncateTables();
this.init();
Copy link
Member

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.

@imbajin imbajin changed the base branch from release-1.5.0 to master October 28, 2025 04:34
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Oct 28, 2025
@imbajin imbajin changed the base branch from master to release-1.5.0 October 28, 2025 04:34
@dosubot dosubot bot added size:XS This PR changes 0-9 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Oct 28, 2025
@LYD031106 LYD031106 changed the base branch from release-1.5.0 to master October 28, 2025 15:02
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Oct 28, 2025
@LYD031106 LYD031106 changed the base branch from master to release-1.5.0 October 28, 2025 15:02
@dosubot dosubot bot added size:XS This PR changes 0-9 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Oct 28, 2025
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. and removed size:XS This PR changes 0-9 lines, ignoring generated files. labels Nov 16, 2025
Session session = this.sessions.session();
for (MysqlTable table : this.tables()) {
if (table instanceof MysqlTables.Meta || table instanceof MysqlTables.Counters){
continue;
Copy link
Member

Choose a reason for hiding this comment

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

‼️ Critical: Fragile implementation with instanceof checks

The current fix uses instanceof checks to skip specific table types during truncation. This approach has several issues:

  1. Tight coupling: Creates dependency on concrete implementation classes rather than using a more abstract marker (e.g., an interface like MetaTable or a table property)
  2. Not extensible: If new meta tables are added in the future, developers must remember to update this condition
  3. Violates Open/Closed Principle: The store layer shouldn't need to know about specific table implementations
Suggested change
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){
Copy link
Member

Choose a reason for hiding this comment

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

‼️ Critical: Unclear why Counters table is preserved

The PR description mentions fixing Meta table truncation but doesn't explain why the Counters table should also be skipped. This needs clarification:

  1. Is this fixing an existing bug with Counters table? (not mentioned in issue [Bug] The backend store version is inconsistent #2209)
  2. What data does the Counters table hold that should survive truncation?
  3. 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.

Copy link
Author

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();
Copy link
Member

Choose a reason for hiding this comment

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

⚠️ Important: Verify init() is necessary after truncate

Adding this.init() after truncation appears to reinitialize the meta table. However:

  1. Does init() have any side effects beyond restoring meta data?
  2. Is this call idempotent and safe to call multiple times?
  3. 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;
Copy link
Member

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(){
Copy link
Member

Choose a reason for hiding this comment

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

⚠️ Important: Inconsistent test lifecycle annotations

The setUp() method in MysqlTest calls super.setup(), but the base class method setup() doesn't have @Before annotation. This means:

  1. The base class setup runs for every test in MysqlTest (due to @before here)
  2. But may not run correctly for other test classes extending BaseMysqlUnitTest
  3. The naming inconsistency (setup vs setUp) is confusing
Suggested change
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
Copy link
Member

Choose a reason for hiding this comment

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

⚠️ Important: Incomplete test coverage

The test only verifies that the version is preserved, but doesn't verify:

  1. That data in non-meta tables IS actually cleared - The bug could still exist if truncate silently fails
  2. That the Counters table content is preserved (if that's intentional)
  3. 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)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files. store Store module

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

[Bug] The backend store version is inconsistent

2 participants