aerospike
Version:
Aerospike Client Library
1,551 lines (1,499 loc) • 110 kB
JavaScript
// *****************************************************************************
// Copyright 2013-2025 Aerospike, Inc.
//
// 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.
// *****************************************************************************
'use strict'
const path = require('path')
const util = require('util')
const EventEmitter = require('events')
const as = require('bindings')('aerospike.node')
const AerospikeError = require('./error')
const status = require('./status')
const txnState = require('./txn_state')
const abortStatus = require('./abort_status')
const commitStatus = require('./commit_status')
const Transaction = require('./transaction')
const Context = require('./cdt_context')
const Commands = require('./commands')
const Config = require('./config')
const EventLoop = require('./event_loop')
const IndexJob = require('./index_job')
const Query = require('./query')
const Scan = require('./scan')
const UdfJob = require('./udf_job')
const operations = require('./operations')
const utils = require('./utils')
// number of client instances currently connected to any Aerospike cluster
let _connectedClients = 0
const { _transactionPool } = require('./aerospike')
// callback function for cluster events (node added/removed, etc.)
function eventsCallback (event) {
/**
* @event Client#nodeAdded
* @type {object}
* @property {string} nodeName - Name of the cluster node that triggered this event.
* @property {string} nodeAddress - IP address & port of the cluster node that triggered this event.
* @since v2.7.0
*/
/**
* @event Client#nodeRemoved
* @type {object}
* @property {string} nodeName - Name of the cluster node that triggered this event.
* @property {string} nodeAddress - IP address & port of the cluster node that triggered this event.
* @since v2.7.0
*/
/**
* @event Client#disconnected
* @since v2.7.0
*
* @example
*
* const Aerospike = require('aerospike')
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
* hosts: '192.168.33.10:3000',
* }
*
* Aerospike.connect(config, (error, client) => {
* if (error) throw error
*
* client.on('disconnected', () => {
* console.log('Client got disconnected from cluster')
* })
*
* // client is now ready to accept commands, e.g. get/put/...
* client.close()
* })
*/
this.emit(event.name, event)
/**
* @event Client#event
* @description Instead of adding listeners for the {@link
* event:Client#nodeAdded|nodeAdded}, {@link
* event:Client#nodeRemoved|nodeRemoved} and {@link
* event:Client#disconnected|disconnected} events, applications can also
* subscribe to the <code>event</code> event to receive callbacks for any
* kind of cluster event.
*
* @type {object}
* @property {string} name - Name of the event.
* @property {string} [nodeName] - Name of the cluster node that triggered this event.
* @property {string} [nodeAddress] - IP address & port of the cluster node that triggered this event.
* @since v2.7.0
*
* @example
*
* const Aerospike = require('aerospike')
*
* Aerospike.connect((error, client) => {
* if (error) throw error
*
* client.on('event', (event) => {
* var now = new Date().toUTCString()
* console.log(now, event.name, event.nodeName) // Example output:
* // Thu, 13 Jul 2017 06:47:35 GMT nodeAdded BB94DC07D270009
* // Thu, 13 Jul 2017 06:47:35 GMT nodeAdded C1D4DC0AD270002
* // Thu, 13 Jul 2017 06:48:52 GMT nodeRemoved C1D4DC0AD270002
* // Thu, 13 Jul 2017 06:49:08 GMT nodeRemoved BB94DC07D270009
* // Thu, 13 Jul 2017 06:49:08 GMT disconnected
* })
*
* // client is now ready to accept commands, e.g. get/put/...
* client.close()
* })
*/
this.emit('event', event)
}
/**
* @class Client
* @classdesc Aerospike client
*
* @summary Construct a new Aerospike client instance.
*
* @param {Config} config - Configuration used to initialize the client.
*/
function Client (config) {
EventEmitter.call(this)
/**
* @name Client#config
*
* @summary A copy of the configuration with which the client was initialized.
*
* @type {Config}
*/
this.config = new Config(config)
/** @private */
this.as_client = as.client(this.config)
/** @private */
this.connected = false
/**
* @name Client#captureStackTraces
*
* @summary Set to <code>true</code> to enable capturing of debug stacktraces for
* every database command.
*
* @description The client will capture a stacktrace before each database
* command is executed, instead of capturing the stacktrace only when an
* error is raised. This generally results in much more useful stacktraces
* that include stackframes from the calling application issuing the database
* command.
*
* **Note:** Enabling this feature incurs a significant performance overhead for
* every database command. It is recommended to leave this feature disabled
* in production environments.
*
* By default, the client will set this flag to true, if the
* <code>AEROSPIKE_DEBUG_STACKTRACES</code> environment variable is set (to
* any value).
*
* @type {boolean}
* @default <code>true</code>, if
* <code>process.env.AEROSPIKE_DEBUG_STACKTRACES</code> is set;
* <code>false</code> otherwise.
*/
this.captureStackTraces = !!process.env.AEROSPIKE_DEBUG_STACKTRACES
}
util.inherits(Client, EventEmitter)
/**
* @private
*/
Client.prototype.asExec = function (cmd, args) {
return this.as_client[cmd].apply(this.as_client, args)
}
/**
* @function Client#getNodes
*
* @summary Returns a list of all cluster nodes known to the client.
*
* @return {Array.<{name: string, address: string}>} List of node objects
*
* @since v2.6.0
*
* @example
*
* const Aerospike = require('aerospike')
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
* hosts: '192.168.33.10:3000',
* }
*
* Aerospike.connect(config, (error, client) => {
* if (error) throw error
* console.log(client.getNodes()) // [ { name: 'SAMPLEADDRESS', address: 'SAMPLENAME' }, ...]
* client.close()
* })
*
*/
Client.prototype.getNodes = function () {
return this.as_client.getNodes()
}
Client.prototype.abort = function (transaction, callback) {
_transactionPool.tendTransactions()
if (transaction instanceof Transaction) {
if (transaction.getState() === txnState.COMMITTED) {
const err = new AerospikeError('The transaction has already been committed.')
err.code = status.TXN_ALREADY_COMMITTED
throw err
} else if (transaction.getState() === txnState.ABORTED) {
return abortStatus.ALREADY_ABORTED
} else if (transaction.getDestroyed() === true) {
throw new AerospikeError('The object has been destroyed, please create a new transaction.')
}
} else {
throw new AerospikeError('transaction must be an instance of class Transaction.')
}
const cmd = new Commands.TransactionAbort(this, [transaction.transaction], callback)
return cmd.execute()
}
Client.prototype.commit = function (transaction, callback) {
_transactionPool.tendTransactions()
if (transaction instanceof Transaction) {
if (transaction.getState() === txnState.COMMITTED) {
return commitStatus.ALREADY_COMMITTED
} else if (transaction.getState() === txnState.ABORTED) {
const err = new AerospikeError('The transaction has already been aborted.')
err.code = status.TXN_ALREADY_ABORTED
throw err
} else if (transaction.getDestroyed() === true) {
throw new AerospikeError('The object has been destroyed, please create a new transaction.')
}
} else {
throw new AerospikeError('transaction must be an instance of class Transaction.')
}
const cmd = new Commands.TransactionCommit(this, [transaction.transaction], callback)
return cmd.execute()
}
Client.prototype.enableMetrics = function (policy, callback) {
let args
if (typeof policy === 'function') {
callback = policy
policy = null
}
if (!(policy) || !(policy.metricsListeners)) {
args = [policy, null, null, null, null]
} else {
args = [
policy,
policy.metricsListeners.enableListener,
policy.metricsListeners.snapshotListener,
policy.metricsListeners.nodeCloseListener,
policy.metricsListeners.disableListener
]
}
const cmd = new Commands.EnableMetrics(this, args, callback)
return cmd.execute()
}
Client.prototype.disableMetrics = function (callback) {
const cmd = new Commands.DisableMetrics(this, [], callback)
return cmd.execute()
}
/**
* @function Client#contextToBase64
*
* @summary Returns a serialized CDT Context
*
* @param {Object} context - {@link CdtContext}
*
* @return {String} serialized context - base64 representation of the CDT Context
*
* @since v5.6.0
*
* @example <caption>How to use CDT context serialization</caption>
*
* const Aerospike = require('aerospike');
* const Context = Aerospike.cdt.Context
* // Define host configuration
* let config = {
* hosts: '192.168.33.10:3000',
* policies: {
* operate : new Aerospike.OperatePolicy({socketTimeout : 0, totalTimeout : 0}),
* write : new Aerospike.WritePolicy({socketTimeout : 0, totalTimeout : 0}),
* read : new Aerospike.ReadPolicy({socketTimeout : 0, totalTimeout : 0})
* }
* }
*
*
* Aerospike.connect(config, async (error, client) => {
* // Create a context
* let context = new Context().addMapKey('nested')
*
* // Create keys for records to be written
* let recordKey = new Aerospike.Key('test', 'demo', 'record')
* let contextKey = new Aerospike.Key('test', 'demo', 'context')
*
* // Put record with a CDT
* await client.put(recordKey, {exampleBin: {nested: {food: 'blueberry', drink: 'koolaid'}}})
*
* // Test the context with client.operate()
* var ops = [
* Aerospike.maps.getByKey('exampleBin', 'food', Aerospike.maps.returnType.KEY_VALUE).withContext(context)
* ]
* let results = await client.operate(recordKey, ops)
* console.log(results.bins.exampleBin) // [ 'food', 'blueberry' ]
*
* // Serialize CDT Context
* let serializedContext = client.contextToBase64(context)
*
* // Put record with bin containing the serialized record
* await client.put(contextKey, {context: serializedContext})
*
* // Get context when needed for operation
* let contextRecord = await client.get(contextKey)
*
* // Deserialize CDT Context
* context = client.contextFromBase64(contextRecord.bins.context)
*
* // Test the context with client.operate()
* ops = [
* Aerospike.maps.getByKey('exampleBin', 'food', Aerospike.maps.returnType.KEY_VALUE).withContext(context)
* ]
* results = await client.operate(recordKey, ops)
* console.log(results.bins.exampleBin) // [ 'food', 'blueberry' ]
*
* // Close the client
* client.close()
* })
*/
Client.prototype.contextToBase64 = function (context) {
return this.as_client.contextToBase64({ context })
}
Client.prototype.setXDRFilter = function (expression, dataCenter, namespace, policy, callback) {
if (typeof policy === 'function') {
callback = policy
policy = null
}
const cmd = new Commands.SetXDRFilter(this, [expression, dataCenter, namespace, policy], callback)
return cmd.execute()
}
/**
* @function Client#contextFromBase64
*
* @summary Returns a deserialized CDT Context
*
* @param {String} serializedContext - base64 serialized {@link CdtContext}
*
* @return {CdtContext} Deserialized CDT Context
*
* @since v5.6.0
*
*/
Client.prototype.contextFromBase64 = function (serializedContext) {
const context = new Context()
context.items = this.as_client.contextFromBase64({ context: serializedContext })
return context
}
/**
* @function Client#changePassword
*
* @summary Change a user's password.
*
* @param {String} user - User name for the password change.
* @param {String} password - User password in clear-text format.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // User must be created before password is changed. See {@link Client#createUser} for an example.
* client.changePassword("khob", "TryTiger7!", ["Engineer"])
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.changePassword = function (user, password, policy) {
const cmd = new Commands.ChangePassword(this, [user, password, policy, this.config.user])
cmd.execute()
}
/**
* @function Client#createUser
*
* @summary Create user with password and roles. Clear-text password will be hashed using bcrypt before sending to server.
*
* @param {String} user - User name for the new user.
* @param {String} password - User password in clear-text format.
* @param {Array.<String>} roles - Optional array of role names. For more information on roles, see {@link Role}.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* client.createUser("khob", "MightyMice55!", ["Engineer"])
* // Must wait a short length of time of the user to be fully created.
* await wait(5)
* const user = await client.queryUser("khob", null)
* console.log(user)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.createUser = function (user, password, roles, policy) {
const cmd = new Commands.UserCreate(this, [user, password, roles, policy])
cmd.execute()
}
/**
* @function Client#createRole
*
* @summary Create user defined role with optional privileges, whitelist and read/write quotas.
* Quotas require server security configuration "enable-quotas" to be set to true.
*
* @param {String} roleName - role name
* @param {Array.<Privilege>} privileges - List of privileges assigned to a role.
* @param {Object} policy - Optional {@link AdminPolicy}.
* @param {Array.<String>} whitelist - Optional list of allowable IP addresses assigned to role. IP addresses can contain wildcards (ie. 10.1.2.0/24).
* @param {Number} readQuota - Optional maximum reads per second limit, pass in zero for no limit.
* @param {Number} writeQuota - Optional maximum writes per second limit, pass in zero for no limit.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configs can be used.
* user: 'admin',
* password: 'admin'
* })
*
* client.createRole("Engineer", [new Aerospike.admin.Privilege(Aerospike.privilegeCode.READ_WRITE), new Aerospike.admin.Privilege(Aerospike.privilegeCode.TRUNCATE)], null)
* // Must wait a short length of time of the role to be fully created.
* await wait(5)
* const role = await client.queryRole("Engineer", null)
* console.log(role)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.createRole = function (roleName, privileges, policy, whitelist, readQuota, writeQuota) {
const cmd = new Commands.RoleCreate(this, [roleName, privileges, policy, whitelist, readQuota, writeQuota])
cmd.execute()
}
/**
* @function Client#dropRole
*
* @summary Drop user defined role.
*
* @param {String} roleName - role name
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // A role must be created before a role can be dropped. See {@link Client#createRole} for an example.
* client.dropRole("Engineer")
* // Must wait a short length of time of the role to be fully dropped.
* await wait(5)
* let roles = await client.queryRoles()
* // 'Engineer' should no longer appear in the logged list of roles
* console.log(roles)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.dropRole = function (roleName, policy) {
const cmd = new Commands.RoleDrop(this, [roleName, policy])
cmd.execute()
}
/**
* @function Client#dropUser
*
* @summary Remove a User from cluster
*
* @param {String} user - User name to be dropped.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // A user must be created before a user can be dropped. See {@link Client#createUser} for an example.
* client.dropUser("khob")
* // Must wait a short length of time of the role to be fully dropped.
* await wait(5)
* let users = await client.queryUsers()
* // 'khob' should no longer appear in the logged list of roles
* console.log(users)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.dropUser = function (user, policy) {
const cmd = new Commands.UserDrop(this, [user, policy])
cmd.execute()
}
/**
* @function Client#grantPrivileges
*
* @summary Grant privileges to an user defined role.
*
* @param {String} roleName - role name
* @param {Array.<Privilege>} privileges - list of privileges assigned to a role.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // A role must be created before privileges can be granted. See {@link Client#createUser} for an example.
* client.grantPrivileges("Engineer", [new Aerospike.admin.Privilege(Aerospike.privilegeCode.SINDEX_ADMIN)])
* // Must wait a short length of time for the privilege to be granted.
* await wait(5)
* let role = await client.queryRole("Engineer")
* console.log(role)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.grantPrivileges = function (roleName, privileges, policy) {
const cmd = new Commands.PrivilegeGrant(this, [roleName, privileges, policy])
cmd.execute()
}
/**
* @function Client#grantRoles
*
* @summary Drop user defined role.
*
* @param {String} user - User name for granted roles
* @param {Array.<String>} roles - Optional array of role names. For more information on roles, see {@link Role}.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // A user must be created before roles can be granted. See {@link Client#createUser} for an example.
* client.grantRoles("khob", ["Engineer"])
* // Must wait a short length of time for the role to be granted
* await wait(5)
* let user = await client.queryUser("khob")
* console.log(user)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*
*/
Client.prototype.grantRoles = function (user, roles, policy) {
const cmd = new Commands.RoleGrant(this, [user, roles, policy])
cmd.execute()
}
/**
* @function Client#queryRole
*
* @summary Retrieves an {@link Role} from the database.
*
* @param {String} roleName - role name filter.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @returns {Role} - For more information on roles, see {@link Role}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // A role must be created before a role can be queried. See {@link Client#createRole} for an example.
* let role = await client.queryRole("Engineer")
* console.log(role)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.queryRole = async function (roleName, policy) {
const cmd = new Commands.QueryRole(this, [roleName, policy])
return cmd.execute()
}
/**
* @function Client#queryRoles
*
* @summary Retrieve all roles and role information from the database.
*
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @returns {Array.<Role>} - For more information on roles, see {@link Role}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* let roles = await client.queryRoles()
* console.log(roles)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.queryRoles = function (policy) {
const cmd = new Commands.QueryRoles(this, [policy])
return cmd.execute()
}
/**
* @function Client#queryUser
*
* @summary Retrieves an {@link User} from the database.
*
* @param {String} user - User name filter.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @returns {User} - For more information on users, see {@link User}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* // A user must be created before a user can be queried. See {@link Client#createUser} for an example.
* let user = await client.queryUser("khob")
* console.log(user)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.queryUser = function (user, policy) {
const cmd = new Commands.QueryUser(this, [user, policy])
return cmd.execute()
}
/**
* @function Client#queryUsers
*
* @summary Retrieves All user and user information from the database.
*
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @returns {Array.<User>} - For more information on users, see {@link User}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
*
* let users = await client.queryUsers()
* console.log(users)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.queryUsers = function (policy) {
const cmd = new Commands.QueryUsers(this, [policy])
return cmd.execute()
}
/**
* @function Client#revokePrivileges
*
* @summary Revoke privileges from an user defined role.
*
* @param {String} roleName - role name
* @param {Array.<Privilege>} privileges - List of privileges assigned to a role.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
* // A role must be created before privileges can be revoked. See {@link Client#createRole} for an example.
* client.revokePrivileges("Engineer", [new Aerospike.admin.Privilege(Aerospike.privilegeCode.SINDEX_ADMIN)])
* // Must wait a short length of time for the privilege to be granted.
* await wait(5)
* let users = await client.queryRole("Engineer")
* console.log(users)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.revokePrivileges = function (roleName, privileges, policy) {
const cmd = new Commands.PrivilegeRevoke(this, [roleName, privileges, policy])
cmd.execute()
}
/**
* @function Client#revokeRoles
*
* @summary Remove roles from user's list of roles.
*
* @param {String} user - User name for revoked roles.
* @param {Array.<String>} roles - Optional array of role names. For more information on roles, see {@link Role}.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
* // A user must be created before roles can be revoked. See {@link Client#createUser} for an example.
* client.revokeRoles("khob", ["Engineer"])
* // Must wait a short length of time for the privilege to be granted.
* await wait(5)
* let user = await client.queryUser("khob")
* console.log(user)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*/
Client.prototype.revokeRoles = function (user, roles, policy) {
const cmd = new Commands.RoleRevoke(this, [user, roles, policy])
cmd.execute()
}
/**
* @function Client#setQuotas
*
* @summary Set maximum reads/writes per second limits for a role. If a quota is zero, the limit is removed.
* Quotas require server security configuration "enable-quotas" to be set to true.
*
* @param {String} roleName - role name
* @param {Number} readQuota - maximum reads per second limit, pass in zero for no limit.
* @param {Number} writeQuota - maximum writes per second limit, pass in zero for no limit.
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
* // Quotas must be enabled in the server configurations for quotas to be set.
* client.setQuotas("Engineer", 200, 300)
* // Must wait a short length of time for the privilegee to be granted.
* await wait(5)
* let role = await client.queryRole("Engineer")
* console.log(role)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*
*/
Client.prototype.setQuotas = function (roleName, readQuota, writeQuota, policy) {
const cmd = new Commands.RoleSetQuotas(this, [roleName, readQuota, writeQuota, policy])
cmd.execute()
}
/**
* @function Client#setWhitelist
*
* @summary Set IP address whitelist for a role. If whitelist is null or empty, remove existing whitelist from role.
*
* @param {String} roleName - role name
* @param {Array.<String>} whitelist - Optional list of allowable IP addresses assigned to role.
* IP addresses can contain wildcards (ie. 10.1.2.0/24).
* @param {Object} policy - Optional {@link AdminPolicy}.
*
* @example
*
* const Aerospike = require('aerospike')
*
* function wait (ms) {
* return new Promise(resolve => setTimeout(resolve, ms))
* }
*
* ;(async function () {
* let client
* try {
* client = await Aerospike.connect({
* hosts: '192.168.33.10:3000',
* policies: {
* write : new Aerospike.WritePolicy({socketTimeout : 1, totalTimeout : 1}),
* },
* // Must have security enabled in server configuration before user and password configurations can be used.
* user: 'admin',
* password: 'admin'
* })
* // Quotas must be enabled in the server configurations for quotas to be set.
* client.setWhitelist("Engineer", ["172.17.0.2"])
* // Must wait a short length of time for the privilegee to be granted.
* await wait(5)
* let role = await client.queryRole("Engineer")
* console.log(role)
* } catch (error) {
* console.error('Error:', error)
* process.exit(1)
* } finally {
* if (client) client.close()
* }
* })()
*
*/
Client.prototype.setWhitelist = function (roleName, whitelist, policy) {
const cmd = new Commands.RoleSetWhitelist(this, [roleName, whitelist, policy])
cmd.execute()
}
/**
* @function Client#addSeedHost
*
* @summary Adds a seed host to the cluster.
*
* @param {String} hostname - Hostname/IP address of the new seed host
* @param {Number} [port=3000] - Port number; defaults to {@link Config#port} or 3000.
*
* @since v2.6.0
*/
Client.prototype.addSeedHost = function (hostname, port) {
port = port || this.config.port
this.as_client.addSeedHost(hostname, port)
}
/**
* @function Client#removeSeedHost
*
* @summary Removes a seed host from the cluster.
*
* @param {String} hostname - Hostname/IP address of the seed host
* @param {Number} [port=3000] - Port number; defaults to {@link Config#port} or 3000.
*
* @since v2.6.0
*/
Client.prototype.removeSeedHost = function (hostname, port) {
port = port || this.config.port
this.as_client.removeSeedHost(hostname, port)
}
/**
* @function Client#batchExists
*
* @summary Checks the existence of a batch of records from the database cluster.
*
* @param {Key[]} keys - An array of Keys used to locate the records in the cluster.
* @param {BatchPolicy} [policy] - The Batch Policy to use for this operation.
* @param {batchRecordsCallback} [callback] - The function to call when
* the operation completes, with the results of the batch operation.
*
* @returns {?Promise} - If no callback function is passed, the function
* returns a Promise that resolves to the results of the batch operation.
*
* @deprecated since v2.0 - use {@link Client#batchRead} instead.
*
* @example
*
* const Aerospike = require('aerospike')
* const Key = Aerospike.Key
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
* hosts: '192.168.33.10:3000',
* // Timeouts disabled, latency dependent on server location. Configure as needed.
* policies: {
* batch : new Aerospike.BatchPolicy({socketTimeout : 0, totalTimeout : 0}),
* }
* }
*
* var keys = [
* new Key('test', 'demo', 'key1'),
* new Key('test', 'demo', 'key2'),
* new Key('test', 'demo', 'key3')
* ]
*
* ;(async () => {
* // Establishes a connection to the server
* let client = await Aerospike.connect(config);
*
* // Place some records for demonstration
* await client.put(keys[0], {example: 30})
* await client.put(keys[1], {example: 35})
* await client.put(keys[2], {example: 40})
*
* let results = await client.batchExists(keys)
* results.forEach((result) => {
* switch (result.status) {
* case Aerospike.status.OK:
* console.log("Record found")
* break
* case Aerospike.status.ERR_RECORD_NOT_FOUND:
* console.log("Record not found")
* break
* default:
* // error while reading record
* console.log("Other error")
* break
* }
* })
*
* // Close the connection to the server
* await client.close();
* })();
*
*
*/
Client.prototype.batchExists = function (keys, policy, callback) {
if (typeof policy === 'function') {
callback = policy
policy = null
}
const cmd = new Commands.BatchExists(this, [keys, policy], callback)
return cmd.execute()
}
/**
* @function Client#batchGet
*
* @summary Reads a batch of records from the database cluster.
*
* @param {Key[]} keys - An array of keys, used to locate the records in the cluster.
* @param {BatchPolicy} [policy] - The Batch Policy to use for this operation.
* @param {batchRecordsCallback} [callback] - The function to call when
* the operation completes, with the results of the batch operation.
*
* @returns {?Promise} - If no callback function is passed, the function
* returns a Promise that resolves to the results of the batch operation.
*
* @deprecated since v2.0 - use {@link Client#batchRead} instead.
*
* @example
*
* const Aerospike = require('aerospike')
* const Key = Aerospike.Key
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
* hosts: '192.168.33.10:3000',
* // Timeouts disabled, latency dependent on server location. Configure as needed.
* policies: {
* batch : new Aerospike.BatchPolicy({socketTimeout : 0, totalTimeout : 0}),
* }
* }
*
* var keys = [
* new Key('test', 'demo', 'key1'),
* new Key('test', 'demo', 'key2'),
* new Key('test', 'demo', 'key3')
* ]
*
* ;(async () => {
* // Establishes a connection to the server
* let client = await Aerospike.connect(config);
*
* // Place some records for demonstration
* await client.put(keys[0], {example: 30})
* await client.put(keys[1], {example: 35})
* await client.put(keys[2], {example: 40})
*
* let results = await client.batchGet(keys)
* results.forEach((result) => {
* switch (result.status) {
* case Aerospike.status.OK:
* console.log("Record found")
* break
* case Aerospike.status.ERR_RECORD_NOT_FOUND:
* console.log("Record not found")
* break
* default:
* // error while reading record
* console.log("Other error")
* break
* }
* })
*
* // Close the connection to the server
* await client.close();
* })();
*
*/
Client.prototype.batchGet = function (keys, policy, callback) {
if (typeof policy === 'function') {
callback = policy
policy = null
}
const cmd = new Commands.BatchGet(this, [keys, policy], callback)
return cmd.execute()
}
/**
* @function Client#batchRead
*
* @summary Read multiple records for specified batch keys in one batch call.
*
* @description
*
* This method allows different namespaces/bins to be requested for each key in
* the batch. This method requires server >= 3.6.0.
*
* @param {object[]} records - {@link Record} List of keys and bins to retrieve.
* @param {number} records[].type - {@link Record#type} Batch type.
* @param {Key} records[].key - Record Key.
* @param {string[]} [records[].bins] - List of bins to retrieve.
* @param {boolean} [records[].readAllBins] - Whether to retrieve all bins or
* just the meta data of the record. If true, ignore <code>bins</code> and read
* all bins; if false and <code>bins</code> is specified, read specified bins;
* if false and <code>bins</code> is not specified, read only record meta data
* (generation, expiration, etc.)
* @param {Object[]} [records[].ops] - List of {@link module:aerospike/operations|operations}
* @param {BatchPolicy} [policy] - The Batch Policy to use for this operation.
* @param {batchRecordsCallback} [callback] - The function to call when
* the operation completes, with the results of the batch operation.
*
* @returns {?Promise} - If no callback function is passed, the function
* returns a Promise that resolves to the results of the batch operation.
*
* @since v2.0
*
* @example
*
* const Aerospike = require('aerospike')
* const batchType = Aerospike.batchType
* const op = Aerospike.operations
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
* hosts: '192.168.33.10:3000',
* // Timeouts disabled, latency dependent on server location. Configure as needed.
* policies: {
* batch : new Aerospike.BatchPolicy({socketTimeout : 0, totalTimeout : 0}),
* }
* }
*
* var batchRecords = [
* { type: batchType.BATCH_READ,
* key: new Aerospike.Key('test', 'demo', 'key1'), bins: ['example'] },
* { type: batchType.BATCH_READ,
* key: new Aerospike.Key('test', 'demo', 'key2'), readAllBins: true },
* { type: batchType.BATCH_READ,
* key: new Aerospike.Key('test', 'demo', 'key3'),
* ops:[
* op.read('example')
* ]},
* { type: batchType.BATCH_READ,
* key: new Aerospike.Key('test', 'demo', 'key4')}
* ]
*
*
* ;(async () => {
* // Establishes a connection to the server
* let client = await Aerospike.connect(config);
*
* // Place some records for demonstration
* await client.put(batchRecords[0].key, {example: 30})
* await client.put(batchRecords[1].key, {example: 35})
* await client.put(batchRecords[2].key, {example: 40})
* await client.put(batchRecords[3].key, {example: 45})
*
* let results = await client.batchRead(batchRecords)
* results.forEach((result) => {
*
* switch (result.status) {
* case Aerospike.status.OK:
* console.log("Record found")
* // Since the fourth record didn't specify bins to read,
* // the fourth record will return no bins, eventhough the batchRead succeeded.
* console.log(result.record.bins)
* break
* case Aerospike.status.ERR_RECORD_NOT_FOUND:
* console.log("Record not found")
* break
* default:
* // error while reading record
* console.log("Other error")
* break
* }
* })
* // Close the connection to the server
* await client.close();
* })();
*/
Client.prototype.batchRead = function (records, policy, callback) {
if (typeof policy === 'function') {
callback = policy
policy = null
}
const cmd = new Commands.BatchRead(this, [records, policy], callback)
return cmd.execute()
}
/**
* @function Client#batchWrite
*
* @summary Read/Write multiple records for specified batch keys in one batch call.
*
* @description
*
* This method allows different sub-commands for each key in the batch.
* This method requires server >= 6.0.0.
*
* @param {object[]} records - {@link Record} List of batch sub-commands to perform.
* @param {number} records[].type - {@link Record#type} Batch type.
* @param {Key} records[].key - Record Key.
* @param {BatchPolicy} [policy] - The Batch Policy to use for this operation.
* @param {batchRecordsCallback} [callback] - The function to call when
* the operation completes, with the results of the batch operation.
*
* @returns {?Promise} - If no callback function is passed, the function
* returns a Promise that resolves to the results of the batch operation.
*
* @since v5.0.0
*
* @example
*
* const Aerospike = require('aerospike')
* const batchType = Aerospike.batchType
* const Key = Aerospike.Key
* const op = Aerospike.operations
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
* hosts: '192.168.33.10:3000',
* // Timeouts disabled, latency dependent on server location. Configure as needed.
* policies: {
* batch : new Aerospike.BatchPolicy({socketTimeout : 0, totalTimeout : 0}),
* }
* }
*
* const batchRecords = [
* {
* type: batchType.BATCH_REMOVE,
* key: new Key("test", "demo", 'key1')
* },
* {
* type: batchType.BATCH_WRITE,
* key: new Key("test", "demo", 'key2'),
* ops: [
* op.write('example', 30),
* op.write('blob', Buffer.from('foo'))
* ],
* policy: new Aerospike.BatchWritePolicy({
* exists: Aerospike.policy.exists.IGNORE
* })
* },
* {
* type: batchType.BATCH_WRITE,
* key: new Key("test", "demo", 'key3'),
* ops: [
* op.write('example', 35),
* op.write('blob', Buffer.from('bar'))
* ],
* policy: new Aerospike.BatchWritePolicy({
* exists: Aerospike.policy.exists.IGNORE
* })
* }
* ]
*
* const batchReadRecords = [
* {
* type: batchType.BATCH_READ,
* key: new Key("test", "demo", 'key1'),
* readAllBins: true
* },
* {
* type: batchType.BATCH_READ,
* key: new Key("test", "demo", 'key2'),
* readAllBins: true
* },
* {
* type: batchType.BATCH_READ,
* key: new Key("test", "demo", 'key3'),
* readAllBins: true
* }
* ]
*
* ;(async () => {
* // Establishes a connection to the server
* let client = await Aerospike.connect(config);
*
* // Place a record for demonstration
* await client.put(new Key("test", "demo", 'key1'), {example: 30, user: 'Doug', extra: 'unused'})
*
* let results = await client.batchWrite(batchRecords)
* results.forEach((result) => {
* switch (result.status) {
* case Aerospike.status.OK:
* console.log("Record found")
* break
* case Aerospike.status.ERR_RECORD_NOT_FOUND:
* console.log("Record not found")
* break
* default:
* // error while reading record
* console.log("Other error")
* break
* }
* })
*
* results = await client.batchWrite(batchRecords)
* results.forEach((result) => {
* switch (result.status) {
* case Aerospike.status.OK:
* console.log("Record found")
* break
* case Aerospike.status.ERR_RECORD_NOT_FOUND:
* console.log("Record not found")
* break
* default:
* // error while reading record
* console.log("Other error")
* break
* }
* })
* // Close the connection to the server
* await client.close();
* })();
*/
Client.prototype.batchWrite = function (records, policy, callback) {
if (typeof policy === 'function') {
callback = policy
policy = null
}
const cmd = new Commands.BatchWrite(this, [records, policy], callback)
return cmd.execute()
}
/**
* @function Client#batchApply
*
* @summary Apply UDF (user defined function) on multiple keys.
*
* @description
*
* This method allows multiple sub-commands for each key in the batch.
* This method requires server >= 6.0.0.
*
* @param {Key[]} keys - An array of keys, used to locate the records in the cluster.
* @param {object[]} udf - Server UDF module/function and argList to apply.
* @param {BatchPolicy} [batchPolicy] - The Batch Policy to use for this operation.
* @param {BatchApplyPolicy} [batchApplyPolicy] UDF policy configuration parameters.
* @param {batchRecordsCallback} [callback] - The function to call when
* the operation completes, with the results of the batch operation.
*
* @returns {?Promise} - If no callback function is passed, the function
* returns a Promise that resolves to the results of the batch operation.
*
* @since v5.0.0
*
* @example