private final int snapshotChunkSize;
private final String logName;
private ByteSource snapshotBytes;
- private int offset = INITIAL_OFFSET;
+ private long offset = INITIAL_OFFSET;
// the next snapshot chunk is sent only if the replyReceivedForOffset matches offset
- private int replyReceivedForOffset = -1;
+ private long replyReceivedForOffset = -1;
// if replyStatus is false, the previous chunk is attempted
private boolean replyStatus = false;
private int chunkIndex = FIRST_CHUNK_INDEX;
chunkIndex = FIRST_CHUNK_INDEX;
}
- int incrementOffset() {
- // if offset is -1 doesnt matter whether it was the initial value or reset, move the offset to 0 to begin with
+ private long incrementOffset() {
+ // if offset is -1 doesn't matter whether it was the initial value or reset, move the offset to 0 to begin with
if (offset == INITIAL_OFFSET) {
offset = 0;
} else {
byte[] getNextChunk() throws IOException {
// increment offset to indicate next chunk is in flight, canSendNextChunk() wont let us hit this again until,
// markSendStatus() is called with either success or failure
- int start = incrementOffset();
+ final var start = incrementOffset();
if (replyStatus || currentChunk == null) {
int size = snapshotChunkSize;
if (snapshotChunkSize > snapshotSize) {
}
currentChunk = new byte[size];
- int numRead = snapshotInputStream.read(currentChunk);
+ final var numRead = snapshotInputStream.read(currentChunk);
if (numRead != size) {
throw new IOException(String.format(
- "The # of bytes read from the input stream, %d,"
- + "does not match the expected # %d", numRead, size));
+ "The # of bytes read from the input stream, %d, does not match the expected # %d",
+ numRead, size));
}
nextChunkHashCode = Arrays.hashCode(currentChunk);
--- /dev/null
+/*
+ * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.raft.behaviors;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.Objects;
+import org.junit.Test;
+
+public class LeaderInstallSnapshotStateTest {
+ // Prime number on purpose
+ private static final int CHUNK_SIZE = 9_999_991;
+ // More than Integer.MAX_VALUE
+ private static final long SIZE = 4_294_967_294L;
+
+ @Test
+ public void testSnapshotLongerThanInteger() throws IOException {
+ try (var fts = new LeaderInstallSnapshotState(CHUNK_SIZE, "test")) {
+ fts.setSnapshotBytes(new MockByteSource(SIZE));
+
+ int chunkIndex = 0;
+ long offset = 0;
+ long expectedChunkSize = CHUNK_SIZE;
+ while (offset < SIZE) {
+ offset = offset + CHUNK_SIZE;
+ if (offset > SIZE) {
+ // We reached last chunk
+ expectedChunkSize = CHUNK_SIZE - (offset - SIZE);
+ offset = SIZE;
+ }
+ chunkIndex ++;
+ final byte[] chunk = fts.getNextChunk();
+ assertEquals("byte size not matching for chunk:", expectedChunkSize, chunk.length);
+ assertEquals("chunk index not matching", chunkIndex, fts.getChunkIndex());
+ fts.markSendStatus(true);
+ if (!fts.isLastChunk(chunkIndex)) {
+ fts.incrementChunkIndex();
+ }
+ }
+
+ assertEquals("totalChunks not matching", chunkIndex, fts.getTotalChunks());
+ }
+ }
+
+ private static final class MockByteSource extends ByteSource {
+ private final long size;
+
+ private MockByteSource(final long size) {
+ this.size = size;
+ }
+
+ @Override
+ public long size() {
+ return size;
+ }
+
+ @Override
+ public InputStream openStream() {
+ return new MockInputStream(size);
+ }
+ }
+
+ private static final class MockInputStream extends InputStream {
+ private long remaining;
+
+ MockInputStream(final long size) {
+ remaining = size;
+ }
+
+ @Override
+ public int read() {
+ if (remaining > 0) {
+ remaining--;
+ return 0;
+ }
+ return -1;
+ }
+
+ @Override
+ public int read(final byte[] bytes, final int off, final int len) {
+ Objects.checkFromIndexSize(off, len, bytes.length);
+ if (remaining <= 0) {
+ return -1;
+ }
+ final int count = len <= remaining ? len : (int) remaining;
+ Arrays.fill(bytes, off, off + count, (byte) 0);
+ remaining -= count;
+ return count;
+ }
+ }
+}