| … | … | @@ -104,6 +104,10 @@ export class DeployServer extends ReadyResource { |
| 104 | 104 | this._overlayProtocol = null |
| 105 | 105 | |
| 106 | 106 | this.manifest = null |
| 107 | // Overlay lookup maps + the manifest identity they were built from. |
| 108 | // See _overlayLookupMaps. |
| 109 | this._lookupMaps = null |
| 110 | this._lookupMapsFor = null |
| 107 | 111 | this.stats = { totalConnections: 0, filesServed: 0, bytesServed: 0 } |
| 108 | 112 | |
| 109 | 113 | // Symlink-target blob dir. Durability follows the corestore: a |
| … | … | @@ -618,24 +622,59 @@ export class DeployServer extends ReadyResource { |
| 618 | 622 | * @param {Duplex} conn — an open connection (secret-stream / duplex) |
| 619 | 623 | * @returns {boolean} true if the content channel was attached |
| 620 | 624 | */ |
| 625 | /** |
| 626 | * content-hash → disk-path and path → disk-path lookups for the overlay, |
| 627 | * built once per manifest rather than once per connection. |
| 628 | * |
| 629 | * These were rebuilt from scratch inside serveOnConnection on EVERY inbound |
| 630 | * connection. On tabertron's tree store (39,484 files / 2,977 trees) that |
| 631 | * measured 3.8 MB of garbage and ~79,000 map insertions per connection, for |
| 632 | * a result that is identical every time until the manifest changes. |
| 633 | * |
| 634 | * The cache key is manifest IDENTITY, which is exactly right for the two |
| 635 | * ways manifest.files changes: |
| 636 | * |
| 637 | * _scan / _scanBoot assign a NEW manifest object -> identity differs, |
| 638 | * so the next caller rebuilds. |
| 639 | * _scanIncremental push()es into the EXISTING array -> identity is |
| 640 | * unchanged, and it maintains these same Map objects |
| 641 | * itself with its own .set() calls, so reuse is not |
| 642 | * merely an optimization here: rebuilding would be |
| 643 | * redundant work that throws away nothing. |
| 644 | * |
| 645 | * A version counter would be wrong for the same reason — the incremental |
| 646 | * path deliberately keeps the live maps current so peers connected BEFORE a |
| 647 | * rescan can fetch new bytes without reconnecting. |
| 648 | */ |
| 649 | _overlayLookupMaps () { |
| 650 | if (this._lookupMaps && this._lookupMapsFor === this.manifest) { |
| 651 | return this._lookupMaps |
| 652 | } |
| 653 | |
| 654 | const contentHashPaths = new Map() |
| 655 | const filePaths = new Map() |
| 656 | for (const f of (this.manifest && this.manifest.files) || []) { |
| 657 | if (f.hash) { |
| 658 | // f.diskPath points to the symlink-target blob for symlink |
| 659 | // entries and to the actual source file for regular files. |
| 660 | // Falls back to dir-relative path for back-compat with any |
| 661 | // older manifest shapes that lack f.diskPath. |
| 662 | const diskPath = f.diskPath || path.join(this.dir, f.path) |
| 663 | contentHashPaths.set(f.hash, diskPath) |
| 664 | filePaths.set(f.path, diskPath) |
| 665 | } |
| 666 | } |
| 667 | |
| 668 | this._lookupMaps = { contentHashPaths, filePaths } |
| 669 | this._lookupMapsFor = this.manifest |
| 670 | return this._lookupMaps |
| 671 | } |
| 672 | |
| 621 | 673 | serveOnConnection (conn) { |
| 622 | 674 | if (!this._overlayProtocol) return false |
| 623 | 675 | try { |
| 624 | 676 | const mux = Protomux.from(conn) |
| 625 | | // Build content-hash → disk-path map so content-request works. |
| 626 | | const contentHashPaths = new Map() |
| 627 | | const filePaths = new Map() |
| 628 | | for (const f of (this.manifest && this.manifest.files) || []) { |
| 629 | | if (f.hash) { |
| 630 | | // f.diskPath points to the symlink-target blob for symlink |
| 631 | | // entries and to the actual source file for regular files. |
| 632 | | // Falls back to dir-relative path for back-compat with any |
| 633 | | // older manifest shapes that lack f.diskPath. |
| 634 | | const diskPath = f.diskPath || path.join(this.dir, f.path) |
| 635 | | contentHashPaths.set(f.hash, diskPath) |
| 636 | | filePaths.set(f.path, diskPath) |
| 637 | | } |
| 638 | | } |
| 677 | const { contentHashPaths, filePaths } = this._overlayLookupMaps() |
| 639 | 678 | this._overlayProtocol._contentHashPaths = contentHashPaths |
| 640 | 679 | this._overlayProtocol._filePaths = filePaths |
| 641 | 680 | this._overlayProtocol.attach(mux) |