Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,19 @@ This feature is useful when using Amazon ElastiCache instances with Auto-failove

On ElastiCache instances with Auto-failover enabled, `reconnectOnError` does not execute. Instead of returning a Valkey error, AWS closes all connections to the master endpoint until the new primary node is ready. iovalkey reconnects via `retryStrategy` instead of `reconnectOnError` after about a minute. On ElastiCache instances with Auto-failover enabled, test failover events with the `Failover primary` option in the AWS console.

## Family Fallback

iovalkey supports family fallback, which allows the client to attempt family 4 or 6 if initial connection fails. This is enabled by default. By default alternate is false, which means it will attempt to use the other family (if `options.family = undefined or 4, it will attempt to use 6, and vice versa). If alternate is true, it will alternate between family 4 and 6, possibly leading to a lot of error logs.

```js
const redis = new Redis({
familyFallback: {
enabled: true,
alternate: false,
},
});
```

## Connection Events

The Valkey instance will emit some events about the state of the connection to the Valkey server.
Expand Down
2 changes: 1 addition & 1 deletion docs/assets/search.js

Large diffs are not rendered by default.

1,542 changes: 764 additions & 778 deletions docs/classes/Cluster.html

Large diffs are not rendered by default.

1,546 changes: 766 additions & 780 deletions docs/classes/Redis.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion docs/index.html

Large diffs are not rendered by default.

1,522 changes: 754 additions & 768 deletions docs/interfaces/ChainableCommander.html

Large diffs are not rendered by default.

40 changes: 20 additions & 20 deletions docs/interfaces/ClusterOptions.html

Large diffs are not rendered by default.

61 changes: 37 additions & 24 deletions docs/interfaces/CommonRedisOptions.html

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion docs/interfaces/NatMap.html

This file was deleted.

2 changes: 1 addition & 1 deletion docs/interfaces/SentinelAddress.html

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/interfaces/SentinelConnectionOptions.html

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions lib/Redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import Commander from "./utils/Commander";
import { defaults, noop } from "./utils/lodash";
import Deque = require("denque");
import { updateTriedFamilies, validateFamilyFallbackOptions } from "./redis/event_handler";
const debug = Debug("redis");

type RedisStatus =
Expand Down Expand Up @@ -179,6 +180,8 @@

this.connectionEpoch += 1;
this.setStatus("connecting");
validateFamilyFallbackOptions(this);
updateTriedFamilies(this, this.options.family as 4 | 6);

const { options } = this;

Expand Down Expand Up @@ -559,35 +562,35 @@
});
}

scanStream(options?: ScanStreamOptions) {

Check warning on line 565 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 565 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 565 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 565 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 565 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member scanStream should be declared before all private instance method definitions

Check warning on line 565 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member scanStream should be declared before all private instance method definitions
return this.createScanStream("scan", { options });
}

scanBufferStream(options?: ScanStreamOptions) {

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member scanBufferStream should be declared before all private instance method definitions

Check warning on line 569 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member scanBufferStream should be declared before all private instance method definitions
return this.createScanStream("scanBuffer", { options });
}

sscanStream(key: string, options?: ScanStreamOptions) {

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member sscanStream should be declared before all private instance method definitions

Check warning on line 573 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member sscanStream should be declared before all private instance method definitions
return this.createScanStream("sscan", { key, options });
}

sscanBufferStream(key: string, options?: ScanStreamOptions) {

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member sscanBufferStream should be declared before all private instance method definitions

Check warning on line 577 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member sscanBufferStream should be declared before all private instance method definitions
return this.createScanStream("sscanBuffer", { key, options });
}

hscanStream(key: string, options?: ScanStreamOptions) {

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member hscanStream should be declared before all private instance method definitions

Check warning on line 581 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member hscanStream should be declared before all private instance method definitions
return this.createScanStream("hscan", { key, options });
}

hscanBufferStream(key: string, options?: ScanStreamOptions) {

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member hscanBufferStream should be declared before all private instance method definitions

Check warning on line 585 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member hscanBufferStream should be declared before all private instance method definitions
return this.createScanStream("hscanBuffer", { key, options });
}

zscanStream(key: string, options?: ScanStreamOptions) {

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member zscanStream should be declared before all private instance method definitions

Check warning on line 589 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member zscanStream should be declared before all private instance method definitions
return this.createScanStream("zscan", { key, options });
}

zscanBufferStream(key: string, options?: ScanStreamOptions) {

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member zscanBufferStream should be declared before all private instance method definitions

Check warning on line 593 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member zscanBufferStream should be declared before all private instance method definitions
return this.createScanStream("zscanBuffer", { key, options });
}

Expand All @@ -596,7 +599,7 @@
*
* @ignore
*/
silentEmit(eventName: string, arg?: unknown): boolean {

Check warning on line 602 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 602 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 602 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 602 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 602 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member silentEmit should be declared before all private instance method definitions

Check warning on line 602 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member silentEmit should be declared before all private instance method definitions
let error: unknown;
if (eventName === "error") {
error = arg;
Expand Down Expand Up @@ -631,7 +634,7 @@
/**
* @ignore
*/
recoverFromFatalError(

Check warning on line 637 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (22.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 637 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (20.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 637 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-valkey (18.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 637 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (22.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 637 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (18.x)

Member recoverFromFatalError should be declared before all private instance method definitions

Check warning on line 637 in lib/Redis.ts

View workflow job for this annotation

GitHub Actions / test-redis (20.x)

Member recoverFromFatalError should be declared before all private instance method definitions
_commandError: Error,
err: Error,
options: FlushQueueOptions
Expand Down
33 changes: 33 additions & 0 deletions lib/redis/RedisOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ import { StandaloneConnectionOptions } from "../connectors/StandaloneConnector";

export type ReconnectOnError = (err: Error) => boolean | 1 | 2;

export type FamilyFallback = {
/**
* If true, the client will attempt family 4 and 6 once.
* If false, the client will only attempt to connect using options.family or default (family: 4).
* @default true
*/
enabled?: boolean;
/**
* If true, the client will alternate between family 4 and 6.
* If false, the client will attempt family 4 and 6 once.
* @default false
*/
alternate?: boolean;
_initialFamily?: 4 | 6;
_triedFamilyFour?: boolean;
_triedFamilySix?: boolean;
};

export interface CommonRedisOptions extends CommanderOptions {
Connector?: ConnectorConstructor;
retryStrategy?: (times: number) => number | void | null;
Expand Down Expand Up @@ -188,6 +206,21 @@ export interface CommonRedisOptions extends CommanderOptions {
string,
{ lua: string; numberOfKeys?: number; readOnly?: boolean }
>;

/**
* Allows the client to attempt family 4 or 6 if initial connection fails.
* @example
* ```js
* const redis = new Redis({
* familyFallback: {
* enabled: true,
* alternate: false,
* },
* });
* ```
* @default undefined
*/
familyFallback?: FamilyFallback;
}

export type RedisOptions = CommonRedisOptions &
Expand Down
77 changes: 77 additions & 0 deletions lib/redis/event_handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import Deque = require("denque");
import { AbortError } from "redis-errors";
import Command from "../Command";
import { MaxRetriesPerRequestError } from "../errors";
import Redis from "../Redis";
import { CommandItem, Respondable } from "../types";
import { FamilyFallback } from "./RedisOptions";
import { Debug, noop, CONNECTION_CLOSED_ERROR_MSG } from "../utils";
import DataHandler from "../DataHandler";

Expand Down Expand Up @@ -198,6 +200,7 @@ export function closeHandler(self) {
self.setStatus("reconnecting", retryDelay);
self.reconnectTimeout = setTimeout(function () {
self.reconnectTimeout = null;
handleFamilyFallbackOptions(self);
self.connect().catch(noop);
}, retryDelay);

Expand Down Expand Up @@ -337,3 +340,77 @@ export function readyHandler(self) {
}
};
}

/**
* Handles the family fallback logic for reconnection attempts
* @param self - The Redis client instance
*/
export function handleFamilyFallbackOptions(self: Redis): void {
validateFamilyFallbackOptions(self);
const fallback = self.options.familyFallback;
if (!fallback.enabled) {
return;
}

const currentFamily = self.options.family as 4 | 6;
const nextFamily = getNextFamilyToTry(fallback, currentFamily);

self.options.family = nextFamily;
updateTriedFamilies(self, nextFamily);
}

/**
* Validates and normalizes the family fallback options
* @param self - The Redis client instance
* @throws {Error} If validation fails
*/
export function validateFamilyFallbackOptions(self: Redis) {
const { familyFallback: options } = self.options;
const initialFamily =
options?._initialFamily ?? (self.options.family as 4 | 6);

const fallback: FamilyFallback = {
enabled: true,
alternate: false,
_initialFamily: initialFamily,
_triedFamilyFour: false,
_triedFamilySix: false,
...options,
};
self.options.familyFallback = fallback;
}

/**
* Determines the next IP family to try based on the fallback strategy
* @param fallback - The family fallback configuration
* @param currentFamily - The current IP family (4 or 6)
* @returns The next IP family to try (4 or 6)
*/
export function getNextFamilyToTry(
fallback: FamilyFallback,
currentFamily: 4 | 6
): 4 | 6 {
if (fallback.alternate) {
return currentFamily === 4 ? 6 : 4;
}

if (!fallback._triedFamilyFour) return 4;
if (!fallback._triedFamilySix) return 6;

return fallback._initialFamily;
}

/**
* Updates the tried families tracking in the fallback options
* @param self - The Redis client instance
* @param family - The family that was just tried (4 or 6)
*/
export function updateTriedFamilies(self: Redis, family: 4 | 6): void {
const { familyFallback } = self.options;

if (family === 4 && !familyFallback._triedFamilyFour) {
familyFallback._triedFamilyFour = true;
} else if (family === 6 && !familyFallback._triedFamilySix) {
familyFallback._triedFamilySix = true;
}
}
Loading
Loading