Skip to content

Commit 3e74390

Browse files
zeebe-bors-cloud[bot]romansmirnov
andauthored
merge: #8239
8239: [Backport stable/1.2] fix(snapshot): create snapshot exporter position is -1 r=romansmirnov a=romansmirnov ## Description backports #8176 ## Related issues <!-- Which issues are closed by this PR or are related --> relates to #7978 Co-authored-by: Roman <roman.smirnov@camunda.com>
2 parents 5fd6471 + f10e864 commit 3e74390

4 files changed

Lines changed: 323 additions & 49 deletions

File tree

atomix/cluster/src/test/java/io/atomix/raft/RaftFailOverTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,30 @@ public void shouldTruncateLogOnNewerSnapshotEvenAfterRestart() throws Throwable
411411
assertThat(entries.get(0).index()).isEqualTo(66);
412412
}
413413

414+
@Test
415+
public void shouldNotReplicateSnapshotWhenIndexIsZero() throws Exception {
416+
// given
417+
final var follower = raftRule.shutdownFollower();
418+
419+
raftRule.appendEntries(100);
420+
raftRule.doSnapshot(0);
421+
422+
// expect
423+
final var leaderSnapshot = raftRule.getSnapshotFromLeader();
424+
assertThat(leaderSnapshot.getIndex()).isEqualTo(0);
425+
426+
// when
427+
raftRule.joinCluster(follower);
428+
429+
// then
430+
final var memberLogs = raftRule.getMemberLogs();
431+
assertMemberLogs(memberLogs);
432+
433+
// snapshot is not replicated to follower
434+
final var followerSnapshotStore = raftRule.getPersistedSnapshotStore(follower);
435+
assertThat(followerSnapshotStore.getLatestSnapshot()).isNotPresent();
436+
}
437+
414438
private void assertMemberLogs(final Map<String, List<IndexedRaftLogEntry>> memberLog) {
415439
final var members = memberLog.keySet();
416440
final var iterator = members.iterator();

broker/src/main/java/io/camunda/zeebe/broker/system/partitions/impl/StateControllerImpl.java

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import io.camunda.zeebe.db.ZeebeDbFactory;
1414
import io.camunda.zeebe.logstreams.impl.Loggers;
1515
import io.camunda.zeebe.snapshots.ConstructableSnapshotStore;
16+
import io.camunda.zeebe.snapshots.PersistedSnapshot;
1617
import io.camunda.zeebe.snapshots.TransientSnapshot;
1718
import io.camunda.zeebe.util.FileUtil;
1819
import io.camunda.zeebe.util.sched.ConcurrencyControl;
@@ -141,26 +142,46 @@ private void takeTransientSnapshotInternal(
141142
return;
142143
}
143144

144-
final long exportedPosition = exporterPositionSupplier.applyAsLong(db);
145-
final long snapshotPosition =
146-
determineSnapshotPosition(lowerBoundSnapshotPosition, exportedPosition);
147-
final var optionalIndexed = entrySupplier.getPreviousIndexedEntry(snapshotPosition);
148-
if (optionalIndexed.isEmpty()) {
149-
future.completeExceptionally(
150-
new IllegalStateException(
151-
String.format(
152-
"Failed to take snapshot. Expected to find an indexed entry for determined snapshot position %d (processedPosition = %d, exportedPosition=%d), but found no matching indexed entry which contains this position.",
153-
snapshotPosition, lowerBoundSnapshotPosition, exportedPosition)));
154-
return;
145+
long index = 0;
146+
long term = 0;
147+
long exportedPosition = exporterPositionSupplier.applyAsLong(db);
148+
149+
if (exportedPosition != -1) {
150+
151+
final long snapshotPosition =
152+
determineSnapshotPosition(lowerBoundSnapshotPosition, exportedPosition);
153+
final var optionalIndexed = entrySupplier.getPreviousIndexedEntry(snapshotPosition);
154+
155+
if (optionalIndexed.isEmpty()) {
156+
future.completeExceptionally(
157+
new IllegalStateException(
158+
String.format(
159+
"Failed to take snapshot. Expected to find an indexed entry for determined snapshot position %d (processedPosition = %d, exportedPosition=%d), but found no matching indexed entry which contains this position.",
160+
snapshotPosition, lowerBoundSnapshotPosition, exportedPosition)));
161+
return;
162+
}
163+
164+
final var snapshotIndexedEntry = optionalIndexed.get();
165+
index = snapshotIndexedEntry.index();
166+
term = snapshotIndexedEntry.term();
167+
} else {
168+
final Optional<PersistedSnapshot> latestSnapshot =
169+
constructableSnapshotStore.getLatestSnapshot();
170+
exportedPosition = 0;
171+
172+
if (latestSnapshot.isPresent()) {
173+
// re-use index and term from the latest snapshot
174+
// to ensure that the records from there are not
175+
// compacted until they get exported
176+
final PersistedSnapshot persistedSnapshot = latestSnapshot.get();
177+
index = persistedSnapshot.getIndex();
178+
term = persistedSnapshot.getTerm();
179+
} // otherwise index/term remains 0
155180
}
156181

157-
final var snapshotIndexedEntry = optionalIndexed.get();
158-
final Optional<TransientSnapshot> transientSnapshot =
182+
final var transientSnapshot =
159183
constructableSnapshotStore.newTransientSnapshot(
160-
snapshotIndexedEntry.index(),
161-
snapshotIndexedEntry.term(),
162-
lowerBoundSnapshotPosition,
163-
exportedPosition);
184+
index, term, lowerBoundSnapshotPosition, exportedPosition);
164185

165186
// Now takeSnapshot result can be either true, false or error.
166187
transientSnapshot.ifPresentOrElse(

broker/src/test/java/io/camunda/zeebe/broker/system/partitions/impl/StateControllerImplTest.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,49 @@ public void shouldCloseDbOnlyAfterTakingSnapshot() {
310310
assertThat(snapshotTaken.join()).isNotEmpty();
311311
}
312312

313+
@Test
314+
public void shouldSetExporterPositionToZero() {
315+
// given
316+
snapshotController.recover().join();
317+
318+
exporterPosition.set(-1L);
319+
final long snapshotPosition = 5;
320+
321+
// when
322+
final var transientSnapshot = snapshotController.takeTransientSnapshot(snapshotPosition).join();
323+
324+
// then
325+
final var snapshot = transientSnapshot.get().snapshotId();
326+
assertThat(snapshot.getIndex()).isEqualTo(0);
327+
assertThat(snapshot.getTerm()).isEqualTo(0);
328+
assertThat(snapshot.getProcessedPosition()).isEqualTo(snapshotPosition);
329+
assertThat(snapshot.getExportedPosition()).isEqualTo(0);
330+
}
331+
332+
@Test
333+
public void shouldKeepIndexAndTerm() {
334+
// given
335+
snapshotController.recover().join();
336+
337+
final long snapshotPosition = 5;
338+
exporterPosition.set(4L);
339+
takeSnapshot(snapshotPosition);
340+
341+
final var latestSnapshot = store.getLatestSnapshot().get();
342+
343+
exporterPosition.set(-1L);
344+
345+
// when
346+
final var transientSnapshot = snapshotController.takeTransientSnapshot(snapshotPosition).join();
347+
348+
// then
349+
final var snapshot = transientSnapshot.get().snapshotId();
350+
assertThat(snapshot.getIndex()).isEqualTo(latestSnapshot.getIndex());
351+
assertThat(snapshot.getTerm()).isEqualTo(latestSnapshot.getTerm());
352+
assertThat(snapshot.getProcessedPosition()).isEqualTo(snapshotPosition);
353+
assertThat(snapshot.getExportedPosition()).isEqualTo(0);
354+
}
355+
313356
private File takeSnapshot(final long position) {
314357
final var snapshot = snapshotController.takeTransientSnapshot(position).join().orElseThrow();
315358
return snapshot.persist().join().getPath().toFile();

0 commit comments

Comments
 (0)