org.voltdb.client.ProcedureInvocationExtensions Maven / Gradle / Ivy
/* This file is part of VoltDB.
* Copyright (C) 2008-2018 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see .
*/
package org.voltdb.client;
import java.nio.ByteBuffer;
/**
* Helper class for managing a defined set of extensions to ProcedureInvocation
* and StoredProcedureInvocation.
*
* Extensions allow additions of flags and extra payloads to the wire protocol
* without changing the protocol version number. Unknown extensions can be ignored.
*
* Examples of possible extensions:
* - Additional timeout values
* - Additional tracing information
* - Additional context needed for better error messages.
*
* Each extension has one byte to identify it. This is followed by one byte that
* stored the size in 2^(N-1) format.
* examples: If size = 0 => payload = 0B
* If size = 1 => payload = 1B
* If size = 2 => payload = 2B
* If size = 3 => payload = 4B
* If size = 5 => payload = 16B
* If size = 10 => payload = 512B
*
* If the payload only has 300 bytes of data, it is still stored in a 512B chunk of
* the buffer using padding.
*
*/
public abstract class ProcedureInvocationExtensions {
public static final byte BATCH_TIMEOUT = 1; // batch timeout
public static final byte ALL_PARTITION = 2; // whether proc is part of run-everywhere
private static final int INTEGER_SIZE = 4;
public static byte readNextType(ByteBuffer buf) {
return buf.get();
}
public static void writeBatchTimeoutWithTypeByte(ByteBuffer buf, int timeoutValue) {
buf.put(BATCH_TIMEOUT);
writeLength(buf, INTEGER_SIZE);
buf.putInt(timeoutValue);
}
public static int readBatchTimeout(ByteBuffer buf) {
int len = readLength(buf);
if (len != INTEGER_SIZE) {
throw new IllegalStateException(
"Batch timeout extension serialization length expected to be 4");
}
int timeout = buf.getInt();
if ((timeout < 0) && (timeout != BatchTimeoutOverrideType.NO_TIMEOUT)) {
throw new IllegalStateException("Invalid timeout value deserialized: " + timeout);
}
return timeout;
}
public static void writeAllPartitionWithTypeByte(ByteBuffer buf) {
buf.put(ALL_PARTITION);
writeLength(buf, 0);
}
public static boolean readAllPartition(ByteBuffer buf) {
int len = readLength(buf);
if (len != 0) {
throw new IllegalStateException(
"All-Partition extension serialization length expected to be 0");
}
return true;
}
public static void skipUnknownExtension(ByteBuffer buf) {
int len = readLength(buf);
buf.position(buf.position() + len); // skip ahead
}
/**
* Over-clever way to store lots of lengths in a single byte
* Length must be a power of two >= 0.
*/
private static void writeLength(ByteBuffer buf, int length) {
assert(length >= 0);
assert(length == (length & (~length + 1))); // check if power of two
// shockingly fast log_2 uses intrinsics in JDK >= 1.7
byte log2size = (byte) (32 - Integer.numberOfLeadingZeros(length));
buf.put(log2size);
}
private static int readLength(ByteBuffer buf) {
byte log2size = buf.get();
if (log2size == 0) {
return 0;
}
else {
return 1 << (log2size - 1);
}
}
}