@beam-network/sdk 0.6.1 → 0.7.0
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/README.md +21 -0
- package/dist/client.js +57 -19
- package/dist/models.d.ts +34 -2
- package/dist/nats-control.d.ts +2 -0
- package/dist/nats-control.js +14 -1
- package/dist/performance.d.ts +30 -3
- package/dist/performance.js +104 -5
- package/dist/provider-signing.js +15 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -195,3 +195,24 @@ Multipart control helpers (`createMultipartUpload`, `listMultipartParts`, `compl
|
|
|
195
195
|
Route streaming flushes at 1,024 routes, with no timer, and sends the final partial batch at stream completion. Signing overlaps acknowledged publication through bounded buffering; existing encoded-payload limits still apply. A source grant is reused across destinations only within the same signing generation and actual expiry.
|
|
196
196
|
|
|
197
197
|
The optional `onDiagnostics` callback receives bounded preparation measurements and source-grant reuse counts. Callbacks are best effort, may be dropped under load, and do not affect transfer outcomes. Durations may overlap; do not sum them into elapsed transfer time. Transfer status also exposes a typed `performance` summary when supported by Core.
|
|
198
|
+
|
|
199
|
+
Optional `storage_location` (`StorageLocation` in Go) describes the physical
|
|
200
|
+
storage location. It is separate from the region used to sign provider requests.
|
|
201
|
+
Leave it unset when unknown; a signing region such as R2's `auto` is not a location.
|
|
202
|
+
|
|
203
|
+
When supported by Core, diagnostics use `sdk-performance/v2`: bounded histograms,
|
|
204
|
+
preparation milestones, concurrency high-water marks, and separate provider,
|
|
205
|
+
callback, manifest, signing, and transport waits. Histogram bins are noncumulative,
|
|
206
|
+
with upper bounds in milliseconds of 0.1, 0.5, 1, 2, 5, 10, 25, 50, 100, 250,
|
|
207
|
+
500, 1000, 2500, 5000, 10000, 30000, 120000, then overflow. `unmeasured` explicitly
|
|
208
|
+
identifies unavailable measurements. Process CPU includes other concurrent work
|
|
209
|
+
in the SDK process; it is not transfer-exclusive CPU. Detailed reporting can be
|
|
210
|
+
disabled with `BEAM_SDK_PERFORMANCE_DETAILS=false`. Older peers retain v1 reports.
|
|
211
|
+
No credentials or storage grants are included in these diagnostics.
|
|
212
|
+
|
|
213
|
+
`sdk.producer_wait` measures waiting for a signed route separately from publication
|
|
214
|
+
backpressure. Configured-limit gauges preserve starting limits; effective-limit
|
|
215
|
+
gauges report the highest limit reached. Optional `source_renewals` counts source
|
|
216
|
+
grants recreated in later preparation generations. A bounded 16 KiB history tracks
|
|
217
|
+
131,072 chunk indices; the counter is omitted after takeover or beyond that bound.
|
|
218
|
+
It does not include separate recovery-control signing operations.
|
package/dist/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SdkPerformanceCollector } from "./performance.js";
|
|
1
|
+
import { SdkPerformanceCollector, SourceSignatureHistory, currentSdkPerformance, measureSdkPhase } from "./performance.js";
|
|
2
2
|
import { signMultipartRecovery, abortMultipartUpload, createMultipartUpload, expiresAtIso, prepareProviderDestination, prepareProviderSource, releaseProviderClients, signAbortMultipartUpload, signCompleteMultipartUpload, signDestinationReadRange, signDestinationRoute, signFinalObjectHead, signListMultipartUpload, signSourceReadRange, signSourceChunk, boundedGrantExpiry, isHuggingFaceProvider } from "./provider-signing.js";
|
|
3
3
|
import { describe as describeHuggingFace, hashSourceStream, huggingFaceCommit, huggingFaceCompleteLfsUpload, huggingFaceLfsBatch, huggingFacePreupload, huggingFaceVerifyLfsUpload, readSourceSample } from "./huggingface.js";
|
|
4
4
|
import { BeamTransferControl, BEAM_DEFAULT_NATS_URL, compactSignedRoutes, isRecoverableRouteStreamError } from "./nats-control.js";
|
|
@@ -300,10 +300,9 @@ export class BeamClient {
|
|
|
300
300
|
destinationsById.set(preparedDestination.destination_id, destination);
|
|
301
301
|
});
|
|
302
302
|
await input.throwIfCancelled?.();
|
|
303
|
-
const
|
|
304
|
-
const preparedSources = await Promise.all(retainedRecoveryInput.sources.map((source, index) => prepareProviderSource(source, { index, expiresIn, fetchImpl: this.fetchImpl, signal: input.signal })));
|
|
303
|
+
const discoveryTelemetry = new SdkPerformanceCollector();
|
|
304
|
+
const preparedSources = await discoveryTelemetry.measure("sdk.discovery", () => Promise.all(retainedRecoveryInput.sources.map((source, index) => prepareProviderSource(source, { index, expiresIn, fetchImpl: this.fetchImpl, signal: input.signal }))));
|
|
305
305
|
await input.throwIfCancelled?.();
|
|
306
|
-
const discoveryMs = performance.now() - discoveryStarted;
|
|
307
306
|
const huggingFace = await this.planHuggingFaceUploads({
|
|
308
307
|
sources: retainedRecoveryInput.sources,
|
|
309
308
|
preparedSources,
|
|
@@ -353,13 +352,18 @@ export class BeamClient {
|
|
|
353
352
|
const assertOwnership = () => input.signal?.throwIfAborted();
|
|
354
353
|
const routeStreamLock = new AsyncMutex();
|
|
355
354
|
const releaseInitialStream = await routeStreamLock.acquire();
|
|
355
|
+
const sourceSignatureHistory = new SourceSignatureHistory(!resume);
|
|
356
356
|
const streamPreparedRoutes = async (routeGenerationId, recoveryReplay) => {
|
|
357
|
-
const telemetry = new SdkPerformanceCollector();
|
|
358
|
-
|
|
359
|
-
|
|
357
|
+
const telemetry = new SdkPerformanceCollector(sourceSignatureHistory);
|
|
358
|
+
telemetry.seedDiscovery(discoveryTelemetry);
|
|
359
|
+
telemetry.gauge("signing_configured_limit", this.routeSigningConcurrency);
|
|
360
|
+
telemetry.gauge("multipart_configured_limit", this.multipartControlConcurrency);
|
|
361
|
+
telemetry.gauge("signing_limit", this.routeSigningConcurrency);
|
|
362
|
+
telemetry.gauge("multipart_limit", this.multipartControlConcurrency);
|
|
360
363
|
const pendingRoutes = new Set();
|
|
361
364
|
let signingConcurrency = this.routeSigningConcurrency;
|
|
362
365
|
let signedInWindow = 0;
|
|
366
|
+
let activeSigning = 0;
|
|
363
367
|
let signingWindowStartedAt = performance.now();
|
|
364
368
|
let routeStream = null;
|
|
365
369
|
let routeStreamBeginAttempted = false;
|
|
@@ -420,14 +424,14 @@ export class BeamClient {
|
|
|
420
424
|
assertOwnership();
|
|
421
425
|
recoveryMultipartUploads.set(state.manifest.multipart_group_id, state);
|
|
422
426
|
validateMultipartGroupManifest([state.manifest], prepared.transfer_id);
|
|
423
|
-
await input.onMultipartGroupReady?.(multipartGroupIdentity(prepared.transfer_id, state.manifest));
|
|
427
|
+
await telemetry.measure("sdk.multipart_callback", async () => input.onMultipartGroupReady?.(multipartGroupIdentity(prepared.transfer_id, state.manifest)));
|
|
424
428
|
await routeStream.addManifestGroups([state.manifest]);
|
|
425
429
|
multipartGroupWaiters.get(state.manifest.multipart_group_id)?.resolve(state);
|
|
426
430
|
},
|
|
427
431
|
onGroupFailed: (groupId, error) => multipartGroupWaiters.get(groupId)?.reject(error)
|
|
428
432
|
}));
|
|
429
433
|
const streamNextCompletedRoute = async () => {
|
|
430
|
-
const settled = await Promise.race([...pendingRoutes].map((pending) => pending.then((route) => ({ pending, route }))));
|
|
434
|
+
const settled = await telemetry.measure("sdk.producer_wait", () => Promise.race([...pendingRoutes].map((pending) => pending.then((route) => ({ pending, route })))));
|
|
431
435
|
pendingRoutes.delete(settled.pending);
|
|
432
436
|
assertOwnership();
|
|
433
437
|
await routeStream.addRoute(settled.route);
|
|
@@ -436,6 +440,8 @@ export class BeamClient {
|
|
|
436
440
|
const elapsedMs = performance.now() - signingWindowStartedAt;
|
|
437
441
|
if (!this.routeSigningConcurrencyOverridden && elapsedMs > 4_000 && signingConcurrency < 256) {
|
|
438
442
|
signingConcurrency = Math.min(256, signingConcurrency * 2);
|
|
443
|
+
telemetry.increment("concurrency_changes");
|
|
444
|
+
telemetry.gauge("signing_limit", signingConcurrency);
|
|
439
445
|
}
|
|
440
446
|
signedInWindow = 0;
|
|
441
447
|
signingWindowStartedAt = performance.now();
|
|
@@ -445,10 +451,9 @@ export class BeamClient {
|
|
|
445
451
|
await throwIfCancelled?.(prepared.transfer_id);
|
|
446
452
|
// This promise belongs to this chunk and signing generation only. Its
|
|
447
453
|
// immutable credential/range identity and actual expiry cannot be renewed.
|
|
448
|
-
|
|
454
|
+
let sourceUses = 0;
|
|
455
|
+
const sourceGrant = telemetry.measure("sdk.source_signing", () => signSourceChunk({ source: sourcesById.get(chunk.source_id), fallbackUrl: chunk.source_url, chunk, expiresIn, fetchImpl: this.fetchImpl })).then(grant => { telemetry.sourceCreated(chunk.chunk_index); return grant; });
|
|
449
456
|
void sourceGrant.catch(() => { });
|
|
450
|
-
telemetry.counters.source_signatures++;
|
|
451
|
-
telemetry.counters.source_reuses += Math.max(0, chunk.destinations.length - 1);
|
|
452
457
|
for (const target of chunk.destinations) {
|
|
453
458
|
const pendingRoute = (async () => {
|
|
454
459
|
await throwIfCancelled?.(prepared.transfer_id);
|
|
@@ -465,14 +470,16 @@ export class BeamClient {
|
|
|
465
470
|
const multipartGroupId = multipartGroupStateKey(prepared.transfer_id, target.destination_id, chunk.source_id, finalObjectKey);
|
|
466
471
|
const upload = isDirectPutDestination(destination)
|
|
467
472
|
? undefined
|
|
468
|
-
: await multipartGroupWaiters.get(multipartGroupId)?.promise;
|
|
473
|
+
: await telemetry.measure("sdk.multipart_ready_wait", async () => multipartGroupWaiters.get(multipartGroupId)?.promise);
|
|
469
474
|
if (!isDirectPutDestination(destination) && !upload)
|
|
470
475
|
throw new Error(`multipart group manifest is missing for ${chunk.source_id}:${target.destination_id}`);
|
|
476
|
+
telemetry.gauge("signing_active_peak", ++activeSigning);
|
|
471
477
|
return telemetry.measure("sdk.signing", () => this.signProviderRoute({
|
|
472
478
|
chunk,
|
|
473
479
|
target,
|
|
474
480
|
source,
|
|
475
|
-
sourceGrant,
|
|
481
|
+
sourceGrant: telemetry.measure("sdk.source_grant_wait", () => sourceGrant).then(grant => { if (sourceUses++ > 0)
|
|
482
|
+
telemetry.counters.source_reuses++; return grant; }),
|
|
476
483
|
destination,
|
|
477
484
|
expiresIn,
|
|
478
485
|
...(upload ? { upload } : {}),
|
|
@@ -483,11 +490,12 @@ export class BeamClient {
|
|
|
483
490
|
partNumber: typeof metadata.part_number === "number"
|
|
484
491
|
? metadata.part_number
|
|
485
492
|
: multipartPartNumber(chunk.source_chunk_index),
|
|
486
|
-
}));
|
|
493
|
+
})).finally(() => { activeSigning--; });
|
|
487
494
|
})();
|
|
488
495
|
pendingRoutes.add(pendingRoute);
|
|
496
|
+
telemetry.gauge("signing_pending_peak", pendingRoutes.size);
|
|
489
497
|
if (pendingRoutes.size >= signingConcurrency)
|
|
490
|
-
await streamNextCompletedRoute
|
|
498
|
+
await telemetry.measure("sdk.signing_queue", streamNextCompletedRoute);
|
|
491
499
|
}
|
|
492
500
|
}
|
|
493
501
|
while (pendingRoutes.size)
|
|
@@ -1252,6 +1260,7 @@ class RouteStreamSender {
|
|
|
1252
1260
|
routeCount = 0;
|
|
1253
1261
|
sendTail = Promise.resolve();
|
|
1254
1262
|
sendError = null;
|
|
1263
|
+
sending = false;
|
|
1255
1264
|
constructor(control, options) {
|
|
1256
1265
|
this.control = control;
|
|
1257
1266
|
this.options = options;
|
|
@@ -1259,6 +1268,7 @@ class RouteStreamSender {
|
|
|
1259
1268
|
this.telemetry = options.telemetry ?? new SdkPerformanceCollector();
|
|
1260
1269
|
}
|
|
1261
1270
|
async begin() {
|
|
1271
|
+
this.telemetry.startDelaySampling();
|
|
1262
1272
|
await this.control.request("transfer.route_stream.begin", compact({
|
|
1263
1273
|
transfer_id: this.options.transferId,
|
|
1264
1274
|
route_generation_id: this.options.routeGenerationId,
|
|
@@ -1275,6 +1285,7 @@ class RouteStreamSender {
|
|
|
1275
1285
|
});
|
|
1276
1286
|
}
|
|
1277
1287
|
async addManifestGroups(groups) {
|
|
1288
|
+
const started = performance.now();
|
|
1278
1289
|
if (!groups.length)
|
|
1279
1290
|
return;
|
|
1280
1291
|
await Promise.all(groups.map(async (group) => {
|
|
@@ -1290,8 +1301,12 @@ class RouteStreamSender {
|
|
|
1290
1301
|
idempotencyKey: `transfer:${this.options.transferId}:route-stream:${this.streamId}:manifest:${identity}`
|
|
1291
1302
|
});
|
|
1292
1303
|
}));
|
|
1304
|
+
this.telemetry.observe("sdk.manifest_ack", performance.now() - started);
|
|
1305
|
+
this.telemetry.mark("first_manifest_ms");
|
|
1293
1306
|
}
|
|
1294
1307
|
async addRoute(route) {
|
|
1308
|
+
this.telemetry.mark("first_route_ms");
|
|
1309
|
+
const assemblyStarted = performance.now();
|
|
1295
1310
|
const deliveryIndex = signedRouteDeliveryIndex(route);
|
|
1296
1311
|
if (deliveryIndex === undefined) {
|
|
1297
1312
|
throw new Error("route delivery_index is required");
|
|
@@ -1305,6 +1320,8 @@ class RouteStreamSender {
|
|
|
1305
1320
|
this.seenDeliveryIndices.add(deliveryIndex);
|
|
1306
1321
|
this.routeCount += 1;
|
|
1307
1322
|
this.batch.push({ ...route, delivery_index: deliveryIndex });
|
|
1323
|
+
this.telemetry.gauge("buffered_batches_peak", 1 + Number(this.sending));
|
|
1324
|
+
this.telemetry.observe("sdk.route_assembly", performance.now() - assemblyStarted);
|
|
1308
1325
|
if (this.batch.length >= ROUTE_STREAM_BATCH_ROUTES) {
|
|
1309
1326
|
await this.enqueueFlush();
|
|
1310
1327
|
}
|
|
@@ -1314,12 +1331,14 @@ class RouteStreamSender {
|
|
|
1314
1331
|
this.seenDeliveryIndices.size !== this.options.totalRoutes) {
|
|
1315
1332
|
throw new Error(`route stream has incomplete or duplicate delivery indices: received ${this.routeCount} of ${this.options.totalRoutes} routes`);
|
|
1316
1333
|
}
|
|
1334
|
+
this.telemetry.mark("final_flush_ms");
|
|
1317
1335
|
await this.enqueueFlush();
|
|
1318
1336
|
await this.sendTail;
|
|
1319
1337
|
if (this.sendError)
|
|
1320
1338
|
throw this.sendError;
|
|
1321
1339
|
this.telemetry.observe("sdk.preparation", performance.now() - this.started);
|
|
1322
|
-
|
|
1340
|
+
this.telemetry.finish();
|
|
1341
|
+
const sdkPerformance = this.telemetry.snapshot(this.control.supportsPerformanceV2?.(this.options.transferId) ?? false);
|
|
1323
1342
|
this.options.onDiagnostics?.(sdkPerformance);
|
|
1324
1343
|
return this.control.request("transfer.route_stream.complete", {
|
|
1325
1344
|
sdk_performance: sdkPerformance,
|
|
@@ -1341,6 +1360,7 @@ class RouteStreamSender {
|
|
|
1341
1360
|
if (this.sendError)
|
|
1342
1361
|
throw this.sendError;
|
|
1343
1362
|
const routes = this.batch.splice(0, this.batch.length);
|
|
1363
|
+
const encodingStarted = performance.now();
|
|
1344
1364
|
const chunks = this.control.splitRoutesForPayload("transfer.route_stream.batch", {
|
|
1345
1365
|
transfer_id: this.options.transferId,
|
|
1346
1366
|
route_generation_id: this.options.routeGenerationId,
|
|
@@ -1348,19 +1368,27 @@ class RouteStreamSender {
|
|
|
1348
1368
|
batch_id: `${this.streamId}:estimate`,
|
|
1349
1369
|
batch_index: this.batchIndex
|
|
1350
1370
|
}, routes);
|
|
1371
|
+
this.telemetry.observe("sdk.batch_encode", performance.now() - encodingStarted);
|
|
1372
|
+
this.sending = true;
|
|
1351
1373
|
for (const chunk of chunks) {
|
|
1374
|
+
const queuedAt = performance.now();
|
|
1352
1375
|
const batchIndex = this.batchIndex;
|
|
1353
1376
|
this.batchIndex += 1;
|
|
1354
1377
|
this.sendTail = this.sendTail.then(async () => {
|
|
1378
|
+
this.telemetry.observe("sdk.batch_queue", performance.now() - queuedAt);
|
|
1355
1379
|
if (this.sendError)
|
|
1356
1380
|
return;
|
|
1357
1381
|
try {
|
|
1382
|
+
const checksumStarted = performance.now();
|
|
1358
1383
|
const chunkChecksum = new RouteKeysChecksum();
|
|
1359
1384
|
await chunkChecksum.addMany(chunk.map(routeKeyForSignedRoute));
|
|
1360
1385
|
this.checksum.merge(chunkChecksum);
|
|
1361
1386
|
const routeChecksum = chunkChecksum.value();
|
|
1362
1387
|
const coordinateChecksum = await routeCoordinateChecksum(chunk);
|
|
1363
1388
|
const batchId = `${this.streamId}:${batchIndex}:${coordinateChecksum}`;
|
|
1389
|
+
this.telemetry.observe("sdk.batch_checksum", performance.now() - checksumStarted);
|
|
1390
|
+
this.telemetry.gauge("batch_routes_max", chunk.length);
|
|
1391
|
+
this.telemetry.mark("first_batch_ms");
|
|
1364
1392
|
this.telemetry.counters.route_batches++;
|
|
1365
1393
|
await this.telemetry.measure("sdk.batch_ack", () => this.control.request("transfer.route_stream.batch", {
|
|
1366
1394
|
transfer_id: this.options.transferId,
|
|
@@ -1381,8 +1409,10 @@ class RouteStreamSender {
|
|
|
1381
1409
|
}
|
|
1382
1410
|
});
|
|
1383
1411
|
}
|
|
1412
|
+
this.sendTail = this.sendTail.finally(() => { this.sending = false; });
|
|
1384
1413
|
}
|
|
1385
1414
|
async abort() {
|
|
1415
|
+
this.telemetry.finish();
|
|
1386
1416
|
this.sendError ??= new Error("route_stream_aborted");
|
|
1387
1417
|
await this.sendTail;
|
|
1388
1418
|
}
|
|
@@ -1672,7 +1702,12 @@ function restoreProviderMultipartIdentities(prepared, destinations, identities,
|
|
|
1672
1702
|
}
|
|
1673
1703
|
async function createMultipartGroupManifest(input) {
|
|
1674
1704
|
const groups = input.prepared.plan_descriptor.sources.flatMap((source) => input.prepared.plan_descriptor.destinations.map((destination) => ({ source, destination })));
|
|
1705
|
+
const queuedAt = performance.now();
|
|
1706
|
+
const telemetry = currentSdkPerformance();
|
|
1707
|
+
let active = 0;
|
|
1675
1708
|
const results = await mapOrderedWithConcurrency(groups, input.concurrency, async ({ source, destination }) => {
|
|
1709
|
+
telemetry?.observe("sdk.multipart_queue", performance.now() - queuedAt);
|
|
1710
|
+
telemetry?.gauge("multipart_active_peak", ++active);
|
|
1676
1711
|
let multipartGroupId = null;
|
|
1677
1712
|
try {
|
|
1678
1713
|
const destinationConfig = input.destinationsById.get(destination.destination_id);
|
|
@@ -1690,12 +1725,12 @@ async function createMultipartGroupManifest(input) {
|
|
|
1690
1725
|
const finalObjectMetadata = { "beam-transfer-id": input.prepared.transfer_id };
|
|
1691
1726
|
input.signal?.throwIfAborted();
|
|
1692
1727
|
const retainedUpload = input.multipartUploads.get(multipartGroupId);
|
|
1693
|
-
const uploadId = retainedUpload?.uploadId ?? await createMultipartUpload({
|
|
1728
|
+
const uploadId = retainedUpload?.uploadId ?? await measureSdkPhase("sdk.multipart_provider", () => createMultipartUpload({
|
|
1694
1729
|
destination: destinationConfig,
|
|
1695
1730
|
objectKey: finalObjectKey,
|
|
1696
1731
|
metadata: finalObjectMetadata,
|
|
1697
1732
|
signal: input.signal
|
|
1698
|
-
});
|
|
1733
|
+
}));
|
|
1699
1734
|
const createdUpload = !retainedUpload;
|
|
1700
1735
|
if (createdUpload) {
|
|
1701
1736
|
input.multipartUploads.set(multipartGroupId, {
|
|
@@ -1758,6 +1793,9 @@ async function createMultipartGroupManifest(input) {
|
|
|
1758
1793
|
input.onGroupFailed(multipartGroupId, error);
|
|
1759
1794
|
return { ok: false, error };
|
|
1760
1795
|
}
|
|
1796
|
+
finally {
|
|
1797
|
+
active--;
|
|
1798
|
+
}
|
|
1761
1799
|
});
|
|
1762
1800
|
const failure = results.find((result) => !result.ok);
|
|
1763
1801
|
if (failure && !failure.ok) {
|
package/dist/models.d.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
export type SignedUrlFlow = "signed_url";
|
|
2
2
|
export interface SdkPerformanceSummary {
|
|
3
|
-
|
|
3
|
+
gauges?: Record<string, number>;
|
|
4
|
+
milestones?: Record<string, number>;
|
|
5
|
+
unmeasured?: string[];
|
|
6
|
+
detail_dropped?: number;
|
|
7
|
+
schema_version: "sdk-performance/v1" | "sdk-performance/v2";
|
|
4
8
|
measurements: Array<{
|
|
5
|
-
name:
|
|
9
|
+
name: string;
|
|
6
10
|
count: number;
|
|
7
11
|
work_ms: number;
|
|
8
12
|
max_ms: number;
|
|
13
|
+
histogram?: number[];
|
|
9
14
|
}>;
|
|
10
15
|
counters: {
|
|
11
16
|
source_signatures: number;
|
|
12
17
|
source_reuses: number;
|
|
13
18
|
route_batches: number;
|
|
19
|
+
source_renewals?: number;
|
|
14
20
|
};
|
|
15
21
|
}
|
|
16
22
|
export interface BeamClientOptions {
|
|
@@ -158,6 +164,21 @@ export interface IntegrityAuditChallenge {
|
|
|
158
164
|
chunks: IntegrityAuditChallengeChunk[];
|
|
159
165
|
}
|
|
160
166
|
export interface TransferPerformance {
|
|
167
|
+
recovery_bandwidth?: {
|
|
168
|
+
excluded_tasks: number;
|
|
169
|
+
accepted_bytes: number;
|
|
170
|
+
};
|
|
171
|
+
deadline?: {
|
|
172
|
+
version: "assignment-deadline/v1";
|
|
173
|
+
decisions: number;
|
|
174
|
+
selected_min_ms: number;
|
|
175
|
+
selected_max_ms: number;
|
|
176
|
+
estimated_max_ms: number;
|
|
177
|
+
sample_count_max: number;
|
|
178
|
+
data_age_max_ms: number | null;
|
|
179
|
+
reasons: Record<string, number>;
|
|
180
|
+
};
|
|
181
|
+
sdk_detail?: SdkPerformanceSummary;
|
|
161
182
|
schema_version: "transfer-performance/v1";
|
|
162
183
|
runtime_epoch: string;
|
|
163
184
|
coverage: "complete" | "restarted";
|
|
@@ -177,6 +198,7 @@ export interface TransferPerformance {
|
|
|
177
198
|
source_signatures: number;
|
|
178
199
|
source_reuses: number;
|
|
179
200
|
route_batches: number;
|
|
201
|
+
source_renewals?: number;
|
|
180
202
|
};
|
|
181
203
|
detail_dropped: number;
|
|
182
204
|
unmeasured?: string[];
|
|
@@ -233,6 +255,8 @@ export interface TransferTerminalSignalWaiter {
|
|
|
233
255
|
close(): Promise<void>;
|
|
234
256
|
}
|
|
235
257
|
export interface S3ProviderConfig {
|
|
258
|
+
/** Physical storage location; independent of the signing region. */
|
|
259
|
+
storage_location?: string;
|
|
236
260
|
provider: "s3";
|
|
237
261
|
id?: string;
|
|
238
262
|
bucket: string;
|
|
@@ -244,6 +268,8 @@ export interface S3ProviderConfig {
|
|
|
244
268
|
endpoint_url?: string;
|
|
245
269
|
}
|
|
246
270
|
export interface R2ProviderConfig {
|
|
271
|
+
/** Physical storage location; independent of the signing region. */
|
|
272
|
+
storage_location?: string;
|
|
247
273
|
provider: "r2";
|
|
248
274
|
id?: string;
|
|
249
275
|
bucket: string;
|
|
@@ -254,6 +280,8 @@ export interface R2ProviderConfig {
|
|
|
254
280
|
endpoint_url?: string;
|
|
255
281
|
}
|
|
256
282
|
export interface S3CompatibleProviderConfig {
|
|
283
|
+
/** Physical storage location; independent of the signing region. */
|
|
284
|
+
storage_location?: string;
|
|
257
285
|
provider: string;
|
|
258
286
|
driver?: "s3-compatible";
|
|
259
287
|
id?: string;
|
|
@@ -268,6 +296,8 @@ export interface S3CompatibleProviderConfig {
|
|
|
268
296
|
account_id?: string;
|
|
269
297
|
}
|
|
270
298
|
export interface HippiusProviderConfig {
|
|
299
|
+
/** Physical storage location; independent of the signing region. */
|
|
300
|
+
storage_location?: string;
|
|
271
301
|
provider: "hippius";
|
|
272
302
|
id?: string;
|
|
273
303
|
bucket: string;
|
|
@@ -277,6 +307,8 @@ export interface HippiusProviderConfig {
|
|
|
277
307
|
}
|
|
278
308
|
export type HuggingFaceRepoType = "model" | "dataset" | "space" | "kernel" | "bucket";
|
|
279
309
|
export interface HuggingFaceProviderConfig {
|
|
310
|
+
/** Physical storage location; independent of the signing region. */
|
|
311
|
+
storage_location?: string;
|
|
280
312
|
provider: "huggingface";
|
|
281
313
|
id?: string;
|
|
282
314
|
/** Namespace and repo or bucket name separated by a slash, for example `org/dataset`. */
|
package/dist/nats-control.d.ts
CHANGED
|
@@ -135,6 +135,7 @@ export declare class BeamTransferControl {
|
|
|
135
135
|
private readonly recoveryOperations;
|
|
136
136
|
private readonly recoveryRequestedEpoch;
|
|
137
137
|
private readonly runtimeEpochs;
|
|
138
|
+
private readonly runtimeCapabilities;
|
|
138
139
|
private helloTimer;
|
|
139
140
|
constructor(options: TransferControlOptions);
|
|
140
141
|
close(): Promise<void>;
|
|
@@ -155,6 +156,7 @@ export declare class BeamTransferControl {
|
|
|
155
156
|
splitRoutesForPayload(messageType: TransferClientMessageType, basePayload: Record<string, unknown>, routes: SignedChunkRoute[]): SignedChunkRoute[][];
|
|
156
157
|
private connection;
|
|
157
158
|
private ensureHelloMonitor;
|
|
159
|
+
supportsPerformanceV2(transferId: string): boolean;
|
|
158
160
|
private pollRuntimeHello;
|
|
159
161
|
private observeRuntimeEpoch;
|
|
160
162
|
private recoverLease;
|
package/dist/nats-control.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { currentSdkPerformance } from "./performance.js";
|
|
1
2
|
import { encode, decode } from "@msgpack/msgpack";
|
|
2
3
|
import { connect } from "nats";
|
|
3
4
|
export const TRANSFER_CLIENT_CONTROL_SCHEMA_VERSION = "transfer-client-control/v7";
|
|
@@ -65,6 +66,7 @@ export class BeamTransferControl {
|
|
|
65
66
|
recoveryOperations = new Map();
|
|
66
67
|
recoveryRequestedEpoch = new Map();
|
|
67
68
|
runtimeEpochs = new Map();
|
|
69
|
+
runtimeCapabilities = new Map();
|
|
68
70
|
helloTimer = null;
|
|
69
71
|
constructor(options) {
|
|
70
72
|
this.apiKey = options.apiKey;
|
|
@@ -277,7 +279,12 @@ export class BeamTransferControl {
|
|
|
277
279
|
producer: "sdk",
|
|
278
280
|
payload
|
|
279
281
|
};
|
|
282
|
+
const encodeStarted = performance.now();
|
|
280
283
|
const bytes = encode(envelope);
|
|
284
|
+
if (messageType === "transfer.route_stream.batch") {
|
|
285
|
+
currentSdkPerformance()?.observe("sdk.batch_encode", performance.now() - encodeStarted);
|
|
286
|
+
currentSdkPerformance()?.gauge("batch_bytes_max", bytes.byteLength);
|
|
287
|
+
}
|
|
281
288
|
if (bytes.byteLength > this.maxPayloadBytes) {
|
|
282
289
|
throw new Error(`NATS lifecycle request is ${bytes.byteLength} bytes, above maxPayloadBytes=${this.maxPayloadBytes}`);
|
|
283
290
|
}
|
|
@@ -488,10 +495,15 @@ export class BeamTransferControl {
|
|
|
488
495
|
}, 5_000);
|
|
489
496
|
this.helloTimer.unref?.();
|
|
490
497
|
}
|
|
498
|
+
supportsPerformanceV2(transferId) {
|
|
499
|
+
const value = this.runtimeCapabilities.get(transferShardId(transferId, this.shardCount));
|
|
500
|
+
return !!value && value.until > Date.now() && value.capabilities.includes("sdk-performance/v2");
|
|
501
|
+
}
|
|
491
502
|
async pollRuntimeHello(shardId) {
|
|
492
503
|
try {
|
|
493
504
|
try {
|
|
494
|
-
await this.request("runtime.hello", { capabilities: ["integrity-signing/v1"] }, { idempotencyKey: `runtime:hello:${shardId}:integrity-signing`, shardId });
|
|
505
|
+
const hello = await this.request("runtime.hello", { capabilities: ["integrity-signing/v1", "sdk-performance/v2"] }, { idempotencyKey: `runtime:hello:${shardId}:integrity-signing`, shardId });
|
|
506
|
+
this.runtimeCapabilities.set(shardId, { until: Date.now() + 15_000, capabilities: hello.capabilities ?? [] });
|
|
495
507
|
}
|
|
496
508
|
catch {
|
|
497
509
|
await this.request("runtime.hello", {}, { idempotencyKey: `runtime:hello:${shardId}`, shardId });
|
|
@@ -510,6 +522,7 @@ export class BeamTransferControl {
|
|
|
510
522
|
return;
|
|
511
523
|
if (previous.runtimeEpoch === runtimeEpoch && previous.transportEpoch === transportEpoch)
|
|
512
524
|
return;
|
|
525
|
+
this.runtimeCapabilities.delete(shardId);
|
|
513
526
|
if (previous.runtimeEpoch !== runtimeEpoch) {
|
|
514
527
|
this.authToken = null;
|
|
515
528
|
this.authTokenExpiresAt = 0;
|
package/dist/performance.d.ts
CHANGED
|
@@ -1,12 +1,39 @@
|
|
|
1
1
|
import type { SdkPerformanceSummary } from './models.js';
|
|
2
|
+
export declare const currentSdkPerformance: () => SdkPerformanceCollector | undefined;
|
|
3
|
+
export declare const measureSdkPhase: <T>(name: string, work: () => Promise<T>) => Promise<T>;
|
|
4
|
+
/** Bounded identities only: no URLs, credentials or grant bodies. */
|
|
5
|
+
export declare class SourceSignatureHistory {
|
|
6
|
+
complete: boolean;
|
|
7
|
+
private readonly bits;
|
|
8
|
+
constructor(complete?: boolean);
|
|
9
|
+
created(index: number): boolean;
|
|
10
|
+
}
|
|
2
11
|
export declare class SdkPerformanceCollector {
|
|
12
|
+
private readonly sourceHistory?;
|
|
13
|
+
constructor(sourceHistory?: SourceSignatureHistory | undefined);
|
|
14
|
+
private sourceRenewals;
|
|
15
|
+
sourceCreated(index: number): void;
|
|
16
|
+
private readonly started;
|
|
17
|
+
private readonly details;
|
|
18
|
+
private readonly cpu;
|
|
19
|
+
private finished;
|
|
20
|
+
private delayTimer?;
|
|
3
21
|
private readonly measurements;
|
|
22
|
+
private readonly gauges;
|
|
23
|
+
private readonly milestones;
|
|
24
|
+
private dropped;
|
|
4
25
|
readonly counters: {
|
|
5
26
|
source_signatures: number;
|
|
6
27
|
source_reuses: number;
|
|
7
28
|
route_batches: number;
|
|
8
29
|
};
|
|
9
|
-
observe(name:
|
|
10
|
-
|
|
11
|
-
|
|
30
|
+
observe(name: string, milliseconds: number): void;
|
|
31
|
+
gauge(name: string, value: number): void;
|
|
32
|
+
increment(name: string): void;
|
|
33
|
+
seedDiscovery(other: SdkPerformanceCollector): void;
|
|
34
|
+
mark(name: string): void;
|
|
35
|
+
measure<T>(name: string, work: () => Promise<T>): Promise<T>;
|
|
36
|
+
startDelaySampling(): void;
|
|
37
|
+
finish(): void;
|
|
38
|
+
snapshot(v2?: boolean): SdkPerformanceSummary;
|
|
12
39
|
}
|
package/dist/performance.js
CHANGED
|
@@ -1,25 +1,124 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
const NAMES = ["sdk.discovery", "sdk.multipart_create", "sdk.signing", "sdk.batch_ack", "sdk.buffer_wait", "sdk.preparation", "sdk.source_signing", "sdk.destination_signing", "sdk.source_grant_wait", "sdk.multipart_ready_wait", "sdk.multipart_provider", "sdk.multipart_queue", "sdk.multipart_callback", "sdk.manifest_ack", "sdk.provider_client_setup", "sdk.metadata_request", "sdk.route_assembly", "sdk.batch_encode", "sdk.batch_checksum", "sdk.batch_queue", "sdk.signing_queue", "sdk.process_cpu", "sdk.event_loop_delay", "sdk.producer_wait"];
|
|
3
|
+
const BOUNDS = [0.1, .5, 1, 2, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000, 120000];
|
|
4
|
+
const GAUGES = new Set(["signing_configured_limit", "multipart_configured_limit", "signing_limit", "multipart_limit", "signing_active_peak", "signing_pending_peak", "multipart_active_peak", "buffered_batches_peak", "batch_routes_max", "batch_bytes_max", "concurrency_changes", "source_waiters", "provider_clients_created", "provider_clients_reused"]);
|
|
5
|
+
const MILESTONES = new Set(["first_manifest_ms", "first_route_ms", "first_batch_ms", "final_flush_ms", "prepared_ms"]);
|
|
6
|
+
const context = new AsyncLocalStorage();
|
|
7
|
+
let activeDelaySamplers = 0;
|
|
8
|
+
export const currentSdkPerformance = () => context.getStore();
|
|
9
|
+
export const measureSdkPhase = (name, work) => context.getStore()?.measure(name, work) ?? work();
|
|
10
|
+
/** Bounded identities only: no URLs, credentials or grant bodies. */
|
|
11
|
+
export class SourceSignatureHistory {
|
|
12
|
+
complete;
|
|
13
|
+
bits = new Uint8Array(16_384);
|
|
14
|
+
constructor(complete = true) {
|
|
15
|
+
this.complete = complete;
|
|
16
|
+
}
|
|
17
|
+
created(index) {
|
|
18
|
+
if (!Number.isSafeInteger(index) || index < 0 || index >= this.bits.length * 8) {
|
|
19
|
+
this.complete = false;
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const byte = index >>> 3, mask = 1 << (index & 7);
|
|
23
|
+
const renewed = (this.bits[byte] & mask) !== 0;
|
|
24
|
+
this.bits[byte] |= mask;
|
|
25
|
+
return renewed;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
1
28
|
export class SdkPerformanceCollector {
|
|
29
|
+
sourceHistory;
|
|
30
|
+
constructor(sourceHistory) {
|
|
31
|
+
this.sourceHistory = sourceHistory;
|
|
32
|
+
}
|
|
33
|
+
sourceRenewals = 0;
|
|
34
|
+
sourceCreated(index) {
|
|
35
|
+
this.counters.source_signatures++;
|
|
36
|
+
if (this.sourceHistory?.created(index))
|
|
37
|
+
this.sourceRenewals++;
|
|
38
|
+
}
|
|
39
|
+
started = performance.now();
|
|
40
|
+
details = process.env.BEAM_SDK_PERFORMANCE_DETAILS !== 'false';
|
|
41
|
+
cpu = process.cpuUsage();
|
|
42
|
+
finished = false;
|
|
43
|
+
delayTimer;
|
|
2
44
|
measurements = new Map();
|
|
45
|
+
gauges = {};
|
|
46
|
+
milestones = {};
|
|
47
|
+
dropped = 0;
|
|
3
48
|
counters = { source_signatures: 0, source_reuses: 0, route_batches: 0 };
|
|
4
49
|
observe(name, milliseconds) {
|
|
5
|
-
|
|
50
|
+
const index = NAMES.indexOf(name);
|
|
51
|
+
if (index < 0 || (!this.details && index >= 6) || !Number.isFinite(milliseconds) || milliseconds < 0 || milliseconds > 86_400_000)
|
|
52
|
+
return;
|
|
53
|
+
const value = this.measurements.get(name) ?? { count: 0, work_ms: 0, max_ms: 0, histogram: Array(18).fill(0) };
|
|
54
|
+
if (value.count >= 1_000_000 || value.work_ms + milliseconds > 86_400_000) {
|
|
55
|
+
this.dropped++;
|
|
6
56
|
return;
|
|
7
|
-
|
|
57
|
+
}
|
|
8
58
|
value.count++;
|
|
9
59
|
value.work_ms += milliseconds;
|
|
10
60
|
value.max_ms = Math.max(value.max_ms, milliseconds);
|
|
61
|
+
const bin = BOUNDS.findIndex(bound => milliseconds <= bound);
|
|
62
|
+
value.histogram[bin < 0 ? 17 : bin]++;
|
|
11
63
|
this.measurements.set(name, value);
|
|
12
64
|
}
|
|
65
|
+
gauge(name, value) {
|
|
66
|
+
if (this.details && GAUGES.has(name) && Number.isFinite(value) && value >= 0)
|
|
67
|
+
this.gauges[name] = Math.min(1_000_000_000, Math.max(this.gauges[name] ?? 0, value));
|
|
68
|
+
}
|
|
69
|
+
increment(name) { this.gauge(name, (this.gauges[name] ?? 0) + 1); }
|
|
70
|
+
seedDiscovery(other) {
|
|
71
|
+
for (const name of ["sdk.discovery", "sdk.metadata_request", "sdk.provider_client_setup"]) {
|
|
72
|
+
const value = other.measurements.get(name);
|
|
73
|
+
if (value)
|
|
74
|
+
this.measurements.set(name, { ...value, histogram: [...value.histogram] });
|
|
75
|
+
}
|
|
76
|
+
for (const name of ["provider_clients_created", "provider_clients_reused"])
|
|
77
|
+
if (other.gauges[name] !== undefined)
|
|
78
|
+
this.gauges[name] = other.gauges[name];
|
|
79
|
+
}
|
|
80
|
+
mark(name) {
|
|
81
|
+
if (this.details && MILESTONES.has(name) && this.milestones[name] === undefined)
|
|
82
|
+
this.milestones[name] = performance.now() - this.started;
|
|
83
|
+
}
|
|
13
84
|
async measure(name, work) {
|
|
14
85
|
const start = performance.now();
|
|
15
86
|
try {
|
|
16
|
-
return await work
|
|
87
|
+
return await context.run(this, work);
|
|
17
88
|
}
|
|
18
89
|
finally {
|
|
19
90
|
this.observe(name, performance.now() - start);
|
|
20
91
|
}
|
|
21
92
|
}
|
|
22
|
-
|
|
23
|
-
|
|
93
|
+
startDelaySampling() {
|
|
94
|
+
if (!this.details || this.delayTimer || this.finished || activeDelaySamplers >= 16)
|
|
95
|
+
return;
|
|
96
|
+
activeDelaySamplers++;
|
|
97
|
+
let last = performance.now();
|
|
98
|
+
this.delayTimer = setInterval(() => {
|
|
99
|
+
const now = performance.now();
|
|
100
|
+
this.observe('sdk.event_loop_delay', Math.max(0, now - last - 100));
|
|
101
|
+
last = now;
|
|
102
|
+
}, 100);
|
|
103
|
+
this.delayTimer.unref();
|
|
104
|
+
}
|
|
105
|
+
finish() {
|
|
106
|
+
if (this.finished)
|
|
107
|
+
return;
|
|
108
|
+
this.finished = true;
|
|
109
|
+
if (this.delayTimer) {
|
|
110
|
+
clearInterval(this.delayTimer);
|
|
111
|
+
this.delayTimer = undefined;
|
|
112
|
+
activeDelaySamplers--;
|
|
113
|
+
}
|
|
114
|
+
const used = process.cpuUsage(this.cpu);
|
|
115
|
+
this.observe('sdk.process_cpu', (used.user + used.system) / 1000);
|
|
116
|
+
this.mark('prepared_ms');
|
|
117
|
+
}
|
|
118
|
+
snapshot(v2 = true) {
|
|
119
|
+
const measurements = [...this.measurements].filter(([name]) => v2 || NAMES.slice(0, 6).includes(name))
|
|
120
|
+
.map(([name, value]) => ({ name, count: value.count, work_ms: value.work_ms, max_ms: value.max_ms, ...(v2 ? { histogram: [...value.histogram] } : {}) }));
|
|
121
|
+
return { schema_version: v2 ? 'sdk-performance/v2' : 'sdk-performance/v1', measurements, counters: { ...this.counters, ...(v2 && this.sourceHistory?.complete ? { source_renewals: this.sourceRenewals } : {}) },
|
|
122
|
+
...(v2 ? { gauges: { ...this.gauges }, milestones: { ...this.milestones }, unmeasured: NAMES.slice(6).filter(name => !this.measurements.has(name)), detail_dropped: this.dropped } : {}) };
|
|
24
123
|
}
|
|
25
124
|
}
|
package/dist/provider-signing.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { currentSdkPerformance, measureSdkPhase } from "./performance.js";
|
|
1
2
|
import { AbortMultipartUploadCommand, CompleteMultipartUploadCommand, CreateMultipartUploadCommand, GetObjectCommand, HeadObjectCommand, ListPartsCommand, PutObjectCommand, S3Client, UploadPartCommand } from "@aws-sdk/client-s3";
|
|
2
3
|
import { UploadPartCopyCommand, DeleteObjectCommand, ListObjectsV2Command } from "@aws-sdk/client-s3";
|
|
3
4
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
@@ -82,7 +83,7 @@ export async function prepareProviderSource(source, options = {}) {
|
|
|
82
83
|
if (isS3CompatibleProvider(source)) {
|
|
83
84
|
const endpoint = s3CompatibleEndpoint(source);
|
|
84
85
|
const client = createS3CompatibleClient(source, endpoint);
|
|
85
|
-
const head = await client.send(new HeadObjectCommand({ Bucket: source.bucket, Key: source.key }), { abortSignal: options.signal });
|
|
86
|
+
const head = await measureSdkPhase("sdk.metadata_request", () => client.send(new HeadObjectCommand({ Bucket: source.bucket, Key: source.key }), { abortSignal: options.signal }));
|
|
86
87
|
const getUrl = await getSignedUrl(client, new GetObjectCommand({ Bucket: source.bucket, Key: source.key }), { expiresIn });
|
|
87
88
|
return {
|
|
88
89
|
source_id: source.id ?? `src_${index}`,
|
|
@@ -113,7 +114,7 @@ export async function prepareProviderSource(source, options = {}) {
|
|
|
113
114
|
size,
|
|
114
115
|
filename: filename(source.key),
|
|
115
116
|
expires_at: expiresAtIso(expiresIn),
|
|
116
|
-
metadata: { bucket: source.bucket, key: source.key, base_url: baseUrl }
|
|
117
|
+
metadata: compactMetadata({ bucket: source.bucket, key: source.key, base_url: baseUrl, storage_location: source.storage_location })
|
|
117
118
|
};
|
|
118
119
|
}
|
|
119
120
|
if (isHuggingFaceProvider(source)) {
|
|
@@ -161,7 +162,7 @@ export async function prepareProviderSourceForPlan(source, options = {}) {
|
|
|
161
162
|
provider: "hippius",
|
|
162
163
|
size,
|
|
163
164
|
filename: filename(source.key),
|
|
164
|
-
metadata: { bucket: source.bucket, key: source.key, base_url: baseUrl }
|
|
165
|
+
metadata: compactMetadata({ bucket: source.bucket, key: source.key, base_url: baseUrl, storage_location: source.storage_location })
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
if (isHuggingFaceProvider(source)) {
|
|
@@ -199,6 +200,7 @@ export function prepareProviderDestination(destination, options = {}) {
|
|
|
199
200
|
metadata: {
|
|
200
201
|
bucket: destination.bucket,
|
|
201
202
|
key: destination.key,
|
|
203
|
+
storage_location: destination.storage_location,
|
|
202
204
|
base_url: destination.base_url ?? "https://api.hippius.com"
|
|
203
205
|
}
|
|
204
206
|
};
|
|
@@ -336,14 +338,14 @@ export async function signDestinationRoute(input) {
|
|
|
336
338
|
}
|
|
337
339
|
const destinationExpiresAt = expiresAtIso(input.expiresIn);
|
|
338
340
|
const [destUrl, sourceRoute] = await Promise.all([
|
|
339
|
-
input.destUrl ?? signDestinationUrl({
|
|
341
|
+
input.destUrl ?? measureSdkPhase("sdk.destination_signing", () => signDestinationUrl({
|
|
340
342
|
destination: input.destination,
|
|
341
343
|
objectKey: targetObjectKey,
|
|
342
344
|
uploadId: input.uploadId,
|
|
343
345
|
partNumber: input.partNumber,
|
|
344
346
|
expiresIn: input.expiresIn,
|
|
345
347
|
fetchImpl: input.fetchImpl,
|
|
346
|
-
}),
|
|
348
|
+
})),
|
|
347
349
|
input.sourceGrant ?? signSourceChunk({
|
|
348
350
|
source: input.source,
|
|
349
351
|
fallbackUrl: input.chunk.source_url,
|
|
@@ -522,8 +524,11 @@ function s3ClientCacheKey(source, endpoint) {
|
|
|
522
524
|
function createS3CompatibleClient(source, endpoint = s3CompatibleEndpoint(source)) {
|
|
523
525
|
const cacheKey = s3ClientCacheKey(source, endpoint);
|
|
524
526
|
const cached = s3ClientCache.get(source);
|
|
525
|
-
if (cached?.identity === cacheKey)
|
|
527
|
+
if (cached?.identity === cacheKey) {
|
|
528
|
+
currentSdkPerformance()?.increment("provider_clients_reused");
|
|
526
529
|
return cached.client;
|
|
530
|
+
}
|
|
531
|
+
const setupStarted = performance.now();
|
|
527
532
|
const client = new S3Client({
|
|
528
533
|
region: s3CompatibleRegion(source),
|
|
529
534
|
endpoint,
|
|
@@ -536,6 +541,8 @@ function createS3CompatibleClient(source, endpoint = s3CompatibleEndpoint(source
|
|
|
536
541
|
}
|
|
537
542
|
});
|
|
538
543
|
s3ClientCache.set(source, { identity: cacheKey, client });
|
|
544
|
+
currentSdkPerformance()?.increment("provider_clients_created");
|
|
545
|
+
currentSdkPerformance()?.observe("sdk.provider_client_setup", performance.now() - setupStarted);
|
|
539
546
|
return client;
|
|
540
547
|
}
|
|
541
548
|
export function isS3CompatibleProvider(config) {
|
|
@@ -586,6 +593,7 @@ function s3CompatibleMetadata(source, endpoint = s3CompatibleEndpoint(source)) {
|
|
|
586
593
|
bucket: source.bucket,
|
|
587
594
|
key: source.key,
|
|
588
595
|
region: s3CompatibleRegion(source),
|
|
596
|
+
storage_location: source.storage_location,
|
|
589
597
|
endpoint_url: endpoint,
|
|
590
598
|
account_id: "account_id" in source ? source.account_id : undefined
|
|
591
599
|
});
|
|
@@ -599,6 +607,7 @@ export function isHuggingFaceProvider(config) {
|
|
|
599
607
|
function huggingFaceMetadata(config, etag, commitHash) {
|
|
600
608
|
return compactMetadata({
|
|
601
609
|
driver: "huggingface",
|
|
610
|
+
storage_location: config.storage_location,
|
|
602
611
|
repo_id: config.repo_id,
|
|
603
612
|
repo_type: huggingFaceRepoType(config),
|
|
604
613
|
revision: huggingFaceRevision(config),
|