UNPKG

aerospike

Version:
1,551 lines (1,499 loc) 110 kB
// ***************************************************************************** // 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