@voidhash/mimic-effect 1.0.0-beta.1 → 1.0.0-beta.10
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/.turbo/turbo-build.log +116 -74
- package/dist/ColdStorage.cjs +9 -5
- package/dist/ColdStorage.d.cts.map +1 -1
- package/dist/ColdStorage.d.mts.map +1 -1
- package/dist/ColdStorage.mjs +9 -5
- package/dist/ColdStorage.mjs.map +1 -1
- package/dist/DocumentInstance.cjs +263 -0
- package/dist/DocumentInstance.d.cts +78 -0
- package/dist/DocumentInstance.d.cts.map +1 -0
- package/dist/DocumentInstance.d.mts +78 -0
- package/dist/DocumentInstance.d.mts.map +1 -0
- package/dist/DocumentInstance.mjs +264 -0
- package/dist/DocumentInstance.mjs.map +1 -0
- package/dist/Errors.cjs +10 -1
- package/dist/Errors.d.cts +18 -3
- package/dist/Errors.d.cts.map +1 -1
- package/dist/Errors.d.mts +18 -3
- package/dist/Errors.d.mts.map +1 -1
- package/dist/Errors.mjs +9 -1
- package/dist/Errors.mjs.map +1 -1
- package/dist/HotStorage.cjs +39 -12
- package/dist/HotStorage.d.cts +17 -1
- package/dist/HotStorage.d.cts.map +1 -1
- package/dist/HotStorage.d.mts +17 -1
- package/dist/HotStorage.d.mts.map +1 -1
- package/dist/HotStorage.mjs +39 -12
- package/dist/HotStorage.mjs.map +1 -1
- package/dist/Metrics.cjs +29 -1
- package/dist/Metrics.d.cts +5 -0
- package/dist/Metrics.d.cts.map +1 -1
- package/dist/Metrics.d.mts +5 -0
- package/dist/Metrics.d.mts.map +1 -1
- package/dist/Metrics.mjs +26 -1
- package/dist/Metrics.mjs.map +1 -1
- package/dist/MimicClusterServerEngine.cjs +44 -139
- package/dist/MimicClusterServerEngine.d.cts.map +1 -1
- package/dist/MimicClusterServerEngine.d.mts +1 -1
- package/dist/MimicClusterServerEngine.d.mts.map +1 -1
- package/dist/MimicClusterServerEngine.mjs +46 -141
- package/dist/MimicClusterServerEngine.mjs.map +1 -1
- package/dist/MimicServer.cjs +20 -20
- package/dist/MimicServer.d.cts.map +1 -1
- package/dist/MimicServer.d.mts.map +1 -1
- package/dist/MimicServer.mjs +20 -20
- package/dist/MimicServer.mjs.map +1 -1
- package/dist/MimicServerEngine.cjs +92 -11
- package/dist/MimicServerEngine.d.cts +12 -4
- package/dist/MimicServerEngine.d.cts.map +1 -1
- package/dist/MimicServerEngine.d.mts +12 -4
- package/dist/MimicServerEngine.d.mts.map +1 -1
- package/dist/MimicServerEngine.mjs +94 -13
- package/dist/MimicServerEngine.mjs.map +1 -1
- package/dist/PresenceManager.cjs +5 -5
- package/dist/PresenceManager.d.cts.map +1 -1
- package/dist/PresenceManager.d.mts.map +1 -1
- package/dist/PresenceManager.mjs +5 -5
- package/dist/PresenceManager.mjs.map +1 -1
- package/dist/Protocol.d.cts +1 -1
- package/dist/Protocol.d.mts +1 -1
- package/dist/Types.d.cts +9 -2
- package/dist/Types.d.cts.map +1 -1
- package/dist/Types.d.mts +9 -2
- package/dist/Types.d.mts.map +1 -1
- package/dist/index.cjs +5 -6
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +3 -3
- package/dist/testing/ColdStorageTestSuite.cjs +508 -0
- package/dist/testing/ColdStorageTestSuite.d.cts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.d.mts +36 -0
- package/dist/testing/ColdStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/ColdStorageTestSuite.mjs +508 -0
- package/dist/testing/ColdStorageTestSuite.mjs.map +1 -0
- package/dist/testing/FailingStorage.cjs +162 -0
- package/dist/testing/FailingStorage.d.cts +43 -0
- package/dist/testing/FailingStorage.d.cts.map +1 -0
- package/dist/testing/FailingStorage.d.mts +43 -0
- package/dist/testing/FailingStorage.d.mts.map +1 -0
- package/dist/testing/FailingStorage.mjs +163 -0
- package/dist/testing/FailingStorage.mjs.map +1 -0
- package/dist/testing/HotStorageTestSuite.cjs +820 -0
- package/dist/testing/HotStorageTestSuite.d.cts +42 -0
- package/dist/testing/HotStorageTestSuite.d.cts.map +1 -0
- package/dist/testing/HotStorageTestSuite.d.mts +42 -0
- package/dist/testing/HotStorageTestSuite.d.mts.map +1 -0
- package/dist/testing/HotStorageTestSuite.mjs +820 -0
- package/dist/testing/HotStorageTestSuite.mjs.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.cjs +487 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts +37 -0
- package/dist/testing/StorageIntegrationTestSuite.d.cts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts +37 -0
- package/dist/testing/StorageIntegrationTestSuite.d.mts.map +1 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs +487 -0
- package/dist/testing/StorageIntegrationTestSuite.mjs.map +1 -0
- package/dist/testing/assertions.cjs +117 -0
- package/dist/testing/assertions.mjs +112 -0
- package/dist/testing/assertions.mjs.map +1 -0
- package/dist/testing/index.cjs +14 -0
- package/dist/testing/index.d.cts +6 -0
- package/dist/testing/index.d.mts +6 -0
- package/dist/testing/index.mjs +7 -0
- package/dist/testing/types.cjs +15 -0
- package/dist/testing/types.d.cts +90 -0
- package/dist/testing/types.d.cts.map +1 -0
- package/dist/testing/types.d.mts +90 -0
- package/dist/testing/types.d.mts.map +1 -0
- package/dist/testing/types.mjs +16 -0
- package/dist/testing/types.mjs.map +1 -0
- package/package.json +8 -3
- package/src/ColdStorage.ts +21 -12
- package/src/DocumentInstance.ts +527 -0
- package/src/Errors.ts +15 -1
- package/src/HotStorage.ts +115 -24
- package/src/Metrics.ts +30 -0
- package/src/MimicClusterServerEngine.ts +120 -275
- package/src/MimicServer.ts +83 -75
- package/src/MimicServerEngine.ts +230 -30
- package/src/PresenceManager.ts +44 -34
- package/src/Types.ts +9 -2
- package/src/index.ts +5 -35
- package/src/testing/ColdStorageTestSuite.ts +589 -0
- package/src/testing/FailingStorage.ts +338 -0
- package/src/testing/HotStorageTestSuite.ts +1105 -0
- package/src/testing/StorageIntegrationTestSuite.ts +736 -0
- package/src/testing/assertions.ts +188 -0
- package/src/testing/index.ts +83 -0
- package/src/testing/types.ts +100 -0
- package/tests/ColdStorage.test.ts +8 -120
- package/tests/DocumentInstance.test.ts +669 -0
- package/tests/HotStorage.test.ts +7 -126
- package/tests/StorageIntegration.test.ts +259 -0
- package/tsdown.config.ts +1 -1
- package/dist/DocumentManager.cjs +0 -229
- package/dist/DocumentManager.d.cts +0 -59
- package/dist/DocumentManager.d.cts.map +0 -1
- package/dist/DocumentManager.d.mts +0 -59
- package/dist/DocumentManager.d.mts.map +0 -1
- package/dist/DocumentManager.mjs +0 -227
- package/dist/DocumentManager.mjs.map +0 -1
- package/src/DocumentManager.ts +0 -506
- package/tests/DocumentManager.test.ts +0 -335
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentManager.d.cts","names":[],"sources":["../src/DocumentManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;AAgF0C,KApC9B,YAAA,GAoCqC;EAAM,SAAA,OAAA,EAAA,IAAA;EACtD,SAAA,OAAA,EAAA,MAAA;;;;;AASD;AAE2C;;UArC1B,eAAA;;;;EAoEJ,SAAA,MAAA,EAAA,CAAA,UAAyB,EAAA,MAAA,EAAA,WAAQ,EA9D7B,WAAA,CAAY,WAgEwC,EAAA,GA/D9D,MAAA,CAAO,MA+DuD,CA/DhD,YA+DgD,CAAA;EAwXxD;;;gDAlbmC,MAAA,CAAO,OAAO;;;;8CAOvD,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,yBAAyB,KAAA,CAAM;;;;;0CAMxB,MAAA,CAAO;;cAChD;;;;cASY,kBAAA,SAA2B,uBAAA;cAEG;;;;cA+B9B,wBAAA,SAAiC,6BAAA;cA0XjC"}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { ResolvedConfig } from "./Types.mjs";
|
|
2
|
-
import { ServerBroadcast, SnapshotMessage } from "./Protocol.mjs";
|
|
3
|
-
import { ColdStorageTag } from "./ColdStorage.mjs";
|
|
4
|
-
import { HotStorageTag } from "./HotStorage.mjs";
|
|
5
|
-
import { Context, Effect, Layer, Scope, Stream } from "effect";
|
|
6
|
-
import { Transaction } from "@voidhash/mimic";
|
|
7
|
-
|
|
8
|
-
//#region src/DocumentManager.d.ts
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Result of submitting a transaction
|
|
12
|
-
*/
|
|
13
|
-
type SubmitResult = {
|
|
14
|
-
readonly success: true;
|
|
15
|
-
readonly version: number;
|
|
16
|
-
} | {
|
|
17
|
-
readonly success: false;
|
|
18
|
-
readonly reason: string;
|
|
19
|
-
};
|
|
20
|
-
/**
|
|
21
|
-
* Internal service for managing document lifecycle.
|
|
22
|
-
*/
|
|
23
|
-
interface DocumentManager {
|
|
24
|
-
/**
|
|
25
|
-
* Submit a transaction to a document.
|
|
26
|
-
*/
|
|
27
|
-
readonly submit: (documentId: string, transaction: Transaction.Transaction) => Effect.Effect<SubmitResult>;
|
|
28
|
-
/**
|
|
29
|
-
* Get a snapshot of a document.
|
|
30
|
-
*/
|
|
31
|
-
readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage>;
|
|
32
|
-
/**
|
|
33
|
-
* Subscribe to broadcasts for a document.
|
|
34
|
-
*/
|
|
35
|
-
readonly subscribe: (documentId: string) => Effect.Effect<Stream.Stream<ServerBroadcast>, never, Scope.Scope>;
|
|
36
|
-
/**
|
|
37
|
-
* Touch a document to update its last activity time.
|
|
38
|
-
* Call this on any client activity to prevent idle GC.
|
|
39
|
-
*/
|
|
40
|
-
readonly touch: (documentId: string) => Effect.Effect<void>;
|
|
41
|
-
}
|
|
42
|
-
declare const DocumentManagerTag_base: Context.TagClass<DocumentManagerTag, "@voidhash/mimic-effect/DocumentManager", DocumentManager>;
|
|
43
|
-
/**
|
|
44
|
-
* Context tag for DocumentManager service
|
|
45
|
-
*/
|
|
46
|
-
declare class DocumentManagerTag extends DocumentManagerTag_base {}
|
|
47
|
-
declare const DocumentManagerConfigTag_base: Context.TagClass<DocumentManagerConfigTag, "@voidhash/mimic-effect/DocumentManagerConfig", ResolvedConfig<Primitive.AnyPrimitive>>;
|
|
48
|
-
/**
|
|
49
|
-
* Context tag for DocumentManager configuration
|
|
50
|
-
*/
|
|
51
|
-
declare class DocumentManagerConfigTag extends DocumentManagerConfigTag_base {}
|
|
52
|
-
declare const DocumentManager: {
|
|
53
|
-
Tag: typeof DocumentManagerTag;
|
|
54
|
-
ConfigTag: typeof DocumentManagerConfigTag;
|
|
55
|
-
layer: Layer.Layer<DocumentManagerTag, never, ColdStorageTag | HotStorageTag | DocumentManagerConfigTag>;
|
|
56
|
-
};
|
|
57
|
-
//#endregion
|
|
58
|
-
export { DocumentManager, DocumentManagerConfigTag, DocumentManagerTag, SubmitResult };
|
|
59
|
-
//# sourceMappingURL=DocumentManager.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentManager.d.mts","names":[],"sources":["../src/DocumentManager.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;AAgF0C,KApC9B,YAAA,GAoCqC;EAAM,SAAA,OAAA,EAAA,IAAA;EACtD,SAAA,OAAA,EAAA,MAAA;;;;;AASD;AAE2C;;UArC1B,eAAA;;;;EAoEJ,SAAA,MAAA,EAAA,CAAA,UAAyB,EAAA,MAAA,EAAA,WAAQ,EA9D7B,WAAA,CAAY,WAgEwC,EAAA,GA/D9D,MAAA,CAAO,MA+DuD,CA/DhD,YA+DgD,CAAA;EAwXxD;;;gDAlbmC,MAAA,CAAO,OAAO;;;;8CAOvD,MAAA,CAAO,OAAO,MAAA,CAAO,OAAO,yBAAyB,KAAA,CAAM;;;;;0CAMxB,MAAA,CAAO;;cAChD;;;;cASY,kBAAA,SAA2B,uBAAA;cAEG;;;;cA+B9B,wBAAA,SAAiC,6BAAA;cA0XjC"}
|
package/dist/DocumentManager.mjs
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { ColdStorageTag } from "./ColdStorage.mjs";
|
|
2
|
-
import { HotStorageTag } from "./HotStorage.mjs";
|
|
3
|
-
import { documentsActive, documentsCreated, documentsEvicted, documentsRestored, storageSnapshotLatency, storageSnapshots, storageWalAppends, transactionsLatency, transactionsProcessed, transactionsRejected } from "./Metrics.mjs";
|
|
4
|
-
import { Context, Duration, Effect, HashMap, Layer, Metric, PubSub, Ref, Schedule, Stream } from "effect";
|
|
5
|
-
import { ServerDocument } from "@voidhash/mimic/server";
|
|
6
|
-
|
|
7
|
-
//#region src/DocumentManager.ts
|
|
8
|
-
/**
|
|
9
|
-
* @voidhash/mimic-effect - DocumentManager
|
|
10
|
-
*
|
|
11
|
-
* Internal service for managing document lifecycle, including:
|
|
12
|
-
* - Document creation and restoration
|
|
13
|
-
* - Transaction processing
|
|
14
|
-
* - WAL management
|
|
15
|
-
* - Snapshot scheduling
|
|
16
|
-
* - Idle document GC
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* Context tag for DocumentManager service
|
|
20
|
-
*/
|
|
21
|
-
var DocumentManagerTag = class extends Context.Tag("@voidhash/mimic-effect/DocumentManager")() {};
|
|
22
|
-
/**
|
|
23
|
-
* Context tag for DocumentManager configuration
|
|
24
|
-
*/
|
|
25
|
-
var DocumentManagerConfigTag = class extends Context.Tag("@voidhash/mimic-effect/DocumentManagerConfig")() {};
|
|
26
|
-
/**
|
|
27
|
-
* Create the DocumentManager layer.
|
|
28
|
-
* Requires ColdStorage, HotStorage, and DocumentManagerConfig.
|
|
29
|
-
*/
|
|
30
|
-
const layer = Layer.scoped(DocumentManagerTag, Effect.gen(function* () {
|
|
31
|
-
const coldStorage = yield* ColdStorageTag;
|
|
32
|
-
const hotStorage = yield* HotStorageTag;
|
|
33
|
-
const config = yield* DocumentManagerConfigTag;
|
|
34
|
-
const store = yield* Ref.make(HashMap.empty());
|
|
35
|
-
const SCHEMA_VERSION = 1;
|
|
36
|
-
/**
|
|
37
|
-
* Compute initial state for a new document
|
|
38
|
-
*/
|
|
39
|
-
const computeInitialState = (documentId) => {
|
|
40
|
-
if (config.initial === void 0) return Effect.succeed(void 0);
|
|
41
|
-
if (typeof config.initial === "function") return config.initial({ documentId });
|
|
42
|
-
return Effect.succeed(config.initial);
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* Restore a document from storage
|
|
46
|
-
*/
|
|
47
|
-
const restoreDocument = (documentId) => Effect.gen(function* () {
|
|
48
|
-
const storedDoc = yield* Effect.catchAll(coldStorage.load(documentId), () => Effect.succeed(void 0));
|
|
49
|
-
let initialState;
|
|
50
|
-
let initialVersion = 0;
|
|
51
|
-
if (storedDoc) {
|
|
52
|
-
initialState = storedDoc.state;
|
|
53
|
-
initialVersion = storedDoc.version;
|
|
54
|
-
} else initialState = yield* computeInitialState(documentId);
|
|
55
|
-
const pubsub = yield* PubSub.unbounded();
|
|
56
|
-
const lastSnapshotVersion = yield* Ref.make(initialVersion);
|
|
57
|
-
const lastSnapshotTime = yield* Ref.make(Date.now());
|
|
58
|
-
const transactionsSinceSnapshot = yield* Ref.make(0);
|
|
59
|
-
const lastActivityTime = yield* Ref.make(Date.now());
|
|
60
|
-
const document = ServerDocument.make({
|
|
61
|
-
schema: config.schema,
|
|
62
|
-
initialState,
|
|
63
|
-
initialVersion,
|
|
64
|
-
maxTransactionHistory: config.maxTransactionHistory,
|
|
65
|
-
onBroadcast: (message) => {
|
|
66
|
-
Effect.runSync(PubSub.publish(pubsub, {
|
|
67
|
-
type: "transaction",
|
|
68
|
-
transaction: message.transaction,
|
|
69
|
-
version: message.version
|
|
70
|
-
}));
|
|
71
|
-
},
|
|
72
|
-
onRejection: (transactionId, reason) => {
|
|
73
|
-
Effect.runSync(PubSub.publish(pubsub, {
|
|
74
|
-
type: "error",
|
|
75
|
-
transactionId,
|
|
76
|
-
reason
|
|
77
|
-
}));
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
const walEntries = yield* Effect.catchAll(hotStorage.getEntries(documentId, initialVersion), () => Effect.succeed([]));
|
|
81
|
-
for (const entry of walEntries) {
|
|
82
|
-
const result = document.submit(entry.transaction);
|
|
83
|
-
if (!result.success) yield* Effect.logWarning("Skipping corrupted WAL entry", {
|
|
84
|
-
documentId,
|
|
85
|
-
version: entry.version,
|
|
86
|
-
reason: result.reason
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
const instance = {
|
|
90
|
-
document,
|
|
91
|
-
pubsub,
|
|
92
|
-
lastSnapshotVersion,
|
|
93
|
-
lastSnapshotTime,
|
|
94
|
-
transactionsSinceSnapshot,
|
|
95
|
-
lastActivityTime
|
|
96
|
-
};
|
|
97
|
-
if (storedDoc) yield* Metric.increment(documentsRestored);
|
|
98
|
-
else yield* Metric.increment(documentsCreated);
|
|
99
|
-
yield* Metric.incrementBy(documentsActive, 1);
|
|
100
|
-
return instance;
|
|
101
|
-
});
|
|
102
|
-
/**
|
|
103
|
-
* Get or create a document instance
|
|
104
|
-
*/
|
|
105
|
-
const getOrCreateDocument = (documentId) => Effect.gen(function* () {
|
|
106
|
-
const current = yield* Ref.get(store);
|
|
107
|
-
const existing = HashMap.get(current, documentId);
|
|
108
|
-
if (existing._tag === "Some") {
|
|
109
|
-
yield* Ref.set(existing.value.lastActivityTime, Date.now());
|
|
110
|
-
return existing.value;
|
|
111
|
-
}
|
|
112
|
-
const instance = yield* restoreDocument(documentId);
|
|
113
|
-
yield* Ref.update(store, (map) => HashMap.set(map, documentId, instance));
|
|
114
|
-
return instance;
|
|
115
|
-
});
|
|
116
|
-
/**
|
|
117
|
-
* Save a snapshot to ColdStorage and truncate WAL
|
|
118
|
-
*/
|
|
119
|
-
const saveSnapshot = (documentId, instance) => Effect.gen(function* () {
|
|
120
|
-
const state = instance.document.get();
|
|
121
|
-
const version = instance.document.getVersion();
|
|
122
|
-
if (state === void 0) return;
|
|
123
|
-
const storedDoc = {
|
|
124
|
-
state,
|
|
125
|
-
version,
|
|
126
|
-
schemaVersion: SCHEMA_VERSION,
|
|
127
|
-
savedAt: Date.now()
|
|
128
|
-
};
|
|
129
|
-
const snapshotStartTime = Date.now();
|
|
130
|
-
yield* Effect.catchAll(coldStorage.save(documentId, storedDoc), (e) => Effect.logError("Failed to save snapshot", {
|
|
131
|
-
documentId,
|
|
132
|
-
error: e
|
|
133
|
-
}));
|
|
134
|
-
const snapshotDuration = Date.now() - snapshotStartTime;
|
|
135
|
-
yield* Metric.increment(storageSnapshots);
|
|
136
|
-
yield* Metric.update(storageSnapshotLatency, snapshotDuration);
|
|
137
|
-
yield* Effect.catchAll(hotStorage.truncate(documentId, version), (e) => Effect.logError("Failed to truncate WAL", {
|
|
138
|
-
documentId,
|
|
139
|
-
error: e
|
|
140
|
-
}));
|
|
141
|
-
yield* Ref.set(instance.lastSnapshotVersion, version);
|
|
142
|
-
yield* Ref.set(instance.lastSnapshotTime, Date.now());
|
|
143
|
-
yield* Ref.set(instance.transactionsSinceSnapshot, 0);
|
|
144
|
-
});
|
|
145
|
-
/**
|
|
146
|
-
* Check if snapshot should be triggered
|
|
147
|
-
*/
|
|
148
|
-
const checkSnapshotTriggers = (documentId, instance) => Effect.gen(function* () {
|
|
149
|
-
const txCount = yield* Ref.get(instance.transactionsSinceSnapshot);
|
|
150
|
-
const lastTime = yield* Ref.get(instance.lastSnapshotTime);
|
|
151
|
-
const now = Date.now();
|
|
152
|
-
const intervalMs = Duration.toMillis(config.snapshot.interval);
|
|
153
|
-
if (txCount >= config.snapshot.transactionThreshold) {
|
|
154
|
-
yield* saveSnapshot(documentId, instance);
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
if (now - lastTime >= intervalMs) {
|
|
158
|
-
yield* saveSnapshot(documentId, instance);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
yield* Effect.gen(function* () {
|
|
163
|
-
yield* Effect.gen(function* () {
|
|
164
|
-
const current = yield* Ref.get(store);
|
|
165
|
-
const now = Date.now();
|
|
166
|
-
const maxIdleMs = Duration.toMillis(config.maxIdleTime);
|
|
167
|
-
for (const [documentId, instance] of current) if (now - (yield* Ref.get(instance.lastActivityTime)) >= maxIdleMs) {
|
|
168
|
-
yield* saveSnapshot(documentId, instance);
|
|
169
|
-
yield* Ref.update(store, (map) => HashMap.remove(map, documentId));
|
|
170
|
-
yield* Metric.increment(documentsEvicted);
|
|
171
|
-
yield* Metric.incrementBy(documentsActive, -1);
|
|
172
|
-
yield* Effect.logInfo("Document evicted due to idle timeout", { documentId });
|
|
173
|
-
}
|
|
174
|
-
}).pipe(Effect.repeat(Schedule.spaced("1 minute")), Effect.fork);
|
|
175
|
-
});
|
|
176
|
-
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
177
|
-
const current = yield* Ref.get(store);
|
|
178
|
-
for (const [documentId, instance] of current) yield* saveSnapshot(documentId, instance);
|
|
179
|
-
yield* Effect.logInfo("DocumentManager shutdown complete");
|
|
180
|
-
}));
|
|
181
|
-
return {
|
|
182
|
-
submit: (documentId, transaction) => Effect.gen(function* () {
|
|
183
|
-
const instance = yield* getOrCreateDocument(documentId);
|
|
184
|
-
const submitStartTime = Date.now();
|
|
185
|
-
const result = instance.document.submit(transaction);
|
|
186
|
-
const latency = Date.now() - submitStartTime;
|
|
187
|
-
yield* Metric.update(transactionsLatency, latency);
|
|
188
|
-
if (result.success) {
|
|
189
|
-
yield* Metric.increment(transactionsProcessed);
|
|
190
|
-
const walEntry = {
|
|
191
|
-
transaction,
|
|
192
|
-
version: result.version,
|
|
193
|
-
timestamp: Date.now()
|
|
194
|
-
};
|
|
195
|
-
yield* Effect.catchAll(hotStorage.append(documentId, walEntry), (e) => Effect.logError("Failed to append to WAL", {
|
|
196
|
-
documentId,
|
|
197
|
-
error: e
|
|
198
|
-
}));
|
|
199
|
-
yield* Metric.increment(storageWalAppends);
|
|
200
|
-
yield* Ref.update(instance.transactionsSinceSnapshot, (n) => n + 1);
|
|
201
|
-
yield* checkSnapshotTriggers(documentId, instance);
|
|
202
|
-
} else yield* Metric.increment(transactionsRejected);
|
|
203
|
-
return result;
|
|
204
|
-
}),
|
|
205
|
-
getSnapshot: (documentId) => Effect.gen(function* () {
|
|
206
|
-
return (yield* getOrCreateDocument(documentId)).document.getSnapshot();
|
|
207
|
-
}),
|
|
208
|
-
subscribe: (documentId) => Effect.gen(function* () {
|
|
209
|
-
const instance = yield* getOrCreateDocument(documentId);
|
|
210
|
-
return Stream.fromPubSub(instance.pubsub);
|
|
211
|
-
}),
|
|
212
|
-
touch: (documentId) => Effect.gen(function* () {
|
|
213
|
-
const current = yield* Ref.get(store);
|
|
214
|
-
const existing = HashMap.get(current, documentId);
|
|
215
|
-
if (existing._tag === "Some") yield* Ref.set(existing.value.lastActivityTime, Date.now());
|
|
216
|
-
})
|
|
217
|
-
};
|
|
218
|
-
}));
|
|
219
|
-
const DocumentManager = {
|
|
220
|
-
Tag: DocumentManagerTag,
|
|
221
|
-
ConfigTag: DocumentManagerConfigTag,
|
|
222
|
-
layer
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
//#endregion
|
|
226
|
-
export { DocumentManager, DocumentManagerConfigTag, DocumentManagerTag, layer };
|
|
227
|
-
//# sourceMappingURL=DocumentManager.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DocumentManager.mjs","names":["layer: Layer.Layer<\n DocumentManagerTag,\n never,\n ColdStorageTag | HotStorageTag | DocumentManagerConfigTag\n>","initialState: Primitive.InferSetInput<typeof config.schema> | undefined","instance: DocumentInstance<typeof config.schema>","Metrics.documentsRestored","Metrics.documentsCreated","Metrics.documentsActive","storedDoc: StoredDocument","Metrics.storageSnapshots","Metrics.storageSnapshotLatency","Metrics.documentsEvicted","Metrics.transactionsLatency","Metrics.transactionsProcessed","walEntry: WalEntry","Metrics.storageWalAppends","Metrics.transactionsRejected"],"sources":["../src/DocumentManager.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - DocumentManager\n *\n * Internal service for managing document lifecycle, including:\n * - Document creation and restoration\n * - Transaction processing\n * - WAL management\n * - Snapshot scheduling\n * - Idle document GC\n */\nimport {\n Context,\n Duration,\n Effect,\n Fiber,\n HashMap,\n Layer,\n Metric,\n PubSub,\n Ref,\n Schedule,\n Scope,\n Stream,\n} from \"effect\";\nimport { Primitive, Transaction } from \"@voidhash/mimic\";\nimport { ServerDocument } from \"@voidhash/mimic/server\";\nimport type {\n Initial,\n ResolvedConfig,\n StoredDocument,\n WalEntry,\n} from \"./Types\";\nimport type { SnapshotMessage, ServerBroadcast } from \"./Protocol\";\nimport { ColdStorageTag, type ColdStorage } from \"./ColdStorage\";\nimport { HotStorageTag, type HotStorage } from \"./HotStorage\";\nimport * as Metrics from \"./Metrics\";\n\n// =============================================================================\n// Submit Result Types\n// =============================================================================\n\n/**\n * Result of submitting a transaction\n */\nexport type SubmitResult =\n | { readonly success: true; readonly version: number }\n | { readonly success: false; readonly reason: string };\n\n// =============================================================================\n// DocumentManager Interface\n// =============================================================================\n\n/**\n * Internal service for managing document lifecycle.\n */\nexport interface DocumentManager {\n /**\n * Submit a transaction to a document.\n */\n readonly submit: (\n documentId: string,\n transaction: Transaction.Transaction\n ) => Effect.Effect<SubmitResult>;\n\n /**\n * Get a snapshot of a document.\n */\n readonly getSnapshot: (documentId: string) => Effect.Effect<SnapshotMessage>;\n\n /**\n * Subscribe to broadcasts for a document.\n */\n readonly subscribe: (\n documentId: string\n ) => Effect.Effect<Stream.Stream<ServerBroadcast>, never, Scope.Scope>;\n\n /**\n * Touch a document to update its last activity time.\n * Call this on any client activity to prevent idle GC.\n */\n readonly touch: (documentId: string) => Effect.Effect<void>;\n}\n\n// =============================================================================\n// Context Tag\n// =============================================================================\n\n/**\n * Context tag for DocumentManager service\n */\nexport class DocumentManagerTag extends Context.Tag(\n \"@voidhash/mimic-effect/DocumentManager\"\n)<DocumentManagerTag, DocumentManager>() {}\n\n// =============================================================================\n// Internal Types\n// =============================================================================\n\n/**\n * Document instance state\n */\ninterface DocumentInstance<TSchema extends Primitive.AnyPrimitive> {\n /** The underlying ServerDocument */\n readonly document: ServerDocument.ServerDocument<TSchema>;\n /** PubSub for broadcasting messages */\n readonly pubsub: PubSub.PubSub<ServerBroadcast>;\n /** Version at last snapshot */\n readonly lastSnapshotVersion: Ref.Ref<number>;\n /** Timestamp of last snapshot (ms) */\n readonly lastSnapshotTime: Ref.Ref<number>;\n /** Transactions since last snapshot */\n readonly transactionsSinceSnapshot: Ref.Ref<number>;\n /** Last activity timestamp (ms) */\n readonly lastActivityTime: Ref.Ref<number>;\n}\n\n// =============================================================================\n// Config Context Tag\n// =============================================================================\n\n/**\n * Context tag for DocumentManager configuration\n */\nexport class DocumentManagerConfigTag extends Context.Tag(\n \"@voidhash/mimic-effect/DocumentManagerConfig\"\n)<DocumentManagerConfigTag, ResolvedConfig<Primitive.AnyPrimitive>>() {}\n\n// =============================================================================\n// Layer Implementation\n// =============================================================================\n\n/**\n * Create the DocumentManager layer.\n * Requires ColdStorage, HotStorage, and DocumentManagerConfig.\n */\nexport const layer: Layer.Layer<\n DocumentManagerTag,\n never,\n ColdStorageTag | HotStorageTag | DocumentManagerConfigTag\n> = Layer.scoped(\n DocumentManagerTag,\n Effect.gen(function* () {\n const coldStorage = yield* ColdStorageTag;\n const hotStorage = yield* HotStorageTag;\n const config = yield* DocumentManagerConfigTag;\n\n // Store: documentId -> DocumentInstance\n const store = yield* Ref.make(\n HashMap.empty<string, DocumentInstance<Primitive.AnyPrimitive>>()\n );\n\n // Current schema version (hard-coded to 1 for now)\n const SCHEMA_VERSION = 1;\n\n /**\n * Compute initial state for a new document\n */\n const computeInitialState = (\n documentId: string\n ): Effect.Effect<Primitive.InferSetInput<typeof config.schema> | undefined> => {\n if (config.initial === undefined) {\n return Effect.succeed(undefined);\n }\n\n // Check if it's a function or static value\n if (typeof config.initial === \"function\") {\n return (config.initial as (ctx: { documentId: string }) => Effect.Effect<Primitive.InferSetInput<typeof config.schema>>)({ documentId });\n }\n\n return Effect.succeed(config.initial as Primitive.InferSetInput<typeof config.schema>);\n };\n\n /**\n * Restore a document from storage\n */\n const restoreDocument = (\n documentId: string\n ): Effect.Effect<DocumentInstance<typeof config.schema>> =>\n Effect.gen(function* () {\n // 1. Load snapshot from ColdStorage\n const storedDoc = yield* Effect.catchAll(\n coldStorage.load(documentId),\n () => Effect.succeed(undefined)\n );\n\n let initialState: Primitive.InferSetInput<typeof config.schema> | undefined;\n let initialVersion = 0;\n\n if (storedDoc) {\n // Use stored state\n initialState = storedDoc.state as Primitive.InferSetInput<typeof config.schema>;\n initialVersion = storedDoc.version;\n } else {\n // Compute initial state\n initialState = yield* computeInitialState(documentId);\n }\n\n // 2. Create PubSub for broadcasting\n const pubsub = yield* PubSub.unbounded<ServerBroadcast>();\n\n // 3. Create refs for tracking\n const lastSnapshotVersion = yield* Ref.make(initialVersion);\n const lastSnapshotTime = yield* Ref.make(Date.now());\n const transactionsSinceSnapshot = yield* Ref.make(0);\n const lastActivityTime = yield* Ref.make(Date.now());\n\n // 4. Create ServerDocument with callbacks\n const document = ServerDocument.make({\n schema: config.schema,\n initialState,\n initialVersion,\n maxTransactionHistory: config.maxTransactionHistory,\n onBroadcast: (message: ServerDocument.TransactionMessage) => {\n // This is called synchronously by ServerDocument\n // We need to publish to PubSub\n Effect.runSync(\n PubSub.publish(pubsub, {\n type: \"transaction\",\n transaction: message.transaction,\n version: message.version,\n })\n );\n },\n onRejection: (transactionId: string, reason: string) => {\n Effect.runSync(\n PubSub.publish(pubsub, {\n type: \"error\",\n transactionId,\n reason,\n })\n );\n },\n });\n\n // 5. Load and replay WAL entries\n const walEntries = yield* Effect.catchAll(\n hotStorage.getEntries(documentId, initialVersion),\n () => Effect.succeed([] as WalEntry[])\n );\n\n for (const entry of walEntries) {\n const result = document.submit(entry.transaction);\n if (!result.success) {\n yield* Effect.logWarning(\"Skipping corrupted WAL entry\", {\n documentId,\n version: entry.version,\n reason: result.reason,\n });\n }\n }\n\n const instance: DocumentInstance<typeof config.schema> = {\n document,\n pubsub,\n lastSnapshotVersion,\n lastSnapshotTime,\n transactionsSinceSnapshot,\n lastActivityTime,\n };\n\n // Track metrics - determine if restored or created\n if (storedDoc) {\n yield* Metric.increment(Metrics.documentsRestored);\n } else {\n yield* Metric.increment(Metrics.documentsCreated);\n }\n yield* Metric.incrementBy(Metrics.documentsActive, 1);\n\n return instance;\n });\n\n /**\n * Get or create a document instance\n */\n const getOrCreateDocument = (\n documentId: string\n ): Effect.Effect<DocumentInstance<typeof config.schema>> =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n\n if (existing._tag === \"Some\") {\n // Update activity time\n yield* Ref.set(existing.value.lastActivityTime, Date.now());\n return existing.value as DocumentInstance<typeof config.schema>;\n }\n\n // Restore document\n const instance = yield* restoreDocument(documentId);\n\n // Store it\n yield* Ref.update(store, (map) =>\n HashMap.set(map, documentId, instance)\n );\n\n return instance;\n });\n\n /**\n * Save a snapshot to ColdStorage and truncate WAL\n */\n const saveSnapshot = (\n documentId: string,\n instance: DocumentInstance<typeof config.schema>\n ): Effect.Effect<void> =>\n Effect.gen(function* () {\n const state = instance.document.get();\n const version = instance.document.getVersion();\n\n if (state === undefined) {\n return;\n }\n\n const storedDoc: StoredDocument = {\n state,\n version,\n schemaVersion: SCHEMA_VERSION,\n savedAt: Date.now(),\n };\n\n const snapshotStartTime = Date.now();\n\n // Save to ColdStorage\n yield* Effect.catchAll(coldStorage.save(documentId, storedDoc), (e) =>\n Effect.logError(\"Failed to save snapshot\", { documentId, error: e })\n );\n\n // Track snapshot metrics\n const snapshotDuration = Date.now() - snapshotStartTime;\n yield* Metric.increment(Metrics.storageSnapshots);\n yield* Metric.update(Metrics.storageSnapshotLatency, snapshotDuration);\n\n // Truncate WAL\n yield* Effect.catchAll(hotStorage.truncate(documentId, version), (e) =>\n Effect.logError(\"Failed to truncate WAL\", { documentId, error: e })\n );\n\n // Update tracking\n yield* Ref.set(instance.lastSnapshotVersion, version);\n yield* Ref.set(instance.lastSnapshotTime, Date.now());\n yield* Ref.set(instance.transactionsSinceSnapshot, 0);\n });\n\n /**\n * Check if snapshot should be triggered\n */\n const checkSnapshotTriggers = (\n documentId: string,\n instance: DocumentInstance<typeof config.schema>\n ): Effect.Effect<void> =>\n Effect.gen(function* () {\n const txCount = yield* Ref.get(instance.transactionsSinceSnapshot);\n const lastTime = yield* Ref.get(instance.lastSnapshotTime);\n const now = Date.now();\n\n const intervalMs = Duration.toMillis(config.snapshot.interval);\n const threshold = config.snapshot.transactionThreshold;\n\n // Check transaction threshold\n if (txCount >= threshold) {\n yield* saveSnapshot(documentId, instance);\n return;\n }\n\n // Check time interval\n if (now - lastTime >= intervalMs) {\n yield* saveSnapshot(documentId, instance);\n return;\n }\n });\n\n /**\n * Start background GC fiber\n */\n const startGCFiber = Effect.gen(function* () {\n const gcLoop = Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const now = Date.now();\n const maxIdleMs = Duration.toMillis(config.maxIdleTime);\n\n for (const [documentId, instance] of current) {\n const lastActivity = yield* Ref.get(instance.lastActivityTime);\n if (now - lastActivity >= maxIdleMs) {\n // Save final snapshot before eviction\n yield* saveSnapshot(documentId, instance);\n\n // Remove from store\n yield* Ref.update(store, (map) => HashMap.remove(map, documentId));\n\n // Track eviction metrics\n yield* Metric.increment(Metrics.documentsEvicted);\n yield* Metric.incrementBy(Metrics.documentsActive, -1);\n\n yield* Effect.logInfo(\"Document evicted due to idle timeout\", {\n documentId,\n });\n }\n }\n });\n\n // Run GC every minute\n yield* gcLoop.pipe(\n Effect.repeat(Schedule.spaced(\"1 minute\")),\n Effect.fork\n );\n });\n\n // Start GC fiber\n yield* startGCFiber;\n\n // Cleanup on shutdown\n yield* Effect.addFinalizer(() =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n for (const [documentId, instance] of current) {\n yield* saveSnapshot(documentId, instance);\n }\n yield* Effect.logInfo(\"DocumentManager shutdown complete\");\n })\n );\n\n return {\n submit: (documentId, transaction) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n const submitStartTime = Date.now();\n\n // Submit to ServerDocument\n const result = instance.document.submit(transaction);\n\n // Track latency\n const latency = Date.now() - submitStartTime;\n yield* Metric.update(Metrics.transactionsLatency, latency);\n\n if (result.success) {\n // Track success\n yield* Metric.increment(Metrics.transactionsProcessed);\n\n // Append to WAL\n const walEntry: WalEntry = {\n transaction,\n version: result.version,\n timestamp: Date.now(),\n };\n\n yield* Effect.catchAll(\n hotStorage.append(documentId, walEntry),\n (e) =>\n Effect.logError(\"Failed to append to WAL\", {\n documentId,\n error: e,\n })\n );\n\n // Track WAL append\n yield* Metric.increment(Metrics.storageWalAppends);\n\n // Increment transaction count\n yield* Ref.update(\n instance.transactionsSinceSnapshot,\n (n) => n + 1\n );\n\n // Check snapshot triggers\n yield* checkSnapshotTriggers(documentId, instance);\n } else {\n // Track rejection\n yield* Metric.increment(Metrics.transactionsRejected);\n }\n\n return result;\n }),\n\n getSnapshot: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return instance.document.getSnapshot();\n }),\n\n subscribe: (documentId) =>\n Effect.gen(function* () {\n const instance = yield* getOrCreateDocument(documentId);\n return Stream.fromPubSub(instance.pubsub);\n }),\n\n touch: (documentId) =>\n Effect.gen(function* () {\n const current = yield* Ref.get(store);\n const existing = HashMap.get(current, documentId);\n if (existing._tag === \"Some\") {\n yield* Ref.set(existing.value.lastActivityTime, Date.now());\n }\n }),\n };\n })\n);\n\n// =============================================================================\n// Re-export namespace\n// =============================================================================\n\nexport const DocumentManager = {\n Tag: DocumentManagerTag,\n ConfigTag: DocumentManagerConfigTag,\n layer,\n};\n"],"mappings":"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"}
|