sliftutils 1.7.28 → 1.7.29

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.
@@ -14,8 +14,9 @@ import { getTakeoverStamp } from "./deployTakeover";
14
14
  import {
15
15
  getStorageServerConfig, getTrust, getRequests, getLoadedBucket, writeBucketFile,
16
16
  deleteBucketFile, assertWritesAllowed, assertMutable, LoadedBucket,
17
- getBucketConfig, listAccountBuckets, ServerBucketInfo,
17
+ getBucketConfig, listAccountBuckets, ServerBucketInfo, setRoutingChangedBroadcaster,
18
18
  } from "./storageServerState";
19
+ import { StorageClientController } from "./storageClientController";
19
20
 
20
21
  // The remote storage server's API. Authentication uses certs.ts machine identities: a client
21
22
  // proves it owns its machine key by signing a timestamped token (bound to this server, so tokens
@@ -105,6 +106,31 @@ function assertValidPath(path: string) {
105
106
  }
106
107
  }
107
108
 
109
+ // Every client pings us (immediately on connect, then continuously), so pings tell us exactly who
110
+ // our currently-connected clients are. Client nodeIds never reconnect (a reconnect is a NEW
111
+ // nodeId), so the disconnect callback fully cleans an entry up.
112
+ const connectedClients = new Set<string>();
113
+ function trackCaller(): void {
114
+ let nodeId = SocketFunction.getCaller().nodeId;
115
+ if (connectedClients.has(nodeId)) return;
116
+ connectedClients.add(nodeId);
117
+ SocketFunction.onNextDisconnect(nodeId, () => {
118
+ connectedClients.delete(nodeId);
119
+ });
120
+ }
121
+
122
+ // The moment any routing config changes on this server, every connected client is told - clients
123
+ // must never have to wait for a poll to learn the topology changed
124
+ setRoutingChangedBroadcaster(() => {
125
+ console.log(`Broadcasting routing config change to ${connectedClients.size} connected clients`);
126
+ for (let nodeId of [...connectedClients]) {
127
+ void StorageClientController.nodes[nodeId].routingConfigChanged().catch(() => {
128
+ // The client is gone (or too old to know the controller); the disconnect callback
129
+ // cleans the registry, nothing to do here
130
+ });
131
+ }
132
+ });
133
+
108
134
  function getCallerMachineId(): string {
109
135
  let caller = SocketFunction.getCaller();
110
136
  let machineId = sessions.get(caller.nodeId);
@@ -155,8 +181,10 @@ async function getBucket(account: string, bucketName: string): Promise<LoadedBuc
155
181
  class RemoteStorageControllerBase {
156
182
  // Latency measurement (see SourceWrapper's pinging); no auth, it measures the transport. The
157
183
  // takeover stamp piggybacks on it so every connected client learns of a deploy takeover
158
- // within one ping interval.
184
+ // within one ping interval. Pings also tell us who our clients ARE - the trackCaller
185
+ // registry is what routing-change broadcasts push to.
159
186
  async ping(): Promise<{ takeover?: string }> {
187
+ trackCaller();
160
188
  return { takeover: getTakeoverStamp() };
161
189
  }
162
190
 
@@ -37,6 +37,7 @@ export type LoadedBucket = {
37
37
  };
38
38
  export declare function addExtraListenPort(port: number): void;
39
39
  export declare function getLoadedBucket(account: string, bucketName: string): Promise<LoadedBucket | undefined>;
40
+ export declare function setRoutingChangedBroadcaster(broadcaster: () => void): void;
40
41
  export declare function assertMutable(bucket: LoadedBucket, filePath: string, writeTime: number): Promise<void>;
41
42
  export declare function writeBucketFile(account: string, bucketName: string, filePath: string, data: Buffer, config?: {
42
43
  lastModified?: number;
@@ -563,6 +563,18 @@ export function getLoadedBucket(account: string, bucketName: string): Promise<Lo
563
563
  return loaded;
564
564
  }
565
565
 
566
+ // Called whenever any bucket's routing config is applied (created, updated in place, or rebuilt).
567
+ // storageController wires this to its connected-client broadcast, so every client is pushed the
568
+ // change immediately - never waiting for a poll. (A callback, not an import - storageController
569
+ // imports us, so importing it back would be a cycle.)
570
+ let routingChangedBroadcaster: (() => void) | undefined;
571
+ export function setRoutingChangedBroadcaster(broadcaster: () => void): void {
572
+ routingChangedBroadcaster = broadcaster;
573
+ }
574
+ function notifyRoutingChanged(): void {
575
+ routingChangedBroadcaster?.();
576
+ }
577
+
566
578
  // Routing reloads are serialized per bucket, so concurrent writes/syncs can't rebuild the same
567
579
  // store twice in parallel
568
580
  const routingReloads = new Map<string, Promise<void>>();
@@ -610,6 +622,7 @@ async function checkRoutingChanged(account: string, bucketName: string, config?:
610
622
  buckets.set(key, Promise.resolve(updated));
611
623
  scheduleWindowBoundaryRebuild(updated);
612
624
  scheduleBoundaryScans(updated);
625
+ notifyRoutingChanged();
613
626
  return;
614
627
  }
615
628
  console.log(`Rebuilding the store for bucket ${key} (${reason})`);
@@ -617,6 +630,7 @@ async function checkRoutingChanged(account: string, bucketName: string, config?:
617
630
  await loaded.store.dispose();
618
631
  }
619
632
  buckets.set(key, Promise.resolve(buildBucket(account, bucketName, routing, plan)));
633
+ notifyRoutingChanged();
620
634
  }
621
635
 
622
636
  // Per-write options are evaluated at the WRITE's time (and the key's route), not the current
@@ -657,6 +671,7 @@ async function writeRoutingConfig(account: string, bucketName: string, data: Buf
657
671
  if (!loaded) {
658
672
  await new ArchivesDisk(getBucketFolder(account, bucketName)).set(ROUTING_FILE, data, { lastModified: config?.lastModified });
659
673
  buckets.set(key, Promise.resolve(buildBucket(account, bucketName, incoming)));
674
+ notifyRoutingChanged();
660
675
  console.log(`Created bucket ${key}`);
661
676
  return;
662
677
  }