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

com.aerospike.client.command.BatchExecutor 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.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.cluster.Cluster;
import com.aerospike.client.command.Batch.BatchCommand;
import com.aerospike.client.policy.BatchPolicy;

public final class BatchExecutor {

	public static void execute(Cluster cluster, BatchPolicy policy, BatchCommand[] commands, BatchStatus status) {
		if (policy.maxConcurrentThreads == 1 || commands.length <= 1) {
			// Run batch requests sequentially in same thread.
			for (BatchCommand command : commands) {
				try {
					command.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 (! command.splitRetry) {
						command.setInDoubt(ae.getInDoubt());
					}
					status.setException(ae);

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

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

					if (!policy.respondAllKeys) {
						throw e;
					}
				}
			}
			status.checkException();
			return;
		}

		// Run batch requests in parallel in separate threads.
		BatchExecutor executor = new BatchExecutor(cluster, policy, commands, status);
		executor.execute();
	}

	private final BatchStatus status;
	private final ExecutorService threadPool;
	private final AtomicBoolean done;
	private final AtomicInteger completedCount;
	private final BatchCommand[] commands;
	private final int maxConcurrentThreads;
	private boolean completed;

	private BatchExecutor(Cluster cluster, BatchPolicy policy, BatchCommand[] commands, BatchStatus status) {
		this.commands = commands;
		this.status = status;
		this.threadPool = cluster.getThreadPool();
		this.done = new AtomicBoolean();
		this.completedCount = new AtomicInteger();
		this.maxConcurrentThreads = (policy.maxConcurrentThreads == 0 || policy.maxConcurrentThreads >= commands.length)?
									commands.length : policy.maxConcurrentThreads;
	}

	void execute() {
		// Start threads.
		for (int i = 0; i < maxConcurrentThreads; i++) {
			BatchCommand cmd = commands[i];
			cmd.parent = this;
			threadPool.execute(cmd);
		}

		// Multiple threads write to the batch record array/list, so one might think that memory barriers
		// are needed. That should not be necessary because of this synchronized waitTillComplete().
		waitTillComplete();

		// Throw an exception if an error occurred.
		status.checkException();
	}

	void onComplete() {
		int finished = completedCount.incrementAndGet();

		if (finished < commands.length) {
			int nextThread = finished + maxConcurrentThreads - 1;

			// Determine if a new thread needs to be started.
			if (nextThread < commands.length && ! done.get()) {
				// Start new thread.
				BatchCommand cmd = commands[nextThread];
				cmd.parent = this;
				threadPool.execute(cmd);
			}
		}
		else {
			// Ensure executor succeeds or fails exactly once.
			if (done.compareAndSet(false, true)) {
				notifyCompleted();
			}
		}
	}

	boolean isDone() {
		return done.get();
	}

	private synchronized void waitTillComplete() {
		while (! completed) {
			try {
				super.wait();
			}
			catch (InterruptedException ie) {
			}
		}
	}

	private synchronized void notifyCompleted() {
		completed = true;
		super.notify();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy