js-redis-server 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -35
- package/dist/cluster.d.ts +7 -1
- package/dist/cluster.d.ts.map +1 -1
- package/dist/cluster.js +103 -7
- package/dist/cluster.js.map +1 -1
- package/dist/commands/cluster.d.ts +3 -0
- package/dist/commands/cluster.d.ts.map +1 -1
- package/dist/commands/cluster.js +63 -20
- package/dist/commands/cluster.js.map +1 -1
- package/dist/commands/command.js +27 -18
- package/dist/commands/command.js.map +1 -1
- package/dist/commands/config.d.ts +9 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +142 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/connection.d.ts.map +1 -1
- package/dist/commands/connection.js +73 -9
- package/dist/commands/connection.js.map +1 -1
- package/dist/commands/hashes.d.ts.map +1 -1
- package/dist/commands/hashes.js +5 -7
- package/dist/commands/hashes.js.map +1 -1
- package/dist/commands/index.d.ts +5 -4
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +25 -4
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/keys.d.ts +19 -0
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +112 -5
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/lists.d.ts +13 -0
- package/dist/commands/lists.d.ts.map +1 -1
- package/dist/commands/lists.js +140 -26
- package/dist/commands/lists.js.map +1 -1
- package/dist/commands/scripts.d.ts.map +1 -1
- package/dist/commands/scripts.js +9 -12
- package/dist/commands/scripts.js.map +1 -1
- package/dist/commands/sets.d.ts +1 -0
- package/dist/commands/sets.d.ts.map +1 -1
- package/dist/commands/sets.js +33 -8
- package/dist/commands/sets.js.map +1 -1
- package/dist/commands/streams.d.ts +63 -0
- package/dist/commands/streams.d.ts.map +1 -0
- package/dist/commands/streams.js +504 -0
- package/dist/commands/streams.js.map +1 -0
- package/dist/commands/strings.d.ts +5 -0
- package/dist/commands/strings.d.ts.map +1 -1
- package/dist/commands/strings.js +20 -2
- package/dist/commands/strings.js.map +1 -1
- package/dist/commands/zsets.d.ts +13 -3
- package/dist/commands/zsets.d.ts.map +1 -1
- package/dist/commands/zsets.js +174 -30
- package/dist/commands/zsets.js.map +1 -1
- package/dist/core/client-session.d.ts +114 -2
- package/dist/core/client-session.d.ts.map +1 -1
- package/dist/core/client-session.js +181 -12
- package/dist/core/client-session.js.map +1 -1
- package/dist/core/command-executor.d.ts +79 -0
- package/dist/core/command-executor.d.ts.map +1 -1
- package/dist/core/command-executor.js +130 -3
- package/dist/core/command-executor.js.map +1 -1
- package/dist/core/command-schema.d.ts +3 -0
- package/dist/core/command-schema.d.ts.map +1 -1
- package/dist/core/command-schema.js +2 -1
- package/dist/core/command-schema.js.map +1 -1
- package/dist/core/execution-policies/auth-policy.d.ts +10 -0
- package/dist/core/execution-policies/auth-policy.d.ts.map +1 -0
- package/dist/core/execution-policies/auth-policy.js +39 -0
- package/dist/core/execution-policies/auth-policy.js.map +1 -0
- package/dist/core/execution-policies/cluster-policy.d.ts.map +1 -1
- package/dist/core/execution-policies/cluster-policy.js +8 -2
- package/dist/core/execution-policies/cluster-policy.js.map +1 -1
- package/dist/core/execution-policies/index.d.ts +1 -0
- package/dist/core/execution-policies/index.d.ts.map +1 -1
- package/dist/core/execution-policies/index.js +3 -1
- package/dist/core/execution-policies/index.js.map +1 -1
- package/dist/core/execution-policies/transaction-policy.d.ts.map +1 -1
- package/dist/core/execution-policies/transaction-policy.js +0 -4
- package/dist/core/execution-policies/transaction-policy.js.map +1 -1
- package/dist/core/lua-runtime.d.ts +1 -1
- package/dist/core/lua-runtime.d.ts.map +1 -1
- package/dist/core/lua-runtime.js +26 -34
- package/dist/core/lua-runtime.js.map +1 -1
- package/dist/core/redis-context.d.ts +6 -1
- package/dist/core/redis-context.d.ts.map +1 -1
- package/dist/core/redis-context.js.map +1 -1
- package/dist/core/redis-error.d.ts +55 -1
- package/dist/core/redis-error.d.ts.map +1 -1
- package/dist/core/redis-error.js +105 -3
- package/dist/core/redis-error.js.map +1 -1
- package/dist/core/redis-value.d.ts +4 -0
- package/dist/core/redis-value.d.ts.map +1 -1
- package/dist/core/redis-value.js +4 -0
- package/dist/core/redis-value.js.map +1 -1
- package/dist/core/resp-encoder.js +5 -0
- package/dist/core/resp-encoder.js.map +1 -1
- package/dist/core/transports/resp2/decoder.d.ts +5 -1
- package/dist/core/transports/resp2/decoder.d.ts.map +1 -1
- package/dist/core/transports/resp2/decoder.js +20 -8
- package/dist/core/transports/resp2/decoder.js.map +1 -1
- package/dist/core/transports/resp2/server.d.ts +3 -1
- package/dist/core/transports/resp2/server.d.ts.map +1 -1
- package/dist/core/transports/resp2/server.js +3 -0
- package/dist/core/transports/resp2/server.js.map +1 -1
- package/dist/core/transports/resp2/session-adapter.d.ts.map +1 -1
- package/dist/core/transports/resp2/session-adapter.js +8 -1
- package/dist/core/transports/resp2/session-adapter.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/state/cluster-topology.d.ts +6 -0
- package/dist/state/cluster-topology.d.ts.map +1 -1
- package/dist/state/cluster-topology.js +21 -0
- package/dist/state/cluster-topology.js.map +1 -1
- package/dist/state/data-types.d.ts +11 -0
- package/dist/state/data-types.d.ts.map +1 -1
- package/dist/state/data-types.js +12 -1
- package/dist/state/data-types.js.map +1 -1
- package/dist/state/database.d.ts +3 -1
- package/dist/state/database.d.ts.map +1 -1
- package/dist/state/database.js +6 -0
- package/dist/state/database.js.map +1 -1
- package/dist/state/keyspace.d.ts.map +1 -1
- package/dist/state/keyspace.js +1 -0
- package/dist/state/keyspace.js.map +1 -1
- package/dist/state/mutation-events.d.ts +1 -0
- package/dist/state/mutation-events.d.ts.map +1 -1
- package/dist/state/mutation-events.js.map +1 -1
- package/dist/state/server-state.d.ts +7 -0
- package/dist/state/server-state.d.ts.map +1 -1
- package/dist/state/server-state.js +2 -0
- package/dist/state/server-state.js.map +1 -1
- package/package.json +5 -5
|
@@ -4,56 +4,168 @@ import { type ClientSessionMode, type ParkHandler, type RedisClientSession, type
|
|
|
4
4
|
import { RedisResult } from './redis-result';
|
|
5
5
|
import type { RespVersion } from './resp-encoder';
|
|
6
6
|
import { type RedisTurnHandle, type RedisTurnQueue } from './turn-queue';
|
|
7
|
-
import type { RedisDatabase, RedisServerState } from '../state';
|
|
7
|
+
import type { RedisClusterNodeRole, RedisDatabase, RedisServerState } from '../state';
|
|
8
8
|
export type ClientSessionOptions = {
|
|
9
9
|
id?: string;
|
|
10
10
|
server: RedisServerState;
|
|
11
11
|
executor: CommandExecutor;
|
|
12
12
|
database?: number;
|
|
13
|
+
nodeRole?: RedisClusterNodeRole;
|
|
13
14
|
signal?: AbortSignal;
|
|
14
15
|
park?: ParkHandler;
|
|
15
16
|
turnQueue?: RedisTurnQueue;
|
|
16
17
|
};
|
|
18
|
+
/**
|
|
19
|
+
* Mutable view of the turn currently held by an in-flight command. A blocking
|
|
20
|
+
* command suspends its turn while parked and is later resumed on a *new* handle,
|
|
21
|
+
* so the holder must be able to both read the current turn and swap it.
|
|
22
|
+
*/
|
|
17
23
|
type TurnAccess = {
|
|
18
24
|
get(): RedisTurnHandle | undefined;
|
|
19
25
|
set(turn: RedisTurnHandle | undefined): void;
|
|
20
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Per-connection server state and the concrete {@link RedisClientSession}.
|
|
29
|
+
*
|
|
30
|
+
* One instance exists per connected client and owns everything that is scoped
|
|
31
|
+
* to that connection rather than to the shared server:
|
|
32
|
+
* - the selected database index and the resolved {@link RedisDatabase};
|
|
33
|
+
* - the session {@link ClientSessionMode} (normal / transaction / subscribed);
|
|
34
|
+
* - the negotiated RESP protocol version (HELLO);
|
|
35
|
+
* - the cluster READONLY flag (replica reads via READONLY/READWRITE);
|
|
36
|
+
* - the MULTI command queue and its dirty bit;
|
|
37
|
+
* - WATCH key registrations for optimistic locking.
|
|
38
|
+
*
|
|
39
|
+
* Commands are serialized through a per-database turn queue so that, even though
|
|
40
|
+
* execution is async, only one command mutates a given database at a time. This
|
|
41
|
+
* is also what makes blocking commands (BLPOP, ...) cooperate instead of
|
|
42
|
+
* deadlock — see {@link createTurnAwareParkHandler}.
|
|
43
|
+
*/
|
|
21
44
|
export declare class ClientSession implements RedisClientSession {
|
|
22
45
|
private static nextId;
|
|
23
46
|
readonly id: string;
|
|
24
47
|
readonly server: RedisServerState;
|
|
48
|
+
/** Aborted when the connection closes; threaded into every command's ctx. */
|
|
25
49
|
readonly signal: AbortSignal;
|
|
26
50
|
private readonly executor;
|
|
27
51
|
private readonly signalSource?;
|
|
52
|
+
private readonly nodeRole?;
|
|
28
53
|
private readonly parkHandler;
|
|
29
54
|
private readonly turnQueueOverride?;
|
|
55
|
+
/**
|
|
56
|
+
* The turn handle of the command currently executing on this session,
|
|
57
|
+
* exposed so {@link executeTransaction} can hand the turn off to another
|
|
58
|
+
* database's queue when a queued SELECT switches databases mid-EXEC.
|
|
59
|
+
*/
|
|
60
|
+
private activeTurnAccess?;
|
|
30
61
|
private selectedDatabaseId;
|
|
31
62
|
private sessionMode;
|
|
32
63
|
private respVersion;
|
|
64
|
+
private authenticated;
|
|
65
|
+
/** Set by READONLY, cleared by READWRITE/RESET; lets a replica serve reads. */
|
|
66
|
+
private clusterReadOnlyMode;
|
|
67
|
+
/** Commands buffered between MULTI and EXEC, in submission order. */
|
|
33
68
|
private transactionPlans;
|
|
69
|
+
/** True once a queued command errored — forces EXEC to abort with EXECABORT. */
|
|
34
70
|
private transactionDirty;
|
|
71
|
+
/** Active WATCH registrations, keyed by `db:keyHex`. */
|
|
35
72
|
private readonly watches;
|
|
73
|
+
/** Subset of watched keys mutated since WATCH — non-empty fails the next EXEC. */
|
|
36
74
|
private readonly dirtyWatches;
|
|
37
75
|
constructor(options: ClientSessionOptions);
|
|
38
76
|
get selectedDatabase(): number;
|
|
39
77
|
get mode(): ClientSessionMode;
|
|
40
78
|
get protocolVersion(): RespVersion;
|
|
79
|
+
get clusterReadOnly(): boolean;
|
|
80
|
+
get isAuthenticated(): boolean;
|
|
81
|
+
setAuthenticated(value: boolean): void;
|
|
82
|
+
/** The live database object for the currently selected index. */
|
|
41
83
|
get db(): RedisDatabase;
|
|
42
84
|
setProtocolVersion(version: RespVersion): void;
|
|
85
|
+
/** Toggle replica read mode for this connection (READONLY / READWRITE). */
|
|
86
|
+
setClusterReadOnly(value: boolean): void;
|
|
43
87
|
selectDatabase(database: number): void;
|
|
88
|
+
/** MULTI: enter transaction mode. Redis forbids nesting. */
|
|
44
89
|
beginTransaction(): void;
|
|
90
|
+
/** Buffer one command while in MULTI; replies "+QUEUED" to the client. */
|
|
45
91
|
queueTransaction(plan: CommandPlan): void;
|
|
92
|
+
/**
|
|
93
|
+
* EXEC step 1: hand back the queued plans and atomically reset the session to
|
|
94
|
+
* normal mode (clearing the queue, dirty bit, and WATCHes). The caller is
|
|
95
|
+
* responsible for actually running the returned plans via
|
|
96
|
+
* {@link executeTransaction}. Returns an empty list if not in MULTI.
|
|
97
|
+
*/
|
|
46
98
|
drainTransaction(): CommandPlan[];
|
|
99
|
+
/** DISCARD: drop the queued commands and leave transaction mode. */
|
|
47
100
|
discardTransaction(): void;
|
|
101
|
+
/** Flag the transaction as poisoned (a queued command failed). No-op outside MULTI. */
|
|
48
102
|
markTransactionDirty(): void;
|
|
49
103
|
isTransactionDirty(): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* EXEC step 2: run the drained plans in order and collect their replies into a
|
|
106
|
+
* single array reply. Each command runs in its own fresh execution context.
|
|
107
|
+
* Streaming commands (SUBSCRIBE/MONITOR) are not permitted inside a
|
|
108
|
+
* transaction: the stream is closed immediately and replaced with an error
|
|
109
|
+
* entry so the array stays positionally aligned with the queued commands.
|
|
110
|
+
*/
|
|
50
111
|
executeTransaction(plans: readonly CommandPlan[]): Promise<RedisResult>;
|
|
112
|
+
/**
|
|
113
|
+
* Release the currently held serialization turn and acquire a fresh one on
|
|
114
|
+
* the selected database's queue. Called when a queued SELECT switches
|
|
115
|
+
* databases mid-EXEC so subsequent commands run under the correct
|
|
116
|
+
* per-database turn (see {@link executeTransaction}).
|
|
117
|
+
*
|
|
118
|
+
* No-op when a fixed turn-queue override is in force (a single queue already
|
|
119
|
+
* serializes every database) or when no managed turn is active.
|
|
120
|
+
*/
|
|
121
|
+
private handoffTurnToSelectedDb;
|
|
122
|
+
/**
|
|
123
|
+
* WATCH the given keys for optimistic locking. Each key is subscribed in the
|
|
124
|
+
* keyspace; any mutation flips its entry into {@link dirtyWatches}, which a
|
|
125
|
+
* subsequent EXEC checks via {@link isWatchDirty}. Already-watched keys are
|
|
126
|
+
* skipped so re-WATCHing is idempotent.
|
|
127
|
+
*/
|
|
51
128
|
watch(keys: readonly Buffer[]): void;
|
|
129
|
+
/** UNWATCH / cleanup: drop every keyspace subscription and clear dirty state. */
|
|
52
130
|
unwatch(): void;
|
|
131
|
+
/** True if any watched key was mutated since WATCH — EXEC must return nil. */
|
|
53
132
|
isWatchDirty(): boolean;
|
|
133
|
+
/**
|
|
134
|
+
* Public entry point for executing one client command.
|
|
135
|
+
*
|
|
136
|
+
* Acquires a turn on the database's turn queue before running, guaranteeing
|
|
137
|
+
* serialized access to the keyspace, and always releases it afterward. The
|
|
138
|
+
* acquired turn is exposed to the command via a turn-aware park handler so a
|
|
139
|
+
* blocking command can yield the turn while parked (see
|
|
140
|
+
* {@link createTurnAwareParkHandler}); `turn` is reassigned through the
|
|
141
|
+
* {@link TurnAccess} closure because suspending returns a *new* handle.
|
|
142
|
+
*/
|
|
54
143
|
execute(rawCommand: Buffer | string, rawArgs: readonly Buffer[]): Promise<ExecutorResult>;
|
|
55
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Build the {@link RedisExecutionContext} passed to a command's `execute`.
|
|
146
|
+
* When a turn is supplied (the normal client path) the context gets a
|
|
147
|
+
* turn-aware park handler so blocking commands release their turn while
|
|
148
|
+
* waiting; without one (e.g. nested transaction execution) the plain park
|
|
149
|
+
* handler is used.
|
|
150
|
+
*/
|
|
151
|
+
createExecutionContext(turnAccess?: TurnAccess, parkOverride?: ParkHandler): RedisExecutionContext;
|
|
152
|
+
/** Tear down the session: abort in-flight work and reset all per-connection state. */
|
|
56
153
|
close(): void;
|
|
154
|
+
/**
|
|
155
|
+
* Wrap the base park handler so that parking also yields the command's turn.
|
|
156
|
+
*
|
|
157
|
+
* Blocking commands (BLPOP, BRPOP, ...) must not hold the database turn while
|
|
158
|
+
* they wait, or no other client could ever produce the value that unblocks
|
|
159
|
+
* them — a deadlock. The flow:
|
|
160
|
+
* 1. Start the underlying park, capturing its eventual value.
|
|
161
|
+
* 2. Clear the local turn and call `turn.suspend(parked)`, which releases the
|
|
162
|
+
* turn back to the queue and resolves with a fresh turn once the park
|
|
163
|
+
* settles and this session is scheduled again.
|
|
164
|
+
* 3. Store the new turn (so `finally`/subsequent parks see it) and return the
|
|
165
|
+
* parked value.
|
|
166
|
+
*
|
|
167
|
+
* If there is no current turn, fall back to plain parking.
|
|
168
|
+
*/
|
|
57
169
|
private createTurnAwareParkHandler;
|
|
58
170
|
}
|
|
59
171
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-session.d.ts","sourceRoot":"","sources":["../../src/core/client-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAEhB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"client-session.d.ts","sourceRoot":"","sources":["../../src/core/client-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAEhB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC3B,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AACxE,OAAO,KAAK,EACV,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAEjB,MAAM,UAAU,CAAA;AAEjB,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,gBAAgB,CAAA;IACxB,QAAQ,EAAE,eAAe,CAAA;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,oBAAoB,CAAA;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,IAAI,CAAC,EAAE,WAAW,CAAA;IAClB,SAAS,CAAC,EAAE,cAAc,CAAA;CAC3B,CAAA;AAED;;;;GAIG;AACH,KAAK,UAAU,GAAG;IAChB,GAAG,IAAI,eAAe,GAAG,SAAS,CAAA;IAClC,GAAG,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,GAAG,IAAI,CAAA;CAC7C,CAAA;AAQD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,YAAW,kBAAkB;IACtD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAI;IAEzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAE5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAiB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAsB;IAChD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAgB;IACnD;;;;OAIG;IACH,OAAO,CAAC,gBAAgB,CAAC,CAAY;IACrC,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,aAAa,CAAQ;IAC7B,+EAA+E;IAC/E,OAAO,CAAC,mBAAmB,CAAQ;IACnC,qEAAqE;IACrE,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,gFAAgF;IAChF,OAAO,CAAC,gBAAgB,CAAQ;IAChC,wDAAwD;IACxD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoB;gBAErC,OAAO,EAAE,oBAAoB;IAmBzC,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED,IAAI,IAAI,IAAI,iBAAiB,CAE5B;IAED,IAAI,eAAe,IAAI,WAAW,CAEjC;IAED,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAItC,iEAAiE;IACjE,IAAI,EAAE,IAAI,aAAa,CAEtB;IAED,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAI9C,2EAA2E;IAC3E,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAIxC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAYtC,4DAA4D;IAC5D,gBAAgB,IAAI,IAAI;IAUxB,0EAA0E;IAC1E,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAQzC;;;;;OAKG;IACH,gBAAgB,IAAI,WAAW,EAAE;IAajC,oEAAoE;IACpE,kBAAkB,IAAI,IAAI;IAO1B,uFAAuF;IACvF,oBAAoB,IAAI,IAAI;IAM5B,kBAAkB,IAAI,OAAO;IAI7B;;;;;;OAMG;IACG,kBAAkB,CACtB,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,OAAO,CAAC,WAAW,CAAC;IAuDvB;;;;;;;;OAQG;YACW,uBAAuB;IAcrC;;;;;OAKG;IACH,KAAK,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAsBpC,iFAAiF;IACjF,OAAO,IAAI,IAAI;IASf,8EAA8E;IAC9E,YAAY,IAAI,OAAO;IAIvB;;;;;;;;;OASG;IACG,OAAO,CACX,UAAU,EAAE,MAAM,GAAG,MAAM,EAC3B,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,OAAO,CAAC,cAAc,CAAC;IAuB1B;;;;;;OAMG;IACH,sBAAsB,CACpB,UAAU,CAAC,EAAE,UAAU,EACvB,YAAY,CAAC,EAAE,WAAW,GACzB,qBAAqB;IAuBxB,sFAAsF;IACtF,KAAK,IAAI,IAAI;IASb;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,0BAA0B;CAkBnC"}
|
|
@@ -5,27 +5,60 @@ const redis_context_1 = require("./redis-context");
|
|
|
5
5
|
const redis_error_1 = require("./redis-error");
|
|
6
6
|
const redis_result_1 = require("./redis-result");
|
|
7
7
|
const redis_value_1 = require("./redis-value");
|
|
8
|
+
/**
|
|
9
|
+
* Per-connection server state and the concrete {@link RedisClientSession}.
|
|
10
|
+
*
|
|
11
|
+
* One instance exists per connected client and owns everything that is scoped
|
|
12
|
+
* to that connection rather than to the shared server:
|
|
13
|
+
* - the selected database index and the resolved {@link RedisDatabase};
|
|
14
|
+
* - the session {@link ClientSessionMode} (normal / transaction / subscribed);
|
|
15
|
+
* - the negotiated RESP protocol version (HELLO);
|
|
16
|
+
* - the cluster READONLY flag (replica reads via READONLY/READWRITE);
|
|
17
|
+
* - the MULTI command queue and its dirty bit;
|
|
18
|
+
* - WATCH key registrations for optimistic locking.
|
|
19
|
+
*
|
|
20
|
+
* Commands are serialized through a per-database turn queue so that, even though
|
|
21
|
+
* execution is async, only one command mutates a given database at a time. This
|
|
22
|
+
* is also what makes blocking commands (BLPOP, ...) cooperate instead of
|
|
23
|
+
* deadlock — see {@link createTurnAwareParkHandler}.
|
|
24
|
+
*/
|
|
8
25
|
class ClientSession {
|
|
9
26
|
static nextId = 0;
|
|
10
27
|
id;
|
|
11
28
|
server;
|
|
29
|
+
/** Aborted when the connection closes; threaded into every command's ctx. */
|
|
12
30
|
signal;
|
|
13
31
|
executor;
|
|
14
32
|
signalSource;
|
|
33
|
+
nodeRole;
|
|
15
34
|
parkHandler;
|
|
16
35
|
turnQueueOverride;
|
|
36
|
+
/**
|
|
37
|
+
* The turn handle of the command currently executing on this session,
|
|
38
|
+
* exposed so {@link executeTransaction} can hand the turn off to another
|
|
39
|
+
* database's queue when a queued SELECT switches databases mid-EXEC.
|
|
40
|
+
*/
|
|
41
|
+
activeTurnAccess;
|
|
17
42
|
selectedDatabaseId;
|
|
18
43
|
sessionMode = 'normal';
|
|
19
44
|
respVersion = 2;
|
|
45
|
+
authenticated = false;
|
|
46
|
+
/** Set by READONLY, cleared by READWRITE/RESET; lets a replica serve reads. */
|
|
47
|
+
clusterReadOnlyMode = false;
|
|
48
|
+
/** Commands buffered between MULTI and EXEC, in submission order. */
|
|
20
49
|
transactionPlans = [];
|
|
50
|
+
/** True once a queued command errored — forces EXEC to abort with EXECABORT. */
|
|
21
51
|
transactionDirty = false;
|
|
52
|
+
/** Active WATCH registrations, keyed by `db:keyHex`. */
|
|
22
53
|
watches = new Map();
|
|
54
|
+
/** Subset of watched keys mutated since WATCH — non-empty fails the next EXEC. */
|
|
23
55
|
dirtyWatches = new Set();
|
|
24
56
|
constructor(options) {
|
|
25
57
|
this.id = options.id ?? `client-${++ClientSession.nextId}`;
|
|
26
58
|
this.server = options.server;
|
|
27
59
|
this.executor = options.executor;
|
|
28
60
|
this.selectedDatabaseId = options.database ?? 0;
|
|
61
|
+
this.nodeRole = options.nodeRole;
|
|
29
62
|
this.parkHandler = options.park ?? (0, redis_context_1.createDefaultParkHandler)();
|
|
30
63
|
this.turnQueueOverride = options.turnQueue;
|
|
31
64
|
if (options.signal) {
|
|
@@ -46,12 +79,26 @@ class ClientSession {
|
|
|
46
79
|
get protocolVersion() {
|
|
47
80
|
return this.respVersion;
|
|
48
81
|
}
|
|
82
|
+
get clusterReadOnly() {
|
|
83
|
+
return this.clusterReadOnlyMode;
|
|
84
|
+
}
|
|
85
|
+
get isAuthenticated() {
|
|
86
|
+
return this.authenticated;
|
|
87
|
+
}
|
|
88
|
+
setAuthenticated(value) {
|
|
89
|
+
this.authenticated = value;
|
|
90
|
+
}
|
|
91
|
+
/** The live database object for the currently selected index. */
|
|
49
92
|
get db() {
|
|
50
93
|
return this.server.getDatabase(this.selectedDatabaseId);
|
|
51
94
|
}
|
|
52
95
|
setProtocolVersion(version) {
|
|
53
96
|
this.respVersion = version;
|
|
54
97
|
}
|
|
98
|
+
/** Toggle replica read mode for this connection (READONLY / READWRITE). */
|
|
99
|
+
setClusterReadOnly(value) {
|
|
100
|
+
this.clusterReadOnlyMode = value;
|
|
101
|
+
}
|
|
55
102
|
selectDatabase(database) {
|
|
56
103
|
if (!Number.isInteger(database) ||
|
|
57
104
|
database < 0 ||
|
|
@@ -60,6 +107,7 @@ class ClientSession {
|
|
|
60
107
|
}
|
|
61
108
|
this.selectedDatabaseId = database;
|
|
62
109
|
}
|
|
110
|
+
/** MULTI: enter transaction mode. Redis forbids nesting. */
|
|
63
111
|
beginTransaction() {
|
|
64
112
|
if (this.sessionMode === 'transaction') {
|
|
65
113
|
throw new redis_error_1.RedisCommandError('MULTI calls can not be nested');
|
|
@@ -68,12 +116,19 @@ class ClientSession {
|
|
|
68
116
|
this.transactionPlans = [];
|
|
69
117
|
this.transactionDirty = false;
|
|
70
118
|
}
|
|
119
|
+
/** Buffer one command while in MULTI; replies "+QUEUED" to the client. */
|
|
71
120
|
queueTransaction(plan) {
|
|
72
121
|
if (this.sessionMode !== 'transaction') {
|
|
73
122
|
throw new redis_error_1.RedisCommandError('MULTI has not been called');
|
|
74
123
|
}
|
|
75
124
|
this.transactionPlans.push(plan);
|
|
76
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* EXEC step 1: hand back the queued plans and atomically reset the session to
|
|
128
|
+
* normal mode (clearing the queue, dirty bit, and WATCHes). The caller is
|
|
129
|
+
* responsible for actually running the returned plans via
|
|
130
|
+
* {@link executeTransaction}. Returns an empty list if not in MULTI.
|
|
131
|
+
*/
|
|
77
132
|
drainTransaction() {
|
|
78
133
|
if (this.sessionMode !== 'transaction') {
|
|
79
134
|
return [];
|
|
@@ -85,12 +140,14 @@ class ClientSession {
|
|
|
85
140
|
this.unwatch();
|
|
86
141
|
return plans;
|
|
87
142
|
}
|
|
143
|
+
/** DISCARD: drop the queued commands and leave transaction mode. */
|
|
88
144
|
discardTransaction() {
|
|
89
145
|
this.transactionPlans = [];
|
|
90
146
|
this.sessionMode = 'normal';
|
|
91
147
|
this.transactionDirty = false;
|
|
92
148
|
this.unwatch();
|
|
93
149
|
}
|
|
150
|
+
/** Flag the transaction as poisoned (a queued command failed). No-op outside MULTI. */
|
|
94
151
|
markTransactionDirty() {
|
|
95
152
|
if (this.sessionMode === 'transaction') {
|
|
96
153
|
this.transactionDirty = true;
|
|
@@ -99,13 +156,50 @@ class ClientSession {
|
|
|
99
156
|
isTransactionDirty() {
|
|
100
157
|
return this.transactionDirty;
|
|
101
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* EXEC step 2: run the drained plans in order and collect their replies into a
|
|
161
|
+
* single array reply. Each command runs in its own fresh execution context.
|
|
162
|
+
* Streaming commands (SUBSCRIBE/MONITOR) are not permitted inside a
|
|
163
|
+
* transaction: the stream is closed immediately and replaced with an error
|
|
164
|
+
* entry so the array stays positionally aligned with the queued commands.
|
|
165
|
+
*/
|
|
102
166
|
async executeTransaction(plans) {
|
|
103
167
|
const values = [];
|
|
168
|
+
// Blocking commands must not park while the EXEC turn is held — that would
|
|
169
|
+
// deadlock because no other session could produce the wakeup write. Override
|
|
170
|
+
// park so any blocking command queued in MULTI behaves non-blocking (returns
|
|
171
|
+
// null immediately), matching real Redis BLPOP-inside-MULTI semantics.
|
|
172
|
+
const noBlockCtx = this.createExecutionContext(undefined, async () => null);
|
|
173
|
+
let currentDbId = this.selectedDatabaseId;
|
|
104
174
|
for (const plan of plans) {
|
|
105
175
|
if (this.signal.aborted) {
|
|
106
176
|
throw createAbortError();
|
|
107
177
|
}
|
|
108
|
-
|
|
178
|
+
// A queued SELECT on a previous iteration may have switched databases.
|
|
179
|
+
// Move the held turn onto the now-selected database's queue so its
|
|
180
|
+
// keyspace stays serialized against other sessions (#94 follow-up). Only
|
|
181
|
+
// one turn is ever held at a time (release-then-acquire), so this cannot
|
|
182
|
+
// deadlock even if two transactions select databases in opposite orders.
|
|
183
|
+
if (this.selectedDatabaseId !== currentDbId) {
|
|
184
|
+
await this.handoffTurnToSelectedDb();
|
|
185
|
+
currentDbId = this.selectedDatabaseId;
|
|
186
|
+
}
|
|
187
|
+
// executePlan converts RedisCommandErrors into error results, but a
|
|
188
|
+
// command whose execute() throws an unexpected runtime error (TypeError,
|
|
189
|
+
// etc.) would otherwise propagate out and abandon the partial results
|
|
190
|
+
// array. Real Redis always replies with an N-element EXEC array, so trap
|
|
191
|
+
// the failure into this command's slot and keep running the rest (#83).
|
|
192
|
+
let result;
|
|
193
|
+
try {
|
|
194
|
+
result = await this.executor.executePlan(plan, noBlockCtx);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
if (this.signal.aborted) {
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
200
|
+
values.push(redis_value_1.RedisValue.error(err.message));
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
109
203
|
if (result instanceof redis_result_1.RedisResult) {
|
|
110
204
|
values.push(result.value);
|
|
111
205
|
continue;
|
|
@@ -115,6 +209,33 @@ class ClientSession {
|
|
|
115
209
|
}
|
|
116
210
|
return redis_result_1.RedisResult.create(redis_value_1.RedisValue.array(values));
|
|
117
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Release the currently held serialization turn and acquire a fresh one on
|
|
214
|
+
* the selected database's queue. Called when a queued SELECT switches
|
|
215
|
+
* databases mid-EXEC so subsequent commands run under the correct
|
|
216
|
+
* per-database turn (see {@link executeTransaction}).
|
|
217
|
+
*
|
|
218
|
+
* No-op when a fixed turn-queue override is in force (a single queue already
|
|
219
|
+
* serializes every database) or when no managed turn is active.
|
|
220
|
+
*/
|
|
221
|
+
async handoffTurnToSelectedDb() {
|
|
222
|
+
if (this.turnQueueOverride) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const turnAccess = this.activeTurnAccess;
|
|
226
|
+
if (!turnAccess) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
turnAccess.get()?.release();
|
|
230
|
+
const nextTurn = await this.db.turnQueue.waitTurn();
|
|
231
|
+
turnAccess.set(nextTurn);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* WATCH the given keys for optimistic locking. Each key is subscribed in the
|
|
235
|
+
* keyspace; any mutation flips its entry into {@link dirtyWatches}, which a
|
|
236
|
+
* subsequent EXEC checks via {@link isWatchDirty}. Already-watched keys are
|
|
237
|
+
* skipped so re-WATCHing is idempotent.
|
|
238
|
+
*/
|
|
118
239
|
watch(keys) {
|
|
119
240
|
const database = this.selectedDatabaseId;
|
|
120
241
|
const db = this.db;
|
|
@@ -133,6 +254,7 @@ class ClientSession {
|
|
|
133
254
|
});
|
|
134
255
|
}
|
|
135
256
|
}
|
|
257
|
+
/** UNWATCH / cleanup: drop every keyspace subscription and clear dirty state. */
|
|
136
258
|
unwatch() {
|
|
137
259
|
for (const watch of this.watches.values()) {
|
|
138
260
|
watch.unsubscribe();
|
|
@@ -140,47 +262,94 @@ class ClientSession {
|
|
|
140
262
|
this.watches.clear();
|
|
141
263
|
this.dirtyWatches.clear();
|
|
142
264
|
}
|
|
265
|
+
/** True if any watched key was mutated since WATCH — EXEC must return nil. */
|
|
143
266
|
isWatchDirty() {
|
|
144
267
|
return this.dirtyWatches.size > 0;
|
|
145
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Public entry point for executing one client command.
|
|
271
|
+
*
|
|
272
|
+
* Acquires a turn on the database's turn queue before running, guaranteeing
|
|
273
|
+
* serialized access to the keyspace, and always releases it afterward. The
|
|
274
|
+
* acquired turn is exposed to the command via a turn-aware park handler so a
|
|
275
|
+
* blocking command can yield the turn while parked (see
|
|
276
|
+
* {@link createTurnAwareParkHandler}); `turn` is reassigned through the
|
|
277
|
+
* {@link TurnAccess} closure because suspending returns a *new* handle.
|
|
278
|
+
*/
|
|
146
279
|
async execute(rawCommand, rawArgs) {
|
|
147
280
|
if (this.signal.aborted) {
|
|
148
281
|
throw createAbortError();
|
|
149
282
|
}
|
|
150
283
|
const turnQueue = this.turnQueueOverride ?? this.db.turnQueue;
|
|
151
284
|
let turn = await turnQueue.waitTurn();
|
|
285
|
+
const turnAccess = {
|
|
286
|
+
get: () => turn,
|
|
287
|
+
set: nextTurn => {
|
|
288
|
+
turn = nextTurn;
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
this.activeTurnAccess = turnAccess;
|
|
152
292
|
try {
|
|
153
|
-
const ctx = this.createExecutionContext(
|
|
154
|
-
get: () => turn,
|
|
155
|
-
set: nextTurn => {
|
|
156
|
-
turn = nextTurn;
|
|
157
|
-
},
|
|
158
|
-
});
|
|
293
|
+
const ctx = this.createExecutionContext(turnAccess);
|
|
159
294
|
return await this.executor.executeRaw(rawCommand, rawArgs, ctx);
|
|
160
295
|
}
|
|
161
296
|
finally {
|
|
297
|
+
this.activeTurnAccess = undefined;
|
|
162
298
|
turn?.release();
|
|
163
299
|
}
|
|
164
300
|
}
|
|
165
|
-
|
|
301
|
+
/**
|
|
302
|
+
* Build the {@link RedisExecutionContext} passed to a command's `execute`.
|
|
303
|
+
* When a turn is supplied (the normal client path) the context gets a
|
|
304
|
+
* turn-aware park handler so blocking commands release their turn while
|
|
305
|
+
* waiting; without one (e.g. nested transaction execution) the plain park
|
|
306
|
+
* handler is used.
|
|
307
|
+
*/
|
|
308
|
+
createExecutionContext(turnAccess, parkOverride) {
|
|
309
|
+
// `db` is a live getter, not a snapshot: a queued `SELECT N` runs mid-EXEC
|
|
310
|
+
// and updates `selectedDatabaseId`, so every command must resolve the
|
|
311
|
+
// currently selected database at access time, not at context-build time
|
|
312
|
+
// (issue #94). Arrow keeps `this` bound to the session without aliasing.
|
|
313
|
+
const resolveDb = () => this.db;
|
|
166
314
|
return {
|
|
167
|
-
db
|
|
315
|
+
get db() {
|
|
316
|
+
return resolveDb();
|
|
317
|
+
},
|
|
168
318
|
server: this.server,
|
|
169
319
|
session: this,
|
|
170
320
|
executor: this.executor,
|
|
321
|
+
...(this.nodeRole ? { nodeRole: this.nodeRole } : {}),
|
|
171
322
|
signal: this.signal,
|
|
172
|
-
park:
|
|
173
|
-
|
|
174
|
-
|
|
323
|
+
park: parkOverride ??
|
|
324
|
+
(turnAccess
|
|
325
|
+
? this.createTurnAwareParkHandler(turnAccess)
|
|
326
|
+
: this.parkHandler),
|
|
175
327
|
};
|
|
176
328
|
}
|
|
329
|
+
/** Tear down the session: abort in-flight work and reset all per-connection state. */
|
|
177
330
|
close() {
|
|
178
331
|
this.signalSource?.abort();
|
|
179
332
|
this.unwatch();
|
|
180
333
|
this.transactionPlans = [];
|
|
181
334
|
this.transactionDirty = false;
|
|
182
335
|
this.sessionMode = 'normal';
|
|
336
|
+
this.clusterReadOnlyMode = false;
|
|
183
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Wrap the base park handler so that parking also yields the command's turn.
|
|
340
|
+
*
|
|
341
|
+
* Blocking commands (BLPOP, BRPOP, ...) must not hold the database turn while
|
|
342
|
+
* they wait, or no other client could ever produce the value that unblocks
|
|
343
|
+
* them — a deadlock. The flow:
|
|
344
|
+
* 1. Start the underlying park, capturing its eventual value.
|
|
345
|
+
* 2. Clear the local turn and call `turn.suspend(parked)`, which releases the
|
|
346
|
+
* turn back to the queue and resolves with a fresh turn once the park
|
|
347
|
+
* settles and this session is scheduled again.
|
|
348
|
+
* 3. Store the new turn (so `finally`/subsequent parks see it) and return the
|
|
349
|
+
* parked value.
|
|
350
|
+
*
|
|
351
|
+
* If there is no current turn, fall back to plain parking.
|
|
352
|
+
*/
|
|
184
353
|
createTurnAwareParkHandler(turnAccess) {
|
|
185
354
|
return async (request) => {
|
|
186
355
|
const turn = turnAccess.get();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-session.js","sourceRoot":"","sources":["../../src/core/client-session.ts"],"names":[],"mappings":";;;AAEA,mDAOwB;AACxB,+CAAiD;AACjD,iDAA4C;AAC5C,+CAA0C;
|
|
1
|
+
{"version":3,"file":"client-session.js","sourceRoot":"","sources":["../../src/core/client-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,21 +4,100 @@ import type { ExecutionPolicy } from './execution-policies';
|
|
|
4
4
|
import type { RedisExecutionContext } from './redis-context';
|
|
5
5
|
import { RedisResult } from './redis-result';
|
|
6
6
|
import { ResponseStream } from './response-stream';
|
|
7
|
+
/**
|
|
8
|
+
* The result of running a command: either a finished {@link RedisResult} or a
|
|
9
|
+
* long-lived {@link ResponseStream} (e.g. SUBSCRIBE / MONITOR) whose frames the
|
|
10
|
+
* transport drains over time.
|
|
11
|
+
*/
|
|
7
12
|
export type ExecutorResult = RedisResult | ResponseStream;
|
|
8
13
|
export type CommandExecutorOptions = {
|
|
9
14
|
registry: CommandRegistry;
|
|
10
15
|
policies?: readonly ExecutionPolicy[];
|
|
11
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Central command pipeline shared by every client session.
|
|
19
|
+
*
|
|
20
|
+
* Responsibilities:
|
|
21
|
+
* 1. Resolve a raw command name to a {@link CommandDefinition} (case-insensitive).
|
|
22
|
+
* 2. Parse raw argument buffers into typed args and extract routing keys,
|
|
23
|
+
* producing a {@link CommandPlan}.
|
|
24
|
+
* 3. Run the configured {@link ExecutionPolicy} chain around the command's own
|
|
25
|
+
* `execute`, giving policies (transaction, cluster, ...) a chance to
|
|
26
|
+
* short-circuit, rewrite, or wrap the result.
|
|
27
|
+
*
|
|
28
|
+
* Two execution paths exist on purpose:
|
|
29
|
+
* - {@link executePlan} / {@link executeRaw} — async, used for real network
|
|
30
|
+
* clients; may return a {@link ResponseStream} and may await async commands.
|
|
31
|
+
* - {@link executePlanSync} — synchronous mirror used by the Lua runtime, where
|
|
32
|
+
* `redis.call` must complete in a single tick. Streams and promises are
|
|
33
|
+
* rejected rather than awaited.
|
|
34
|
+
*
|
|
35
|
+
* The executor is stateless per-call: all mutable state lives on the
|
|
36
|
+
* {@link RedisExecutionContext} (and the session it carries).
|
|
37
|
+
*/
|
|
12
38
|
export declare class CommandExecutor {
|
|
13
39
|
private readonly registry;
|
|
14
40
|
private readonly policies;
|
|
15
41
|
constructor(options: CommandExecutorOptions);
|
|
16
42
|
getCommandDefinition(name: string): CommandDefinition<unknown> | undefined;
|
|
17
43
|
getCommandDefinitions(): readonly CommandDefinition<unknown>[];
|
|
44
|
+
/**
|
|
45
|
+
* Resolve a raw command + args into a {@link CommandPlan} without executing it.
|
|
46
|
+
* The command name is matched case-insensitively against the registry.
|
|
47
|
+
*
|
|
48
|
+
* @throws {UnknownRedisCommandError} if no command is registered under the name.
|
|
49
|
+
*/
|
|
18
50
|
plan(rawCommand: Buffer | string, rawArgs: readonly Buffer[]): CommandPlan;
|
|
51
|
+
/**
|
|
52
|
+
* Plan and execute a raw command in one step — the normal entry point for a
|
|
53
|
+
* network client.
|
|
54
|
+
*
|
|
55
|
+
* Errors thrown during *planning* (unknown command, arity/parse failures) are
|
|
56
|
+
* caught here and converted into a RESP error reply. Such a failure also marks
|
|
57
|
+
* any open MULTI transaction dirty so a later EXEC is aborted, matching Redis:
|
|
58
|
+
* a command that cannot even be parsed must not silently vanish from the queue.
|
|
59
|
+
* Execution-time errors are handled inside {@link executePlan}.
|
|
60
|
+
*/
|
|
19
61
|
executeRaw(rawCommand: Buffer | string, rawArgs: readonly Buffer[], ctx: RedisExecutionContext): Promise<ExecutorResult>;
|
|
62
|
+
private static normalizeCommandName;
|
|
63
|
+
/**
|
|
64
|
+
* Run a pre-built plan through the full async pipeline.
|
|
65
|
+
*
|
|
66
|
+
* Order of operations:
|
|
67
|
+
* 1. `beforeExecute` for each policy. The first policy that returns a result
|
|
68
|
+
* short-circuits execution (e.g. the transaction policy queues the command
|
|
69
|
+
* and returns "+QUEUED"; the cluster policy returns a MOVED/CROSSSLOT
|
|
70
|
+
* error). A short-circuit error during MULTI also dirties the transaction.
|
|
71
|
+
* 2. The command's own `execute`.
|
|
72
|
+
* 3. If a {@link ResponseStream} is produced, run it through every policy's
|
|
73
|
+
* `onStream` hook; otherwise await the value and run it through every
|
|
74
|
+
* `afterExecute` hook (each may replace the result).
|
|
75
|
+
*
|
|
76
|
+
* Execution-time {@link RedisCommandError}s become RESP error replies (and
|
|
77
|
+
* dirty an open transaction when appropriate). Non-Redis errors propagate.
|
|
78
|
+
*/
|
|
20
79
|
executePlan(plan: CommandPlan, ctx: RedisExecutionContext): Promise<ExecutorResult>;
|
|
80
|
+
/**
|
|
81
|
+
* Synchronous counterpart to {@link executePlan}, used by the Lua runtime for
|
|
82
|
+
* `redis.call` / `redis.pcall`. Lua expects each nested command to resolve
|
|
83
|
+
* immediately, so anything that would require awaiting — a command that
|
|
84
|
+
* returns a promise, a {@link ResponseStream}, or an async policy hook — is
|
|
85
|
+
* rejected with a {@link RedisCommandError} instead of being awaited. Async
|
|
86
|
+
* command definitions are rejected before invocation so they cannot leave
|
|
87
|
+
* orphaned work running after the script error (see
|
|
88
|
+
* {@link assertSyncCommandDefinition}, {@link assertSyncCommandResult}, and
|
|
89
|
+
* {@link assertSyncPolicyResult}).
|
|
90
|
+
*
|
|
91
|
+
* The policy chain and transaction-dirty handling otherwise mirror the async
|
|
92
|
+
* path exactly.
|
|
93
|
+
*/
|
|
21
94
|
executePlanSync(plan: CommandPlan, ctx: RedisExecutionContext): RedisResult;
|
|
95
|
+
/**
|
|
96
|
+
* Build a {@link CommandPlan} from a resolved definition: parse the raw buffers
|
|
97
|
+
* against the command's schema (may throw arity/type errors) and extract the
|
|
98
|
+
* routing keys used for cluster slot validation. Flags are copied onto the plan
|
|
99
|
+
* so policies can inspect them without re-resolving the definition.
|
|
100
|
+
*/
|
|
22
101
|
private createPlan;
|
|
23
102
|
}
|
|
24
103
|
//# sourceMappingURL=command-executor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-executor.d.ts","sourceRoot":"","sources":["../../src/core/command-executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"command-executor.d.ts","sourceRoot":"","sources":["../../src/core/command-executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAO3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAoB,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEpE;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,cAAc,CAAA;AAEzD,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,eAAe,CAAA;IACzB,QAAQ,CAAC,EAAE,SAAS,eAAe,EAAE,CAAA;CACtC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;gBAEzC,OAAO,EAAE,sBAAsB;IAK3C,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAAC,OAAO,CAAC,GAAG,SAAS;IAI1E,qBAAqB,IAAI,SAAS,iBAAiB,CAAC,OAAO,CAAC,EAAE;IAI9D;;;;;OAKG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,WAAW;IAW1E;;;;;;;;;OASG;IACG,UAAU,CACd,UAAU,EAAE,MAAM,GAAG,MAAM,EAC3B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,cAAc,CAAC;IA2B1B,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAMnC;;;;;;;;;;;;;;;OAeG;IACG,WAAW,CACf,IAAI,EAAE,WAAW,EACjB,GAAG,EAAE,qBAAqB,GACzB,OAAO,CAAC,cAAc,CAAC;IAgD1B;;;;;;;;;;;;;OAaG;IACH,eAAe,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,qBAAqB,GAAG,WAAW;IA6C3E;;;;;OAKG;IACH,OAAO,CAAC,UAAU;CAcnB"}
|