From 53c6d33b0c2e612aff1352a8f237ee6e83cdfe42 Mon Sep 17 00:00:00 2001
From: Valery Kharseko <vharseko@3a-systems.ru>
Date: Wed, 21 Oct 2026 07:00:50 +0000
Subject: [PATCH] [#1227] Keep the record a changelog cursor read in advance when the head log file it is positioned in is rotated (#1228)
---
opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogTest.java | 64 ++++++++++++++++++++++++++++++++
1 files changed, 64 insertions(+), 0 deletions(-)
diff --git a/opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogTest.java b/opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogTest.java
index aee0ad1..1c7a1d1 100644
--- a/opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogTest.java
+++ b/opendj-server-legacy/src/test/java/org/opends/server/replication/server/changelog/file/LogTest.java
@@ -32,6 +32,7 @@
import org.opends.server.replication.server.changelog.api.DBCursor.KeyMatchingStrategy;
import org.opends.server.replication.server.changelog.api.DBCursor.PositionStrategy;
import org.opends.server.replication.server.changelog.file.Log.LogRotationParameters;
+import org.opends.server.replication.server.changelog.file.Log.RepositionableCursor;
import org.opends.server.replication.server.changelog.file.LogFileTest.FailingStringRecordParser;
import org.opends.server.replication.server.changelog.file.Record.Mapper;
import org.opends.server.util.StaticUtils;
@@ -360,6 +361,69 @@
}
}
+ @DataProvider
+ Object[][] positionedOnTheHeadLogFile()
+ {
+ return new Object[][] {
+ // key to position to, key matching strategy, position strategy, index of the first record read
+ { "key009", EQUAL_TO_KEY, ON_MATCHING_KEY, 9 },
+ { "key009", EQUAL_TO_KEY, AFTER_MATCHING_KEY, 10 },
+ { "key009", GREATER_THAN_OR_EQUAL_TO_KEY, ON_MATCHING_KEY, 9 },
+ { "key009", GREATER_THAN_OR_EQUAL_TO_KEY, AFTER_MATCHING_KEY, 10 },
+ // key is between key009 and key010: the record before it is read in advance
+ { "key0095", LESS_THAN_OR_EQUAL_TO_KEY, ON_MATCHING_KEY, 9 },
+ // key is after the newest key: the newest record is read in advance
+ { "key0105", LESS_THAN_OR_EQUAL_TO_KEY, ON_MATCHING_KEY, 10 },
+ };
+ }
+
+ /**
+ * A cursor positioned on a key has read the record it is positioned on in advance, and is past it
+ * in the file: when the head log file it is positioned in is rotated before it reads anything, it
+ * must still read that record first (issue #1227).
+ */
+ @Test(dataProvider = "positionedOnTheHeadLogFile")
+ public void aCursorReadsTheRecordItIsPositionedOnWhenTheHeadLogFileIsRotatedBeforeItsFirstRead(String key,
+ KeyMatchingStrategy matchingStrategy, PositionStrategy positionStrategy, int firstIndex) throws Exception
+ {
+ try (Log<String, String> log = openLog(LogFileTest.RECORD_PARSER);
+ DBCursor<Record<String, String>> cursor = log.getCursor(key, matchingStrategy, positionStrategy))
+ {
+ // the head log file holds 2 records: this append rotates it
+ log.append(Record.from("key011", "value11"));
+
+ assertThatCursorCanBeFullyReadFromStart(cursor, firstIndex, 11);
+ }
+ }
+
+ /**
+ * A cursor which read the newest record is positioned again to read the records written since - the
+ * way FileReplicaDBCursor does - and reads the record it is positioned on in advance as well: when
+ * the head log file is rotated before its next read, it must still read that record first (issue
+ * #1227).
+ */
+ @Test
+ public void aCursorPositionedAgainReadsTheRecordItIsPositionedOnWhenTheHeadLogFileIsRotatedBeforeItsNextRead()
+ throws Exception
+ {
+ try (Log<String, String> log = openLog(LogFileTest.RECORD_PARSER))
+ {
+ // the head log file holds 2 records: this append rotates it, and the new one holds key011
+ log.append(Record.from("key011", "value11"));
+ try (RepositionableCursor<String, String> cursor = log.getCursor())
+ {
+ assertThatCursorCanBeFullyReadFromStart(cursor, 1, 11);
+ log.append(Record.from("key012", "value12"));
+
+ assertThat(cursor.positionTo("key011", GREATER_THAN_OR_EQUAL_TO_KEY, AFTER_MATCHING_KEY)).isTrue();
+ // the head log file holds 2 records: this append rotates it
+ log.append(Record.from("key013", "value13"));
+
+ assertThatCursorCanBeFullyRead(cursor, 12, 13);
+ }
+ }
+ }
+
@Test
public void testClear() throws Exception
{
--
Gitblit v1.10.0