Cloud Service or Cloud Simulator only.

Rate limiting is used to provide efficient access to Oracle NoSQL Database Cloud Service, maximize throughput and protect from resource starvation. The SDK provides built-in rate limiting functionality for data operations on tables such as get, put, delete, deleteRange, prepare, query and their variants. Rate limiting in the driver allows spreading the operations over time thus maximizing throughput by avoiding costly throttling errors and the associated retry handling (see READ_LIMIT_EXCEEDED and WRITE_LIMIT_EXCEEDED).

For general understanding of rate limiting, see articles such as Understanding and implementing rate limiting in Node.js, Rate Limiting Part 1, Rate Limiting Part 2, and many others.

Each operation listed above does reads and/or writes on a table and thus consumes certain number of read and write units (see ConsumedCapacity for more explanation). Each table in NoSQL Database Cloud Service has table limits defining maximum number of read and write units that can be consumed by all clients accessing the table, per 1 second time. Thus, the total rate of operations on a given table is limited. If a client tries to perform the operations at faster rate, the server will respond with a read or write throttling error, in which case the client can retry the operation after certain time, see RetryConfig. Handling throttling errors and their retries is costly both for the server and the client application since the application has to wait before an operation can be retried (retrying immediately or not waiting enough will only result in another throttling error and more needless load on the server). A much better strategy for a client is to spread out the operations over time to an extent that the table limits for given table are not exceeded and thus avoiding throttling errors and their retries. Rate limiting built in to the SDK provides this functionality.

An instance of RateLimiter class is used to enforce one table limit. Thus the driver will instantiate two instances of RateLimiter class for each table in use, one for read limit and another for write limit. You have a choice of using a default implementation of RateLimiter provided by the driver or providing a custom impelemntation of RateLimiter class. See rateLimiter. For details on the default rate limiter, see SimpleRateLimiter. To provide custom RateLimiter class, implement a class with instance methods as described below and set rateLimiter to the constructor function of that class or module name exporting this class. The driver will create each instance of this class by using its constructor with no arguments.

In order to create a pair of rate limiters for a table, the driver will have to know its table limits. The table limits are already known if one of tableDDL, getTable, forCompletion, forTableState has been called. Otherwise, the driver will call getTable in the background to obtain its table limits as soon as any data operation is issued for that table. This means that enabling rate limiting for a table may be delayed until its table limits are obtained successfully in the background.

The main operation of rate limiting in the driver is to call consumeUnits to consume a number of (read or write) units for a given operation. Depending on impelementation of RateLimiter, its current state and the number of units to consume, this call may asynchoronously block (sleep) for certain amount of time (and return Promise of this amount in milliseconds) before letting the operation proceed. This API also needs to correctly operate in the presence of timeout set by the caller. See consumeUnits for details. Note that it may be possible to consume certain amount of units without blocking (e.g. if there has been no or very little recent activity). In this state the rate limiter is said to be under its limit. Conversely, even consuming 0 units may block as a result of consuming units for recent past operations. In this state the rate limiter is said to be over its limit.

Rate limiting works best when we know in advance how many units each operation will consume, which would allow to know precisely how long to wait before issuing each operation. Unfortunately, we don't know how many units an operation requires until we get the result back, with this information returned as part of ConsumedCapacity object. It may be difficult or impossible to estimate number of units required before the operation completes. The driver takes an approach where consumeUnits is called twice, once before the operation passing 0 units to (possibly) wait until the rate limiter is under its limit and then after the operation passing the number of units returned as part of ConsumedCapacity of the result. This will allow to delay subsequent operations and stagger subsequent concurrent operations over time.

Also, note that by default the rate limiting only works as expected when used within one NoSQLClient instance. When using multiple NoSQLClient instances, whether in the same process, different process or even running on different machine, rate limiters in one instance will not be aware of operations performed by other instances and thus will not correctly rate limit the operations. If multiple concurrent NoSQLClient instances are required, you can set rateLimiterPercent to allocate only a percentage of each table's limits to each instance. Although not optimal (not accounting for overuse or underuse at a particular instance), this will allow correct rate limiting of operations on multiple concurrent instances.

As mentioned above, the driver provides a default rate limiter as SimpleRateLimiter class, which you can use by setting rateLimiter to true. Alternatively, you can provide your own custom rate limiter class by setting rateLimiter to its constructor function or module name that exports it.

Unfortunately, there is no perfect driver-side rate limiting stategy so it is still possible for throttling errors to occur. RateLimiter interface provides onThrottle method that the driver calls when an operation results in throttling error. When creating custom rate limiter, implementing this method will allow you to adjust the rate limiter's state to account for this information.

See

Hierarchy

  • RateLimiter

Implemented by

Methods

  • This function should consume the number of units provided and if needed, block (sleep) asynchronoulsy for the amount of time computed from current state and the units provided.

    Async

    Returns

    Promise resolved with the amount of time blocked in milliseconds (or 0 if the operation was not blocked) or rejected with an error if timeout is reached and consumeOnTimeout is false

    Parameters

    • units: number

      Number of units to consume

    • timeout: number

      Timeout in milliseconds. The resulting amount of time to sleep should not exceed this timeout. If the sleep time exceeds the timeout, the behavior should be according to consumeOnTimeout parameter, see below

    • consumeOnTimeout: boolean

      Defines how rate limiter behaves when the timeout is reached. If false, this call should result in error (use appropriate error class for your application) and the units should not be consumed. If true, the units should be consumed even when timeout is reached and the call completed successfully. In either case, if the computed wait time exceeds timeout, this call should still block for the amount of time equal to the timeout before either throwing an error or returning successfully (depending on the value of consumeOnTimeout). The driver uses consumeOnTimeout=true when calling consumeUnits after an operation completes successfully (see above), in which case the error should not be thrown and the result of the operation should be returned to the application

    Returns Promise<number>

  • Defines the behavior of the rate limiter when throttling error occurs. If throttling error has occured, this usually means that current rate limiter state does not correctly reflect the rate of incoming operations and needs to be adjusted. For example, you may remove any unused credits that were previously used to allow operations to proceed without waiting.

    Parameters

    Returns void

  • Configures rate limiter by setting its limit in units. Note that this method is called both when rate limiter is configured for the first time and also if/when table limits change, so it may need to account for the current state due to outstanding operations already rate-limited by this instance, however there is no need to change state or sleep time of these outstanding operations and the new limit only needs to apply to the new operations issued.

    Parameters

    • limit: number

      Limit in units

    Returns void

Generated using TypeDoc