@dxos/echo-pipeline 0.1.37-next.8c6108a → 0.1.37-next.a4acfcd

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.
@@ -1,8 +1,10 @@
1
1
  import "@dxos/node-std/globals"
2
2
 
3
3
  // packages/core/echo/echo-pipeline/src/metadata/metadata-store.ts
4
+ import CRC32 from "crc-32";
4
5
  import assert from "@dxos/node-std/assert";
5
6
  import { synchronized } from "@dxos/async";
7
+ import { DataCorruptionError } from "@dxos/errors";
6
8
  import { log } from "@dxos/log";
7
9
  import { schema } from "@dxos/protocols";
8
10
  var __decorate = function(decorators, target, key, desc) {
@@ -47,31 +49,35 @@ var MetadataStore = class {
47
49
  const file = this._directory.getOrCreateFile("EchoMetadata");
48
50
  try {
49
51
  const { size: fileLength } = await file.stat();
50
- if (fileLength < 4) {
52
+ if (fileLength < 8) {
51
53
  return;
52
54
  }
53
55
  const dataSize = fromBytesInt32(await file.read(0, 4));
56
+ const checksum = fromBytesInt32(await file.read(4, 4));
54
57
  log("loaded", {
55
- size: dataSize
58
+ size: dataSize,
59
+ checksum
56
60
  }, {
57
61
  file: "metadata-store.ts",
58
- line: 67,
62
+ line: 70,
59
63
  scope: this,
60
64
  callSite: (f, a) => f(...a)
61
65
  });
62
- {
63
- if (fileLength < dataSize + 4) {
64
- throw new Error("Metadata storage is corrupted");
65
- }
66
+ if (fileLength < dataSize + 8) {
67
+ throw new DataCorruptionError("Metadata size is smaller than expected.");
68
+ }
69
+ const data = await file.read(8, dataSize);
70
+ const calculatedChecksum = CRC32.buf(data);
71
+ if (calculatedChecksum !== checksum) {
72
+ throw new DataCorruptionError("Metadata checksum is invalid.");
66
73
  }
67
- const data = await file.read(4, dataSize);
68
74
  this._metadata = schema.getCodecForType("dxos.echo.metadata.EchoMetadata").decode(data);
69
75
  } catch (err) {
70
76
  log.error("failed to load metadata", {
71
77
  err
72
78
  }, {
73
79
  file: "metadata-store.ts",
74
- line: 79,
80
+ line: 85,
75
81
  scope: this,
76
82
  callSite: (f, a) => f(...a)
77
83
  });
@@ -91,16 +97,21 @@ var MetadataStore = class {
91
97
  const file = this._directory.getOrCreateFile("EchoMetadata");
92
98
  try {
93
99
  const encoded = Buffer.from(schema.getCodecForType("dxos.echo.metadata.EchoMetadata").encode(data));
94
- await file.write(0, toBytesInt32(encoded.length));
100
+ const checksum = CRC32.buf(encoded);
101
+ const result = Buffer.alloc(8 + encoded.length);
102
+ result.writeInt32LE(encoded.length, 0);
103
+ result.writeInt32LE(checksum, 4);
104
+ encoded.copy(result, 8);
105
+ await file.write(0, result);
95
106
  log("saved", {
96
- size: encoded.length
107
+ size: encoded.length,
108
+ checksum
97
109
  }, {
98
110
  file: "metadata-store.ts",
99
- line: 102,
111
+ line: 116,
100
112
  scope: this,
101
113
  callSite: (f, a) => f(...a)
102
114
  });
103
- await file.write(4, encoded);
104
115
  } finally {
105
116
  await file.close();
106
117
  }
@@ -111,7 +122,7 @@ var MetadataStore = class {
111
122
  async clear() {
112
123
  log("clearing all metadata", {}, {
113
124
  file: "metadata-store.ts",
114
- line: 115,
125
+ line: 126,
115
126
  scope: this,
116
127
  callSite: (f, a) => f(...a)
117
128
  });
@@ -157,11 +168,6 @@ __decorate([
157
168
  __decorate([
158
169
  synchronized
159
170
  ], MetadataStore.prototype, "_save", null);
160
- var toBytesInt32 = (num) => {
161
- const buf = Buffer.alloc(4);
162
- buf.writeInt32LE(num, 0);
163
- return buf;
164
- };
165
171
  var fromBytesInt32 = (buf) => buf.readInt32LE(0);
166
172
 
167
173
  // packages/core/echo/echo-pipeline/src/common/codec.ts
@@ -212,17 +218,31 @@ var TimeframeClock = class {
212
218
  constructor(_timeframe = new Timeframe()) {
213
219
  this._timeframe = _timeframe;
214
220
  this.update = new Event();
221
+ this._pendingTimeframe = _timeframe;
215
222
  }
223
+ /**
224
+ * Timeframe that was processed by ECHO.
225
+ */
216
226
  get timeframe() {
217
227
  return this._timeframe;
218
228
  }
219
- updateTimeframe(key, seq) {
220
- this._timeframe = Timeframe.merge(this._timeframe, new Timeframe([
229
+ /**
230
+ * Timeframe that is currently being processed by ECHO.
231
+ * Will be equal to `timeframe` after the processing is complete.
232
+ */
233
+ get pendingTimeframe() {
234
+ return this._pendingTimeframe;
235
+ }
236
+ updatePendingTimeframe(key, seq) {
237
+ this._pendingTimeframe = Timeframe.merge(this._pendingTimeframe, new Timeframe([
221
238
  [
222
239
  key,
223
240
  seq
224
241
  ]
225
242
  ]));
243
+ }
244
+ updateTimeframe() {
245
+ this._timeframe = this._pendingTimeframe;
226
246
  this.update.emit(this._timeframe);
227
247
  }
228
248
  hasGaps(timeframe) {
@@ -235,7 +255,7 @@ var TimeframeClock = class {
235
255
  current: this._timeframe
236
256
  }, {
237
257
  file: "timeframe-clock.ts",
238
- line: 48,
258
+ line: 67,
239
259
  scope: this,
240
260
  callSite: (f, a) => f(...a)
241
261
  });
@@ -246,7 +266,7 @@ var TimeframeClock = class {
246
266
  deps: Timeframe.dependencies(target, this._timeframe)
247
267
  }, {
248
268
  file: "timeframe-clock.ts",
249
- line: 50,
269
+ line: 69,
250
270
  scope: this,
251
271
  callSite: (f, a) => f(...a)
252
272
  });
@@ -316,6 +336,9 @@ var PipelineState = class {
316
336
  get timeframe() {
317
337
  return this._timeframeClock.timeframe;
318
338
  }
339
+ get pendingTimeframe() {
340
+ return this._timeframeClock.pendingTimeframe;
341
+ }
319
342
  get targetTimeframe() {
320
343
  return this._targetTimeframe ? this._targetTimeframe : new Timeframe2();
321
344
  }
@@ -343,7 +366,7 @@ var PipelineState = class {
343
366
  target: this.targetTimeframe
344
367
  }, {
345
368
  file: "pipeline.ts",
346
- line: 101,
369
+ line: 105,
347
370
  scope: this,
348
371
  callSite: (f, a) => f(...a)
349
372
  });
@@ -373,7 +396,7 @@ var PipelineState = class {
373
396
  dependencies: Timeframe2.dependencies(this.targetTimeframe, this.timeframe)
374
397
  }, {
375
398
  file: "pipeline.ts",
376
- line: 127,
399
+ line: 131,
377
400
  scope: this,
378
401
  callSite: (f, a) => f(...a)
379
402
  });
@@ -399,7 +422,7 @@ var Pipeline = class {
399
422
  this._feedSetIterator.stalled.on((iterator) => {
400
423
  log4.warn(`Stalled after ${iterator.options.stallTimeout}ms with ${iterator.size} feeds.`, {}, {
401
424
  file: "pipeline.ts",
402
- line: 203,
425
+ line: 207,
403
426
  scope: this,
404
427
  callSite: (f, a) => f(...a)
405
428
  });
@@ -434,14 +457,14 @@ var Pipeline = class {
434
457
  async start() {
435
458
  log4("starting...", {}, {
436
459
  file: "pipeline.ts",
437
- line: 245,
460
+ line: 249,
438
461
  scope: this,
439
462
  callSite: (f, a) => f(...a)
440
463
  });
441
464
  await this._feedSetIterator.open();
442
465
  log4("started", {}, {
443
466
  file: "pipeline.ts",
444
- line: 247,
467
+ line: 251,
445
468
  scope: this,
446
469
  callSite: (f, a) => f(...a)
447
470
  });
@@ -449,7 +472,7 @@ var Pipeline = class {
449
472
  async stop() {
450
473
  log4("stopping...", {}, {
451
474
  file: "pipeline.ts",
452
- line: 252,
475
+ line: 256,
453
476
  scope: this,
454
477
  callSite: (f, a) => f(...a)
455
478
  });
@@ -457,7 +480,7 @@ var Pipeline = class {
457
480
  await this._processingTrigger.wait();
458
481
  log4("stopped", {}, {
459
482
  file: "pipeline.ts",
460
- line: 255,
483
+ line: 259,
461
484
  scope: this,
462
485
  callSite: (f, a) => f(...a)
463
486
  });
@@ -471,9 +494,10 @@ var Pipeline = class {
471
494
  this._isOpen = true;
472
495
  for await (const block of this._feedSetIterator) {
473
496
  this._processingTrigger.reset();
497
+ this._timeframeClock.updatePendingTimeframe(PublicKey.from(block.feedKey), block.seq);
474
498
  yield block;
475
499
  this._processingTrigger.wake();
476
- this._timeframeClock.updateTimeframe(PublicKey.from(block.feedKey), block.seq);
500
+ this._timeframeClock.updateTimeframe();
477
501
  }
478
502
  this._isOpen = false;
479
503
  }
@@ -1506,7 +1530,7 @@ var DataServiceImpl = class {
1506
1530
 
1507
1531
  // packages/core/echo/echo-pipeline/src/space/data-pipeline.ts
1508
1532
  import assert9 from "@dxos/node-std/assert";
1509
- import { Event as Event3, scheduleTask as scheduleTask2, synchronized as synchronized5, trackLeaks as trackLeaks4 } from "@dxos/async";
1533
+ import { scheduleTask as scheduleTask2, synchronized as synchronized5, trackLeaks as trackLeaks4 } from "@dxos/async";
1510
1534
  import { Context as Context3 } from "@dxos/context";
1511
1535
  import { failUndefined as failUndefined3 } from "@dxos/debug";
1512
1536
  import { ItemManager } from "@dxos/echo-db";
@@ -1532,7 +1556,8 @@ var DataPipeline = class DataPipeline2 {
1532
1556
  this._ctx = new Context3();
1533
1557
  this._lastAutomaticSnapshotTimeframe = new Timeframe3();
1534
1558
  this._isOpen = false;
1535
- this.onTimeframeReached = new Event3();
1559
+ this._lastTimeframeSaveTime = 0;
1560
+ this._lastSnapshotSaveTime = 0;
1536
1561
  }
1537
1562
  get isOpen() {
1538
1563
  return this._isOpen;
@@ -1577,58 +1602,8 @@ var DataPipeline = class DataPipeline2 {
1577
1602
  scheduleTask2(this._ctx, async () => {
1578
1603
  await this._consumePipeline();
1579
1604
  });
1580
- this._createPeriodicSnapshots();
1581
1605
  this._isOpen = true;
1582
1606
  }
1583
- _createPeriodicSnapshots() {
1584
- this.onTimeframeReached.debounce(TIMEFRAME_SAVE_DEBOUNCE_INTERVAL).on(this._ctx, async () => {
1585
- await this._saveLatestTimeframe();
1586
- });
1587
- if (!DISABLE_SNAPSHOT_CACHE) {
1588
- this.onTimeframeReached.debounce(AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL).on(this._ctx, async () => {
1589
- var _a, _b;
1590
- const latestTimeframe = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe;
1591
- if (!latestTimeframe) {
1592
- return;
1593
- }
1594
- if (latestTimeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
1595
- const snapshot = await this._saveSnapshot();
1596
- this._lastAutomaticSnapshotTimeframe = (_b = snapshot.timeframe) != null ? _b : failUndefined3();
1597
- log12("save", {
1598
- snapshot
1599
- }, {
1600
- file: "data-pipeline.ts",
1601
- line: 161,
1602
- scope: this,
1603
- callSite: (f, a) => f(...a)
1604
- });
1605
- }
1606
- });
1607
- }
1608
- }
1609
- async _saveSnapshot() {
1610
- const snapshot = await this.createSnapshot();
1611
- const snapshotKey = await this._params.snapshotManager.store(snapshot);
1612
- await this._params.metadataStore.setSpaceSnapshot(this._params.spaceKey, snapshotKey);
1613
- return snapshot;
1614
- }
1615
- async _saveLatestTimeframe() {
1616
- var _a, _b;
1617
- const latestTimeframe = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe;
1618
- log12("save latest timeframe", {
1619
- latestTimeframe,
1620
- spaceKey: this._params.spaceKey
1621
- }, {
1622
- file: "data-pipeline.ts",
1623
- line: 176,
1624
- scope: this,
1625
- callSite: (f, a) => f(...a)
1626
- });
1627
- if (latestTimeframe) {
1628
- const newTimeframe = Timeframe3.merge((_b = this._targetTimeframe) != null ? _b : new Timeframe3(), latestTimeframe);
1629
- await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1630
- }
1631
- }
1632
1607
  async close() {
1633
1608
  var _a, _b, _c;
1634
1609
  if (!this._isOpen) {
@@ -1636,37 +1611,32 @@ var DataPipeline = class DataPipeline2 {
1636
1611
  }
1637
1612
  log12("close", {}, {
1638
1613
  file: "data-pipeline.ts",
1639
- line: 188,
1614
+ line: 145,
1640
1615
  scope: this,
1641
1616
  callSite: (f, a) => f(...a)
1642
1617
  });
1643
1618
  this._isOpen = false;
1619
+ await this._ctx.dispose();
1620
+ await ((_a = this._pipeline) == null ? void 0 : _a.stop());
1644
1621
  try {
1645
- await this._saveLatestTimeframe();
1646
- await this._saveSnapshot();
1622
+ if (this._pipeline) {
1623
+ await this._saveTargetTimeframe(this._pipeline.state.timeframe);
1624
+ if (!DISABLE_SNAPSHOT_CACHE) {
1625
+ await this._saveSnapshot(this._pipeline.state.timeframe);
1626
+ }
1627
+ }
1647
1628
  } catch (err) {
1648
1629
  log12.catch(err, {}, {
1649
1630
  file: "data-pipeline.ts",
1650
- line: 195,
1631
+ line: 160,
1651
1632
  scope: this,
1652
1633
  callSite: (f, a) => f(...a)
1653
1634
  });
1654
1635
  }
1655
- await this._ctx.dispose();
1656
- await ((_a = this._pipeline) == null ? void 0 : _a.stop());
1657
1636
  await ((_b = this.databaseBackend) == null ? void 0 : _b.close());
1658
1637
  await ((_c = this._itemManager) == null ? void 0 : _c.destroy());
1659
1638
  await this._params.snapshotManager.close();
1660
1639
  }
1661
- createSnapshot() {
1662
- var _a, _b;
1663
- assert9(this.databaseBackend, "Database backend is not initialized.");
1664
- return {
1665
- spaceKey: this._params.spaceKey.asUint8Array(),
1666
- timeframe: (_b = (_a = this._pipeline) == null ? void 0 : _a.state.timeframe) != null ? _b : new Timeframe3(),
1667
- database: this.databaseBackend.createSnapshot()
1668
- };
1669
- }
1670
1640
  async _consumePipeline() {
1671
1641
  assert9(this._pipeline, "Pipeline is not initialized.");
1672
1642
  for await (const msg of this._pipeline.consume()) {
@@ -1675,7 +1645,7 @@ var DataPipeline = class DataPipeline2 {
1675
1645
  msg
1676
1646
  }, {
1677
1647
  file: "data-pipeline.ts",
1678
- line: 217,
1648
+ line: 172,
1679
1649
  scope: this,
1680
1650
  callSite: (f, a) => f(...a)
1681
1651
  });
@@ -1687,7 +1657,7 @@ var DataPipeline = class DataPipeline2 {
1687
1657
  feedKey
1688
1658
  }, {
1689
1659
  file: "data-pipeline.ts",
1690
- line: 223,
1660
+ line: 178,
1691
1661
  scope: this,
1692
1662
  callSite: (f, a) => f(...a)
1693
1663
  });
@@ -1702,18 +1672,58 @@ var DataPipeline = class DataPipeline2 {
1702
1672
  memberKey: feedInfo.assertion.identityKey
1703
1673
  }
1704
1674
  });
1705
- this.onTimeframeReached.emit(data.timeframe);
1675
+ await this._noteTargetStateIfNeeded(this._pipeline.state.pendingTimeframe);
1706
1676
  }
1707
1677
  } catch (err) {
1708
1678
  log12.catch(err, {}, {
1709
1679
  file: "data-pipeline.ts",
1710
- line: 239,
1680
+ line: 196,
1711
1681
  scope: this,
1712
1682
  callSite: (f, a) => f(...a)
1713
1683
  });
1714
1684
  }
1715
1685
  }
1716
1686
  }
1687
+ _createSnapshot(timeframe) {
1688
+ assert9(this.databaseBackend, "Database backend is not initialized.");
1689
+ return {
1690
+ spaceKey: this._params.spaceKey.asUint8Array(),
1691
+ timeframe,
1692
+ database: this.databaseBackend.createSnapshot()
1693
+ };
1694
+ }
1695
+ async _saveSnapshot(timeframe) {
1696
+ const snapshot = await this._createSnapshot(timeframe);
1697
+ const snapshotKey = await this._params.snapshotManager.store(snapshot);
1698
+ await this._params.metadataStore.setSpaceSnapshot(this._params.spaceKey, snapshotKey);
1699
+ return snapshot;
1700
+ }
1701
+ async _saveTargetTimeframe(timeframe) {
1702
+ var _a;
1703
+ const newTimeframe = Timeframe3.merge((_a = this._targetTimeframe) != null ? _a : new Timeframe3(), timeframe);
1704
+ await this._params.metadataStore.setSpaceLatestTimeframe(this._params.spaceKey, newTimeframe);
1705
+ this._targetTimeframe = newTimeframe;
1706
+ }
1707
+ async _noteTargetStateIfNeeded(timeframe) {
1708
+ var _a;
1709
+ if (Date.now() - this._lastTimeframeSaveTime > TIMEFRAME_SAVE_DEBOUNCE_INTERVAL) {
1710
+ this._lastTimeframeSaveTime = Date.now();
1711
+ await this._saveTargetTimeframe(timeframe);
1712
+ }
1713
+ if (!DISABLE_SNAPSHOT_CACHE && Date.now() - this._lastSnapshotSaveTime > AUTOMATIC_SNAPSHOT_DEBOUNCE_INTERVAL && timeframe.totalMessages() - this._lastAutomaticSnapshotTimeframe.totalMessages() > MESSAGES_PER_SNAPSHOT) {
1714
+ this._lastSnapshotSaveTime = Date.now();
1715
+ const snapshot = await this._saveSnapshot(timeframe);
1716
+ this._lastAutomaticSnapshotTimeframe = (_a = snapshot.timeframe) != null ? _a : failUndefined3();
1717
+ log12("save", {
1718
+ snapshot
1719
+ }, {
1720
+ file: "data-pipeline.ts",
1721
+ line: 239,
1722
+ scope: this,
1723
+ callSite: (f, a) => f(...a)
1724
+ });
1725
+ }
1726
+ }
1717
1727
  async waitUntilTimeframe(timeframe) {
1718
1728
  assert9(this._pipeline, "Pipeline is not initialized.");
1719
1729
  await this._pipeline.state.waitUntilTimeframe(timeframe);
@@ -1763,4 +1773,4 @@ export {
1763
1773
  DataServiceImpl,
1764
1774
  DataPipeline
1765
1775
  };
1766
- //# sourceMappingURL=chunk-4TOOIHMA.mjs.map
1776
+ //# sourceMappingURL=chunk-4YAT2XJW.mjs.map