@dxos/random-access-storage 0.1.57-main.e87098f → 0.1.57

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.
@@ -13,12 +13,13 @@ var getFullPath = (root, path) => join(root, stringDiff(path, root));
13
13
 
14
14
  // packages/common/random-access-storage/src/common/directory.ts
15
15
  var Directory = class _Directory {
16
- constructor(type, path, _list, _getOrCreateFile, _delete) {
16
+ constructor(type, path, _list, _getOrCreateFile, _delete, _onFlush) {
17
17
  this.type = type;
18
18
  this.path = path;
19
19
  this._list = _list;
20
20
  this._getOrCreateFile = _getOrCreateFile;
21
21
  this._delete = _delete;
22
+ this._onFlush = _onFlush;
22
23
  }
23
24
  toString() {
24
25
  return `Directory(${JSON.stringify({
@@ -44,6 +45,9 @@ var Directory = class _Directory {
44
45
  getOrCreateFile(filename, opts) {
45
46
  return this._getOrCreateFile(this.path, filename, opts);
46
47
  }
48
+ async flush() {
49
+ await this._onFlush?.();
50
+ }
47
51
  /**
48
52
  * Close and delete all files in the directory and all its sub-directories.
49
53
  */
@@ -55,7 +59,7 @@ var Directory = class _Directory {
55
59
  // packages/common/random-access-storage/src/common/file.ts
56
60
  import pify from "pify";
57
61
  import { log } from "@dxos/log";
58
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/common/random-access-storage/src/common/file.ts";
62
+ var __dxlog_file = "/mnt/ramdisk/work/packages/common/random-access-storage/src/common/file.ts";
59
63
  var pifyFields = (object, type, fields) => {
60
64
  for (const field of fields) {
61
65
  if (!object[field]) {
@@ -100,7 +104,7 @@ var wrapFile = (native, type) => {
100
104
  };
101
105
 
102
106
  // packages/common/random-access-storage/src/common/abstract-storage.ts
103
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/common/random-access-storage/src/common/abstract-storage.ts";
107
+ var __dxlog_file2 = "/mnt/ramdisk/work/packages/common/random-access-storage/src/common/abstract-storage.ts";
104
108
  var AbstractStorage = class {
105
109
  // TODO(burdon): Make required.
106
110
  constructor(path) {
@@ -328,97 +332,7 @@ import { callbackify } from "@dxos/node-std/util";
328
332
  import { synchronized } from "@dxos/async";
329
333
  import { invariant } from "@dxos/invariant";
330
334
  import { log as log3 } from "@dxos/log";
331
- import { TimeSeriesCounter, TimeUsageCounter, trace } from "@dxos/tracing";
332
-
333
- // packages/common/random-access-storage/src/monitor.ts
334
- import { unrefTimeout } from "@dxos/async";
335
- var BUCKET_COLLECTION_INTERVAL = 1e3;
336
- var StorageMonitor = class {
337
- constructor() {
338
- this._enabled = false;
339
- /**
340
- * By resource.
341
- */
342
- this._buckets = /* @__PURE__ */ new Map();
343
- }
344
- enable() {
345
- this._enabled = true;
346
- const timer = setInterval(() => {
347
- this._finalizeBuckets();
348
- }, BUCKET_COLLECTION_INTERVAL);
349
- unrefTimeout(timer);
350
- }
351
- beginOp(op) {
352
- if (!this._enabled) {
353
- return {
354
- end: () => {
355
- }
356
- };
357
- }
358
- const beginTime = performance.now();
359
- return {
360
- end: () => {
361
- const endTime = performance.now();
362
- const duration = endTime - beginTime;
363
- const bucket = this._getOrInitBucket(op.resource);
364
- switch (op.type) {
365
- case "read":
366
- bucket.readOps++;
367
- bucket.readBytes += op.size ?? 0;
368
- bucket.readTime += duration;
369
- break;
370
- case "write":
371
- bucket.writeOps++;
372
- bucket.writeBytes += op.size ?? 0;
373
- bucket.writeTime += duration;
374
- break;
375
- default:
376
- bucket.otherOps++;
377
- bucket.otherTime += duration;
378
- }
379
- }
380
- };
381
- }
382
- _getOrInitBucket(resource) {
383
- const bucket = this._buckets.get(resource);
384
- if (bucket) {
385
- return bucket;
386
- }
387
- const newBucket = {
388
- resource,
389
- beginTime: performance.now(),
390
- endTime: 0,
391
- usage: 0,
392
- readOps: 0,
393
- readBytes: 0,
394
- readTime: 0,
395
- writeOps: 0,
396
- writeBytes: 0,
397
- writeTime: 0,
398
- otherOps: 0,
399
- otherTime: 0
400
- };
401
- this._buckets.set(resource, newBucket);
402
- return newBucket;
403
- }
404
- _finalizeBuckets() {
405
- const now = performance.now();
406
- const toEmit = [];
407
- for (const bucket of this._buckets.values()) {
408
- bucket.endTime = now;
409
- bucket.usage = (bucket.readTime + bucket.writeTime + bucket.otherTime) / (bucket.endTime - bucket.beginTime);
410
- toEmit.push(bucket);
411
- }
412
- this._buckets.clear();
413
- for (const bucket of toEmit) {
414
- this._getOrInitBucket(bucket.resource);
415
- }
416
- (globalThis.__STORAGE_METRICS ??= []).push(...toEmit);
417
- }
418
- };
419
- var STORAGE_MONITOR = new StorageMonitor();
420
-
421
- // packages/common/random-access-storage/src/browser/web-fs.ts
335
+ import { TimeSeriesCounter, trace } from "@dxos/tracing";
422
336
  function _ts_decorate(decorators, target, key, desc) {
423
337
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
424
338
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -429,7 +343,7 @@ function _ts_decorate(decorators, target, key, desc) {
429
343
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
430
344
  return c > 3 && r && Object.defineProperty(target, key, r), r;
431
345
  }
432
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/common/random-access-storage/src/browser/web-fs.ts";
346
+ var __dxlog_file3 = "/mnt/ramdisk/work/packages/common/random-access-storage/src/browser/web-fs.ts";
433
347
  var WebFS = class {
434
348
  constructor(path) {
435
349
  this.path = path;
@@ -463,7 +377,7 @@ var WebFS = class {
463
377
  this._root = await navigator.storage.getDirectory();
464
378
  invariant(this._root, "Root is undefined", {
465
379
  F: __dxlog_file3,
466
- L: 68,
380
+ L: 67,
467
381
  S: this,
468
382
  A: [
469
383
  "this._root",
@@ -473,7 +387,9 @@ var WebFS = class {
473
387
  return this._root;
474
388
  }
475
389
  createDirectory(sub = "") {
476
- return new Directory(this.type, getFullPath(this.path, sub), (path) => this._list(path), (...args) => this.getOrCreateFile(...args), () => this._delete(sub));
390
+ return new Directory(this.type, getFullPath(this.path, sub), (path) => this._list(path), (...args) => this.getOrCreateFile(...args), () => this._delete(sub), async () => {
391
+ await Promise.all(Array.from(this._getFiles(sub)).map(([path, file]) => file.flush()));
392
+ });
477
393
  }
478
394
  getOrCreateFile(path, filename, opts) {
479
395
  const fullName = this._getFullFilename(path, filename);
@@ -502,7 +418,7 @@ var WebFS = class {
502
418
  await Promise.all(Array.from(this._getFiles(path)).map(async ([path2, file]) => {
503
419
  await file.destroy().catch((err) => log3.warn(err, void 0, {
504
420
  F: __dxlog_file3,
505
- L: 108,
421
+ L: 110,
506
422
  S: this,
507
423
  C: (f, a) => f(...a)
508
424
  }));
@@ -517,7 +433,7 @@ var WebFS = class {
517
433
  recursive: true
518
434
  }).catch((err) => log3.warn(err, void 0, {
519
435
  F: __dxlog_file3,
520
- L: 118,
436
+ L: 120,
521
437
  S: this,
522
438
  C: (f, a) => f(...a)
523
439
  }));
@@ -561,6 +477,27 @@ _ts_decorate([
561
477
  var WebFile = class WebFile2 extends EventEmitter {
562
478
  constructor({ fileName, file, destroy }) {
563
479
  super();
480
+ /**
481
+ * Current view of the file contents.
482
+ */
483
+ this._buffer = null;
484
+ this._loadBufferPromise = null;
485
+ this._flushScheduled = false;
486
+ this._flushPromise = null;
487
+ //
488
+ // Metrics
489
+ //
490
+ this._flushes = new TimeSeriesCounter();
491
+ this._operations = new TimeSeriesCounter();
492
+ this._reads = new TimeSeriesCounter();
493
+ this._readBytes = new TimeSeriesCounter();
494
+ this._writes = new TimeSeriesCounter();
495
+ this._writeBytes = new TimeSeriesCounter();
496
+ this.type = StorageType.WEBFS;
497
+ //
498
+ // random-access-storage library compatibility
499
+ //
500
+ // TODO(dmaretskyi): Are those all needed?
564
501
  this.opened = true;
565
502
  this.suspended = false;
566
503
  this.closed = false;
@@ -571,17 +508,10 @@ var WebFile = class WebFile2 extends EventEmitter {
571
508
  this.deletable = true;
572
509
  this.truncatable = true;
573
510
  this.statable = true;
574
- this._usage = new TimeUsageCounter();
575
- this._operations = new TimeSeriesCounter();
576
- this._reads = new TimeSeriesCounter();
577
- this._readBytes = new TimeSeriesCounter();
578
- this._writes = new TimeSeriesCounter();
579
- this._writeBytes = new TimeSeriesCounter();
580
511
  this.destroyed = false;
581
512
  this.directory = "";
582
513
  // TODO(dmaretskyi): is this used?
583
514
  this.filename = "";
584
- this.type = StorageType.WEBFS;
585
515
  this.native = {
586
516
  write: callbackify(this.write.bind(this)),
587
517
  read: callbackify(this.read.bind(this)),
@@ -593,133 +523,187 @@ var WebFile = class WebFile2 extends EventEmitter {
593
523
  this._fileName = fileName;
594
524
  this._fileHandle = file;
595
525
  this._destroy = destroy;
526
+ void this._loadBufferGuarded();
596
527
  }
597
- async write(offset, data) {
598
- const span = this._usage.beginRecording();
599
- this._operations.inc();
600
- this._writes.inc();
601
- this._writeBytes.inc(data.length);
602
- const metric = STORAGE_MONITOR.beginOp({
603
- resource: this._fileName,
528
+ get _bufferSize() {
529
+ return this._buffer?.length;
530
+ }
531
+ async _loadBuffer() {
532
+ const fileHandle = await this._fileHandle;
533
+ const file = await fileHandle.getFile();
534
+ this._buffer = new Uint8Array(await file.arrayBuffer());
535
+ }
536
+ async _loadBufferGuarded() {
537
+ await (this._loadBufferPromise ??= this._loadBuffer());
538
+ }
539
+ // Do not call directly, use _flushLater or _flushNow.
540
+ async _flushCache() {
541
+ if (this.destroyed) {
542
+ return;
543
+ }
544
+ this._flushes.inc();
545
+ await this._loadBufferGuarded();
546
+ invariant(this._buffer, void 0, {
547
+ F: __dxlog_file3,
548
+ L: 279,
549
+ S: this,
550
+ A: [
551
+ "this._buffer",
552
+ ""
553
+ ]
554
+ });
555
+ const fileHandle = await this._fileHandle;
556
+ const writable = await fileHandle.createWritable({
557
+ keepExistingData: true
558
+ });
559
+ await writable.write({
604
560
  type: "write",
605
- size: data.length
561
+ data: this._buffer,
562
+ position: 0
606
563
  });
607
- try {
608
- const fileHandle = await this._fileHandle;
609
- const writable = await fileHandle.createWritable({
610
- keepExistingData: true
611
- });
612
- await writable.write({
613
- type: "write",
614
- data,
615
- position: offset
616
- });
617
- await writable.close();
618
- } finally {
619
- span.end();
620
- metric.end();
564
+ await writable.close();
565
+ }
566
+ _flushLater() {
567
+ if (this._flushScheduled) {
568
+ return;
621
569
  }
570
+ setTimeout(async () => {
571
+ const promiseBefore = this._flushPromise;
572
+ await this._flushPromise;
573
+ this._flushScheduled = false;
574
+ if (promiseBefore !== this._flushPromise) {
575
+ return;
576
+ }
577
+ this._flushPromise = this._flushCache().catch((err) => log3.warn(err, void 0, {
578
+ F: __dxlog_file3,
579
+ L: 303,
580
+ S: this,
581
+ C: (f, a) => f(...a)
582
+ }));
583
+ });
584
+ this._flushScheduled = true;
585
+ }
586
+ async _flushNow() {
587
+ this._flushPromise = (this._flushPromise ?? Promise.resolve()).then(() => this._flushCache()).catch((err) => log3.warn(err, void 0, {
588
+ F: __dxlog_file3,
589
+ L: 312,
590
+ S: this,
591
+ C: (f, a) => f(...a)
592
+ }));
593
+ await this._flushPromise;
622
594
  }
623
595
  async read(offset, size) {
624
- const span = this._usage.beginRecording();
625
596
  this._operations.inc();
626
597
  this._reads.inc();
627
598
  this._readBytes.inc(size);
628
- const metric = STORAGE_MONITOR.beginOp({
629
- resource: this._fileName,
630
- type: "read",
631
- size
632
- });
633
- try {
634
- const fileHandle = await this._fileHandle;
635
- const file = await fileHandle.getFile();
636
- if (offset + size > file.size) {
637
- throw new Error("Read out of bounds");
638
- }
639
- return Buffer.from(await file.slice(offset, offset + size).arrayBuffer());
640
- } finally {
641
- span.end();
642
- metric.end();
599
+ if (!this._buffer) {
600
+ await this._loadBufferGuarded();
601
+ invariant(this._buffer, void 0, {
602
+ F: __dxlog_file3,
603
+ L: 324,
604
+ S: this,
605
+ A: [
606
+ "this._buffer",
607
+ ""
608
+ ]
609
+ });
610
+ }
611
+ if (offset + size > this._buffer.length) {
612
+ throw new Error("Read out of bounds");
643
613
  }
614
+ return Buffer.from(this._buffer.slice(offset, offset + size));
644
615
  }
645
- async del(offset, size) {
646
- const span = this._usage.beginRecording();
616
+ async write(offset, data) {
647
617
  this._operations.inc();
648
- const metric = STORAGE_MONITOR.beginOp({
649
- resource: this._fileName,
650
- type: "delete",
651
- size
652
- });
653
- try {
654
- if (offset < 0 || size < 0) {
655
- return;
656
- }
657
- const fileHandle = await this._fileHandle;
658
- const writable = await fileHandle.createWritable({
659
- keepExistingData: true
618
+ this._writes.inc();
619
+ this._writeBytes.inc(data.length);
620
+ if (!this._buffer) {
621
+ await this._loadBufferGuarded();
622
+ invariant(this._buffer, void 0, {
623
+ F: __dxlog_file3,
624
+ L: 342,
625
+ S: this,
626
+ A: [
627
+ "this._buffer",
628
+ ""
629
+ ]
660
630
  });
661
- const file = await fileHandle.getFile();
662
- let leftoverSize = 0;
663
- if (offset + size < file.size) {
664
- const leftover = Buffer.from(await file.slice(offset + size, file.size).arrayBuffer());
665
- leftoverSize = leftover.length;
666
- await writable.write({
667
- type: "write",
668
- data: leftover,
669
- position: offset
670
- });
671
- }
672
- await writable.write({
673
- type: "truncate",
674
- size: offset + leftoverSize
631
+ }
632
+ if (offset + data.length <= this._buffer.length) {
633
+ this._buffer.set(data, offset);
634
+ } else {
635
+ const newCache = new Uint8Array(offset + data.length);
636
+ newCache.set(this._buffer);
637
+ newCache.set(data, offset);
638
+ this._buffer = newCache;
639
+ }
640
+ this._flushLater();
641
+ }
642
+ async del(offset, size) {
643
+ this._operations.inc();
644
+ if (offset < 0 || size < 0) {
645
+ return;
646
+ }
647
+ if (!this._buffer) {
648
+ await this._loadBufferGuarded();
649
+ invariant(this._buffer, void 0, {
650
+ F: __dxlog_file3,
651
+ L: 367,
652
+ S: this,
653
+ A: [
654
+ "this._buffer",
655
+ ""
656
+ ]
675
657
  });
676
- await writable.close();
677
- } finally {
678
- span.end();
679
- metric.end();
680
658
  }
659
+ let leftoverSize = 0;
660
+ if (offset + size < this._buffer.length) {
661
+ leftoverSize = this._buffer.length - (offset + size);
662
+ this._buffer.set(this._buffer.slice(offset + size, offset + size + leftoverSize), offset);
663
+ }
664
+ this._buffer = this._buffer.slice(0, offset + leftoverSize);
665
+ this._flushLater();
681
666
  }
682
667
  async stat() {
683
- const span = this._usage.beginRecording();
684
668
  this._operations.inc();
685
- const metric = STORAGE_MONITOR.beginOp({
686
- resource: this._fileName,
687
- type: "stat"
688
- });
689
- try {
690
- const fileHandle = await this._fileHandle;
691
- const file = await fileHandle.getFile();
692
- return {
693
- size: file.size
694
- };
695
- } finally {
696
- span.end();
697
- metric.end();
669
+ if (!this._buffer) {
670
+ await this._loadBufferGuarded();
671
+ invariant(this._buffer, void 0, {
672
+ F: __dxlog_file3,
673
+ L: 387,
674
+ S: this,
675
+ A: [
676
+ "this._buffer",
677
+ ""
678
+ ]
679
+ });
698
680
  }
681
+ return {
682
+ size: this._buffer.length
683
+ };
699
684
  }
700
685
  async truncate(offset) {
701
- const span = this._usage.beginRecording();
702
686
  this._operations.inc();
703
- const metric = STORAGE_MONITOR.beginOp({
704
- resource: this._fileName,
705
- type: "truncate"
706
- });
707
- try {
708
- const fileHandle = await this._fileHandle;
709
- const writable = await fileHandle.createWritable({
710
- keepExistingData: true
711
- });
712
- await writable.write({
713
- type: "truncate",
714
- size: offset
687
+ if (!this._buffer) {
688
+ await this._loadBufferGuarded();
689
+ invariant(this._buffer, void 0, {
690
+ F: __dxlog_file3,
691
+ L: 400,
692
+ S: this,
693
+ A: [
694
+ "this._buffer",
695
+ ""
696
+ ]
715
697
  });
716
- await writable.close();
717
- } finally {
718
- span.end();
719
- metric.end();
720
698
  }
699
+ this._buffer = this._buffer.slice(0, offset);
700
+ this._flushLater();
701
+ }
702
+ async flush() {
703
+ await this._flushNow();
721
704
  }
722
705
  async close() {
706
+ await this._flushNow();
723
707
  }
724
708
  async destroy() {
725
709
  this.destroyed = true;
@@ -731,7 +715,7 @@ _ts_decorate([
731
715
  ], WebFile.prototype, "_fileName", void 0);
732
716
  _ts_decorate([
733
717
  trace.metricsCounter()
734
- ], WebFile.prototype, "_usage", void 0);
718
+ ], WebFile.prototype, "_flushes", void 0);
735
719
  _ts_decorate([
736
720
  trace.metricsCounter()
737
721
  ], WebFile.prototype, "_operations", void 0);
@@ -748,20 +732,8 @@ _ts_decorate([
748
732
  trace.metricsCounter()
749
733
  ], WebFile.prototype, "_writeBytes", void 0);
750
734
  _ts_decorate([
751
- synchronized
752
- ], WebFile.prototype, "write", null);
753
- _ts_decorate([
754
- synchronized
755
- ], WebFile.prototype, "read", null);
756
- _ts_decorate([
757
- synchronized
758
- ], WebFile.prototype, "del", null);
759
- _ts_decorate([
760
- synchronized
761
- ], WebFile.prototype, "stat", null);
762
- _ts_decorate([
763
- synchronized
764
- ], WebFile.prototype, "truncate", null);
735
+ trace.info()
736
+ ], WebFile.prototype, "_bufferSize", null);
765
737
  _ts_decorate([
766
738
  synchronized
767
739
  ], WebFile.prototype, "destroy", null);