
com.aerospike.client.async.AsyncClient Maven / Gradle / Ivy
/*
* Copyright 2012-2018 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.async;
import java.io.Closeable;
import java.util.List;
import java.util.concurrent.ExecutorService;
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.AerospikeException;
import com.aerospike.client.BatchRead;
import com.aerospike.client.Bin;
import com.aerospike.client.Host;
import com.aerospike.client.Key;
import com.aerospike.client.Log;
import com.aerospike.client.Operation;
import com.aerospike.client.Record;
import com.aerospike.client.Value;
import com.aerospike.client.cluster.Cluster;
import com.aerospike.client.listener.BatchListListener;
import com.aerospike.client.listener.BatchSequenceListener;
import com.aerospike.client.listener.DeleteListener;
import com.aerospike.client.listener.ExecuteListener;
import com.aerospike.client.listener.ExistsArrayListener;
import com.aerospike.client.listener.ExistsListener;
import com.aerospike.client.listener.ExistsSequenceListener;
import com.aerospike.client.listener.RecordArrayListener;
import com.aerospike.client.listener.RecordListener;
import com.aerospike.client.listener.RecordSequenceListener;
import com.aerospike.client.listener.WriteListener;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.QueryPolicy;
import com.aerospike.client.policy.ScanPolicy;
import com.aerospike.client.policy.WritePolicy;
import com.aerospike.client.query.Statement;
import com.aerospike.client.util.Util;
/**
* THIS CLASS IS OBSOLETE.
*
* The new efficient asynchronous API is located directly in AerospikeClient.
* This class is just a thin wrapper over the new AerospikeClient asynchronous API.
* This class exists solely to provide compatibility with legacy applications.
*
* Asynchronous Aerospike client.
*
* Your application uses this class to perform asynchronous database operations
* such as writing and reading records, and selecting sets of records. Write
* operations include specialized functionality such as append/prepend and arithmetic
* addition.
*
* This client is thread-safe. One client instance should be used per cluster.
* Multiple threads should share this cluster instance.
*
* Each record may have multiple bins, unless the Aerospike server nodes are
* configured as "single-bin". In "multi-bin" mode, partial records may be
* written or read by specifying the relevant subset of bins.
*/
public class AsyncClient extends AerospikeClient implements IAsyncClient, Closeable {
//-------------------------------------------------------
// Member variables.
//-------------------------------------------------------
/**
* Default read policy that is used when asynchronous read command policy is null.
*/
public final Policy asyncReadPolicyDefault;
/**
* Default write policy that is used when asynchronous write command policy is null.
*/
public final WritePolicy asyncWritePolicyDefault;
/**
* Default scan policy that is used when asynchronous scan command policy is null.
*/
public final ScanPolicy asyncScanPolicyDefault;
/**
* Default query policy that is used when asynchronous query command policy is null.
*/
public final QueryPolicy asyncQueryPolicyDefault;
/**
* Default batch policy that is used when asynchronous batch command policy is null.
*/
public final BatchPolicy asyncBatchPolicyDefault;
private final NioEventLoops eventLoops;
private final ExecutorService taskThreadPool;
private final Throttle throttle;
private final int maxCommandsPerEventLoop;
private final boolean useListener;
//-------------------------------------------------------
// Constructors
//-------------------------------------------------------
/**
* Initialize asynchronous client.
* If the host connection succeeds, the client will:
*
* - Add host to the cluster map
* - Request host's list of other nodes in cluster
* - Add these nodes to cluster map
*
* If the connection succeeds, the client is ready to process database requests.
* If the connection fails, the cluster will remain in a disconnected state
* until the server is activated.
*
* @param hostname host name
* @param port host port
* @throws AerospikeException if host connection fails
*/
public AsyncClient(String hostname, int port) throws AerospikeException {
this(new AsyncClientPolicy(), new Host(hostname, port));
}
/**
* Initialize asynchronous client.
* The client policy is used to set defaults and size internal data structures.
* If the host connection succeeds, the client will:
*
* - Add host to the cluster map
* - Request host's list of other nodes in cluster
* - Add these nodes to cluster map
*
* If the connection succeeds, the client is ready to process database requests.
* If the connection fails and the policy's failIfNotConnected is true, a connection
* exception will be thrown. Otherwise, the cluster will remain in a disconnected state
* until the server is activated.
*
* @param policy client configuration parameters, pass in null for defaults
* @param hostname host name
* @param port host port
* @throws AerospikeException if host connection fails
*/
public AsyncClient(AsyncClientPolicy policy, String hostname, int port) throws AerospikeException {
this(policy, new Host(hostname, port));
}
/**
* Initialize asynchronous client with suitable hosts to seed the cluster map.
* The client policy is used to set defaults and size internal data structures.
* For each host connection that succeeds, the client will:
*
* - Add host to the cluster map
* - Request host's list of other nodes in cluster
* - Add these nodes to cluster map
*
* In most cases, only one host is necessary to seed the cluster. The remaining hosts
* are added as future seeds in case of a complete network failure.
*
* If one connection succeeds, the client is ready to process database requests.
* If all connections fail and the policy's failIfNotConnected is true, a connection
* exception will be thrown. Otherwise, the cluster will remain in a disconnected state
* until the server is activated.
*
* @param policy client configuration parameters, pass in null for defaults
* @param hosts array of potential hosts to seed the cluster
* @throws AerospikeException if all host connections fail
*/
public AsyncClient(AsyncClientPolicy policy, Host... hosts) throws AerospikeException {
super(policy);
if (policy == null) {
policy = new AsyncClientPolicy();
}
this.asyncReadPolicyDefault = policy.asyncReadPolicyDefault;
this.asyncWritePolicyDefault = policy.asyncWritePolicyDefault;
this.asyncScanPolicyDefault = policy.asyncScanPolicyDefault;
this.asyncQueryPolicyDefault = policy.asyncQueryPolicyDefault;
this.asyncBatchPolicyDefault = policy.asyncBatchPolicyDefault;
EventPolicy eventPolicy = new EventPolicy();
if (policy.asyncSelectorTimeout > 0) {
eventPolicy.minTimeout = policy.asyncSelectorTimeout;
}
eventLoops = new NioEventLoops(eventPolicy, policy.asyncSelectorThreads);
policy.eventLoops = eventLoops;
try {
// BLOCK mode or a task thread pool requires extra listener code that sits on top
// of the new efficient async API. Otherwise, the new efficient async API is used
// directly. All modes are still limited by ClientPolicy.maxConnsPerNode.
useListener = (policy.asyncMaxCommandAction == MaxCommandAction.BLOCK || policy.asyncTaskThreadPool != null);
taskThreadPool = policy.asyncTaskThreadPool;
if (policy.asyncMaxCommandAction == MaxCommandAction.BLOCK) {
throttle = new Throttle(policy.asyncMaxCommands);
maxCommandsPerEventLoop = policy.asyncMaxCommands / eventLoops.getSize();
}
else {
throttle = null;
maxCommandsPerEventLoop = 0;
}
super.cluster = new Cluster(policy, hosts);
}
catch (Exception e) {
eventLoops.close();
throw e;
}
}
//-------------------------------------------------------
// Destructor
//-------------------------------------------------------
/**
* Close all client connections to database server nodes.
*/
@Override
public final void close() {
super.close();
eventLoops.close();
}
//-------------------------------------------------------
// Default Policies
//-------------------------------------------------------
public final Policy getAsyncReadPolicyDefault() {
return asyncReadPolicyDefault;
}
public final WritePolicy getAsyncWritePolicyDefault() {
return asyncWritePolicyDefault;
}
public final ScanPolicy getAsyncScanPolicyDefault() {
return asyncScanPolicyDefault;
}
public final QueryPolicy getAsyncQueryPolicyDefault() {
return asyncQueryPolicyDefault;
}
public final BatchPolicy getAsyncBatchPolicyDefault() {
return asyncBatchPolicyDefault;
}
//-------------------------------------------------------
// Write Record Operations
//-------------------------------------------------------
/**
* Asynchronously write record bin(s).
* This method schedules the put command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy specifies the transaction timeout, record expiration and how the transaction is
* handled when the record already exists.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @param bins array of bin name/value pairs
* @throws AerospikeException if queue is full
*/
public final void put(final WritePolicy policy, final WriteListener listener, final Key key, final Bin... bins) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
put(findEventLoop(), new AWriteListener(listener), wp, key, bins);
}
});
}
else {
put(findEventLoop(), listener, wp, key, bins);
}
}
//-------------------------------------------------------
// String Operations
//-------------------------------------------------------
/**
* Asynchronously append bin string values to existing record bin values.
* This method schedules the append command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy specifies the transaction timeout, record expiration and how the transaction is
* handled when the record already exists.
* This call only works for string values.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @param bins array of bin name/value pairs
* @throws AerospikeException if queue is full
*/
public final void append(final WritePolicy policy, final WriteListener listener, final Key key, final Bin... bins) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
append(findEventLoop(), new AWriteListener(listener), wp, key, bins);
}
});
}
else {
append(findEventLoop(), listener, wp, key, bins);
}
}
/**
* Asynchronously prepend bin string values to existing record bin values.
* This method schedules the prepend command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy specifies the transaction timeout, record expiration and how the transaction is
* handled when the record already exists.
* This call works only for string values.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @param bins array of bin name/value pairs
* @throws AerospikeException if queue is full
*/
public final void prepend(final WritePolicy policy, final WriteListener listener, final Key key, final Bin... bins) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
prepend(findEventLoop(), new AWriteListener(listener), wp, key, bins);
}
});
}
else {
prepend(findEventLoop(), listener, wp, key, bins);
}
}
//-------------------------------------------------------
// Arithmetic Operations
//-------------------------------------------------------
/**
* Asynchronously add integer bin values to existing record bin values.
* This method schedules the add command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy specifies the transaction timeout, record expiration and how the transaction is
* handled when the record already exists.
* This call only works for integer values.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @param bins array of bin name/value pairs
* @throws AerospikeException if queue is full
*/
public final void add(final WritePolicy policy, final WriteListener listener, final Key key, final Bin... bins) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
add(findEventLoop(), new AWriteListener(listener), wp, key, bins);
}
});
}
else {
add(findEventLoop(), listener, wp, key, bins);
}
}
//-------------------------------------------------------
// Delete Operations
//-------------------------------------------------------
/**
* Asynchronously delete record for specified key.
* This method schedules the delete command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy specifies the transaction timeout.
*
* @param policy delete configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @throws AerospikeException if queue is full
*/
public final void delete(final WritePolicy policy, final DeleteListener listener, final Key key) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
delete(findEventLoop(), new ADeleteListener(listener), wp, key);
}
});
}
else {
delete(findEventLoop(), listener, wp, key);
}
}
//-------------------------------------------------------
// Touch Operations
//-------------------------------------------------------
/**
* Asynchronously create record if it does not already exist. If the record exists, the record's
* time to expiration will be reset to the policy's expiration.
*
* This method schedules the touch command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @throws AerospikeException if queue is full
*/
public final void touch(final WritePolicy policy, final WriteListener listener, final Key key) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
touch(findEventLoop(), new AWriteListener(listener), wp, key);
}
});
}
else {
touch(findEventLoop(), listener, wp, key);
}
}
//-------------------------------------------------------
// Existence-Check Operations
//-------------------------------------------------------
/**
* Asynchronously determine if a record key exists.
* This method schedules the exists command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy can be used to specify timeouts.
*
* @param policy generic configuration parameters, pass in null for defaults
* @param listener where to send results
* @param key unique record identifier
* @throws AerospikeException if queue is full
*/
public final void exists(final Policy policy, final ExistsListener listener, final Key key) throws AerospikeException {
final Policy p = (policy != null)? policy : asyncReadPolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
exists(findEventLoop(), new AExistsListener(listener), p, key);
}
});
}
else {
exists(findEventLoop(), listener, p, key);
}
}
/**
* Asynchronously check if multiple record keys exist in one batch call.
* This method schedules the exists command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in a single call.
*
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @throws AerospikeException if queue is full
*/
public final void exists(final BatchPolicy policy, final ExistsArrayListener listener, final Key[] keys) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
exists(findEventLoop(), new AExistsArrayListener(listener, commands), bp, keys);
}
});
}
else {
exists(findEventLoop(), listener, bp, keys);
}
}
/**
* Asynchronously check if multiple record keys exist in one batch call.
* This method schedules the exists command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in multiple unordered calls.
*
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @throws AerospikeException if queue is full
*/
public final void exists(final BatchPolicy policy, final ExistsSequenceListener listener, final Key[] keys) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
exists(findEventLoop(), new AExistsSequenceListener(listener, commands), bp, keys);
}
});
}
else {
exists(findEventLoop(), listener, bp, keys);
}
}
//-------------------------------------------------------
// Read Record Operations
//-------------------------------------------------------
/**
* Asynchronously read entire record for specified key.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy can be used to specify timeouts.
*
* @param policy generic configuration parameters, pass in null for defaults
* @param listener where to send results
* @param key unique record identifier
* @throws AerospikeException if queue is full
*/
public final void get(final Policy policy, final RecordListener listener, final Key key) throws AerospikeException {
final Policy p = (policy != null)? policy : asyncReadPolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
get(findEventLoop(), new ARecordListener(listener), p, key);
}
});
}
else {
get(findEventLoop(), listener, p, key);
}
}
/**
* Asynchronously read record header and bins for specified key.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy can be used to specify timeouts.
*
* @param policy generic configuration parameters, pass in null for defaults
* @param listener where to send results
* @param key unique record identifier
* @param binNames bins to retrieve
* @throws AerospikeException if queue is full
*/
public final void get(final Policy policy, final RecordListener listener, final Key key, final String... binNames) throws AerospikeException {
final Policy p = (policy != null)? policy : asyncReadPolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
get(findEventLoop(), new ARecordListener(listener), p, key, binNames);
}
});
}
else {
get(findEventLoop(), listener, p, key, binNames);
}
}
/**
* Asynchronously read record generation and expiration only for specified key. Bins are not read.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The policy can be used to specify timeouts.
*
* @param policy generic configuration parameters, pass in null for defaults
* @param listener where to send results
* @param key unique record identifier
* @throws AerospikeException if queue is full
*/
public final void getHeader(final Policy policy, final RecordListener listener, final Key key) throws AerospikeException {
final Policy p = (policy != null)? policy : asyncReadPolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
getHeader(findEventLoop(), new ARecordListener(listener), p, key);
}
});
}
else {
getHeader(findEventLoop(), listener, p, key);
}
}
//-------------------------------------------------------
// Batch Read Operations
//-------------------------------------------------------
/**
* Asynchronously read multiple records for specified batch keys in one batch call.
* This method allows different namespaces/bins to be requested for each key in the batch.
* The returned records are located in the same list.
* If the BatchRead key field is not found, the corresponding record field will be null.
*
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in a single call.
* This method requires Aerospike Server version >= 3.6.0.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param records list of unique record identifiers and the bins to retrieve.
* @throws AerospikeException if read fails
*/
public final void get(final BatchPolicy policy, final BatchListListener listener, final List records) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
get(findEventLoop(), new ABatchListListener(listener, commands), bp, records);
}
});
}
else {
get(findEventLoop(), listener, bp, records);
}
}
/**
* Asynchronously read multiple records for specified batch keys in one batch call.
* This method allows different namespaces/bins to be requested for each key in the batch.
* The returned records are located in the same list.
* If the BatchRead key field is not found, the corresponding record field will be null.
*
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in a single call.
* This method requires Aerospike Server version >= 3.6.0.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param records list of unique record identifiers and the bins to retrieve.
* @throws AerospikeException if read fails
*/
public final void get(final BatchPolicy policy, final BatchSequenceListener listener, final List records) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
get(findEventLoop(), new ABatchSequenceListener(listener, commands), bp, records);
}
});
}
else {
get(findEventLoop(), listener, bp, records);
}
}
/**
* Asynchronously read multiple records for specified keys in one batch call.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in a single call.
*
* If a key is not found, the record will be null.
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @throws AerospikeException if queue is full
*/
public final void get(final BatchPolicy policy, final RecordArrayListener listener, final Key[] keys) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
get(findEventLoop(), new ARecordArrayListener(listener, commands), bp, keys);
}
});
}
else {
get(findEventLoop(), listener, bp, keys);
}
}
/**
* Asynchronously read multiple records for specified keys in one batch call.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in multiple unordered calls.
*
* If a key is not found, the record will be null.
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @throws AerospikeException if queue is full
*/
public final void get(final BatchPolicy policy, final RecordSequenceListener listener, final Key[] keys) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
get(findEventLoop(), new ARecordSequenceListener(listener, commands), bp, keys);
}
});
}
else {
get(findEventLoop(), listener, bp, keys);
}
}
/**
* Asynchronously read multiple record headers and bins for specified keys in one batch call.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in a single call.
*
* If a key is not found, the record will be null.
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @param binNames array of bins to retrieve
* @throws AerospikeException if queue is full
*/
public final void get(final BatchPolicy policy, final RecordArrayListener listener, final Key[] keys, final String... binNames) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
get(findEventLoop(), new ARecordArrayListener(listener, commands), bp, keys, binNames);
}
});
}
else {
get(findEventLoop(), listener, bp, keys, binNames);
}
}
/**
* Asynchronously read multiple record headers and bins for specified keys in one batch call.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in multiple unordered calls.
*
* If a key is not found, the record will be null.
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @param binNames array of bins to retrieve
* @throws AerospikeException if queue is full
*/
public final void get(final BatchPolicy policy, final RecordSequenceListener listener, final Key[] keys, final String... binNames) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
get(findEventLoop(), new ARecordSequenceListener(listener, commands), bp, keys, binNames);
}
});
}
else {
get(findEventLoop(), listener, bp, keys, binNames);
}
}
/**
* Asynchronously read multiple record header data for specified keys in one batch call.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in a single call.
*
* If a key is not found, the record will be null.
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @throws AerospikeException if queue is full
*/
public final void getHeader(final BatchPolicy policy, final RecordArrayListener listener, final Key[] keys) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
getHeader(findEventLoop(), new ARecordArrayListener(listener, commands), bp, keys);
}
});
}
else {
getHeader(findEventLoop(), listener, bp, keys);
}
}
/**
* Asynchronously read multiple record header data for specified keys in one batch call.
* This method schedules the get command with a channel selector and returns.
* Another thread will process the command and send the results to the listener in multiple unordered calls.
*
* If a key is not found, the record will be null.
* The policy can be used to specify timeouts and maximum parallel commands.
*
* @param policy batch configuration parameters, pass in null for defaults
* @param listener where to send results
* @param keys array of unique record identifiers
* @throws AerospikeException if queue is full
*/
public final void getHeader(final BatchPolicy policy, final RecordSequenceListener listener, final Key[] keys) throws AerospikeException {
final BatchPolicy bp = (policy != null)? policy : asyncBatchPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
getHeader(findEventLoop(), new ARecordSequenceListener(listener, commands), bp, keys);
}
});
}
else {
getHeader(findEventLoop(), listener, bp, keys);
}
}
//-------------------------------------------------------
// Generic Database Operations
//-------------------------------------------------------
/**
* Asynchronously perform multiple read/write operations on a single key in one batch call.
* An example would be to add an integer value to an existing record and then
* read the result, all in one database call.
*
* This method schedules the operate command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* The server executes operations in the same order as the operations array. Both scalar
* bin operations (Operation) and CDT bin operations (ListOperation, MapOperation) can be
* performed in same call.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @param operations database operations to perform
* @throws AerospikeException if queue is full
*/
public final void operate(final WritePolicy policy, final RecordListener listener, final Key key, final Operation... operations) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
operate(findEventLoop(), new ARecordListener(listener), wp, key, operations);
}
});
}
else {
operate(findEventLoop(), listener, wp, key, operations);
}
}
//-------------------------------------------------------
// Scan Operations
//-------------------------------------------------------
/**
* Asynchronously read all records in specified namespace and set. If the policy's
* concurrentNodes
is specified, each server node will be read in
* parallel. Otherwise, server nodes are read in series.
*
* This method schedules the scan command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* @param policy scan configuration parameters, pass in null for defaults
* @param listener where to send results
* @param namespace namespace - equivalent to database name
* @param setName optional set name - equivalent to database table
* @param binNames optional bin to retrieve. All bins will be returned if not specified.
* Aerospike 2 servers ignore this parameter.
* @throws AerospikeException if queue is full
*/
public final void scanAll(final ScanPolicy policy, final RecordSequenceListener listener, final String namespace, final String setName, final String... binNames) throws AerospikeException {
final ScanPolicy sp = (policy != null)? policy : asyncScanPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
scanAll(findEventLoop(), new ARecordSequenceListener(listener, commands), sp, namespace, setName, binNames);
}
});
}
else {
scanAll(findEventLoop(), listener, sp, namespace, setName, binNames);
}
}
//---------------------------------------------------------------
// User defined functions
//---------------------------------------------------------------
/**
* Asynchronously execute user defined function on server and return results.
* The function operates on a single record.
* The package name is used to locate the udf file location on the server:
*
* udf file = /.lua
*
* This method schedules the execute command with a channel selector and returns.
* Another thread will process the command and send the results to the listener.
*
* @param policy write configuration parameters, pass in null for defaults
* @param listener where to send results, pass in null for fire and forget
* @param key unique record identifier
* @param packageName server package name where user defined function resides
* @param functionName user defined function
* @param functionArgs arguments passed in to user defined function
* @throws AerospikeException if transaction fails
*/
public final void execute(
final WritePolicy policy,
final ExecuteListener listener,
final Key key,
final String packageName,
final String functionName,
final Value... functionArgs
) throws AerospikeException {
final WritePolicy wp = (policy != null)? policy : asyncWritePolicyDefault;
if (useListener) {
commandBegin(1, new Runnable() {
public void run() {
execute(findEventLoop(), new AExecuteListener(listener), wp, key, packageName, functionName, functionArgs);
}
});
}
else {
execute(findEventLoop(), listener, wp, key, packageName, functionName, functionArgs);
}
}
//-------------------------------------------------------
// Query Operations
//-------------------------------------------------------
/**
* Asynchronously execute query on all server nodes. The query policy's
* maxConcurrentNodes
dictate how many nodes can be queried in parallel.
* The default is to query all nodes in parallel.
*
* This method schedules the node's query commands with channel selectors and returns.
* Selector threads will process the commands and send the results to the listener.
*
* @param policy query configuration parameters, pass in null for defaults
* @param listener where to send results
* @param statement database query command parameters
* @throws AerospikeException if query fails
*/
public final void query(final QueryPolicy policy, final RecordSequenceListener listener, final Statement statement) throws AerospikeException {
final QueryPolicy qp = (policy != null)? policy : asyncQueryPolicyDefault;
if (useListener) {
// Reserve one command for each node.
final int commands = cluster.getNodes().length;
commandBegin(commands, new Runnable() {
public void run() {
query(findEventLoop(), new ARecordSequenceListener(listener, commands), qp, statement);
}
});
}
else {
query(findEventLoop(), listener, qp, statement);
}
}
//-------------------------------------------------------
// Private
//-------------------------------------------------------
private final class AWriteListener implements WriteListener {
private final WriteListener listener;
private AWriteListener(WriteListener listener) {
this.listener = listener;
}
@Override
public void onSuccess(final Key key) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onSuccess(key);
}
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onFailure(ae);
}
}
});
}
}
private final class ADeleteListener implements DeleteListener {
private final DeleteListener listener;
private ADeleteListener(DeleteListener listener) {
this.listener = listener;
}
@Override
public void onSuccess(final Key key, final boolean existed) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onSuccess(key, existed);
}
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onFailure(ae);
}
}
});
}
}
private final class AExistsListener implements ExistsListener {
private final ExistsListener listener;
private AExistsListener(ExistsListener listener) {
this.listener = listener;
}
@Override
public void onSuccess(final Key key, final boolean existed) {
commandComplete(1, new Runnable() {
public void run() {
listener.onSuccess(key, existed);
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(1, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class AExistsArrayListener implements ExistsArrayListener {
private final ExistsArrayListener listener;
private final int commands;
private AExistsArrayListener(ExistsArrayListener listener, int commands) {
this.listener = listener;
this.commands = commands;
}
@Override
public void onSuccess(final Key[] keys, final boolean[] exists) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onSuccess(keys, exists);
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class AExistsSequenceListener implements ExistsSequenceListener {
private final ExistsSequenceListener listener;
private final int commands;
private AExistsSequenceListener(ExistsSequenceListener listener, int commands) {
this.listener = listener;
this.commands = commands;
}
@Override
public void onExists(final Key key, final boolean exists) {
callListener(new Runnable() {
public void run() {
listener.onExists(key, exists);
}
});
}
@Override
public void onSuccess() {
commandComplete(commands, new Runnable() {
public void run() {
listener.onSuccess();
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class ARecordListener implements RecordListener {
private final RecordListener listener;
private ARecordListener(RecordListener listener) {
this.listener = listener;
}
@Override
public void onSuccess(final Key key, final Record record) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onSuccess(key, record);
}
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onFailure(ae);
}
}
});
}
}
private final class ABatchListListener implements BatchListListener {
private final BatchListListener listener;
private final int commands;
private ABatchListListener(BatchListListener listener, int commands) {
this.listener = listener;
this.commands = commands;
}
@Override
public void onSuccess(final List records) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onSuccess(records);
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class ABatchSequenceListener implements BatchSequenceListener {
private final BatchSequenceListener listener;
private final int commands;
private ABatchSequenceListener(BatchSequenceListener listener, int commands) {
this.listener = listener;
this.commands = commands;
}
@Override
public void onRecord(final BatchRead record) {
callListener(new Runnable() {
public void run() {
listener.onRecord(record);
}
});
}
@Override
public void onSuccess() {
commandComplete(commands, new Runnable() {
public void run() {
listener.onSuccess();
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class ARecordArrayListener implements RecordArrayListener {
private final RecordArrayListener listener;
private final int commands;
private ARecordArrayListener(RecordArrayListener listener, int commands) {
this.listener = listener;
this.commands = commands;
}
@Override
public void onSuccess(final Key[] keys, final Record[] records) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onSuccess(keys, records);
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class ARecordSequenceListener implements RecordSequenceListener {
private final RecordSequenceListener listener;
private final int commands;
private ARecordSequenceListener(RecordSequenceListener listener, int commands) {
this.listener = listener;
this.commands = commands;
}
@Override
public void onRecord(final Key key, final Record record) {
callListener(new Runnable() {
public void run() {
listener.onRecord(key, record);
}
});
}
@Override
public void onSuccess() {
commandComplete(commands, new Runnable() {
public void run() {
listener.onSuccess();
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(commands, new Runnable() {
public void run() {
listener.onFailure(ae);
}
});
}
}
private final class AExecuteListener implements ExecuteListener {
private final ExecuteListener listener;
private AExecuteListener(ExecuteListener listener) {
this.listener = listener;
}
@Override
public void onSuccess(final Key key, final Object obj) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onSuccess(key, obj);
}
}
});
}
@Override
public void onFailure(final AerospikeException ae) {
commandComplete(1, new Runnable() {
public void run() {
if (listener != null) {
listener.onFailure(ae);
}
}
});
}
}
private final EventLoop findEventLoop() {
// Find event loop that is not running at capacity.
// maxCommandsPerEventLoop is a soft limit.
// That hard limit is the blocking throttle.
// Try robin-robin first.
NioEventLoop eventLoop = eventLoops.next();
// asyncPending is not atomic, so it's value is only approximate.
if (cluster.eventState[eventLoop.index].pending < maxCommandsPerEventLoop) {
return eventLoop;
}
// Try backwards.
for (int i = eventLoop.index - 1; i >= 0; i--) {
if (cluster.eventState[i].pending < maxCommandsPerEventLoop) {
return eventLoops.eventLoops[i];
}
}
// Try forwards.
for (int i = eventLoop.index + 1; i < eventLoops.eventLoops.length; i++) {
if (cluster.eventState[i].pending < maxCommandsPerEventLoop) {
return eventLoops.eventLoops[i];
}
}
// Nothing worked. Use original event loop.
return eventLoop;
}
private final void commandBegin(int commands, final Runnable runnable) {
if (throttle != null) {
if (throttle.waitForSlot(commands)) {
try {
runnable.run();
}
catch (Exception e) {
throttle.addSlot(commands);
throw e;
}
}
}
else {
runnable.run();
}
}
private final void commandComplete(int commands, final Runnable runnable) {
if (throttle != null) {
throttle.addSlot(commands);
}
callListener(runnable);
}
private final void callListener(final Runnable runnable) {
if (taskThreadPool != null) {
taskThreadPool.submit(new Runnable() {
public void run() {
try {
runnable.run();
}
catch (Exception e) {
Log.error("Listener error: " + Util.getErrorMessage(e));
}
}
});
}
else {
runnable.run();
}
}
}