sliftutils 1.7.32 → 1.7.33
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/builders/extensionBuild.ts +1 -1
- package/bundler/bundleEntry.ts +1 -3
- package/bundler/bundler.ts +2 -4
- package/bundler/sourceMaps.ts +1 -2
- package/examplestorage/browser.tsx +1 -2
- package/examplestorage/exampleserver.ts +1 -2
- package/index.d.ts +105 -100
- package/misc/dist/environment.ts.cache +2 -2
- package/misc/dist/getSecret.ts.cache +2 -2
- package/misc/dist/strings.ts.cache +2 -2
- package/misc/dist/zip.ts.cache +2 -2
- package/misc/https/dist/certs.ts.cache +2 -2
- package/misc/https/dist/dns.ts.cache +7 -16
- package/misc/https/dist/persistentLocalStorage.ts.cache +2 -2
- package/misc/https/dns.d.ts +1 -4
- package/misc/https/dns.ts +5 -14
- package/misc/https/hostServer.d.ts +2 -0
- package/misc/https/hostServer.ts +6 -7
- package/misc/https/httpsCerts.ts +4 -10
- package/misc/openrouter.ts +1 -2
- package/package.json +1 -1
- package/render-utils/FullscreenModal.tsx +1 -4
- package/render-utils/GenericFormat.tsx +2 -8
- package/render-utils/Input.tsx +5 -13
- package/render-utils/InputLabel.tsx +4 -6
- package/render-utils/InputPicker.tsx +1 -2
- package/render-utils/URLParam.ts +2 -3
- package/render-utils/dist/observer.tsx.cache +2 -2
- package/render-utils/mobxTyped.ts +2 -3
- package/render-utils/niceStringify.ts +2 -4
- package/storage/ArchivesDisk.ts +4 -14
- package/storage/BulkDatabase2/BulkDatabase2.d.ts +1 -2
- package/storage/BulkDatabase2/BulkDatabase2.ts +4 -11
- package/storage/BulkDatabase2/BulkDatabaseBase.ts +42 -114
- package/storage/BulkDatabase2/BulkDatabaseFormat.ts +31 -91
- package/storage/BulkDatabase2/BulkDatabaseMerge.ts +19 -57
- package/storage/BulkDatabase2/BulkDatabaseReader.ts +4 -12
- package/storage/BulkDatabase2/LoadedIndex.ts +6 -18
- package/storage/BulkDatabase2/WriteOverlay.ts +3 -8
- package/storage/BulkDatabase2/blockCache.ts +12 -28
- package/storage/BulkDatabase2/dist/BulkDatabaseBase.ts.cache +40 -103
- package/storage/BulkDatabase2/dist/BulkDatabaseFormat.ts.cache +19 -50
- package/storage/BulkDatabase2/dist/BulkDatabaseMerge.ts.cache +18 -50
- package/storage/BulkDatabase2/dist/BulkDatabaseReader.ts.cache +7 -15
- package/storage/BulkDatabase2/dist/LoadedIndex.ts.cache +9 -21
- package/storage/BulkDatabase2/dist/WriteOverlay.ts.cache +6 -11
- package/storage/BulkDatabase2/dist/blockCache.ts.cache +15 -31
- package/storage/BulkDatabase2/dist/mergeLock.ts.cache +10 -29
- package/storage/BulkDatabase2/dist/mergeMarkers.ts.cache +10 -18
- package/storage/BulkDatabase2/dist/streamLog.ts.cache +17 -43
- package/storage/BulkDatabase2/dist/syncClient.ts.cache +11 -27
- package/storage/BulkDatabase2/mergeLock.ts +7 -26
- package/storage/BulkDatabase2/mergeMarkers.ts +7 -15
- package/storage/BulkDatabase2/streamLog.ts +14 -40
- package/storage/BulkDatabase2/syncClient.ts +8 -24
- package/storage/FileFolderAPI.tsx +28 -75
- package/storage/IArchives.d.ts +3 -3
- package/storage/IArchives.ts +21 -50
- package/storage/IStorage.ts +2 -4
- package/storage/PendingManager.tsx +2 -3
- package/storage/TransactionStorage.ts +11 -24
- package/storage/backblaze.ts +12 -43
- package/storage/dist/ArchivesDisk.ts.cache +7 -17
- package/storage/dist/FileFolderAPI.tsx.cache +25 -63
- package/storage/dist/IArchives.ts.cache +10 -20
- package/storage/dist/IndexedDBFileFolderAPI.ts.cache +2 -2
- package/storage/dist/JSONStorage.ts.cache +2 -2
- package/storage/dist/PendingManager.tsx.cache +5 -6
- package/storage/dist/TransactionStorage.ts.cache +14 -27
- package/storage/dist/backblaze.ts.cache +15 -46
- package/storage/dist/embeddingFormats.ts.cache +11 -24
- package/storage/dist/fileSystemPointer.ts.cache +4 -7
- package/storage/dist/remoteFileStorage.ts.cache +17 -36
- package/storage/embeddingFormats.ts +11 -27
- package/storage/fileSystemPointer.ts +2 -7
- package/storage/proxydatabase/dist/ivfEmbeddingDatabase.ts.cache +8 -18
- package/storage/proxydatabase/dist/transactionSet.ts.cache +4 -7
- package/storage/proxydatabase/inMemoryDatabase.ts +1 -3
- package/storage/proxydatabase/ivfEmbeddingDatabase.ts +8 -23
- package/storage/proxydatabase/transactionSet.ts +2 -5
- package/storage/remoteFileServer.ts +20 -48
- package/storage/remoteFileStorage.ts +14 -33
- package/storage/remoteStorage/ArchivesRemote.d.ts +1 -4
- package/storage/remoteStorage/ArchivesRemote.ts +11 -26
- package/storage/remoteStorage/ArchivesUrl.ts +2 -4
- package/storage/remoteStorage/accessPage.tsx +1 -5
- package/storage/remoteStorage/blobStore.d.ts +5 -13
- package/storage/remoteStorage/blobStore.ts +52 -130
- package/storage/remoteStorage/createArchives.d.ts +9 -13
- package/storage/remoteStorage/createArchives.ts +43 -187
- package/storage/remoteStorage/deployTakeover.d.ts +18 -32
- package/storage/remoteStorage/deployTakeover.ts +70 -384
- package/storage/remoteStorage/dist/ArchivesRemote.ts.cache +12 -24
- package/storage/remoteStorage/dist/ArchivesUrl.ts.cache +5 -7
- package/storage/remoteStorage/dist/blobStore.ts.cache +47 -117
- package/storage/remoteStorage/dist/cliArgs.ts.cache +2 -2
- package/storage/remoteStorage/dist/createArchives.ts.cache +48 -189
- package/storage/remoteStorage/dist/deployTakeover.ts.cache +81 -387
- package/storage/remoteStorage/dist/intermediateSources.ts.cache +119 -0
- package/storage/remoteStorage/dist/remoteConfig.ts.cache +36 -24
- package/storage/remoteStorage/dist/sourceWrapper.ts.cache +20 -40
- package/storage/remoteStorage/dist/storageClientController.ts.cache +4 -6
- package/storage/remoteStorage/dist/storageController.ts.cache +11 -73
- package/storage/remoteStorage/dist/storageServerState.ts.cache +265 -178
- package/storage/remoteStorage/grantAccessCli.ts +1 -3
- package/storage/remoteStorage/intermediateSources.d.ts +21 -0
- package/storage/remoteStorage/intermediateSources.ts +117 -0
- package/storage/remoteStorage/remoteConfig.ts +33 -20
- package/storage/remoteStorage/sourceWrapper.d.ts +8 -19
- package/storage/remoteStorage/sourceWrapper.ts +19 -41
- package/storage/remoteStorage/storageClientController.ts +1 -3
- package/storage/remoteStorage/storageController.d.ts +4 -5
- package/storage/remoteStorage/storageController.ts +10 -81
- package/storage/remoteStorage/storageServer.ts +20 -34
- package/storage/remoteStorage/storageServerState.d.ts +28 -5
- package/storage/remoteStorage/storageServerState.ts +271 -207
|
@@ -13,18 +13,11 @@ import { ROUTING_FILE, getRoute, routeContains } from "./remoteConfig";
|
|
|
13
13
|
import { BulkDatabaseBase, noopReactiveDeps } from "../BulkDatabase2/BulkDatabaseBase";
|
|
14
14
|
import { wrapHandle, NodeJSDirectoryHandleWrapper, DirectoryWrapper } from "../FileFolderAPI";
|
|
15
15
|
|
|
16
|
-
// The storage engine of the remote storage server. Data lives in synchronization sources (at
|
|
17
|
-
// minimum an ArchivesDisk, the local disk); BlobStore keeps an index of every file (path, last
|
|
18
|
-
// modified time, size, and which source currently holds the data) in a BulkDatabase2, and
|
|
19
|
-
// synchronizes the index from all sources (see ArchivesSource in IArchives.ts).
|
|
20
|
-
// Every startup fully rescans each source's metadata, so the index self-heals; the file with the
|
|
21
|
-
// highest write time wins across all sources, so multiple sources need no stacking order.
|
|
16
|
+
// The storage engine of the remote storage server. Data lives in synchronization sources (at minimum an ArchivesDisk, the local disk); BlobStore keeps an index of every file (path, last modified time, size, and which source currently holds the data) in a BulkDatabase2, and synchronizes the index from all sources (see ArchivesSource in IArchives.ts). Every startup fully rescans each source's metadata, so the index self-heals; the file with the highest write time wins across all sources, so multiple sources need no stacking order.
|
|
22
17
|
|
|
23
18
|
export const DEFAULT_FAST_WRITE_DELAY = timeInMinute * 5;
|
|
24
19
|
const FAST_FLUSH_POLL = 1000 * 15;
|
|
25
|
-
// Fast writes are never delayed past our own valid window, and within this margin of the window's
|
|
26
|
-
// end they write through immediately - so when the next window's source takes over, the writes are
|
|
27
|
-
// already on disk
|
|
20
|
+
// Fast writes are never delayed past our own valid window, and within this margin of the window's end they write through immediately - so when the next window's source takes over, the writes are already on disk
|
|
28
21
|
export const WINDOW_END_FLUSH_MARGIN = timeInMinute * 5;
|
|
29
22
|
// Index changes are buffered in memory and written to the BulkDatabase2 in batches
|
|
30
23
|
const INDEX_FLUSH_INTERVAL = 1000 * 30;
|
|
@@ -32,38 +25,31 @@ const INDEX_FLUSH_INTERVAL = 1000 * 30;
|
|
|
32
25
|
const CHANGES_POLL_INTERVAL = 1000 * 60;
|
|
33
26
|
// Sources that don't support getChangesAfter get a full metadata rescan this often
|
|
34
27
|
const FULL_RESCAN_INTERVAL = 1000 * 60 * 60;
|
|
35
|
-
// On a request for a file the index doesn't know, changes-after sources are re-polled, at most
|
|
36
|
-
// this often
|
|
28
|
+
// On a request for a file the index doesn't know, changes-after sources are re-polled, at most this often
|
|
37
29
|
const MISS_CHECK_INTERVAL = 1000 * 5;
|
|
38
30
|
// Change polls re-request this much overlap, so clock skew between us and a source can't drop changes
|
|
39
31
|
const CHANGES_POLL_OVERLAP = timeInMinute;
|
|
40
32
|
const SCAN_RETRY_DELAY = 1000 * 30;
|
|
41
|
-
// Deletes are tombstones (an empty file IS a missing file): the size-0 index entry is what lets a
|
|
42
|
-
// deletion propagate/reconcile like any other write, and it expires after this long
|
|
33
|
+
// Deletes are tombstones (an empty file IS a missing file): the size-0 index entry is what lets a deletion propagate/reconcile like any other write, and it expires after this long
|
|
43
34
|
const TOMBSTONE_EXPIRY = 1000 * 60 * 60 * 24 * 7;
|
|
44
35
|
const TOMBSTONE_CLEANUP_INTERVAL = 1000 * 60 * 60;
|
|
45
36
|
// While a metadata scan or full sync is running, its progress is logged this often
|
|
46
37
|
const SYNC_PROGRESS_LOG_INTERVAL = 1000 * 60;
|
|
47
38
|
const DISK_LIMIT_CHECK_INTERVAL = 1000 * 60;
|
|
48
|
-
// Full syncs download this many files concurrently (high-latency sources like backblaze would
|
|
49
|
-
// otherwise crawl one round-trip at a time)
|
|
39
|
+
// Full syncs download this many files concurrently (high-latency sources like backblaze would otherwise crawl one round-trip at a time)
|
|
50
40
|
const FULL_SYNC_PARALLEL = 8;
|
|
51
|
-
// A full sync running longer than this is console.errored (and again every interval after), so a
|
|
52
|
-
// sync that will take days is loud instead of a quiet console.log every minute
|
|
41
|
+
// A full sync running longer than this is console.errored (and again every interval after), so a sync that will take days is loud instead of a quiet console.log every minute
|
|
53
42
|
const FULL_SYNC_SLOW_ERROR_INTERVAL = 1000 * 60 * 60;
|
|
54
43
|
|
|
55
44
|
export type WriteConfig = {
|
|
56
|
-
// Resolve once the write is in memory; flush to the sources after writeDelay, coalescing
|
|
57
|
-
// writes to the same key (only the latest is written). Data is lost if the process crashes first.
|
|
45
|
+
// Resolve once the write is in memory; flush to the sources after writeDelay, coalescing writes to the same key (only the latest is written). Data is lost if the process crashes first.
|
|
58
46
|
fast?: boolean;
|
|
59
47
|
writeDelay?: number;
|
|
60
|
-
// Stamps the write with this last-write time instead of now. Older than the current file's
|
|
61
|
-
// time no-ops; more than 15 minutes in the future throws. See IArchives.set.
|
|
48
|
+
// Stamps the write with this last-write time instead of now. Older than the current file's time no-ops; more than 15 minutes in the future throws. See IArchives.set.
|
|
62
49
|
lastModified?: number;
|
|
63
50
|
};
|
|
64
51
|
|
|
65
|
-
// What the storage server needs from a bucket's store. BlobStore implements it fully; ArchivesDisk
|
|
66
|
-
// also satisfies it (used directly for rawDisk buckets), minus the optional index-backed methods.
|
|
52
|
+
// What the storage server needs from a bucket's store. BlobStore implements it fully; ArchivesDisk also satisfies it (used directly for rawDisk buckets), minus the optional index-backed methods.
|
|
67
53
|
export type IBucketStore = {
|
|
68
54
|
get(fileName: string, config?: { range?: { start: number; end: number } }): Promise<Buffer | undefined>;
|
|
69
55
|
get2(fileName: string, config?: { range?: { start: number; end: number } }): Promise<{ data: Buffer; writeTime: number; size: number } | undefined>;
|
|
@@ -105,11 +91,9 @@ type IndexEntry = {
|
|
|
105
91
|
writeTime: number;
|
|
106
92
|
size: number;
|
|
107
93
|
source: number;
|
|
108
|
-
// When WE last changed this entry (not the file's write time) — what getChangesAfter filters
|
|
109
|
-
// on, so late-arriving files with old write times are still reported as changes
|
|
94
|
+
// When WE last changed this entry (not the file's write time) — what getChangesAfter filters on, so late-arriving files with old write times are still reported as changes
|
|
110
95
|
changedAt: number;
|
|
111
|
-
// In-memory only (not persisted): when the file was last served, for readerDiskLimit's LRU
|
|
112
|
-
// eviction. Starts as changedAt on load.
|
|
96
|
+
// In-memory only (not persisted): when the file was last served, for readerDiskLimit's LRU eviction. Starts as changedAt on load.
|
|
113
97
|
lastAccess: number;
|
|
114
98
|
};
|
|
115
99
|
|
|
@@ -124,15 +108,13 @@ type SourceState = {
|
|
|
124
108
|
lastMissCheck: number;
|
|
125
109
|
// Per-slot stop token: a removed source's loops stop without touching the rest of the store
|
|
126
110
|
stopped: { stop: boolean };
|
|
127
|
-
// Source slots are stable forever (index entries reference sources by slot number), so a
|
|
128
|
-
// removed source's slot stays in the arrays, marked dead - never scanned, written, or read
|
|
111
|
+
// Source slots are stable forever (index entries reference sources by slot number), so a removed source's slot stays in the arrays, marked dead - never scanned, written, or read
|
|
129
112
|
dead?: boolean;
|
|
130
113
|
};
|
|
131
114
|
|
|
132
115
|
// One source of a live source-list update; see BlobStore.updateSources
|
|
133
116
|
export type BlobSourceSpec = {
|
|
134
|
-
// Matched against ArchivesSource.identity ("disk" for the base slot); equal identities pair
|
|
135
|
-
// off in order
|
|
117
|
+
// Matched against ArchivesSource.identity ("disk" for the base slot); equal identities pair off in order
|
|
136
118
|
identity: string;
|
|
137
119
|
validWindow: [number, number];
|
|
138
120
|
route?: [number, number];
|
|
@@ -169,11 +151,12 @@ export class BlobStore implements IBucketStore {
|
|
|
169
151
|
private folder: string,
|
|
170
152
|
private sources: ArchivesSource[],
|
|
171
153
|
private config?: {
|
|
172
|
-
// Called whenever a key's index entry changes (our own writes AND files pulled in via
|
|
173
|
-
// synchronization) — how the storage server notices routing config updates.
|
|
154
|
+
// Called whenever a key's index entry changes (our own writes AND files pulled in via synchronization) — how the storage server notices routing config updates.
|
|
174
155
|
onIndexChanged?: (key: string) => void;
|
|
175
156
|
// LRU-bound the disk (base source) to this many bytes; see CommonConfig.readerDiskLimit
|
|
176
157
|
readerDiskLimit?: number;
|
|
158
|
+
// Every accepted write ("original") and every write that actually reached the sources ("flushed"). Fast writes coalesce, so the two counts differ.
|
|
159
|
+
onWriteCounted?: (kind: "original" | "flushed", bytes: number) => void;
|
|
177
160
|
}
|
|
178
161
|
) { }
|
|
179
162
|
|
|
@@ -187,18 +170,15 @@ export class BlobStore implements IBucketStore {
|
|
|
187
170
|
}
|
|
188
171
|
return wrapHandle(base);
|
|
189
172
|
});
|
|
190
|
-
// The in-memory copy of the index. All reads are served from it; changes are buffered in
|
|
191
|
-
// dirty and flushed to the BulkDatabase2 in batches (undefined = delete).
|
|
173
|
+
// The in-memory copy of the index. All reads are served from it; changes are buffered in dirty and flushed to the BulkDatabase2 in batches (undefined = delete).
|
|
192
174
|
private mem = new Map<string, IndexEntry>();
|
|
193
|
-
// Live totals over mem (tombstones excluded), adjusted on every mutation and recomputed on
|
|
194
|
-
// load - so any drift heals on restart. computeIndexTotals gives the walk-the-index truth.
|
|
175
|
+
// Live totals over mem (tombstones excluded), adjusted on every mutation and recomputed on load - so any drift heals on restart. computeIndexTotals gives the walk-the-index truth.
|
|
195
176
|
private indexFileCount = 0;
|
|
196
177
|
private indexByteCount = 0;
|
|
197
178
|
// The same totals per holding source (index 0 = our disk, which readerDiskLimit bounds)
|
|
198
179
|
private sourceFileCounts = this.sources.map(() => 0);
|
|
199
180
|
private sourceByteCounts = this.sources.map(() => 0);
|
|
200
|
-
// Background scans / full syncs currently in progress (a Set - one source can have a change
|
|
201
|
-
// poll's full sync and a rescan overlapping)
|
|
181
|
+
// Background scans / full syncs currently in progress (a Set - one source can have a change poll's full sync and a rescan overlapping)
|
|
202
182
|
private syncActivities = new Set<SyncActivity>();
|
|
203
183
|
private dirty = new Map<string, IndexEntry | undefined>();
|
|
204
184
|
private overlay = new Map<string, OverlayEntry>();
|
|
@@ -224,9 +204,7 @@ export class BlobStore implements IBucketStore {
|
|
|
224
204
|
}
|
|
225
205
|
});
|
|
226
206
|
|
|
227
|
-
// Stops all synchronization scans/polls and flushes pending writes. Only used when the store
|
|
228
|
-
// genuinely cannot continue (rawDisk flip, process shutdown) - routine config changes go
|
|
229
|
-
// through updateSources instead, which the store survives.
|
|
207
|
+
// Stops all synchronization scans/polls and flushes pending writes. Only used when the store genuinely cannot continue (rawDisk flip, process shutdown) - routine config changes go through updateSources instead, which the store survives.
|
|
230
208
|
public async dispose(): Promise<void> {
|
|
231
209
|
this.stopped.stop = true;
|
|
232
210
|
for (let state of this.sourceStates) {
|
|
@@ -249,8 +227,7 @@ export class BlobStore implements IBucketStore {
|
|
|
249
227
|
let source = sourceMap.get(entry.key);
|
|
250
228
|
// Explicit checks, as 0 is a valid size and a valid source number
|
|
251
229
|
if (size === undefined || source === undefined) continue;
|
|
252
|
-
// The routing config is only ever read off our own disk (see applyScanned), and a
|
|
253
|
-
// loaded bucket always has it there - a persisted entry pointing elsewhere is stale
|
|
230
|
+
// The routing config is only ever read off our own disk (see applyScanned), and a loaded bucket always has it there - a persisted entry pointing elsewhere is stale
|
|
254
231
|
if (entry.key === ROUTING_FILE) {
|
|
255
232
|
source = 0;
|
|
256
233
|
}
|
|
@@ -287,12 +264,7 @@ export class BlobStore implements IBucketStore {
|
|
|
287
264
|
this.dirty.set(key, undefined);
|
|
288
265
|
}
|
|
289
266
|
|
|
290
|
-
/** Applies a config change to the RUNNING store: windows/routes update in place, new sources
|
|
291
|
-
* are added (their sync starts immediately), and removed sources' slots go dead (their scans
|
|
292
|
-
* stop, their index entries drop). The store survives every routine config evolution - it is
|
|
293
|
-
* never destroyed for a source-list change, only for structural flips it cannot express
|
|
294
|
-
* (rawDisk). Pending fast writes are re-capped to the new flush deadline (flushing
|
|
295
|
-
* immediately when it has already passed). */
|
|
267
|
+
/** Applies a config change to the RUNNING store: windows/routes update in place, new sources are added (their sync starts immediately), and removed sources' slots go dead (their scans stop, their index entries drop). The store survives every routine config evolution - it is never destroyed for a source-list change, only for structural flips it cannot express (rawDisk). Pending fast writes are re-capped to the new flush deadline (flushing immediately when it has already passed). */
|
|
296
268
|
public updateSources(specs: BlobSourceSpec[]): void {
|
|
297
269
|
if (!specs.length || specs[0].identity !== "disk") {
|
|
298
270
|
throw new Error(`updateSources expects the disk source first (identity "disk"), got ${JSON.stringify(specs.map(x => x.identity))} (store ${this.folder})`);
|
|
@@ -304,8 +276,7 @@ export class BlobStore implements IBucketStore {
|
|
|
304
276
|
this.sources[i].validWindow = window;
|
|
305
277
|
};
|
|
306
278
|
setWindow(0, specs[0].validWindow);
|
|
307
|
-
// Live slots pair with specs by identity, in order (the same endpoint can appear several
|
|
308
|
-
// times, e.g. one entry per window)
|
|
279
|
+
// Live slots pair with specs by identity, in order (the same endpoint can appear several times, e.g. one entry per window)
|
|
309
280
|
let liveByIdentity = new Map<string, number[]>();
|
|
310
281
|
let originalLength = this.sources.length;
|
|
311
282
|
for (let i = 1; i < originalLength; i++) {
|
|
@@ -362,9 +333,7 @@ export class BlobStore implements IBucketStore {
|
|
|
362
333
|
}
|
|
363
334
|
}
|
|
364
335
|
|
|
365
|
-
// The slot stays in the arrays forever (index entries reference sources by slot number); it
|
|
366
|
-
// just goes dead - loops stop, and its index entries drop (other sources' scans re-find any
|
|
367
|
-
// copy that's still reachable through the new config)
|
|
336
|
+
// The slot stays in the arrays forever (index entries reference sources by slot number); it just goes dead - loops stop, and its index entries drop (other sources' scans re-find any copy that's still reachable through the new config)
|
|
368
337
|
private removeSource(sourceIndex: number): void {
|
|
369
338
|
let state = this.sourceStates[sourceIndex];
|
|
370
339
|
let source = this.sources[sourceIndex].source;
|
|
@@ -381,16 +350,13 @@ export class BlobStore implements IBucketStore {
|
|
|
381
350
|
console.log(`Removed sync source ${source.getDebugName()} (store ${this.folder}): its scans are stopped and ${dropped} index entries it held were dropped`);
|
|
382
351
|
}
|
|
383
352
|
|
|
384
|
-
/** Rescans our own disk's metadata into the index - used around valid window handoffs, where
|
|
385
|
-
* another process wrote files to the shared folder that our index hasn't seen. */
|
|
353
|
+
/** Rescans our own disk's metadata into the index - used around valid window handoffs, where another process wrote files to the shared folder that our index hasn't seen. */
|
|
386
354
|
public async rescanBase(): Promise<void> {
|
|
387
355
|
await this.init();
|
|
388
356
|
await this.scanSource(0);
|
|
389
357
|
}
|
|
390
358
|
|
|
391
|
-
/** A boundary scan of the node that owned (part of) our route in the valid window before ours,
|
|
392
|
-
* when that node is different storage (a disk rescan can't see its writes): just its changes
|
|
393
|
-
* since the boundary neighborhood, with matching values pulled onto our own disk. */
|
|
359
|
+
/** A boundary scan of the node that owned (part of) our route in the valid window before ours, when that node is different storage (a disk rescan can't see its writes): just its changes since the boundary neighborhood, with matching values pulled onto our own disk. */
|
|
394
360
|
public async boundaryScanRemote(source: IArchives, config: { since: number; route?: [number, number] }): Promise<void> {
|
|
395
361
|
await this.init();
|
|
396
362
|
let scanStart = Date.now();
|
|
@@ -457,8 +423,7 @@ export class BlobStore implements IBucketStore {
|
|
|
457
423
|
};
|
|
458
424
|
}
|
|
459
425
|
|
|
460
|
-
/** Walks the whole index for exact totals - more expensive than getSyncProgress, but immune to
|
|
461
|
-
* any drift in the maintained counters (and loads the index first, so it's never cold zeros). */
|
|
426
|
+
/** Walks the whole index for exact totals - more expensive than getSyncProgress, but immune to any drift in the maintained counters (and loads the index first, so it's never cold zeros). */
|
|
462
427
|
public async computeIndexTotals(): Promise<{
|
|
463
428
|
fileCount: number;
|
|
464
429
|
byteCount: number;
|
|
@@ -535,8 +500,7 @@ export class BlobStore implements IBucketStore {
|
|
|
535
500
|
await this.pollChanges(sourceIndex);
|
|
536
501
|
if (!noFullSync()) await this.copySourceFiles(sourceIndex);
|
|
537
502
|
}, state.stopped);
|
|
538
|
-
// Change polls only show what the source HAS, never what it's missing, so pushes run on
|
|
539
|
-
// the full-rescan cadence (findInfo on an index-backed source is cheap)
|
|
503
|
+
// Change polls only show what the source HAS, never what it's missing, so pushes run on the full-rescan cadence (findInfo on an index-backed source is cheap)
|
|
540
504
|
runInfinitePoll(FULL_RESCAN_INTERVAL, async () => {
|
|
541
505
|
let files = await source.findInfo("");
|
|
542
506
|
await this.reconcileSource(sourceIndex, new Map(files.map(x => [x.path, x.createTime])));
|
|
@@ -550,8 +514,7 @@ export class BlobStore implements IBucketStore {
|
|
|
550
514
|
}
|
|
551
515
|
}
|
|
552
516
|
|
|
553
|
-
// Full metadata scan (size, writeTime, path) of one source, applied to the index. Returns the
|
|
554
|
-
// source's listing (path -> write time), which reconcileSource uses for the push direction.
|
|
517
|
+
// Full metadata scan (size, writeTime, path) of one source, applied to the index. Returns the source's listing (path -> write time), which reconcileSource uses for the push direction.
|
|
555
518
|
private async scanSource(sourceIndex: number): Promise<Map<string, number>> {
|
|
556
519
|
let { source, validWindow, route } = this.sources[sourceIndex];
|
|
557
520
|
let state = this.sourceStates[sourceIndex];
|
|
@@ -584,10 +547,7 @@ export class BlobStore implements IBucketStore {
|
|
|
584
547
|
tally[this.applyScanned(sourceIndex, file)]++;
|
|
585
548
|
}
|
|
586
549
|
state.scannedCount = files.length;
|
|
587
|
-
// Index entries this source was the holder of, but that vanished from it (e.g. deleted
|
|
588
|
-
// while we were offline), come out of the index. Entries changed after the scan started
|
|
589
|
-
// are kept — the scan listing may simply predate them. Tombstones have no physical file
|
|
590
|
-
// for a listing to vouch for, so they're exempt (cleanupTombstones expires them instead).
|
|
550
|
+
// Index entries this source was the holder of, but that vanished from it (e.g. deleted while we were offline), come out of the index. Entries changed after the scan started are kept — the scan listing may simply predate them. Tombstones have no physical file for a listing to vouch for, so they're exempt (cleanupTombstones expires them instead).
|
|
591
551
|
let removedFromIndex = 0;
|
|
592
552
|
let missingOnSource = 0;
|
|
593
553
|
for (let [key, entry] of this.mem) {
|
|
@@ -597,15 +557,13 @@ export class BlobStore implements IBucketStore {
|
|
|
597
557
|
removedFromIndex++;
|
|
598
558
|
continue;
|
|
599
559
|
}
|
|
600
|
-
// Counted only when the source SHOULD hold the entry (its window/route match) - these
|
|
601
|
-
// are what the reconcile pass pushes to it
|
|
560
|
+
// Counted only when the source SHOULD hold the entry (its window/route match) - these are what the reconcile pass pushes to it
|
|
602
561
|
if (entry.size === 0 || key === ROUTING_FILE) continue;
|
|
603
562
|
if (entry.writeTime < validWindow[0] || entry.writeTime > validWindow[1]) continue;
|
|
604
563
|
if (!routeContains(route, getRoute(key))) continue;
|
|
605
564
|
missingOnSource++;
|
|
606
565
|
}
|
|
607
|
-
// Percentages are of the union of both sides (our index + their listing), so every count
|
|
608
|
-
// has a stable denominator
|
|
566
|
+
// Percentages are of the union of both sides (our index + their listing), so every count has a stable denominator
|
|
609
567
|
let union = indexSizeBefore + newPaths;
|
|
610
568
|
let pct = (n: number) => `${Math.round(n / Math.max(union, 1) * 1000) / 10}%`;
|
|
611
569
|
console.log(`Metadata scan of ${source.getDebugName()} finished in ${Math.round((Date.now() - scanStart) / 1000)}s (store ${this.folder}): ${files.length} listed vs ${indexSizeBefore} indexed (union ${union}): ${formatScanTally(tally, union)}, ${missingOnSource} in index but missing on source (${pct(missingOnSource)}), ${removedFromIndex} removed from index (${pct(removedFromIndex)})`);
|
|
@@ -613,10 +571,7 @@ export class BlobStore implements IBucketStore {
|
|
|
613
571
|
return seen;
|
|
614
572
|
}
|
|
615
573
|
|
|
616
|
-
// The push direction of synchronization: everything we know that the source is missing (or
|
|
617
|
-
// holds an older copy of) is written to it — including deletions, as tombstone writes. This is
|
|
618
|
-
// what heals a source whose background writes failed (e.g. it was down): the next scan sees
|
|
619
|
-
// what's missing and re-sends it.
|
|
574
|
+
// The push direction of synchronization: everything we know that the source is missing (or holds an older copy of) is written to it — including deletions, as tombstone writes. This is what heals a source whose background writes failed (e.g. it was down): the next scan sees what's missing and re-sends it.
|
|
620
575
|
private async reconcileSource(sourceIndex: number, listing: Map<string, number>): Promise<void> {
|
|
621
576
|
let { source, validWindow, route } = this.sources[sourceIndex];
|
|
622
577
|
let state = this.sourceStates[sourceIndex];
|
|
@@ -625,8 +580,7 @@ export class BlobStore implements IBucketStore {
|
|
|
625
580
|
for (let [key, entry] of this.mem) {
|
|
626
581
|
if (this.stopped.stop || state.stopped.stop) return;
|
|
627
582
|
if (entry.source === sourceIndex) continue;
|
|
628
|
-
// The routing file is NEVER synchronized between storage nodes - it is only ever
|
|
629
|
-
// written directly to each node, and only ever read off our own disk
|
|
583
|
+
// The routing file is NEVER synchronized between storage nodes - it is only ever written directly to each node, and only ever read off our own disk
|
|
630
584
|
if (key === ROUTING_FILE) continue;
|
|
631
585
|
if (!acceptsWrites) continue;
|
|
632
586
|
if (!routeContains(route, getRoute(key))) continue;
|
|
@@ -652,11 +606,7 @@ export class BlobStore implements IBucketStore {
|
|
|
652
606
|
// An in-flight scan can outlive its source's removal; its results are dead
|
|
653
607
|
if (!this.isLive(sourceIndex)) return "filtered";
|
|
654
608
|
if (file.path === ROUTING_FILE) {
|
|
655
|
-
// The routing config is NEVER pulled from other sources - it only ever arrives as an
|
|
656
|
-
// explicit, version-validated write, and is only ever read off our own disk. Route and
|
|
657
|
-
// valid-window filters can't possibly apply to it either: it is the file DEFINING
|
|
658
|
-
// them, so filtering it would mean certain sources could never have their routing
|
|
659
|
-
// config updated, ever.
|
|
609
|
+
// The routing config is NEVER pulled from other sources - it only ever arrives as an explicit, version-validated write, and is only ever read off our own disk. Route and valid-window filters can't possibly apply to it either: it is the file DEFINING them, so filtering it would mean certain sources could never have their routing config updated, ever.
|
|
660
610
|
if (sourceIndex !== 0) return "filtered";
|
|
661
611
|
} else {
|
|
662
612
|
let { validWindow, route } = this.sources[sourceIndex];
|
|
@@ -692,9 +642,7 @@ export class BlobStore implements IBucketStore {
|
|
|
692
642
|
state.changesAfterTime = pollStart - CHANGES_POLL_OVERLAP;
|
|
693
643
|
}
|
|
694
644
|
|
|
695
|
-
// Downloads the files a source currently holds onto our own base source (the local disk),
|
|
696
|
-
// preserving their modified times — so a newer local write always wins. Skipped for noFullSync
|
|
697
|
-
// sources (fronting a large database without copying it); reads still down-cache lazily.
|
|
645
|
+
// Downloads the files a source currently holds onto our own base source (the local disk), preserving their modified times — so a newer local write always wins. Skipped for noFullSync sources (fronting a large database without copying it); reads still down-cache lazily.
|
|
698
646
|
private async copySourceFiles(sourceIndex: number): Promise<void> {
|
|
699
647
|
if (sourceIndex === 0) return;
|
|
700
648
|
let { source } = this.sources[sourceIndex];
|
|
@@ -779,16 +727,12 @@ export class BlobStore implements IBucketStore {
|
|
|
779
727
|
}
|
|
780
728
|
}
|
|
781
729
|
|
|
782
|
-
// findInfo and getChangesAfter list from the index, so they must wait for our own base
|
|
783
|
-
// source's initial scan (which might lag minutes) before the listing is trustworthy. The base
|
|
784
|
-
// (local disk) is implicitly required - remote sources are not, they come and go.
|
|
730
|
+
// findInfo and getChangesAfter list from the index, so they must wait for our own base source's initial scan (which might lag minutes) before the listing is trustworthy. The base (local disk) is implicitly required - remote sources are not, they come and go.
|
|
785
731
|
private async waitForRequiredScans(): Promise<void> {
|
|
786
732
|
await this.sourceStates[0].initialScan.promise;
|
|
787
733
|
}
|
|
788
734
|
|
|
789
|
-
// A requested file isn't in the index: our own base source (implicitly required) is checked
|
|
790
|
-
// directly if its initial scan hasn't finished, and changes-after sources are re-polled (at
|
|
791
|
-
// most every 5 seconds)
|
|
735
|
+
// A requested file isn't in the index: our own base source (implicitly required) is checked directly if its initial scan hasn't finished, and changes-after sources are re-polled (at most every 5 seconds)
|
|
792
736
|
private async checkMissingKey(key: string): Promise<void> {
|
|
793
737
|
for (let i = 0; i < this.sources.length; i++) {
|
|
794
738
|
if (!this.isLive(i)) continue;
|
|
@@ -859,14 +803,12 @@ export class BlobStore implements IBucketStore {
|
|
|
859
803
|
}
|
|
860
804
|
return result;
|
|
861
805
|
}
|
|
862
|
-
// The routing file is only ever read off our own disk - falling back to another source's
|
|
863
|
-
// copy would synchronize it between nodes through the read path, which it never is
|
|
806
|
+
// The routing file is only ever read off our own disk - falling back to another source's copy would synchronize it between nodes through the read path, which it never is
|
|
864
807
|
if (key === ROUTING_FILE) {
|
|
865
808
|
if (holderError) throw holderError;
|
|
866
809
|
return undefined;
|
|
867
810
|
}
|
|
868
|
-
// The holder is down or lost the file. ANY other source's copy beats no value - even an
|
|
869
|
-
// OLDER one - and it's copied onto our disk so the next read doesn't depend on luck.
|
|
811
|
+
// The holder is down or lost the file. ANY other source's copy beats no value - even an OLDER one - and it's copied onto our disk so the next read doesn't depend on luck.
|
|
870
812
|
for (let i = 0; i < this.sources.length; i++) {
|
|
871
813
|
if (i === holder || !this.isLive(i)) continue;
|
|
872
814
|
let fallback: { data: Buffer; writeTime: number; size: number } | undefined;
|
|
@@ -889,8 +831,7 @@ export class BlobStore implements IBucketStore {
|
|
|
889
831
|
return undefined;
|
|
890
832
|
}
|
|
891
833
|
|
|
892
|
-
// The read's bytes came from a remote source, so write them onto our own base source (the
|
|
893
|
-
// local disk), which becomes the entry's new holder - reads only pay the remote fetch once
|
|
834
|
+
// The read's bytes came from a remote source, so write them onto our own base source (the local disk), which becomes the entry's new holder - reads only pay the remote fetch once
|
|
894
835
|
private async cacheRead(key: string, result: { data: Buffer; writeTime: number }): Promise<void> {
|
|
895
836
|
await this.sources[0].source.set(key, result.data, { lastModified: result.writeTime });
|
|
896
837
|
this.setIndexEntry(key, { writeTime: result.writeTime, size: result.data.length, source: 0 });
|
|
@@ -898,6 +839,7 @@ export class BlobStore implements IBucketStore {
|
|
|
898
839
|
|
|
899
840
|
public async set(key: string, data: Buffer, config?: WriteConfig): Promise<string> {
|
|
900
841
|
await this.init();
|
|
842
|
+
this.config?.onWriteCounted?.("original", data.length);
|
|
901
843
|
let lastModified = config?.lastModified;
|
|
902
844
|
if (lastModified) {
|
|
903
845
|
assertValidLastModified(lastModified);
|
|
@@ -909,16 +851,12 @@ export class BlobStore implements IBucketStore {
|
|
|
909
851
|
}
|
|
910
852
|
let writeTime = lastModified || Date.now();
|
|
911
853
|
if (config?.fast) {
|
|
912
|
-
// A writeDelay of zero is a real choice (no delay at all), so only an omitted delay
|
|
913
|
-
// gets the default
|
|
854
|
+
// A writeDelay of zero is a real choice (no delay at all), so only an omitted delay gets the default
|
|
914
855
|
let writeDelay = config.writeDelay;
|
|
915
856
|
if (writeDelay === undefined) {
|
|
916
857
|
writeDelay = DEFAULT_FAST_WRITE_DELAY;
|
|
917
858
|
}
|
|
918
|
-
// The delay never extends past our own valid window's end (minus the margin, so the
|
|
919
|
-
// writes are on disk before the next window's source takes over - a deploy switchover
|
|
920
|
-
// is just this too, since its remap ends our window). Past that point fast writes
|
|
921
|
-
// write through immediately.
|
|
859
|
+
// The delay never extends past our own valid window's end (minus the margin, so the writes are on disk before the next window's source takes over - a deploy switchover is just this too, since its remap ends our window). Past that point fast writes write through immediately.
|
|
922
860
|
let deadline = this.sources[0].validWindow[1] - WINDOW_END_FLUSH_MARGIN;
|
|
923
861
|
if (writeDelay > 0 && Date.now() < deadline) {
|
|
924
862
|
let flushAt = Math.min(Date.now() + writeDelay, deadline);
|
|
@@ -935,9 +873,7 @@ export class BlobStore implements IBucketStore {
|
|
|
935
873
|
}
|
|
936
874
|
|
|
937
875
|
public async del(key: string, config?: WriteConfig): Promise<void> {
|
|
938
|
-
// Deletes are tombstone writes (an empty file IS a missing file): the size-0 index entry is
|
|
939
|
-
// ordered by write time like any other write, propagates through synchronization, and
|
|
940
|
-
// expires after TOMBSTONE_EXPIRY
|
|
876
|
+
// Deletes are tombstone writes (an empty file IS a missing file): the size-0 index entry is ordered by write time like any other write, propagates through synchronization, and expires after TOMBSTONE_EXPIRY
|
|
941
877
|
await this.set(key, Buffer.alloc(0), config);
|
|
942
878
|
}
|
|
943
879
|
|
|
@@ -952,19 +888,15 @@ export class BlobStore implements IBucketStore {
|
|
|
952
888
|
}
|
|
953
889
|
|
|
954
890
|
private async writeToSources(key: string, data: Buffer, writeTime: number): Promise<void> {
|
|
955
|
-
// The routing file is NEVER synchronized between storage nodes: the writer writes it
|
|
956
|
-
|
|
957
|
-
// routing/valid windows can't possibly apply to the file defining them) and never forward
|
|
958
|
-
// it to other sources.
|
|
891
|
+
// The routing file is NEVER synchronized between storage nodes: the writer writes it directly to each node, so we store it on our own disk only (no valid-window filter - routing/valid windows can't possibly apply to the file defining them) and never forward it to other sources.
|
|
892
|
+
this.config?.onWriteCounted?.("flushed", data.length);
|
|
959
893
|
let isRouting = key === ROUTING_FILE;
|
|
960
894
|
let writable = this.getWritableSources({ ignoreWindow: isRouting });
|
|
961
895
|
let first = writable.shift();
|
|
962
896
|
if (first === undefined) {
|
|
963
897
|
throw new Error(`No source accepts writes (every source's valid window is in the past), so writes cannot be stored (store ${this.folder})`);
|
|
964
898
|
}
|
|
965
|
-
// Only our own (first) source blocks the write. Downstream sources are written in the
|
|
966
|
-
// background: a down downstream source must not fail or stall writes, and reconcileSource
|
|
967
|
-
// re-sends anything they missed once they come back.
|
|
899
|
+
// Only our own (first) source blocks the write. Downstream sources are written in the background: a down downstream source must not fail or stall writes, and reconcileSource re-sends anything they missed once they come back.
|
|
968
900
|
if (data.length === 0) {
|
|
969
901
|
// A tombstone stores nothing on our own source - the index entry alone records it
|
|
970
902
|
await this.sources[first].source.del(key);
|
|
@@ -976,8 +908,7 @@ export class BlobStore implements IBucketStore {
|
|
|
976
908
|
let route = getRoute(key);
|
|
977
909
|
for (let i of writable) {
|
|
978
910
|
if (!routeContains(this.sources[i].route, route)) continue;
|
|
979
|
-
// Downstream sources receive tombstones as actual empty writes, so their listings show
|
|
980
|
-
// the deletion (size 0) and other stores scan it in as a tombstone
|
|
911
|
+
// Downstream sources receive tombstones as actual empty writes, so their listings show the deletion (size 0) and other stores scan it in as a tombstone
|
|
981
912
|
void this.sources[i].source.set(key, data, { lastModified: writeTime }).catch((e: Error) => {
|
|
982
913
|
console.error(`Background write of ${key} to sync source ${this.sources[i].source.getDebugName()} failed: ${e.stack ?? e}`);
|
|
983
914
|
});
|
|
@@ -1018,10 +949,7 @@ export class BlobStore implements IBucketStore {
|
|
|
1018
949
|
return files;
|
|
1019
950
|
}
|
|
1020
951
|
|
|
1021
|
-
// All files changed after the given time — fast, straight from the in-memory index. Filters on
|
|
1022
|
-
// when WE learned of the change (changedAt), so files synchronized late (with old write times)
|
|
1023
|
-
// are still reported. Deletions ARE reported, as size-0 tombstone entries — that's how they
|
|
1024
|
-
// propagate to stores syncing from us.
|
|
952
|
+
// All files changed after the given time — fast, straight from the in-memory index. Filters on when WE learned of the change (changedAt), so files synchronized late (with old write times) are still reported. Deletions ARE reported, as size-0 tombstone entries — that's how they propagate to stores syncing from us.
|
|
1025
953
|
public async getChangesAfter(time: number): Promise<ArchiveFileInfo[]> {
|
|
1026
954
|
await this.init();
|
|
1027
955
|
await this.waitForRequiredScans();
|
|
@@ -1098,10 +1026,7 @@ export class BlobStore implements IBucketStore {
|
|
|
1098
1026
|
}
|
|
1099
1027
|
}
|
|
1100
1028
|
|
|
1101
|
-
// readerDiskLimit: the disk is only a bounded read cache, so once it exceeds the limit, the
|
|
1102
|
-
// least recently used files are deleted from it - but ONLY when another source verifiably
|
|
1103
|
-
// holds a same-or-newer copy (the only copy of a file is never deleted), and the index entry
|
|
1104
|
-
// repoints to that source so reads keep working (re-caching on the next read).
|
|
1029
|
+
// readerDiskLimit: the disk is only a bounded read cache, so once it exceeds the limit, the least recently used files are deleted from it - but ONLY when another source verifiably holds a same-or-newer copy (the only copy of a file is never deleted), and the index entry repoints to that source so reads keep working (re-caching on the next read).
|
|
1105
1030
|
private evicting = false;
|
|
1106
1031
|
private async enforceDiskLimit(): Promise<void> {
|
|
1107
1032
|
let limit = this.config?.readerDiskLimit;
|
|
@@ -1148,10 +1073,7 @@ export class BlobStore implements IBucketStore {
|
|
|
1148
1073
|
}
|
|
1149
1074
|
}
|
|
1150
1075
|
|
|
1151
|
-
// Tombstones only need to exist long enough for every store to learn of the deletion; expired
|
|
1152
|
-
// ones come out of the index. The physical empty file is removed only on backblaze sources:
|
|
1153
|
-
// remote stores expire their own tombstones (a del there would just mint a fresh one), and our
|
|
1154
|
-
// own disk never stored anything for it.
|
|
1076
|
+
// Tombstones only need to exist long enough for every store to learn of the deletion; expired ones come out of the index. The physical empty file is removed only on backblaze sources: remote stores expire their own tombstones (a del there would just mint a fresh one), and our own disk never stored anything for it.
|
|
1155
1077
|
private async cleanupTombstones(): Promise<void> {
|
|
1156
1078
|
let cutoff = Date.now() - TOMBSTONE_EXPIRY;
|
|
1157
1079
|
for (let [key, entry] of this.mem) {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import { IArchives, RemoteConfig, RemoteConfigBase, HostedConfig, BackblazeConfig, ArchiveFileInfo, ArchivesConfig, ArchivesSyncStatus } from "../IArchives";
|
|
4
4
|
import { ServerBucketInfo } from "./storageServerState";
|
|
5
|
+
/** The address, port, account, and bucket name a bucket routing URL addresses. Throws when the URL isn't a hosted bucket routing URL (https://host:port/file/<account>/<bucketName>/storage/storagerouting.json). */
|
|
6
|
+
export { parseHostedUrl, parseBackblazeUrl, getBucketBaseUrl } from "./remoteConfig";
|
|
5
7
|
export declare function createApiArchives(source: HostedConfig | BackblazeConfig): IArchives;
|
|
6
8
|
export declare class ArchivesChain implements IArchives {
|
|
7
9
|
private configured;
|
|
@@ -71,12 +73,7 @@ export declare class ArchivesChain implements IArchives {
|
|
|
71
73
|
getChangesAfter(time: number): Promise<ArchiveFileInfo[]>;
|
|
72
74
|
getSyncStatus(): Promise<ArchivesSyncStatus>;
|
|
73
75
|
getConfig(): Promise<ArchivesConfig>;
|
|
74
|
-
/** True only when EVERY write-receiving source would accept our writes (partial write access
|
|
75
|
-
* desynchronizes sources, so it counts as no access). */
|
|
76
76
|
hasWriteAccess(): Promise<boolean>;
|
|
77
|
-
/** Returns the full key written. Plain keys come back unchanged; keys containing VARIABLE_SHARD
|
|
78
|
-
* are automatically materialized (a shard value is picked and embedded, see setVariableShard)
|
|
79
|
-
* and the caller needs the returned key to ever read the value back. */
|
|
80
77
|
set(fileName: string, data: Buffer, config?: {
|
|
81
78
|
lastModified?: number;
|
|
82
79
|
}): Promise<string>;
|
|
@@ -91,18 +88,17 @@ export declare class ArchivesChain implements IArchives {
|
|
|
91
88
|
dispose(): void;
|
|
92
89
|
}
|
|
93
90
|
export declare function createArchives(config: RemoteConfig | RemoteConfigBase): ArchivesChain;
|
|
94
|
-
/** Every bucket an account has on one storage server - active and inactive - with each bucket's
|
|
95
|
-
* configuration. One authenticated call (the normal trust system applies): no ArchivesChain, no
|
|
96
|
-
* synchronization, and inactive buckets on the server stay inactive. Any URL addressing the
|
|
97
|
-
* server works (a bucket routing URL, or just https://host:port). */
|
|
98
91
|
export declare function listServerBuckets(config: {
|
|
99
92
|
url: string;
|
|
100
93
|
account: string;
|
|
101
94
|
}): Promise<ServerBucketInfo[]>;
|
|
102
|
-
/**
|
|
103
|
-
|
|
104
|
-
|
|
95
|
+
/** Zeroes the write statistics listServerBuckets reports, for every bucket in the account. */
|
|
96
|
+
export declare function clearServerWriteStats(config: {
|
|
97
|
+
url: string;
|
|
98
|
+
account: string;
|
|
99
|
+
}): Promise<{
|
|
100
|
+
clearedBuckets: number;
|
|
101
|
+
}>;
|
|
105
102
|
export declare function getBucketInfo(config: {
|
|
106
103
|
url: string;
|
|
107
|
-
accountName?: string;
|
|
108
104
|
}): Promise<ArchivesConfig>;
|