From c2d48672a66ce0438f57c9bce6fadedf60ae8187 Mon Sep 17 00:00:00 2001 From: yuluo-yx Date: Sat, 8 Aug 2026 18:04:51 +0800 Subject: [PATCH] [ISSUE #10863] fix(common): persist empty checkpoints --- .../rocketmq/common/utils/CheckpointFile.java | 11 +-- .../common/utils/CheckpointFileTest.java | 70 +++++++++++++++++++ 2 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 common/src/test/java/org/apache/rocketmq/common/utils/CheckpointFileTest.java diff --git a/common/src/main/java/org/apache/rocketmq/common/utils/CheckpointFile.java b/common/src/main/java/org/apache/rocketmq/common/utils/CheckpointFile.java index 1cb85d05eee..e1e45692506 100644 --- a/common/src/main/java/org/apache/rocketmq/common/utils/CheckpointFile.java +++ b/common/src/main/java/org/apache/rocketmq/common/utils/CheckpointFile.java @@ -24,7 +24,6 @@ import java.nio.file.Files; import java.util.ArrayList; import java.util.List; -import org.apache.commons.collections.CollectionUtils; import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.UtilAll; @@ -75,9 +74,6 @@ public String getBackFilePath() { * Write entries to file */ public void write(final List entries) throws IOException { - if (entries.isEmpty()) { - return; - } synchronized (this) { StringBuilder entryContent = new StringBuilder(); for (T entry : entries) { @@ -143,11 +139,10 @@ private List read(String filePath) throws IOException { */ public List read() throws IOException { try { - List result = this.read(this.filePath); - if (CollectionUtils.isEmpty(result)) { - result = this.read(this.getBackFilePath()); + if (new File(this.filePath).exists()) { + return this.read(this.filePath); } - return result; + return this.read(this.getBackFilePath()); } catch (IOException e) { return this.read(this.getBackFilePath()); } diff --git a/common/src/test/java/org/apache/rocketmq/common/utils/CheckpointFileTest.java b/common/src/test/java/org/apache/rocketmq/common/utils/CheckpointFileTest.java new file mode 100644 index 00000000000..cb13c180efd --- /dev/null +++ b/common/src/test/java/org/apache/rocketmq/common/utils/CheckpointFileTest.java @@ -0,0 +1,70 @@ +/* + * 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.rocketmq.common.utils; + +import java.io.File; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CheckpointFileTest { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Test + public void writeEmptyEntriesClearsPreviousCheckpoint() throws Exception { + File file = new File(temporaryFolder.getRoot(), "checkpoint"); + CheckpointFile checkpointFile = newCheckpointFile(file); + checkpointFile.write(Arrays.asList(1, 2)); + + checkpointFile.write(Collections.emptyList()); + + assertThat(checkpointFile.read()).isEmpty(); + assertThat(Files.readAllLines(file.toPath()).get(0)).isEqualTo("0"); + } + + @Test + public void readFallsBackToBackupWhenPrimaryIsCorrupt() throws Exception { + File file = new File(temporaryFolder.getRoot(), "checkpoint"); + CheckpointFile checkpointFile = newCheckpointFile(file); + checkpointFile.write(Collections.singletonList(1)); + checkpointFile.write(Collections.singletonList(2)); + Files.write(file.toPath(), Arrays.asList("1", "1", "2")); + + assertThat(checkpointFile.read()).containsExactly(1); + } + + private CheckpointFile newCheckpointFile(File file) { + return new CheckpointFile<>(file.getAbsolutePath(), new CheckpointFile.CheckpointSerializer() { + @Override + public String toLine(Integer entry) { + return entry.toString(); + } + + @Override + public Integer fromLine(String line) { + return Integer.valueOf(line); + } + }); + } +}