All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.permazen.kv.raft.SnapshotReceive Maven / Gradle / Ivy


/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.kv.raft;

import com.google.common.base.Preconditions;
import com.google.common.collect.AbstractIterator;
import com.google.common.primitives.Bytes;

import io.permazen.kv.KeyRange;
import io.permazen.kv.mvcc.AtomicKVStore;
import io.permazen.kv.mvcc.Mutations;
import io.permazen.kv.raft.msg.InstallSnapshot;
import io.permazen.kv.util.KeyListEncoder;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.Map;

import org.dellroad.stuff.io.ByteBufferInputStream;

/**
 * Represents and in-progress snapshot installation from the follower's point of view.
 *
 * 

* Instances are not thread safe. */ class SnapshotReceive { private final AtomicKVStore kv; private final byte[] prefix; private final long snapshotTerm; private final long snapshotIndex; private final Map snapshotConfig; private long pairIndex; private byte[] previousKey; // Constructors SnapshotReceive(AtomicKVStore kv, byte[] prefix, long snapshotTerm, long snapshotIndex, Map snapshotConfig) { Preconditions.checkArgument(kv != null, "null kv"); Preconditions.checkArgument(prefix != null, "null prefix"); Preconditions.checkArgument(snapshotTerm > 0); Preconditions.checkArgument(snapshotIndex > 0); Preconditions.checkArgument(snapshotConfig != null); this.kv = kv; this.prefix = prefix; this.snapshotTerm = snapshotTerm; this.snapshotIndex = snapshotIndex; this.snapshotConfig = snapshotConfig; } // Public methods public long getSnapshotTerm() { return this.snapshotTerm; } public long getSnapshotIndex() { return this.snapshotIndex; } public Map getSnapshotConfig() { return this.snapshotConfig; } /** * Apply the next chunk of key/value pairs. * * @param buf encoded key/value pairs * @throws IllegalArgumentException if {@code buf} contains invalid data * @throws IllegalArgumentException if {@code buf} is null */ public void applyNextChunk(ByteBuffer buf) { // Sanity check Preconditions.checkArgument(buf != null, "null buf"); // Read and apply key/value pairs in a single operation final PutMutations mutations = new PutMutations(buf, this.prefix, this.previousKey); this.kv.mutate(mutations, false); assert mutations.getEndKey() != null || (this.pairIndex == 0 && mutations.getNumPuts() == 0); // Advance our installation frontier this.pairIndex += mutations.getNumPuts(); this.previousKey = mutations.getEndKey(); } public boolean matches(InstallSnapshot msg) { return this.snapshotTerm == msg.getSnapshotTerm() && this.snapshotIndex == msg.getSnapshotIndex() && this.pairIndex == msg.getPairIndex(); } // Object @Override public String toString() { return this.getClass().getSimpleName() + "[snapshotTerm=" + this.snapshotTerm + ",snapshotIndex=" + this.snapshotIndex + ",snapshotConfig=" + this.snapshotConfig + ",pairIndex=" + this.pairIndex + "]"; } // PutMutations private static class PutMutations implements Mutations { private final ByteBuffer buf; private final byte[] prefix; private final byte[] startKey; // These fields are copied back from completed PutIterators private byte[] endKey; private int numPuts; PutMutations(ByteBuffer buf, byte[] prefix, byte[] startKey) { this.buf = buf; this.prefix = prefix; this.startKey = startKey; } // Mutations @Override public Iterable getRemoveRanges() { return Collections.emptySet(); } @Override public Iterable> getPutPairs() { return () -> new PutIterator(this, this.buf.asReadOnlyBuffer(), this.prefix, this.startKey); } @Override public Iterable> getAdjustPairs() { return Collections.emptySet(); } // Iteration completion writebacks public byte[] getEndKey() { return this.endKey; } public void setEndKey(byte[] endKey) { this.endKey = endKey; } public int getNumPuts() { return this.numPuts; } public void setNumPuts(int numPuts) { this.numPuts = numPuts; } } // PutIterator private static class PutIterator extends AbstractIterator> { private final PutMutations mutations; private final ByteBuffer buf; private final ByteBufferInputStream input; private final byte[] prefix; private byte[] previousKey; private int numPuts; PutIterator(PutMutations mutations, ByteBuffer buf, byte[] prefix, byte[] startKey) { this.mutations = mutations; this.buf = buf; this.input = new ByteBufferInputStream(buf); this.prefix = prefix; this.previousKey = startKey; } @Override protected Map.Entry computeNext() { // Check if there's more data if (!this.buf.hasRemaining()) { this.mutations.setEndKey(this.previousKey); this.mutations.setNumPuts(this.numPuts); return this.endOfData(); } // Decode next key/value pair final byte[] key; final byte[] value; try { key = KeyListEncoder.read(input, this.previousKey); value = KeyListEncoder.read(input, null); } catch (IOException e) { throw new IllegalArgumentException("invalid encoded key/value data", e.getCause()); } this.previousKey = key; this.numPuts++; // Done return new AbstractMap.SimpleImmutableEntry<>(Bytes.concat(this.prefix, key), value); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy