@morlay/session-rdb 0.0.16-alpha.4 → 0.0.17
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 +44 -10
- package/dist/artifact.d.mts +9 -2
- package/dist/artifact.mjs +3 -3
- package/dist/{import-BPEHfHNk.mjs → import-DZIAjOUr.mjs} +2 -1
- package/dist/import.d.mts +1 -1
- package/dist/import.mjs +1 -1
- package/dist/{index-CgAqCQAb.d.mts → index-BEKoV1Uy.d.mts} +11 -2
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +509 -6
- package/dist/{log-BIuXv09P.mjs → log-DO69NQnn.mjs} +16 -1
- package/dist/schema-Bo6Hy5gW.d.mts +3476 -0
- package/dist/{sqlite-IH5I48aL.mjs → sqlite-BIPdMNQb.mjs} +504 -6
- package/dist/storage.d.mts +10 -2
- package/dist/storage.mjs +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.mjs +1 -1
- package/drizzle/postgres/20260910120001_v3_storage_tables/migration.sql +65 -0
- package/drizzle/postgres/20260910120001_v3_storage_tables/snapshot.json +1236 -0
- package/drizzle/postgres/20260910130001_v3_session_title_backfill/migration.sql +10 -0
- package/drizzle/postgres/20260910130001_v3_session_title_backfill/snapshot.json +1236 -0
- package/drizzle/sqlite/20260910120000_v3_storage_tables/migration.sql +66 -0
- package/drizzle/sqlite/20260910120000_v3_storage_tables/snapshot.json +958 -0
- package/drizzle/sqlite/20260910130000_v3_session_title_backfill/migration.sql +13 -0
- package/drizzle/sqlite/20260910130000_v3_session_title_backfill/snapshot.json +958 -0
- package/package.json +20 -18
- package/src/backend.ts +7 -0
- package/src/branch.ts +2 -0
- package/src/drizzle/postgres-v3.ts +5 -0
- package/src/drizzle/sqlite-v3.ts +5 -0
- package/src/entities/v3/index.ts +20 -0
- package/src/entities/v3/session-projcache-rows.ts +27 -0
- package/src/entities/v3/sessions.ts +6 -0
- package/src/entities/v3/storage-units.ts +10 -0
- package/src/entities/v3/workspace-sessions.ts +24 -0
- package/src/entities/v3/workspace-state.ts +18 -0
- package/src/entities/v3/workspaces.ts +19 -0
- package/src/import-storages.ts +173 -0
- package/src/index.ts +53 -0
- package/src/log.ts +19 -0
- package/src/postgres.ts +70 -2
- package/src/schema.ts +16 -2
- package/src/sqlite.ts +87 -4
- package/src/storage-takeover/index.ts +60 -0
- package/src/storage-takeover/projection-cache.ts +445 -0
- package/src/storage-takeover/repository.ts +420 -0
- package/src/storage-takeover/storage-backend.ts +177 -0
- package/src/storage-takeover/types.ts +82 -0
- package/dist/schema-COK7-wRV.d.mts +0 -104
package/src/postgres.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { and, eq, gte, sql } from "drizzle-orm";
|
|
4
|
+
import { and, desc, eq, gte, sql } from "drizzle-orm";
|
|
5
5
|
import type { PgAsyncDatabase, PgAsyncTransaction } from "drizzle-orm/pg-core";
|
|
6
6
|
import type { NodePgDatabase, NodePgQueryResultHKT } from "drizzle-orm/node-postgres";
|
|
7
7
|
import { migrate } from "drizzle-orm/node-postgres/migrator";
|
|
@@ -16,7 +16,9 @@ import {
|
|
|
16
16
|
} from "./backend.ts";
|
|
17
17
|
import { toPostgresSchema } from "./adapters/index.ts";
|
|
18
18
|
import { postgresTableDefs } from "./entities/index.ts";
|
|
19
|
-
import { sessionConflictRow, sessionInsertRow } from "./log.ts";
|
|
19
|
+
import { sessionConflictRow, sessionInsertRow, titleOfEventData } from "./log.ts";
|
|
20
|
+
import { createStorageRepository } from "./storage-takeover/repository.ts";
|
|
21
|
+
import type { StorageRepository } from "./storage-takeover/types.ts";
|
|
20
22
|
|
|
21
23
|
/** drizzle-kit 生成的迁移目录(随包根 drizzle/ 发布;src/dist 形态经相对 URL 统一解析)。 */
|
|
22
24
|
const postgresMigrationsDir = fileURLToPath(new URL("../drizzle/postgres/", import.meta.url));
|
|
@@ -35,14 +37,54 @@ export class PostgresBackend implements Backend {
|
|
|
35
37
|
|
|
36
38
|
private readonly tables: Record<string, any>;
|
|
37
39
|
|
|
40
|
+
/** storages 接管表访问层;句柄在 open()(含迁移)完成后解析。 */
|
|
41
|
+
readonly storage: StorageRepository;
|
|
42
|
+
|
|
43
|
+
private readonly opened: Promise<void>;
|
|
44
|
+
private resolveOpened!: () => void;
|
|
45
|
+
private rejectOpened!: (error: unknown) => void;
|
|
46
|
+
/** 事务期间的连接覆盖:storage 写方法经它加入当前事务。 */
|
|
47
|
+
private txOverride: unknown;
|
|
48
|
+
|
|
38
49
|
constructor(
|
|
39
50
|
private readonly db: NodePgDatabase,
|
|
40
51
|
private readonly options: PostgresBackendOptions,
|
|
41
52
|
) {
|
|
42
53
|
this.tables = toPostgresSchema(postgresTableDefs, this.options.schema ?? "public");
|
|
54
|
+
this.opened = new Promise<void>((resolve, reject) => {
|
|
55
|
+
this.resolveOpened = resolve;
|
|
56
|
+
this.rejectOpened = reject;
|
|
57
|
+
});
|
|
58
|
+
// 每个 storage 原语都会重新 await 同一 promise,失败仍逐一可见;这个
|
|
59
|
+
// 守卫只防止 open 失败早于首次使用时的 unhandled rejection。
|
|
60
|
+
this.opened.catch(() => {});
|
|
61
|
+
this.storage = createStorageRepository({
|
|
62
|
+
db: () => this.opened.then(() => this.txOverride ?? this.db),
|
|
63
|
+
writeAtomically: (fn) =>
|
|
64
|
+
this.db.transaction(async (tx) => {
|
|
65
|
+
const previous = this.txOverride;
|
|
66
|
+
this.txOverride = tx;
|
|
67
|
+
try {
|
|
68
|
+
return await fn();
|
|
69
|
+
} finally {
|
|
70
|
+
this.txOverride = previous;
|
|
71
|
+
}
|
|
72
|
+
}),
|
|
73
|
+
tables: this.tables,
|
|
74
|
+
});
|
|
43
75
|
}
|
|
44
76
|
|
|
45
77
|
async open(): Promise<void> {
|
|
78
|
+
try {
|
|
79
|
+
await this.doOpen();
|
|
80
|
+
this.resolveOpened();
|
|
81
|
+
} catch (error: unknown) {
|
|
82
|
+
this.rejectOpened(error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private async doOpen(): Promise<void> {
|
|
46
88
|
const schema = this.options.schema ?? "public";
|
|
47
89
|
// drizzle-kit 迁移:v2 baseline 由旧版本建表(首次打开时标记为已应用),
|
|
48
90
|
// 本版本只执行 v3 diff(删 f_original_seq)。
|
|
@@ -149,6 +191,7 @@ export class PostgresBackend implements Backend {
|
|
|
149
191
|
updateHead: (id, headEventId, headSequence) =>
|
|
150
192
|
this.updateHead(tx, id, headEventId, headSequence),
|
|
151
193
|
bumpRevision: (id) => this.bumpRevision(tx, id),
|
|
194
|
+
refreshTitle: (id) => this.refreshTitle(tx, id),
|
|
152
195
|
deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(tx, id, fromSequence),
|
|
153
196
|
getPrevBridge: (id, sequence) => this.getPrevBridge(tx, id, sequence),
|
|
154
197
|
};
|
|
@@ -291,6 +334,31 @@ export class PostgresBackend implements Backend {
|
|
|
291
334
|
.execute();
|
|
292
335
|
}
|
|
293
336
|
|
|
337
|
+
/** 从事件表重算标题(最后一条 `session/title`),写回会话行的 f_title 列。 */
|
|
338
|
+
private async refreshTitle(
|
|
339
|
+
exec: PgAsyncDatabase<NodePgQueryResultHKT>,
|
|
340
|
+
id: SessionId,
|
|
341
|
+
): Promise<void> {
|
|
342
|
+
const bridges = this.tables["t_session_events"];
|
|
343
|
+
const entities = this.tables["t_events"];
|
|
344
|
+
const row = (
|
|
345
|
+
await exec
|
|
346
|
+
.select({ fSequence: bridges.fSequence, fData: entities.fData })
|
|
347
|
+
.from(bridges)
|
|
348
|
+
.innerJoin(entities, eq(entities.fEventId, bridges.fEventId))
|
|
349
|
+
.where(and(eq(bridges.fSessionId, id), eq(entities.fType, "session/title")))
|
|
350
|
+
.orderBy(desc(bridges.fSequence))
|
|
351
|
+
.limit(1)
|
|
352
|
+
.execute()
|
|
353
|
+
)[0] as { fSequence: number; fData: string } | undefined;
|
|
354
|
+
const title = row === undefined ? undefined : titleOfEventData(row.fData);
|
|
355
|
+
await exec
|
|
356
|
+
.update(this.tables["t_sessions"])
|
|
357
|
+
.set({ fTitle: title ?? null, fTitleSeq: title === undefined ? null : row!.fSequence })
|
|
358
|
+
.where(eq(this.tables["t_sessions"].fSessionId, id))
|
|
359
|
+
.execute();
|
|
360
|
+
}
|
|
361
|
+
|
|
294
362
|
private async deleteBridgeTail(
|
|
295
363
|
exec: PgAsyncDatabase<NodePgQueryResultHKT>,
|
|
296
364
|
id: SessionId,
|
package/src/schema.ts
CHANGED
|
@@ -21,6 +21,16 @@ export const tEvents = sqliteTables["t_events"]!;
|
|
|
21
21
|
|
|
22
22
|
export const tSessionEvents = sqliteTables["t_session_events"]!;
|
|
23
23
|
|
|
24
|
+
export const tStorageUnits = sqliteTables["t_storage_units"]!;
|
|
25
|
+
|
|
26
|
+
export const tWorkspaces = sqliteTables["t_workspaces"]!;
|
|
27
|
+
|
|
28
|
+
export const tWorkspaceSessions = sqliteTables["t_workspace_sessions"]!;
|
|
29
|
+
|
|
30
|
+
export const tWorkspaceState = sqliteTables["t_workspace_state"]!;
|
|
31
|
+
|
|
32
|
+
export const tSessionProjcacheRows = sqliteTables["t_session_projcache_row"]!;
|
|
33
|
+
|
|
24
34
|
export type { SessionRow } from "./backend.ts";
|
|
25
35
|
|
|
26
36
|
export type { EventRow } from "./backend.ts";
|
|
@@ -70,7 +80,9 @@ export function eventKind(event: { type: string; data?: unknown }): EventKind |
|
|
|
70
80
|
return "turn";
|
|
71
81
|
case "tool/call":
|
|
72
82
|
case "tool/result":
|
|
73
|
-
case "tool/
|
|
83
|
+
case "tool/ptc-dispatch-start":
|
|
84
|
+
case "tool/ptc-dispatch":
|
|
85
|
+
case "tool/code-dispatch-start": // v2 旧名(读路径归一为 ptc)
|
|
74
86
|
case "tool/code-dispatch":
|
|
75
87
|
return "tool";
|
|
76
88
|
case "request/header":
|
|
@@ -158,7 +170,9 @@ export function eventDimensions(event: SessionEvent): {
|
|
|
158
170
|
name: typeof data["name"] === "string" ? data["name"] : "",
|
|
159
171
|
actionId: typeof data["callId"] === "string" ? data["callId"] : "",
|
|
160
172
|
};
|
|
161
|
-
case "tool/
|
|
173
|
+
case "tool/ptc-dispatch-start":
|
|
174
|
+
case "tool/ptc-dispatch":
|
|
175
|
+
case "tool/code-dispatch-start": // v2 旧名(读路径归一为 ptc)
|
|
162
176
|
case "tool/code-dispatch":
|
|
163
177
|
return {
|
|
164
178
|
kind,
|
package/src/sqlite.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { mkdir, open } from "node:fs/promises";
|
|
|
4
4
|
import { dirname, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { DatabaseSync } from "node:sqlite";
|
|
7
|
-
import { and, eq, gte, sql } from "drizzle-orm";
|
|
7
|
+
import { and, desc, eq, gte, sql } from "drizzle-orm";
|
|
8
8
|
import { drizzle, type NodeSQLiteDatabase } from "drizzle-orm/node-sqlite";
|
|
9
9
|
import { migrate } from "drizzle-orm/node-sqlite/migrator";
|
|
10
10
|
import type { SessionId } from "@deepseek-ai/dsh-session";
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
type EventRow,
|
|
17
17
|
type SessionRow,
|
|
18
18
|
} from "./backend.ts";
|
|
19
|
-
import { sessionConflictRow, sessionInsertRow } from "./log.ts";
|
|
19
|
+
import { sessionConflictRow, sessionInsertRow, titleOfEventData } from "./log.ts";
|
|
20
20
|
import {
|
|
21
21
|
DEFAULT_BUSY_TIMEOUT_MS,
|
|
22
22
|
SCHEMA_VERSION,
|
|
@@ -24,9 +24,16 @@ import {
|
|
|
24
24
|
tEvents,
|
|
25
25
|
tPersistenceState,
|
|
26
26
|
tSessionEvents,
|
|
27
|
+
tSessionProjcacheRows,
|
|
27
28
|
tSessions,
|
|
29
|
+
tStorageUnits,
|
|
30
|
+
tWorkspaceSessions,
|
|
31
|
+
tWorkspaceState,
|
|
32
|
+
tWorkspaces,
|
|
28
33
|
type JournalMode,
|
|
29
34
|
} from "./schema.ts";
|
|
35
|
+
import { createStorageRepository } from "./storage-takeover/repository.ts";
|
|
36
|
+
import type { StorageRepository } from "./storage-takeover/types.ts";
|
|
30
37
|
|
|
31
38
|
type SqliteDb = NodeSQLiteDatabase & { $client: DatabaseSync };
|
|
32
39
|
|
|
@@ -178,12 +185,69 @@ export class SqliteBackend implements Backend {
|
|
|
178
185
|
readonly kind = "sqlite" as const;
|
|
179
186
|
storeIdentity!: string;
|
|
180
187
|
|
|
188
|
+
/** storages 接管表访问层;句柄在 open() 完成后解析。 */
|
|
189
|
+
readonly storage: StorageRepository;
|
|
190
|
+
|
|
181
191
|
private dbPath = "";
|
|
182
192
|
private db!: SqliteDb;
|
|
183
|
-
|
|
184
|
-
|
|
193
|
+
private readonly dbReady: Promise<SqliteDb>;
|
|
194
|
+
private resolveDb!: (db: SqliteDb) => void;
|
|
195
|
+
private rejectDb!: (error: unknown) => void;
|
|
196
|
+
|
|
197
|
+
constructor(private readonly options: SqliteBackendOptions) {
|
|
198
|
+
this.dbReady = new Promise<SqliteDb>((resolve, reject) => {
|
|
199
|
+
this.resolveDb = resolve;
|
|
200
|
+
this.rejectDb = reject;
|
|
201
|
+
});
|
|
202
|
+
// 每个 storage 原语都会重新 await 同一 promise,失败仍逐一可见;这个
|
|
203
|
+
// 守卫只防止 open 失败早于首次使用时的 unhandled rejection。
|
|
204
|
+
this.dbReady.catch(() => {});
|
|
205
|
+
this.storage = createStorageRepository({
|
|
206
|
+
db: () => this.dbReady,
|
|
207
|
+
// 同步驱动:读路径直读介质(open 之前的调用是装配错误,fail loud)。
|
|
208
|
+
dbSync: () => {
|
|
209
|
+
if (this.db === undefined) throw new Error("sqlite session database is not open");
|
|
210
|
+
return this.db;
|
|
211
|
+
},
|
|
212
|
+
writeAtomically: (fn) =>
|
|
213
|
+
enqueueSqliteTx(this.dbPath, async () => {
|
|
214
|
+
const db = await this.dbReady;
|
|
215
|
+
db.$client.exec("BEGIN IMMEDIATE");
|
|
216
|
+
try {
|
|
217
|
+
const result = await fn();
|
|
218
|
+
db.$client.exec("COMMIT");
|
|
219
|
+
return result;
|
|
220
|
+
} catch (error: unknown) {
|
|
221
|
+
try {
|
|
222
|
+
db.$client.exec("ROLLBACK");
|
|
223
|
+
} catch {
|
|
224
|
+
// 原始 SQLite 失败仍是可操作的根因。
|
|
225
|
+
}
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
}),
|
|
229
|
+
tables: {
|
|
230
|
+
t_sessions: tSessions,
|
|
231
|
+
t_storage_units: tStorageUnits,
|
|
232
|
+
t_workspaces: tWorkspaces,
|
|
233
|
+
t_workspace_sessions: tWorkspaceSessions,
|
|
234
|
+
t_workspace_state: tWorkspaceState,
|
|
235
|
+
t_session_projcache_row: tSessionProjcacheRows,
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
}
|
|
185
239
|
|
|
186
240
|
async open(): Promise<void> {
|
|
241
|
+
try {
|
|
242
|
+
await this.doOpen();
|
|
243
|
+
this.resolveDb(this.db);
|
|
244
|
+
} catch (error: unknown) {
|
|
245
|
+
this.rejectDb(error);
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private async doOpen(): Promise<void> {
|
|
187
251
|
const actual =
|
|
188
252
|
this.options.path === ":memory:" ? this.options.path : resolve(this.options.path);
|
|
189
253
|
this.dbPath = actual;
|
|
@@ -290,6 +354,7 @@ export class SqliteBackend implements Backend {
|
|
|
290
354
|
insertBridges: (rows) => this.insertBridges(rows),
|
|
291
355
|
updateHead: (id, headEventId, headSequence) => this.updateHead(id, headEventId, headSequence),
|
|
292
356
|
bumpRevision: (id) => this.bumpRevision(id),
|
|
357
|
+
refreshTitle: (id) => this.refreshTitle(id),
|
|
293
358
|
deleteBridgeTail: (id, fromSequence) => this.deleteBridgeTail(id, fromSequence),
|
|
294
359
|
getPrevBridge: (id, sequence) => this.getPrevBridge(id, sequence),
|
|
295
360
|
};
|
|
@@ -388,6 +453,24 @@ export class SqliteBackend implements Backend {
|
|
|
388
453
|
.run();
|
|
389
454
|
}
|
|
390
455
|
|
|
456
|
+
/** 从事件表重算标题(最后一条 `session/title`),写回会话行的 f_title 列。 */
|
|
457
|
+
private async refreshTitle(id: SessionId): Promise<void> {
|
|
458
|
+
const row = this.db
|
|
459
|
+
.select({ fSequence: tSessionEvents.fSequence, fData: tEvents.fData })
|
|
460
|
+
.from(tSessionEvents)
|
|
461
|
+
.innerJoin(tEvents, eq(tEvents.fEventId, tSessionEvents.fEventId))
|
|
462
|
+
.where(and(eq(tSessionEvents.fSessionId, id), eq(tEvents.fType, "session/title")))
|
|
463
|
+
.orderBy(desc(tSessionEvents.fSequence))
|
|
464
|
+
.limit(1)
|
|
465
|
+
.get() as { fSequence: number; fData: string } | undefined;
|
|
466
|
+
const title = row === undefined ? undefined : titleOfEventData(row.fData);
|
|
467
|
+
this.db
|
|
468
|
+
.update(tSessions)
|
|
469
|
+
.set({ fTitle: title ?? null, fTitleSeq: title === undefined ? null : row!.fSequence })
|
|
470
|
+
.where(eq(tSessions.fSessionId, id))
|
|
471
|
+
.run();
|
|
472
|
+
}
|
|
473
|
+
|
|
391
474
|
private async deleteBridgeTail(id: SessionId, fromSequence: number): Promise<void> {
|
|
392
475
|
this.db
|
|
393
476
|
.delete(tSessionEvents)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* storages 接管的装配入口:在 session-rdb 插件内注册
|
|
3
|
+
*
|
|
4
|
+
* 1. storage hub 的 `rdb` KV 后端——上游 `storage-domain` 的 `StorageBackend.kv`
|
|
5
|
+
* 契约实现(名字来自上游),把 workspace 域映射到 rdb 语义专用表;
|
|
6
|
+
* 2. `ctx.sessionProjectionCache` 服务——替换上游 `session-projection-cache`
|
|
7
|
+
* 插件,直接读写 rdb 的投影 checkpoint 表。
|
|
8
|
+
*
|
|
9
|
+
* 两者都经 `ctx.inject` 挂在可选依赖上:装配里缺 `storage` 或
|
|
10
|
+
* `sessionProjections`(纯 cordis 单测)时静默跳过,消费者按上游的缺服务
|
|
11
|
+
* 路径降级。
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Context } from "@deepseek-ai/cordis";
|
|
15
|
+
import { storageBackendServiceKey } from "@deepseek-ai/dsh-storage";
|
|
16
|
+
import type { ProjectionCacheConfig } from "./projection-cache.ts";
|
|
17
|
+
import { SessionProjectionCacheRdb } from "./projection-cache.ts";
|
|
18
|
+
import { RDB_STORAGE_BACKEND, RdbStorageBackend } from "./storage-backend.ts";
|
|
19
|
+
import type { StorageRepository } from "./types.ts";
|
|
20
|
+
|
|
21
|
+
/** 装配参数。 */
|
|
22
|
+
export interface StorageTakeoverOptions {
|
|
23
|
+
/** storages 接管表访问层(与事件日志同介质)。 */
|
|
24
|
+
repository: StorageRepository;
|
|
25
|
+
/** 投影缓存的写节流参数(上游 base 装配的部署值)。 */
|
|
26
|
+
projectionCache: ProjectionCacheConfig;
|
|
27
|
+
/** 介质就绪信号:投影缓存直读介质前必须等到。 */
|
|
28
|
+
ready: Promise<unknown>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Install the storages takeover on the session-rdb plugin context.
|
|
33
|
+
* @param ctx - session-rdb plugin context.
|
|
34
|
+
* @param options - repository and projection-cache throttle parameters.
|
|
35
|
+
*/
|
|
36
|
+
export function installStorageTakeover(ctx: Context, options: StorageTakeoverOptions): void {
|
|
37
|
+
// workspace 域:上游 workspaceRegistry 仍持有服务与内存态,这里只提供它
|
|
38
|
+
// 依赖的 storage-domain 介质(backend 名 `rdb`,由 profile patch 路由)。
|
|
39
|
+
ctx.inject(["storage"], (storageCtx) => {
|
|
40
|
+
const backend = new RdbStorageBackend(options.repository);
|
|
41
|
+
storageCtx.effect(() => {
|
|
42
|
+
const unregister = storageCtx.storage.backend.register(RDB_STORAGE_BACKEND, backend);
|
|
43
|
+
return async () => {
|
|
44
|
+
unregister();
|
|
45
|
+
await backend.close();
|
|
46
|
+
};
|
|
47
|
+
}, "session-rdb.storageBackend");
|
|
48
|
+
storageCtx.provide(storageBackendServiceKey(RDB_STORAGE_BACKEND), backend);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 投影 checkpoint:上游插件被禁用后由本服务提供同名 API。
|
|
52
|
+
ctx.inject(["sessionProjections"], (projectionCtx) => {
|
|
53
|
+
new SessionProjectionCacheRdb(
|
|
54
|
+
projectionCtx,
|
|
55
|
+
options.projectionCache,
|
|
56
|
+
options.repository,
|
|
57
|
+
options.ready,
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
}
|