* feat(errors): Add specialized timeout error types for maintenance scenarios - Added `SocketTimeoutDuringMaintananceError`, a subclass of `TimeoutError`, to handle socket timeouts during maintenance. - Added `CommandTimeoutDuringMaintenanceError`, another subclass of `TimeoutError`, to address command write timeouts during maintenance. * feat(linked-list): Add EmptyAwareSinglyLinkedList and enhance DoublyLinkedList functionality - Introduced `EmptyAwareSinglyLinkedList`, a subclass of `SinglyLinkedList` that emits an `empty` event when the list becomes empty due to `reset`, `shift`, or `remove` operations. - Added `nodes()` iterator method to `DoublyLinkedList` for iterating over nodes directly. - Enhanced unit tests for `DoublyLinkedList` and `SinglyLinkedList` to cover edge cases and new functionality. - Added comprehensive tests for `EmptyAwareSinglyLinkedList` to validate `empty` event emission under various scenarios. - Improved code formatting and consistency. * refactor(commands-queue): Improve push notification handling - Replaced `setInvalidateCallback` with a more flexible `addPushHandler` method, allowing multiple handlers for push notifications. - Introduced the `PushHandler` type to standardize push notification processing. - Refactored `RedisCommandsQueue` to use a `#pushHandlers` array, enabling dynamic and modular handling of push notifications. - Updated `RedisClient` to leverage the new handler mechanism for `invalidate` push notifications, simplifying and decoupling logic. * feat(commands-queue): Add method to wait for in-flight commands to complete - Introduced `waitForInflightCommandsToComplete` method to asynchronously wait for all in-flight commands to finish processing. - Utilized the `empty` event from `#waitingForReply` to signal when all commands have been completed. * feat(commands-queue): Introduce maintenance mode support for commands-queue - Added `#maintenanceCommandTimeout` and `setMaintenanceCommandTimeout` method to dynamically adjust command timeouts during maintenance * refator(client): Extract socket event listener setup into helper method * refactor(socket): Add maintenance mode support and dynamic timeout handling - Added `#maintenanceTimeout` and `setMaintenanceTimeout` method to dynamically adjust socket timeouts during maintenance. * feat(client): Add Redis Enterprise maintenance configuration options - Added `maintPushNotifications` option to control how the client handles Redis Enterprise maintenance push notifications (`disabled`, `enabled`, `au to`). - Added `maintMovingEndpointType` option to specify the endpoint type for reconnecting during a MOVING notification (`auto`, `internal-ip`, `external-ip`, etc.). - Added `maintRelaxedCommandTimeout` option to define a relaxed timeout for commands during maintenance. - Added `maintRelaxedSocketTimeout` option to define a relaxed timeout for the socket during maintenance. - Enforced RESP3 requirement for maintenance-related features (`maintPushNotifications`). * feat(client): Add socket helpers and pause mechanism - Introduced `#paused` flag with corresponding `_pause` and `_unpause` methods to temporarily halt writing commands to the socket during maintenance windows. - Updated `#write` method to respect the `#paused` flag, preventing new commands from being written during maintenance. - Added `_ejectSocket` method to safely detach from and return the current socket - Added `_insertSocket` method to receive and start using a new socket * feat(client): Add Redis Enterprise maintenance handling capabilities - Introduced `EnterpriseMaintenanceManager` to manage Redis Enterprise maintenance events and push notifications. - Integrated `EnterpriseMaintenanceManager` into `RedisClient` to handle maintenance push notifications and manage socket transitions. - Implemented graceful handling of MOVING, MIGRATING, and FAILOVER push notifications, including socket replacement and timeout adjustments. * test: add E2E test infrastructure for Redis maintenance scenarios * test: add E2E tests for Redis Enterprise maintenance timeout handling (#3) * test: add connection handoff test --------- Co-authored-by: Pavel Pashov <pavel.pashov@redis.com> Co-authored-by: Pavel Pashov <60297174+PavelPashov@users.noreply.github.com>
@redis/time-series
This package provides support for the RedisTimeSeries module, which adds a time series data structure to Redis.
Should be used with redis/@redis/client.
⚠️ To use these extra commands, your Redis server must have the RedisTimeSeries module installed.
Usage
For a complete example, see time-series.js in the Node Redis examples folder.
Creating Time Series data structure in Redis
The TS.CREATE command creates a new time series.
Here, we'll create a new time series "temperature":
import { createClient } from 'redis';
import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@redis/time-series';
...
const created = await client.ts.create('temperature', {
RETENTION: 86400000, // 1 day in milliseconds
ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression - When not specified, the option is set to COMPRESSED
DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, // No duplicates - When not specified: set to the global DUPLICATE_POLICY configuration of the database (which by default, is BLOCK).
});
if (created === 'OK') {
console.log('Created timeseries.');
} else {
console.log('Error creating timeseries :(');
process.exit(1);
}
Adding new value to a Time Series data structure in Redis
With RedisTimeSeries, we can add a single value to time series data structure using the TS.ADD command and if we would like to add multiple values we can use the TS.MADD command.
let value = Math.floor(Math.random() * 1000) + 1; // Random data point value
let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00
let num = 0;
while (num < 10000) {
// Add a new value to the timeseries, providing our own timestamp:
// https://redis.io/commands/ts.add/
await client.ts.add('temperature', currentTimestamp, value);
console.log(`Added timestamp ${currentTimestamp}, value ${value}.`);
num += 1;
value = Math.floor(Math.random() * 1000) + 1; // Get another random value
currentTimestamp += 1000; // Move on one second.
}
// Add multiple values to the timeseries in round trip to the server:
// https://redis.io/commands/ts.madd/
const response = await client.ts.mAdd([{
key: 'temperature',
timestamp: currentTimestamp + 60000,
value: Math.floor(Math.random() * 1000) + 1
}, {
key: 'temperature',
timestamp: currentTimestamp + 120000,
value: Math.floor(Math.random() * 1000) + 1
}]);
Retrieving Time Series data from Redis
With RedisTimeSeries, we can retrieve the time series data using the TS.RANGE command by passing the criteria as follows:
// Query the timeseries with TS.RANGE:
// https://redis.io/commands/ts.range/
const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00
const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00
const rangeResponse = await client.ts.range('temperature', fromTimestamp, toTimestamp, {
// Group into 10 second averages.
AGGREGATION: {
type: TimeSeriesAggregationType.AVERAGE,
timeBucket: 10000
}
});
console.log('RANGE RESPONSE:');
// rangeResponse looks like:
// [
// { timestamp: 1640995200000, value: 356.8 },
// { timestamp: 1640995210000, value: 534.8 },
// { timestamp: 1640995220000, value: 481.3 },
// { timestamp: 1640995230000, value: 437 },
// { timestamp: 1640995240000, value: 507.3 },
// { timestamp: 1640995250000, value: 581.2 },
// { timestamp: 1640995260000, value: 600 }
// ]
Altering Time Series data Stored in Redis
RedisTimeSeries includes commands that can update values in a time series data structure.
Using the TS.ALTER command, we can update time series retention like this:
// https://redis.io/commands/ts.alter/
const alterResponse = await client.ts.alter('temperature', {
RETENTION: 0 // Keep the entries forever
});
Retrieving Information about the timeseries Stored in Redis
RedisTimeSeries also includes commands that can help to view the information on the state of a time series.
Using the TS.INFO command, we can view timeseries information like this:
// Get some information about the state of the timeseries.
// https://redis.io/commands/ts.info/
const tsInfo = await client.ts.info('temperature');
// tsInfo looks like this:
// {
// totalSamples: 1440,
// memoryUsage: 28904,
// firstTimestamp: 1641508920000,
// lastTimestamp: 1641595320000,
// retentionTime: 86400000,
// chunkCount: 7,
// chunkSize: 4096,
// chunkType: 'uncompressed',
// duplicatePolicy: 'block',
// labels: [],
// sourceKey: null,
// rules: []
// }