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

com.aerospike.client.command.Batch Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2023 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.aerospike.client.command;

import java.util.List;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.BatchRead;
import com.aerospike.client.BatchRecord;
import com.aerospike.client.Key;
import com.aerospike.client.Operation;
import com.aerospike.client.Record;
import com.aerospike.client.ResultCode;
import com.aerospike.client.cluster.Cluster;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.ReadModeSC;
import com.aerospike.client.policy.Replica;

public final class Batch {
	//-------------------------------------------------------
	// ReadList
	//-------------------------------------------------------

	public static final class ReadListCommand extends BatchCommand {
		private final List records;

		public ReadListCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy policy,
			List records,
			BatchStatus status
		) {
			super(cluster, batch, policy, status, true);
			this.records = records;
		}

		@Override
		protected void writeBuffer() {
			if (batch.node.hasBatchAny()) {
				setBatchOperate(batchPolicy, records, batch);
			}
			else {
				setBatchRead(batchPolicy, records, batch);
			}
		}

		@Override
		protected boolean parseRow() {
			skipKey(fieldCount);

			BatchRead record = records.get(batchIndex);

			if (resultCode == 0) {
				record.setRecord(parseRecord());
			}
			else {
				record.setError(resultCode, false);
				status.setRowError();
			}
			return true;
		}

		@Override
		protected BatchCommand createCommand(BatchNode batchNode) {
			return new ReadListCommand(cluster, batchNode, batchPolicy, records, status);
		}

		@Override
		protected List generateBatchNodes() {
			return BatchNodeList.generate(cluster, batchPolicy, records, sequenceAP, sequenceSC, batch, status);
		}
	}

	//-------------------------------------------------------
	// GetArray
	//-------------------------------------------------------

	public static final class GetArrayCommand extends BatchCommand {
		private final Key[] keys;
		private final String[] binNames;
		private final Operation[] ops;
		private final Record[] records;
		private final int readAttr;

		public GetArrayCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy policy,
			Key[] keys,
			String[] binNames,
			Operation[] ops,
			Record[] records,
			int readAttr,
			boolean isOperation,
			BatchStatus status
		) {
			super(cluster, batch, policy, status, isOperation);
			this.keys = keys;
			this.binNames = binNames;
			this.ops = ops;
			this.records = records;
			this.readAttr = readAttr;
		}

		@Override
		protected void writeBuffer() {
			if (batch.node.hasBatchAny()) {
				BatchAttr attr = new BatchAttr(policy, readAttr, ops);
				setBatchOperate(batchPolicy, keys, batch, binNames, ops, attr);
			}
			else {
				setBatchRead(batchPolicy, keys, batch, binNames, ops, readAttr);
			}
		}

		@Override
		protected boolean parseRow() {
			skipKey(fieldCount);

			if (resultCode == 0) {
				records[batchIndex] = parseRecord();
			}
			return true;
		}

		@Override
		protected BatchCommand createCommand(BatchNode batchNode) {
			return new GetArrayCommand(cluster, batchNode, batchPolicy, keys, binNames, ops, records, readAttr, isOperation, status);
		}

		@Override
		protected List generateBatchNodes() {
			return BatchNodeList.generate(cluster, batchPolicy, keys, sequenceAP, sequenceSC, batch, false, status);
		}
	}

	//-------------------------------------------------------
	// ExistsArray
	//-------------------------------------------------------

	public static final class ExistsArrayCommand extends BatchCommand {
		private final Key[] keys;
		private final boolean[] existsArray;

		public ExistsArrayCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy policy,
			Key[] keys,
			boolean[] existsArray,
			BatchStatus status
		) {
			super(cluster, batch, policy, status, false);
			this.keys = keys;
			this.existsArray = existsArray;
		}

		@Override
		protected void writeBuffer() {
			if (batch.node.hasBatchAny()) {
				BatchAttr attr = new BatchAttr(policy, Command.INFO1_READ | Command.INFO1_NOBINDATA);
				setBatchOperate(batchPolicy, keys, batch, null, null, attr);
			}
			else {
				setBatchRead(batchPolicy, keys, batch, null, null, Command.INFO1_READ | Command.INFO1_NOBINDATA);
			}
		}

		@Override
		protected boolean parseRow() {
			skipKey(fieldCount);

			if (opCount > 0) {
				throw new AerospikeException.Parse("Received bins that were not requested!");
			}

			existsArray[batchIndex] = resultCode == 0;
			return true;
		}

		@Override
		protected BatchCommand createCommand(BatchNode batchNode) {
			return new ExistsArrayCommand(cluster, batchNode, batchPolicy, keys, existsArray, status);
		}

		@Override
		protected List generateBatchNodes() {
			return BatchNodeList.generate(cluster, batchPolicy, keys, sequenceAP, sequenceSC, batch, false, status);
		}
	}

	//-------------------------------------------------------
	// OperateList
	//-------------------------------------------------------

	public static final class OperateListCommand extends BatchCommand {
		private final List records;

		public OperateListCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy policy,
			List records,
			BatchStatus status
		) {
			super(cluster, batch, policy, status, true);
			this.records = records;
		}

		@Override
		protected boolean isWrite() {
			// This method is only called to set inDoubt on node level errors.
			// setError() will filter out reads when setting record level inDoubt.
			return true;
		}

		@Override
		protected void writeBuffer() {
			setBatchOperate(batchPolicy, records, batch);
		}

		@Override
		protected boolean parseRow() {
			skipKey(fieldCount);

			BatchRecord record = records.get(batchIndex);

			if (resultCode == 0) {
				record.setRecord(parseRecord());
				return true;
			}

			if (resultCode == ResultCode.UDF_BAD_RESPONSE) {
				Record r = parseRecord();
				String m = r.getString("FAILURE");

				if (m != null) {
					// Need to store record because failure bin contains an error message.
					record.record = r;
					record.resultCode = resultCode;
					record.inDoubt = Command.batchInDoubt(record.hasWrite, commandSentCounter);
					status.setRowError();
					return true;
				}
			}

			record.setError(resultCode, Command.batchInDoubt(record.hasWrite, commandSentCounter));
			status.setRowError();
			return true;
		}

		@Override
		protected void setInDoubt(boolean inDoubt) {
			if (!inDoubt) {
				return;
			}

			for (int index : batch.offsets) {
				BatchRecord record = records.get(index);

				if (record.resultCode == ResultCode.NO_RESPONSE) {
					record.inDoubt = record.hasWrite;
				}
			}
		}

		@Override
		protected BatchCommand createCommand(BatchNode batchNode) {
			return new OperateListCommand(cluster, batchNode, batchPolicy, records, status);
		}

		@Override
		protected List generateBatchNodes() {
			return BatchNodeList.generate(cluster, batchPolicy, records, sequenceAP, sequenceSC, batch, status);
		}
	}

	//-------------------------------------------------------
	// OperateArray
	//-------------------------------------------------------

	public static final class OperateArrayCommand extends BatchCommand {
		private final Key[] keys;
		private final Operation[] ops;
		private final BatchRecord[] records;
		private final BatchAttr attr;

		public OperateArrayCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy batchPolicy,
			Key[] keys,
			Operation[] ops,
			BatchRecord[] records,
			BatchAttr attr,
			BatchStatus status
		) {
			super(cluster, batch, batchPolicy, status, ops != null);
 			this.keys = keys;
			this.ops = ops;
			this.records = records;
			this.attr = attr;
		}

		@Override
		protected boolean isWrite() {
			return attr.hasWrite;
		}

		@Override
		protected void writeBuffer() {
			setBatchOperate(batchPolicy, keys, batch, null, ops, attr);
		}

		@Override
		protected boolean parseRow() {
			skipKey(fieldCount);

			BatchRecord record = records[batchIndex];

			if (resultCode == 0) {
				record.setRecord(parseRecord());
			}
			else {
				record.setError(resultCode, Command.batchInDoubt(attr.hasWrite, commandSentCounter));
				status.setRowError();
			}
			return true;
		}

		@Override
		protected void setInDoubt(boolean inDoubt) {
			if (!inDoubt || !attr.hasWrite) {
				return;
			}

			for (int index : batch.offsets) {
				BatchRecord record = records[index];

				if (record.resultCode == ResultCode.NO_RESPONSE) {
					record.inDoubt = inDoubt;
				}
			}
		}

		@Override
		protected BatchCommand createCommand(BatchNode batchNode) {
			return new OperateArrayCommand(cluster, batchNode, batchPolicy, keys, ops, records, attr, status);
		}

		@Override
		protected List generateBatchNodes() {
			return BatchNodeList.generate(cluster, batchPolicy, keys, records, sequenceAP, sequenceSC, batch, attr.hasWrite, status);
		}
	}

	//-------------------------------------------------------
	// UDF
	//-------------------------------------------------------

	public static final class UDFCommand extends BatchCommand {
		private final Key[] keys;
		private final String packageName;
		private final String functionName;
		private final byte[] argBytes;
		private final BatchRecord[] records;
		private final BatchAttr attr;

		public UDFCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy batchPolicy,
			Key[] keys,
			String packageName,
			String functionName,
			byte[] argBytes,
			BatchRecord[] records,
			BatchAttr attr,
			BatchStatus status
		) {
			super(cluster, batch, batchPolicy, status, false);
 			this.keys = keys;
 			this.packageName = packageName;
 			this.functionName = functionName;
 			this.argBytes = argBytes;
			this.records = records;
			this.attr = attr;
		}

		@Override
		protected boolean isWrite() {
			return attr.hasWrite;
		}

		@Override
		protected void writeBuffer() {
			setBatchUDF(batchPolicy, keys, batch, packageName, functionName, argBytes, attr);
		}

		@Override
		protected boolean parseRow() {
			skipKey(fieldCount);

			BatchRecord record = records[batchIndex];

			if (resultCode == 0) {
				record.setRecord(parseRecord());
				return true;
			}

			if (resultCode == ResultCode.UDF_BAD_RESPONSE) {
				Record r = parseRecord();
				String m = r.getString("FAILURE");

				if (m != null) {
					// Need to store record because failure bin contains an error message.
					record.record = r;
					record.resultCode = resultCode;
					record.inDoubt = Command.batchInDoubt(attr.hasWrite, commandSentCounter);
					status.setRowError();
					return true;
				}
			}

			record.setError(resultCode, Command.batchInDoubt(attr.hasWrite, commandSentCounter));
			status.setRowError();
			return true;
		}

		@Override
		protected void setInDoubt(boolean inDoubt) {
			if (!inDoubt || !attr.hasWrite) {
				return;
			}

			for (int index : batch.offsets) {
				BatchRecord record = records[index];

				if (record.resultCode == ResultCode.NO_RESPONSE) {
					record.inDoubt = inDoubt;
				}
			}
		}

		@Override
		protected BatchCommand createCommand(BatchNode batchNode) {
			return new UDFCommand(cluster, batchNode, batchPolicy, keys, packageName, functionName, argBytes, records, attr, status);
		}

		@Override
		protected List generateBatchNodes() {
			return BatchNodeList.generate(cluster, batchPolicy, keys, records, sequenceAP, sequenceSC, batch, attr.hasWrite, status);
		}
	}

	//-------------------------------------------------------
	// Batch Base Command
	//-------------------------------------------------------

	public static abstract class BatchCommand extends MultiCommand implements Runnable {
		final BatchNode batch;
		final BatchPolicy batchPolicy;
		final BatchStatus status;
		BatchExecutor parent;
		int sequenceAP;
		int sequenceSC;
		boolean splitRetry;

		public BatchCommand(
			Cluster cluster,
			BatchNode batch,
			BatchPolicy batchPolicy,
			BatchStatus status,
			boolean isOperation
		) {
			super(cluster, batchPolicy, batch.node, isOperation);
			this.batch = batch;
			this.batchPolicy = batchPolicy;
			this.status = status;
		}

		@Override
		public void run() {
			try {
				execute();
			}
			catch (AerospikeException ae) {
				// Set error/inDoubt for keys associated this batch command when
				// the command was not retried and split. If a split retry occurred,
				// those new subcommands have already set error/inDoubt on the affected
				// subset of keys.
				if (! splitRetry) {
					setInDoubt(ae.getInDoubt());
				}
				status.setException(ae);
			}
			catch (RuntimeException re) {
				if (! splitRetry) {
					setInDoubt(true);
				}
				status.setException(re);
			}
			catch (Throwable e) {
				if (! splitRetry) {
					setInDoubt(true);
				}
				status.setException(new RuntimeException(e));
			}
			finally {
				parent.onComplete();
			}
		}

		@Override
		protected boolean prepareRetry(boolean timeout) {
			if (! ((batchPolicy.replica == Replica.SEQUENCE || batchPolicy.replica == Replica.PREFER_RACK) &&
				   (parent == null || ! parent.isDone()))) {
				// Perform regular retry to same node.
				return true;
			}
			sequenceAP++;

			if (! timeout || batchPolicy.readModeSC != ReadModeSC.LINEARIZE) {
				sequenceSC++;
			}
			return false;
		}

		@Override
		protected boolean retryBatch(
			Cluster cluster,
			int socketTimeout,
			int totalTimeout,
			long deadline,
			int iteration,
			int commandSentCounter
		) {
			// Retry requires keys for this node to be split among other nodes.
			// This is both recursive and exponential.
			List batchNodes = generateBatchNodes();

			if (batchNodes.size() == 1 && batchNodes.get(0).node == batch.node) {
				// Batch node is the same.  Go through normal retry.
				return false;
			}

			splitRetry = true;

			// Run batch requests sequentially in same thread.
			for (BatchNode batchNode : batchNodes) {
				BatchCommand command = createCommand(batchNode);
				command.parent = parent;
				command.sequenceAP = sequenceAP;
				command.sequenceSC = sequenceSC;
				command.socketTimeout = socketTimeout;
				command.totalTimeout = totalTimeout;
				command.iteration = iteration;
				command.commandSentCounter = commandSentCounter;
				command.deadline = deadline;

				try {
					command.executeCommand();
				}
				catch (AerospikeException ae) {
					if (! command.splitRetry) {
						command.setInDoubt(ae.getInDoubt());
					}
					status.setException(ae);

					if (!batchPolicy.respondAllKeys) {
						throw ae;
					}
				}
				catch (RuntimeException re) {
					if (! command.splitRetry) {
						command.setInDoubt(true);
					}
					status.setException(re);

					if (!batchPolicy.respondAllKeys) {
						throw re;
					}
				}
				catch (Throwable e) {
					if (! command.splitRetry) {
						command.setInDoubt(true);
					}
					status.setException(new RuntimeException(e));

					if (!batchPolicy.respondAllKeys) {
						throw e;
					}
				}
			}
			return true;
		}

		protected void setInDoubt(boolean inDoubt) {
			// Do nothing by default. Batch writes will override this method.
		}

		abstract BatchCommand createCommand(BatchNode batchNode);
		abstract List generateBatchNodes();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy