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

io.permazen.kv.raft.msg.CommitRequest Maven / Gradle / Ivy


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

package io.permazen.kv.raft.msg;

import com.google.common.base.Preconditions;

import io.permazen.kv.mvcc.Reads;
import io.permazen.util.LongEncoder;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Sent from followers to leaders to start the commit of a transaction.
 */
public class CommitRequest extends Message {

    private final long txId;
    private final long baseTerm;
    private final long baseIndex;
    private final ByteBuffer readsData;
    private final boolean readOnly;                             // derived field

    private ByteBuffer mutationData;
    private boolean mutationDataInvalid;                        // mutationData has already been grabbed

// Constructors

    /**
     * Constructor.
     *
     * @param clusterId cluster ID
     * @param senderId identity of sender
     * @param recipientId identity of recipient
     * @param term sender's current term
     * @param txId sender's transaction ID
     * @param baseTerm term of the log entry on which the transaction is based
     * @param baseIndex index of the log entry on which the transaction is based
     * @param readsData keys read during the transaction
     * @param mutationData transaction mutations, or null for none (i.e., read only transaction)
     */
    @SuppressWarnings("this-escape")
    public CommitRequest(int clusterId, String senderId, String recipientId, long term,
      long txId, long baseTerm, long baseIndex, ByteBuffer readsData, ByteBuffer mutationData) {
        super(Message.COMMIT_REQUEST_TYPE, clusterId, senderId, recipientId, term);
        this.txId = txId;
        this.baseTerm = baseTerm;
        this.baseIndex = baseIndex;
        this.readsData = readsData;
        this.mutationData = mutationData;
        this.readOnly = this.mutationData == null;
        this.checkArguments();
    }

    CommitRequest(ByteBuffer buf, int version) {
        super(Message.COMMIT_REQUEST_TYPE, buf, version);
        this.txId = LongEncoder.read(buf);
        this.baseTerm = LongEncoder.read(buf);
        this.baseIndex = LongEncoder.read(buf);
        final boolean readsOptional = version > Message.VERSION_1;
        this.readsData = !readsOptional || Message.getBoolean(buf) ? Message.getByteBuffer(buf) : null;
        this.mutationData = Message.getBoolean(buf) ? Message.getByteBuffer(buf) : null;
        this.readOnly = this.mutationData == null;
        this.checkArguments();
    }

    @Override
    void checkArguments() {
        super.checkArguments();
        Preconditions.checkArgument(this.txId != 0);
        Preconditions.checkArgument(this.baseTerm >= 0);
        Preconditions.checkArgument(this.baseIndex >= 0);
    }

// Properties

    public long getTxId() {
        return this.txId;
    }

    public long getBaseTerm() {
        return this.baseTerm;
    }

    public long getBaseIndex() {
        return this.baseIndex;
    }

    public ByteBuffer getReadsData() {
        return this.readsData != null ? this.readsData.asReadOnlyBuffer() : null;
    }

    /**
     * Determine whether this instance represents a read-only transaction.
     *
     * @return true if there is no writes data, otherwise false
     */
    public boolean isReadOnly() {
        return this.readOnly;
    }

    /**
     * Get the transaction's mutations.
     *
     * 

* This method may only be invoked once. * * @return transaction mutations, or null if transaction is read-only * @throws IllegalStateException if this method has already been invoked */ public ByteBuffer getMutationData() { Preconditions.checkState(!this.mutationDataInvalid); final ByteBuffer result = this.mutationData; this.mutationData = null; this.mutationDataInvalid = true; return result; } // Message @Override public void visit(MessageSwitch handler) { handler.caseCommitRequest(this); } @Override public void writeTo(ByteBuffer dest, int version) { Preconditions.checkState(!this.mutationDataInvalid); super.writeTo(dest, version); LongEncoder.write(dest, this.txId); LongEncoder.write(dest, this.baseTerm); LongEncoder.write(dest, this.baseIndex); if (version > Message.VERSION_1) { Message.putBoolean(dest, this.readsData != null); if (this.readsData != null) Message.putByteBuffer(dest, this.readsData); } else Message.putByteBuffer(dest, this.readsData != null ? this.readsData : this.getEmptyReadsByteBuffer()); Message.putBoolean(dest, this.mutationData != null); if (this.mutationData != null) Message.putByteBuffer(dest, this.mutationData); } @Override protected int calculateSize(int version) { Preconditions.checkState(!this.mutationDataInvalid); return super.calculateSize(version) + LongEncoder.encodeLength(this.txId) + LongEncoder.encodeLength(this.baseTerm) + LongEncoder.encodeLength(this.baseIndex) + (version > Message.VERSION_1 ? 1 + (this.readsData != null ? Message.calculateSize(this.readsData) : 0) : Message.calculateSize(this.readsData != null ? this.readsData : this.getEmptyReadsByteBuffer())) + 1 + (this.mutationData != null ? Message.calculateSize(this.mutationData) : 0); } private ByteBuffer getEmptyReadsByteBuffer() { final Reads reads = new Reads(); try (ByteArrayOutputStream output = new ByteArrayOutputStream()) { reads.serialize(output); return ByteBuffer.wrap(output.toByteArray()); } catch (IOException e) { throw new RuntimeException("unexpected exception", e); } } // Object @Override public String toString() { return this.getClass().getSimpleName() + "[\"" + this.getSenderId() + "\"->\"" + this.getRecipientId() + "\"" + ",clusterId=" + String.format("%08x", this.getClusterId()) + ",term=" + this.getTerm() + ",txId=" + this.txId + ",base=" + this.baseIndex + "t" + this.baseTerm + (this.readsData != null ? ",readsData=" + this.describe(this.readsData) : "") + (this.mutationData != null ? ",mutationData=" + this.describe(this.mutationData) : this.mutationDataInvalid ? ",mutationData=invalid" : "") + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy