policies/command_queue_policy.js

// *****************************************************************************
// Copyright 2013-2023 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'

/**
 * Policy governing the use of the global command queue.
 *
 * **Which commands are affected by the command queue?**
 *
 * Not all client commands use the command queue. Only single-key commands
 * (e.g. Put, Get, etc.), the BatchRead, BatchWrite commands, and {@link Query#foreach},
 * {@link Scan#foreach} commands use the command queue (if enabled).
 *
 * Commands that are based on the Aerospike info protocol (Index
 * Create/Remove, UDF Register/Remove, Truncate, Info), the legacy Batch
 * Get/Select/Exists commands as well as all other Query and Scan commands do
 * not use the command queue and will always be executed immediately.
 *
 * @see {@link module:aerospike.setupGlobalCommandQueue
 * Aerospike.setupGlobalCommandQueue} - function used to initialize the global
 * command queue.
 *
 * @example
 *
 * const Aerospike = require('aerospike')
 *
 * const policy = {
 *   maxCommandsInProcess: 50,
 *   maxCommandsInQueue: 150
 * }
 * Aerospike.setupGlobalCommandQueue(policy)
 *
 * Aerospike.connect()
 *   .then(client => {
 *     let commands = []
 *     for (var i = 0; i < 100; i++) {
 *       let cmd = client.put(new Aerospike.Key('test', 'test', i), {i: i})
 *       commands.push(cmd)
 *     }
 *
 *     // First 50 commands will be executed immediately,
 *     // remaining commands will be queued and executed once the client frees up.
 *     Promise.all(commands)
 *       .then(() => console.info('All commands executed successfully'))
 *       .catch(error => console.error('Error:', error))
 *       .then(() => client.close())
 *   })
 */
class CommandQueuePolicy {
  /**
   * Initializes a new CommandQueuePolicy from the provided policy values.
   *
   * @param {Object} [props] - Policy values
   * @param {number} [props.maxCommandsInProcess] - Maximum number of async
   * commands that can be processed at any point in time.
   * @param {number} [props.maxCommandsInQueue] - Maximum number of commands that can be queued for later execution.
   * @param {number} [props.queueInitialCapacity] - Initial capacity of the command queue.
   */
  constructor (props) {
    props = props || {}

    /**
     * Maximum number of commands that can be processed at any point in time.
     * Each executing command requires a socket connection. Consuming too many
     * sockets can negatively affect application reliability and performance.
     * If you do not limit command count in your application, this setting
     * should be used to enforce a limit internally in the client.
     *
     * If this limit is reached, the next command will be placed on the
     * client's command queue for later execution. If this limit is zero, all
     * commands will be executed immediately and the command queue will not be
     * used. (Note: {@link Config#maxConnsPerNode} may still limit number of
     * connections per cluster node.)
     *
     * If defined, a reasonable value is 40. The optimal value will depend on
     * the CPU speed and network bandwidth.
     *
     * @type Number
     * @default 0 (execute all commands immediately)
     */
    this.maxCommandsInProcess = props.maxCommandsInProcess

    /**
     * Maximum number of commands that can be stored in the global command
     * queue for later execution. Queued commands consume memory, but they do
     * not consume sockets. This limit should be defined when it's possible
     * that the application executes so many commands that memory could be
     * exhausted.
     *
     * If this limit is reached, the next command will be rejected with error
     * code <code>ERR_ASYNC_QUEUE_FULL</code>. If this limit is zero, all
     * commands will be accepted into the delay queue.
     *
     * The optimal value will depend on the application's magnitude of command
     * bursts and the amount of memory available to store commands.
     *
     * @type Number
     * @default 0 (no command queue limit)
     */
    this.maxCommandsInQueue = props.maxCommandsInQueue

    /**
     * Initial capacity of the command queue. The command queue can resize
     * beyond this initial capacity.
     *
     * @type Number
     * @default 256 (if command queue is used)
     */
    this.queueInitialCapacity = props.queueInitialCapacity
  }
}

module.exports = CommandQueuePolicy