@milaboratories/pl-middle-layer 1.64.28 → 1.64.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,7 +32,9 @@ async function initDriverKit(pl, workdir, frontendDownloadPath, _ops) {
32
32
  blobDriver,
33
33
  logger: ops.logger,
34
34
  spillPath: ops.pframesSpillPath,
35
- options: ops.pFrameDriverOps
35
+ cachePath: ops.parquetCachePath,
36
+ options: ops.pFrameDriverOps,
37
+ cacheOps: ops.parquetCacheOps
36
38
  }),
37
39
  frontendDriver: new _milaboratories_pl_drivers.DownloadUrlDriver(ops.logger, pl.httpDispatcher, frontendDownloadPath, signer, ops.frontendDownloadDriverOps)
38
40
  };
@@ -1 +1 @@
1
- {"version":3,"file":"driver_kit.cjs","names":["DefaultDriverKitOpsSettings","DefaultDriverKitOpsPaths","HmacSha256Signer","DownloadDriver","DownloadBlobToURLDriver","UploadDriver","LogsStreamDriver","LogsDriver","LsDriver","createPFrameDriver","DownloadUrlDriver"],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const pFrameDriver = await createPFrameDriver({\n blobDriver,\n logger: ops.logger,\n spillPath: ops.pframesSpillPath,\n options: ops.pFrameDriverOps,\n });\n\n const frontendDownloadDriver = new DownloadUrlDriver(\n ops.logger,\n pl.httpDispatcher,\n frontendDownloadPath,\n signer,\n ops.frontendDownloadDriverOps,\n );\n\n const driverKit = {\n blobDriver,\n blobToURLDriver: blobToURLDriver,\n logDriver,\n lsDriver,\n signer,\n uploadDriver,\n pFrameDriver,\n frontendDriver: frontendDownloadDriver,\n };\n\n const dispose = async () => {\n const disposePromises = Object.values(driverKit).flatMap((driver) =>\n isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],\n );\n await Promise.all(disposePromises);\n };\n\n return {\n ...driverKit,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;;;;AA6DA,eAAsB,cACpB,IACA,SACA,sBACA,MAC+B;CAC/B,MAAM,MAAoB;EACxB,GAAGA,YAAAA;EACH,GAAGC,YAAAA,yBAAyB,QAAQ;EACpC,GAAG;EACJ;CAED,MAAM,SAAS,IAAIC,2BAAAA,iBAAiB,IAAI,YAAY;CAEpD,MAAM,kBAAA,GAAA,2BAAA,sBAAsC,IAAI,QAAQ,IAAI,IAAI,iBAAiB;CACjF,MAAM,cAAA,GAAA,2BAAA,kBAA8B,IAAI,IAAI,OAAO;CACnD,MAAM,oBAAA,GAAA,2BAAA,wBAA0C,IAAI,IAAI,OAAO;CAC/D,MAAM,wBAAA,GAAA,2BAAA,4BAAkD,IAAI,IAAI,OAAO;CAEvE,MAAM,aAAa,MAAMC,2BAAAA,eAAe,KACtC,IAAI,QACJ,gBACA,YACA,IAAI,kBACJ,IAAI,6BACJ,QACA,IAAI,cACL;CAED,MAAM,kBAAkB,IAAIC,2BAAAA,wBAC1B,IAAI,QACJ,QACA,gBACA,IAAI,uBACJ,IAAI,2BACL;CAED,MAAM,eAAe,IAAIC,2BAAAA,aACvB,IAAI,QACJ,QACA,kBACA,sBACA,IAAI,gBACL;CACD,MAAM,mBAAmB,IAAIC,2BAAAA,iBAAiB,IAAI,QAAQ,YAAY,IAAI,mBAAmB;CA0B7F,MAAM,YAAY;EAChB;EACiB;EACjB,WA5BgB,IAAIC,2BAAAA,WAAW,IAAI,QAAQ,kBAAkB,WAAW;EA6BxE,UA5Be,MAAMC,2BAAAA,SAAS,KAC9B,IAAI,QACJ,IACA,QACA,IAAI,kBACJ,IAAI,wBACJ,IAAI,6BACL;EAsBC;EACA;EACA,cAtBmB,MAAMC,eAAAA,mBAAmB;GAC5C;GACA,QAAQ,IAAI;GACZ,WAAW,IAAI;GACf,SAAS,IAAI;GACd,CAAC;EAkBA,gBAhB6B,IAAIC,2BAAAA,kBACjC,IAAI,QACJ,GAAG,gBACH,sBACA,QACA,IAAI,0BACL;EAWA;CAED,MAAM,UAAU,YAAY;EAC1B,MAAM,kBAAkB,OAAO,OAAO,UAAU,CAAC,SAAS,YAAA,GAAA,wBAAA,mBACtC,OAAO,GAAG,CAAC,OAAO,OAAO,eAAe,CAAC,GAAG,EAAE,CACjE;AACD,QAAM,QAAQ,IAAI,gBAAgB;;AAGpC,QAAO;EACL,GAAG;EACH;GACC,OAAO,eAAe;EACxB"}
1
+ {"version":3,"file":"driver_kit.cjs","names":["DefaultDriverKitOpsSettings","DefaultDriverKitOpsPaths","HmacSha256Signer","DownloadDriver","DownloadBlobToURLDriver","UploadDriver","LogsStreamDriver","LogsDriver","LsDriver","createPFrameDriver","DownloadUrlDriver"],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const pFrameDriver = await createPFrameDriver({\n blobDriver,\n logger: ops.logger,\n spillPath: ops.pframesSpillPath,\n cachePath: ops.parquetCachePath,\n options: ops.pFrameDriverOps,\n cacheOps: ops.parquetCacheOps,\n });\n\n const frontendDownloadDriver = new DownloadUrlDriver(\n ops.logger,\n pl.httpDispatcher,\n frontendDownloadPath,\n signer,\n ops.frontendDownloadDriverOps,\n );\n\n const driverKit = {\n blobDriver,\n blobToURLDriver: blobToURLDriver,\n logDriver,\n lsDriver,\n signer,\n uploadDriver,\n pFrameDriver,\n frontendDriver: frontendDownloadDriver,\n };\n\n const dispose = async () => {\n const disposePromises = Object.values(driverKit).flatMap((driver) =>\n isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],\n );\n await Promise.all(disposePromises);\n };\n\n return {\n ...driverKit,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;;;;AA6DA,eAAsB,cACpB,IACA,SACA,sBACA,MAC+B;CAC/B,MAAM,MAAoB;EACxB,GAAGA,YAAAA;EACH,GAAGC,YAAAA,yBAAyB,QAAQ;EACpC,GAAG;EACJ;CAED,MAAM,SAAS,IAAIC,2BAAAA,iBAAiB,IAAI,YAAY;CAEpD,MAAM,kBAAA,GAAA,2BAAA,sBAAsC,IAAI,QAAQ,IAAI,IAAI,iBAAiB;CACjF,MAAM,cAAA,GAAA,2BAAA,kBAA8B,IAAI,IAAI,OAAO;CACnD,MAAM,oBAAA,GAAA,2BAAA,wBAA0C,IAAI,IAAI,OAAO;CAC/D,MAAM,wBAAA,GAAA,2BAAA,4BAAkD,IAAI,IAAI,OAAO;CAEvE,MAAM,aAAa,MAAMC,2BAAAA,eAAe,KACtC,IAAI,QACJ,gBACA,YACA,IAAI,kBACJ,IAAI,6BACJ,QACA,IAAI,cACL;CAED,MAAM,kBAAkB,IAAIC,2BAAAA,wBAC1B,IAAI,QACJ,QACA,gBACA,IAAI,uBACJ,IAAI,2BACL;CAED,MAAM,eAAe,IAAIC,2BAAAA,aACvB,IAAI,QACJ,QACA,kBACA,sBACA,IAAI,gBACL;CACD,MAAM,mBAAmB,IAAIC,2BAAAA,iBAAiB,IAAI,QAAQ,YAAY,IAAI,mBAAmB;CA4B7F,MAAM,YAAY;EAChB;EACiB;EACjB,WA9BgB,IAAIC,2BAAAA,WAAW,IAAI,QAAQ,kBAAkB,WAAW;EA+BxE,UA9Be,MAAMC,2BAAAA,SAAS,KAC9B,IAAI,QACJ,IACA,QACA,IAAI,kBACJ,IAAI,wBACJ,IAAI,6BACL;EAwBC;EACA;EACA,cAxBmB,MAAMC,eAAAA,mBAAmB;GAC5C;GACA,QAAQ,IAAI;GACZ,WAAW,IAAI;GACf,WAAW,IAAI;GACf,SAAS,IAAI;GACb,UAAU,IAAI;GACf,CAAC;EAkBA,gBAhB6B,IAAIC,2BAAAA,kBACjC,IAAI,QACJ,GAAG,gBACH,sBACA,QACA,IAAI,0BACL;EAWA;CAED,MAAM,UAAU,YAAY;EAC1B,MAAM,kBAAkB,OAAO,OAAO,UAAU,CAAC,SAAS,YAAA,GAAA,wBAAA,mBACtC,OAAO,GAAG,CAAC,OAAO,OAAO,eAAe,CAAC,GAAG,EAAE,CACjE;AACD,QAAM,QAAQ,IAAI,gBAAgB;;AAGpC,QAAO;EACL,GAAG;EACH;GACC,OAAO,eAAe;EACxB"}
@@ -31,7 +31,9 @@ async function initDriverKit(pl, workdir, frontendDownloadPath, _ops) {
31
31
  blobDriver,
32
32
  logger: ops.logger,
33
33
  spillPath: ops.pframesSpillPath,
34
- options: ops.pFrameDriverOps
34
+ cachePath: ops.parquetCachePath,
35
+ options: ops.pFrameDriverOps,
36
+ cacheOps: ops.parquetCacheOps
35
37
  }),
36
38
  frontendDriver: new DownloadUrlDriver(ops.logger, pl.httpDispatcher, frontendDownloadPath, signer, ops.frontendDownloadDriverOps)
37
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"driver_kit.js","names":[],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const pFrameDriver = await createPFrameDriver({\n blobDriver,\n logger: ops.logger,\n spillPath: ops.pframesSpillPath,\n options: ops.pFrameDriverOps,\n });\n\n const frontendDownloadDriver = new DownloadUrlDriver(\n ops.logger,\n pl.httpDispatcher,\n frontendDownloadPath,\n signer,\n ops.frontendDownloadDriverOps,\n );\n\n const driverKit = {\n blobDriver,\n blobToURLDriver: blobToURLDriver,\n logDriver,\n lsDriver,\n signer,\n uploadDriver,\n pFrameDriver,\n frontendDriver: frontendDownloadDriver,\n };\n\n const dispose = async () => {\n const disposePromises = Object.values(driverKit).flatMap((driver) =>\n isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],\n );\n await Promise.all(disposePromises);\n };\n\n return {\n ...driverKit,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;;;AA6DA,eAAsB,cACpB,IACA,SACA,sBACA,MAC+B;CAC/B,MAAM,MAAoB;EACxB,GAAG;EACH,GAAG,yBAAyB,QAAQ;EACpC,GAAG;EACJ;CAED,MAAM,SAAS,IAAI,iBAAiB,IAAI,YAAY;CAEpD,MAAM,iBAAiB,qBAAqB,IAAI,QAAQ,IAAI,IAAI,iBAAiB;CACjF,MAAM,aAAa,iBAAiB,IAAI,IAAI,OAAO;CACnD,MAAM,mBAAmB,uBAAuB,IAAI,IAAI,OAAO;CAC/D,MAAM,uBAAuB,2BAA2B,IAAI,IAAI,OAAO;CAEvE,MAAM,aAAa,MAAM,eAAe,KACtC,IAAI,QACJ,gBACA,YACA,IAAI,kBACJ,IAAI,6BACJ,QACA,IAAI,cACL;CAED,MAAM,kBAAkB,IAAI,wBAC1B,IAAI,QACJ,QACA,gBACA,IAAI,uBACJ,IAAI,2BACL;CAED,MAAM,eAAe,IAAI,aACvB,IAAI,QACJ,QACA,kBACA,sBACA,IAAI,gBACL;CACD,MAAM,mBAAmB,IAAI,iBAAiB,IAAI,QAAQ,YAAY,IAAI,mBAAmB;CA0B7F,MAAM,YAAY;EAChB;EACiB;EACjB,WA5BgB,IAAI,WAAW,IAAI,QAAQ,kBAAkB,WAAW;EA6BxE,UA5Be,MAAM,SAAS,KAC9B,IAAI,QACJ,IACA,QACA,IAAI,kBACJ,IAAI,wBACJ,IAAI,6BACL;EAsBC;EACA;EACA,cAtBmB,MAAM,mBAAmB;GAC5C;GACA,QAAQ,IAAI;GACZ,WAAW,IAAI;GACf,SAAS,IAAI;GACd,CAAC;EAkBA,gBAhB6B,IAAI,kBACjC,IAAI,QACJ,GAAG,gBACH,sBACA,QACA,IAAI,0BACL;EAWA;CAED,MAAM,UAAU,YAAY;EAC1B,MAAM,kBAAkB,OAAO,OAAO,UAAU,CAAC,SAAS,WACxD,kBAAkB,OAAO,GAAG,CAAC,OAAO,OAAO,eAAe,CAAC,GAAG,EAAE,CACjE;AACD,QAAM,QAAQ,IAAI,gBAAgB;;AAGpC,QAAO;EACL,GAAG;EACH;GACC,OAAO,eAAe;EACxB"}
1
+ {"version":3,"file":"driver_kit.js","names":[],"sources":["../../src/middle_layer/driver_kit.ts"],"sourcesContent":["import type { PlClient } from \"@milaboratories/pl-client\";\nimport type { InternalLsDriver } from \"@milaboratories/pl-drivers\";\nimport {\n createDownloadClient,\n createLogsClient,\n createUploadBlobClient,\n createUploadProgressClient,\n DownloadDriver,\n DownloadBlobToURLDriver,\n LogsDriver,\n LogsStreamDriver,\n LsDriver,\n UploadDriver,\n DownloadUrlDriver,\n} from \"@milaboratories/pl-drivers\";\nimport type * as Sdk from \"@milaboratories/pl-model-common\";\nimport type { Signer } from \"@milaboratories/ts-helpers\";\nimport { isAsyncDisposable } from \"@milaboratories/helpers\";\nimport { HmacSha256Signer } from \"@milaboratories/ts-helpers\";\nimport type { InternalPFrameDriver } from \"../pool\";\nimport { createPFrameDriver } from \"../pool\";\nimport type { DriverKitOps, DriverKitOpsConstructor } from \"./ops\";\nimport { DefaultDriverKitOpsPaths, DefaultDriverKitOpsSettings } from \"./ops\";\n\n/**\n * Drivers offered by the middle-layer for internal consumers,\n * like configuration rendering routines.\n *\n * This intertface is basically a version of the DriverKit from\n * UI SDK with extended API.\n * */\nexport interface MiddleLayerDriverKit extends Sdk.DriverKit, AsyncDisposable {\n /** Dispose the driver kit and all its resources. */\n dispose(): Promise<void>;\n\n // override with wider interface\n readonly blobDriver: DownloadDriver;\n // override with wider interface\n readonly blobToURLDriver: DownloadBlobToURLDriver;\n // override with wider interface\n readonly logDriver: LogsDriver;\n // override with wider interface\n readonly lsDriver: InternalLsDriver;\n // override with wider interface\n readonly pFrameDriver: InternalPFrameDriver;\n // override with wider interface\n readonly frontendDriver: DownloadUrlDriver;\n\n /**\n * Signer is initialized from local secret in drivers initialization routine,\n * so constitutes a part of the driver kit\n * */\n readonly signer: Signer;\n\n /**\n * Used to retrieve upload progress, and initiate upload porecesses driven by\n * upload requests from block outputs.\n * */\n readonly uploadDriver: UploadDriver;\n}\n\nexport async function initDriverKit(\n pl: PlClient,\n workdir: string,\n frontendDownloadPath: string,\n _ops: DriverKitOpsConstructor,\n): Promise<MiddleLayerDriverKit> {\n const ops: DriverKitOps = {\n ...DefaultDriverKitOpsSettings,\n ...DefaultDriverKitOpsPaths(workdir),\n ..._ops,\n };\n\n const signer = new HmacSha256Signer(ops.localSecret);\n\n const downloadClient = createDownloadClient(ops.logger, pl, ops.localProjections);\n const logsClient = createLogsClient(pl, ops.logger);\n const uploadBlobClient = createUploadBlobClient(pl, ops.logger);\n const uploadProgressClient = createUploadProgressClient(pl, ops.logger);\n\n const blobDriver = await DownloadDriver.init(\n ops.logger,\n downloadClient,\n logsClient,\n ops.blobDownloadPath,\n ops.blobDownloadRangesCachePath,\n signer,\n ops.blobDriverOps,\n );\n\n const blobToURLDriver = new DownloadBlobToURLDriver(\n ops.logger,\n signer,\n downloadClient,\n ops.downloadBlobToURLPath,\n ops.downloadBlobToURLDriverOps,\n );\n\n const uploadDriver = new UploadDriver(\n ops.logger,\n signer,\n uploadBlobClient,\n uploadProgressClient,\n ops.uploadDriverOps,\n );\n const logsStreamDriver = new LogsStreamDriver(ops.logger, logsClient, ops.logStreamDriverOps);\n const logDriver = new LogsDriver(ops.logger, logsStreamDriver, blobDriver);\n const lsDriver = await LsDriver.init(\n ops.logger,\n pl,\n signer,\n ops.localProjections,\n ops.openFileDialogCallback,\n ops.virtualLocalStoragesOverride,\n );\n\n const pFrameDriver = await createPFrameDriver({\n blobDriver,\n logger: ops.logger,\n spillPath: ops.pframesSpillPath,\n cachePath: ops.parquetCachePath,\n options: ops.pFrameDriverOps,\n cacheOps: ops.parquetCacheOps,\n });\n\n const frontendDownloadDriver = new DownloadUrlDriver(\n ops.logger,\n pl.httpDispatcher,\n frontendDownloadPath,\n signer,\n ops.frontendDownloadDriverOps,\n );\n\n const driverKit = {\n blobDriver,\n blobToURLDriver: blobToURLDriver,\n logDriver,\n lsDriver,\n signer,\n uploadDriver,\n pFrameDriver,\n frontendDriver: frontendDownloadDriver,\n };\n\n const dispose = async () => {\n const disposePromises = Object.values(driverKit).flatMap((driver) =>\n isAsyncDisposable(driver) ? [driver[Symbol.asyncDispose]()] : [],\n );\n await Promise.all(disposePromises);\n };\n\n return {\n ...driverKit,\n dispose,\n [Symbol.asyncDispose]: dispose,\n };\n}\n"],"mappings":";;;;;;;AA6DA,eAAsB,cACpB,IACA,SACA,sBACA,MAC+B;CAC/B,MAAM,MAAoB;EACxB,GAAG;EACH,GAAG,yBAAyB,QAAQ;EACpC,GAAG;EACJ;CAED,MAAM,SAAS,IAAI,iBAAiB,IAAI,YAAY;CAEpD,MAAM,iBAAiB,qBAAqB,IAAI,QAAQ,IAAI,IAAI,iBAAiB;CACjF,MAAM,aAAa,iBAAiB,IAAI,IAAI,OAAO;CACnD,MAAM,mBAAmB,uBAAuB,IAAI,IAAI,OAAO;CAC/D,MAAM,uBAAuB,2BAA2B,IAAI,IAAI,OAAO;CAEvE,MAAM,aAAa,MAAM,eAAe,KACtC,IAAI,QACJ,gBACA,YACA,IAAI,kBACJ,IAAI,6BACJ,QACA,IAAI,cACL;CAED,MAAM,kBAAkB,IAAI,wBAC1B,IAAI,QACJ,QACA,gBACA,IAAI,uBACJ,IAAI,2BACL;CAED,MAAM,eAAe,IAAI,aACvB,IAAI,QACJ,QACA,kBACA,sBACA,IAAI,gBACL;CACD,MAAM,mBAAmB,IAAI,iBAAiB,IAAI,QAAQ,YAAY,IAAI,mBAAmB;CA4B7F,MAAM,YAAY;EAChB;EACiB;EACjB,WA9BgB,IAAI,WAAW,IAAI,QAAQ,kBAAkB,WAAW;EA+BxE,UA9Be,MAAM,SAAS,KAC9B,IAAI,QACJ,IACA,QACA,IAAI,kBACJ,IAAI,wBACJ,IAAI,6BACL;EAwBC;EACA;EACA,cAxBmB,MAAM,mBAAmB;GAC5C;GACA,QAAQ,IAAI;GACZ,WAAW,IAAI;GACf,WAAW,IAAI;GACf,SAAS,IAAI;GACb,UAAU,IAAI;GACf,CAAC;EAkBA,gBAhB6B,IAAI,kBACjC,IAAI,QACJ,GAAG,gBACH,sBACA,QACA,IAAI,0BACL;EAWA;CAED,MAAM,UAAU,YAAY;EAC1B,MAAM,kBAAkB,OAAO,OAAO,UAAU,CAAC,SAAS,WACxD,kBAAkB,OAAO,GAAG,CAAC,OAAO,OAAO,eAAe,CAAC,GAAG,EAAE,CACjE;AACD,QAAM,QAAQ,IAAI,gBAAgB;;AAGpC,QAAO;EACL,GAAG;EACH;GACC,OAAO,eAAe;EACxB"}
@@ -32,14 +32,20 @@ const DefaultDriverKitOpsSettings = {
32
32
  pollingInterval: 1e3,
33
33
  stopPollingDelay: 1e3
34
34
  },
35
- pFrameDriverOps: require_driver.PFrameDriverOpsDefaults
35
+ pFrameDriverOps: require_driver.PFrameDriverOpsDefaults,
36
+ parquetCacheOps: {
37
+ maxSizeBytes: 8 * 1024 * 1024 * 1024,
38
+ admissionFraction: .2,
39
+ maxFilesTracked: 5e4
40
+ }
36
41
  };
37
42
  function DefaultDriverKitOpsPaths(workDir) {
38
43
  return {
39
44
  blobDownloadPath: node_path.default.join(workDir, "download"),
40
45
  blobDownloadRangesCachePath: node_path.default.join(workDir, "downloadRangesCache"),
41
46
  downloadBlobToURLPath: node_path.default.join(workDir, "downloadToURL"),
42
- pframesSpillPath: node_path.default.join(workDir, "pframes")
47
+ pframesSpillPath: node_path.default.join(workDir, "pframes"),
48
+ parquetCachePath: node_path.default.join(workDir, "parquetCache")
43
49
  };
44
50
  }
45
51
  /** Some defaults fot MiddleLayerOps. */
@@ -1 +1 @@
1
- {"version":3,"file":"ops.cjs","names":["ConsoleLoggerAdapter","PFrameDriverOpsDefaults","path"],"sources":["../../src/middle_layer/ops.ts"],"sourcesContent":["import type { TemporalSynchronizedTreeOps } from \"./types\";\nimport type {\n DownloadBlobToURLDriverOps,\n DownloadDriverOps,\n DownloadUrlDriverOps,\n OpenFileDialogCallback,\n VirtualLocalStorageSpec,\n} from \"@milaboratories/pl-drivers\";\nimport type { UploadDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { LogsStreamDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter } from \"@milaboratories/ts-helpers\";\nimport type { LocalStorageProjection } from \"@milaboratories/pl-drivers\";\nimport path from \"node:path\";\nimport { PFrameDriverOpsDefaults, type PFrameDriverOps } from \"../pool\";\n\n/** Paths part of {@link DriverKitOps}. */\nexport type DriverKitOpsPaths = {\n /** Common root where to put downloaded blobs / downloaded blob cache */\n readonly blobDownloadPath: string;\n\n /** Common root for a cache for range queries. */\n readonly blobDownloadRangesCachePath: string;\n\n /** Common root where to put downloaded blobs with */\n readonly downloadBlobToURLPath: string;\n\n /**\n * List of pl storages that have projections in local file system.\n *\n * This option affect two drivers:\n *\n * (1) LS driver generates \"index\" handles instead of \"upload\" for paths inside those locations\n *\n * (2) Download driver directly serves content retrieval requests for blobs from listed storages,\n * and don't apply any caching for such blobs (i.e. preventing duplication of files for Downloaded\n * type handles, making OnDemand and Downloaded handles equivalent)\n *\n * */\n readonly localProjections: LocalStorageProjection[];\n\n /**\n * List of virtual storages that will allow homogeneous access to local FSs through LS API.\n * If undefined, default list will be created.\n * */\n readonly virtualLocalStoragesOverride?: VirtualLocalStorageSpec[];\n\n /** Path to the directory where pframes will spill temporary files and store materialized views */\n readonly pframesSpillPath: string;\n};\n\n/** Options required to initialize full set of middle layer driver kit */\nexport type DriverKitOpsSettings = {\n //\n // Common\n //\n\n readonly logger: MiLogger;\n\n //\n // Signer\n //\n\n /**\n * Local secret, that is used to sign and verify different pieces of information\n * that can be used to access local data, like local paths for ongoing uploads.\n *\n * Use {@link MiddleLayer.generateLocalSecret} to generate sufficiently random string.\n * */\n readonly localSecret: string;\n\n //\n // Blob Driver\n //\n\n /**\n * Settings related to the download driver making operations with blobs. This driver is also used\n * to download logs when source process terminates and log terns into a blob\n */\n readonly blobDriverOps: DownloadDriverOps;\n\n //\n // Frontend Driver\n //\n\n /** Settings related to the frontend driver that downloads frontends. */\n readonly frontendDownloadDriverOps: DownloadUrlDriverOps;\n\n //\n // Blob To URL Driver\n //\n\n readonly downloadBlobToURLDriverOps: DownloadBlobToURLDriverOps;\n\n //\n // Upload Driver\n //\n\n /**\n * Settings related to the upload driver that actually performs upload and helps render upload\n * and indexing progresses from related pl resources.\n * */\n readonly uploadDriverOps: UploadDriverOps;\n\n //\n // Log streaming ops\n // (static logs are served via the blob driver)\n //\n\n /** Settings related to the streaming log driver */\n readonly logStreamDriverOps: LogsStreamDriverOps;\n\n //\n // LS Driver\n //\n\n /**\n * Callback to access system file open dialog, must be provided by the environment,\n * to allow for {@link showOpenSingleFileDialog} / {@link showOpenMultipleFilesDialog}\n * calls from the UI.\n */\n readonly openFileDialogCallback: OpenFileDialogCallback;\n\n //\n // PFrame Driver\n //\n\n /** Settings related to the PFrame driver */\n readonly pFrameDriverOps: PFrameDriverOps;\n};\n\nexport type DriverKitOps = DriverKitOpsPaths & DriverKitOpsSettings;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultDriverKitOpsSettings: Pick<\n DriverKitOpsSettings,\n | \"logger\"\n | \"blobDriverOps\"\n | \"frontendDownloadDriverOps\"\n | \"downloadBlobToURLDriverOps\"\n | \"uploadDriverOps\"\n | \"logStreamDriverOps\"\n | \"pFrameDriverOps\"\n> = {\n logger: new ConsoleLoggerAdapter(),\n blobDriverOps: {\n cacheSoftSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n rangesCacheMaxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n nConcurrentDownloads: 10,\n },\n frontendDownloadDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n withGunzip: true,\n nConcurrentDownloads: 50,\n },\n downloadBlobToURLDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n nConcurrentDownloads: 10,\n },\n uploadDriverOps: {\n nConcurrentPartUploads: 10,\n nConcurrentGetProgresses: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n logStreamDriverOps: {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n pFrameDriverOps: PFrameDriverOpsDefaults,\n};\n\nexport function DefaultDriverKitOpsPaths(\n workDir: string,\n): Pick<\n DriverKitOpsPaths,\n \"blobDownloadPath\" | \"blobDownloadRangesCachePath\" | \"downloadBlobToURLPath\" | \"pframesSpillPath\"\n> {\n return {\n blobDownloadPath: path.join(workDir, \"download\"),\n blobDownloadRangesCachePath: path.join(workDir, \"downloadRangesCache\"),\n downloadBlobToURLPath: path.join(workDir, \"downloadToURL\"),\n pframesSpillPath: path.join(workDir, \"pframes\"),\n };\n}\n\n/** Fields with default values are marked as optional here. */\nexport type DriverKitOpsConstructor = Omit<\n DriverKitOpsSettings,\n keyof typeof DefaultDriverKitOpsSettings\n> &\n Partial<typeof DefaultDriverKitOpsSettings> &\n Omit<DriverKitOpsPaths, keyof ReturnType<typeof DefaultDriverKitOpsPaths>> &\n Partial<ReturnType<typeof DefaultDriverKitOpsPaths>>;\n\nexport type MiddleLayerOpsPaths = DriverKitOpsPaths & {\n /** Common root where to put frontend code. */\n readonly frontendDownloadPath: string;\n};\n\n/** Debug options for middle layer. */\nexport type MiddleLayerDebugOptions = {\n /** If true, will dump initial tree state to the file with root resource id as name. */\n dumpInitialTreeState: boolean;\n};\n\n/** Configuration controlling different aspects of middle layer behaviour. */\nexport type MiddleLayerOpsSettings = DriverKitOpsSettings & {\n /** Debug options. */\n readonly debugOps: MiddleLayerDebugOptions;\n\n /** Contain temporal options controlling how often should pl trees be\n * synchronized with the pl server. */\n readonly defaultTreeOptions: TemporalSynchronizedTreeOps;\n\n /** Defines interval in milliseconds for running periodic project maintenance job.\n * Project maintenance includes background staging rendering and cached outputs cleanup. */\n readonly projectRefreshInterval: number;\n\n /** How often to check for dev block updates */\n readonly devBlockUpdateRecheckInterval: number;\n\n /** Prioritize this channel if update is available in this block */\n readonly preferredUpdateChannel?: string;\n};\n\nexport type MiddleLayerOps = MiddleLayerOpsSettings & MiddleLayerOpsPaths;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultMiddleLayerOpsSettings: Pick<\n MiddleLayerOps,\n | keyof typeof DefaultDriverKitOpsSettings\n | \"defaultTreeOptions\"\n | \"projectRefreshInterval\"\n | \"devBlockUpdateRecheckInterval\"\n | \"debugOps\"\n> = {\n ...DefaultDriverKitOpsSettings,\n defaultTreeOptions: {\n pollingInterval: 200,\n stopPollingDelay: 2500,\n initialTreeLoadingTimeout: 100 * 60 * 60 * 1000, // disable timeout for loading project tree (100 hours)\n },\n debugOps: {\n dumpInitialTreeState: false,\n },\n devBlockUpdateRecheckInterval: 1000,\n projectRefreshInterval: 2000,\n};\n\nexport function DefaultMiddleLayerOpsPaths(\n workDir: string,\n): Pick<\n MiddleLayerOpsPaths,\n keyof ReturnType<typeof DefaultDriverKitOpsPaths> | \"frontendDownloadPath\"\n> {\n return {\n ...DefaultDriverKitOpsPaths(workDir),\n frontendDownloadPath: path.join(workDir, \"frontend\"),\n };\n}\n\nexport type MiddleLayerOpsConstructor = Omit<\n MiddleLayerOpsSettings,\n keyof typeof DefaultMiddleLayerOpsSettings\n> &\n Partial<typeof DefaultMiddleLayerOpsSettings> &\n Omit<MiddleLayerOpsPaths, keyof Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>> &\n Partial<Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>>;\n"],"mappings":";;;;;;;AAsIA,MAAa,8BAST;CACF,QAAQ,4CAAIA,sBAAsB;CAClC,eAAe;EACb,oBAAoB,IAAI,OAAO,OAAO;EACtC,yBAAyB,IAAI,OAAO,OAAO;EAC3C,sBAAsB;EACvB;CACD,2BAA2B;EACzB,oBAAoB,IAAI,OAAO,OAAO;EACtC,YAAY;EACZ,sBAAsB;EACvB;CACD,4BAA4B;EAC1B,oBAAoB,IAAI,OAAO,OAAO;EACtC,sBAAsB;EACvB;CACD,iBAAiB;EACf,wBAAwB;EACxB,0BAA0B;EAC1B,iBAAiB;EACjB,kBAAkB;EACnB;CACD,oBAAoB;EAClB,oBAAoB;EACpB,iBAAiB;EACjB,kBAAkB;EACnB;CACD,iBAAiBC,eAAAA;CAClB;AAED,SAAgB,yBACd,SAIA;AACA,QAAO;EACL,kBAAkBC,UAAAA,QAAK,KAAK,SAAS,WAAW;EAChD,6BAA6BA,UAAAA,QAAK,KAAK,SAAS,sBAAsB;EACtE,uBAAuBA,UAAAA,QAAK,KAAK,SAAS,gBAAgB;EAC1D,kBAAkBA,UAAAA,QAAK,KAAK,SAAS,UAAU;EAChD;;;AA8CH,MAAa,gCAOT;CACF,GAAG;CACH,oBAAoB;EAClB,iBAAiB;EACjB,kBAAkB;EAClB,2BAA2B,MAAW,KAAK;EAC5C;CACD,UAAU,EACR,sBAAsB,OACvB;CACD,+BAA+B;CAC/B,wBAAwB;CACzB;AAED,SAAgB,2BACd,SAIA;AACA,QAAO;EACL,GAAG,yBAAyB,QAAQ;EACpC,sBAAsBA,UAAAA,QAAK,KAAK,SAAS,WAAW;EACrD"}
1
+ {"version":3,"file":"ops.cjs","names":["ConsoleLoggerAdapter","PFrameDriverOpsDefaults","path"],"sources":["../../src/middle_layer/ops.ts"],"sourcesContent":["import type { TemporalSynchronizedTreeOps } from \"./types\";\nimport type {\n DownloadBlobToURLDriverOps,\n DownloadDriverOps,\n DownloadUrlDriverOps,\n OpenFileDialogCallback,\n VirtualLocalStorageSpec,\n} from \"@milaboratories/pl-drivers\";\nimport type { UploadDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { LogsStreamDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter } from \"@milaboratories/ts-helpers\";\nimport type { LocalStorageProjection } from \"@milaboratories/pl-drivers\";\nimport path from \"node:path\";\nimport { PFrameDriverOpsDefaults, type PFrameDriverOps, type ParquetCacheOps } from \"../pool\";\n\n/** Paths part of {@link DriverKitOps}. */\nexport type DriverKitOpsPaths = {\n /** Common root where to put downloaded blobs / downloaded blob cache */\n readonly blobDownloadPath: string;\n\n /** Common root for a cache for range queries. */\n readonly blobDownloadRangesCachePath: string;\n\n /** Common root where to put downloaded blobs with */\n readonly downloadBlobToURLPath: string;\n\n /**\n * List of pl storages that have projections in local file system.\n *\n * This option affect two drivers:\n *\n * (1) LS driver generates \"index\" handles instead of \"upload\" for paths inside those locations\n *\n * (2) Download driver directly serves content retrieval requests for blobs from listed storages,\n * and don't apply any caching for such blobs (i.e. preventing duplication of files for Downloaded\n * type handles, making OnDemand and Downloaded handles equivalent)\n *\n * */\n readonly localProjections: LocalStorageProjection[];\n\n /**\n * List of virtual storages that will allow homogeneous access to local FSs through LS API.\n * If undefined, default list will be created.\n * */\n readonly virtualLocalStoragesOverride?: VirtualLocalStorageSpec[];\n\n /** Path to the directory where pframes will spill temporary files and store materialized views */\n readonly pframesSpillPath: string;\n\n /**\n * Path to the directory backing the persistent parquet blob cache. Unlike\n * {@link pframesSpillPath} this directory is NOT emptied on startup - the cache\n * (and its lifetime counters) is meant to survive restarts.\n */\n readonly parquetCachePath: string;\n};\n\n/** Options required to initialize full set of middle layer driver kit */\nexport type DriverKitOpsSettings = {\n //\n // Common\n //\n\n readonly logger: MiLogger;\n\n //\n // Signer\n //\n\n /**\n * Local secret, that is used to sign and verify different pieces of information\n * that can be used to access local data, like local paths for ongoing uploads.\n *\n * Use {@link MiddleLayer.generateLocalSecret} to generate sufficiently random string.\n * */\n readonly localSecret: string;\n\n //\n // Blob Driver\n //\n\n /**\n * Settings related to the download driver making operations with blobs. This driver is also used\n * to download logs when source process terminates and log terns into a blob\n */\n readonly blobDriverOps: DownloadDriverOps;\n\n //\n // Frontend Driver\n //\n\n /** Settings related to the frontend driver that downloads frontends. */\n readonly frontendDownloadDriverOps: DownloadUrlDriverOps;\n\n //\n // Blob To URL Driver\n //\n\n readonly downloadBlobToURLDriverOps: DownloadBlobToURLDriverOps;\n\n //\n // Upload Driver\n //\n\n /**\n * Settings related to the upload driver that actually performs upload and helps render upload\n * and indexing progresses from related pl resources.\n * */\n readonly uploadDriverOps: UploadDriverOps;\n\n //\n // Log streaming ops\n // (static logs are served via the blob driver)\n //\n\n /** Settings related to the streaming log driver */\n readonly logStreamDriverOps: LogsStreamDriverOps;\n\n //\n // LS Driver\n //\n\n /**\n * Callback to access system file open dialog, must be provided by the environment,\n * to allow for {@link showOpenSingleFileDialog} / {@link showOpenMultipleFilesDialog}\n * calls from the UI.\n */\n readonly openFileDialogCallback: OpenFileDialogCallback;\n\n //\n // PFrame Driver\n //\n\n /** Settings related to the PFrame driver */\n readonly pFrameDriverOps: PFrameDriverOps;\n\n /**\n * Tuning for the persistent parquet blob cache. Its directory is\n * {@link DriverKitOpsPaths.parquetCachePath}; this carries only the\n * size/eviction knobs.\n */\n readonly parquetCacheOps: ParquetCacheOps;\n};\n\nexport type DriverKitOps = DriverKitOpsPaths & DriverKitOpsSettings;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultDriverKitOpsSettings: Pick<\n DriverKitOpsSettings,\n | \"logger\"\n | \"blobDriverOps\"\n | \"frontendDownloadDriverOps\"\n | \"downloadBlobToURLDriverOps\"\n | \"uploadDriverOps\"\n | \"logStreamDriverOps\"\n | \"pFrameDriverOps\"\n | \"parquetCacheOps\"\n> = {\n logger: new ConsoleLoggerAdapter(),\n blobDriverOps: {\n cacheSoftSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n rangesCacheMaxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n nConcurrentDownloads: 10,\n },\n frontendDownloadDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n withGunzip: true,\n nConcurrentDownloads: 50,\n },\n downloadBlobToURLDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n nConcurrentDownloads: 10,\n },\n uploadDriverOps: {\n nConcurrentPartUploads: 10,\n nConcurrentGetProgresses: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n logStreamDriverOps: {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n pFrameDriverOps: PFrameDriverOpsDefaults,\n parquetCacheOps: {\n maxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n admissionFraction: 0.2, // a single file may occupy at most 20% of the budget\n maxFilesTracked: 50_000, // ghost (previously-evicted) files remembered to guide readmission\n },\n};\n\nexport function DefaultDriverKitOpsPaths(\n workDir: string,\n): Pick<\n DriverKitOpsPaths,\n | \"blobDownloadPath\"\n | \"blobDownloadRangesCachePath\"\n | \"downloadBlobToURLPath\"\n | \"pframesSpillPath\"\n | \"parquetCachePath\"\n> {\n return {\n blobDownloadPath: path.join(workDir, \"download\"),\n blobDownloadRangesCachePath: path.join(workDir, \"downloadRangesCache\"),\n downloadBlobToURLPath: path.join(workDir, \"downloadToURL\"),\n pframesSpillPath: path.join(workDir, \"pframes\"),\n parquetCachePath: path.join(workDir, \"parquetCache\"),\n };\n}\n\n/** Fields with default values are marked as optional here. */\nexport type DriverKitOpsConstructor = Omit<\n DriverKitOpsSettings,\n keyof typeof DefaultDriverKitOpsSettings\n> &\n Partial<typeof DefaultDriverKitOpsSettings> &\n Omit<DriverKitOpsPaths, keyof ReturnType<typeof DefaultDriverKitOpsPaths>> &\n Partial<ReturnType<typeof DefaultDriverKitOpsPaths>>;\n\nexport type MiddleLayerOpsPaths = DriverKitOpsPaths & {\n /** Common root where to put frontend code. */\n readonly frontendDownloadPath: string;\n};\n\n/** Debug options for middle layer. */\nexport type MiddleLayerDebugOptions = {\n /** If true, will dump initial tree state to the file with root resource id as name. */\n dumpInitialTreeState: boolean;\n};\n\n/** Configuration controlling different aspects of middle layer behaviour. */\nexport type MiddleLayerOpsSettings = DriverKitOpsSettings & {\n /** Debug options. */\n readonly debugOps: MiddleLayerDebugOptions;\n\n /** Contain temporal options controlling how often should pl trees be\n * synchronized with the pl server. */\n readonly defaultTreeOptions: TemporalSynchronizedTreeOps;\n\n /** Defines interval in milliseconds for running periodic project maintenance job.\n * Project maintenance includes background staging rendering and cached outputs cleanup. */\n readonly projectRefreshInterval: number;\n\n /** How often to check for dev block updates */\n readonly devBlockUpdateRecheckInterval: number;\n\n /** Prioritize this channel if update is available in this block */\n readonly preferredUpdateChannel?: string;\n};\n\nexport type MiddleLayerOps = MiddleLayerOpsSettings & MiddleLayerOpsPaths;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultMiddleLayerOpsSettings: Pick<\n MiddleLayerOps,\n | keyof typeof DefaultDriverKitOpsSettings\n | \"defaultTreeOptions\"\n | \"projectRefreshInterval\"\n | \"devBlockUpdateRecheckInterval\"\n | \"debugOps\"\n> = {\n ...DefaultDriverKitOpsSettings,\n defaultTreeOptions: {\n pollingInterval: 200,\n stopPollingDelay: 2500,\n initialTreeLoadingTimeout: 100 * 60 * 60 * 1000, // disable timeout for loading project tree (100 hours)\n },\n debugOps: {\n dumpInitialTreeState: false,\n },\n devBlockUpdateRecheckInterval: 1000,\n projectRefreshInterval: 2000,\n};\n\nexport function DefaultMiddleLayerOpsPaths(\n workDir: string,\n): Pick<\n MiddleLayerOpsPaths,\n keyof ReturnType<typeof DefaultDriverKitOpsPaths> | \"frontendDownloadPath\"\n> {\n return {\n ...DefaultDriverKitOpsPaths(workDir),\n frontendDownloadPath: path.join(workDir, \"frontend\"),\n };\n}\n\nexport type MiddleLayerOpsConstructor = Omit<\n MiddleLayerOpsSettings,\n keyof typeof DefaultMiddleLayerOpsSettings\n> &\n Partial<typeof DefaultMiddleLayerOpsSettings> &\n Omit<MiddleLayerOpsPaths, keyof Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>> &\n Partial<Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>>;\n"],"mappings":";;;;;;;AAoJA,MAAa,8BAUT;CACF,QAAQ,4CAAIA,sBAAsB;CAClC,eAAe;EACb,oBAAoB,IAAI,OAAO,OAAO;EACtC,yBAAyB,IAAI,OAAO,OAAO;EAC3C,sBAAsB;EACvB;CACD,2BAA2B;EACzB,oBAAoB,IAAI,OAAO,OAAO;EACtC,YAAY;EACZ,sBAAsB;EACvB;CACD,4BAA4B;EAC1B,oBAAoB,IAAI,OAAO,OAAO;EACtC,sBAAsB;EACvB;CACD,iBAAiB;EACf,wBAAwB;EACxB,0BAA0B;EAC1B,iBAAiB;EACjB,kBAAkB;EACnB;CACD,oBAAoB;EAClB,oBAAoB;EACpB,iBAAiB;EACjB,kBAAkB;EACnB;CACD,iBAAiBC,eAAAA;CACjB,iBAAiB;EACf,cAAc,IAAI,OAAO,OAAO;EAChC,mBAAmB;EACnB,iBAAiB;EAClB;CACF;AAED,SAAgB,yBACd,SAQA;AACA,QAAO;EACL,kBAAkBC,UAAAA,QAAK,KAAK,SAAS,WAAW;EAChD,6BAA6BA,UAAAA,QAAK,KAAK,SAAS,sBAAsB;EACtE,uBAAuBA,UAAAA,QAAK,KAAK,SAAS,gBAAgB;EAC1D,kBAAkBA,UAAAA,QAAK,KAAK,SAAS,UAAU;EAC/C,kBAAkBA,UAAAA,QAAK,KAAK,SAAS,eAAe;EACrD;;;AA8CH,MAAa,gCAOT;CACF,GAAG;CACH,oBAAoB;EAClB,iBAAiB;EACjB,kBAAkB;EAClB,2BAA2B,MAAW,KAAK;EAC5C;CACD,UAAU,EACR,sBAAsB,OACvB;CACD,+BAA+B;CAC/B,wBAAwB;CACzB;AAED,SAAgB,2BACd,SAIA;AACA,QAAO;EACL,GAAG,yBAAyB,QAAQ;EACpC,sBAAsBA,UAAAA,QAAK,KAAK,SAAS,WAAW;EACrD"}
@@ -1,5 +1,5 @@
1
1
  import { TemporalSynchronizedTreeOps } from "./types.js";
2
- import { PFrameDriverOps } from "../pool/driver.js";
2
+ import { PFrameDriverOps, ParquetCacheOps } from "../pool/driver.js";
3
3
  import { MiLogger } from "@milaboratories/ts-helpers";
4
4
  import { DownloadBlobToURLDriverOps, DownloadDriverOps, DownloadUrlDriverOps, LocalStorageProjection, LogsStreamDriverOps, OpenFileDialogCallback, UploadDriverOps, VirtualLocalStorageSpec } from "@milaboratories/pl-drivers";
5
5
 
@@ -28,6 +28,12 @@ type DriverKitOpsPaths = {
28
28
  * */
29
29
  readonly virtualLocalStoragesOverride?: VirtualLocalStorageSpec[]; /** Path to the directory where pframes will spill temporary files and store materialized views */
30
30
  readonly pframesSpillPath: string;
31
+ /**
32
+ * Path to the directory backing the persistent parquet blob cache. Unlike
33
+ * {@link pframesSpillPath} this directory is NOT emptied on startup - the cache
34
+ * (and its lifetime counters) is meant to survive restarts.
35
+ */
36
+ readonly parquetCachePath: string;
31
37
  };
32
38
  /** Options required to initialize full set of middle layer driver kit */
33
39
  type DriverKitOpsSettings = {
@@ -59,11 +65,17 @@ type DriverKitOpsSettings = {
59
65
  */
60
66
  readonly openFileDialogCallback: OpenFileDialogCallback; /** Settings related to the PFrame driver */
61
67
  readonly pFrameDriverOps: PFrameDriverOps;
68
+ /**
69
+ * Tuning for the persistent parquet blob cache. Its directory is
70
+ * {@link DriverKitOpsPaths.parquetCachePath}; this carries only the
71
+ * size/eviction knobs.
72
+ */
73
+ readonly parquetCacheOps: ParquetCacheOps;
62
74
  };
63
75
  type DriverKitOps = DriverKitOpsPaths & DriverKitOpsSettings;
64
76
  /** Some defaults fot MiddleLayerOps. */
65
- declare const DefaultDriverKitOpsSettings: Pick<DriverKitOpsSettings, "logger" | "blobDriverOps" | "frontendDownloadDriverOps" | "downloadBlobToURLDriverOps" | "uploadDriverOps" | "logStreamDriverOps" | "pFrameDriverOps">;
66
- declare function DefaultDriverKitOpsPaths(workDir: string): Pick<DriverKitOpsPaths, "blobDownloadPath" | "blobDownloadRangesCachePath" | "downloadBlobToURLPath" | "pframesSpillPath">;
77
+ declare const DefaultDriverKitOpsSettings: Pick<DriverKitOpsSettings, "logger" | "blobDriverOps" | "frontendDownloadDriverOps" | "downloadBlobToURLDriverOps" | "uploadDriverOps" | "logStreamDriverOps" | "pFrameDriverOps" | "parquetCacheOps">;
78
+ declare function DefaultDriverKitOpsPaths(workDir: string): Pick<DriverKitOpsPaths, "blobDownloadPath" | "blobDownloadRangesCachePath" | "downloadBlobToURLPath" | "pframesSpillPath" | "parquetCachePath">;
67
79
  /** Fields with default values are marked as optional here. */
68
80
  type DriverKitOpsConstructor = Omit<DriverKitOpsSettings, keyof typeof DefaultDriverKitOpsSettings> & Partial<typeof DefaultDriverKitOpsSettings> & Omit<DriverKitOpsPaths, keyof ReturnType<typeof DefaultDriverKitOpsPaths>> & Partial<ReturnType<typeof DefaultDriverKitOpsPaths>>;
69
81
  type MiddleLayerOpsPaths = DriverKitOpsPaths & {
@@ -1 +1 @@
1
- {"version":3,"file":"ops.d.ts","names":[],"sources":["../../src/middle_layer/ops.ts"],"mappings":";;;;;;AAiBA;AAAA,KAAY,iBAAA;mFAED,gBAAA;EAAA,SAGA,2BAAA,UAGA;EAAA,SAAA,qBAAA;EAckB;;;;;;AAa7B;;;;;;EAb6B,SAAlB,gBAAA,EAAkB,sBAAA;EA+DD;;;;EAAA,SAzDjB,4BAAA,GAA+B,uBAAA,IAmFC;EAAA,SAhFhC,gBAAA;AAAA;;KAIC,oBAAA;EAAA,SAKD,MAAA,EAAQ,QAAA;EA6BR;;;;;;EAAA,SAjBA,WAAA;EAyCoB;;;;EAAA,SA/BpB,aAAA,EAAe,iBAAA,EAiDiB;EAAA,SA1ChC,yBAAA,EAA2B,oBAAA;EAAA,SAM3B,0BAAA,EAA4B,0BAAA;EAuCf;;;;EAAA,SA7Bb,eAAA,EAAiB,eAAA,EAqE3B;EAAA,SA7DU,kBAAA,EAAoB,mBAAA;EAwBW;;AAuC1C;;;EAvC0C,SAb/B,sBAAA,EAAwB,sBAAA,EAqDjC;EAAA,SA9CS,eAAA,EAAiB,eAAA;AAAA;AAAA,KAGhB,YAAA,GAAe,iBAAA,GAAoB,oBAAA;;cAGlC,2BAAA,EAA6B,IAAA,CACxC,oBAAA;AAAA,iBAsCc,wBAAA,CACd,OAAA,WACC,IAAA,CACD,iBAAA;;KAYU,uBAAA,GAA0B,IAAA,CACpC,oBAAA,eACa,2BAAA,IAEb,OAAA,QAAe,2BAAA,IACf,IAAA,CAAK,iBAAA,QAAyB,UAAA,QAAkB,wBAAA,KAChD,OAAA,CAAQ,UAAA,QAAkB,wBAAA;AAAA,KAEhB,mBAAA,GAAsB,iBAAA;EARI,uDAU3B,oBAAA;AAAA;;KAIC,uBAAA;EAToB,uFAW9B,oBAAA;AAAA;;KAIU,sBAAA,GAAyB,oBAAA;EAd5B,8BAgBE,QAAA,EAAU,uBAAA;EAtBiB;;EAAA,SA0B3B,kBAAA,EAAoB,2BAAA;EAtB7B;;EAAA,SA0BS,sBAAA,UAzBJ;EAAA,SA4BI,6BAAA,UA5BuC;EAAA,SA+BvC,sBAAA;AAAA;AAAA,KAGC,cAAA,GAAiB,sBAAA,GAAyB,mBAAA;;cAGzC,6BAAA,EAA+B,IAAA,CAC1C,cAAA,eACe,2BAAA;AAAA,iBAmBD,0BAAA,CACd,OAAA,WACC,IAAA,CACD,mBAAA,QACM,UAAA,QAAkB,wBAAA;AAAA,KAQd,yBAAA,GAA4B,IAAA,CACtC,sBAAA,eACa,6BAAA,IAEb,OAAA,QAAe,6BAAA,IACf,IAAA,CAAK,mBAAA,QAA2B,OAAA,CAAQ,UAAA,QAAkB,0BAAA,MAC1D,OAAA,CAAQ,OAAA,CAAQ,UAAA,QAAkB,0BAAA"}
1
+ {"version":3,"file":"ops.d.ts","names":[],"sources":["../../src/middle_layer/ops.ts"],"mappings":";;;;;;AAiBA;AAAA,KAAY,iBAAA;mFAED,gBAAA;EAAA,SAGA,2BAAA,UAGA;EAAA,SAAA,qBAAA;EAckB;;;;;;;AAoB7B;;;;;EApB6B,SAAlB,gBAAA,EAAkB,sBAAA;EA4DU;;;;EAAA,SAtD5B,4BAAA,GAA+B,uBAAA,IAiGd;EAAA,SA9FjB,gBAAA;EA8FgC;;;;;EAAA,SAvFhC,gBAAA;AAAA;;KAIC,oBAAA;EAAA,SAKD,MAAA,EAAQ,QAAA;EA6CR;;;;;;EAAA,SAjCA,WAAA;EA2DiB;;;;EAAA,SAjDjB,aAAA,EAAe,iBAAA,EA2Dd;EAAA,SApDD,yBAAA,EAA2B,oBAAA;EAAA,SAM3B,0BAAA,EAA4B,0BAAA;EA8CZ;;AAG3B;;EAH2B,SApChB,eAAA,EAAiB,eAAA,EAuCc;EAAA,SA/B/B,kBAAA,EAAoB,mBAAA;EA4Ef;;;;;EAAA,SAjEL,sBAAA,EAAwB,sBAAA,EAoEjC;EAAA,SA7DS,eAAA,EAAiB,eAAA;EA6DT;AAiBnB;;;;EAjBmB,SAtDR,eAAA,EAAiB,eAAA;AAAA;AAAA,KAGhB,YAAA,GAAe,iBAAA,GAAoB,oBAAA;;cAGlC,2BAAA,EAA6B,IAAA,CACxC,oBAAA;AAAA,iBA4Cc,wBAAA,CACd,OAAA,WACC,IAAA,CACD,iBAAA;;KAiBU,uBAAA,GAA0B,IAAA,CACpC,oBAAA,eACa,2BAAA,IAEb,OAAA,QAAe,2BAAA,IACf,IAAA,CAAK,iBAAA,QAAyB,UAAA,QAAkB,wBAAA,KAChD,OAAA,CAAQ,UAAA,QAAkB,wBAAA;AAAA,KAEhB,mBAAA,GAAsB,iBAAA;EAFxB,uDAIC,oBAAA;AAAA;;KAIC,uBAAA;EAbV,uFAeA,oBAAA;AAAA;;KAIU,sBAAA,GAAyB,oBAAA;EAf9B,8BAiBI,QAAA,EAAU,uBAAA;EAjB6B;;EAAA,SAqBvC,kBAAA,EAAoB,2BAAA;EApBH;;EAAA,SAwBjB,sBAAA,UAtBC;EAAA,SAyBD,6BAAA;WAGA,sBAAA;AAAA;AAAA,KAGC,cAAA,GAAiB,sBAAA,GAAyB,mBAAA;;cAGzC,6BAAA,EAA+B,IAAA,CAC1C,cAAA,eACe,2BAAA;AAAA,iBAmBD,0BAAA,CACd,OAAA,WACC,IAAA,CACD,mBAAA,QACM,UAAA,QAAkB,wBAAA;AAAA,KAQd,yBAAA,GAA4B,IAAA,CACtC,sBAAA,eACa,6BAAA,IAEb,OAAA,QAAe,6BAAA,IACf,IAAA,CAAK,mBAAA,QAA2B,OAAA,CAAQ,UAAA,QAAkB,0BAAA,MAC1D,OAAA,CAAQ,OAAA,CAAQ,UAAA,QAAkB,0BAAA"}
@@ -31,14 +31,20 @@ const DefaultDriverKitOpsSettings = {
31
31
  pollingInterval: 1e3,
32
32
  stopPollingDelay: 1e3
33
33
  },
34
- pFrameDriverOps: PFrameDriverOpsDefaults
34
+ pFrameDriverOps: PFrameDriverOpsDefaults,
35
+ parquetCacheOps: {
36
+ maxSizeBytes: 8 * 1024 * 1024 * 1024,
37
+ admissionFraction: .2,
38
+ maxFilesTracked: 5e4
39
+ }
35
40
  };
36
41
  function DefaultDriverKitOpsPaths(workDir) {
37
42
  return {
38
43
  blobDownloadPath: path.join(workDir, "download"),
39
44
  blobDownloadRangesCachePath: path.join(workDir, "downloadRangesCache"),
40
45
  downloadBlobToURLPath: path.join(workDir, "downloadToURL"),
41
- pframesSpillPath: path.join(workDir, "pframes")
46
+ pframesSpillPath: path.join(workDir, "pframes"),
47
+ parquetCachePath: path.join(workDir, "parquetCache")
42
48
  };
43
49
  }
44
50
  /** Some defaults fot MiddleLayerOps. */
@@ -1 +1 @@
1
- {"version":3,"file":"ops.js","names":[],"sources":["../../src/middle_layer/ops.ts"],"sourcesContent":["import type { TemporalSynchronizedTreeOps } from \"./types\";\nimport type {\n DownloadBlobToURLDriverOps,\n DownloadDriverOps,\n DownloadUrlDriverOps,\n OpenFileDialogCallback,\n VirtualLocalStorageSpec,\n} from \"@milaboratories/pl-drivers\";\nimport type { UploadDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { LogsStreamDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter } from \"@milaboratories/ts-helpers\";\nimport type { LocalStorageProjection } from \"@milaboratories/pl-drivers\";\nimport path from \"node:path\";\nimport { PFrameDriverOpsDefaults, type PFrameDriverOps } from \"../pool\";\n\n/** Paths part of {@link DriverKitOps}. */\nexport type DriverKitOpsPaths = {\n /** Common root where to put downloaded blobs / downloaded blob cache */\n readonly blobDownloadPath: string;\n\n /** Common root for a cache for range queries. */\n readonly blobDownloadRangesCachePath: string;\n\n /** Common root where to put downloaded blobs with */\n readonly downloadBlobToURLPath: string;\n\n /**\n * List of pl storages that have projections in local file system.\n *\n * This option affect two drivers:\n *\n * (1) LS driver generates \"index\" handles instead of \"upload\" for paths inside those locations\n *\n * (2) Download driver directly serves content retrieval requests for blobs from listed storages,\n * and don't apply any caching for such blobs (i.e. preventing duplication of files for Downloaded\n * type handles, making OnDemand and Downloaded handles equivalent)\n *\n * */\n readonly localProjections: LocalStorageProjection[];\n\n /**\n * List of virtual storages that will allow homogeneous access to local FSs through LS API.\n * If undefined, default list will be created.\n * */\n readonly virtualLocalStoragesOverride?: VirtualLocalStorageSpec[];\n\n /** Path to the directory where pframes will spill temporary files and store materialized views */\n readonly pframesSpillPath: string;\n};\n\n/** Options required to initialize full set of middle layer driver kit */\nexport type DriverKitOpsSettings = {\n //\n // Common\n //\n\n readonly logger: MiLogger;\n\n //\n // Signer\n //\n\n /**\n * Local secret, that is used to sign and verify different pieces of information\n * that can be used to access local data, like local paths for ongoing uploads.\n *\n * Use {@link MiddleLayer.generateLocalSecret} to generate sufficiently random string.\n * */\n readonly localSecret: string;\n\n //\n // Blob Driver\n //\n\n /**\n * Settings related to the download driver making operations with blobs. This driver is also used\n * to download logs when source process terminates and log terns into a blob\n */\n readonly blobDriverOps: DownloadDriverOps;\n\n //\n // Frontend Driver\n //\n\n /** Settings related to the frontend driver that downloads frontends. */\n readonly frontendDownloadDriverOps: DownloadUrlDriverOps;\n\n //\n // Blob To URL Driver\n //\n\n readonly downloadBlobToURLDriverOps: DownloadBlobToURLDriverOps;\n\n //\n // Upload Driver\n //\n\n /**\n * Settings related to the upload driver that actually performs upload and helps render upload\n * and indexing progresses from related pl resources.\n * */\n readonly uploadDriverOps: UploadDriverOps;\n\n //\n // Log streaming ops\n // (static logs are served via the blob driver)\n //\n\n /** Settings related to the streaming log driver */\n readonly logStreamDriverOps: LogsStreamDriverOps;\n\n //\n // LS Driver\n //\n\n /**\n * Callback to access system file open dialog, must be provided by the environment,\n * to allow for {@link showOpenSingleFileDialog} / {@link showOpenMultipleFilesDialog}\n * calls from the UI.\n */\n readonly openFileDialogCallback: OpenFileDialogCallback;\n\n //\n // PFrame Driver\n //\n\n /** Settings related to the PFrame driver */\n readonly pFrameDriverOps: PFrameDriverOps;\n};\n\nexport type DriverKitOps = DriverKitOpsPaths & DriverKitOpsSettings;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultDriverKitOpsSettings: Pick<\n DriverKitOpsSettings,\n | \"logger\"\n | \"blobDriverOps\"\n | \"frontendDownloadDriverOps\"\n | \"downloadBlobToURLDriverOps\"\n | \"uploadDriverOps\"\n | \"logStreamDriverOps\"\n | \"pFrameDriverOps\"\n> = {\n logger: new ConsoleLoggerAdapter(),\n blobDriverOps: {\n cacheSoftSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n rangesCacheMaxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n nConcurrentDownloads: 10,\n },\n frontendDownloadDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n withGunzip: true,\n nConcurrentDownloads: 50,\n },\n downloadBlobToURLDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n nConcurrentDownloads: 10,\n },\n uploadDriverOps: {\n nConcurrentPartUploads: 10,\n nConcurrentGetProgresses: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n logStreamDriverOps: {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n pFrameDriverOps: PFrameDriverOpsDefaults,\n};\n\nexport function DefaultDriverKitOpsPaths(\n workDir: string,\n): Pick<\n DriverKitOpsPaths,\n \"blobDownloadPath\" | \"blobDownloadRangesCachePath\" | \"downloadBlobToURLPath\" | \"pframesSpillPath\"\n> {\n return {\n blobDownloadPath: path.join(workDir, \"download\"),\n blobDownloadRangesCachePath: path.join(workDir, \"downloadRangesCache\"),\n downloadBlobToURLPath: path.join(workDir, \"downloadToURL\"),\n pframesSpillPath: path.join(workDir, \"pframes\"),\n };\n}\n\n/** Fields with default values are marked as optional here. */\nexport type DriverKitOpsConstructor = Omit<\n DriverKitOpsSettings,\n keyof typeof DefaultDriverKitOpsSettings\n> &\n Partial<typeof DefaultDriverKitOpsSettings> &\n Omit<DriverKitOpsPaths, keyof ReturnType<typeof DefaultDriverKitOpsPaths>> &\n Partial<ReturnType<typeof DefaultDriverKitOpsPaths>>;\n\nexport type MiddleLayerOpsPaths = DriverKitOpsPaths & {\n /** Common root where to put frontend code. */\n readonly frontendDownloadPath: string;\n};\n\n/** Debug options for middle layer. */\nexport type MiddleLayerDebugOptions = {\n /** If true, will dump initial tree state to the file with root resource id as name. */\n dumpInitialTreeState: boolean;\n};\n\n/** Configuration controlling different aspects of middle layer behaviour. */\nexport type MiddleLayerOpsSettings = DriverKitOpsSettings & {\n /** Debug options. */\n readonly debugOps: MiddleLayerDebugOptions;\n\n /** Contain temporal options controlling how often should pl trees be\n * synchronized with the pl server. */\n readonly defaultTreeOptions: TemporalSynchronizedTreeOps;\n\n /** Defines interval in milliseconds for running periodic project maintenance job.\n * Project maintenance includes background staging rendering and cached outputs cleanup. */\n readonly projectRefreshInterval: number;\n\n /** How often to check for dev block updates */\n readonly devBlockUpdateRecheckInterval: number;\n\n /** Prioritize this channel if update is available in this block */\n readonly preferredUpdateChannel?: string;\n};\n\nexport type MiddleLayerOps = MiddleLayerOpsSettings & MiddleLayerOpsPaths;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultMiddleLayerOpsSettings: Pick<\n MiddleLayerOps,\n | keyof typeof DefaultDriverKitOpsSettings\n | \"defaultTreeOptions\"\n | \"projectRefreshInterval\"\n | \"devBlockUpdateRecheckInterval\"\n | \"debugOps\"\n> = {\n ...DefaultDriverKitOpsSettings,\n defaultTreeOptions: {\n pollingInterval: 200,\n stopPollingDelay: 2500,\n initialTreeLoadingTimeout: 100 * 60 * 60 * 1000, // disable timeout for loading project tree (100 hours)\n },\n debugOps: {\n dumpInitialTreeState: false,\n },\n devBlockUpdateRecheckInterval: 1000,\n projectRefreshInterval: 2000,\n};\n\nexport function DefaultMiddleLayerOpsPaths(\n workDir: string,\n): Pick<\n MiddleLayerOpsPaths,\n keyof ReturnType<typeof DefaultDriverKitOpsPaths> | \"frontendDownloadPath\"\n> {\n return {\n ...DefaultDriverKitOpsPaths(workDir),\n frontendDownloadPath: path.join(workDir, \"frontend\"),\n };\n}\n\nexport type MiddleLayerOpsConstructor = Omit<\n MiddleLayerOpsSettings,\n keyof typeof DefaultMiddleLayerOpsSettings\n> &\n Partial<typeof DefaultMiddleLayerOpsSettings> &\n Omit<MiddleLayerOpsPaths, keyof Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>> &\n Partial<Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>>;\n"],"mappings":";;;;;;AAsIA,MAAa,8BAST;CACF,QAAQ,IAAI,sBAAsB;CAClC,eAAe;EACb,oBAAoB,IAAI,OAAO,OAAO;EACtC,yBAAyB,IAAI,OAAO,OAAO;EAC3C,sBAAsB;EACvB;CACD,2BAA2B;EACzB,oBAAoB,IAAI,OAAO,OAAO;EACtC,YAAY;EACZ,sBAAsB;EACvB;CACD,4BAA4B;EAC1B,oBAAoB,IAAI,OAAO,OAAO;EACtC,sBAAsB;EACvB;CACD,iBAAiB;EACf,wBAAwB;EACxB,0BAA0B;EAC1B,iBAAiB;EACjB,kBAAkB;EACnB;CACD,oBAAoB;EAClB,oBAAoB;EACpB,iBAAiB;EACjB,kBAAkB;EACnB;CACD,iBAAiB;CAClB;AAED,SAAgB,yBACd,SAIA;AACA,QAAO;EACL,kBAAkB,KAAK,KAAK,SAAS,WAAW;EAChD,6BAA6B,KAAK,KAAK,SAAS,sBAAsB;EACtE,uBAAuB,KAAK,KAAK,SAAS,gBAAgB;EAC1D,kBAAkB,KAAK,KAAK,SAAS,UAAU;EAChD;;;AA8CH,MAAa,gCAOT;CACF,GAAG;CACH,oBAAoB;EAClB,iBAAiB;EACjB,kBAAkB;EAClB,2BAA2B,MAAW,KAAK;EAC5C;CACD,UAAU,EACR,sBAAsB,OACvB;CACD,+BAA+B;CAC/B,wBAAwB;CACzB;AAED,SAAgB,2BACd,SAIA;AACA,QAAO;EACL,GAAG,yBAAyB,QAAQ;EACpC,sBAAsB,KAAK,KAAK,SAAS,WAAW;EACrD"}
1
+ {"version":3,"file":"ops.js","names":[],"sources":["../../src/middle_layer/ops.ts"],"sourcesContent":["import type { TemporalSynchronizedTreeOps } from \"./types\";\nimport type {\n DownloadBlobToURLDriverOps,\n DownloadDriverOps,\n DownloadUrlDriverOps,\n OpenFileDialogCallback,\n VirtualLocalStorageSpec,\n} from \"@milaboratories/pl-drivers\";\nimport type { UploadDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { LogsStreamDriverOps } from \"@milaboratories/pl-drivers\";\nimport type { MiLogger } from \"@milaboratories/ts-helpers\";\nimport { ConsoleLoggerAdapter } from \"@milaboratories/ts-helpers\";\nimport type { LocalStorageProjection } from \"@milaboratories/pl-drivers\";\nimport path from \"node:path\";\nimport { PFrameDriverOpsDefaults, type PFrameDriverOps, type ParquetCacheOps } from \"../pool\";\n\n/** Paths part of {@link DriverKitOps}. */\nexport type DriverKitOpsPaths = {\n /** Common root where to put downloaded blobs / downloaded blob cache */\n readonly blobDownloadPath: string;\n\n /** Common root for a cache for range queries. */\n readonly blobDownloadRangesCachePath: string;\n\n /** Common root where to put downloaded blobs with */\n readonly downloadBlobToURLPath: string;\n\n /**\n * List of pl storages that have projections in local file system.\n *\n * This option affect two drivers:\n *\n * (1) LS driver generates \"index\" handles instead of \"upload\" for paths inside those locations\n *\n * (2) Download driver directly serves content retrieval requests for blobs from listed storages,\n * and don't apply any caching for such blobs (i.e. preventing duplication of files for Downloaded\n * type handles, making OnDemand and Downloaded handles equivalent)\n *\n * */\n readonly localProjections: LocalStorageProjection[];\n\n /**\n * List of virtual storages that will allow homogeneous access to local FSs through LS API.\n * If undefined, default list will be created.\n * */\n readonly virtualLocalStoragesOverride?: VirtualLocalStorageSpec[];\n\n /** Path to the directory where pframes will spill temporary files and store materialized views */\n readonly pframesSpillPath: string;\n\n /**\n * Path to the directory backing the persistent parquet blob cache. Unlike\n * {@link pframesSpillPath} this directory is NOT emptied on startup - the cache\n * (and its lifetime counters) is meant to survive restarts.\n */\n readonly parquetCachePath: string;\n};\n\n/** Options required to initialize full set of middle layer driver kit */\nexport type DriverKitOpsSettings = {\n //\n // Common\n //\n\n readonly logger: MiLogger;\n\n //\n // Signer\n //\n\n /**\n * Local secret, that is used to sign and verify different pieces of information\n * that can be used to access local data, like local paths for ongoing uploads.\n *\n * Use {@link MiddleLayer.generateLocalSecret} to generate sufficiently random string.\n * */\n readonly localSecret: string;\n\n //\n // Blob Driver\n //\n\n /**\n * Settings related to the download driver making operations with blobs. This driver is also used\n * to download logs when source process terminates and log terns into a blob\n */\n readonly blobDriverOps: DownloadDriverOps;\n\n //\n // Frontend Driver\n //\n\n /** Settings related to the frontend driver that downloads frontends. */\n readonly frontendDownloadDriverOps: DownloadUrlDriverOps;\n\n //\n // Blob To URL Driver\n //\n\n readonly downloadBlobToURLDriverOps: DownloadBlobToURLDriverOps;\n\n //\n // Upload Driver\n //\n\n /**\n * Settings related to the upload driver that actually performs upload and helps render upload\n * and indexing progresses from related pl resources.\n * */\n readonly uploadDriverOps: UploadDriverOps;\n\n //\n // Log streaming ops\n // (static logs are served via the blob driver)\n //\n\n /** Settings related to the streaming log driver */\n readonly logStreamDriverOps: LogsStreamDriverOps;\n\n //\n // LS Driver\n //\n\n /**\n * Callback to access system file open dialog, must be provided by the environment,\n * to allow for {@link showOpenSingleFileDialog} / {@link showOpenMultipleFilesDialog}\n * calls from the UI.\n */\n readonly openFileDialogCallback: OpenFileDialogCallback;\n\n //\n // PFrame Driver\n //\n\n /** Settings related to the PFrame driver */\n readonly pFrameDriverOps: PFrameDriverOps;\n\n /**\n * Tuning for the persistent parquet blob cache. Its directory is\n * {@link DriverKitOpsPaths.parquetCachePath}; this carries only the\n * size/eviction knobs.\n */\n readonly parquetCacheOps: ParquetCacheOps;\n};\n\nexport type DriverKitOps = DriverKitOpsPaths & DriverKitOpsSettings;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultDriverKitOpsSettings: Pick<\n DriverKitOpsSettings,\n | \"logger\"\n | \"blobDriverOps\"\n | \"frontendDownloadDriverOps\"\n | \"downloadBlobToURLDriverOps\"\n | \"uploadDriverOps\"\n | \"logStreamDriverOps\"\n | \"pFrameDriverOps\"\n | \"parquetCacheOps\"\n> = {\n logger: new ConsoleLoggerAdapter(),\n blobDriverOps: {\n cacheSoftSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n rangesCacheMaxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n nConcurrentDownloads: 10,\n },\n frontendDownloadDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n withGunzip: true,\n nConcurrentDownloads: 50,\n },\n downloadBlobToURLDriverOps: {\n cacheSoftSizeBytes: 1 * 1024 * 1024 * 1024, // 1 GB\n nConcurrentDownloads: 10,\n },\n uploadDriverOps: {\n nConcurrentPartUploads: 10,\n nConcurrentGetProgresses: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n logStreamDriverOps: {\n nConcurrentGetLogs: 10,\n pollingInterval: 1000,\n stopPollingDelay: 1000,\n },\n pFrameDriverOps: PFrameDriverOpsDefaults,\n parquetCacheOps: {\n maxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB\n admissionFraction: 0.2, // a single file may occupy at most 20% of the budget\n maxFilesTracked: 50_000, // ghost (previously-evicted) files remembered to guide readmission\n },\n};\n\nexport function DefaultDriverKitOpsPaths(\n workDir: string,\n): Pick<\n DriverKitOpsPaths,\n | \"blobDownloadPath\"\n | \"blobDownloadRangesCachePath\"\n | \"downloadBlobToURLPath\"\n | \"pframesSpillPath\"\n | \"parquetCachePath\"\n> {\n return {\n blobDownloadPath: path.join(workDir, \"download\"),\n blobDownloadRangesCachePath: path.join(workDir, \"downloadRangesCache\"),\n downloadBlobToURLPath: path.join(workDir, \"downloadToURL\"),\n pframesSpillPath: path.join(workDir, \"pframes\"),\n parquetCachePath: path.join(workDir, \"parquetCache\"),\n };\n}\n\n/** Fields with default values are marked as optional here. */\nexport type DriverKitOpsConstructor = Omit<\n DriverKitOpsSettings,\n keyof typeof DefaultDriverKitOpsSettings\n> &\n Partial<typeof DefaultDriverKitOpsSettings> &\n Omit<DriverKitOpsPaths, keyof ReturnType<typeof DefaultDriverKitOpsPaths>> &\n Partial<ReturnType<typeof DefaultDriverKitOpsPaths>>;\n\nexport type MiddleLayerOpsPaths = DriverKitOpsPaths & {\n /** Common root where to put frontend code. */\n readonly frontendDownloadPath: string;\n};\n\n/** Debug options for middle layer. */\nexport type MiddleLayerDebugOptions = {\n /** If true, will dump initial tree state to the file with root resource id as name. */\n dumpInitialTreeState: boolean;\n};\n\n/** Configuration controlling different aspects of middle layer behaviour. */\nexport type MiddleLayerOpsSettings = DriverKitOpsSettings & {\n /** Debug options. */\n readonly debugOps: MiddleLayerDebugOptions;\n\n /** Contain temporal options controlling how often should pl trees be\n * synchronized with the pl server. */\n readonly defaultTreeOptions: TemporalSynchronizedTreeOps;\n\n /** Defines interval in milliseconds for running periodic project maintenance job.\n * Project maintenance includes background staging rendering and cached outputs cleanup. */\n readonly projectRefreshInterval: number;\n\n /** How often to check for dev block updates */\n readonly devBlockUpdateRecheckInterval: number;\n\n /** Prioritize this channel if update is available in this block */\n readonly preferredUpdateChannel?: string;\n};\n\nexport type MiddleLayerOps = MiddleLayerOpsSettings & MiddleLayerOpsPaths;\n\n/** Some defaults fot MiddleLayerOps. */\nexport const DefaultMiddleLayerOpsSettings: Pick<\n MiddleLayerOps,\n | keyof typeof DefaultDriverKitOpsSettings\n | \"defaultTreeOptions\"\n | \"projectRefreshInterval\"\n | \"devBlockUpdateRecheckInterval\"\n | \"debugOps\"\n> = {\n ...DefaultDriverKitOpsSettings,\n defaultTreeOptions: {\n pollingInterval: 200,\n stopPollingDelay: 2500,\n initialTreeLoadingTimeout: 100 * 60 * 60 * 1000, // disable timeout for loading project tree (100 hours)\n },\n debugOps: {\n dumpInitialTreeState: false,\n },\n devBlockUpdateRecheckInterval: 1000,\n projectRefreshInterval: 2000,\n};\n\nexport function DefaultMiddleLayerOpsPaths(\n workDir: string,\n): Pick<\n MiddleLayerOpsPaths,\n keyof ReturnType<typeof DefaultDriverKitOpsPaths> | \"frontendDownloadPath\"\n> {\n return {\n ...DefaultDriverKitOpsPaths(workDir),\n frontendDownloadPath: path.join(workDir, \"frontend\"),\n };\n}\n\nexport type MiddleLayerOpsConstructor = Omit<\n MiddleLayerOpsSettings,\n keyof typeof DefaultMiddleLayerOpsSettings\n> &\n Partial<typeof DefaultMiddleLayerOpsSettings> &\n Omit<MiddleLayerOpsPaths, keyof Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>> &\n Partial<Awaited<ReturnType<typeof DefaultMiddleLayerOpsPaths>>>;\n"],"mappings":";;;;;;AAoJA,MAAa,8BAUT;CACF,QAAQ,IAAI,sBAAsB;CAClC,eAAe;EACb,oBAAoB,IAAI,OAAO,OAAO;EACtC,yBAAyB,IAAI,OAAO,OAAO;EAC3C,sBAAsB;EACvB;CACD,2BAA2B;EACzB,oBAAoB,IAAI,OAAO,OAAO;EACtC,YAAY;EACZ,sBAAsB;EACvB;CACD,4BAA4B;EAC1B,oBAAoB,IAAI,OAAO,OAAO;EACtC,sBAAsB;EACvB;CACD,iBAAiB;EACf,wBAAwB;EACxB,0BAA0B;EAC1B,iBAAiB;EACjB,kBAAkB;EACnB;CACD,oBAAoB;EAClB,oBAAoB;EACpB,iBAAiB;EACjB,kBAAkB;EACnB;CACD,iBAAiB;CACjB,iBAAiB;EACf,cAAc,IAAI,OAAO,OAAO;EAChC,mBAAmB;EACnB,iBAAiB;EAClB;CACF;AAED,SAAgB,yBACd,SAQA;AACA,QAAO;EACL,kBAAkB,KAAK,KAAK,SAAS,WAAW;EAChD,6BAA6B,KAAK,KAAK,SAAS,sBAAsB;EACtE,uBAAuB,KAAK,KAAK,SAAS,gBAAgB;EAC1D,kBAAkB,KAAK,KAAK,SAAS,UAAU;EAC/C,kBAAkB,KAAK,KAAK,SAAS,eAAe;EACrD;;;AA8CH,MAAa,gCAOT;CACF,GAAG;CACH,oBAAoB;EAClB,iBAAiB;EACjB,kBAAkB;EAClB,2BAA2B,MAAW,KAAK;EAC5C;CACD,UAAU,EACR,sBAAsB,OACvB;CACD,+BAA+B;CAC/B,wBAAwB;CACzB;AAED,SAAgB,2BACd,SAIA;AACA,QAAO;EACL,GAAG,yBAAyB,QAAQ;EACpC,sBAAsB,KAAK,KAAK,SAAS,WAAW;EACrD"}
@@ -45,7 +45,7 @@ var LocalBlobProviderImpl = class extends _milaboratories_helpers.RefCountPoolBa
45
45
  },
46
46
  resolveBlobContent: async (blobId) => {
47
47
  const blob = await this.getByKey(blobId).awaitStableValue(signal);
48
- return await this.blobDriver.getContent(blob.handle, { signal });
48
+ return await this.blobDriver.getContentDirect(blob.handle, { signal });
49
49
  }
50
50
  };
51
51
  }
@@ -136,23 +136,29 @@ var BlobStore = class extends _milaboratories_pl_model_middle_layer.PFrameIntern
136
136
  }
137
137
  };
138
138
  var RemoteBlobProviderImpl = class RemoteBlobProviderImpl {
139
- constructor(pool, server) {
139
+ constructor(pool, server, cache) {
140
140
  this.pool = pool;
141
141
  this.server = server;
142
+ this.cache = cache;
142
143
  }
143
- static async init(blobDriver, logger, serverOptions) {
144
+ static async init(blobDriver, logger, serverOptions, cacheConfig) {
144
145
  const pool = new RemoteBlobPool(blobDriver, logger);
145
146
  const store = new BlobStore({
146
147
  remoteBlobProvider: pool,
147
148
  logger
148
149
  });
149
- const handler = _milaboratories_pframes_rs_node.HttpHelpers.createRequestHandler({ store });
150
+ const cachingStore = await _milaboratories_pframes_rs_node.HttpHelpers.createCachingObjectStore({
151
+ upstream: store,
152
+ config: cacheConfig,
153
+ logger
154
+ });
155
+ const handler = _milaboratories_pframes_rs_node.HttpHelpers.createRequestHandler({ store: cachingStore });
150
156
  const server = await _milaboratories_pframes_rs_node.HttpHelpers.createHttpServer({
151
157
  ...serverOptions,
152
158
  handler
153
159
  });
154
160
  logger("info", `PFrames HTTP server started on ${server.info.url}`);
155
- return new RemoteBlobProviderImpl(pool, server);
161
+ return new RemoteBlobProviderImpl(pool, server, cachingStore);
156
162
  }
157
163
  acquire(params) {
158
164
  return this.pool.acquire(params);
@@ -161,11 +167,14 @@ var RemoteBlobProviderImpl = class RemoteBlobProviderImpl {
161
167
  return this.server.info;
162
168
  }
163
169
  async getCacheMetrics() {
164
- return null;
170
+ return this.cache.getMetrics();
171
+ }
172
+ async resetCache() {
173
+ await this.cache.reset();
165
174
  }
166
- async resetCache() {}
167
175
  async [Symbol.asyncDispose]() {
168
176
  await this.server.stop();
177
+ await this.cache[Symbol.asyncDispose]();
169
178
  await this.pool[Symbol.asyncDispose]();
170
179
  }
171
180
  };
@@ -178,7 +187,10 @@ async function createPFrameDriver(params) {
178
187
  await (0, _milaboratories_ts_helpers.emptyDir)(resolvedSpillPath);
179
188
  const logger = (level, message) => params.logger[level](message);
180
189
  const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);
181
- const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, { port: params.options.parquetServerPort });
190
+ const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, { port: params.options.parquetServerPort }, {
191
+ cachePath: node_path.default.resolve(params.cachePath),
192
+ ...params.cacheOps
193
+ });
182
194
  const resolveDataInfo = (spec, data) => {
183
195
  if ((0, _milaboratories_pl_tree.isPlTreeNodeAccessor)(data)) return require_data.parseDataInfoResource(data) ?? (0, _milaboratories_pf_driver.makeJsonDataInfo)(spec, []);
184
196
  return (0, _platforma_sdk_model.isDataInfo)(data) ? data.type === "ParquetPartitioned" ? (0, _platforma_sdk_model.mapDataInfo)(data, (a) => require_data.traverseParquetChunkResource(a)) : (0, _platforma_sdk_model.mapDataInfo)(data, (a) => require_data.makeLocalBlobRef(a)) : (0, _milaboratories_pf_driver.makeJsonDataInfo)(spec, data);
@@ -1 +1 @@
1
- {"version":3,"file":"driver.cjs","names":["RefCountPoolBase","PFrameDriverError","PFrameInternal","Readable","HttpHelpers","AbstractPFrameDriverOpsDefaults","path","parseDataInfoResource","traverseParquetChunkResource","makeLocalBlobRef","AbstractPFrameDriver"],"sources":["../../src/pool/driver.ts"],"sourcesContent":["import {\n mapDataInfo,\n isDataInfo,\n ensureError,\n PFrameDriverError,\n isAbortError,\n type LocalBlobHandleAndSize,\n type RemoteBlobHandleAndSize,\n type RemoteBlobHandle,\n type ContentHandler,\n type PColumnSpec,\n type PColumnDataUniversal,\n} from \"@platforma-sdk/model\";\nimport { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { emptyDir } from \"@milaboratories/ts-helpers\";\nimport { RefCountPoolBase, type PoolEntry, type MiLogger } from \"@milaboratories/helpers\";\nimport type { DownloadDriver } from \"@milaboratories/pl-drivers\";\nimport { isPlTreeNodeAccessor, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport type { Computable, ComputableStableDefined } from \"@milaboratories/computable\";\nimport {\n makeJsonDataInfo,\n AbstractPFrameDriver,\n AbstractPFrameDriverOpsDefaults,\n type AbstractInternalPFrameDriver,\n type AbstractPFrameDriverOps,\n type LocalBlobProvider,\n type RemoteBlobProvider,\n} from \"@milaboratories/pf-driver\";\nimport { HttpHelpers } from \"@milaboratories/pframes-rs-node\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport {\n BlobResourceRef,\n makeLocalBlobRef,\n parseDataInfoResource,\n traverseParquetChunkResource,\n} from \"./data\";\nimport { isDownloadNetworkError400 } from \"@milaboratories/pl-drivers\";\nimport { parseSignedResourceId } from \"@milaboratories/pl-client\";\n\nfunction makeBlobId(res: BlobResourceRef): PFrameInternal.PFrameBlobId {\n const { globalId } = parseSignedResourceId(res.resourceInfo.id);\n return String(globalId) as PFrameInternal.PFrameBlobId;\n}\n\ntype LocalBlob = ComputableStableDefined<LocalBlobHandleAndSize>;\nclass LocalBlobProviderImpl\n extends RefCountPoolBase<BlobResourceRef, PFrameInternal.PFrameBlobId, LocalBlob>\n implements LocalBlobProvider<BlobResourceRef>\n{\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): LocalBlob {\n return this.blobDriver.getDownloadedBlob(params.resourceInfo);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): LocalBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Local blob with id ${blobId} not found.`);\n return resource;\n }\n\n public makeDataSource(\n signal: AbortSignal,\n ): Omit<PFrameInternal.PFrameDataSourceV2, \"parquetServer\"> {\n return {\n preloadBlob: async (blobIds: PFrameInternal.PFrameBlobId[]) => {\n try {\n await Promise.all(\n blobIds.map((blobId) => this.getByKey(blobId).awaitStableFullValue(signal)),\n );\n } catch (err: unknown) {\n if (!isAbortError(err)) throw err;\n }\n },\n resolveBlobContent: async (blobId: PFrameInternal.PFrameBlobId) => {\n const computable = this.getByKey(blobId);\n const blob = await computable.awaitStableValue(signal);\n return await this.blobDriver.getContent(blob.handle, { signal });\n },\n };\n }\n}\n\ntype RemoteBlob = Computable<RemoteBlobHandleAndSize>;\nclass RemoteBlobPool extends RefCountPoolBase<\n BlobResourceRef,\n PFrameInternal.PFrameBlobId,\n RemoteBlob\n> {\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): RemoteBlob {\n if (params.onDemandSnapshot === undefined) {\n throw new PFrameDriverError(\n `BlobResourceRef for rid ${params.toJSON()} is missing the on-demand snapshot; ` +\n `remote (parquet) blobs must be captured via makeRemoteBlobRef.`,\n );\n }\n return this.blobDriver.getOnDemandBlob(params.onDemandSnapshot);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): RemoteBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Remote blob with id ${blobId} not found.`);\n return resource;\n }\n\n public async withContent<T>(\n handle: RemoteBlobHandle,\n options: {\n range: PFrameInternal.FileRange;\n signal: AbortSignal;\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n return await this.blobDriver.withContent(handle, {\n range: {\n from: options.range.start,\n to: options.range.end + 1,\n },\n signal: options.signal,\n handler: options.handler,\n });\n }\n}\n\ninterface BlobStoreOptions extends PFrameInternal.ObjectStoreOptions {\n remoteBlobProvider: RemoteBlobPool;\n}\n\nclass BlobStore extends PFrameInternal.BaseObjectStore {\n private readonly remoteBlobProvider: RemoteBlobPool;\n\n constructor(options: BlobStoreOptions) {\n super(options);\n this.remoteBlobProvider = options.remoteBlobProvider;\n }\n\n public override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n },\n ): Promise<void> {\n const blobId = filename.slice(\n 0,\n -PFrameInternal.ParquetExtension.length,\n ) as PFrameInternal.PFrameBlobId;\n const respond = async (response: PFrameInternal.ObjectStoreResponse): Promise<void> => {\n try {\n await params.callback(response);\n } catch (error: unknown) {\n this.logger(\n \"warn\",\n `PFrames blob store received unhandled rejection from HTTP handler: ${ensureError(error)}`,\n );\n }\n };\n\n try {\n const computable = this.remoteBlobProvider.tryGetByKey(blobId);\n if (!computable) return await respond({ type: \"NotFound\" });\n\n let blob: RemoteBlobHandleAndSize;\n try {\n blob = await computable.getValue();\n } catch (error: unknown) {\n this.logger(\n \"error\",\n `PFrames blob store failed to get blob from computable: ${ensureError(error)}`,\n );\n return await respond({ type: \"InternalError\" });\n }\n params.signal.throwIfAborted();\n\n const translatedRange = this.translate(blob.size, params.range);\n if (!translatedRange) {\n return await respond({\n type: \"RangeNotSatisfiable\",\n size: blob.size,\n });\n }\n\n if (params.method === \"HEAD\") {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n });\n }\n\n this.logger(\n \"info\",\n `PFrames blob store requesting content for ${blobId}, ` +\n `range [${translatedRange.start}..=${translatedRange.end}]`,\n );\n return await this.remoteBlobProvider.withContent(blob.handle, {\n range: translatedRange,\n signal: params.signal,\n handler: async (data) => {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n data: Readable.fromWeb(data),\n });\n },\n });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n if (isDownloadNetworkError400(error) && error.statusCode === 404) {\n this.logger(\"info\", `PFrames blob store known race error: ${ensureError(error)}`);\n } else {\n this.logger(\"warn\", `PFrames blob store unhandled error: ${ensureError(error)}`);\n }\n }\n return await respond({ type: \"InternalError\" });\n }\n }\n}\n\nclass RemoteBlobProviderImpl implements RemoteBlobProvider<BlobResourceRef> {\n constructor(\n private readonly pool: RemoteBlobPool,\n private readonly server: PFrameInternal.HttpServer,\n // TODO: private readonly cache: PFrameInternal.CachingObjectStore,\n ) {}\n\n public static async init(\n blobDriver: DownloadDriver,\n logger: PFrameInternal.Logger,\n serverOptions: Omit<PFrameInternal.HttpServerOptions, \"handler\">,\n ): Promise<RemoteBlobProviderImpl> {\n const pool = new RemoteBlobPool(blobDriver, logger);\n const store = new BlobStore({ remoteBlobProvider: pool, logger });\n // TODO: const cachingStore = HttpHelpers.createCachingObjectStore({ ... });\n\n const handler = HttpHelpers.createRequestHandler({ store });\n const server = await HttpHelpers.createHttpServer({ ...serverOptions, handler });\n logger(\"info\", `PFrames HTTP server started on ${server.info.url}`);\n\n return new RemoteBlobProviderImpl(pool, server);\n }\n\n public acquire(params: BlobResourceRef): PoolEntry<PFrameInternal.PFrameBlobId> {\n return this.pool.acquire(params);\n }\n\n public httpServerInfo(): PFrameInternal.HttpServerInfo {\n return this.server.info;\n }\n\n public async getCacheMetrics(): Promise<PFrameInternal.CacheMetrics | null> {\n return null; // TODO: this.cache.getMetrics()\n }\n\n public async resetCache(): Promise<void> {\n // TODO: this.cache.reset()\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.server.stop();\n // TODO: await this.cache[Symbol.asyncDispose]();\n await this.pool[Symbol.asyncDispose]();\n }\n}\n\nexport interface InternalPFrameDriver extends AbstractInternalPFrameDriver<\n PColumnDataUniversal<PlTreeNodeAccessor>\n> {}\n\nexport type PFrameDriverOps = AbstractPFrameDriverOps & {\n /** Port to run parquet HTTP server on. */\n parquetServerPort: number;\n};\n\nexport const PFrameDriverOpsDefaults: PFrameDriverOps = {\n ...AbstractPFrameDriverOpsDefaults,\n parquetServerPort: 0, // 0 means that some unused port will be assigned by the OS\n};\n\nexport async function createPFrameDriver(params: {\n blobDriver: DownloadDriver;\n logger: MiLogger;\n spillPath: string;\n options: PFrameDriverOps;\n}): Promise<InternalPFrameDriver> {\n const resolvedSpillPath = path.resolve(params.spillPath);\n await emptyDir(resolvedSpillPath);\n\n const logger: PFrameInternal.Logger = (level, message) => params.logger[level](message);\n const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);\n const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, {\n port: params.options.parquetServerPort,\n });\n\n const resolveDataInfo = (spec: PColumnSpec, data: PColumnDataUniversal<PlTreeNodeAccessor>) => {\n if (isPlTreeNodeAccessor(data)) {\n return parseDataInfoResource(data) ?? makeJsonDataInfo(spec, []);\n }\n\n return isDataInfo(data)\n ? data.type === \"ParquetPartitioned\"\n ? mapDataInfo(data, (a) => traverseParquetChunkResource(a))\n : mapDataInfo(data, (a) => makeLocalBlobRef(a))\n : makeJsonDataInfo(spec, data);\n };\n\n return new AbstractPFrameDriver({\n logger,\n localBlobProvider,\n remoteBlobProvider,\n spillPath: resolvedSpillPath,\n options: params.options,\n resolveDataInfo,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"driver.cjs","names":["RefCountPoolBase","PFrameDriverError","PFrameInternal","Readable","HttpHelpers","AbstractPFrameDriverOpsDefaults","path","parseDataInfoResource","traverseParquetChunkResource","makeLocalBlobRef","AbstractPFrameDriver"],"sources":["../../src/pool/driver.ts"],"sourcesContent":["import {\n mapDataInfo,\n isDataInfo,\n ensureError,\n PFrameDriverError,\n isAbortError,\n type LocalBlobHandleAndSize,\n type RemoteBlobHandleAndSize,\n type RemoteBlobHandle,\n type ContentHandler,\n type PColumnSpec,\n type PColumnDataUniversal,\n} from \"@platforma-sdk/model\";\nimport { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { emptyDir } from \"@milaboratories/ts-helpers\";\nimport { RefCountPoolBase, type PoolEntry, type MiLogger } from \"@milaboratories/helpers\";\nimport type { DownloadDriver } from \"@milaboratories/pl-drivers\";\nimport { isPlTreeNodeAccessor, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport type { Computable, ComputableStableDefined } from \"@milaboratories/computable\";\nimport {\n makeJsonDataInfo,\n AbstractPFrameDriver,\n AbstractPFrameDriverOpsDefaults,\n type AbstractInternalPFrameDriver,\n type AbstractPFrameDriverOps,\n type LocalBlobProvider,\n type RemoteBlobProvider,\n} from \"@milaboratories/pf-driver\";\nimport { HttpHelpers } from \"@milaboratories/pframes-rs-node\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport {\n BlobResourceRef,\n makeLocalBlobRef,\n parseDataInfoResource,\n traverseParquetChunkResource,\n} from \"./data\";\nimport { isDownloadNetworkError400 } from \"@milaboratories/pl-drivers\";\nimport { parseSignedResourceId } from \"@milaboratories/pl-client\";\n\nfunction makeBlobId(res: BlobResourceRef): PFrameInternal.PFrameBlobId {\n const { globalId } = parseSignedResourceId(res.resourceInfo.id);\n return String(globalId) as PFrameInternal.PFrameBlobId;\n}\n\ntype LocalBlob = ComputableStableDefined<LocalBlobHandleAndSize>;\nclass LocalBlobProviderImpl\n extends RefCountPoolBase<BlobResourceRef, PFrameInternal.PFrameBlobId, LocalBlob>\n implements LocalBlobProvider<BlobResourceRef>\n{\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): LocalBlob {\n return this.blobDriver.getDownloadedBlob(params.resourceInfo);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): LocalBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Local blob with id ${blobId} not found.`);\n return resource;\n }\n\n public makeDataSource(\n signal: AbortSignal,\n ): Omit<PFrameInternal.PFrameDataSourceV2, \"parquetServer\"> {\n return {\n preloadBlob: async (blobIds: PFrameInternal.PFrameBlobId[]) => {\n try {\n await Promise.all(\n blobIds.map((blobId) => this.getByKey(blobId).awaitStableFullValue(signal)),\n );\n } catch (err: unknown) {\n if (!isAbortError(err)) throw err;\n }\n },\n resolveBlobContent: async (blobId: PFrameInternal.PFrameBlobId) => {\n const computable = this.getByKey(blobId);\n const blob = await computable.awaitStableValue(signal);\n return await this.blobDriver.getContentDirect(blob.handle, { signal });\n },\n };\n }\n}\n\ntype RemoteBlob = Computable<RemoteBlobHandleAndSize>;\nclass RemoteBlobPool extends RefCountPoolBase<\n BlobResourceRef,\n PFrameInternal.PFrameBlobId,\n RemoteBlob\n> {\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): RemoteBlob {\n if (params.onDemandSnapshot === undefined) {\n throw new PFrameDriverError(\n `BlobResourceRef for rid ${params.toJSON()} is missing the on-demand snapshot; ` +\n `remote (parquet) blobs must be captured via makeRemoteBlobRef.`,\n );\n }\n return this.blobDriver.getOnDemandBlob(params.onDemandSnapshot);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): RemoteBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Remote blob with id ${blobId} not found.`);\n return resource;\n }\n\n public async withContent<T>(\n handle: RemoteBlobHandle,\n options: {\n range: PFrameInternal.FileRange;\n signal: AbortSignal;\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n return await this.blobDriver.withContent(handle, {\n range: {\n from: options.range.start,\n to: options.range.end + 1,\n },\n signal: options.signal,\n handler: options.handler,\n });\n }\n}\n\ninterface BlobStoreOptions extends PFrameInternal.ObjectStoreOptions {\n remoteBlobProvider: RemoteBlobPool;\n}\n\nclass BlobStore extends PFrameInternal.BaseObjectStore {\n private readonly remoteBlobProvider: RemoteBlobPool;\n\n constructor(options: BlobStoreOptions) {\n super(options);\n this.remoteBlobProvider = options.remoteBlobProvider;\n }\n\n public override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n },\n ): Promise<void> {\n const blobId = filename.slice(\n 0,\n -PFrameInternal.ParquetExtension.length,\n ) as PFrameInternal.PFrameBlobId;\n const respond = async (response: PFrameInternal.ObjectStoreResponse): Promise<void> => {\n try {\n await params.callback(response);\n } catch (error: unknown) {\n this.logger(\n \"warn\",\n `PFrames blob store received unhandled rejection from HTTP handler: ${ensureError(error)}`,\n );\n }\n };\n\n try {\n const computable = this.remoteBlobProvider.tryGetByKey(blobId);\n if (!computable) return await respond({ type: \"NotFound\" });\n\n let blob: RemoteBlobHandleAndSize;\n try {\n blob = await computable.getValue();\n } catch (error: unknown) {\n this.logger(\n \"error\",\n `PFrames blob store failed to get blob from computable: ${ensureError(error)}`,\n );\n return await respond({ type: \"InternalError\" });\n }\n params.signal.throwIfAborted();\n\n const translatedRange = this.translate(blob.size, params.range);\n if (!translatedRange) {\n return await respond({\n type: \"RangeNotSatisfiable\",\n size: blob.size,\n });\n }\n\n if (params.method === \"HEAD\") {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n });\n }\n\n this.logger(\n \"info\",\n `PFrames blob store requesting content for ${blobId}, ` +\n `range [${translatedRange.start}..=${translatedRange.end}]`,\n );\n return await this.remoteBlobProvider.withContent(blob.handle, {\n range: translatedRange,\n signal: params.signal,\n handler: async (data) => {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n data: Readable.fromWeb(data),\n });\n },\n });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n if (isDownloadNetworkError400(error) && error.statusCode === 404) {\n this.logger(\"info\", `PFrames blob store known race error: ${ensureError(error)}`);\n } else {\n this.logger(\"warn\", `PFrames blob store unhandled error: ${ensureError(error)}`);\n }\n }\n return await respond({ type: \"InternalError\" });\n }\n }\n}\n\nclass RemoteBlobProviderImpl implements RemoteBlobProvider<BlobResourceRef> {\n constructor(\n private readonly pool: RemoteBlobPool,\n private readonly server: PFrameInternal.HttpServer,\n private readonly cache: PFrameInternal.CachingObjectStore,\n ) {}\n\n public static async init(\n blobDriver: DownloadDriver,\n logger: PFrameInternal.Logger,\n serverOptions: Omit<PFrameInternal.HttpServerOptions, \"handler\">,\n cacheConfig: PFrameInternal.CacheConfig,\n ): Promise<RemoteBlobProviderImpl> {\n const pool = new RemoteBlobPool(blobDriver, logger);\n const store = new BlobStore({ remoteBlobProvider: pool, logger });\n const cachingStore = await HttpHelpers.createCachingObjectStore({\n upstream: store,\n config: cacheConfig,\n logger,\n });\n\n const handler = HttpHelpers.createRequestHandler({ store: cachingStore });\n const server = await HttpHelpers.createHttpServer({ ...serverOptions, handler });\n logger(\"info\", `PFrames HTTP server started on ${server.info.url}`);\n\n return new RemoteBlobProviderImpl(pool, server, cachingStore);\n }\n\n public acquire(params: BlobResourceRef): PoolEntry<PFrameInternal.PFrameBlobId> {\n return this.pool.acquire(params);\n }\n\n public httpServerInfo(): PFrameInternal.HttpServerInfo {\n return this.server.info;\n }\n\n public async getCacheMetrics(): Promise<PFrameInternal.CacheMetrics | null> {\n return this.cache.getMetrics();\n }\n\n public async resetCache(): Promise<void> {\n await this.cache.reset();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.server.stop();\n await this.cache[Symbol.asyncDispose]();\n await this.pool[Symbol.asyncDispose]();\n }\n}\n\nexport interface InternalPFrameDriver extends AbstractInternalPFrameDriver<\n PColumnDataUniversal<PlTreeNodeAccessor>\n> {}\n\nexport type PFrameDriverOps = AbstractPFrameDriverOps & {\n /** Port to run parquet HTTP server on. */\n parquetServerPort: number;\n};\n\nexport const PFrameDriverOpsDefaults: PFrameDriverOps = {\n ...AbstractPFrameDriverOpsDefaults,\n parquetServerPort: 0, // 0 means that some unused port will be assigned by the OS\n};\n\n/**\n * Tuning for the persistent parquet blob cache, minus its filesystem path (which\n * is supplied separately - see {@link createPFrameDriver}'s `cachePath`). Carries\n * only the size/eviction knobs of {@link PFrameInternal.CacheConfig}.\n */\nexport type ParquetCacheOps = Omit<PFrameInternal.CacheConfig, \"cachePath\">;\n\nexport async function createPFrameDriver(params: {\n blobDriver: DownloadDriver;\n logger: MiLogger;\n spillPath: string;\n cachePath: string;\n options: PFrameDriverOps;\n cacheOps: ParquetCacheOps;\n}): Promise<InternalPFrameDriver> {\n const resolvedSpillPath = path.resolve(params.spillPath);\n await emptyDir(resolvedSpillPath);\n\n const logger: PFrameInternal.Logger = (level, message) => params.logger[level](message);\n const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);\n const remoteBlobProvider = await RemoteBlobProviderImpl.init(\n params.blobDriver,\n logger,\n { port: params.options.parquetServerPort },\n { cachePath: path.resolve(params.cachePath), ...params.cacheOps },\n );\n\n const resolveDataInfo = (spec: PColumnSpec, data: PColumnDataUniversal<PlTreeNodeAccessor>) => {\n if (isPlTreeNodeAccessor(data)) {\n return parseDataInfoResource(data) ?? makeJsonDataInfo(spec, []);\n }\n\n return isDataInfo(data)\n ? data.type === \"ParquetPartitioned\"\n ? mapDataInfo(data, (a) => traverseParquetChunkResource(a))\n : mapDataInfo(data, (a) => makeLocalBlobRef(a))\n : makeJsonDataInfo(spec, data);\n };\n\n return new AbstractPFrameDriver({\n logger,\n localBlobProvider,\n remoteBlobProvider,\n spillPath: resolvedSpillPath,\n options: params.options,\n resolveDataInfo,\n });\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { PColumnDataUniversal } from "@platforma-sdk/model";
2
+ import { PFrameInternal } from "@milaboratories/pl-model-middle-layer";
2
3
  import { PlTreeNodeAccessor } from "@milaboratories/pl-tree";
3
4
  import { DownloadDriver } from "@milaboratories/pl-drivers";
4
5
  import { AbstractInternalPFrameDriver, AbstractPFrameDriverOps } from "@milaboratories/pf-driver";
@@ -8,6 +9,12 @@ interface InternalPFrameDriver extends AbstractInternalPFrameDriver<PColumnDataU
8
9
  type PFrameDriverOps = AbstractPFrameDriverOps & {
9
10
  /** Port to run parquet HTTP server on. */parquetServerPort: number;
10
11
  };
12
+ /**
13
+ * Tuning for the persistent parquet blob cache, minus its filesystem path (which
14
+ * is supplied separately - see {@link createPFrameDriver}'s `cachePath`). Carries
15
+ * only the size/eviction knobs of {@link PFrameInternal.CacheConfig}.
16
+ */
17
+ type ParquetCacheOps = Omit<PFrameInternal.CacheConfig, "cachePath">;
11
18
  //#endregion
12
- export { InternalPFrameDriver, PFrameDriverOps };
19
+ export { InternalPFrameDriver, PFrameDriverOps, ParquetCacheOps };
13
20
  //# sourceMappingURL=driver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"driver.d.ts","names":[],"sources":["../../src/pool/driver.ts"],"mappings":";;;;;;UAuSiB,oBAAA,SAA6B,4BAAA,CAC5C,oBAAA,CAAqB,kBAAA;AAAA,KAGX,eAAA,GAAkB,uBAAA;EAJQ,0CAMpC,iBAAA;AAAA"}
1
+ {"version":3,"file":"driver.d.ts","names":[],"sources":["../../src/pool/driver.ts"],"mappings":";;;;;;;UA4SiB,oBAAA,SAA6B,4BAAA,CAC5C,oBAAA,CAAqB,kBAAA;AAAA,KAGX,eAAA,GAAkB,uBAAA;EAJQ,0CAMpC,iBAAA;AAAA;;;;;;KAaU,eAAA,GAAkB,IAAA,CAAK,cAAA,CAAe,WAAA"}
@@ -43,7 +43,7 @@ var LocalBlobProviderImpl = class extends RefCountPoolBase {
43
43
  },
44
44
  resolveBlobContent: async (blobId) => {
45
45
  const blob = await this.getByKey(blobId).awaitStableValue(signal);
46
- return await this.blobDriver.getContent(blob.handle, { signal });
46
+ return await this.blobDriver.getContentDirect(blob.handle, { signal });
47
47
  }
48
48
  };
49
49
  }
@@ -134,23 +134,29 @@ var BlobStore = class extends PFrameInternal.BaseObjectStore {
134
134
  }
135
135
  };
136
136
  var RemoteBlobProviderImpl = class RemoteBlobProviderImpl {
137
- constructor(pool, server) {
137
+ constructor(pool, server, cache) {
138
138
  this.pool = pool;
139
139
  this.server = server;
140
+ this.cache = cache;
140
141
  }
141
- static async init(blobDriver, logger, serverOptions) {
142
+ static async init(blobDriver, logger, serverOptions, cacheConfig) {
142
143
  const pool = new RemoteBlobPool(blobDriver, logger);
143
144
  const store = new BlobStore({
144
145
  remoteBlobProvider: pool,
145
146
  logger
146
147
  });
147
- const handler = HttpHelpers.createRequestHandler({ store });
148
+ const cachingStore = await HttpHelpers.createCachingObjectStore({
149
+ upstream: store,
150
+ config: cacheConfig,
151
+ logger
152
+ });
153
+ const handler = HttpHelpers.createRequestHandler({ store: cachingStore });
148
154
  const server = await HttpHelpers.createHttpServer({
149
155
  ...serverOptions,
150
156
  handler
151
157
  });
152
158
  logger("info", `PFrames HTTP server started on ${server.info.url}`);
153
- return new RemoteBlobProviderImpl(pool, server);
159
+ return new RemoteBlobProviderImpl(pool, server, cachingStore);
154
160
  }
155
161
  acquire(params) {
156
162
  return this.pool.acquire(params);
@@ -159,11 +165,14 @@ var RemoteBlobProviderImpl = class RemoteBlobProviderImpl {
159
165
  return this.server.info;
160
166
  }
161
167
  async getCacheMetrics() {
162
- return null;
168
+ return this.cache.getMetrics();
169
+ }
170
+ async resetCache() {
171
+ await this.cache.reset();
163
172
  }
164
- async resetCache() {}
165
173
  async [Symbol.asyncDispose]() {
166
174
  await this.server.stop();
175
+ await this.cache[Symbol.asyncDispose]();
167
176
  await this.pool[Symbol.asyncDispose]();
168
177
  }
169
178
  };
@@ -176,7 +185,10 @@ async function createPFrameDriver(params) {
176
185
  await emptyDir(resolvedSpillPath);
177
186
  const logger = (level, message) => params.logger[level](message);
178
187
  const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);
179
- const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, { port: params.options.parquetServerPort });
188
+ const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, { port: params.options.parquetServerPort }, {
189
+ cachePath: path.resolve(params.cachePath),
190
+ ...params.cacheOps
191
+ });
180
192
  const resolveDataInfo = (spec, data) => {
181
193
  if (isPlTreeNodeAccessor(data)) return parseDataInfoResource(data) ?? makeJsonDataInfo(spec, []);
182
194
  return isDataInfo(data) ? data.type === "ParquetPartitioned" ? mapDataInfo(data, (a) => traverseParquetChunkResource(a)) : mapDataInfo(data, (a) => makeLocalBlobRef(a)) : makeJsonDataInfo(spec, data);
@@ -1 +1 @@
1
- {"version":3,"file":"driver.js","names":[],"sources":["../../src/pool/driver.ts"],"sourcesContent":["import {\n mapDataInfo,\n isDataInfo,\n ensureError,\n PFrameDriverError,\n isAbortError,\n type LocalBlobHandleAndSize,\n type RemoteBlobHandleAndSize,\n type RemoteBlobHandle,\n type ContentHandler,\n type PColumnSpec,\n type PColumnDataUniversal,\n} from \"@platforma-sdk/model\";\nimport { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { emptyDir } from \"@milaboratories/ts-helpers\";\nimport { RefCountPoolBase, type PoolEntry, type MiLogger } from \"@milaboratories/helpers\";\nimport type { DownloadDriver } from \"@milaboratories/pl-drivers\";\nimport { isPlTreeNodeAccessor, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport type { Computable, ComputableStableDefined } from \"@milaboratories/computable\";\nimport {\n makeJsonDataInfo,\n AbstractPFrameDriver,\n AbstractPFrameDriverOpsDefaults,\n type AbstractInternalPFrameDriver,\n type AbstractPFrameDriverOps,\n type LocalBlobProvider,\n type RemoteBlobProvider,\n} from \"@milaboratories/pf-driver\";\nimport { HttpHelpers } from \"@milaboratories/pframes-rs-node\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport {\n BlobResourceRef,\n makeLocalBlobRef,\n parseDataInfoResource,\n traverseParquetChunkResource,\n} from \"./data\";\nimport { isDownloadNetworkError400 } from \"@milaboratories/pl-drivers\";\nimport { parseSignedResourceId } from \"@milaboratories/pl-client\";\n\nfunction makeBlobId(res: BlobResourceRef): PFrameInternal.PFrameBlobId {\n const { globalId } = parseSignedResourceId(res.resourceInfo.id);\n return String(globalId) as PFrameInternal.PFrameBlobId;\n}\n\ntype LocalBlob = ComputableStableDefined<LocalBlobHandleAndSize>;\nclass LocalBlobProviderImpl\n extends RefCountPoolBase<BlobResourceRef, PFrameInternal.PFrameBlobId, LocalBlob>\n implements LocalBlobProvider<BlobResourceRef>\n{\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): LocalBlob {\n return this.blobDriver.getDownloadedBlob(params.resourceInfo);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): LocalBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Local blob with id ${blobId} not found.`);\n return resource;\n }\n\n public makeDataSource(\n signal: AbortSignal,\n ): Omit<PFrameInternal.PFrameDataSourceV2, \"parquetServer\"> {\n return {\n preloadBlob: async (blobIds: PFrameInternal.PFrameBlobId[]) => {\n try {\n await Promise.all(\n blobIds.map((blobId) => this.getByKey(blobId).awaitStableFullValue(signal)),\n );\n } catch (err: unknown) {\n if (!isAbortError(err)) throw err;\n }\n },\n resolveBlobContent: async (blobId: PFrameInternal.PFrameBlobId) => {\n const computable = this.getByKey(blobId);\n const blob = await computable.awaitStableValue(signal);\n return await this.blobDriver.getContent(blob.handle, { signal });\n },\n };\n }\n}\n\ntype RemoteBlob = Computable<RemoteBlobHandleAndSize>;\nclass RemoteBlobPool extends RefCountPoolBase<\n BlobResourceRef,\n PFrameInternal.PFrameBlobId,\n RemoteBlob\n> {\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): RemoteBlob {\n if (params.onDemandSnapshot === undefined) {\n throw new PFrameDriverError(\n `BlobResourceRef for rid ${params.toJSON()} is missing the on-demand snapshot; ` +\n `remote (parquet) blobs must be captured via makeRemoteBlobRef.`,\n );\n }\n return this.blobDriver.getOnDemandBlob(params.onDemandSnapshot);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): RemoteBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Remote blob with id ${blobId} not found.`);\n return resource;\n }\n\n public async withContent<T>(\n handle: RemoteBlobHandle,\n options: {\n range: PFrameInternal.FileRange;\n signal: AbortSignal;\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n return await this.blobDriver.withContent(handle, {\n range: {\n from: options.range.start,\n to: options.range.end + 1,\n },\n signal: options.signal,\n handler: options.handler,\n });\n }\n}\n\ninterface BlobStoreOptions extends PFrameInternal.ObjectStoreOptions {\n remoteBlobProvider: RemoteBlobPool;\n}\n\nclass BlobStore extends PFrameInternal.BaseObjectStore {\n private readonly remoteBlobProvider: RemoteBlobPool;\n\n constructor(options: BlobStoreOptions) {\n super(options);\n this.remoteBlobProvider = options.remoteBlobProvider;\n }\n\n public override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n },\n ): Promise<void> {\n const blobId = filename.slice(\n 0,\n -PFrameInternal.ParquetExtension.length,\n ) as PFrameInternal.PFrameBlobId;\n const respond = async (response: PFrameInternal.ObjectStoreResponse): Promise<void> => {\n try {\n await params.callback(response);\n } catch (error: unknown) {\n this.logger(\n \"warn\",\n `PFrames blob store received unhandled rejection from HTTP handler: ${ensureError(error)}`,\n );\n }\n };\n\n try {\n const computable = this.remoteBlobProvider.tryGetByKey(blobId);\n if (!computable) return await respond({ type: \"NotFound\" });\n\n let blob: RemoteBlobHandleAndSize;\n try {\n blob = await computable.getValue();\n } catch (error: unknown) {\n this.logger(\n \"error\",\n `PFrames blob store failed to get blob from computable: ${ensureError(error)}`,\n );\n return await respond({ type: \"InternalError\" });\n }\n params.signal.throwIfAborted();\n\n const translatedRange = this.translate(blob.size, params.range);\n if (!translatedRange) {\n return await respond({\n type: \"RangeNotSatisfiable\",\n size: blob.size,\n });\n }\n\n if (params.method === \"HEAD\") {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n });\n }\n\n this.logger(\n \"info\",\n `PFrames blob store requesting content for ${blobId}, ` +\n `range [${translatedRange.start}..=${translatedRange.end}]`,\n );\n return await this.remoteBlobProvider.withContent(blob.handle, {\n range: translatedRange,\n signal: params.signal,\n handler: async (data) => {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n data: Readable.fromWeb(data),\n });\n },\n });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n if (isDownloadNetworkError400(error) && error.statusCode === 404) {\n this.logger(\"info\", `PFrames blob store known race error: ${ensureError(error)}`);\n } else {\n this.logger(\"warn\", `PFrames blob store unhandled error: ${ensureError(error)}`);\n }\n }\n return await respond({ type: \"InternalError\" });\n }\n }\n}\n\nclass RemoteBlobProviderImpl implements RemoteBlobProvider<BlobResourceRef> {\n constructor(\n private readonly pool: RemoteBlobPool,\n private readonly server: PFrameInternal.HttpServer,\n // TODO: private readonly cache: PFrameInternal.CachingObjectStore,\n ) {}\n\n public static async init(\n blobDriver: DownloadDriver,\n logger: PFrameInternal.Logger,\n serverOptions: Omit<PFrameInternal.HttpServerOptions, \"handler\">,\n ): Promise<RemoteBlobProviderImpl> {\n const pool = new RemoteBlobPool(blobDriver, logger);\n const store = new BlobStore({ remoteBlobProvider: pool, logger });\n // TODO: const cachingStore = HttpHelpers.createCachingObjectStore({ ... });\n\n const handler = HttpHelpers.createRequestHandler({ store });\n const server = await HttpHelpers.createHttpServer({ ...serverOptions, handler });\n logger(\"info\", `PFrames HTTP server started on ${server.info.url}`);\n\n return new RemoteBlobProviderImpl(pool, server);\n }\n\n public acquire(params: BlobResourceRef): PoolEntry<PFrameInternal.PFrameBlobId> {\n return this.pool.acquire(params);\n }\n\n public httpServerInfo(): PFrameInternal.HttpServerInfo {\n return this.server.info;\n }\n\n public async getCacheMetrics(): Promise<PFrameInternal.CacheMetrics | null> {\n return null; // TODO: this.cache.getMetrics()\n }\n\n public async resetCache(): Promise<void> {\n // TODO: this.cache.reset()\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.server.stop();\n // TODO: await this.cache[Symbol.asyncDispose]();\n await this.pool[Symbol.asyncDispose]();\n }\n}\n\nexport interface InternalPFrameDriver extends AbstractInternalPFrameDriver<\n PColumnDataUniversal<PlTreeNodeAccessor>\n> {}\n\nexport type PFrameDriverOps = AbstractPFrameDriverOps & {\n /** Port to run parquet HTTP server on. */\n parquetServerPort: number;\n};\n\nexport const PFrameDriverOpsDefaults: PFrameDriverOps = {\n ...AbstractPFrameDriverOpsDefaults,\n parquetServerPort: 0, // 0 means that some unused port will be assigned by the OS\n};\n\nexport async function createPFrameDriver(params: {\n blobDriver: DownloadDriver;\n logger: MiLogger;\n spillPath: string;\n options: PFrameDriverOps;\n}): Promise<InternalPFrameDriver> {\n const resolvedSpillPath = path.resolve(params.spillPath);\n await emptyDir(resolvedSpillPath);\n\n const logger: PFrameInternal.Logger = (level, message) => params.logger[level](message);\n const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);\n const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, {\n port: params.options.parquetServerPort,\n });\n\n const resolveDataInfo = (spec: PColumnSpec, data: PColumnDataUniversal<PlTreeNodeAccessor>) => {\n if (isPlTreeNodeAccessor(data)) {\n return parseDataInfoResource(data) ?? makeJsonDataInfo(spec, []);\n }\n\n return isDataInfo(data)\n ? data.type === \"ParquetPartitioned\"\n ? mapDataInfo(data, (a) => traverseParquetChunkResource(a))\n : mapDataInfo(data, (a) => makeLocalBlobRef(a))\n : makeJsonDataInfo(spec, data);\n };\n\n return new AbstractPFrameDriver({\n logger,\n localBlobProvider,\n remoteBlobProvider,\n spillPath: resolvedSpillPath,\n options: params.options,\n resolveDataInfo,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"driver.js","names":[],"sources":["../../src/pool/driver.ts"],"sourcesContent":["import {\n mapDataInfo,\n isDataInfo,\n ensureError,\n PFrameDriverError,\n isAbortError,\n type LocalBlobHandleAndSize,\n type RemoteBlobHandleAndSize,\n type RemoteBlobHandle,\n type ContentHandler,\n type PColumnSpec,\n type PColumnDataUniversal,\n} from \"@platforma-sdk/model\";\nimport { PFrameInternal } from \"@milaboratories/pl-model-middle-layer\";\nimport { emptyDir } from \"@milaboratories/ts-helpers\";\nimport { RefCountPoolBase, type PoolEntry, type MiLogger } from \"@milaboratories/helpers\";\nimport type { DownloadDriver } from \"@milaboratories/pl-drivers\";\nimport { isPlTreeNodeAccessor, type PlTreeNodeAccessor } from \"@milaboratories/pl-tree\";\nimport type { Computable, ComputableStableDefined } from \"@milaboratories/computable\";\nimport {\n makeJsonDataInfo,\n AbstractPFrameDriver,\n AbstractPFrameDriverOpsDefaults,\n type AbstractInternalPFrameDriver,\n type AbstractPFrameDriverOps,\n type LocalBlobProvider,\n type RemoteBlobProvider,\n} from \"@milaboratories/pf-driver\";\nimport { HttpHelpers } from \"@milaboratories/pframes-rs-node\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport {\n BlobResourceRef,\n makeLocalBlobRef,\n parseDataInfoResource,\n traverseParquetChunkResource,\n} from \"./data\";\nimport { isDownloadNetworkError400 } from \"@milaboratories/pl-drivers\";\nimport { parseSignedResourceId } from \"@milaboratories/pl-client\";\n\nfunction makeBlobId(res: BlobResourceRef): PFrameInternal.PFrameBlobId {\n const { globalId } = parseSignedResourceId(res.resourceInfo.id);\n return String(globalId) as PFrameInternal.PFrameBlobId;\n}\n\ntype LocalBlob = ComputableStableDefined<LocalBlobHandleAndSize>;\nclass LocalBlobProviderImpl\n extends RefCountPoolBase<BlobResourceRef, PFrameInternal.PFrameBlobId, LocalBlob>\n implements LocalBlobProvider<BlobResourceRef>\n{\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): LocalBlob {\n return this.blobDriver.getDownloadedBlob(params.resourceInfo);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): LocalBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Local blob with id ${blobId} not found.`);\n return resource;\n }\n\n public makeDataSource(\n signal: AbortSignal,\n ): Omit<PFrameInternal.PFrameDataSourceV2, \"parquetServer\"> {\n return {\n preloadBlob: async (blobIds: PFrameInternal.PFrameBlobId[]) => {\n try {\n await Promise.all(\n blobIds.map((blobId) => this.getByKey(blobId).awaitStableFullValue(signal)),\n );\n } catch (err: unknown) {\n if (!isAbortError(err)) throw err;\n }\n },\n resolveBlobContent: async (blobId: PFrameInternal.PFrameBlobId) => {\n const computable = this.getByKey(blobId);\n const blob = await computable.awaitStableValue(signal);\n return await this.blobDriver.getContentDirect(blob.handle, { signal });\n },\n };\n }\n}\n\ntype RemoteBlob = Computable<RemoteBlobHandleAndSize>;\nclass RemoteBlobPool extends RefCountPoolBase<\n BlobResourceRef,\n PFrameInternal.PFrameBlobId,\n RemoteBlob\n> {\n constructor(\n private readonly blobDriver: DownloadDriver,\n private readonly logger: PFrameInternal.Logger,\n ) {\n super();\n }\n\n protected calculateParamsKey(params: BlobResourceRef): PFrameInternal.PFrameBlobId {\n return makeBlobId(params);\n }\n\n protected createNewResource(\n params: BlobResourceRef,\n _key: PFrameInternal.PFrameBlobId,\n ): RemoteBlob {\n if (params.onDemandSnapshot === undefined) {\n throw new PFrameDriverError(\n `BlobResourceRef for rid ${params.toJSON()} is missing the on-demand snapshot; ` +\n `remote (parquet) blobs must be captured via makeRemoteBlobRef.`,\n );\n }\n return this.blobDriver.getOnDemandBlob(params.onDemandSnapshot);\n }\n\n public getByKey(blobId: PFrameInternal.PFrameBlobId): RemoteBlob {\n const resource = super.tryGetByKey(blobId);\n if (!resource) throw new PFrameDriverError(`Remote blob with id ${blobId} not found.`);\n return resource;\n }\n\n public async withContent<T>(\n handle: RemoteBlobHandle,\n options: {\n range: PFrameInternal.FileRange;\n signal: AbortSignal;\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n return await this.blobDriver.withContent(handle, {\n range: {\n from: options.range.start,\n to: options.range.end + 1,\n },\n signal: options.signal,\n handler: options.handler,\n });\n }\n}\n\ninterface BlobStoreOptions extends PFrameInternal.ObjectStoreOptions {\n remoteBlobProvider: RemoteBlobPool;\n}\n\nclass BlobStore extends PFrameInternal.BaseObjectStore {\n private readonly remoteBlobProvider: RemoteBlobPool;\n\n constructor(options: BlobStoreOptions) {\n super(options);\n this.remoteBlobProvider = options.remoteBlobProvider;\n }\n\n public override async request(\n filename: PFrameInternal.ParquetFileName,\n params: {\n method: PFrameInternal.HttpMethod;\n range?: PFrameInternal.HttpRange;\n signal: AbortSignal;\n callback: (response: PFrameInternal.ObjectStoreResponse) => Promise<void>;\n },\n ): Promise<void> {\n const blobId = filename.slice(\n 0,\n -PFrameInternal.ParquetExtension.length,\n ) as PFrameInternal.PFrameBlobId;\n const respond = async (response: PFrameInternal.ObjectStoreResponse): Promise<void> => {\n try {\n await params.callback(response);\n } catch (error: unknown) {\n this.logger(\n \"warn\",\n `PFrames blob store received unhandled rejection from HTTP handler: ${ensureError(error)}`,\n );\n }\n };\n\n try {\n const computable = this.remoteBlobProvider.tryGetByKey(blobId);\n if (!computable) return await respond({ type: \"NotFound\" });\n\n let blob: RemoteBlobHandleAndSize;\n try {\n blob = await computable.getValue();\n } catch (error: unknown) {\n this.logger(\n \"error\",\n `PFrames blob store failed to get blob from computable: ${ensureError(error)}`,\n );\n return await respond({ type: \"InternalError\" });\n }\n params.signal.throwIfAborted();\n\n const translatedRange = this.translate(blob.size, params.range);\n if (!translatedRange) {\n return await respond({\n type: \"RangeNotSatisfiable\",\n size: blob.size,\n });\n }\n\n if (params.method === \"HEAD\") {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n });\n }\n\n this.logger(\n \"info\",\n `PFrames blob store requesting content for ${blobId}, ` +\n `range [${translatedRange.start}..=${translatedRange.end}]`,\n );\n return await this.remoteBlobProvider.withContent(blob.handle, {\n range: translatedRange,\n signal: params.signal,\n handler: async (data) => {\n return await respond({\n type: \"Ok\",\n size: blob.size,\n range: translatedRange,\n data: Readable.fromWeb(data),\n });\n },\n });\n } catch (error: unknown) {\n if (!isAbortError(error)) {\n if (isDownloadNetworkError400(error) && error.statusCode === 404) {\n this.logger(\"info\", `PFrames blob store known race error: ${ensureError(error)}`);\n } else {\n this.logger(\"warn\", `PFrames blob store unhandled error: ${ensureError(error)}`);\n }\n }\n return await respond({ type: \"InternalError\" });\n }\n }\n}\n\nclass RemoteBlobProviderImpl implements RemoteBlobProvider<BlobResourceRef> {\n constructor(\n private readonly pool: RemoteBlobPool,\n private readonly server: PFrameInternal.HttpServer,\n private readonly cache: PFrameInternal.CachingObjectStore,\n ) {}\n\n public static async init(\n blobDriver: DownloadDriver,\n logger: PFrameInternal.Logger,\n serverOptions: Omit<PFrameInternal.HttpServerOptions, \"handler\">,\n cacheConfig: PFrameInternal.CacheConfig,\n ): Promise<RemoteBlobProviderImpl> {\n const pool = new RemoteBlobPool(blobDriver, logger);\n const store = new BlobStore({ remoteBlobProvider: pool, logger });\n const cachingStore = await HttpHelpers.createCachingObjectStore({\n upstream: store,\n config: cacheConfig,\n logger,\n });\n\n const handler = HttpHelpers.createRequestHandler({ store: cachingStore });\n const server = await HttpHelpers.createHttpServer({ ...serverOptions, handler });\n logger(\"info\", `PFrames HTTP server started on ${server.info.url}`);\n\n return new RemoteBlobProviderImpl(pool, server, cachingStore);\n }\n\n public acquire(params: BlobResourceRef): PoolEntry<PFrameInternal.PFrameBlobId> {\n return this.pool.acquire(params);\n }\n\n public httpServerInfo(): PFrameInternal.HttpServerInfo {\n return this.server.info;\n }\n\n public async getCacheMetrics(): Promise<PFrameInternal.CacheMetrics | null> {\n return this.cache.getMetrics();\n }\n\n public async resetCache(): Promise<void> {\n await this.cache.reset();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.server.stop();\n await this.cache[Symbol.asyncDispose]();\n await this.pool[Symbol.asyncDispose]();\n }\n}\n\nexport interface InternalPFrameDriver extends AbstractInternalPFrameDriver<\n PColumnDataUniversal<PlTreeNodeAccessor>\n> {}\n\nexport type PFrameDriverOps = AbstractPFrameDriverOps & {\n /** Port to run parquet HTTP server on. */\n parquetServerPort: number;\n};\n\nexport const PFrameDriverOpsDefaults: PFrameDriverOps = {\n ...AbstractPFrameDriverOpsDefaults,\n parquetServerPort: 0, // 0 means that some unused port will be assigned by the OS\n};\n\n/**\n * Tuning for the persistent parquet blob cache, minus its filesystem path (which\n * is supplied separately - see {@link createPFrameDriver}'s `cachePath`). Carries\n * only the size/eviction knobs of {@link PFrameInternal.CacheConfig}.\n */\nexport type ParquetCacheOps = Omit<PFrameInternal.CacheConfig, \"cachePath\">;\n\nexport async function createPFrameDriver(params: {\n blobDriver: DownloadDriver;\n logger: MiLogger;\n spillPath: string;\n cachePath: string;\n options: PFrameDriverOps;\n cacheOps: ParquetCacheOps;\n}): Promise<InternalPFrameDriver> {\n const resolvedSpillPath = path.resolve(params.spillPath);\n await emptyDir(resolvedSpillPath);\n\n const logger: PFrameInternal.Logger = (level, message) => params.logger[level](message);\n const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);\n const remoteBlobProvider = await RemoteBlobProviderImpl.init(\n params.blobDriver,\n logger,\n { port: params.options.parquetServerPort },\n { cachePath: path.resolve(params.cachePath), ...params.cacheOps },\n );\n\n const resolveDataInfo = (spec: PColumnSpec, data: PColumnDataUniversal<PlTreeNodeAccessor>) => {\n if (isPlTreeNodeAccessor(data)) {\n return parseDataInfoResource(data) ?? makeJsonDataInfo(spec, []);\n }\n\n return isDataInfo(data)\n ? data.type === \"ParquetPartitioned\"\n ? mapDataInfo(data, (a) => traverseParquetChunkResource(a))\n : mapDataInfo(data, (a) => makeLocalBlobRef(a))\n : makeJsonDataInfo(spec, data);\n };\n\n return new AbstractPFrameDriver({\n logger,\n localBlobProvider,\n remoteBlobProvider,\n spillPath: resolvedSpillPath,\n options: params.options,\n resolveDataInfo,\n });\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- import { InternalPFrameDriver, PFrameDriverOps } from "./driver.js";
1
+ import { InternalPFrameDriver, PFrameDriverOps, ParquetCacheOps } from "./driver.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-middle-layer",
3
- "version": "1.64.28",
3
+ "version": "1.64.29",
4
4
  "description": "Pl Middle Layer",
5
5
  "keywords": [],
6
6
  "license": "UNLICENSED",
@@ -19,8 +19,8 @@
19
19
  }
20
20
  },
21
21
  "dependencies": {
22
- "@milaboratories/pframes-rs-node": "1.1.46",
23
- "@milaboratories/pframes-rs-wasm": "1.1.46",
22
+ "@milaboratories/pframes-rs-node": "1.1.50",
23
+ "@milaboratories/pframes-rs-wasm": "1.1.50",
24
24
  "canonicalize": "~2.1.0",
25
25
  "denque": "^2.1.0",
26
26
  "es-toolkit": "^1.39.10",
@@ -32,22 +32,22 @@
32
32
  "yaml": "^2.8.0",
33
33
  "zod": "~3.25.76",
34
34
  "@milaboratories/computable": "2.9.5",
35
+ "@milaboratories/pf-spec-driver": "1.4.13",
35
36
  "@milaboratories/helpers": "1.14.2",
37
+ "@milaboratories/pf-driver": "1.7.11",
36
38
  "@milaboratories/pl-client": "3.11.4",
37
- "@milaboratories/pf-spec-driver": "1.4.12",
38
- "@milaboratories/pf-driver": "1.7.10",
39
- "@milaboratories/pl-drivers": "1.15.6",
40
- "@milaboratories/pl-http": "1.2.4",
41
- "@milaboratories/pl-errors": "1.4.22",
42
39
  "@milaboratories/pl-deployments": "3.0.7",
43
- "@milaboratories/pl-model-backend": "1.4.7",
44
- "@milaboratories/ts-helpers": "1.8.3",
40
+ "@milaboratories/pl-drivers": "1.16.0",
41
+ "@milaboratories/pl-errors": "1.4.22",
45
42
  "@milaboratories/pl-model-common": "1.46.1",
43
+ "@milaboratories/pl-http": "1.2.4",
46
44
  "@milaboratories/pl-model-middle-layer": "1.30.6",
45
+ "@milaboratories/pl-model-backend": "1.4.7",
47
46
  "@milaboratories/pl-tree": "1.12.12",
47
+ "@milaboratories/ts-helpers": "1.8.3",
48
48
  "@milaboratories/resolve-helper": "1.1.3",
49
49
  "@platforma-sdk/block-tools": "2.10.19",
50
- "@platforma-sdk/workflow-tengo": "6.6.1",
50
+ "@platforma-sdk/workflow-tengo": "6.6.2",
51
51
  "@platforma-sdk/model": "1.79.6"
52
52
  },
53
53
  "devDependencies": {
@@ -57,8 +57,8 @@
57
57
  "typescript": "~5.9.3",
58
58
  "vitest": "^4.1.3",
59
59
  "@milaboratories/build-configs": "2.0.0",
60
- "@milaboratories/ts-builder": "1.5.2",
61
- "@milaboratories/ts-configs": "1.2.3"
60
+ "@milaboratories/ts-configs": "1.2.3",
61
+ "@milaboratories/ts-builder": "1.5.2"
62
62
  },
63
63
  "engines": {
64
64
  "node": ">=22.19.0"
@@ -118,7 +118,9 @@ export async function initDriverKit(
118
118
  blobDriver,
119
119
  logger: ops.logger,
120
120
  spillPath: ops.pframesSpillPath,
121
+ cachePath: ops.parquetCachePath,
121
122
  options: ops.pFrameDriverOps,
123
+ cacheOps: ops.parquetCacheOps,
122
124
  });
123
125
 
124
126
  const frontendDownloadDriver = new DownloadUrlDriver(
@@ -12,7 +12,7 @@ import type { MiLogger } from "@milaboratories/ts-helpers";
12
12
  import { ConsoleLoggerAdapter } from "@milaboratories/ts-helpers";
13
13
  import type { LocalStorageProjection } from "@milaboratories/pl-drivers";
14
14
  import path from "node:path";
15
- import { PFrameDriverOpsDefaults, type PFrameDriverOps } from "../pool";
15
+ import { PFrameDriverOpsDefaults, type PFrameDriverOps, type ParquetCacheOps } from "../pool";
16
16
 
17
17
  /** Paths part of {@link DriverKitOps}. */
18
18
  export type DriverKitOpsPaths = {
@@ -47,6 +47,13 @@ export type DriverKitOpsPaths = {
47
47
 
48
48
  /** Path to the directory where pframes will spill temporary files and store materialized views */
49
49
  readonly pframesSpillPath: string;
50
+
51
+ /**
52
+ * Path to the directory backing the persistent parquet blob cache. Unlike
53
+ * {@link pframesSpillPath} this directory is NOT emptied on startup - the cache
54
+ * (and its lifetime counters) is meant to survive restarts.
55
+ */
56
+ readonly parquetCachePath: string;
50
57
  };
51
58
 
52
59
  /** Options required to initialize full set of middle layer driver kit */
@@ -127,6 +134,13 @@ export type DriverKitOpsSettings = {
127
134
 
128
135
  /** Settings related to the PFrame driver */
129
136
  readonly pFrameDriverOps: PFrameDriverOps;
137
+
138
+ /**
139
+ * Tuning for the persistent parquet blob cache. Its directory is
140
+ * {@link DriverKitOpsPaths.parquetCachePath}; this carries only the
141
+ * size/eviction knobs.
142
+ */
143
+ readonly parquetCacheOps: ParquetCacheOps;
130
144
  };
131
145
 
132
146
  export type DriverKitOps = DriverKitOpsPaths & DriverKitOpsSettings;
@@ -141,6 +155,7 @@ export const DefaultDriverKitOpsSettings: Pick<
141
155
  | "uploadDriverOps"
142
156
  | "logStreamDriverOps"
143
157
  | "pFrameDriverOps"
158
+ | "parquetCacheOps"
144
159
  > = {
145
160
  logger: new ConsoleLoggerAdapter(),
146
161
  blobDriverOps: {
@@ -169,19 +184,29 @@ export const DefaultDriverKitOpsSettings: Pick<
169
184
  stopPollingDelay: 1000,
170
185
  },
171
186
  pFrameDriverOps: PFrameDriverOpsDefaults,
187
+ parquetCacheOps: {
188
+ maxSizeBytes: 8 * 1024 * 1024 * 1024, // 8 GB
189
+ admissionFraction: 0.2, // a single file may occupy at most 20% of the budget
190
+ maxFilesTracked: 50_000, // ghost (previously-evicted) files remembered to guide readmission
191
+ },
172
192
  };
173
193
 
174
194
  export function DefaultDriverKitOpsPaths(
175
195
  workDir: string,
176
196
  ): Pick<
177
197
  DriverKitOpsPaths,
178
- "blobDownloadPath" | "blobDownloadRangesCachePath" | "downloadBlobToURLPath" | "pframesSpillPath"
198
+ | "blobDownloadPath"
199
+ | "blobDownloadRangesCachePath"
200
+ | "downloadBlobToURLPath"
201
+ | "pframesSpillPath"
202
+ | "parquetCachePath"
179
203
  > {
180
204
  return {
181
205
  blobDownloadPath: path.join(workDir, "download"),
182
206
  blobDownloadRangesCachePath: path.join(workDir, "downloadRangesCache"),
183
207
  downloadBlobToURLPath: path.join(workDir, "downloadToURL"),
184
208
  pframesSpillPath: path.join(workDir, "pframes"),
209
+ parquetCachePath: path.join(workDir, "parquetCache"),
185
210
  };
186
211
  }
187
212
 
@@ -88,7 +88,7 @@ class LocalBlobProviderImpl
88
88
  resolveBlobContent: async (blobId: PFrameInternal.PFrameBlobId) => {
89
89
  const computable = this.getByKey(blobId);
90
90
  const blob = await computable.awaitStableValue(signal);
91
- return await this.blobDriver.getContent(blob.handle, { signal });
91
+ return await this.blobDriver.getContentDirect(blob.handle, { signal });
92
92
  },
93
93
  };
94
94
  }
@@ -251,23 +251,28 @@ class RemoteBlobProviderImpl implements RemoteBlobProvider<BlobResourceRef> {
251
251
  constructor(
252
252
  private readonly pool: RemoteBlobPool,
253
253
  private readonly server: PFrameInternal.HttpServer,
254
- // TODO: private readonly cache: PFrameInternal.CachingObjectStore,
254
+ private readonly cache: PFrameInternal.CachingObjectStore,
255
255
  ) {}
256
256
 
257
257
  public static async init(
258
258
  blobDriver: DownloadDriver,
259
259
  logger: PFrameInternal.Logger,
260
260
  serverOptions: Omit<PFrameInternal.HttpServerOptions, "handler">,
261
+ cacheConfig: PFrameInternal.CacheConfig,
261
262
  ): Promise<RemoteBlobProviderImpl> {
262
263
  const pool = new RemoteBlobPool(blobDriver, logger);
263
264
  const store = new BlobStore({ remoteBlobProvider: pool, logger });
264
- // TODO: const cachingStore = HttpHelpers.createCachingObjectStore({ ... });
265
+ const cachingStore = await HttpHelpers.createCachingObjectStore({
266
+ upstream: store,
267
+ config: cacheConfig,
268
+ logger,
269
+ });
265
270
 
266
- const handler = HttpHelpers.createRequestHandler({ store });
271
+ const handler = HttpHelpers.createRequestHandler({ store: cachingStore });
267
272
  const server = await HttpHelpers.createHttpServer({ ...serverOptions, handler });
268
273
  logger("info", `PFrames HTTP server started on ${server.info.url}`);
269
274
 
270
- return new RemoteBlobProviderImpl(pool, server);
275
+ return new RemoteBlobProviderImpl(pool, server, cachingStore);
271
276
  }
272
277
 
273
278
  public acquire(params: BlobResourceRef): PoolEntry<PFrameInternal.PFrameBlobId> {
@@ -279,16 +284,16 @@ class RemoteBlobProviderImpl implements RemoteBlobProvider<BlobResourceRef> {
279
284
  }
280
285
 
281
286
  public async getCacheMetrics(): Promise<PFrameInternal.CacheMetrics | null> {
282
- return null; // TODO: this.cache.getMetrics()
287
+ return this.cache.getMetrics();
283
288
  }
284
289
 
285
290
  public async resetCache(): Promise<void> {
286
- // TODO: this.cache.reset()
291
+ await this.cache.reset();
287
292
  }
288
293
 
289
294
  async [Symbol.asyncDispose](): Promise<void> {
290
295
  await this.server.stop();
291
- // TODO: await this.cache[Symbol.asyncDispose]();
296
+ await this.cache[Symbol.asyncDispose]();
292
297
  await this.pool[Symbol.asyncDispose]();
293
298
  }
294
299
  }
@@ -307,20 +312,32 @@ export const PFrameDriverOpsDefaults: PFrameDriverOps = {
307
312
  parquetServerPort: 0, // 0 means that some unused port will be assigned by the OS
308
313
  };
309
314
 
315
+ /**
316
+ * Tuning for the persistent parquet blob cache, minus its filesystem path (which
317
+ * is supplied separately - see {@link createPFrameDriver}'s `cachePath`). Carries
318
+ * only the size/eviction knobs of {@link PFrameInternal.CacheConfig}.
319
+ */
320
+ export type ParquetCacheOps = Omit<PFrameInternal.CacheConfig, "cachePath">;
321
+
310
322
  export async function createPFrameDriver(params: {
311
323
  blobDriver: DownloadDriver;
312
324
  logger: MiLogger;
313
325
  spillPath: string;
326
+ cachePath: string;
314
327
  options: PFrameDriverOps;
328
+ cacheOps: ParquetCacheOps;
315
329
  }): Promise<InternalPFrameDriver> {
316
330
  const resolvedSpillPath = path.resolve(params.spillPath);
317
331
  await emptyDir(resolvedSpillPath);
318
332
 
319
333
  const logger: PFrameInternal.Logger = (level, message) => params.logger[level](message);
320
334
  const localBlobProvider = new LocalBlobProviderImpl(params.blobDriver, logger);
321
- const remoteBlobProvider = await RemoteBlobProviderImpl.init(params.blobDriver, logger, {
322
- port: params.options.parquetServerPort,
323
- });
335
+ const remoteBlobProvider = await RemoteBlobProviderImpl.init(
336
+ params.blobDriver,
337
+ logger,
338
+ { port: params.options.parquetServerPort },
339
+ { cachePath: path.resolve(params.cachePath), ...params.cacheOps },
340
+ );
324
341
 
325
342
  const resolveDataInfo = (spec: PColumnSpec, data: PColumnDataUniversal<PlTreeNodeAccessor>) => {
326
343
  if (isPlTreeNodeAccessor(data)) {