You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Fix typo and improve Sentinel docs (#2931)
This commit is contained in:
@@ -14,7 +14,7 @@ const sentinel = await createSentinel({
|
|||||||
port: 1234
|
port: 1234
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
.on('error', err => console.error('Redis Sentinel Error', err));
|
.on('error', err => console.error('Redis Sentinel Error', err))
|
||||||
.connect();
|
.connect();
|
||||||
|
|
||||||
await sentinel.set('key', 'value');
|
await sentinel.set('key', 'value');
|
||||||
@@ -26,16 +26,19 @@ In the above example, we configure the sentinel object to fetch the configuratio
|
|||||||
|
|
||||||
## `createSentinel` configuration
|
## `createSentinel` configuration
|
||||||
|
|
||||||
| Property | Default | Description |
|
| Property | Default | Description |
|
||||||
|-----------------------|---------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|----------------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| name | | The sentinel identifier for a particular database cluster |
|
| name | | The sentinel identifier for a particular database cluster |
|
||||||
| sentinelRootNodes | | An array of root nodes that are part of the sentinel cluster, which will be used to get the topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the sentinel configuration from the server |
|
| sentinelRootNodes | | An array of root nodes that are part of the sentinel cluster, which will be used to get the topology. Each element in the array is a client configuration object. There is no need to specify every node in the cluster: 3 should be enough to reliably connect and obtain the sentinel configuration from the server |
|
||||||
| maxCommandRediscovers | `16` | The maximum number of times a command will retry due to topology changes. |
|
| maxCommandRediscovers | `16` | The maximum number of times a command will retry due to topology changes. |
|
||||||
| nodeClientOptions | | The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with |
|
| nodeClientOptions | | The configuration values for every node in the cluster. Use this for example when specifying an ACL user to connect with |
|
||||||
| sentinelClientOptions | | The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with |
|
| sentinelClientOptions | | The configuration values for every sentinel in the cluster. Use this for example when specifying an ACL user to connect with |
|
||||||
| masterPoolSize | `1` | The number of clients connected to the master node |
|
| masterPoolSize | `1` | The number of clients connected to the master node |
|
||||||
| replicaPoolSize | `0` | The number of clients connected to each replica node. When greater than 0, the client will distribute the load by executing read-only commands (such as `GET`, `GEOSEARCH`, etc.) across all the cluster nodes. |
|
| replicaPoolSize | `0` | The number of clients connected to each replica node. When greater than 0, the client will distribute the load by executing read-only commands (such as `GET`, `GEOSEARCH`, etc.) across all the cluster nodes. |
|
||||||
| reserveClient | `false` | When `true`, one client will be reserved for the sentinel object. When `false`, the sentinel object will wait for the first available client from the pool. |
|
| scanInterval | `10000` | Interval in milliseconds to periodically scan for changes in the sentinel topology. The client will query the sentinel for changes at this interval. |
|
||||||
|
| passthroughClientErrorEvents | `false` | When `true`, error events from client instances inside the sentinel will be propagated to the sentinel instance. This allows handling all client errors through a single error handler on the sentinel instance. |
|
||||||
|
| reserveClient | `false` | When `true`, one client will be reserved for the sentinel object. When `false`, the sentinel object will wait for the first available client from the pool. |
|
||||||
|
|
||||||
## PubSub
|
## PubSub
|
||||||
|
|
||||||
It supports PubSub via the normal mechanisms, including migrating the listeners if the node they are connected to goes down.
|
It supports PubSub via the normal mechanisms, including migrating the listeners if the node they are connected to goes down.
|
||||||
@@ -60,7 +63,7 @@ createSentinel({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
In addition, it also provides the ability have a pool of clients connected to the replica nodes, and to direct all read-only commands to them:
|
In addition, it also provides the ability have a pool of clients connected to the replica nodes, and to direct all read-only commands to them:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
createSentinel({
|
createSentinel({
|
||||||
@@ -85,9 +88,9 @@ const result = await sentinel.use(async client => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
`.getMasterClientLease()`
|
`.acquire()`
|
||||||
```javascript
|
```javascript
|
||||||
const clientLease = await sentinel.getMasterClientLease();
|
const clientLease = await sentinel.acquire();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await clientLease.watch('key');
|
await clientLease.watch('key');
|
||||||
|
@@ -134,7 +134,7 @@ describe(`test with scripts`, () => {
|
|||||||
}, GLOBAL.SENTINEL.WITH_SCRIPT);
|
}, GLOBAL.SENTINEL.WITH_SCRIPT);
|
||||||
|
|
||||||
testUtils.testWithClientSentinel('with script multi', async sentinel => {
|
testUtils.testWithClientSentinel('with script multi', async sentinel => {
|
||||||
const reply = await sentinel.multi().set('key', 2).square('key').exec();
|
const reply = await sentinel.multi().set('key', 2).square('key').exec();
|
||||||
assert.deepEqual(reply, ['OK', 4]);
|
assert.deepEqual(reply, ['OK', 4]);
|
||||||
}, GLOBAL.SENTINEL.WITH_SCRIPT);
|
}, GLOBAL.SENTINEL.WITH_SCRIPT);
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ describe(`test with scripts`, () => {
|
|||||||
);
|
);
|
||||||
}, GLOBAL.SENTINEL.WITH_SCRIPT)
|
}, GLOBAL.SENTINEL.WITH_SCRIPT)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe(`test with functions`, () => {
|
describe(`test with functions`, () => {
|
||||||
testUtils.testWithClientSentinel('with function', async sentinel => {
|
testUtils.testWithClientSentinel('with function', async sentinel => {
|
||||||
@@ -178,14 +178,14 @@ describe(`test with functions`, () => {
|
|||||||
MATH_FUNCTION.code,
|
MATH_FUNCTION.code,
|
||||||
{ REPLACE: true }
|
{ REPLACE: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
const reply = await sentinel.use(
|
const reply = await sentinel.use(
|
||||||
async (client: any) => {
|
async (client: any) => {
|
||||||
await client.set('key', '2');
|
await client.set('key', '2');
|
||||||
return client.math.square('key');
|
return client.math.square('key');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.equal(reply, 4);
|
assert.equal(reply, 4);
|
||||||
}, GLOBAL.SENTINEL.WITH_FUNCTION);
|
}, GLOBAL.SENTINEL.WITH_FUNCTION);
|
||||||
});
|
});
|
||||||
@@ -216,7 +216,7 @@ describe(`test with replica pool size 1`, () => {
|
|||||||
testUtils.testWithClientSentinel('client lease', async sentinel => {
|
testUtils.testWithClientSentinel('client lease', async sentinel => {
|
||||||
sentinel.on("error", () => { });
|
sentinel.on("error", () => { });
|
||||||
|
|
||||||
const clientLease = await sentinel.aquire();
|
const clientLease = await sentinel.acquire();
|
||||||
clientLease.set('x', 456);
|
clientLease.set('x', 456);
|
||||||
|
|
||||||
let matched = false;
|
let matched = false;
|
||||||
@@ -243,7 +243,7 @@ describe(`test with replica pool size 1`, () => {
|
|||||||
return await client.get("x");
|
return await client.get("x");
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
await sentinel.set("x", 1);
|
await sentinel.set("x", 1);
|
||||||
assert.equal(await promise, null);
|
assert.equal(await promise, null);
|
||||||
}, GLOBAL.SENTINEL.WITH_REPLICA_POOL_SIZE_1);
|
}, GLOBAL.SENTINEL.WITH_REPLICA_POOL_SIZE_1);
|
||||||
@@ -276,7 +276,7 @@ describe(`test with masterPoolSize 2, reserve client true`, () => {
|
|||||||
assert.equal(await promise2, "2");
|
assert.equal(await promise2, "2");
|
||||||
}, Object.assign(GLOBAL.SENTINEL.WITH_RESERVE_CLIENT_MASTER_POOL_SIZE_2, {skipTest: true}));
|
}, Object.assign(GLOBAL.SENTINEL.WITH_RESERVE_CLIENT_MASTER_POOL_SIZE_2, {skipTest: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`test with masterPoolSize 2`, () => {
|
describe(`test with masterPoolSize 2`, () => {
|
||||||
testUtils.testWithClientSentinel('multple clients', async sentinel => {
|
testUtils.testWithClientSentinel('multple clients', async sentinel => {
|
||||||
sentinel.on("error", () => { });
|
sentinel.on("error", () => { });
|
||||||
@@ -313,14 +313,14 @@ describe(`test with masterPoolSize 2`, () => {
|
|||||||
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
|
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
|
||||||
|
|
||||||
testUtils.testWithClientSentinel('lease - watch - clean', async sentinel => {
|
testUtils.testWithClientSentinel('lease - watch - clean', async sentinel => {
|
||||||
const leasedClient = await sentinel.aquire();
|
const leasedClient = await sentinel.acquire();
|
||||||
await leasedClient.set('x', 1);
|
await leasedClient.set('x', 1);
|
||||||
await leasedClient.watch('x');
|
await leasedClient.watch('x');
|
||||||
assert.deepEqual(await leasedClient.multi().get('x').exec(), ['1'])
|
assert.deepEqual(await leasedClient.multi().get('x').exec(), ['1'])
|
||||||
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
|
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
|
||||||
|
|
||||||
testUtils.testWithClientSentinel('lease - watch - dirty', async sentinel => {
|
testUtils.testWithClientSentinel('lease - watch - dirty', async sentinel => {
|
||||||
const leasedClient = await sentinel.aquire();
|
const leasedClient = await sentinel.acquire();
|
||||||
await leasedClient.set('x', 1);
|
await leasedClient.set('x', 1);
|
||||||
await leasedClient.watch('x');
|
await leasedClient.watch('x');
|
||||||
await leasedClient.set('x', 2);
|
await leasedClient.set('x', 2);
|
||||||
@@ -328,11 +328,11 @@ describe(`test with masterPoolSize 2`, () => {
|
|||||||
await assert.rejects(leasedClient.multi().get('x').exec(), new WatchError());
|
await assert.rejects(leasedClient.multi().get('x').exec(), new WatchError());
|
||||||
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
|
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// TODO: Figure out how to modify the test utils
|
// TODO: Figure out how to modify the test utils
|
||||||
// so it would have fine grained controll over
|
// so it would have fine grained controll over
|
||||||
// sentinel
|
// sentinel
|
||||||
// it should somehow replicate the `SentinelFramework` object functionallities
|
// it should somehow replicate the `SentinelFramework` object functionallities
|
||||||
async function steadyState(frame: SentinelFramework) {
|
async function steadyState(frame: SentinelFramework) {
|
||||||
let checkedMaster = false;
|
let checkedMaster = false;
|
||||||
@@ -439,7 +439,7 @@ describe.skip('legacy tests', () => {
|
|||||||
sentinel.on('error', () => { });
|
sentinel.on('error', () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this!.currentTest!.state === 'failed') {
|
if (this!.currentTest!.state === 'failed') {
|
||||||
console.log(`longest event loop blocked delta: ${longestDelta}`);
|
console.log(`longest event loop blocked delta: ${longestDelta}`);
|
||||||
console.log(`longest event loop blocked in failing test: ${longestTestDelta}`);
|
console.log(`longest event loop blocked in failing test: ${longestTestDelta}`);
|
||||||
console.log("trace:");
|
console.log("trace:");
|
||||||
@@ -454,7 +454,7 @@ describe.skip('legacy tests', () => {
|
|||||||
frame.sentinelMaster(),
|
frame.sentinelMaster(),
|
||||||
frame.sentinelReplicas()
|
frame.sentinelReplicas()
|
||||||
])
|
])
|
||||||
|
|
||||||
console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`);
|
console.log(`sentinel sentinels:\n${JSON.stringify(results[0], undefined, '\t')}`);
|
||||||
console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`);
|
console.log(`sentinel master:\n${JSON.stringify(results[1], undefined, '\t')}`);
|
||||||
console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`);
|
console.log(`sentinel replicas:\n${JSON.stringify(results[2], undefined, '\t')}`);
|
||||||
@@ -492,7 +492,7 @@ describe.skip('legacy tests', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// stops master to force sentinel to update
|
// stops master to force sentinel to update
|
||||||
it('stop master', async function () {
|
it('stop master', async function () {
|
||||||
this.timeout(60000);
|
this.timeout(60000);
|
||||||
|
|
||||||
@@ -538,8 +538,8 @@ describe.skip('legacy tests', () => {
|
|||||||
|
|
||||||
tracer.push("connected");
|
tracer.push("connected");
|
||||||
|
|
||||||
const client = await sentinel.aquire();
|
const client = await sentinel.acquire();
|
||||||
tracer.push("aquired lease");
|
tracer.push("acquired lease");
|
||||||
|
|
||||||
await client.set("x", 1);
|
await client.set("x", 1);
|
||||||
await client.watch("x");
|
await client.watch("x");
|
||||||
@@ -586,7 +586,7 @@ describe.skip('legacy tests', () => {
|
|||||||
await sentinel.connect();
|
await sentinel.connect();
|
||||||
tracer.push("connected");
|
tracer.push("connected");
|
||||||
|
|
||||||
const client = await sentinel.aquire();
|
const client = await sentinel.acquire();
|
||||||
tracer.push("got leased client");
|
tracer.push("got leased client");
|
||||||
await client.set("x", 1);
|
await client.set("x", 1);
|
||||||
await client.watch("x");
|
await client.watch("x");
|
||||||
@@ -965,10 +965,10 @@ describe.skip('legacy tests', () => {
|
|||||||
tracer.push("adding node");
|
tracer.push("adding node");
|
||||||
await frame.addNode();
|
await frame.addNode();
|
||||||
tracer.push("added node and waiting on added promise");
|
tracer.push("added node and waiting on added promise");
|
||||||
await nodeAddedPromise;
|
await nodeAddedPromise;
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -32,14 +32,30 @@ export class RedisSentinelClient<
|
|||||||
#internal: RedisSentinelInternal<M, F, S, RESP, TYPE_MAPPING>;
|
#internal: RedisSentinelInternal<M, F, S, RESP, TYPE_MAPPING>;
|
||||||
readonly _self: RedisSentinelClient<M, F, S, RESP, TYPE_MAPPING>;
|
readonly _self: RedisSentinelClient<M, F, S, RESP, TYPE_MAPPING>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the client connection is open
|
||||||
|
*
|
||||||
|
* @returns `true` if the client connection is open, `false` otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
get isOpen() {
|
get isOpen() {
|
||||||
return this._self.#internal.isOpen;
|
return this._self.#internal.isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the client connection is ready to accept commands
|
||||||
|
*
|
||||||
|
* @returns `true` if the client connection is ready, `false` otherwise
|
||||||
|
*/
|
||||||
get isReady() {
|
get isReady() {
|
||||||
return this._self.#internal.isReady;
|
return this._self.#internal.isReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the command options configured for this client
|
||||||
|
*
|
||||||
|
* @returns The command options for this client or `undefined` if none were set
|
||||||
|
*/
|
||||||
get commandOptions() {
|
get commandOptions() {
|
||||||
return this._self.#commandOptions;
|
return this._self.#commandOptions;
|
||||||
}
|
}
|
||||||
@@ -222,6 +238,16 @@ export class RedisSentinelClient<
|
|||||||
|
|
||||||
unwatch = this.UNWATCH;
|
unwatch = this.UNWATCH;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the client lease back to the pool
|
||||||
|
*
|
||||||
|
* After calling this method, the client instance should no longer be used as it
|
||||||
|
* will be returned to the client pool and may be given to other operations.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves when the client is ready to be reused, or undefined
|
||||||
|
* if the client was immediately ready
|
||||||
|
* @throws Error if the lease has already been released
|
||||||
|
*/
|
||||||
release() {
|
release() {
|
||||||
if (this._self.#clientInfo === undefined) {
|
if (this._self.#clientInfo === undefined) {
|
||||||
throw new Error('RedisSentinelClient lease already released');
|
throw new Error('RedisSentinelClient lease already released');
|
||||||
@@ -245,10 +271,20 @@ export default class RedisSentinel<
|
|||||||
#internal: RedisSentinelInternal<M, F, S, RESP, TYPE_MAPPING>;
|
#internal: RedisSentinelInternal<M, F, S, RESP, TYPE_MAPPING>;
|
||||||
#options: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>;
|
#options: RedisSentinelOptions<M, F, S, RESP, TYPE_MAPPING>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the sentinel connection is open
|
||||||
|
*
|
||||||
|
* @returns `true` if the sentinel connection is open, `false` otherwise
|
||||||
|
*/
|
||||||
get isOpen() {
|
get isOpen() {
|
||||||
return this._self.#internal.isOpen;
|
return this._self.#internal.isOpen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the sentinel connection is ready to accept commands
|
||||||
|
*
|
||||||
|
* @returns `true` if the sentinel connection is ready, `false` otherwise
|
||||||
|
*/
|
||||||
get isReady() {
|
get isReady() {
|
||||||
return this._self.#internal.isReady;
|
return this._self.#internal.isReady;
|
||||||
}
|
}
|
||||||
@@ -511,7 +547,28 @@ export default class RedisSentinel<
|
|||||||
|
|
||||||
pUnsubscribe = this.PUNSUBSCRIBE;
|
pUnsubscribe = this.PUNSUBSCRIBE;
|
||||||
|
|
||||||
async aquire(): Promise<RedisSentinelClientType<M, F, S, RESP, TYPE_MAPPING>> {
|
/**
|
||||||
|
* Acquires a master client lease for exclusive operations
|
||||||
|
*
|
||||||
|
* Used when multiple commands need to run on an exclusive client (for example, using `WATCH/MULTI/EXEC`).
|
||||||
|
* The returned client must be released after use with the `release()` method.
|
||||||
|
*
|
||||||
|
* @returns A promise that resolves to a Redis client connected to the master node
|
||||||
|
* @example
|
||||||
|
* ```javascript
|
||||||
|
* const clientLease = await sentinel.acquire();
|
||||||
|
*
|
||||||
|
* try {
|
||||||
|
* await clientLease.watch('key');
|
||||||
|
* const resp = await clientLease.multi()
|
||||||
|
* .get('key')
|
||||||
|
* .exec();
|
||||||
|
* } finally {
|
||||||
|
* clientLease.release();
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async acquire(): Promise<RedisSentinelClientType<M, F, S, RESP, TYPE_MAPPING>> {
|
||||||
const clientInfo = await this._self.#internal.getClientLease();
|
const clientInfo = await this._self.#internal.getClientLease();
|
||||||
return RedisSentinelClient.create(this._self.#options, this._self.#internal, clientInfo, this._self.#commandOptions);
|
return RedisSentinelClient.create(this._self.#options, this._self.#internal, clientInfo, this._self.#commandOptions);
|
||||||
}
|
}
|
||||||
@@ -641,6 +698,12 @@ class RedisSentinelInternal<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a client lease from the master client pool
|
||||||
|
*
|
||||||
|
* @returns A client info object or a promise that resolves to a client info object
|
||||||
|
* when a client becomes available
|
||||||
|
*/
|
||||||
getClientLease(): ClientInfo | Promise<ClientInfo> {
|
getClientLease(): ClientInfo | Promise<ClientInfo> {
|
||||||
const id = this.#masterClientQueue.shift();
|
const id = this.#masterClientQueue.shift();
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
@@ -650,6 +713,16 @@ class RedisSentinelInternal<
|
|||||||
return this.#masterClientQueue.wait().then(id => ({ id }));
|
return this.#masterClientQueue.wait().then(id => ({ id }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases a client lease back to the pool
|
||||||
|
*
|
||||||
|
* If the client was used for a transaction that might have left it in a dirty state,
|
||||||
|
* it will be reset before being returned to the pool.
|
||||||
|
*
|
||||||
|
* @param clientInfo The client info object representing the client to release
|
||||||
|
* @returns A promise that resolves when the client is ready to be reused, or undefined
|
||||||
|
* if the client was immediately ready or no longer exists
|
||||||
|
*/
|
||||||
releaseClientLease(clientInfo: ClientInfo) {
|
releaseClientLease(clientInfo: ClientInfo) {
|
||||||
const client = this.#masterClients[clientInfo.id];
|
const client = this.#masterClients[clientInfo.id];
|
||||||
// client can be undefined if releasing in middle of a reconfigure
|
// client can be undefined if releasing in middle of a reconfigure
|
||||||
|
@@ -49,11 +49,17 @@ export interface RedisSentinelOptions<
|
|||||||
*/
|
*/
|
||||||
replicaPoolSize?: number;
|
replicaPoolSize?: number;
|
||||||
/**
|
/**
|
||||||
* TODO
|
* Interval in milliseconds to periodically scan for changes in the sentinel topology.
|
||||||
|
* The client will query the sentinel for changes at this interval.
|
||||||
|
*
|
||||||
|
* Default: 10000 (10 seconds)
|
||||||
*/
|
*/
|
||||||
scanInterval?: number;
|
scanInterval?: number;
|
||||||
/**
|
/**
|
||||||
* TODO
|
* When `true`, error events from client instances inside the sentinel will be propagated to the sentinel instance.
|
||||||
|
* This allows handling all client errors through a single error handler on the sentinel instance.
|
||||||
|
*
|
||||||
|
* Default: false
|
||||||
*/
|
*/
|
||||||
passthroughClientErrorEvents?: boolean;
|
passthroughClientErrorEvents?: boolean;
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user