This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.0 by this push:
new a6a28781340 branch-4.0: [Fix](autoinc) Fix wrong auto inc value after
FE do checkpoint and restart #57104 (#57119)
a6a28781340 is described below
commit a6a287813402a4244afe6c0f66137dac7e813a34
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Oct 18 10:37:10 2025 +0800
branch-4.0: [Fix](autoinc) Fix wrong auto inc value after FE do checkpoint
and restart #57104 (#57119)
Cherry-picked from #57104
Co-authored-by: bobhan1 <[email protected]>
---
.../doris/catalog/AutoIncrementGenerator.java | 2 +-
.../doris/catalog/AutoIncrementGeneratorTest.java | 178 +++++++++++++++++++++
2 files changed, 179 insertions(+), 1 deletion(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/catalog/AutoIncrementGenerator.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/AutoIncrementGenerator.java
index 2b10791bda1..7d1977a796d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/catalog/AutoIncrementGenerator.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/catalog/AutoIncrementGenerator.java
@@ -56,7 +56,7 @@ public class AutoIncrementGenerator implements
GsonPostProcessable {
this.tableId = tableId;
this.columnId = columnId;
this.nextId = nextId;
- this.batchEndId = -1;
+ this.batchEndId = nextId;
}
public void setEditLog(EditLog editLog) {
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/catalog/AutoIncrementGeneratorTest.java
b/fe/fe-core/src/test/java/org/apache/doris/catalog/AutoIncrementGeneratorTest.java
new file mode 100644
index 00000000000..05ed70a3afb
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/catalog/AutoIncrementGeneratorTest.java
@@ -0,0 +1,178 @@
+// 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.
+
+package org.apache.doris.catalog;
+
+import org.apache.doris.common.Pair;
+import org.apache.doris.common.UserException;
+import org.apache.doris.persist.EditLog;
+import org.apache.doris.persist.gson.GsonUtils;
+import org.apache.doris.utframe.TestWithFeService;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+import java.io.IOException;
+
+public class AutoIncrementGeneratorTest extends TestWithFeService {
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ createDatabase("test_db");
+ createTable("CREATE TABLE test_db.test_auto_inc_table (\n"
+ + " id BIGINT NOT NULL AUTO_INCREMENT,\n"
+ + " name VARCHAR(100)\n"
+ + ") DUPLICATE KEY(id)\n"
+ + "DISTRIBUTED BY HASH(id) BUCKETS 1\n"
+ + "PROPERTIES (\"replication_num\" = \"1\");");
+ }
+
+ /**
+ * Test to reproduce the bug where AutoIncrementGenerator.gsonPostProcess
+ * sets nextId to batchEndId (-1 by default) after checkpoint and restart.
+ *
+ * Scenario:
+ * 1. Create a table with auto-increment column (batchEndId = -1 by
default)
+ * 2. Master FE does checkpoint (serializes the table to JSON)
+ * 3. Master FE restarts and deserializes the table from JSON
+ * 4. gsonPostProcess() is called on AutoIncrementGenerator, setting
nextId = batchEndId = -1
+ * 5. getAutoIncrementRange() returns id starting from -1, which is WRONG
+ */
+ @Test
+ public void testBugAfterCheckpointRestart() throws Exception {
+ // Get the table and its AutoIncrementGenerator
+ Database db =
Env.getCurrentInternalCatalog().getDbOrMetaException("test_db");
+ OlapTable table = (OlapTable)
db.getTableOrMetaException("test_auto_inc_table");
+ AutoIncrementGenerator generator = table.getAutoIncrementGenerator();
+
+ Assertions.assertNotNull(generator, "AutoIncrementGenerator should not
be null");
+
+ long initialBatchEndId = getBatchEndId(generator);
+ System.out.println("Initial batchEndId: " + initialBatchEndId);
+ Assertions.assertTrue(initialBatchEndId >= 0, "Initial batchEndId
should be non-negative");
+
+ // Step 2: Serialize the table to JSON (simulating checkpoint)
+ String tableJson = GsonUtils.GSON.toJson(table);
+ System.out.println("Serialized table to JSON");
+
+ // Step 3: Deserialize the table from JSON (simulating restart)
+ // This will trigger gsonPostProcess() which sets nextId = batchEndId
+ OlapTable deserializedTable = GsonUtils.GSON.fromJson(tableJson,
OlapTable.class);
+ AutoIncrementGenerator deserializedGenerator =
deserializedTable.getAutoIncrementGenerator();
+
+ Assertions.assertNotNull(deserializedGenerator,
+ "AutoIncrementGenerator should not be null after
deserialization");
+
+ // Step 4: Try to get auto-increment range
+ // This should trigger the bug where startId is -1
+ long columnId = getAutoIncrementColumnId(table);
+ Pair<Long, Long> range =
deserializedGenerator.getAutoIncrementRange(columnId, 10, 0);
+
+ long startId = range.first;
+ System.out.println("Start ID after deserialization: " + startId);
+
+ // BUG REPRODUCED: The startId should be >= 0, but due to
gsonPostProcess()
+ // setting nextId = batchEndId = -1, we get a negative startId
+ if (startId < 0) {
+ Assertions.fail("Bug reproduced: startId should not be negative,
but got " + startId
+ + ". This is because gsonPostProcess() set nextId to
batchEndId (-1)");
+ }
+
+ Assertions.assertTrue(startId >= 0, "Start ID should be non-negative,
but got " + startId);
+ }
+
+ /**
+ * Test the normal case without checkpoint/restart - using simple generator
+ */
+ @Test
+ public void testNormalGetAutoIncrementRange() throws UserException {
+ long dbId = 1L;
+ long tableId = 2L;
+ long columnId = 3L;
+ long initialNextId = 100L;
+
+ AutoIncrementGenerator generator = new AutoIncrementGenerator(dbId,
tableId, columnId, initialNextId);
+ EditLog mockEditLog = Mockito.mock(EditLog.class);
+ generator.setEditLog(mockEditLog);
+
+ // Get a range without checkpoint/restart
+ Pair<Long, Long> range = generator.getAutoIncrementRange(columnId, 10,
0);
+
+ Assertions.assertEquals(100L, range.first.longValue(), "Start ID
should be 100");
+ Assertions.assertEquals(10L, range.second.longValue(), "Length should
be 10");
+ }
+
+ /**
+ * Test gsonPostProcess behavior when batchEndId is already set
+ */
+ @Test
+ public void testGsonPostProcessWithValidBatchEndId() throws UserException,
IOException {
+ long dbId = 1L;
+ long tableId = 2L;
+ long columnId = 3L;
+ long initialNextId = 100L;
+
+ AutoIncrementGenerator generator = new AutoIncrementGenerator(dbId,
tableId, columnId, initialNextId);
+ EditLog mockEditLog = Mockito.mock(EditLog.class);
+ generator.setEditLog(mockEditLog);
+
+ // First, get a range to trigger batchEndId update
+ generator.getAutoIncrementRange(columnId, 10, 0);
+
+ // At this point, batchEndId should be > 0
+ long batchEndId = getBatchEndId(generator);
+ Assertions.assertTrue(batchEndId > 0, "batchEndId should be positive
after getAutoIncrementRange");
+
+ // Serialize and deserialize to simulate checkpoint/restart
+ String json = GsonUtils.GSON.toJson(generator);
+ AutoIncrementGenerator deserializedGenerator =
GsonUtils.GSON.fromJson(json, AutoIncrementGenerator.class);
+ deserializedGenerator.setEditLog(mockEditLog);
+
+ // After deserialization, gsonPostProcess sets nextId = batchEndId
+ // This should work correctly when batchEndId > 0
+ Pair<Long, Long> range =
deserializedGenerator.getAutoIncrementRange(columnId, 10, 0);
+
+ Assertions.assertTrue(range.first >= 0, "Start ID should be positive");
+ Assertions.assertEquals(batchEndId, range.first.longValue(), "Start ID
should equal previous batchEndId");
+ }
+
+ /**
+ * Helper method to access private batchEndId field via reflection
+ */
+ private long getBatchEndId(AutoIncrementGenerator generator) {
+ try {
+ java.lang.reflect.Field field =
AutoIncrementGenerator.class.getDeclaredField("batchEndId");
+ field.setAccessible(true);
+ return (long) field.get(generator);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to get batchEndId", e);
+ }
+ }
+
+ /**
+ * Helper method to get auto-increment column ID from table
+ */
+ private long getAutoIncrementColumnId(OlapTable table) {
+ for (Column column : table.getBaseSchema()) {
+ if (column.isAutoInc()) {
+ return column.getUniqueId();
+ }
+ }
+ throw new RuntimeException("No auto-increment column found in table");
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]