sliftutils 1.7.26 → 1.7.27

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/index.d.ts CHANGED
@@ -3330,6 +3330,11 @@ declare module "sliftutils/storage/remoteStorage/deployTakeover" {
3330
3330
  /** The middle-window alternate port of an active remap. The OTHER process of the takeover lives on
3331
3331
  * this port on OUR machine (same disk!), so sources pointing at it are self, never sync targets. */
3332
3332
  export declare function getTakeoverAltPort(): number | undefined;
3333
+ /** For the dying process of a takeover: our own process's data ends at the write handoff - fast
3334
+ * writes must be on disk by then. Undefined for the successor and in normal operation. Both
3335
+ * processes share the config identity (all self windows look like "ours" to both), so this is
3336
+ * the only way the dying side knows the post-handoff windows belong to the other process. */
3337
+ export declare function getOwnWindowEndClip(): number | undefined;
3333
3338
  /** How long to wait between main-port acquisition attempts: tight around the predecessor's
3334
3339
  * scheduled death (when the port actually frees), relaxed otherwise. */
3335
3340
  export declare function getMainPortAcquireDelay(): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sliftutils",
3
- "version": "1.7.26",
3
+ "version": "1.7.27",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -23,6 +23,11 @@ export declare function getTakeoverStamp(): string | undefined;
23
23
  /** The middle-window alternate port of an active remap. The OTHER process of the takeover lives on
24
24
  * this port on OUR machine (same disk!), so sources pointing at it are self, never sync targets. */
25
25
  export declare function getTakeoverAltPort(): number | undefined;
26
+ /** For the dying process of a takeover: our own process's data ends at the write handoff - fast
27
+ * writes must be on disk by then. Undefined for the successor and in normal operation. Both
28
+ * processes share the config identity (all self windows look like "ours" to both), so this is
29
+ * the only way the dying side knows the post-handoff windows belong to the other process. */
30
+ export declare function getOwnWindowEndClip(): number | undefined;
26
31
  /** How long to wait between main-port acquisition attempts: tight around the predecessor's
27
32
  * scheduled death (when the port actually frees), relaxed otherwise. */
28
33
  export declare function getMainPortAcquireDelay(): number;
@@ -416,6 +416,18 @@ export function getTakeoverAltPort(): number | undefined {
416
416
  return current.remap?.altPort;
417
417
  }
418
418
 
419
+ /** For the dying process of a takeover: our own process's data ends at the write handoff - fast
420
+ * writes must be on disk by then. Undefined for the successor and in normal operation. Both
421
+ * processes share the config identity (all self windows look like "ours" to both), so this is
422
+ * the only way the dying side knows the post-handoff windows belong to the other process. */
423
+ export function getOwnWindowEndClip(): number | undefined {
424
+ let dying = current.dying;
425
+ if (!dying) return undefined;
426
+ let remap = current.remap;
427
+ if (remap) return remap.boundaryA;
428
+ return dying.successorStart;
429
+ }
430
+
419
431
  /** How long to wait between main-port acquisition attempts: tight around the predecessor's
420
432
  * scheduled death (when the port actually frees), relaxed otherwise. */
421
433
  export function getMainPortAcquireDelay(): number {
@@ -11,7 +11,7 @@ import {
11
11
  WRITE_PAST_WINDOW_GRACE, STORAGE_WRONG_VALID_WINDOW, STORAGE_WRONG_ROUTE, FULL_ROUTE,
12
12
  } from "../IArchives";
13
13
  import { ROUTING_FILE, parseRoutingData, parseHostedUrl, buildFileUrl, getConfigVersion, getRoute, routeContains, routeIntersection } from "./remoteConfig";
14
- import { applyDeployRemap, getTakeoverAltPort, onTakeoverEvent } from "./deployTakeover";
14
+ import { applyDeployRemap, getTakeoverAltPort, getOwnWindowEndClip, onTakeoverEvent } from "./deployTakeover";
15
15
  import { createApiArchives } from "./createArchives";
16
16
  import type { IStorage } from "../IStorage";
17
17
  import type { AccessRequest, TrustRecord } from "./storageController";
@@ -445,9 +445,36 @@ function computeStorePlan(account: string, bucketName: string, routing: RemoteCo
445
445
  // Being removed from the config entirely is a valid window of NOTHING ([0, 0]): fast
446
446
  // writes flush immediately (nothing may sit in memory on a node that's been cut out),
447
447
  // while the disk data and index stay served.
448
+ // Internally every self entry is the SAME store - one process listening on one or more ports
449
+ // (a takeover's alternate-port middle window included). So the disk window merges all
450
+ // contiguous own windows: an options boundary or the port split must never force a pointless
451
+ // internal flush between two windows that are both us. Only the DYING process of a takeover
452
+ // clips its end - from the write handoff on, the data belongs to the successor process.
453
+ let diskWindow: [number, number] = [0, 0];
454
+ if (self) {
455
+ let [start, end] = self.validWindow;
456
+ let merged = true;
457
+ while (merged) {
458
+ merged = false;
459
+ for (let entry of selfEntries) {
460
+ let [entryStart, entryEnd] = entry.validWindow;
461
+ if (entryStart > end || entryEnd < start) continue;
462
+ if (entryStart < start || entryEnd > end) {
463
+ start = Math.min(start, entryStart);
464
+ end = Math.max(end, entryEnd);
465
+ merged = true;
466
+ }
467
+ }
468
+ }
469
+ let clip = getOwnWindowEndClip();
470
+ if (clip !== undefined && clip < end) {
471
+ end = clip;
472
+ }
473
+ diskWindow = [start, end];
474
+ }
448
475
  let ownIndexes = new Set(selfIndexes);
449
476
  let sourceSpecs: StorePlan["sourceSpecs"] = [{
450
- validWindow: self && self.validWindow || [0, 0],
477
+ validWindow: diskWindow,
451
478
  }];
452
479
  if (selfIndex !== -1) {
453
480
  for (let i = selfIndex + 1; i < effective.sources.length; i++) {