@aztec/blob-client 3.0.0-nightly.20260105 → 4.0.0-nightly.20260107
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 +9 -0
- package/dest/client/config.d.ts +5 -1
- package/dest/client/config.d.ts.map +1 -1
- package/dest/client/config.js +5 -0
- package/dest/client/factory.d.ts +3 -2
- package/dest/client/factory.d.ts.map +1 -1
- package/dest/client/factory.js +5 -2
- package/dest/client/http.d.ts +17 -1
- package/dest/client/http.d.ts.map +1 -1
- package/dest/client/http.js +34 -0
- package/dest/client/interface.d.ts +7 -1
- package/dest/client/interface.d.ts.map +1 -1
- package/dest/client/local.d.ts +3 -1
- package/dest/client/local.d.ts.map +1 -1
- package/dest/client/local.js +3 -0
- package/dest/filestore/filestore_blob_client.d.ts +14 -2
- package/dest/filestore/filestore_blob_client.d.ts.map +1 -1
- package/dest/filestore/filestore_blob_client.js +29 -5
- package/dest/filestore/healthcheck.d.ts +5 -0
- package/dest/filestore/healthcheck.d.ts.map +1 -0
- package/dest/filestore/healthcheck.js +3 -0
- package/package.json +7 -7
- package/src/client/config.ts +10 -0
- package/src/client/factory.ts +7 -2
- package/src/client/http.ts +46 -0
- package/src/client/interface.ts +6 -0
- package/src/client/local.ts +5 -0
- package/src/filestore/filestore_blob_client.ts +33 -5
- package/src/filestore/healthcheck.ts +5 -0
package/README.md
CHANGED
|
@@ -34,6 +34,15 @@ Beacon node URLs for fetching recent blobs directly from L1.
|
|
|
34
34
|
**Archive API URL** (`BLOB_SINK_ARCHIVE_API_URL`):
|
|
35
35
|
Blobscan or similar archive API for historical blob data.
|
|
36
36
|
|
|
37
|
+
### File Store Connectivity Testing
|
|
38
|
+
|
|
39
|
+
All file stores (S3, GCS, HTTP, local) test connectivity by checking if a well-known healthcheck file (`.healthcheck`) exists. This approach was chosen because:
|
|
40
|
+
|
|
41
|
+
1. **HTTP compatibility**: For HTTP-based file stores, requesting a known file is the only reliable way to verify connectivity
|
|
42
|
+
2. **Uniform behavior**: Using the same healthcheck mechanism across all store types ensures consistent behavior and simplifies testing
|
|
43
|
+
|
|
44
|
+
When uploading is enabled, the sequencer uploads the healthcheck file on startup and then periodically re-uploads it (by default every 60 minutes) to ensure it remains available. This guards against accidental deletion, storage pruning, or other failures that might remove the file.
|
|
45
|
+
|
|
37
46
|
### Example Usage
|
|
38
47
|
|
|
39
48
|
```typescript
|
package/dest/client/config.d.ts
CHANGED
|
@@ -36,6 +36,10 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
|
|
|
36
36
|
* URL for uploading blobs to filestore (s3://, gs://, file://)
|
|
37
37
|
*/
|
|
38
38
|
blobFileStoreUploadUrl?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
|
|
41
|
+
*/
|
|
42
|
+
blobHealthcheckUploadIntervalMinutes?: number;
|
|
39
43
|
}
|
|
40
44
|
export declare const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig>;
|
|
41
45
|
/**
|
|
@@ -47,4 +51,4 @@ export declare function getBlobClientConfigFromEnv(): BlobClientConfig;
|
|
|
47
51
|
* Returns whether the given blob client config has any remote sources defined.
|
|
48
52
|
*/
|
|
49
53
|
export declare function hasRemoteBlobSources(config?: BlobClientConfig): boolean;
|
|
50
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
54
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlnLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2NvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQ0wsS0FBSyxrQkFBa0IsRUFDdkIsV0FBVyxFQUdaLE1BQU0sMEJBQTBCLENBQUM7QUFFbEMsT0FBTyxFQUFFLEtBQUssb0JBQW9CLEVBQWdDLE1BQU0sc0JBQXNCLENBQUM7QUFFL0Y7O0dBRUc7QUFDSCxNQUFNLFdBQVcsZ0JBQWlCLFNBQVEsb0JBQW9CO0lBQzVEOztPQUVHO0lBQ0gsU0FBUyxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFckI7O09BRUc7SUFDSCxtQkFBbUIsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO0lBRS9COztPQUVHO0lBQ0gsc0JBQXNCLENBQUMsRUFBRSxXQUFXLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQztJQUUvQzs7T0FFRztJQUNILDRCQUE0QixDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUM7SUFFeEM7O09BRUc7SUFDSCxpQkFBaUIsQ0FBQyxFQUFFLE1BQU0sQ0FBQztJQUUzQjs7T0FFRztJQUNILHFCQUFxQixDQUFDLEVBQUUsT0FBTyxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsaUJBQWlCLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztJQUU3Qjs7T0FFRztJQUNILHNCQUFzQixDQUFDLEVBQUUsTUFBTSxDQUFDO0lBRWhDOztPQUVHO0lBQ0gsb0NBQW9DLENBQUMsRUFBRSxNQUFNLENBQUM7Q0FDL0M7QUFFRCxlQUFPLE1BQU0sdUJBQXVCLEVBQUUsa0JBQWtCLENBQUMsZ0JBQWdCLENBb0R4RSxDQUFDO0FBRUY7OztHQUdHO0FBQ0gsd0JBQWdCLDBCQUEwQixJQUFJLGdCQUFnQixDQUU3RDtBQUVEOztHQUVHO0FBQ0gsd0JBQWdCLG9CQUFvQixDQUFDLE1BQU0sR0FBRSxnQkFBcUIsR0FBRyxPQUFPLENBRTNFIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,oBAAoB,EAAgC,MAAM,sBAAsB,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;IAE/C;;OAEG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IAExC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/client/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,WAAW,EAGZ,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAE,KAAK,oBAAoB,EAAgC,MAAM,sBAAsB,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,oBAAoB;IAC5D;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE/B;;OAEG;IACH,sBAAsB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;IAE/C;;OAEG;IACH,4BAA4B,CAAC,EAAE,MAAM,EAAE,CAAC;IAExC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAEhC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B;;OAEG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;OAEG;IACH,oCAAoC,CAAC,EAAE,MAAM,CAAC;CAC/C;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAAkB,CAAC,gBAAgB,CAoDxE,CAAC;AAEF;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,gBAAgB,CAE7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,gBAAqB,GAAG,OAAO,CAE3E"}
|
package/dest/client/config.js
CHANGED
|
@@ -40,6 +40,11 @@ export const blobClientConfigMapping = {
|
|
|
40
40
|
env: 'BLOB_FILE_STORE_UPLOAD_URL',
|
|
41
41
|
description: 'URL for uploading blobs to filestore (s3://, gs://, file://)'
|
|
42
42
|
},
|
|
43
|
+
blobHealthcheckUploadIntervalMinutes: {
|
|
44
|
+
env: 'BLOB_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES',
|
|
45
|
+
description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
|
|
46
|
+
parseEnv: (val)=>val ? +val : undefined
|
|
47
|
+
},
|
|
43
48
|
...blobArchiveApiConfigMappings
|
|
44
49
|
};
|
|
45
50
|
/**
|
package/dest/client/factory.d.ts
CHANGED
|
@@ -30,10 +30,11 @@ export interface BlobClientWithFileStoresConfig extends BlobClientConfig {
|
|
|
30
30
|
* 2. Creating read-only FileStore clients
|
|
31
31
|
* 3. Creating a writable FileStore client for uploads
|
|
32
32
|
* 4. Creating the BlobClient with these dependencies
|
|
33
|
+
* 5. Starting the client (uploads initial healthcheck file if upload client is configured)
|
|
33
34
|
*
|
|
34
35
|
* @param config - Configuration containing blob client settings and chain metadata
|
|
35
36
|
* @param logger - Optional logger for the blob client
|
|
36
|
-
* @returns A BlobClientInterface configured with file store support
|
|
37
|
+
* @returns A BlobClientInterface configured with file store support, already started
|
|
37
38
|
*/
|
|
38
39
|
export declare function createBlobClientWithFileStores(config: BlobClientWithFileStoresConfig, logger?: Logger): Promise<BlobClientInterface>;
|
|
39
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
40
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9mYWN0b3J5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQVFsRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxLQUFLLGdCQUFnQixFQUF3QixNQUFNLGFBQWEsQ0FBQztBQUUxRSxPQUFPLEtBQUssRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRzFELE1BQU0sV0FBVyxvQkFBb0I7SUFDbkMsTUFBTSxDQUFDLEVBQUUsTUFBTSxDQUFDO0lBQ2hCLDBDQUEwQztJQUMxQyxnQkFBZ0IsQ0FBQyxFQUFFLG1CQUFtQixFQUFFLENBQUM7SUFDekMsMkNBQTJDO0lBQzNDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7Q0FDN0M7QUFFRCx3QkFBZ0IsZ0JBQWdCLENBQUMsTUFBTSxDQUFDLEVBQUUsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLEVBQUUsb0JBQW9CLEdBQUcsbUJBQW1CLENBbUI1RztBQUVEOzs7R0FHRztBQUNILE1BQU0sV0FBVyw4QkFBK0IsU0FBUSxnQkFBZ0I7SUFDdEUsU0FBUyxFQUFFLE1BQU0sQ0FBQztJQUNsQixhQUFhLEVBQUUsTUFBTSxDQUFDO0lBQ3RCLFdBQVcsRUFBRTtRQUFFLGFBQWEsRUFBRTtZQUFFLFFBQVEsSUFBSSxNQUFNLENBQUE7U0FBRSxDQUFBO0tBQUUsQ0FBQztDQUN4RDtBQUVEOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILHdCQUFzQiw4QkFBOEIsQ0FDbEQsTUFBTSxFQUFFLDhCQUE4QixFQUN0QyxNQUFNLENBQUMsRUFBRSxNQUFNLEdBQ2QsT0FBTyxDQUFDLG1CQUFtQixDQUFDLENBdUI5QiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAQlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACzC,2CAA2C;IAC3C,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;CAC7C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,mBAAmB,CAmB5G;AAED;;;GAGG;AACH,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE;QAAE,aAAa,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACxD;AAED
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAQlE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,aAAa,CAAC;AAE1E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAG1D,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACzC,2CAA2C;IAC3C,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;CAC7C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,oBAAoB,GAAG,mBAAmB,CAmB5G;AAED;;;GAGG;AACH,MAAM,WAAW,8BAA+B,SAAQ,gBAAgB;IACtE,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE;QAAE,aAAa,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;CACxD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CAClD,MAAM,EAAE,8BAA8B,EACtC,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAuB9B"}
|
package/dest/client/factory.js
CHANGED
|
@@ -30,10 +30,11 @@ export function createBlobClient(config, deps) {
|
|
|
30
30
|
* 2. Creating read-only FileStore clients
|
|
31
31
|
* 3. Creating a writable FileStore client for uploads
|
|
32
32
|
* 4. Creating the BlobClient with these dependencies
|
|
33
|
+
* 5. Starting the client (uploads initial healthcheck file if upload client is configured)
|
|
33
34
|
*
|
|
34
35
|
* @param config - Configuration containing blob client settings and chain metadata
|
|
35
36
|
* @param logger - Optional logger for the blob client
|
|
36
|
-
* @returns A BlobClientInterface configured with file store support
|
|
37
|
+
* @returns A BlobClientInterface configured with file store support, already started
|
|
37
38
|
*/ export async function createBlobClientWithFileStores(config, logger) {
|
|
38
39
|
const log = logger ?? createLogger('blob-client');
|
|
39
40
|
const fileStoreMetadata = {
|
|
@@ -45,9 +46,11 @@ export function createBlobClient(config, deps) {
|
|
|
45
46
|
createReadOnlyFileStoreBlobClients(config.blobFileStoreUrls, fileStoreMetadata, log),
|
|
46
47
|
createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, fileStoreMetadata, log)
|
|
47
48
|
]);
|
|
48
|
-
|
|
49
|
+
const client = createBlobClient(config, {
|
|
49
50
|
logger: log,
|
|
50
51
|
fileStoreClients,
|
|
51
52
|
fileStoreUploadClient
|
|
52
53
|
});
|
|
54
|
+
await client.start?.();
|
|
55
|
+
return client;
|
|
53
56
|
}
|
package/dest/client/http.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
13
13
|
protected readonly fileStoreClients: FileStoreBlobClient[];
|
|
14
14
|
protected readonly fileStoreUploadClient: FileStoreBlobClient | undefined;
|
|
15
15
|
private disabled;
|
|
16
|
+
private healthcheckUploadIntervalId?;
|
|
16
17
|
constructor(config?: BlobClientConfig, opts?: {
|
|
17
18
|
logger?: Logger;
|
|
18
19
|
archiveClient?: BlobArchiveClient;
|
|
@@ -59,5 +60,20 @@ export declare class HttpBlobClient implements BlobClientInterface {
|
|
|
59
60
|
private getSlotNumber;
|
|
60
61
|
/** @internal - exposed for testing */
|
|
61
62
|
getArchiveClient(): BlobArchiveClient | undefined;
|
|
63
|
+
/** Returns true if this client can upload blobs to filestore. */
|
|
64
|
+
canUpload(): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Start the blob client.
|
|
67
|
+
* Uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
68
|
+
*/
|
|
69
|
+
start(): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
|
|
72
|
+
*/
|
|
73
|
+
private startPeriodicHealthcheckUpload;
|
|
74
|
+
/**
|
|
75
|
+
* Stop the blob client, clearing any periodic tasks.
|
|
76
|
+
*/
|
|
77
|
+
stop(): void;
|
|
62
78
|
}
|
|
63
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
79
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NsaWVudC9odHRwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxRQUFRLEVBQStCLE1BQU0saUJBQWlCLENBQUM7QUFFbkYsT0FBTyxFQUFFLEtBQUssTUFBTSxFQUFnQixNQUFNLHVCQUF1QixDQUFDO0FBT2xFLE9BQU8sS0FBSyxFQUFFLGlCQUFpQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDakUsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsS0FBSyxnQkFBZ0IsRUFBOEIsTUFBTSxhQUFhLENBQUM7QUFDaEYsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxjQUFlLFlBQVcsbUJBQW1CO0lBYXRELE9BQU8sQ0FBQyxRQUFRLENBQUMsSUFBSTtJQVp2QixTQUFTLENBQUMsUUFBUSxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7SUFDL0IsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLEVBQUUsZ0JBQWdCLENBQUM7SUFDNUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFhLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxDQUFDO0lBQ2hFLFNBQVMsQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sS0FBSyxDQUFDO0lBQ3ZDLFNBQVMsQ0FBQyxRQUFRLENBQUMsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsQ0FBQztJQUMzRCxTQUFTLENBQUMsUUFBUSxDQUFDLHFCQUFxQixFQUFFLG1CQUFtQixHQUFHLFNBQVMsQ0FBQztJQUUxRSxPQUFPLENBQUMsUUFBUSxDQUFTO0lBQ3pCLE9BQU8sQ0FBQywyQkFBMkIsQ0FBQyxDQUFpQjtJQUVyRCxZQUNFLE1BQU0sQ0FBQyxFQUFFLGdCQUFnQixFQUNSLElBQUksR0FBRTtRQUNyQixNQUFNLENBQUMsRUFBRSxNQUFNLENBQUM7UUFDaEIsYUFBYSxDQUFDLEVBQUUsaUJBQWlCLENBQUM7UUFDbEMsZ0JBQWdCLENBQUMsRUFBRSxtQkFBbUIsRUFBRSxDQUFDO1FBQ3pDLHFCQUFxQixDQUFDLEVBQUUsbUJBQW1CLENBQUM7UUFDNUMseUVBQXlFO1FBQ3pFLGNBQWMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLElBQUksQ0FBQztLQUNyQyxFQXdCUDtJQUVEOzs7T0FHRztJQUNILE9BQU8sQ0FBQyxzQkFBc0I7SUFVOUI7Ozs7O09BS0c7SUFDSSxXQUFXLENBQUMsS0FBSyxFQUFFLE9BQU8sR0FBRyxJQUFJLENBR3ZDO0lBRVksV0FBVyxrQkFvRXZCO0lBRVksb0JBQW9CLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FtQmpFO0lBRUQ7Ozs7Ozs7Ozs7Ozs7O09BY0c7SUFDVSxjQUFjLENBQ3pCLFNBQVMsRUFBRSxLQUFLLE1BQU0sRUFBRSxFQUN4QixVQUFVLEVBQUUsTUFBTSxFQUFFLEVBQ3BCLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUMzQixPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0E2SWpCO1lBUWEsYUFBYTtJQXNDZCxrQkFBa0IsQ0FDN0IsT0FBTyxFQUFFLE1BQU0sRUFDZixlQUFlLEVBQUUsTUFBTSxHQUFHLE1BQU0sRUFDaEMsVUFBVSxHQUFFLE1BQU0sRUFBTyxFQUN6QixvQkFBb0IsQ0FBQyxFQUFFLE1BQU0sR0FDNUIsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDLENBR2pCO0lBRVksZ0JBQWdCLENBQzNCLE9BQU8sRUFBRSxNQUFNLEVBQ2YsZUFBZSxFQUFFLE1BQU0sR0FBRyxNQUFNLEVBQ2hDLG9CQUFvQixDQUFDLEVBQUUsTUFBTSxHQUM1QixPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FzQ3JCO0lBRUQsT0FBTyxDQUFDLGlCQUFpQjtZQVlYLG1CQUFtQjtZQW1DbkIsYUFBYTtJQTZEM0Isc0NBQXNDO0lBQy9CLGdCQUFnQixJQUFJLGlCQUFpQixHQUFHLFNBQVMsQ0FFdkQ7SUFFRCxpRUFBaUU7SUFDMUQsU0FBUyxJQUFJLE9BQU8sQ0FFMUI7SUFFRDs7O09BR0c7SUFDVSxLQUFLLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVNsQztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLDhCQUE4QjtJQVd0Qzs7T0FFRztJQUNJLElBQUksSUFBSSxJQUFJLENBS2xCO0NBQ0YifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAOlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/client/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AAEnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAOlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAEjF,OAAO,EAAE,KAAK,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,cAAe,YAAW,mBAAmB;IAatD,OAAO,CAAC,QAAQ,CAAC,IAAI;IAZvB,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAC/B,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAC5C,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAChE,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IACvC,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IAC3D,SAAS,CAAC,QAAQ,CAAC,qBAAqB,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAE1E,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,2BAA2B,CAAC,CAAiB;IAErD,YACE,MAAM,CAAC,EAAE,gBAAgB,EACR,IAAI,GAAE;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,aAAa,CAAC,EAAE,iBAAiB,CAAC;QAClC,gBAAgB,CAAC,EAAE,mBAAmB,EAAE,CAAC;QACzC,qBAAqB,CAAC,EAAE,mBAAmB,CAAC;QAC5C,yEAAyE;QACzE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;KACrC,EAwBP;IAED;;;OAGG;IACH,OAAO,CAAC,sBAAsB;IAU9B;;;;;OAKG;IACI,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGvC;IAEY,WAAW,kBAoEvB;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBjE;IAED;;;;;;;;;;;;;;OAcG;IACU,cAAc,CACzB,SAAS,EAAE,KAAK,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,EAAE,EACpB,IAAI,CAAC,EAAE,qBAAqB,GAC3B,OAAO,CAAC,IAAI,EAAE,CAAC,CA6IjB;YAQa,aAAa;IAsCd,kBAAkB,CAC7B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,UAAU,GAAE,MAAM,EAAO,EACzB,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,IAAI,EAAE,CAAC,CAGjB;IAEY,gBAAgB,CAC3B,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,GAAG,MAAM,EAChC,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,QAAQ,EAAE,CAAC,CAsCrB;IAED,OAAO,CAAC,iBAAiB;YAYX,mBAAmB;YAmCnB,aAAa;IA6D3B,sCAAsC;IAC/B,gBAAgB,IAAI,iBAAiB,GAAG,SAAS,CAEvD;IAED,iEAAiE;IAC1D,SAAS,IAAI,OAAO,CAE1B;IAED;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CASlC;IAED;;OAEG;IACH,OAAO,CAAC,8BAA8B;IAWtC;;OAEG;IACI,IAAI,IAAI,IAAI,CAKlB;CACF"}
|
package/dest/client/http.js
CHANGED
|
@@ -5,6 +5,7 @@ import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
|
5
5
|
import { bufferToHex, hexToBuffer } from '@aztec/foundation/string';
|
|
6
6
|
import { createPublicClient, fallback, http } from 'viem';
|
|
7
7
|
import { createBlobArchiveClient } from '../archive/factory.js';
|
|
8
|
+
import { DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES } from '../filestore/healthcheck.js';
|
|
8
9
|
import { getBlobClientConfigFromEnv } from './config.js';
|
|
9
10
|
export class HttpBlobClient {
|
|
10
11
|
opts;
|
|
@@ -15,6 +16,7 @@ export class HttpBlobClient {
|
|
|
15
16
|
fileStoreClients;
|
|
16
17
|
fileStoreUploadClient;
|
|
17
18
|
disabled;
|
|
19
|
+
healthcheckUploadIntervalId;
|
|
18
20
|
constructor(config, opts = {}){
|
|
19
21
|
this.opts = opts;
|
|
20
22
|
this.disabled = false;
|
|
@@ -474,6 +476,38 @@ export class HttpBlobClient {
|
|
|
474
476
|
/** @internal - exposed for testing */ getArchiveClient() {
|
|
475
477
|
return this.archiveClient;
|
|
476
478
|
}
|
|
479
|
+
/** Returns true if this client can upload blobs to filestore. */ canUpload() {
|
|
480
|
+
return this.fileStoreUploadClient !== undefined;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Start the blob client.
|
|
484
|
+
* Uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
485
|
+
*/ async start() {
|
|
486
|
+
if (!this.fileStoreUploadClient) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
await this.fileStoreUploadClient.uploadHealthcheck();
|
|
490
|
+
this.log.debug('Initial healthcheck file uploaded');
|
|
491
|
+
this.startPeriodicHealthcheckUpload();
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
|
|
495
|
+
*/ startPeriodicHealthcheckUpload() {
|
|
496
|
+
const intervalMs = (this.config.blobHealthcheckUploadIntervalMinutes ?? DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES) * 60 * 1000;
|
|
497
|
+
this.healthcheckUploadIntervalId = setInterval(()=>{
|
|
498
|
+
void this.fileStoreUploadClient.uploadHealthcheck().catch((err)=>{
|
|
499
|
+
this.log.warn('Failed to upload periodic healthcheck file', err);
|
|
500
|
+
});
|
|
501
|
+
}, intervalMs);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Stop the blob client, clearing any periodic tasks.
|
|
505
|
+
*/ stop() {
|
|
506
|
+
if (this.healthcheckUploadIntervalId) {
|
|
507
|
+
clearInterval(this.healthcheckUploadIntervalId);
|
|
508
|
+
this.healthcheckUploadIntervalId = undefined;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
477
511
|
}
|
|
478
512
|
function parseBlobJsonsFromResponse(response, logger) {
|
|
479
513
|
try {
|
|
@@ -16,7 +16,13 @@ export interface BlobClientInterface {
|
|
|
16
16
|
sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
|
|
17
17
|
/** Fetches the given blob sidecars by block hash and blob hashes. */
|
|
18
18
|
getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
19
|
+
/** Starts the blob client (e.g., uploads healthcheck file if not exists). */
|
|
20
|
+
start?(): Promise<void>;
|
|
19
21
|
/** Tests all configured blob sources and logs whether they are reachable or not. */
|
|
20
22
|
testSources(): Promise<void>;
|
|
23
|
+
/** Stops the blob client, clearing any periodic tasks. */
|
|
24
|
+
stop?(): void;
|
|
25
|
+
/** Returns true if this client can upload blobs to filestore. */
|
|
26
|
+
canUpload(): boolean;
|
|
21
27
|
}
|
|
22
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
28
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY2xpZW50L2ludGVyZmFjZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUU1Qzs7R0FFRztBQUNILE1BQU0sV0FBVyxxQkFBcUI7SUFDcEM7Ozs7O09BS0c7SUFDSCxnQkFBZ0IsQ0FBQyxFQUFFLE9BQU8sQ0FBQztDQUM1QjtBQUVELE1BQU0sV0FBVyxtQkFBbUI7SUFDbEMsMEVBQTBFO0lBQzFFLG9CQUFvQixDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEQscUVBQXFFO0lBQ3JFLGNBQWMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLFVBQVUsQ0FBQyxFQUFFLE1BQU0sRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFLHFCQUFxQixHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3RHLDZFQUE2RTtJQUM3RSxLQUFLLENBQUMsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsb0ZBQW9GO0lBQ3BGLFdBQVcsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDN0IsMERBQTBEO0lBQzFELElBQUksQ0FBQyxJQUFJLElBQUksQ0FBQztJQUNkLGlFQUFpRTtJQUNqRSxTQUFTLElBQUksT0FBTyxDQUFDO0NBQ3RCIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/client/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtG,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../src/client/interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,0EAA0E;IAC1E,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtD,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IACtG,6EAA6E;IAC7E,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,oFAAoF;IACpF,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,0DAA0D;IAC1D,IAAI,CAAC,IAAI,IAAI,CAAC;IACd,iEAAiE;IACjE,SAAS,IAAI,OAAO,CAAC;CACtB"}
|
package/dest/client/local.d.ts
CHANGED
|
@@ -7,5 +7,7 @@ export declare class LocalBlobClient implements BlobClientInterface {
|
|
|
7
7
|
testSources(): Promise<void>;
|
|
8
8
|
sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
|
|
9
9
|
getBlobSidecar(_blockId: string, blobHashes: Buffer[], _opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
10
|
+
/** Returns true if this client can upload blobs. Always true for local client. */
|
|
11
|
+
canUpload(): boolean;
|
|
10
12
|
}
|
|
11
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9jYWwuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jbGllbnQvbG9jYWwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLEVBQUUsSUFBSSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUMsT0FBTyxLQUFLLEVBQUUsU0FBUyxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFDdkQsT0FBTyxLQUFLLEVBQUUsbUJBQW1CLEVBQUUscUJBQXFCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUVqRixxQkFBYSxlQUFnQixZQUFXLG1CQUFtQjtJQUN6RCxPQUFPLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBWTtJQUV0QyxZQUFZLFNBQVMsRUFBRSxTQUFTLEVBRS9CO0lBRU0sV0FBVyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFbEM7SUFFWSxvQkFBb0IsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUdqRTtJQUVNLGNBQWMsQ0FBQyxRQUFRLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsRUFBRSxLQUFLLENBQUMsRUFBRSxxQkFBcUIsR0FBRyxPQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FFNUc7SUFFRCxrRkFBa0Y7SUFDM0UsU0FBUyxJQUFJLE9BQU8sQ0FFMUI7Q0FDRiJ9
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/client/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,YAAY,SAAS,EAAE,SAAS,EAE/B;IAEM,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAElC;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;IAEM,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAE5G;CACF"}
|
|
1
|
+
{"version":3,"file":"local.d.ts","sourceRoot":"","sources":["../../src/client/local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEjF,qBAAa,eAAgB,YAAW,mBAAmB;IACzD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IAEtC,YAAY,SAAS,EAAE,SAAS,EAE/B;IAEM,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAElC;IAEY,oBAAoB,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;IAEM,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,KAAK,CAAC,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAE5G;IAED,kFAAkF;IAC3E,SAAS,IAAI,OAAO,CAE1B;CACF"}
|
package/dest/client/local.js
CHANGED
|
@@ -13,4 +13,7 @@ export class LocalBlobClient {
|
|
|
13
13
|
getBlobSidecar(_blockId, blobHashes, _opts) {
|
|
14
14
|
return this.blobStore.getBlobsByHashes(blobHashes);
|
|
15
15
|
}
|
|
16
|
+
/** Returns true if this client can upload blobs. Always true for local client. */ canUpload() {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
16
19
|
}
|
|
@@ -15,6 +15,11 @@ export declare class FileStoreBlobClient {
|
|
|
15
15
|
* Format: basePath/blobs/{versionedBlobHash}.data
|
|
16
16
|
*/
|
|
17
17
|
private blobPath;
|
|
18
|
+
/**
|
|
19
|
+
* Get the path for the healthcheck file.
|
|
20
|
+
* Format: basePath/.healthcheck
|
|
21
|
+
*/
|
|
22
|
+
private healthcheckPath;
|
|
18
23
|
/**
|
|
19
24
|
* Fetch blobs by their versioned hashes.
|
|
20
25
|
* @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
|
|
@@ -44,12 +49,19 @@ export declare class FileStoreBlobClient {
|
|
|
44
49
|
*/
|
|
45
50
|
getBaseUrl(): string;
|
|
46
51
|
/**
|
|
47
|
-
* Test if the filestore connection is working.
|
|
52
|
+
* Test if the filestore connection is working by checking for healthcheck file.
|
|
53
|
+
* The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
|
|
54
|
+
* This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
|
|
48
55
|
*/
|
|
49
56
|
testConnection(): Promise<boolean>;
|
|
57
|
+
/**
|
|
58
|
+
* Upload the healthcheck file if it doesn't already exist.
|
|
59
|
+
* This enables read-only clients (HTTP) to verify connectivity.
|
|
60
|
+
*/
|
|
61
|
+
uploadHealthcheck(): Promise<void>;
|
|
50
62
|
/**
|
|
51
63
|
* Check if the store supports write operations.
|
|
52
64
|
*/
|
|
53
65
|
private isWritable;
|
|
54
66
|
}
|
|
55
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
67
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmlsZXN0b3JlX2Jsb2JfY2xpZW50LmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvZmlsZXN0b3JlL2ZpbGVzdG9yZV9ibG9iX2NsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsSUFBSSxFQUFFLEtBQUssUUFBUSxFQUErQixNQUFNLGlCQUFpQixDQUFDO0FBQ25GLE9BQU8sRUFBRSxLQUFLLE1BQU0sRUFBZ0IsTUFBTSx1QkFBdUIsQ0FBQztBQUNsRSxPQUFPLEtBQUssRUFBRSxTQUFTLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUs3RTs7O0dBR0c7QUFDSCxxQkFBYSxtQkFBbUI7SUFJNUIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsUUFBUTtJQUozQixPQUFPLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBUztJQUU3QixZQUNtQixLQUFLLEVBQUUsaUJBQWlCLEdBQUcsU0FBUyxFQUNwQyxRQUFRLEVBQUUsTUFBTSxFQUNqQyxNQUFNLENBQUMsRUFBRSxNQUFNLEVBR2hCO0lBRUQ7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLFFBQVE7SUFJaEI7OztPQUdHO0lBQ0gsT0FBTyxDQUFDLGVBQWU7SUFJdkI7Ozs7T0FJRztJQUNHLGdCQUFnQixDQUFDLFVBQVUsRUFBRSxNQUFNLEVBQUUsR0FBRyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FtQmhFO0lBRUQ7OztPQUdHO0lBQ0gsTUFBTSxDQUFDLGlCQUFpQixFQUFFLE1BQU0sR0FBRyxPQUFPLENBQUMsT0FBTyxDQUFDLENBRWxEO0lBRUQ7Ozs7O09BS0c7SUFDRyxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksRUFBRSxZQUFZLFVBQU8sR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBa0I3RDtJQUVEOzs7O09BSUc7SUFDRyxTQUFTLENBQUMsS0FBSyxFQUFFLElBQUksRUFBRSxFQUFFLFlBQVksVUFBTyxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FFakU7SUFFRDs7T0FFRztJQUNILFVBQVUsSUFBSSxNQUFNLENBRW5CO0lBRUQ7Ozs7T0FJRztJQUNHLGNBQWMsSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLENBT3ZDO0lBRUQ7OztPQUdHO0lBQ0csaUJBQWlCLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQVF2QztJQUVEOztPQUVHO0lBQ0gsT0FBTyxDQUFDLFVBQVU7Q0FHbkIifQ==
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filestore_blob_client.d.ts","sourceRoot":"","sources":["../../src/filestore/filestore_blob_client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"filestore_blob_client.d.ts","sourceRoot":"","sources":["../../src/filestore/filestore_blob_client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAA+B,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,KAAK,MAAM,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAK7E;;;GAGG;AACH,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B,YACmB,KAAK,EAAE,iBAAiB,GAAG,SAAS,EACpC,QAAQ,EAAE,MAAM,EACjC,MAAM,CAAC,EAAE,MAAM,EAGhB;IAED;;;OAGG;IACH,OAAO,CAAC,QAAQ;IAIhB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAIvB;;;;OAIG;IACG,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAmBhE;IAED;;;OAGG;IACH,MAAM,CAAC,iBAAiB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAElD;IAED;;;;;OAKG;IACG,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB7D;IAED;;;;OAIG;IACG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,CAEnB;IAED;;;;OAIG;IACG,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAOvC;IAED;;;OAGG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQvC;IAED;;OAEG;IACH,OAAO,CAAC,UAAU;CAGnB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { computeEthVersionedBlobHash } from '@aztec/blob-lib';
|
|
2
2
|
import { createLogger } from '@aztec/foundation/log';
|
|
3
3
|
import { inboundTransform, outboundTransform } from '../encoding/index.js';
|
|
4
|
+
import { HEALTHCHECK_CONTENT, HEALTHCHECK_FILENAME } from './healthcheck.js';
|
|
4
5
|
/**
|
|
5
6
|
* A blob client that uses a FileStore (S3/GCS/local) as the data source.
|
|
6
7
|
* Blobs are stored as JSON files keyed by their versioned blob hash.
|
|
@@ -20,6 +21,12 @@ import { inboundTransform, outboundTransform } from '../encoding/index.js';
|
|
|
20
21
|
return `${this.basePath}/blobs/${versionedBlobHash}.data`;
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
24
|
+
* Get the path for the healthcheck file.
|
|
25
|
+
* Format: basePath/.healthcheck
|
|
26
|
+
*/ healthcheckPath() {
|
|
27
|
+
return `${this.basePath}/${HEALTHCHECK_FILENAME}`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
23
30
|
* Fetch blobs by their versioned hashes.
|
|
24
31
|
* @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
|
|
25
32
|
* @returns Array of BlobJson objects for found blobs
|
|
@@ -77,11 +84,28 @@ import { inboundTransform, outboundTransform } from '../encoding/index.js';
|
|
|
77
84
|
return this.basePath;
|
|
78
85
|
}
|
|
79
86
|
/**
|
|
80
|
-
* Test if the filestore connection is working.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
87
|
+
* Test if the filestore connection is working by checking for healthcheck file.
|
|
88
|
+
* The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
|
|
89
|
+
* This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
|
|
90
|
+
*/ async testConnection() {
|
|
91
|
+
try {
|
|
92
|
+
return await this.store.exists(this.healthcheckPath());
|
|
93
|
+
} catch (err) {
|
|
94
|
+
this.log.warn(`Connection test failed: ${err?.message ?? String(err)}`);
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Upload the healthcheck file if it doesn't already exist.
|
|
100
|
+
* This enables read-only clients (HTTP) to verify connectivity.
|
|
101
|
+
*/ async uploadHealthcheck() {
|
|
102
|
+
if (!this.isWritable()) {
|
|
103
|
+
this.log.trace('Cannot upload healthcheck: store is read-only');
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const path = this.healthcheckPath();
|
|
107
|
+
await this.store.save(path, Buffer.from(HEALTHCHECK_CONTENT));
|
|
108
|
+
this.log.debug(`Uploaded healthcheck file to ${path}`);
|
|
85
109
|
}
|
|
86
110
|
/**
|
|
87
111
|
* Check if the store supports write operations.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Constants for healthcheck file used to test file store connectivity. */
|
|
2
|
+
export declare const HEALTHCHECK_FILENAME = ".healthcheck";
|
|
3
|
+
export declare const HEALTHCHECK_CONTENT = "ok";
|
|
4
|
+
export declare const DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES = 60;
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhbHRoY2hlY2suZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9maWxlc3RvcmUvaGVhbHRoY2hlY2sudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkVBQTJFO0FBRTNFLGVBQU8sTUFBTSxvQkFBb0IsaUJBQWlCLENBQUM7QUFDbkQsZUFBTyxNQUFNLG1CQUFtQixPQUFPLENBQUM7QUFDeEMsZUFBTyxNQUFNLDJDQUEyQyxLQUFLLENBQUMifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healthcheck.d.ts","sourceRoot":"","sources":["../../src/filestore/healthcheck.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,eAAO,MAAM,oBAAoB,iBAAiB,CAAC;AACnD,eAAO,MAAM,mBAAmB,OAAO,CAAC;AACxC,eAAO,MAAM,2CAA2C,KAAK,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/blob-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-nightly.20260107",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": "./dest/client/bin/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -56,12 +56,12 @@
|
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@aztec/blob-lib": "
|
|
60
|
-
"@aztec/ethereum": "
|
|
61
|
-
"@aztec/foundation": "
|
|
62
|
-
"@aztec/kv-store": "
|
|
63
|
-
"@aztec/stdlib": "
|
|
64
|
-
"@aztec/telemetry-client": "
|
|
59
|
+
"@aztec/blob-lib": "4.0.0-nightly.20260107",
|
|
60
|
+
"@aztec/ethereum": "4.0.0-nightly.20260107",
|
|
61
|
+
"@aztec/foundation": "4.0.0-nightly.20260107",
|
|
62
|
+
"@aztec/kv-store": "4.0.0-nightly.20260107",
|
|
63
|
+
"@aztec/stdlib": "4.0.0-nightly.20260107",
|
|
64
|
+
"@aztec/telemetry-client": "4.0.0-nightly.20260107",
|
|
65
65
|
"express": "^4.21.2",
|
|
66
66
|
"snappy": "^7.2.2",
|
|
67
67
|
"source-map-support": "^0.5.21",
|
package/src/client/config.ts
CHANGED
|
@@ -50,6 +50,11 @@ export interface BlobClientConfig extends BlobArchiveApiConfig {
|
|
|
50
50
|
* URL for uploading blobs to filestore (s3://, gs://, file://)
|
|
51
51
|
*/
|
|
52
52
|
blobFileStoreUploadUrl?: string;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)
|
|
56
|
+
*/
|
|
57
|
+
blobHealthcheckUploadIntervalMinutes?: number;
|
|
53
58
|
}
|
|
54
59
|
|
|
55
60
|
export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
|
|
@@ -98,6 +103,11 @@ export const blobClientConfigMapping: ConfigMappingsType<BlobClientConfig> = {
|
|
|
98
103
|
env: 'BLOB_FILE_STORE_UPLOAD_URL',
|
|
99
104
|
description: 'URL for uploading blobs to filestore (s3://, gs://, file://)',
|
|
100
105
|
},
|
|
106
|
+
blobHealthcheckUploadIntervalMinutes: {
|
|
107
|
+
env: 'BLOB_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES',
|
|
108
|
+
description: 'Interval in minutes for uploading healthcheck file to file store (default: 60 = 1 hour)',
|
|
109
|
+
parseEnv: (val: string | undefined) => (val ? +val : undefined),
|
|
110
|
+
},
|
|
101
111
|
...blobArchiveApiConfigMappings,
|
|
102
112
|
};
|
|
103
113
|
|
package/src/client/factory.ts
CHANGED
|
@@ -58,10 +58,11 @@ export interface BlobClientWithFileStoresConfig extends BlobClientConfig {
|
|
|
58
58
|
* 2. Creating read-only FileStore clients
|
|
59
59
|
* 3. Creating a writable FileStore client for uploads
|
|
60
60
|
* 4. Creating the BlobClient with these dependencies
|
|
61
|
+
* 5. Starting the client (uploads initial healthcheck file if upload client is configured)
|
|
61
62
|
*
|
|
62
63
|
* @param config - Configuration containing blob client settings and chain metadata
|
|
63
64
|
* @param logger - Optional logger for the blob client
|
|
64
|
-
* @returns A BlobClientInterface configured with file store support
|
|
65
|
+
* @returns A BlobClientInterface configured with file store support, already started
|
|
65
66
|
*/
|
|
66
67
|
export async function createBlobClientWithFileStores(
|
|
67
68
|
config: BlobClientWithFileStoresConfig,
|
|
@@ -80,9 +81,13 @@ export async function createBlobClientWithFileStores(
|
|
|
80
81
|
createWritableFileStoreBlobClient(config.blobFileStoreUploadUrl, fileStoreMetadata, log),
|
|
81
82
|
]);
|
|
82
83
|
|
|
83
|
-
|
|
84
|
+
const client = createBlobClient(config, {
|
|
84
85
|
logger: log,
|
|
85
86
|
fileStoreClients,
|
|
86
87
|
fileStoreUploadClient,
|
|
87
88
|
});
|
|
89
|
+
|
|
90
|
+
await client.start?.();
|
|
91
|
+
|
|
92
|
+
return client;
|
|
88
93
|
}
|
package/src/client/http.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { type RpcBlock, createPublicClient, fallback, http } from 'viem';
|
|
|
9
9
|
import { createBlobArchiveClient } from '../archive/factory.js';
|
|
10
10
|
import type { BlobArchiveClient } from '../archive/interface.js';
|
|
11
11
|
import type { FileStoreBlobClient } from '../filestore/filestore_blob_client.js';
|
|
12
|
+
import { DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES } from '../filestore/healthcheck.js';
|
|
12
13
|
import { type BlobClientConfig, getBlobClientConfigFromEnv } from './config.js';
|
|
13
14
|
import type { BlobClientInterface, GetBlobSidecarOptions } from './interface.js';
|
|
14
15
|
|
|
@@ -21,6 +22,7 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
21
22
|
protected readonly fileStoreUploadClient: FileStoreBlobClient | undefined;
|
|
22
23
|
|
|
23
24
|
private disabled = false;
|
|
25
|
+
private healthcheckUploadIntervalId?: NodeJS.Timeout;
|
|
24
26
|
|
|
25
27
|
constructor(
|
|
26
28
|
config?: BlobClientConfig,
|
|
@@ -545,6 +547,50 @@ export class HttpBlobClient implements BlobClientInterface {
|
|
|
545
547
|
public getArchiveClient(): BlobArchiveClient | undefined {
|
|
546
548
|
return this.archiveClient;
|
|
547
549
|
}
|
|
550
|
+
|
|
551
|
+
/** Returns true if this client can upload blobs to filestore. */
|
|
552
|
+
public canUpload(): boolean {
|
|
553
|
+
return this.fileStoreUploadClient !== undefined;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Start the blob client.
|
|
558
|
+
* Uploads the initial healthcheck file (awaited) and starts periodic uploads.
|
|
559
|
+
*/
|
|
560
|
+
public async start(): Promise<void> {
|
|
561
|
+
if (!this.fileStoreUploadClient) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
await this.fileStoreUploadClient.uploadHealthcheck();
|
|
566
|
+
this.log.debug('Initial healthcheck file uploaded');
|
|
567
|
+
|
|
568
|
+
this.startPeriodicHealthcheckUpload();
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Start periodic healthcheck upload to the file store to ensure it remains available even if accidentally deleted.
|
|
573
|
+
*/
|
|
574
|
+
private startPeriodicHealthcheckUpload(): void {
|
|
575
|
+
const intervalMs =
|
|
576
|
+
(this.config.blobHealthcheckUploadIntervalMinutes ?? DEFAULT_HEALTHCHECK_UPLOAD_INTERVAL_MINUTES) * 60 * 1000;
|
|
577
|
+
|
|
578
|
+
this.healthcheckUploadIntervalId = setInterval(() => {
|
|
579
|
+
void this.fileStoreUploadClient!.uploadHealthcheck().catch(err => {
|
|
580
|
+
this.log.warn('Failed to upload periodic healthcheck file', err);
|
|
581
|
+
});
|
|
582
|
+
}, intervalMs);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Stop the blob client, clearing any periodic tasks.
|
|
587
|
+
*/
|
|
588
|
+
public stop(): void {
|
|
589
|
+
if (this.healthcheckUploadIntervalId) {
|
|
590
|
+
clearInterval(this.healthcheckUploadIntervalId);
|
|
591
|
+
this.healthcheckUploadIntervalId = undefined;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
548
594
|
}
|
|
549
595
|
|
|
550
596
|
function parseBlobJsonsFromResponse(response: any, logger: Logger): BlobJson[] {
|
package/src/client/interface.ts
CHANGED
|
@@ -18,6 +18,12 @@ export interface BlobClientInterface {
|
|
|
18
18
|
sendBlobsToFilestore(blobs: Blob[]): Promise<boolean>;
|
|
19
19
|
/** Fetches the given blob sidecars by block hash and blob hashes. */
|
|
20
20
|
getBlobSidecar(blockId: string, blobHashes?: Buffer[], opts?: GetBlobSidecarOptions): Promise<Blob[]>;
|
|
21
|
+
/** Starts the blob client (e.g., uploads healthcheck file if not exists). */
|
|
22
|
+
start?(): Promise<void>;
|
|
21
23
|
/** Tests all configured blob sources and logs whether they are reachable or not. */
|
|
22
24
|
testSources(): Promise<void>;
|
|
25
|
+
/** Stops the blob client, clearing any periodic tasks. */
|
|
26
|
+
stop?(): void;
|
|
27
|
+
/** Returns true if this client can upload blobs to filestore. */
|
|
28
|
+
canUpload(): boolean;
|
|
23
29
|
}
|
package/src/client/local.ts
CHANGED
|
@@ -22,4 +22,9 @@ export class LocalBlobClient implements BlobClientInterface {
|
|
|
22
22
|
public getBlobSidecar(_blockId: string, blobHashes: Buffer[], _opts?: GetBlobSidecarOptions): Promise<Blob[]> {
|
|
23
23
|
return this.blobStore.getBlobsByHashes(blobHashes);
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/** Returns true if this client can upload blobs. Always true for local client. */
|
|
27
|
+
public canUpload(): boolean {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
25
30
|
}
|
|
@@ -3,6 +3,7 @@ import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
|
3
3
|
import type { FileStore, ReadOnlyFileStore } from '@aztec/stdlib/file-store';
|
|
4
4
|
|
|
5
5
|
import { inboundTransform, outboundTransform } from '../encoding/index.js';
|
|
6
|
+
import { HEALTHCHECK_CONTENT, HEALTHCHECK_FILENAME } from './healthcheck.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* A blob client that uses a FileStore (S3/GCS/local) as the data source.
|
|
@@ -27,6 +28,14 @@ export class FileStoreBlobClient {
|
|
|
27
28
|
return `${this.basePath}/blobs/${versionedBlobHash}.data`;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Get the path for the healthcheck file.
|
|
33
|
+
* Format: basePath/.healthcheck
|
|
34
|
+
*/
|
|
35
|
+
private healthcheckPath(): string {
|
|
36
|
+
return `${this.basePath}/${HEALTHCHECK_FILENAME}`;
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
/**
|
|
31
40
|
* Fetch blobs by their versioned hashes.
|
|
32
41
|
* @param blobHashes - Array of versioned blob hashes (0x-prefixed hex strings)
|
|
@@ -104,12 +113,31 @@ export class FileStoreBlobClient {
|
|
|
104
113
|
}
|
|
105
114
|
|
|
106
115
|
/**
|
|
107
|
-
* Test if the filestore connection is working.
|
|
116
|
+
* Test if the filestore connection is working by checking for healthcheck file.
|
|
117
|
+
* The healthcheck file is uploaded periodically by writable clients via HttpBlobClient.start().
|
|
118
|
+
* This provides a uniform connection test across all store types (S3/GCS/Local/HTTP).
|
|
119
|
+
*/
|
|
120
|
+
async testConnection(): Promise<boolean> {
|
|
121
|
+
try {
|
|
122
|
+
return await this.store.exists(this.healthcheckPath());
|
|
123
|
+
} catch (err: any) {
|
|
124
|
+
this.log.warn(`Connection test failed: ${err?.message ?? String(err)}`);
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Upload the healthcheck file if it doesn't already exist.
|
|
131
|
+
* This enables read-only clients (HTTP) to verify connectivity.
|
|
108
132
|
*/
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
async uploadHealthcheck(): Promise<void> {
|
|
134
|
+
if (!this.isWritable()) {
|
|
135
|
+
this.log.trace('Cannot upload healthcheck: store is read-only');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
const path = this.healthcheckPath();
|
|
139
|
+
await (this.store as FileStore).save(path, Buffer.from(HEALTHCHECK_CONTENT));
|
|
140
|
+
this.log.debug(`Uploaded healthcheck file to ${path}`);
|
|
113
141
|
}
|
|
114
142
|
|
|
115
143
|
/**
|