@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
package/dist/Metrics.mjs
CHANGED
|
@@ -63,6 +63,10 @@ const transactionsLatency = Metric.histogram("mimic.transactions.latency_ms", Me
|
|
|
63
63
|
*/
|
|
64
64
|
const storageSnapshots = Metric.counter("mimic.storage.snapshots");
|
|
65
65
|
/**
|
|
66
|
+
* Snapshots triggered by idle document detection
|
|
67
|
+
*/
|
|
68
|
+
const storageIdleSnapshots = Metric.counter("mimic.storage.idle_snapshots");
|
|
69
|
+
/**
|
|
66
70
|
* Snapshot save duration histogram (milliseconds)
|
|
67
71
|
*/
|
|
68
72
|
const storageSnapshotLatency = Metric.histogram("mimic.storage.snapshot_latency_ms", MetricBoundaries.exponential({
|
|
@@ -75,6 +79,22 @@ const storageSnapshotLatency = Metric.histogram("mimic.storage.snapshot_latency_
|
|
|
75
79
|
*/
|
|
76
80
|
const storageWalAppends = Metric.counter("mimic.storage.wal_appends");
|
|
77
81
|
/**
|
|
82
|
+
* Version gaps detected during WAL replay
|
|
83
|
+
*/
|
|
84
|
+
const storageVersionGaps = Metric.counter("mimic.storage.version_gaps");
|
|
85
|
+
/**
|
|
86
|
+
* Failed WAL appends causing transaction rollback
|
|
87
|
+
*/
|
|
88
|
+
const walAppendFailures = Metric.counter("mimic.storage.wal_append_failures");
|
|
89
|
+
/**
|
|
90
|
+
* ColdStorage load failures during document restore
|
|
91
|
+
*/
|
|
92
|
+
const coldStorageLoadFailures = Metric.counter("mimic.storage.cold_load_failures");
|
|
93
|
+
/**
|
|
94
|
+
* HotStorage getEntries failures during document restore
|
|
95
|
+
*/
|
|
96
|
+
const hotStorageLoadFailures = Metric.counter("mimic.storage.hot_load_failures");
|
|
97
|
+
/**
|
|
78
98
|
* Presence set operations
|
|
79
99
|
*/
|
|
80
100
|
const presenceUpdates = Metric.counter("mimic.presence.updates");
|
|
@@ -95,12 +115,17 @@ const MimicMetrics = {
|
|
|
95
115
|
transactionsRejected,
|
|
96
116
|
transactionsLatency,
|
|
97
117
|
storageSnapshots,
|
|
118
|
+
storageIdleSnapshots,
|
|
98
119
|
storageSnapshotLatency,
|
|
99
120
|
storageWalAppends,
|
|
121
|
+
storageVersionGaps,
|
|
122
|
+
walAppendFailures,
|
|
123
|
+
coldStorageLoadFailures,
|
|
124
|
+
hotStorageLoadFailures,
|
|
100
125
|
presenceUpdates,
|
|
101
126
|
presenceActive
|
|
102
127
|
};
|
|
103
128
|
|
|
104
129
|
//#endregion
|
|
105
|
-
export { MimicMetrics, connectionsActive, connectionsDuration, connectionsErrors, connectionsTotal, documentsActive, documentsCreated, documentsEvicted, documentsRestored, presenceActive, presenceUpdates, storageSnapshotLatency, storageSnapshots, storageWalAppends, transactionsLatency, transactionsProcessed, transactionsRejected };
|
|
130
|
+
export { MimicMetrics, connectionsActive, connectionsDuration, connectionsErrors, connectionsTotal, documentsActive, documentsCreated, documentsEvicted, documentsRestored, presenceActive, presenceUpdates, storageIdleSnapshots, storageSnapshotLatency, storageSnapshots, storageVersionGaps, storageWalAppends, transactionsLatency, transactionsProcessed, transactionsRejected, walAppendFailures };
|
|
106
131
|
//# sourceMappingURL=Metrics.mjs.map
|
package/dist/Metrics.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Metrics.mjs","names":[],"sources":["../src/Metrics.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - Metrics\n *\n * Observability metrics using Effect's Metric API.\n */\nimport { Metric, MetricBoundaries } from \"effect\";\n\n// =============================================================================\n// Connection Metrics\n// =============================================================================\n\n/**\n * Current active WebSocket connections\n */\nexport const connectionsActive = Metric.gauge(\"mimic.connections.active\");\n\n/**\n * Total connections over lifetime\n */\nexport const connectionsTotal = Metric.counter(\"mimic.connections.total\");\n\n/**\n * Connection duration histogram (milliseconds)\n */\nexport const connectionsDuration = Metric.histogram(\n \"mimic.connections.duration_ms\",\n MetricBoundaries.exponential({\n start: 100,\n factor: 2,\n count: 15, // Up to ~3.2 million ms (~53 minutes)\n })\n);\n\n/**\n * Connection errors (auth failures, etc.)\n */\nexport const connectionsErrors = Metric.counter(\"mimic.connections.errors\");\n\n// =============================================================================\n// Document Metrics\n// =============================================================================\n\n/**\n * Documents currently in memory\n */\nexport const documentsActive = Metric.gauge(\"mimic.documents.active\");\n\n/**\n * New documents created\n */\nexport const documentsCreated = Metric.counter(\"mimic.documents.created\");\n\n/**\n * Documents restored from storage\n */\nexport const documentsRestored = Metric.counter(\"mimic.documents.restored\");\n\n/**\n * Documents garbage collected (evicted)\n */\nexport const documentsEvicted = Metric.counter(\"mimic.documents.evicted\");\n\n// =============================================================================\n// Transaction Metrics\n// =============================================================================\n\n/**\n * Successfully processed transactions\n */\nexport const transactionsProcessed = Metric.counter(\n \"mimic.transactions.processed\"\n);\n\n/**\n * Rejected transactions\n */\nexport const transactionsRejected = Metric.counter(\n \"mimic.transactions.rejected\"\n);\n\n/**\n * Transaction processing latency histogram (milliseconds)\n */\nexport const transactionsLatency = Metric.histogram(\n \"mimic.transactions.latency_ms\",\n MetricBoundaries.exponential({\n start: 0.1,\n factor: 2,\n count: 15, // Up to ~1638 ms\n })\n);\n\n// =============================================================================\n// Storage Metrics\n// =============================================================================\n\n/**\n * Snapshots saved to ColdStorage\n */\nexport const storageSnapshots = Metric.counter(\"mimic.storage.snapshots\");\n\n/**\n * Snapshot save duration histogram (milliseconds)\n */\nexport const storageSnapshotLatency = Metric.histogram(\n \"mimic.storage.snapshot_latency_ms\",\n MetricBoundaries.exponential({\n start: 1,\n factor: 2,\n count: 12, // Up to ~4 seconds\n })\n);\n\n/**\n * WAL entries written to HotStorage\n */\nexport const storageWalAppends = Metric.counter(\"mimic.storage.wal_appends\");\n\n// =============================================================================\n// Presence Metrics\n// =============================================================================\n\n/**\n * Presence set operations\n */\nexport const presenceUpdates = Metric.counter(\"mimic.presence.updates\");\n\n/**\n * Active presence entries\n */\nexport const presenceActive = Metric.gauge(\"mimic.presence.active\");\n\n// =============================================================================\n// Export namespace\n// =============================================================================\n\nexport const MimicMetrics = {\n // Connection\n connectionsActive,\n connectionsTotal,\n connectionsDuration,\n connectionsErrors,\n\n // Document\n documentsActive,\n documentsCreated,\n documentsRestored,\n documentsEvicted,\n\n // Transaction\n transactionsProcessed,\n transactionsRejected,\n transactionsLatency,\n\n // Storage\n storageSnapshots,\n storageSnapshotLatency,\n storageWalAppends,\n\n // Presence\n presenceUpdates,\n presenceActive,\n};\n"],"mappings":";;;;;;;;;;;AAcA,MAAa,oBAAoB,OAAO,MAAM,2BAA2B;;;;AAKzE,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AAKzE,MAAa,sBAAsB,OAAO,UACxC,iCACA,iBAAiB,YAAY;CAC3B,OAAO;CACP,QAAQ;CACR,OAAO;CACR,CAAC,CACH;;;;AAKD,MAAa,oBAAoB,OAAO,QAAQ,2BAA2B;;;;AAS3E,MAAa,kBAAkB,OAAO,MAAM,yBAAyB;;;;AAKrE,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AAKzE,MAAa,oBAAoB,OAAO,QAAQ,2BAA2B;;;;AAK3E,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AASzE,MAAa,wBAAwB,OAAO,QAC1C,+BACD;;;;AAKD,MAAa,uBAAuB,OAAO,QACzC,8BACD;;;;AAKD,MAAa,sBAAsB,OAAO,UACxC,iCACA,iBAAiB,YAAY;CAC3B,OAAO;CACP,QAAQ;CACR,OAAO;CACR,CAAC,CACH;;;;AASD,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AAKzE,MAAa,yBAAyB,OAAO,UAC3C,qCACA,iBAAiB,YAAY;CAC3B,OAAO;CACP,QAAQ;CACR,OAAO;CACR,CAAC,CACH;;;;AAKD,MAAa,oBAAoB,OAAO,QAAQ,4BAA4B;;;;
|
|
1
|
+
{"version":3,"file":"Metrics.mjs","names":[],"sources":["../src/Metrics.ts"],"sourcesContent":["/**\n * @voidhash/mimic-effect - Metrics\n *\n * Observability metrics using Effect's Metric API.\n */\nimport { Metric, MetricBoundaries } from \"effect\";\n\n// =============================================================================\n// Connection Metrics\n// =============================================================================\n\n/**\n * Current active WebSocket connections\n */\nexport const connectionsActive = Metric.gauge(\"mimic.connections.active\");\n\n/**\n * Total connections over lifetime\n */\nexport const connectionsTotal = Metric.counter(\"mimic.connections.total\");\n\n/**\n * Connection duration histogram (milliseconds)\n */\nexport const connectionsDuration = Metric.histogram(\n \"mimic.connections.duration_ms\",\n MetricBoundaries.exponential({\n start: 100,\n factor: 2,\n count: 15, // Up to ~3.2 million ms (~53 minutes)\n })\n);\n\n/**\n * Connection errors (auth failures, etc.)\n */\nexport const connectionsErrors = Metric.counter(\"mimic.connections.errors\");\n\n// =============================================================================\n// Document Metrics\n// =============================================================================\n\n/**\n * Documents currently in memory\n */\nexport const documentsActive = Metric.gauge(\"mimic.documents.active\");\n\n/**\n * New documents created\n */\nexport const documentsCreated = Metric.counter(\"mimic.documents.created\");\n\n/**\n * Documents restored from storage\n */\nexport const documentsRestored = Metric.counter(\"mimic.documents.restored\");\n\n/**\n * Documents garbage collected (evicted)\n */\nexport const documentsEvicted = Metric.counter(\"mimic.documents.evicted\");\n\n// =============================================================================\n// Transaction Metrics\n// =============================================================================\n\n/**\n * Successfully processed transactions\n */\nexport const transactionsProcessed = Metric.counter(\n \"mimic.transactions.processed\"\n);\n\n/**\n * Rejected transactions\n */\nexport const transactionsRejected = Metric.counter(\n \"mimic.transactions.rejected\"\n);\n\n/**\n * Transaction processing latency histogram (milliseconds)\n */\nexport const transactionsLatency = Metric.histogram(\n \"mimic.transactions.latency_ms\",\n MetricBoundaries.exponential({\n start: 0.1,\n factor: 2,\n count: 15, // Up to ~1638 ms\n })\n);\n\n// =============================================================================\n// Storage Metrics\n// =============================================================================\n\n/**\n * Snapshots saved to ColdStorage\n */\nexport const storageSnapshots = Metric.counter(\"mimic.storage.snapshots\");\n\n/**\n * Snapshots triggered by idle document detection\n */\nexport const storageIdleSnapshots = Metric.counter(\"mimic.storage.idle_snapshots\");\n\n/**\n * Snapshot save duration histogram (milliseconds)\n */\nexport const storageSnapshotLatency = Metric.histogram(\n \"mimic.storage.snapshot_latency_ms\",\n MetricBoundaries.exponential({\n start: 1,\n factor: 2,\n count: 12, // Up to ~4 seconds\n })\n);\n\n/**\n * WAL entries written to HotStorage\n */\nexport const storageWalAppends = Metric.counter(\"mimic.storage.wal_appends\");\n\n/**\n * Version gaps detected during WAL replay\n */\nexport const storageVersionGaps = Metric.counter(\"mimic.storage.version_gaps\");\n\n/**\n * Failed WAL appends causing transaction rollback\n */\nexport const walAppendFailures = Metric.counter(\"mimic.storage.wal_append_failures\");\n\n/**\n * ColdStorage load failures during document restore\n */\nexport const coldStorageLoadFailures = Metric.counter(\"mimic.storage.cold_load_failures\");\n\n/**\n * HotStorage getEntries failures during document restore\n */\nexport const hotStorageLoadFailures = Metric.counter(\"mimic.storage.hot_load_failures\");\n\n// =============================================================================\n// Presence Metrics\n// =============================================================================\n\n/**\n * Presence set operations\n */\nexport const presenceUpdates = Metric.counter(\"mimic.presence.updates\");\n\n/**\n * Active presence entries\n */\nexport const presenceActive = Metric.gauge(\"mimic.presence.active\");\n\n// =============================================================================\n// Export namespace\n// =============================================================================\n\nexport const MimicMetrics = {\n // Connection\n connectionsActive,\n connectionsTotal,\n connectionsDuration,\n connectionsErrors,\n\n // Document\n documentsActive,\n documentsCreated,\n documentsRestored,\n documentsEvicted,\n\n // Transaction\n transactionsProcessed,\n transactionsRejected,\n transactionsLatency,\n\n // Storage\n storageSnapshots,\n storageIdleSnapshots,\n storageSnapshotLatency,\n storageWalAppends,\n storageVersionGaps,\n walAppendFailures,\n coldStorageLoadFailures,\n hotStorageLoadFailures,\n\n // Presence\n presenceUpdates,\n presenceActive,\n};\n"],"mappings":";;;;;;;;;;;AAcA,MAAa,oBAAoB,OAAO,MAAM,2BAA2B;;;;AAKzE,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AAKzE,MAAa,sBAAsB,OAAO,UACxC,iCACA,iBAAiB,YAAY;CAC3B,OAAO;CACP,QAAQ;CACR,OAAO;CACR,CAAC,CACH;;;;AAKD,MAAa,oBAAoB,OAAO,QAAQ,2BAA2B;;;;AAS3E,MAAa,kBAAkB,OAAO,MAAM,yBAAyB;;;;AAKrE,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AAKzE,MAAa,oBAAoB,OAAO,QAAQ,2BAA2B;;;;AAK3E,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AASzE,MAAa,wBAAwB,OAAO,QAC1C,+BACD;;;;AAKD,MAAa,uBAAuB,OAAO,QACzC,8BACD;;;;AAKD,MAAa,sBAAsB,OAAO,UACxC,iCACA,iBAAiB,YAAY;CAC3B,OAAO;CACP,QAAQ;CACR,OAAO;CACR,CAAC,CACH;;;;AASD,MAAa,mBAAmB,OAAO,QAAQ,0BAA0B;;;;AAKzE,MAAa,uBAAuB,OAAO,QAAQ,+BAA+B;;;;AAKlF,MAAa,yBAAyB,OAAO,UAC3C,qCACA,iBAAiB,YAAY;CAC3B,OAAO;CACP,QAAQ;CACR,OAAO;CACR,CAAC,CACH;;;;AAKD,MAAa,oBAAoB,OAAO,QAAQ,4BAA4B;;;;AAK5E,MAAa,qBAAqB,OAAO,QAAQ,6BAA6B;;;;AAK9E,MAAa,oBAAoB,OAAO,QAAQ,oCAAoC;;;;AAKpF,MAAa,0BAA0B,OAAO,QAAQ,mCAAmC;;;;AAKzF,MAAa,yBAAyB,OAAO,QAAQ,kCAAkC;;;;AASvF,MAAa,kBAAkB,OAAO,QAAQ,yBAAyB;;;;AAKvE,MAAa,iBAAiB,OAAO,MAAM,wBAAwB;AAMnE,MAAa,eAAe;CAE1B;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CAGA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACD"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const require_ColdStorage = require('./ColdStorage.cjs');
|
|
2
2
|
const require_HotStorage = require('./HotStorage.cjs');
|
|
3
3
|
const require_Metrics = require('./Metrics.cjs');
|
|
4
|
+
const require_DocumentInstance = require('./DocumentInstance.cjs');
|
|
4
5
|
const require_objectSpread2 = require('./_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.cjs');
|
|
5
6
|
const require_MimicServerEngine = require('./MimicServerEngine.cjs');
|
|
6
7
|
let effect = require("effect");
|
|
7
|
-
let _voidhash_mimic_server = require("@voidhash/mimic/server");
|
|
8
8
|
let _effect_cluster = require("@effect/cluster");
|
|
9
9
|
let _effect_rpc = require("@effect/rpc");
|
|
10
10
|
|
|
@@ -21,6 +21,7 @@ const DEFAULT_MAX_IDLE_TIME = effect.Duration.minutes(5);
|
|
|
21
21
|
const DEFAULT_MAX_TRANSACTION_HISTORY = 1e3;
|
|
22
22
|
const DEFAULT_SNAPSHOT_INTERVAL = effect.Duration.minutes(5);
|
|
23
23
|
const DEFAULT_SNAPSHOT_THRESHOLD = 100;
|
|
24
|
+
const DEFAULT_SNAPSHOT_IDLE_TIMEOUT = effect.Duration.seconds(30);
|
|
24
25
|
const DEFAULT_SHARD_GROUP = "mimic-documents";
|
|
25
26
|
/**
|
|
26
27
|
* Schema for encoded transaction (wire format)
|
|
@@ -99,7 +100,7 @@ const MimicDocumentEntity = _effect_cluster.Entity.make("MimicDocument", [
|
|
|
99
100
|
*/
|
|
100
101
|
var MimicClusterConfigTag = class extends effect.Context.Tag("@voidhash/mimic-effect/MimicClusterConfig")() {};
|
|
101
102
|
const resolveClusterConfig = (config) => {
|
|
102
|
-
var _config$maxTransactio, _config$snapshot, _config$snapshot$tran, _config$snapshot2, _config$shardGroup;
|
|
103
|
+
var _config$maxTransactio, _config$snapshot, _config$snapshot$tran, _config$snapshot2, _config$snapshot3, _config$shardGroup;
|
|
103
104
|
return {
|
|
104
105
|
schema: config.schema,
|
|
105
106
|
initial: config.initial,
|
|
@@ -108,7 +109,8 @@ const resolveClusterConfig = (config) => {
|
|
|
108
109
|
maxTransactionHistory: (_config$maxTransactio = config.maxTransactionHistory) !== null && _config$maxTransactio !== void 0 ? _config$maxTransactio : DEFAULT_MAX_TRANSACTION_HISTORY,
|
|
109
110
|
snapshot: {
|
|
110
111
|
interval: ((_config$snapshot = config.snapshot) === null || _config$snapshot === void 0 ? void 0 : _config$snapshot.interval) ? effect.Duration.decode(config.snapshot.interval) : DEFAULT_SNAPSHOT_INTERVAL,
|
|
111
|
-
transactionThreshold: (_config$snapshot$tran = (_config$snapshot2 = config.snapshot) === null || _config$snapshot2 === void 0 ? void 0 : _config$snapshot2.transactionThreshold) !== null && _config$snapshot$tran !== void 0 ? _config$snapshot$tran : DEFAULT_SNAPSHOT_THRESHOLD
|
|
112
|
+
transactionThreshold: (_config$snapshot$tran = (_config$snapshot2 = config.snapshot) === null || _config$snapshot2 === void 0 ? void 0 : _config$snapshot2.transactionThreshold) !== null && _config$snapshot$tran !== void 0 ? _config$snapshot$tran : DEFAULT_SNAPSHOT_THRESHOLD,
|
|
113
|
+
idleTimeout: ((_config$snapshot3 = config.snapshot) === null || _config$snapshot3 === void 0 ? void 0 : _config$snapshot3.idleTimeout) ? effect.Duration.decode(config.snapshot.idleTimeout) : DEFAULT_SNAPSHOT_IDLE_TIMEOUT
|
|
112
114
|
},
|
|
113
115
|
shardGroup: (_config$shardGroup = config.shardGroup) !== null && _config$shardGroup !== void 0 ? _config$shardGroup : DEFAULT_SHARD_GROUP
|
|
114
116
|
};
|
|
@@ -130,150 +132,53 @@ const encodeTransaction = (tx) => {
|
|
|
130
132
|
/**
|
|
131
133
|
* Create the entity handler for MimicDocument
|
|
132
134
|
*/
|
|
133
|
-
const createEntityHandler = (config, coldStorage, hotStorage) => effect.Effect.
|
|
135
|
+
const createEntityHandler = (config, coldStorage, hotStorage) => effect.Effect.fn("cluster.entity.handler.create")(function* () {
|
|
134
136
|
const documentId = (yield* _effect_cluster.Entity.CurrentAddress).entityId;
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
};
|
|
141
|
-
const storedDoc = yield* effect.Effect.catchAll(coldStorage.load(documentId), () => effect.Effect.succeed(void 0));
|
|
142
|
-
let initialState;
|
|
143
|
-
let initialVersion = 0;
|
|
144
|
-
if (storedDoc) {
|
|
145
|
-
initialState = storedDoc.state;
|
|
146
|
-
initialVersion = storedDoc.version;
|
|
147
|
-
} else initialState = yield* computeInitialState();
|
|
148
|
-
const broadcastPubSub = yield* effect.PubSub.unbounded();
|
|
137
|
+
const instance = yield* require_DocumentInstance.DocumentInstance.make(documentId, {
|
|
138
|
+
schema: config.schema,
|
|
139
|
+
initial: config.initial,
|
|
140
|
+
maxTransactionHistory: config.maxTransactionHistory,
|
|
141
|
+
snapshot: config.snapshot
|
|
142
|
+
}, coldStorage, hotStorage).pipe(effect.Effect.orDie);
|
|
149
143
|
const presencePubSub = yield* effect.PubSub.unbounded();
|
|
150
144
|
const stateRef = yield* effect.Ref.make({
|
|
151
|
-
|
|
152
|
-
broadcastPubSub,
|
|
145
|
+
instance,
|
|
153
146
|
presences: effect.HashMap.empty(),
|
|
154
|
-
presencePubSub
|
|
155
|
-
lastSnapshotVersion: initialVersion,
|
|
156
|
-
lastSnapshotTime: Date.now(),
|
|
157
|
-
transactionsSinceSnapshot: 0
|
|
158
|
-
});
|
|
159
|
-
const document = _voidhash_mimic_server.ServerDocument.make({
|
|
160
|
-
schema: config.schema,
|
|
161
|
-
initialState,
|
|
162
|
-
initialVersion,
|
|
163
|
-
maxTransactionHistory: config.maxTransactionHistory,
|
|
164
|
-
onBroadcast: (message) => {
|
|
165
|
-
effect.Effect.runSync(effect.PubSub.publish(broadcastPubSub, {
|
|
166
|
-
type: "transaction",
|
|
167
|
-
transaction: message.transaction,
|
|
168
|
-
version: message.version
|
|
169
|
-
}));
|
|
170
|
-
},
|
|
171
|
-
onRejection: (transactionId, reason) => {
|
|
172
|
-
effect.Effect.runSync(effect.PubSub.publish(broadcastPubSub, {
|
|
173
|
-
type: "error",
|
|
174
|
-
transactionId,
|
|
175
|
-
reason
|
|
176
|
-
}));
|
|
177
|
-
}
|
|
147
|
+
presencePubSub
|
|
178
148
|
});
|
|
179
|
-
yield* effect.
|
|
180
|
-
|
|
181
|
-
for (const entry of walEntries) {
|
|
182
|
-
const result = document.submit(entry.transaction);
|
|
183
|
-
if (!result.success) yield* effect.Effect.logWarning("Skipping corrupted WAL entry", {
|
|
184
|
-
documentId,
|
|
185
|
-
version: entry.version,
|
|
186
|
-
reason: result.reason
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
if (storedDoc) yield* effect.Metric.increment(require_Metrics.documentsRestored);
|
|
190
|
-
else yield* effect.Metric.increment(require_Metrics.documentsCreated);
|
|
191
|
-
yield* effect.Metric.incrementBy(require_Metrics.documentsActive, 1);
|
|
192
|
-
/**
|
|
193
|
-
* Save snapshot to ColdStorage
|
|
194
|
-
*/
|
|
195
|
-
const saveSnapshot = effect.Effect.gen(function* () {
|
|
196
|
-
const state = yield* effect.Ref.get(stateRef);
|
|
197
|
-
const docState = state.document.get();
|
|
198
|
-
const version = state.document.getVersion();
|
|
199
|
-
if (docState === void 0) return;
|
|
200
|
-
const storedDocument = {
|
|
201
|
-
state: docState,
|
|
202
|
-
version,
|
|
203
|
-
schemaVersion: SCHEMA_VERSION,
|
|
204
|
-
savedAt: Date.now()
|
|
205
|
-
};
|
|
206
|
-
const snapshotStartTime = Date.now();
|
|
207
|
-
yield* effect.Effect.catchAll(coldStorage.save(documentId, storedDocument), (e) => effect.Effect.logError("Failed to save snapshot", {
|
|
208
|
-
documentId,
|
|
209
|
-
error: e
|
|
210
|
-
}));
|
|
211
|
-
const snapshotDuration = Date.now() - snapshotStartTime;
|
|
212
|
-
yield* effect.Metric.increment(require_Metrics.storageSnapshots);
|
|
213
|
-
yield* effect.Metric.update(require_Metrics.storageSnapshotLatency, snapshotDuration);
|
|
214
|
-
yield* effect.Effect.catchAll(hotStorage.truncate(documentId, version), (e) => effect.Effect.logError("Failed to truncate WAL", {
|
|
149
|
+
yield* effect.Effect.addFinalizer(() => effect.Effect.fn("cluster.entity.finalize")(function* () {
|
|
150
|
+
yield* effect.Effect.catchAll(instance.saveSnapshot(), (e) => effect.Effect.logError("Failed to save snapshot during entity finalization", {
|
|
215
151
|
documentId,
|
|
216
152
|
error: e
|
|
217
153
|
}));
|
|
218
|
-
yield* effect.Ref.update(stateRef, (s) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, s), {}, {
|
|
219
|
-
lastSnapshotVersion: version,
|
|
220
|
-
lastSnapshotTime: Date.now(),
|
|
221
|
-
transactionsSinceSnapshot: 0
|
|
222
|
-
}));
|
|
223
|
-
});
|
|
224
|
-
/**
|
|
225
|
-
* Check if snapshot should be triggered
|
|
226
|
-
*/
|
|
227
|
-
const checkSnapshotTriggers = effect.Effect.gen(function* () {
|
|
228
|
-
const state = yield* effect.Ref.get(stateRef);
|
|
229
|
-
const now = Date.now();
|
|
230
|
-
const intervalMs = effect.Duration.toMillis(config.snapshot.interval);
|
|
231
|
-
const threshold = config.snapshot.transactionThreshold;
|
|
232
|
-
if (state.transactionsSinceSnapshot >= threshold) {
|
|
233
|
-
yield* saveSnapshot;
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
if (now - state.lastSnapshotTime >= intervalMs) {
|
|
237
|
-
yield* saveSnapshot;
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
yield* effect.Effect.addFinalizer(() => effect.Effect.gen(function* () {
|
|
242
|
-
yield* saveSnapshot;
|
|
243
154
|
yield* effect.Metric.incrementBy(require_Metrics.documentsActive, -1);
|
|
244
155
|
yield* effect.Metric.increment(require_Metrics.documentsEvicted);
|
|
245
156
|
yield* effect.Effect.logDebug("Entity finalized", { documentId });
|
|
246
|
-
}));
|
|
157
|
+
})());
|
|
158
|
+
if (effect.Duration.toMillis(config.snapshot.idleTimeout) > 0) yield* effect.Effect.fn("cluster.entity.snapshot.loop")(function* () {
|
|
159
|
+
if (yield* instance.needsSnapshot()) {
|
|
160
|
+
yield* effect.Effect.catchAll(instance.saveSnapshot(), (e) => effect.Effect.logWarning("Periodic snapshot failed in cluster entity", {
|
|
161
|
+
documentId,
|
|
162
|
+
error: e
|
|
163
|
+
}));
|
|
164
|
+
yield* effect.Metric.increment(require_Metrics.storageIdleSnapshots);
|
|
165
|
+
}
|
|
166
|
+
})().pipe(effect.Effect.repeat(effect.Schedule.spaced(config.snapshot.idleTimeout)), effect.Effect.fork);
|
|
247
167
|
return {
|
|
248
|
-
Submit: effect.Effect.
|
|
249
|
-
const submitStartTime = Date.now();
|
|
250
|
-
const state = yield* effect.Ref.get(stateRef);
|
|
168
|
+
Submit: effect.Effect.fn("cluster.document.transaction.submit")(function* ({ payload }) {
|
|
251
169
|
const transaction = decodeTransaction(payload.transaction);
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
yield* effect.Metric.increment(require_Metrics.transactionsProcessed);
|
|
257
|
-
const walEntry = {
|
|
258
|
-
transaction,
|
|
259
|
-
version: result.version,
|
|
260
|
-
timestamp: Date.now()
|
|
261
|
-
};
|
|
262
|
-
yield* effect.Effect.catchAll(hotStorage.append(documentId, walEntry), (e) => effect.Effect.logError("Failed to append to WAL", {
|
|
263
|
-
documentId,
|
|
264
|
-
error: e
|
|
265
|
-
}));
|
|
266
|
-
yield* effect.Metric.increment(require_Metrics.storageWalAppends);
|
|
267
|
-
yield* effect.Ref.update(stateRef, (s) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, s), {}, { transactionsSinceSnapshot: s.transactionsSinceSnapshot + 1 }));
|
|
268
|
-
yield* checkSnapshotTriggers;
|
|
269
|
-
} else yield* effect.Metric.increment(require_Metrics.transactionsRejected);
|
|
270
|
-
return result;
|
|
170
|
+
return yield* instance.submit(transaction).pipe(effect.Effect.catchAll((error) => effect.Effect.succeed({
|
|
171
|
+
success: false,
|
|
172
|
+
reason: `Storage error: ${String(error)}`
|
|
173
|
+
})));
|
|
271
174
|
}),
|
|
272
|
-
GetSnapshot: effect.Effect.
|
|
273
|
-
return
|
|
175
|
+
GetSnapshot: effect.Effect.fn("cluster.document.snapshot.get")(function* () {
|
|
176
|
+
return instance.getSnapshot();
|
|
274
177
|
}),
|
|
275
|
-
Touch: effect.Effect.
|
|
276
|
-
|
|
178
|
+
Touch: effect.Effect.fn("cluster.document.touch")(function* () {
|
|
179
|
+
yield* instance.touch();
|
|
180
|
+
}),
|
|
181
|
+
SetPresence: effect.Effect.fn("cluster.presence.set")(function* ({ payload }) {
|
|
277
182
|
const { connectionId, entry } = payload;
|
|
278
183
|
yield* effect.Ref.update(stateRef, (s) => require_objectSpread2._objectSpread2(require_objectSpread2._objectSpread2({}, s), {}, { presences: effect.HashMap.set(s.presences, connectionId, entry) }));
|
|
279
184
|
yield* effect.Metric.increment(require_Metrics.presenceUpdates);
|
|
@@ -287,7 +192,7 @@ const createEntityHandler = (config, coldStorage, hotStorage) => effect.Effect.g
|
|
|
287
192
|
};
|
|
288
193
|
yield* effect.PubSub.publish(state.presencePubSub, event);
|
|
289
194
|
}),
|
|
290
|
-
RemovePresence: effect.Effect.
|
|
195
|
+
RemovePresence: effect.Effect.fn("cluster.presence.remove")(function* ({ payload }) {
|
|
291
196
|
const { connectionId } = payload;
|
|
292
197
|
const state = yield* effect.Ref.get(stateRef);
|
|
293
198
|
if (!effect.HashMap.has(state.presences, connectionId)) return;
|
|
@@ -299,20 +204,20 @@ const createEntityHandler = (config, coldStorage, hotStorage) => effect.Effect.g
|
|
|
299
204
|
};
|
|
300
205
|
yield* effect.PubSub.publish(state.presencePubSub, event);
|
|
301
206
|
}),
|
|
302
|
-
GetPresenceSnapshot: effect.Effect.
|
|
207
|
+
GetPresenceSnapshot: effect.Effect.fn("cluster.presence.snapshot.get")(function* () {
|
|
303
208
|
const state = yield* effect.Ref.get(stateRef);
|
|
304
209
|
const presences = {};
|
|
305
210
|
for (const [id, entry] of state.presences) presences[id] = entry;
|
|
306
211
|
return { presences };
|
|
307
212
|
})
|
|
308
213
|
};
|
|
309
|
-
});
|
|
214
|
+
})();
|
|
310
215
|
var SubscriptionStoreTag = class extends effect.Context.Tag("@voidhash/mimic-effect/SubscriptionStore")() {};
|
|
311
|
-
const subscriptionStoreLayer = effect.Layer.effect(SubscriptionStoreTag, effect.Effect.
|
|
216
|
+
const subscriptionStoreLayer = effect.Layer.effect(SubscriptionStoreTag, effect.Effect.fn("cluster.subscriptions.layer.create")(function* () {
|
|
312
217
|
const documentPubSubs = yield* effect.Ref.make(effect.HashMap.empty());
|
|
313
218
|
const presencePubSubs = yield* effect.Ref.make(effect.HashMap.empty());
|
|
314
219
|
return {
|
|
315
|
-
getOrCreatePubSub:
|
|
220
|
+
getOrCreatePubSub: effect.Effect.fn("cluster.subscriptions.pubsub.get-or-create")(function* (documentId) {
|
|
316
221
|
const current = yield* effect.Ref.get(documentPubSubs);
|
|
317
222
|
const existing = effect.HashMap.get(current, documentId);
|
|
318
223
|
if (existing._tag === "Some") return existing.value;
|
|
@@ -320,7 +225,7 @@ const subscriptionStoreLayer = effect.Layer.effect(SubscriptionStoreTag, effect.
|
|
|
320
225
|
yield* effect.Ref.update(documentPubSubs, (map) => effect.HashMap.set(map, documentId, pubsub));
|
|
321
226
|
return pubsub;
|
|
322
227
|
}),
|
|
323
|
-
getOrCreatePresencePubSub:
|
|
228
|
+
getOrCreatePresencePubSub: effect.Effect.fn("cluster.subscriptions.presence-pubsub.get-or-create")(function* (documentId) {
|
|
324
229
|
const current = yield* effect.Ref.get(presencePubSubs);
|
|
325
230
|
const existing = effect.HashMap.get(current, documentId);
|
|
326
231
|
if (existing._tag === "Some") return existing.value;
|
|
@@ -329,7 +234,7 @@ const subscriptionStoreLayer = effect.Layer.effect(SubscriptionStoreTag, effect.
|
|
|
329
234
|
return pubsub;
|
|
330
235
|
})
|
|
331
236
|
};
|
|
332
|
-
}));
|
|
237
|
+
})());
|
|
333
238
|
/**
|
|
334
239
|
* Create a MimicClusterServerEngine layer.
|
|
335
240
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MimicClusterServerEngine.d.cts","names":[],"sources":["../src/MimicClusterServerEngine.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"MimicClusterServerEngine.d.cts","names":[],"sources":["../src/MimicClusterServerEngine.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;cA0pBa;yBA3JwB,SAAA,CAAU,sBACrC,+BAA+B,aACtC,KAAA,CAAM,MACP,6BAEA,iBAAiB,gBAAgB,sBAAsB,QAAA,CAAS"}
|
|
@@ -4,8 +4,8 @@ import { HotStorageTag } from "./HotStorage.mjs";
|
|
|
4
4
|
import { MimicAuthServiceTag } from "./MimicAuthService.mjs";
|
|
5
5
|
import { MimicServerEngineTag } from "./MimicServerEngine.mjs";
|
|
6
6
|
import { Layer } from "effect";
|
|
7
|
-
import { Sharding } from "@effect/cluster";
|
|
8
7
|
import { Primitive } from "@voidhash/mimic";
|
|
8
|
+
import { Sharding } from "@effect/cluster";
|
|
9
9
|
|
|
10
10
|
//#region src/MimicClusterServerEngine.d.ts
|
|
11
11
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MimicClusterServerEngine.d.mts","names":[],"sources":["../src/MimicClusterServerEngine.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"MimicClusterServerEngine.d.mts","names":[],"sources":["../src/MimicClusterServerEngine.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;cA0pBa;yBA3JwB,SAAA,CAAU,sBACrC,+BAA+B,aACtC,KAAA,CAAM,MACP,6BAEA,iBAAiB,gBAAgB,sBAAsB,QAAA,CAAS"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { __require } from "./_virtual/rolldown_runtime.mjs";
|
|
2
2
|
import { ColdStorageTag } from "./ColdStorage.mjs";
|
|
3
3
|
import { HotStorageTag } from "./HotStorage.mjs";
|
|
4
|
-
import { documentsActive,
|
|
4
|
+
import { documentsActive, documentsEvicted, presenceActive, presenceUpdates, storageIdleSnapshots } from "./Metrics.mjs";
|
|
5
|
+
import { DocumentInstance } from "./DocumentInstance.mjs";
|
|
5
6
|
import { _objectSpread2 } from "./_virtual/_@oxc-project_runtime@0.103.0/helpers/objectSpread2.mjs";
|
|
6
7
|
import { MimicServerEngineTag } from "./MimicServerEngine.mjs";
|
|
7
|
-
import { Context, Duration, Effect, HashMap, Layer, Metric, PubSub, Ref, Schema, Stream } from "effect";
|
|
8
|
-
import { ServerDocument } from "@voidhash/mimic/server";
|
|
8
|
+
import { Context, Duration, Effect, HashMap, Layer, Metric, PubSub, Ref, Schedule, Schema, Stream } from "effect";
|
|
9
9
|
import { Entity } from "@effect/cluster";
|
|
10
10
|
import { Rpc } from "@effect/rpc";
|
|
11
11
|
|
|
@@ -22,6 +22,7 @@ const DEFAULT_MAX_IDLE_TIME = Duration.minutes(5);
|
|
|
22
22
|
const DEFAULT_MAX_TRANSACTION_HISTORY = 1e3;
|
|
23
23
|
const DEFAULT_SNAPSHOT_INTERVAL = Duration.minutes(5);
|
|
24
24
|
const DEFAULT_SNAPSHOT_THRESHOLD = 100;
|
|
25
|
+
const DEFAULT_SNAPSHOT_IDLE_TIMEOUT = Duration.seconds(30);
|
|
25
26
|
const DEFAULT_SHARD_GROUP = "mimic-documents";
|
|
26
27
|
/**
|
|
27
28
|
* Schema for encoded transaction (wire format)
|
|
@@ -100,7 +101,7 @@ const MimicDocumentEntity = Entity.make("MimicDocument", [
|
|
|
100
101
|
*/
|
|
101
102
|
var MimicClusterConfigTag = class extends Context.Tag("@voidhash/mimic-effect/MimicClusterConfig")() {};
|
|
102
103
|
const resolveClusterConfig = (config) => {
|
|
103
|
-
var _config$maxTransactio, _config$snapshot, _config$snapshot$tran, _config$snapshot2, _config$shardGroup;
|
|
104
|
+
var _config$maxTransactio, _config$snapshot, _config$snapshot$tran, _config$snapshot2, _config$snapshot3, _config$shardGroup;
|
|
104
105
|
return {
|
|
105
106
|
schema: config.schema,
|
|
106
107
|
initial: config.initial,
|
|
@@ -109,7 +110,8 @@ const resolveClusterConfig = (config) => {
|
|
|
109
110
|
maxTransactionHistory: (_config$maxTransactio = config.maxTransactionHistory) !== null && _config$maxTransactio !== void 0 ? _config$maxTransactio : DEFAULT_MAX_TRANSACTION_HISTORY,
|
|
110
111
|
snapshot: {
|
|
111
112
|
interval: ((_config$snapshot = config.snapshot) === null || _config$snapshot === void 0 ? void 0 : _config$snapshot.interval) ? Duration.decode(config.snapshot.interval) : DEFAULT_SNAPSHOT_INTERVAL,
|
|
112
|
-
transactionThreshold: (_config$snapshot$tran = (_config$snapshot2 = config.snapshot) === null || _config$snapshot2 === void 0 ? void 0 : _config$snapshot2.transactionThreshold) !== null && _config$snapshot$tran !== void 0 ? _config$snapshot$tran : DEFAULT_SNAPSHOT_THRESHOLD
|
|
113
|
+
transactionThreshold: (_config$snapshot$tran = (_config$snapshot2 = config.snapshot) === null || _config$snapshot2 === void 0 ? void 0 : _config$snapshot2.transactionThreshold) !== null && _config$snapshot$tran !== void 0 ? _config$snapshot$tran : DEFAULT_SNAPSHOT_THRESHOLD,
|
|
114
|
+
idleTimeout: ((_config$snapshot3 = config.snapshot) === null || _config$snapshot3 === void 0 ? void 0 : _config$snapshot3.idleTimeout) ? Duration.decode(config.snapshot.idleTimeout) : DEFAULT_SNAPSHOT_IDLE_TIMEOUT
|
|
113
115
|
},
|
|
114
116
|
shardGroup: (_config$shardGroup = config.shardGroup) !== null && _config$shardGroup !== void 0 ? _config$shardGroup : DEFAULT_SHARD_GROUP
|
|
115
117
|
};
|
|
@@ -131,150 +133,53 @@ const encodeTransaction = (tx) => {
|
|
|
131
133
|
/**
|
|
132
134
|
* Create the entity handler for MimicDocument
|
|
133
135
|
*/
|
|
134
|
-
const createEntityHandler = (config, coldStorage, hotStorage) => Effect.
|
|
136
|
+
const createEntityHandler = (config, coldStorage, hotStorage) => Effect.fn("cluster.entity.handler.create")(function* () {
|
|
135
137
|
const documentId = (yield* Entity.CurrentAddress).entityId;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
};
|
|
142
|
-
const storedDoc = yield* Effect.catchAll(coldStorage.load(documentId), () => Effect.succeed(void 0));
|
|
143
|
-
let initialState;
|
|
144
|
-
let initialVersion = 0;
|
|
145
|
-
if (storedDoc) {
|
|
146
|
-
initialState = storedDoc.state;
|
|
147
|
-
initialVersion = storedDoc.version;
|
|
148
|
-
} else initialState = yield* computeInitialState();
|
|
149
|
-
const broadcastPubSub = yield* PubSub.unbounded();
|
|
138
|
+
const instance = yield* DocumentInstance.make(documentId, {
|
|
139
|
+
schema: config.schema,
|
|
140
|
+
initial: config.initial,
|
|
141
|
+
maxTransactionHistory: config.maxTransactionHistory,
|
|
142
|
+
snapshot: config.snapshot
|
|
143
|
+
}, coldStorage, hotStorage).pipe(Effect.orDie);
|
|
150
144
|
const presencePubSub = yield* PubSub.unbounded();
|
|
151
145
|
const stateRef = yield* Ref.make({
|
|
152
|
-
|
|
153
|
-
broadcastPubSub,
|
|
146
|
+
instance,
|
|
154
147
|
presences: HashMap.empty(),
|
|
155
|
-
presencePubSub
|
|
156
|
-
lastSnapshotVersion: initialVersion,
|
|
157
|
-
lastSnapshotTime: Date.now(),
|
|
158
|
-
transactionsSinceSnapshot: 0
|
|
159
|
-
});
|
|
160
|
-
const document = ServerDocument.make({
|
|
161
|
-
schema: config.schema,
|
|
162
|
-
initialState,
|
|
163
|
-
initialVersion,
|
|
164
|
-
maxTransactionHistory: config.maxTransactionHistory,
|
|
165
|
-
onBroadcast: (message) => {
|
|
166
|
-
Effect.runSync(PubSub.publish(broadcastPubSub, {
|
|
167
|
-
type: "transaction",
|
|
168
|
-
transaction: message.transaction,
|
|
169
|
-
version: message.version
|
|
170
|
-
}));
|
|
171
|
-
},
|
|
172
|
-
onRejection: (transactionId, reason) => {
|
|
173
|
-
Effect.runSync(PubSub.publish(broadcastPubSub, {
|
|
174
|
-
type: "error",
|
|
175
|
-
transactionId,
|
|
176
|
-
reason
|
|
177
|
-
}));
|
|
178
|
-
}
|
|
148
|
+
presencePubSub
|
|
179
149
|
});
|
|
180
|
-
yield*
|
|
181
|
-
|
|
182
|
-
for (const entry of walEntries) {
|
|
183
|
-
const result = document.submit(entry.transaction);
|
|
184
|
-
if (!result.success) yield* Effect.logWarning("Skipping corrupted WAL entry", {
|
|
185
|
-
documentId,
|
|
186
|
-
version: entry.version,
|
|
187
|
-
reason: result.reason
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
if (storedDoc) yield* Metric.increment(documentsRestored);
|
|
191
|
-
else yield* Metric.increment(documentsCreated);
|
|
192
|
-
yield* Metric.incrementBy(documentsActive, 1);
|
|
193
|
-
/**
|
|
194
|
-
* Save snapshot to ColdStorage
|
|
195
|
-
*/
|
|
196
|
-
const saveSnapshot = Effect.gen(function* () {
|
|
197
|
-
const state = yield* Ref.get(stateRef);
|
|
198
|
-
const docState = state.document.get();
|
|
199
|
-
const version = state.document.getVersion();
|
|
200
|
-
if (docState === void 0) return;
|
|
201
|
-
const storedDocument = {
|
|
202
|
-
state: docState,
|
|
203
|
-
version,
|
|
204
|
-
schemaVersion: SCHEMA_VERSION,
|
|
205
|
-
savedAt: Date.now()
|
|
206
|
-
};
|
|
207
|
-
const snapshotStartTime = Date.now();
|
|
208
|
-
yield* Effect.catchAll(coldStorage.save(documentId, storedDocument), (e) => Effect.logError("Failed to save snapshot", {
|
|
209
|
-
documentId,
|
|
210
|
-
error: e
|
|
211
|
-
}));
|
|
212
|
-
const snapshotDuration = Date.now() - snapshotStartTime;
|
|
213
|
-
yield* Metric.increment(storageSnapshots);
|
|
214
|
-
yield* Metric.update(storageSnapshotLatency, snapshotDuration);
|
|
215
|
-
yield* Effect.catchAll(hotStorage.truncate(documentId, version), (e) => Effect.logError("Failed to truncate WAL", {
|
|
150
|
+
yield* Effect.addFinalizer(() => Effect.fn("cluster.entity.finalize")(function* () {
|
|
151
|
+
yield* Effect.catchAll(instance.saveSnapshot(), (e) => Effect.logError("Failed to save snapshot during entity finalization", {
|
|
216
152
|
documentId,
|
|
217
153
|
error: e
|
|
218
154
|
}));
|
|
219
|
-
yield* Ref.update(stateRef, (s) => _objectSpread2(_objectSpread2({}, s), {}, {
|
|
220
|
-
lastSnapshotVersion: version,
|
|
221
|
-
lastSnapshotTime: Date.now(),
|
|
222
|
-
transactionsSinceSnapshot: 0
|
|
223
|
-
}));
|
|
224
|
-
});
|
|
225
|
-
/**
|
|
226
|
-
* Check if snapshot should be triggered
|
|
227
|
-
*/
|
|
228
|
-
const checkSnapshotTriggers = Effect.gen(function* () {
|
|
229
|
-
const state = yield* Ref.get(stateRef);
|
|
230
|
-
const now = Date.now();
|
|
231
|
-
const intervalMs = Duration.toMillis(config.snapshot.interval);
|
|
232
|
-
const threshold = config.snapshot.transactionThreshold;
|
|
233
|
-
if (state.transactionsSinceSnapshot >= threshold) {
|
|
234
|
-
yield* saveSnapshot;
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (now - state.lastSnapshotTime >= intervalMs) {
|
|
238
|
-
yield* saveSnapshot;
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
yield* Effect.addFinalizer(() => Effect.gen(function* () {
|
|
243
|
-
yield* saveSnapshot;
|
|
244
155
|
yield* Metric.incrementBy(documentsActive, -1);
|
|
245
156
|
yield* Metric.increment(documentsEvicted);
|
|
246
157
|
yield* Effect.logDebug("Entity finalized", { documentId });
|
|
247
|
-
}));
|
|
158
|
+
})());
|
|
159
|
+
if (Duration.toMillis(config.snapshot.idleTimeout) > 0) yield* Effect.fn("cluster.entity.snapshot.loop")(function* () {
|
|
160
|
+
if (yield* instance.needsSnapshot()) {
|
|
161
|
+
yield* Effect.catchAll(instance.saveSnapshot(), (e) => Effect.logWarning("Periodic snapshot failed in cluster entity", {
|
|
162
|
+
documentId,
|
|
163
|
+
error: e
|
|
164
|
+
}));
|
|
165
|
+
yield* Metric.increment(storageIdleSnapshots);
|
|
166
|
+
}
|
|
167
|
+
})().pipe(Effect.repeat(Schedule.spaced(config.snapshot.idleTimeout)), Effect.fork);
|
|
248
168
|
return {
|
|
249
|
-
Submit: Effect.
|
|
250
|
-
const submitStartTime = Date.now();
|
|
251
|
-
const state = yield* Ref.get(stateRef);
|
|
169
|
+
Submit: Effect.fn("cluster.document.transaction.submit")(function* ({ payload }) {
|
|
252
170
|
const transaction = decodeTransaction(payload.transaction);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
yield* Metric.increment(transactionsProcessed);
|
|
258
|
-
const walEntry = {
|
|
259
|
-
transaction,
|
|
260
|
-
version: result.version,
|
|
261
|
-
timestamp: Date.now()
|
|
262
|
-
};
|
|
263
|
-
yield* Effect.catchAll(hotStorage.append(documentId, walEntry), (e) => Effect.logError("Failed to append to WAL", {
|
|
264
|
-
documentId,
|
|
265
|
-
error: e
|
|
266
|
-
}));
|
|
267
|
-
yield* Metric.increment(storageWalAppends);
|
|
268
|
-
yield* Ref.update(stateRef, (s) => _objectSpread2(_objectSpread2({}, s), {}, { transactionsSinceSnapshot: s.transactionsSinceSnapshot + 1 }));
|
|
269
|
-
yield* checkSnapshotTriggers;
|
|
270
|
-
} else yield* Metric.increment(transactionsRejected);
|
|
271
|
-
return result;
|
|
171
|
+
return yield* instance.submit(transaction).pipe(Effect.catchAll((error) => Effect.succeed({
|
|
172
|
+
success: false,
|
|
173
|
+
reason: `Storage error: ${String(error)}`
|
|
174
|
+
})));
|
|
272
175
|
}),
|
|
273
|
-
GetSnapshot: Effect.
|
|
274
|
-
return
|
|
176
|
+
GetSnapshot: Effect.fn("cluster.document.snapshot.get")(function* () {
|
|
177
|
+
return instance.getSnapshot();
|
|
275
178
|
}),
|
|
276
|
-
Touch: Effect.
|
|
277
|
-
|
|
179
|
+
Touch: Effect.fn("cluster.document.touch")(function* () {
|
|
180
|
+
yield* instance.touch();
|
|
181
|
+
}),
|
|
182
|
+
SetPresence: Effect.fn("cluster.presence.set")(function* ({ payload }) {
|
|
278
183
|
const { connectionId, entry } = payload;
|
|
279
184
|
yield* Ref.update(stateRef, (s) => _objectSpread2(_objectSpread2({}, s), {}, { presences: HashMap.set(s.presences, connectionId, entry) }));
|
|
280
185
|
yield* Metric.increment(presenceUpdates);
|
|
@@ -288,7 +193,7 @@ const createEntityHandler = (config, coldStorage, hotStorage) => Effect.gen(func
|
|
|
288
193
|
};
|
|
289
194
|
yield* PubSub.publish(state.presencePubSub, event);
|
|
290
195
|
}),
|
|
291
|
-
RemovePresence: Effect.
|
|
196
|
+
RemovePresence: Effect.fn("cluster.presence.remove")(function* ({ payload }) {
|
|
292
197
|
const { connectionId } = payload;
|
|
293
198
|
const state = yield* Ref.get(stateRef);
|
|
294
199
|
if (!HashMap.has(state.presences, connectionId)) return;
|
|
@@ -300,20 +205,20 @@ const createEntityHandler = (config, coldStorage, hotStorage) => Effect.gen(func
|
|
|
300
205
|
};
|
|
301
206
|
yield* PubSub.publish(state.presencePubSub, event);
|
|
302
207
|
}),
|
|
303
|
-
GetPresenceSnapshot: Effect.
|
|
208
|
+
GetPresenceSnapshot: Effect.fn("cluster.presence.snapshot.get")(function* () {
|
|
304
209
|
const state = yield* Ref.get(stateRef);
|
|
305
210
|
const presences = {};
|
|
306
211
|
for (const [id, entry] of state.presences) presences[id] = entry;
|
|
307
212
|
return { presences };
|
|
308
213
|
})
|
|
309
214
|
};
|
|
310
|
-
});
|
|
215
|
+
})();
|
|
311
216
|
var SubscriptionStoreTag = class extends Context.Tag("@voidhash/mimic-effect/SubscriptionStore")() {};
|
|
312
|
-
const subscriptionStoreLayer = Layer.effect(SubscriptionStoreTag, Effect.
|
|
217
|
+
const subscriptionStoreLayer = Layer.effect(SubscriptionStoreTag, Effect.fn("cluster.subscriptions.layer.create")(function* () {
|
|
313
218
|
const documentPubSubs = yield* Ref.make(HashMap.empty());
|
|
314
219
|
const presencePubSubs = yield* Ref.make(HashMap.empty());
|
|
315
220
|
return {
|
|
316
|
-
getOrCreatePubSub:
|
|
221
|
+
getOrCreatePubSub: Effect.fn("cluster.subscriptions.pubsub.get-or-create")(function* (documentId) {
|
|
317
222
|
const current = yield* Ref.get(documentPubSubs);
|
|
318
223
|
const existing = HashMap.get(current, documentId);
|
|
319
224
|
if (existing._tag === "Some") return existing.value;
|
|
@@ -321,7 +226,7 @@ const subscriptionStoreLayer = Layer.effect(SubscriptionStoreTag, Effect.gen(fun
|
|
|
321
226
|
yield* Ref.update(documentPubSubs, (map) => HashMap.set(map, documentId, pubsub));
|
|
322
227
|
return pubsub;
|
|
323
228
|
}),
|
|
324
|
-
getOrCreatePresencePubSub:
|
|
229
|
+
getOrCreatePresencePubSub: Effect.fn("cluster.subscriptions.presence-pubsub.get-or-create")(function* (documentId) {
|
|
325
230
|
const current = yield* Ref.get(presencePubSubs);
|
|
326
231
|
const existing = HashMap.get(current, documentId);
|
|
327
232
|
if (existing._tag === "Some") return existing.value;
|
|
@@ -330,7 +235,7 @@ const subscriptionStoreLayer = Layer.effect(SubscriptionStoreTag, Effect.gen(fun
|
|
|
330
235
|
return pubsub;
|
|
331
236
|
})
|
|
332
237
|
};
|
|
333
|
-
}));
|
|
238
|
+
})());
|
|
334
239
|
/**
|
|
335
240
|
* Create a MimicClusterServerEngine layer.
|
|
336
241
|
*
|