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

com.aerospike.client.Txn Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2025 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;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Multi-record transaction. Each command in the transaction must use the same namespace.
 */
public final class Txn {
	/**
	 * Transaction state.
	 */
	public static enum State {
		OPEN,
		VERIFIED,
		COMMITTED,
		ABORTED;
	}

	private static AtomicLong randomState = new AtomicLong(System.nanoTime());

	private final long id;
	private final ConcurrentHashMap reads;
	private final Set writes;
	private Txn.State state;
	private String namespace;
	private int timeout;
	private int deadline;
	private boolean writeInDoubt;
	private boolean inDoubt;

	/**
	 * Create transaction, assign random transaction id and initialize reads/writes hashmaps with
	 * default capacities.
	 * 

* The default client transaction timeout is zero. This means use the server configuration * mrt-duration as the transaction timeout. The default mrt-duration is 10 seconds. */ public Txn() { id = createId(); reads = new ConcurrentHashMap<>(); writes = ConcurrentHashMap.newKeySet(); state = Txn.State.OPEN; } /** * Create transaction, assign random transaction id and initialize reads/writes hashmaps with * given capacities. *

* The default client transaction timeout is zero. This means use the server configuration * mrt-duration as the transaction timeout. The default mrt-duration is 10 seconds. * * @param readsCapacity expected number of record reads in the transaction. Minimum value is 16. * @param writesCapacity expected number of record writes in the transaction. Minimum value is 16. */ public Txn(int readsCapacity, int writesCapacity) { if (readsCapacity < 16) { readsCapacity = 16; } if (writesCapacity < 16) { writesCapacity = 16; } id = createId(); reads = new ConcurrentHashMap<>(readsCapacity); writes = ConcurrentHashMap.newKeySet(writesCapacity); state = Txn.State.OPEN; } private static long createId() { // xorshift64* doesn't generate zeroes. long oldState; long newState; do { oldState = randomState.get(); newState = oldState; newState ^= newState >>> 12; newState ^= newState << 25; newState ^= newState >>> 27; } while (!randomState.compareAndSet(oldState, newState)); return newState * 0x2545f4914f6cdd1dl; } /** * Return transaction ID. */ public long getId() { return id; } /** * Set transaction timeout in seconds. The timer starts when the transaction monitor record is * created. This occurs when the first command in the transaction is executed. If the timeout * is reached before a commit or abort is called, the server will expire and rollback the * transaction. *

* If the transaction timeout is zero, the server configuration mrt-duration is used. * The default mrt-duration is 10 seconds. */ public void setTimeout(int timeout) { this.timeout = timeout; } /** * Return transaction timeout in seconds. */ public int getTimeout() { return timeout; } /** * Verify current transactions state and namespace for a future read command. */ void prepareRead(String ns) { verifyCommand(); setNamespace(ns); } /** * Verify current transaction state and namespaces for a future batch read command. */ void prepareRead(Key[] keys) { verifyCommand(); setNamespace(keys); } /** * Verify current transaction state and namespaces for a future batch read command. */ void prepareRead(List records) { verifyCommand(); setNamespace(records); } /** * Verify that the transaction state allows future commands. */ public void verifyCommand() { if (state != Txn.State.OPEN) { throw new AerospikeException("Command not allowed in current transaction state: " + state); } } /** * Process the results of a record read. For internal use only. */ public void onRead(Key key, Long version) { if (version != null) { reads.put(key, version); } } /** * Get record version for a given key. */ public Long getReadVersion(Key key) { return reads.get(key); } /** * Get all read keys and their versions. */ public Set> getReads() { return reads.entrySet(); } /** * Process the results of a record write. For internal use only. */ public void onWrite(Key key, Long version, int resultCode) { if (version != null) { reads.put(key, version); } else { if (resultCode == ResultCode.OK) { reads.remove(key); writes.add(key); } } } /** * Add key to write hash when write command is in doubt (usually caused by timeout). */ public void onWriteInDoubt(Key key) { writeInDoubt = true; reads.remove(key); writes.add(key); } /** * Get all write keys and their versions. */ public Set getWrites() { return writes; } /** * Set transaction namespace only if doesn't already exist. * If namespace already exists, verify new namespace is the same. */ public void setNamespace(String ns) { if (namespace == null) { namespace = ns; } else if (! namespace.equals(ns)) { throw new AerospikeException("Namespace must be the same for all commands in the transaction. orig: " + namespace + " new: " + ns); } } /** * Set transaction namespaces for each key only if doesn't already exist. * If namespace already exists, verify new namespace is the same. */ private void setNamespace(Key[] keys) { for (Key key : keys) { setNamespace(key.namespace); } } /** * Set transaction namespaces for each key only if doesn't already exist. * If namespace already exists, verify new namespace is the same. */ private void setNamespace(List records) { for (BatchRead br : records) { setNamespace(br.key.namespace); } } /** * Return transaction namespace. */ public String getNamespace() { return namespace; } /** * Set transaction deadline. The deadline is a wall clock time calculated by the server from the * transaction timeout that is sent by the client when creating the transaction monitor record. * This deadline is used to avoid client/server clock skew issues. For internal use only. */ public void setDeadline(int deadline) { this.deadline = deadline; } /** * Get transaction deadline. For internal use only. */ public int getDeadline() { return deadline; } /** * Return if the transaction monitor record should be closed/deleted. For internal use only. */ public boolean closeMonitor() { return deadline != 0 && !writeInDoubt; } /** * Does transaction monitor record exist. */ public boolean monitorExists() { return deadline != 0; } /** * Set transaction state. For internal use only. */ public void setState(Txn.State state) { this.state = state; } /** * Return transaction state. */ public Txn.State getState() { return state; } /** * Set transaction inDoubt flag. For internal use only. */ public void setInDoubt(boolean inDoubt) { this.inDoubt = inDoubt; } /** * Return if transaction is inDoubt. */ public boolean getInDoubt() { return inDoubt; } /** * Clear transaction. Remove all tracked keys. */ public void clear() { namespace = null; deadline = 0; reads.clear(); writes.clear(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy