@milaboratories/pl-drivers 1.11.27 → 1.11.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.
- package/dist/drivers/download_blob/download_blob.cjs +6 -0
- package/dist/drivers/download_blob/download_blob.cjs.map +1 -1
- package/dist/drivers/download_blob/download_blob.d.ts +3 -1
- package/dist/drivers/download_blob/download_blob.d.ts.map +1 -1
- package/dist/drivers/download_blob/download_blob.js +6 -0
- package/dist/drivers/download_blob/download_blob.js.map +1 -1
- package/dist/drivers/download_blob/sparse_cache/cache.cjs +6 -0
- package/dist/drivers/download_blob/sparse_cache/cache.cjs.map +1 -1
- package/dist/drivers/download_blob/sparse_cache/cache.d.ts +3 -1
- package/dist/drivers/download_blob/sparse_cache/cache.d.ts.map +1 -1
- package/dist/drivers/download_blob/sparse_cache/cache.js +6 -0
- package/dist/drivers/download_blob/sparse_cache/cache.js.map +1 -1
- package/package.json +8 -8
- package/src/drivers/download_blob/download_blob.ts +9 -1
- package/src/drivers/download_blob/sparse_cache/cache.ts +9 -1
- package/src/drivers/download_url/driver.test.ts +1 -1
|
@@ -368,6 +368,12 @@ class DownloadDriver {
|
|
|
368
368
|
task.change.markChanged(`task ${plClient.resourceIdToString(task.rInfo.id)} released`);
|
|
369
369
|
});
|
|
370
370
|
}
|
|
371
|
+
async dispose() {
|
|
372
|
+
await this.rangesCache.dispose();
|
|
373
|
+
}
|
|
374
|
+
async [Symbol.asyncDispose]() {
|
|
375
|
+
await this.dispose();
|
|
376
|
+
}
|
|
371
377
|
}
|
|
372
378
|
/** Keeps a counter to the on demand handle. */
|
|
373
379
|
class OnDemandBlobHolder {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download_blob.cjs","sources":["../../../src/drivers/download_blob/download_blob.ts"],"sourcesContent":["import type {\n ComputableCtx,\n ComputableStableDefined,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n} from '@milaboratories/computable';\nimport type { ResourceId, ResourceType } from '@milaboratories/pl-client';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type {\n AnyLogHandle,\n BlobDriver,\n ContentHandler,\n GetContentOptions,\n LocalBlobHandle,\n LocalBlobHandleAndSize,\n ReadyLogHandle,\n RemoteBlobHandle,\n RemoteBlobHandleAndSize,\n StreamingApiResponse,\n} from '@milaboratories/pl-model-common';\nimport { type RangeBytes, validateRangeBytes } from '@milaboratories/pl-model-common';\nimport type {\n PlTreeEntry,\n ResourceInfo,\n ResourceSnapshot\n} from '@milaboratories/pl-tree';\nimport {\n isPlTreeEntry,\n makeResourceSnapshot,\n treeEntryToResourceInfo,\n} from '@milaboratories/pl-tree';\nimport type { MiLogger, Signer } from '@milaboratories/ts-helpers';\nimport { CallersCounter, mapGet, TaskProcessor } from '@milaboratories/ts-helpers';\nimport Denque from 'denque';\nimport * as fs from 'fs';\nimport { randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline/promises';\nimport { buffer } from 'node:stream/consumers';\nimport { Readable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport type { ClientLogs } from '../../clients/logs';\nimport { withFileContent } from '../helpers/read_file';\nimport {\n isLocalBlobHandle,\n newLocalHandle,\n parseLocalHandle,\n} from '../helpers/download_local_handle';\nimport {\n isRemoteBlobHandle,\n newRemoteHandle,\n parseRemoteHandle,\n} from '../helpers/download_remote_handle';\nimport { Updater, WrongResourceTypeError } from '../helpers/helpers';\nimport { getResourceInfoFromLogHandle, newLogHandle } from '../helpers/logs_handle';\nimport { getSize, OnDemandBlobResourceSnapshot } from '../types';\nimport { blobKey, pathToKey } from './blob_key';\nimport { DownloadBlobTask, nonRecoverableError } from './download_blob_task';\nimport { FilesCache } from '../helpers/files_cache';\nimport { SparseCache, SparseCacheFsFile, SparseCacheFsRanges } from './sparse_cache/cache';\nimport { isOffByOneError } from '../../helpers/download_errors';\n\nexport type DownloadDriverOps = {\n /**\n * A soft limit of the amount of blob storage, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one\n * when they become unneeded.\n * */\n cacheSoftSizeBytes: number;\n\n /**\n * A hard limit of the amount of sparse cache, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one.\n *\n * The sparse cache is used to store ranges of blobs.\n * */\n rangesCacheMaxSizeBytes: number;\n\n /**\n * Max number of concurrent downloads while calculating computable states\n * derived from this driver\n * */\n nConcurrentDownloads: number;\n};\n\n/** DownloadDriver holds a queue of downloading tasks,\n * and notifies every watcher when a file were downloaded. */\nexport class DownloadDriver implements BlobDriver {\n /** Represents a unique key to the path of a blob as a map. */\n private keyToDownload: Map<string, DownloadBlobTask> = new Map();\n\n /** Writes and removes files to a hard drive and holds a counter for every\n * file that should be kept. */\n private cache: FilesCache<DownloadBlobTask>;\n private rangesCache: SparseCache;\n\n /** Downloads files and writes them to the local dir. */\n private downloadQueue: TaskProcessor;\n\n private keyToOnDemand: Map<string, OnDemandBlobHolder> = new Map();\n\n private idToLastLines: Map<string, LastLinesGetter> = new Map();\n private idToProgressLog: Map<string, LastLinesGetter> = new Map();\n\n private readonly saveDir: string;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n private readonly clientLogs: ClientLogs,\n saveDir: string,\n private readonly rangesCacheDir: string,\n private readonly signer: Signer,\n private readonly ops: DownloadDriverOps,\n ) {\n this.cache = new FilesCache(this.ops.cacheSoftSizeBytes);\n\n const fsRanges = new SparseCacheFsRanges(this.logger, this.rangesCacheDir);\n const fsStorage = new SparseCacheFsFile(this.logger, this.rangesCacheDir);\n this.rangesCache = new SparseCache(this.logger, this.ops.rangesCacheMaxSizeBytes, fsRanges, fsStorage);\n\n this.downloadQueue = new TaskProcessor(this.logger, ops.nConcurrentDownloads);\n\n this.saveDir = path.resolve(saveDir);\n }\n\n static async init(\n logger: MiLogger,\n clientDownload: ClientDownload,\n clientLogs: ClientLogs,\n saveDir: string,\n rangesCacheDir: string,\n signer: Signer,\n ops: DownloadDriverOps,\n ): Promise<DownloadDriver> {\n const driver = new DownloadDriver(logger, clientDownload, clientLogs, saveDir, rangesCacheDir, signer, ops);\n await driver.rangesCache.reset();\n\n return driver;\n }\n\n /** Gets a blob or part of the blob by its resource id or downloads a blob and sets it in a cache. */\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx: ComputableCtx,\n ): LocalBlobHandleAndSize | undefined;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ): ComputableStableDefined<LocalBlobHandleAndSize>;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<LocalBlobHandleAndSize | undefined> | LocalBlobHandleAndSize | undefined {\n if (ctx === undefined) {\n return Computable.make((ctx) => this.getDownloadedBlob(res, ctx));\n }\n\n const rInfo = treeEntryToResourceInfo(res, ctx);\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(rInfo, callerId));\n\n const result = this.getDownloadedBlobNoCtx(ctx.watcher, rInfo as ResourceSnapshot, callerId);\n if (result == undefined) {\n ctx.markUnstable('download blob is still undefined');\n }\n\n return result;\n }\n\n private getDownloadedBlobNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n callerId: string,\n ): LocalBlobHandleAndSize | undefined {\n validateDownloadableResourceType('getDownloadedBlob', rInfo.type);\n\n // We don't need to request files with wider limits,\n // PFrame's engine does it disk-optimally by itself.\n\n const task = this.getOrSetNewTask(rInfo, callerId);\n task.attach(w, callerId);\n\n const result = task.getBlob();\n if (!result.done) {\n return undefined;\n }\n if (result.result.ok) {\n return result.result.value;\n }\n throw result.result.error;\n }\n\n private getOrSetNewTask(\n rInfo: ResourceSnapshot,\n callerId: string,\n ): DownloadBlobTask {\n const key = blobKey(rInfo.id);\n\n const inMemoryTask = this.keyToDownload.get(key);\n if (inMemoryTask) {\n return inMemoryTask;\n }\n\n // schedule the blob downloading, then it'll be added to the cache.\n const fPath = path.resolve(this.saveDir, key);\n\n const newTask = new DownloadBlobTask(\n this.logger,\n this.clientDownload,\n rInfo,\n newLocalHandle(fPath, this.signer),\n fPath,\n );\n this.keyToDownload.set(key, newTask);\n\n this.downloadQueue.push({\n fn: () => this.downloadBlob(newTask, callerId),\n recoverableErrorPredicate: (e) => !nonRecoverableError(e),\n });\n\n return newTask;\n }\n\n private async downloadBlob(task: DownloadBlobTask, callerId: string) {\n await task.download();\n const blob = task.getBlob();\n if (blob.done && blob.result.ok) {\n this.cache.addCache(task, callerId);\n }\n }\n\n /** Gets on demand blob. */\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: undefined,\n fromBytes?: number,\n toBytes?: number,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx: ComputableCtx,\n fromBytes?: number,\n toBytes?: number,\n ): RemoteBlobHandleAndSize;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: ComputableCtx,\n ): ComputableStableDefined<RemoteBlobHandleAndSize> | RemoteBlobHandleAndSize | undefined {\n if (ctx === undefined) return Computable.make((ctx) => this.getOnDemandBlob(res, ctx));\n\n const rInfo: OnDemandBlobResourceSnapshot = isPlTreeEntry(res)\n ? makeResourceSnapshot(res, OnDemandBlobResourceSnapshot, ctx)\n : res;\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseOnDemandBlob(rInfo.id, callerId));\n\n // note that the watcher is not needed,\n // the handler never changes.\n const result = this.getOnDemandBlobNoCtx(rInfo, callerId);\n\n return result;\n }\n\n private getOnDemandBlobNoCtx(\n info: OnDemandBlobResourceSnapshot,\n callerId: string,\n ): RemoteBlobHandleAndSize {\n validateDownloadableResourceType('getOnDemandBlob', info.type);\n\n let blob = this.keyToOnDemand.get(blobKey(info.id));\n\n if (blob === undefined) {\n blob = new OnDemandBlobHolder(getSize(info), newRemoteHandle(info, this.signer));\n this.keyToOnDemand.set(blobKey(info.id), blob);\n }\n\n blob.attach(callerId);\n\n return blob.getHandle();\n }\n\n /** Gets a path from a handle. */\n public getLocalPath(handle: LocalBlobHandle): string {\n const { path } = parseLocalHandle(handle, this.signer);\n return path;\n }\n\n /** Gets a content of a blob by a handle. */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options?: GetContentOptions,\n ): Promise<Uint8Array>;\n /** @deprecated Use {@link getContent} with {@link GetContentOptions} instead */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n range?: RangeBytes,\n ): Promise<Uint8Array>;\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n optionsOrRange?: GetContentOptions | RangeBytes,\n ): Promise<Uint8Array> {\n let options: GetContentOptions = {};\n if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {\n if ('range' in optionsOrRange) {\n options = optionsOrRange;\n } else {\n const range = optionsOrRange as RangeBytes;\n validateRangeBytes(range, `getContent`);\n options = { range };\n }\n }\n\n const request = () => this.withContent(handle, {\n ...options,\n handler: async (content) => {\n const chunks: Uint8Array[] = [];\n for await (const chunk of content) {\n options.signal?.throwIfAborted();\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n }\n });\n\n try {\n return await request();\n } catch (error) {\n if (isOffByOneError(error)) {\n return await request();\n }\n throw error;\n }\n }\n\n /** Gets a content stream of a blob by a handle and calls handler with it. */\n public async withContent<T>(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options: GetContentOptions & {\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n const { range, signal, handler } = options;\n\n if (isLocalBlobHandle(handle)) {\n return await withFileContent({ path: this.getLocalPath(handle), range, signal, handler });\n }\n\n if (isRemoteBlobHandle(handle)) {\n const result = parseRemoteHandle(handle, this.signer);\n\n const key = blobKey(result.info.id);\n const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });\n signal?.throwIfAborted();\n\n if (filePath) return await withFileContent({ path: filePath, range, signal, handler });\n\n return await this.clientDownload.withBlobContent(\n result.info,\n { signal },\n options,\n async (content, size) => {\n const [handlerStream, cacheStream] = content.tee();\n \n const handlerPromise = handler(handlerStream, size);\n const _cachePromise = buffer(cacheStream)\n .then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data))\n .catch(() => {\n // Ignore cache errors - they shouldn't affect the main handler result\n // This prevents unhandled promise rejections when the stream fails\n });\n\n return await handlerPromise;\n }\n );\n }\n\n throw new Error('Malformed remote handle');\n }\n\n /**\n * Creates computable that will return blob content once it is downloaded.\n * Uses downloaded blob handle under the hood, so stores corresponding blob in file system.\n */\n public getComputableContent(\n res: ResourceInfo | PlTreeEntry,\n range?: RangeBytes,\n ): ComputableStableDefined<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getComputableContent`);\n }\n\n return Computable.make((ctx) =>\n this.getDownloadedBlob(res, ctx), {\n postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined\n }\n ).withStableType()\n }\n\n /** Returns all logs and schedules a job that reads remain logs.\n * Notifies when a new portion of the log appeared. */\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r as ResourceSnapshot, lines, callerId);\n if (result == undefined)\n ctx.markUnstable('either a file was not downloaded or logs was not read');\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getLastLogs', rInfo.type);\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToLastLines.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, lines);\n this.idToLastLines.set(blobKey(rInfo.id), newLogGetter);\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getProgressLogNoCtx(\n ctx.watcher,\n r as ResourceSnapshot,\n patternToSearch,\n callerId,\n );\n if (result === undefined)\n ctx.markUnstable('either a file was not downloaded or a progress log was not read');\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getProgressLog', rInfo.type);\n\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToProgressLog.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, 1, patternToSearch);\n this.idToProgressLog.set(blobKey(rInfo.id), newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns an Id of a smart object, that can read logs directly from\n * the platform. */\n public getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<AnyLogHandle>;\n public getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): AnyLogHandle;\n public getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<AnyLogHandle> | AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n return this.getLogHandleNoCtx(r as ResourceSnapshot);\n }\n\n private getLogHandleNoCtx(rInfo: ResourceSnapshot): AnyLogHandle {\n validateDownloadableResourceType('getLogHandle', rInfo.type);\n return newLogHandle(false, rInfo);\n }\n\n public async lastLines(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number, // if 0n, then start from the end.\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n public async readText(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n private async releaseBlob(rInfo: ResourceInfo, callerId: string) {\n const task = this.keyToDownload.get(blobKey(rInfo.id));\n if (task == undefined) {\n return;\n }\n\n if (this.cache.existsFile(blobKey(rInfo.id))) {\n const toDelete = this.cache.removeFile(blobKey(rInfo.id), callerId);\n\n await Promise.all(\n toDelete.map(async (cachedFile) => {\n await fsp.rm(cachedFile.path);\n\n this.cache.removeCache(cachedFile);\n\n this.removeTask(\n mapGet(this.keyToDownload, pathToKey(cachedFile.path)),\n `the task ${stringifyWithResourceId(cachedFile)} was removed`\n + `from cache along with ${stringifyWithResourceId(toDelete.map((d) => d.path))}`,\n );\n }),\n );\n } else {\n // The task is still in a downloading queue.\n const deleted = task.counter.dec(callerId);\n if (deleted) {\n this.removeTask(\n task,\n `the task ${stringifyWithResourceId(task.info())} was removed from cache`,\n );\n }\n }\n }\n\n private removeTask(task: DownloadBlobTask, reason: string) {\n task.abort(reason);\n task.change.markChanged(`download task for ${task.path} removed: ${reason}`);\n this.keyToDownload.delete(pathToKey(task.path));\n this.idToLastLines.delete(blobKey(task.rInfo.id));\n this.idToProgressLog.delete(blobKey(task.rInfo.id));\n }\n\n private async releaseOnDemandBlob(blobId: ResourceId, callerId: string) {\n const deleted = this.keyToOnDemand.get(blobKey(blobId))?.release(callerId) ?? false;\n if (deleted) this.keyToOnDemand.delete(blobKey(blobId));\n }\n\n /** Removes all files from a hard drive. */\n async releaseAll() {\n this.downloadQueue.stop();\n\n this.keyToDownload.forEach((task, key) => {\n this.keyToDownload.delete(key);\n task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);\n });\n }\n}\n\n/** Keeps a counter to the on demand handle. */\nclass OnDemandBlobHolder {\n private readonly counter = new CallersCounter();\n\n constructor(\n private readonly size: number,\n private readonly handle: RemoteBlobHandle,\n ) {}\n\n public getHandle(): RemoteBlobHandleAndSize {\n return { handle: this.handle, size: this.size };\n }\n\n public attach(callerId: string) {\n this.counter.inc(callerId);\n }\n\n public release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n}\n\nclass LastLinesGetter {\n private updater: Updater;\n private log: string | undefined;\n private readonly change: ChangeSource = new ChangeSource();\n private error: any | undefined = undefined;\n\n constructor(\n private readonly path: string,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {\n this.updater = new Updater(async () => this.update());\n }\n\n getOrSchedule(w: Watcher): {\n log: string | undefined;\n error?: any | undefined;\n } {\n this.change.attachWatcher(w);\n\n this.updater.schedule();\n\n return {\n log: this.log,\n error: this.error,\n };\n }\n\n async update(): Promise<void> {\n try {\n const newLogs = await getLastLines(this.path, this.lines, this.patternToSearch);\n\n if (this.log != newLogs) this.change.markChanged(`logs for ${this.path} updated`);\n this.log = newLogs;\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.log = '';\n this.error = e;\n this.change.markChanged(`log update for ${this.path} failed, resource not found`);\n return;\n }\n\n throw e;\n }\n }\n}\n\n/** Gets last lines from a file by reading the file from the top and keeping\n * last N lines in a window queue. */\nasync function getLastLines(fPath: string, nLines: number, patternToSearch?: string): Promise<string> {\n let inStream: fs.ReadStream | undefined;\n let rl: readline.Interface | undefined;\n\n try {\n inStream = fs.createReadStream(fPath);\n rl = readline.createInterface({ input: inStream, crlfDelay: Infinity });\n\n const lines = new Denque();\n\n for await (const line of rl) {\n if (patternToSearch != undefined && !line.includes(patternToSearch)) continue;\n\n lines.push(line);\n if (lines.length > nLines) {\n lines.shift();\n }\n }\n\n // last EOL is for keeping backward compat with platforma implementation.\n return lines.toArray().join(os.EOL) + os.EOL;\n } finally {\n // Cleanup resources in finally block to ensure they're always cleaned up\n try {\n if (rl) {\n rl.close();\n }\n } catch (cleanupError) {\n console.error('Error closing readline interface:', cleanupError);\n }\n\n try {\n if (inStream && !inStream.destroyed) {\n inStream.destroy();\n }\n } catch (cleanupError) {\n console.error('Error destroying read stream:', cleanupError);\n }\n }\n}\n\nfunction validateDownloadableResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('Blob/')) {\n let message = `${methodName}: wrong resource type: ${rType.name}, expected: a resource of type that starts with 'Blob/'.`;\n if (rType.name == 'Blob')\n message += ` If it's called from workflow, should a file be exported with 'file.exportFile' function?`;\n\n throw new WrongResourceTypeError(message);\n }\n}\n"],"names":["FilesCache","SparseCacheFsRanges","SparseCacheFsFile","SparseCache","TaskProcessor","path","Computable","treeEntryToResourceInfo","randomUUID","blobKey","DownloadBlobTask","newLocalHandle","nonRecoverableError","isPlTreeEntry","makeResourceSnapshot","OnDemandBlobResourceSnapshot","getSize","newRemoteHandle","parseLocalHandle","validateRangeBytes","isOffByOneError","isLocalBlobHandle","withFileContent","isRemoteBlobHandle","parseRemoteHandle","buffer","newLogHandle","getResourceInfoFromLogHandle","fsp","mapGet","pathToKey","stringifyWithResourceId","resourceIdToString","CallersCounter","ChangeSource","Updater","fs","readline","os","WrongResourceTypeError"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0FA;AAC6D;MAChD,cAAc,CAAA;AAoBN,IAAA,MAAA;AACA,IAAA,cAAA;AACA,IAAA,UAAA;AAEA,IAAA,cAAA;AACA,IAAA,MAAA;AACA,IAAA,GAAA;;AAxBX,IAAA,aAAa,GAAkC,IAAI,GAAG,EAAE;AAEhE;AAC+B;AACvB,IAAA,KAAK;AACL,IAAA,WAAW;;AAGX,IAAA,aAAa;AAEb,IAAA,aAAa,GAAoC,IAAI,GAAG,EAAE;AAE1D,IAAA,aAAa,GAAiC,IAAI,GAAG,EAAE;AACvD,IAAA,eAAe,GAAiC,IAAI,GAAG,EAAE;AAEhD,IAAA,OAAO;AAExB,IAAA,WAAA,CACmB,MAAgB,EAChB,cAA8B,EAC9B,UAAsB,EACvC,OAAe,EACE,cAAsB,EACtB,MAAc,EACd,GAAsB,EAAA;QANtB,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,cAAc,GAAd,cAAc;QACd,IAAA,CAAA,UAAU,GAAV,UAAU;QAEV,IAAA,CAAA,cAAc,GAAd,cAAc;QACd,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,GAAG,GAAH,GAAG;AAEpB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAIA,sBAAU,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAExD,QAAA,MAAM,QAAQ,GAAG,IAAIC,yBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC;AAC1E,QAAA,MAAM,SAAS,GAAG,IAAIC,uBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC;QACzE,IAAI,CAAC,WAAW,GAAG,IAAIC,iBAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,QAAQ,EAAE,SAAS,CAAC;AAEtG,QAAA,IAAI,CAAC,aAAa,GAAG,IAAIC,uBAAa,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,oBAAoB,CAAC;QAE7E,IAAI,CAAC,OAAO,GAAGC,eAAI,CAAC,OAAO,CAAC,OAAO,CAAC;IACtC;AAEA,IAAA,aAAa,IAAI,CACf,MAAgB,EAChB,cAA8B,EAC9B,UAAsB,EACtB,OAAe,EACf,cAAsB,EACtB,MAAc,EACd,GAAsB,EAAA;AAEtB,QAAA,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC;AAC3G,QAAA,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE;AAEhC,QAAA,OAAO,MAAM;IACf;IAUO,iBAAiB,CACtB,GAA+B,EAC/B,GAAmB,EAAA;AAEnB,QAAA,IAAI,GAAG,KAAK,SAAS,EAAE;AACrB,YAAA,OAAOC,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnE;QAEA,MAAM,KAAK,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAE/C,QAAA,MAAM,QAAQ,GAAGC,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAEzD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,OAAO,EAAE,KAAyB,EAAE,QAAQ,CAAC;AAC5F,QAAA,IAAI,MAAM,IAAI,SAAS,EAAE;AACvB,YAAA,GAAG,CAAC,YAAY,CAAC,kCAAkC,CAAC;QACtD;AAEA,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,sBAAsB,CAC5B,CAAU,EACV,KAAuB,EACvB,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,mBAAmB,EAAE,KAAK,CAAC,IAAI,CAAC;;;QAKjE,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC;AAClD,QAAA,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC;AAExB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;AAC7B,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;AAChB,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;AACpB,YAAA,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK;QAC5B;AACA,QAAA,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK;IAC3B;IAEQ,eAAe,CACrB,KAAuB,EACvB,QAAgB,EAAA;QAEhB,MAAM,GAAG,GAAGC,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAE7B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAChD,IAAI,YAAY,EAAE;AAChB,YAAA,OAAO,YAAY;QACrB;;AAGA,QAAA,MAAM,KAAK,GAAGJ,eAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC;QAE7C,MAAM,OAAO,GAAG,IAAIK,mCAAgB,CAClC,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,cAAc,EACnB,KAAK,EACLC,oCAAc,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAClC,KAAK,CACN;QACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC;AAEpC,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACtB,EAAE,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC9C,yBAAyB,EAAE,CAAC,CAAC,KAAK,CAACC,sCAAmB,CAAC,CAAC,CAAC;AAC1D,SAAA,CAAC;AAEF,QAAA,OAAO,OAAO;IAChB;AAEQ,IAAA,MAAM,YAAY,CAAC,IAAsB,EAAE,QAAgB,EAAA;AACjE,QAAA,MAAM,IAAI,CAAC,QAAQ,EAAE;AACrB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE;QAC3B,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE;YAC/B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrC;IACF;IAkBO,eAAe,CACpB,GAA+C,EAC/C,GAAmB,EAAA;QAEnB,IAAI,GAAG,KAAK,SAAS;AAAE,YAAA,OAAON,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAEtF,QAAA,MAAM,KAAK,GAAiCO,oBAAa,CAAC,GAAG;cACzDC,2BAAoB,CAAC,GAAG,EAAEC,kCAA4B,EAAE,GAAG;cAC3D,GAAG;AAEP,QAAA,MAAM,QAAQ,GAAGP,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;;;QAIpE,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;AAEzD,QAAA,OAAO,MAAM;IACf;IAEQ,oBAAoB,CAC1B,IAAkC,EAClC,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC;AAE9D,QAAA,IAAI,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACC,gBAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEnD,QAAA,IAAI,IAAI,KAAK,SAAS,EAAE;AACtB,YAAA,IAAI,GAAG,IAAI,kBAAkB,CAACO,aAAO,CAAC,IAAI,CAAC,EAAEC,sCAAe,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;AAChF,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAACR,gBAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAChD;AAEA,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;AAErB,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE;IACzB;;AAGO,IAAA,YAAY,CAAC,MAAuB,EAAA;AACzC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAGS,sCAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AACtD,QAAA,OAAO,IAAI;IACb;AAYO,IAAA,MAAM,UAAU,CACrB,MAA0C,EAC1C,cAA+C,EAAA;QAE/C,IAAI,OAAO,GAAsB,EAAE;QACnC,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE;AACjE,YAAA,IAAI,OAAO,IAAI,cAAc,EAAE;gBAC7B,OAAO,GAAG,cAAc;YAC1B;iBAAO;gBACL,MAAM,KAAK,GAAG,cAA4B;AAC1C,gBAAAC,gCAAkB,CAAC,KAAK,EAAE,CAAA,UAAA,CAAY,CAAC;AACvC,gBAAA,OAAO,GAAG,EAAE,KAAK,EAAE;YACrB;QACF;QAEA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;AAC7C,YAAA,GAAG,OAAO;AACV,YAAA,OAAO,EAAE,OAAO,OAAO,KAAI;gBACzB,MAAM,MAAM,GAAiB,EAAE;AAC/B,gBAAA,WAAW,MAAM,KAAK,IAAI,OAAO,EAAE;AACjC,oBAAA,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE;AAChC,oBAAA,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBACpB;AACA,gBAAA,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YAC9B;AACD,SAAA,CAAC;AAEF,QAAA,IAAI;YACF,OAAO,MAAM,OAAO,EAAE;QACxB;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,IAAIC,+BAAe,CAAC,KAAK,CAAC,EAAE;gBAC1B,OAAO,MAAM,OAAO,EAAE;YACxB;AACA,YAAA,MAAM,KAAK;QACb;IACF;;AAGO,IAAA,MAAM,WAAW,CACtB,MAA0C,EAC1C,OAEC,EAAA;QAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;AAE1C,QAAA,IAAIC,uCAAiB,CAAC,MAAM,CAAC,EAAE;YAC7B,OAAO,MAAMC,yBAAe,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC3F;AAEA,QAAA,IAAIC,yCAAkB,CAAC,MAAM,CAAC,EAAE;YAC9B,MAAM,MAAM,GAAGC,wCAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;YAErD,MAAM,GAAG,GAAGf,gBAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;YACvF,MAAM,EAAE,cAAc,EAAE;AAExB,YAAA,IAAI,QAAQ;AAAE,gBAAA,OAAO,MAAMa,yBAAe,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAEtF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAC9C,MAAM,CAAC,IAAI,EACX,EAAE,MAAM,EAAE,EACV,OAAO,EACP,OAAO,OAAO,EAAE,IAAI,KAAI;gBACtB,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE;gBAElD,MAAM,cAAc,GAAG,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC;AACnD,gBAAsBG,gBAAM,CAAC,WAAW;AACrC,qBAAA,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC;qBACrF,KAAK,CAAC,MAAK;;;AAGZ,gBAAA,CAAC;gBAEH,OAAO,MAAM,cAAc;AAC7B,YAAA,CAAC,CACF;QACH;AAEA,QAAA,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;IAC5C;AAEA;;;AAGG;IACI,oBAAoB,CACzB,GAA+B,EAC/B,KAAkB,EAAA;QAElB,IAAI,KAAK,EAAE;AACT,YAAAN,gCAAkB,CAAC,KAAK,EAAE,CAAA,oBAAA,CAAsB,CAAC;QACnD;AAEA,QAAA,OAAOb,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KACzB,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;YAClC,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG;SACrE,CACA,CAAC,cAAc,EAAE;IACpB;AAaO,IAAA,WAAW,CAChB,GAA+B,EAC/B,KAAa,EACb,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;YAAE,OAAOA,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAExF,MAAM,CAAC,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAC3C,QAAA,MAAM,QAAQ,GAAGC,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,CAAqB,EAAE,KAAK,EAAE,QAAQ,CAAC;QACzF,IAAI,MAAM,IAAI,SAAS;AACrB,YAAA,GAAG,CAAC,YAAY,CAAC,uDAAuD,CAAC;AAE3E,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,gBAAgB,CACtB,CAAU,EACV,KAAuB,EACvB,KAAa,EACb,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC;AAC3D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC5D,IAAI,IAAI,IAAI,SAAS;AAAE,YAAA,OAAO,SAAS;AAEvC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAGU,sCAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AAE3D,QAAA,IAAI,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACT,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAEzD,QAAA,IAAI,SAAS,IAAI,SAAS,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC;AACrD,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;YACvD,SAAS,GAAG,YAAY;QAC1B;QAEA,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,MAAM,MAAM,CAAC,KAAK;QAEpC,OAAO,MAAM,CAAC,GAAG;IACnB;AAaO,IAAA,cAAc,CACnB,GAA+B,EAC/B,eAAuB,EACvB,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;YAClB,OAAOH,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,CAAC,CAAC;QAEjF,MAAM,CAAC,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAC3C,QAAA,MAAM,QAAQ,GAAGC,sBAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CACrC,GAAG,CAAC,OAAO,EACX,CAAqB,EACrB,eAAe,EACf,QAAQ,CACT;QACD,IAAI,MAAM,KAAK,SAAS;AACtB,YAAA,GAAG,CAAC,YAAY,CAAC,iEAAiE,CAAC;AAErF,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,mBAAmB,CACzB,CAAU,EACV,KAAuB,EACvB,eAAuB,EACvB,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC;AAE9D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC5D,IAAI,IAAI,IAAI,SAAS;AAAE,YAAA,OAAO,SAAS;AACvC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAGU,sCAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AAE3D,QAAA,IAAI,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAACT,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAE3D,QAAA,IAAI,SAAS,IAAI,SAAS,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,eAAe,CAAC;AAClE,YAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;YAEzD,SAAS,GAAG,YAAY;QAC1B;QAEA,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,MAAM,MAAM,CAAC,KAAK;QAEpC,OAAO,MAAM,CAAC,GAAG;IACnB;IAMO,YAAY,CACjB,GAA+B,EAC/B,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;AAAE,YAAA,OAAOH,qBAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAElF,MAAM,CAAC,GAAGC,8BAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAE3C,QAAA,OAAO,IAAI,CAAC,iBAAiB,CAAC,CAAqB,CAAC;IACtD;AAEQ,IAAA,iBAAiB,CAAC,KAAuB,EAAA;AAC/C,QAAA,gCAAgC,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC;AAC5D,QAAA,OAAOmB,wBAAY,CAAC,KAAK,EAAE,KAAK,CAAC;IACnC;IAEO,MAAM,SAAS,CACpB,MAAsB,EACtB,SAAiB,EACjB,WAAoB;IACpB,SAAkB,EAAA;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAC1CC,wCAA4B,CAAC,MAAM,CAAC,EACpC,SAAS,EACT,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,EACxB,SAAS,CACV;QAED,OAAO;AACL,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,YAAA,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAClC;IACH;IAEO,MAAM,QAAQ,CACnB,MAAsB,EACtB,SAAiB,EACjB,WAAoB,EACpB,SAAkB,EAAA;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CACzCA,wCAA4B,CAAC,MAAM,CAAC,EACpC,SAAS,EACT,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,EACxB,SAAS,CACV;QAED,OAAO;AACL,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,YAAA,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAClC;IACH;AAEQ,IAAA,MAAM,WAAW,CAAC,KAAmB,EAAE,QAAgB,EAAA;AAC7D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAClB,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACtD,QAAA,IAAI,IAAI,IAAI,SAAS,EAAE;YACrB;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE;AAC5C,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAACA,gBAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC;AAEnE,YAAA,MAAM,OAAO,CAAC,GAAG,CACf,QAAQ,CAAC,GAAG,CAAC,OAAO,UAAU,KAAI;gBAChC,MAAMmB,cAAG,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;AAE7B,gBAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC;gBAElC,IAAI,CAAC,UAAU,CACbC,gBAAM,CAAC,IAAI,CAAC,aAAa,EAAEC,kBAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EACtD,YAAYC,gCAAuB,CAAC,UAAU,CAAC,CAAA,YAAA;AAC7C,sBAAA,CAAA,sBAAA,EAAyBA,gCAAuB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA,CAAE,CAClF;YACH,CAAC,CAAC,CACH;QACH;aAAO;;YAEL,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1C,IAAI,OAAO,EAAE;AACX,gBAAA,IAAI,CAAC,UAAU,CACb,IAAI,EACJ,CAAA,SAAA,EAAYA,gCAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA,uBAAA,CAAyB,CAC1E;YACH;QACF;IACF;IAEQ,UAAU,CAAC,IAAsB,EAAE,MAAc,EAAA;AACvD,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;AAClB,QAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,kBAAA,EAAqB,IAAI,CAAC,IAAI,CAAA,UAAA,EAAa,MAAM,CAAA,CAAE,CAAC;AAC5E,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAACD,kBAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/C,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAACrB,gBAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACjD,QAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAACA,gBAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrD;AAEQ,IAAA,MAAM,mBAAmB,CAAC,MAAkB,EAAE,QAAgB,EAAA;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAACA,gBAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK;AACnF,QAAA,IAAI,OAAO;YAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAACA,gBAAO,CAAC,MAAM,CAAC,CAAC;IACzD;;AAGA,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE;QAEzB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,KAAI;AACvC,YAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC;AAC9B,YAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQuB,2BAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA,SAAA,CAAW,CAAC;AAC/E,QAAA,CAAC,CAAC;IACJ;AACD;AAED;AACA,MAAM,kBAAkB,CAAA;AAIH,IAAA,IAAA;AACA,IAAA,MAAA;AAJF,IAAA,OAAO,GAAG,IAAIC,wBAAc,EAAE;IAE/C,WAAA,CACmB,IAAY,EACZ,MAAwB,EAAA;QADxB,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,MAAM,GAAN,MAAM;IACtB;IAEI,SAAS,GAAA;AACd,QAAA,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IACjD;AAEO,IAAA,MAAM,CAAC,QAAgB,EAAA;AAC5B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC5B;AAEO,IAAA,OAAO,CAAC,QAAgB,EAAA;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnC;AACD;AAED,MAAM,eAAe,CAAA;AAOA,IAAA,IAAA;AACA,IAAA,KAAA;AACA,IAAA,eAAA;AARX,IAAA,OAAO;AACP,IAAA,GAAG;AACM,IAAA,MAAM,GAAiB,IAAIC,uBAAY,EAAE;IAClD,KAAK,GAAoB,SAAS;AAE1C,IAAA,WAAA,CACmB,IAAY,EACZ,KAAa,EACb,eAAwB,EAAA;QAFxB,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,KAAK,GAAL,KAAK;QACL,IAAA,CAAA,eAAe,GAAf,eAAe;AAEhC,QAAA,IAAI,CAAC,OAAO,GAAG,IAAIC,eAAO,CAAC,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC;IACvD;AAEA,IAAA,aAAa,CAAC,CAAU,EAAA;AAItB,QAAA,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAE5B,QAAA,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;QAEvB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB;IACH;AAEA,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,IAAI;AACF,YAAA,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC;AAE/E,YAAA,IAAI,IAAI,CAAC,GAAG,IAAI,OAAO;gBAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,SAAA,EAAY,IAAI,CAAC,IAAI,CAAA,QAAA,CAAU,CAAC;AACjF,YAAA,IAAI,CAAC,GAAG,GAAG,OAAO;QACpB;QAAE,OAAO,CAAM,EAAE;AACf,YAAA,IAAI,CAAC,CAAC,IAAI,IAAI,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,WAAW,EAAE;;AAEjD,gBAAA,IAAI,CAAC,GAAG,GAAG,EAAE;AACb,gBAAA,IAAI,CAAC,KAAK,GAAG,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,eAAA,EAAkB,IAAI,CAAC,IAAI,CAAA,2BAAA,CAA6B,CAAC;gBACjF;YACF;AAEA,YAAA,MAAM,CAAC;QACT;IACF;AACD;AAED;AACqC;AACrC,eAAe,YAAY,CAAC,KAAa,EAAE,MAAc,EAAE,eAAwB,EAAA;AACjF,IAAA,IAAI,QAAmC;AACvC,IAAA,IAAI,EAAkC;AAEtC,IAAA,IAAI;AACF,QAAA,QAAQ,GAAGC,aAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC;AACrC,QAAA,EAAE,GAAGC,mBAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAEvE,QAAA,MAAM,KAAK,GAAG,IAAI,MAAM,EAAE;AAE1B,QAAA,WAAW,MAAM,IAAI,IAAI,EAAE,EAAE;YAC3B,IAAI,eAAe,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;gBAAE;AAErE,YAAA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAChB,YAAA,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE;gBACzB,KAAK,CAAC,KAAK,EAAE;YACf;QACF;;AAGA,QAAA,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAACC,aAAE,CAAC,GAAG,CAAC,GAAGA,aAAE,CAAC,GAAG;IAC9C;YAAU;;AAER,QAAA,IAAI;YACF,IAAI,EAAE,EAAE;gBACN,EAAE,CAAC,KAAK,EAAE;YACZ;QACF;QAAE,OAAO,YAAY,EAAE;AACrB,YAAA,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,YAAY,CAAC;QAClE;AAEA,QAAA,IAAI;AACF,YAAA,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;gBACnC,QAAQ,CAAC,OAAO,EAAE;YACpB;QACF;QAAE,OAAO,YAAY,EAAE;AACrB,YAAA,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,YAAY,CAAC;QAC9D;IACF;AACF;AAEA,SAAS,gCAAgC,CAAC,UAAkB,EAAE,KAAmB,EAAA;IAC/E,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QACnC,IAAI,OAAO,GAAG,CAAA,EAAG,UAAU,0BAA0B,KAAK,CAAC,IAAI,CAAA,wDAAA,CAA0D;AACzH,QAAA,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM;YACtB,OAAO,IAAI,2FAA2F;AAExG,QAAA,MAAM,IAAIC,8BAAsB,CAAC,OAAO,CAAC;IAC3C;AACF;;;;"}
|
|
1
|
+
{"version":3,"file":"download_blob.cjs","sources":["../../../src/drivers/download_blob/download_blob.ts"],"sourcesContent":["import type {\n ComputableCtx,\n ComputableStableDefined,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n} from '@milaboratories/computable';\nimport type { ResourceId, ResourceType } from '@milaboratories/pl-client';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type {\n AnyLogHandle,\n BlobDriver,\n ContentHandler,\n GetContentOptions,\n LocalBlobHandle,\n LocalBlobHandleAndSize,\n ReadyLogHandle,\n RemoteBlobHandle,\n RemoteBlobHandleAndSize,\n StreamingApiResponse,\n} from '@milaboratories/pl-model-common';\nimport { type RangeBytes, validateRangeBytes } from '@milaboratories/pl-model-common';\nimport type {\n PlTreeEntry,\n ResourceInfo,\n ResourceSnapshot\n} from '@milaboratories/pl-tree';\nimport {\n isPlTreeEntry,\n makeResourceSnapshot,\n treeEntryToResourceInfo,\n} from '@milaboratories/pl-tree';\nimport type { MiLogger, Signer } from '@milaboratories/ts-helpers';\nimport { CallersCounter, mapGet, TaskProcessor } from '@milaboratories/ts-helpers';\nimport Denque from 'denque';\nimport * as fs from 'fs';\nimport { randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline/promises';\nimport { buffer } from 'node:stream/consumers';\nimport { Readable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport type { ClientLogs } from '../../clients/logs';\nimport { withFileContent } from '../helpers/read_file';\nimport {\n isLocalBlobHandle,\n newLocalHandle,\n parseLocalHandle,\n} from '../helpers/download_local_handle';\nimport {\n isRemoteBlobHandle,\n newRemoteHandle,\n parseRemoteHandle,\n} from '../helpers/download_remote_handle';\nimport { Updater, WrongResourceTypeError } from '../helpers/helpers';\nimport { getResourceInfoFromLogHandle, newLogHandle } from '../helpers/logs_handle';\nimport { getSize, OnDemandBlobResourceSnapshot } from '../types';\nimport { blobKey, pathToKey } from './blob_key';\nimport { DownloadBlobTask, nonRecoverableError } from './download_blob_task';\nimport { FilesCache } from '../helpers/files_cache';\nimport { SparseCache, SparseCacheFsFile, SparseCacheFsRanges } from './sparse_cache/cache';\nimport { isOffByOneError } from '../../helpers/download_errors';\n\nexport type DownloadDriverOps = {\n /**\n * A soft limit of the amount of blob storage, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one\n * when they become unneeded.\n * */\n cacheSoftSizeBytes: number;\n\n /**\n * A hard limit of the amount of sparse cache, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one.\n *\n * The sparse cache is used to store ranges of blobs.\n * */\n rangesCacheMaxSizeBytes: number;\n\n /**\n * Max number of concurrent downloads while calculating computable states\n * derived from this driver\n * */\n nConcurrentDownloads: number;\n};\n\n/** DownloadDriver holds a queue of downloading tasks,\n * and notifies every watcher when a file were downloaded. */\nexport class DownloadDriver implements BlobDriver, AsyncDisposable {\n /** Represents a unique key to the path of a blob as a map. */\n private keyToDownload: Map<string, DownloadBlobTask> = new Map();\n\n /** Writes and removes files to a hard drive and holds a counter for every\n * file that should be kept. */\n private cache: FilesCache<DownloadBlobTask>;\n private rangesCache: SparseCache;\n\n /** Downloads files and writes them to the local dir. */\n private downloadQueue: TaskProcessor;\n\n private keyToOnDemand: Map<string, OnDemandBlobHolder> = new Map();\n\n private idToLastLines: Map<string, LastLinesGetter> = new Map();\n private idToProgressLog: Map<string, LastLinesGetter> = new Map();\n\n private readonly saveDir: string;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n private readonly clientLogs: ClientLogs,\n saveDir: string,\n private readonly rangesCacheDir: string,\n private readonly signer: Signer,\n private readonly ops: DownloadDriverOps,\n ) {\n this.cache = new FilesCache(this.ops.cacheSoftSizeBytes);\n\n const fsRanges = new SparseCacheFsRanges(this.logger, this.rangesCacheDir);\n const fsStorage = new SparseCacheFsFile(this.logger, this.rangesCacheDir);\n this.rangesCache = new SparseCache(this.logger, this.ops.rangesCacheMaxSizeBytes, fsRanges, fsStorage);\n\n this.downloadQueue = new TaskProcessor(this.logger, ops.nConcurrentDownloads);\n\n this.saveDir = path.resolve(saveDir);\n }\n\n static async init(\n logger: MiLogger,\n clientDownload: ClientDownload,\n clientLogs: ClientLogs,\n saveDir: string,\n rangesCacheDir: string,\n signer: Signer,\n ops: DownloadDriverOps,\n ): Promise<DownloadDriver> {\n const driver = new DownloadDriver(logger, clientDownload, clientLogs, saveDir, rangesCacheDir, signer, ops);\n await driver.rangesCache.reset();\n\n return driver;\n }\n\n /** Gets a blob or part of the blob by its resource id or downloads a blob and sets it in a cache. */\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx: ComputableCtx,\n ): LocalBlobHandleAndSize | undefined;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ): ComputableStableDefined<LocalBlobHandleAndSize>;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<LocalBlobHandleAndSize | undefined> | LocalBlobHandleAndSize | undefined {\n if (ctx === undefined) {\n return Computable.make((ctx) => this.getDownloadedBlob(res, ctx));\n }\n\n const rInfo = treeEntryToResourceInfo(res, ctx);\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(rInfo, callerId));\n\n const result = this.getDownloadedBlobNoCtx(ctx.watcher, rInfo as ResourceSnapshot, callerId);\n if (result == undefined) {\n ctx.markUnstable('download blob is still undefined');\n }\n\n return result;\n }\n\n private getDownloadedBlobNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n callerId: string,\n ): LocalBlobHandleAndSize | undefined {\n validateDownloadableResourceType('getDownloadedBlob', rInfo.type);\n\n // We don't need to request files with wider limits,\n // PFrame's engine does it disk-optimally by itself.\n\n const task = this.getOrSetNewTask(rInfo, callerId);\n task.attach(w, callerId);\n\n const result = task.getBlob();\n if (!result.done) {\n return undefined;\n }\n if (result.result.ok) {\n return result.result.value;\n }\n throw result.result.error;\n }\n\n private getOrSetNewTask(\n rInfo: ResourceSnapshot,\n callerId: string,\n ): DownloadBlobTask {\n const key = blobKey(rInfo.id);\n\n const inMemoryTask = this.keyToDownload.get(key);\n if (inMemoryTask) {\n return inMemoryTask;\n }\n\n // schedule the blob downloading, then it'll be added to the cache.\n const fPath = path.resolve(this.saveDir, key);\n\n const newTask = new DownloadBlobTask(\n this.logger,\n this.clientDownload,\n rInfo,\n newLocalHandle(fPath, this.signer),\n fPath,\n );\n this.keyToDownload.set(key, newTask);\n\n this.downloadQueue.push({\n fn: () => this.downloadBlob(newTask, callerId),\n recoverableErrorPredicate: (e) => !nonRecoverableError(e),\n });\n\n return newTask;\n }\n\n private async downloadBlob(task: DownloadBlobTask, callerId: string) {\n await task.download();\n const blob = task.getBlob();\n if (blob.done && blob.result.ok) {\n this.cache.addCache(task, callerId);\n }\n }\n\n /** Gets on demand blob. */\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: undefined,\n fromBytes?: number,\n toBytes?: number,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx: ComputableCtx,\n fromBytes?: number,\n toBytes?: number,\n ): RemoteBlobHandleAndSize;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: ComputableCtx,\n ): ComputableStableDefined<RemoteBlobHandleAndSize> | RemoteBlobHandleAndSize | undefined {\n if (ctx === undefined) return Computable.make((ctx) => this.getOnDemandBlob(res, ctx));\n\n const rInfo: OnDemandBlobResourceSnapshot = isPlTreeEntry(res)\n ? makeResourceSnapshot(res, OnDemandBlobResourceSnapshot, ctx)\n : res;\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseOnDemandBlob(rInfo.id, callerId));\n\n // note that the watcher is not needed,\n // the handler never changes.\n const result = this.getOnDemandBlobNoCtx(rInfo, callerId);\n\n return result;\n }\n\n private getOnDemandBlobNoCtx(\n info: OnDemandBlobResourceSnapshot,\n callerId: string,\n ): RemoteBlobHandleAndSize {\n validateDownloadableResourceType('getOnDemandBlob', info.type);\n\n let blob = this.keyToOnDemand.get(blobKey(info.id));\n\n if (blob === undefined) {\n blob = new OnDemandBlobHolder(getSize(info), newRemoteHandle(info, this.signer));\n this.keyToOnDemand.set(blobKey(info.id), blob);\n }\n\n blob.attach(callerId);\n\n return blob.getHandle();\n }\n\n /** Gets a path from a handle. */\n public getLocalPath(handle: LocalBlobHandle): string {\n const { path } = parseLocalHandle(handle, this.signer);\n return path;\n }\n\n /** Gets a content of a blob by a handle. */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options?: GetContentOptions,\n ): Promise<Uint8Array>;\n /** @deprecated Use {@link getContent} with {@link GetContentOptions} instead */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n range?: RangeBytes,\n ): Promise<Uint8Array>;\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n optionsOrRange?: GetContentOptions | RangeBytes,\n ): Promise<Uint8Array> {\n let options: GetContentOptions = {};\n if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {\n if ('range' in optionsOrRange) {\n options = optionsOrRange;\n } else {\n const range = optionsOrRange as RangeBytes;\n validateRangeBytes(range, `getContent`);\n options = { range };\n }\n }\n\n const request = () => this.withContent(handle, {\n ...options,\n handler: async (content) => {\n const chunks: Uint8Array[] = [];\n for await (const chunk of content) {\n options.signal?.throwIfAborted();\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n }\n });\n\n try {\n return await request();\n } catch (error) {\n if (isOffByOneError(error)) {\n return await request();\n }\n throw error;\n }\n }\n\n /** Gets a content stream of a blob by a handle and calls handler with it. */\n public async withContent<T>(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options: GetContentOptions & {\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n const { range, signal, handler } = options;\n\n if (isLocalBlobHandle(handle)) {\n return await withFileContent({ path: this.getLocalPath(handle), range, signal, handler });\n }\n\n if (isRemoteBlobHandle(handle)) {\n const result = parseRemoteHandle(handle, this.signer);\n\n const key = blobKey(result.info.id);\n const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });\n signal?.throwIfAborted();\n\n if (filePath) return await withFileContent({ path: filePath, range, signal, handler });\n\n return await this.clientDownload.withBlobContent(\n result.info,\n { signal },\n options,\n async (content, size) => {\n const [handlerStream, cacheStream] = content.tee();\n \n const handlerPromise = handler(handlerStream, size);\n const _cachePromise = buffer(cacheStream)\n .then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data))\n .catch(() => {\n // Ignore cache errors - they shouldn't affect the main handler result\n // This prevents unhandled promise rejections when the stream fails\n });\n\n return await handlerPromise;\n }\n );\n }\n\n throw new Error('Malformed remote handle');\n }\n\n /**\n * Creates computable that will return blob content once it is downloaded.\n * Uses downloaded blob handle under the hood, so stores corresponding blob in file system.\n */\n public getComputableContent(\n res: ResourceInfo | PlTreeEntry,\n range?: RangeBytes,\n ): ComputableStableDefined<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getComputableContent`);\n }\n\n return Computable.make((ctx) =>\n this.getDownloadedBlob(res, ctx), {\n postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined\n }\n ).withStableType()\n }\n\n /** Returns all logs and schedules a job that reads remain logs.\n * Notifies when a new portion of the log appeared. */\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r as ResourceSnapshot, lines, callerId);\n if (result == undefined)\n ctx.markUnstable('either a file was not downloaded or logs was not read');\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getLastLogs', rInfo.type);\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToLastLines.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, lines);\n this.idToLastLines.set(blobKey(rInfo.id), newLogGetter);\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getProgressLogNoCtx(\n ctx.watcher,\n r as ResourceSnapshot,\n patternToSearch,\n callerId,\n );\n if (result === undefined)\n ctx.markUnstable('either a file was not downloaded or a progress log was not read');\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getProgressLog', rInfo.type);\n\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToProgressLog.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, 1, patternToSearch);\n this.idToProgressLog.set(blobKey(rInfo.id), newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns an Id of a smart object, that can read logs directly from\n * the platform. */\n public getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<AnyLogHandle>;\n public getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): AnyLogHandle;\n public getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<AnyLogHandle> | AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n return this.getLogHandleNoCtx(r as ResourceSnapshot);\n }\n\n private getLogHandleNoCtx(rInfo: ResourceSnapshot): AnyLogHandle {\n validateDownloadableResourceType('getLogHandle', rInfo.type);\n return newLogHandle(false, rInfo);\n }\n\n public async lastLines(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number, // if 0n, then start from the end.\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n public async readText(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n private async releaseBlob(rInfo: ResourceInfo, callerId: string) {\n const task = this.keyToDownload.get(blobKey(rInfo.id));\n if (task == undefined) {\n return;\n }\n\n if (this.cache.existsFile(blobKey(rInfo.id))) {\n const toDelete = this.cache.removeFile(blobKey(rInfo.id), callerId);\n\n await Promise.all(\n toDelete.map(async (cachedFile) => {\n await fsp.rm(cachedFile.path);\n\n this.cache.removeCache(cachedFile);\n\n this.removeTask(\n mapGet(this.keyToDownload, pathToKey(cachedFile.path)),\n `the task ${stringifyWithResourceId(cachedFile)} was removed`\n + `from cache along with ${stringifyWithResourceId(toDelete.map((d) => d.path))}`,\n );\n }),\n );\n } else {\n // The task is still in a downloading queue.\n const deleted = task.counter.dec(callerId);\n if (deleted) {\n this.removeTask(\n task,\n `the task ${stringifyWithResourceId(task.info())} was removed from cache`,\n );\n }\n }\n }\n\n private removeTask(task: DownloadBlobTask, reason: string) {\n task.abort(reason);\n task.change.markChanged(`download task for ${task.path} removed: ${reason}`);\n this.keyToDownload.delete(pathToKey(task.path));\n this.idToLastLines.delete(blobKey(task.rInfo.id));\n this.idToProgressLog.delete(blobKey(task.rInfo.id));\n }\n\n private async releaseOnDemandBlob(blobId: ResourceId, callerId: string) {\n const deleted = this.keyToOnDemand.get(blobKey(blobId))?.release(callerId) ?? false;\n if (deleted) this.keyToOnDemand.delete(blobKey(blobId));\n }\n\n /** Removes all files from a hard drive. */\n async releaseAll() {\n this.downloadQueue.stop();\n\n this.keyToDownload.forEach((task, key) => {\n this.keyToDownload.delete(key);\n task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);\n });\n }\n\n async dispose(): Promise<void> {\n await this.rangesCache.dispose();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\n/** Keeps a counter to the on demand handle. */\nclass OnDemandBlobHolder {\n private readonly counter = new CallersCounter();\n\n constructor(\n private readonly size: number,\n private readonly handle: RemoteBlobHandle,\n ) {}\n\n public getHandle(): RemoteBlobHandleAndSize {\n return { handle: this.handle, size: this.size };\n }\n\n public attach(callerId: string) {\n this.counter.inc(callerId);\n }\n\n public release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n}\n\nclass LastLinesGetter {\n private updater: Updater;\n private log: string | undefined;\n private readonly change: ChangeSource = new ChangeSource();\n private error: any | undefined = undefined;\n\n constructor(\n private readonly path: string,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {\n this.updater = new Updater(async () => this.update());\n }\n\n getOrSchedule(w: Watcher): {\n log: string | undefined;\n error?: any | undefined;\n } {\n this.change.attachWatcher(w);\n\n this.updater.schedule();\n\n return {\n log: this.log,\n error: this.error,\n };\n }\n\n async update(): Promise<void> {\n try {\n const newLogs = await getLastLines(this.path, this.lines, this.patternToSearch);\n\n if (this.log != newLogs) this.change.markChanged(`logs for ${this.path} updated`);\n this.log = newLogs;\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.log = '';\n this.error = e;\n this.change.markChanged(`log update for ${this.path} failed, resource not found`);\n return;\n }\n\n throw e;\n }\n }\n}\n\n/** Gets last lines from a file by reading the file from the top and keeping\n * last N lines in a window queue. */\nasync function getLastLines(fPath: string, nLines: number, patternToSearch?: string): Promise<string> {\n let inStream: fs.ReadStream | undefined;\n let rl: readline.Interface | undefined;\n\n try {\n inStream = fs.createReadStream(fPath);\n rl = readline.createInterface({ input: inStream, crlfDelay: Infinity });\n\n const lines = new Denque();\n\n for await (const line of rl) {\n if (patternToSearch != undefined && !line.includes(patternToSearch)) continue;\n\n lines.push(line);\n if (lines.length > nLines) {\n lines.shift();\n }\n }\n\n // last EOL is for keeping backward compat with platforma implementation.\n return lines.toArray().join(os.EOL) + os.EOL;\n } finally {\n // Cleanup resources in finally block to ensure they're always cleaned up\n try {\n if (rl) {\n rl.close();\n }\n } catch (cleanupError) {\n console.error('Error closing readline interface:', cleanupError);\n }\n\n try {\n if (inStream && !inStream.destroyed) {\n inStream.destroy();\n }\n } catch (cleanupError) {\n console.error('Error destroying read stream:', cleanupError);\n }\n }\n}\n\nfunction validateDownloadableResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('Blob/')) {\n let message = `${methodName}: wrong resource type: ${rType.name}, expected: a resource of type that starts with 'Blob/'.`;\n if (rType.name == 'Blob')\n message += ` If it's called from workflow, should a file be exported with 'file.exportFile' function?`;\n\n throw new WrongResourceTypeError(message);\n }\n}\n"],"names":["FilesCache","SparseCacheFsRanges","SparseCacheFsFile","SparseCache","TaskProcessor","path","Computable","treeEntryToResourceInfo","randomUUID","blobKey","DownloadBlobTask","newLocalHandle","nonRecoverableError","isPlTreeEntry","makeResourceSnapshot","OnDemandBlobResourceSnapshot","getSize","newRemoteHandle","parseLocalHandle","validateRangeBytes","isOffByOneError","isLocalBlobHandle","withFileContent","isRemoteBlobHandle","parseRemoteHandle","buffer","newLogHandle","getResourceInfoFromLogHandle","fsp","mapGet","pathToKey","stringifyWithResourceId","resourceIdToString","CallersCounter","ChangeSource","Updater","fs","readline","os","WrongResourceTypeError"],"mappings":"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"}
|
|
@@ -29,7 +29,7 @@ export type DownloadDriverOps = {
|
|
|
29
29
|
};
|
|
30
30
|
/** DownloadDriver holds a queue of downloading tasks,
|
|
31
31
|
* and notifies every watcher when a file were downloaded. */
|
|
32
|
-
export declare class DownloadDriver implements BlobDriver {
|
|
32
|
+
export declare class DownloadDriver implements BlobDriver, AsyncDisposable {
|
|
33
33
|
private readonly logger;
|
|
34
34
|
private readonly clientDownload;
|
|
35
35
|
private readonly clientLogs;
|
|
@@ -99,5 +99,7 @@ export declare class DownloadDriver implements BlobDriver {
|
|
|
99
99
|
private releaseOnDemandBlob;
|
|
100
100
|
/** Removes all files from a hard drive. */
|
|
101
101
|
releaseAll(): Promise<void>;
|
|
102
|
+
dispose(): Promise<void>;
|
|
103
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
102
104
|
}
|
|
103
105
|
//# sourceMappingURL=download_blob.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download_blob.d.ts","sourceRoot":"","sources":["../../../src/drivers/download_blob/download_blob.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EAExB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAEL,UAAU,EACX,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,EACV,YAAY,EACZ,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,KAAK,UAAU,EAAsB,MAAM,iCAAiC,CAAC;AACtF,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEb,MAAM,yBAAyB,CAAC;AAMjC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAWnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAcrD,OAAO,EAAW,4BAA4B,EAAE,MAAM,UAAU,CAAC;AAOjE,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;SAIK;IACL,kBAAkB,EAAE,MAAM,CAAC;IAE3B;;;;;SAKK;IACL,uBAAuB,EAAE,MAAM,CAAC;IAEhC;;;SAGK;IACL,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF;6DAC6D;AAC7D,qBAAa,cAAe,YAAW,UAAU;
|
|
1
|
+
{"version":3,"file":"download_blob.d.ts","sourceRoot":"","sources":["../../../src/drivers/download_blob/download_blob.ts"],"names":[],"mappings":"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"}
|
|
@@ -343,6 +343,12 @@ class DownloadDriver {
|
|
|
343
343
|
task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);
|
|
344
344
|
});
|
|
345
345
|
}
|
|
346
|
+
async dispose() {
|
|
347
|
+
await this.rangesCache.dispose();
|
|
348
|
+
}
|
|
349
|
+
async [Symbol.asyncDispose]() {
|
|
350
|
+
await this.dispose();
|
|
351
|
+
}
|
|
346
352
|
}
|
|
347
353
|
/** Keeps a counter to the on demand handle. */
|
|
348
354
|
class OnDemandBlobHolder {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download_blob.js","sources":["../../../src/drivers/download_blob/download_blob.ts"],"sourcesContent":["import type {\n ComputableCtx,\n ComputableStableDefined,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n} from '@milaboratories/computable';\nimport type { ResourceId, ResourceType } from '@milaboratories/pl-client';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type {\n AnyLogHandle,\n BlobDriver,\n ContentHandler,\n GetContentOptions,\n LocalBlobHandle,\n LocalBlobHandleAndSize,\n ReadyLogHandle,\n RemoteBlobHandle,\n RemoteBlobHandleAndSize,\n StreamingApiResponse,\n} from '@milaboratories/pl-model-common';\nimport { type RangeBytes, validateRangeBytes } from '@milaboratories/pl-model-common';\nimport type {\n PlTreeEntry,\n ResourceInfo,\n ResourceSnapshot\n} from '@milaboratories/pl-tree';\nimport {\n isPlTreeEntry,\n makeResourceSnapshot,\n treeEntryToResourceInfo,\n} from '@milaboratories/pl-tree';\nimport type { MiLogger, Signer } from '@milaboratories/ts-helpers';\nimport { CallersCounter, mapGet, TaskProcessor } from '@milaboratories/ts-helpers';\nimport Denque from 'denque';\nimport * as fs from 'fs';\nimport { randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline/promises';\nimport { buffer } from 'node:stream/consumers';\nimport { Readable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport type { ClientLogs } from '../../clients/logs';\nimport { withFileContent } from '../helpers/read_file';\nimport {\n isLocalBlobHandle,\n newLocalHandle,\n parseLocalHandle,\n} from '../helpers/download_local_handle';\nimport {\n isRemoteBlobHandle,\n newRemoteHandle,\n parseRemoteHandle,\n} from '../helpers/download_remote_handle';\nimport { Updater, WrongResourceTypeError } from '../helpers/helpers';\nimport { getResourceInfoFromLogHandle, newLogHandle } from '../helpers/logs_handle';\nimport { getSize, OnDemandBlobResourceSnapshot } from '../types';\nimport { blobKey, pathToKey } from './blob_key';\nimport { DownloadBlobTask, nonRecoverableError } from './download_blob_task';\nimport { FilesCache } from '../helpers/files_cache';\nimport { SparseCache, SparseCacheFsFile, SparseCacheFsRanges } from './sparse_cache/cache';\nimport { isOffByOneError } from '../../helpers/download_errors';\n\nexport type DownloadDriverOps = {\n /**\n * A soft limit of the amount of blob storage, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one\n * when they become unneeded.\n * */\n cacheSoftSizeBytes: number;\n\n /**\n * A hard limit of the amount of sparse cache, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one.\n *\n * The sparse cache is used to store ranges of blobs.\n * */\n rangesCacheMaxSizeBytes: number;\n\n /**\n * Max number of concurrent downloads while calculating computable states\n * derived from this driver\n * */\n nConcurrentDownloads: number;\n};\n\n/** DownloadDriver holds a queue of downloading tasks,\n * and notifies every watcher when a file were downloaded. */\nexport class DownloadDriver implements BlobDriver {\n /** Represents a unique key to the path of a blob as a map. */\n private keyToDownload: Map<string, DownloadBlobTask> = new Map();\n\n /** Writes and removes files to a hard drive and holds a counter for every\n * file that should be kept. */\n private cache: FilesCache<DownloadBlobTask>;\n private rangesCache: SparseCache;\n\n /** Downloads files and writes them to the local dir. */\n private downloadQueue: TaskProcessor;\n\n private keyToOnDemand: Map<string, OnDemandBlobHolder> = new Map();\n\n private idToLastLines: Map<string, LastLinesGetter> = new Map();\n private idToProgressLog: Map<string, LastLinesGetter> = new Map();\n\n private readonly saveDir: string;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n private readonly clientLogs: ClientLogs,\n saveDir: string,\n private readonly rangesCacheDir: string,\n private readonly signer: Signer,\n private readonly ops: DownloadDriverOps,\n ) {\n this.cache = new FilesCache(this.ops.cacheSoftSizeBytes);\n\n const fsRanges = new SparseCacheFsRanges(this.logger, this.rangesCacheDir);\n const fsStorage = new SparseCacheFsFile(this.logger, this.rangesCacheDir);\n this.rangesCache = new SparseCache(this.logger, this.ops.rangesCacheMaxSizeBytes, fsRanges, fsStorage);\n\n this.downloadQueue = new TaskProcessor(this.logger, ops.nConcurrentDownloads);\n\n this.saveDir = path.resolve(saveDir);\n }\n\n static async init(\n logger: MiLogger,\n clientDownload: ClientDownload,\n clientLogs: ClientLogs,\n saveDir: string,\n rangesCacheDir: string,\n signer: Signer,\n ops: DownloadDriverOps,\n ): Promise<DownloadDriver> {\n const driver = new DownloadDriver(logger, clientDownload, clientLogs, saveDir, rangesCacheDir, signer, ops);\n await driver.rangesCache.reset();\n\n return driver;\n }\n\n /** Gets a blob or part of the blob by its resource id or downloads a blob and sets it in a cache. */\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx: ComputableCtx,\n ): LocalBlobHandleAndSize | undefined;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ): ComputableStableDefined<LocalBlobHandleAndSize>;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<LocalBlobHandleAndSize | undefined> | LocalBlobHandleAndSize | undefined {\n if (ctx === undefined) {\n return Computable.make((ctx) => this.getDownloadedBlob(res, ctx));\n }\n\n const rInfo = treeEntryToResourceInfo(res, ctx);\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(rInfo, callerId));\n\n const result = this.getDownloadedBlobNoCtx(ctx.watcher, rInfo as ResourceSnapshot, callerId);\n if (result == undefined) {\n ctx.markUnstable('download blob is still undefined');\n }\n\n return result;\n }\n\n private getDownloadedBlobNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n callerId: string,\n ): LocalBlobHandleAndSize | undefined {\n validateDownloadableResourceType('getDownloadedBlob', rInfo.type);\n\n // We don't need to request files with wider limits,\n // PFrame's engine does it disk-optimally by itself.\n\n const task = this.getOrSetNewTask(rInfo, callerId);\n task.attach(w, callerId);\n\n const result = task.getBlob();\n if (!result.done) {\n return undefined;\n }\n if (result.result.ok) {\n return result.result.value;\n }\n throw result.result.error;\n }\n\n private getOrSetNewTask(\n rInfo: ResourceSnapshot,\n callerId: string,\n ): DownloadBlobTask {\n const key = blobKey(rInfo.id);\n\n const inMemoryTask = this.keyToDownload.get(key);\n if (inMemoryTask) {\n return inMemoryTask;\n }\n\n // schedule the blob downloading, then it'll be added to the cache.\n const fPath = path.resolve(this.saveDir, key);\n\n const newTask = new DownloadBlobTask(\n this.logger,\n this.clientDownload,\n rInfo,\n newLocalHandle(fPath, this.signer),\n fPath,\n );\n this.keyToDownload.set(key, newTask);\n\n this.downloadQueue.push({\n fn: () => this.downloadBlob(newTask, callerId),\n recoverableErrorPredicate: (e) => !nonRecoverableError(e),\n });\n\n return newTask;\n }\n\n private async downloadBlob(task: DownloadBlobTask, callerId: string) {\n await task.download();\n const blob = task.getBlob();\n if (blob.done && blob.result.ok) {\n this.cache.addCache(task, callerId);\n }\n }\n\n /** Gets on demand blob. */\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: undefined,\n fromBytes?: number,\n toBytes?: number,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx: ComputableCtx,\n fromBytes?: number,\n toBytes?: number,\n ): RemoteBlobHandleAndSize;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: ComputableCtx,\n ): ComputableStableDefined<RemoteBlobHandleAndSize> | RemoteBlobHandleAndSize | undefined {\n if (ctx === undefined) return Computable.make((ctx) => this.getOnDemandBlob(res, ctx));\n\n const rInfo: OnDemandBlobResourceSnapshot = isPlTreeEntry(res)\n ? makeResourceSnapshot(res, OnDemandBlobResourceSnapshot, ctx)\n : res;\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseOnDemandBlob(rInfo.id, callerId));\n\n // note that the watcher is not needed,\n // the handler never changes.\n const result = this.getOnDemandBlobNoCtx(rInfo, callerId);\n\n return result;\n }\n\n private getOnDemandBlobNoCtx(\n info: OnDemandBlobResourceSnapshot,\n callerId: string,\n ): RemoteBlobHandleAndSize {\n validateDownloadableResourceType('getOnDemandBlob', info.type);\n\n let blob = this.keyToOnDemand.get(blobKey(info.id));\n\n if (blob === undefined) {\n blob = new OnDemandBlobHolder(getSize(info), newRemoteHandle(info, this.signer));\n this.keyToOnDemand.set(blobKey(info.id), blob);\n }\n\n blob.attach(callerId);\n\n return blob.getHandle();\n }\n\n /** Gets a path from a handle. */\n public getLocalPath(handle: LocalBlobHandle): string {\n const { path } = parseLocalHandle(handle, this.signer);\n return path;\n }\n\n /** Gets a content of a blob by a handle. */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options?: GetContentOptions,\n ): Promise<Uint8Array>;\n /** @deprecated Use {@link getContent} with {@link GetContentOptions} instead */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n range?: RangeBytes,\n ): Promise<Uint8Array>;\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n optionsOrRange?: GetContentOptions | RangeBytes,\n ): Promise<Uint8Array> {\n let options: GetContentOptions = {};\n if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {\n if ('range' in optionsOrRange) {\n options = optionsOrRange;\n } else {\n const range = optionsOrRange as RangeBytes;\n validateRangeBytes(range, `getContent`);\n options = { range };\n }\n }\n\n const request = () => this.withContent(handle, {\n ...options,\n handler: async (content) => {\n const chunks: Uint8Array[] = [];\n for await (const chunk of content) {\n options.signal?.throwIfAborted();\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n }\n });\n\n try {\n return await request();\n } catch (error) {\n if (isOffByOneError(error)) {\n return await request();\n }\n throw error;\n }\n }\n\n /** Gets a content stream of a blob by a handle and calls handler with it. */\n public async withContent<T>(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options: GetContentOptions & {\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n const { range, signal, handler } = options;\n\n if (isLocalBlobHandle(handle)) {\n return await withFileContent({ path: this.getLocalPath(handle), range, signal, handler });\n }\n\n if (isRemoteBlobHandle(handle)) {\n const result = parseRemoteHandle(handle, this.signer);\n\n const key = blobKey(result.info.id);\n const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });\n signal?.throwIfAborted();\n\n if (filePath) return await withFileContent({ path: filePath, range, signal, handler });\n\n return await this.clientDownload.withBlobContent(\n result.info,\n { signal },\n options,\n async (content, size) => {\n const [handlerStream, cacheStream] = content.tee();\n \n const handlerPromise = handler(handlerStream, size);\n const _cachePromise = buffer(cacheStream)\n .then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data))\n .catch(() => {\n // Ignore cache errors - they shouldn't affect the main handler result\n // This prevents unhandled promise rejections when the stream fails\n });\n\n return await handlerPromise;\n }\n );\n }\n\n throw new Error('Malformed remote handle');\n }\n\n /**\n * Creates computable that will return blob content once it is downloaded.\n * Uses downloaded blob handle under the hood, so stores corresponding blob in file system.\n */\n public getComputableContent(\n res: ResourceInfo | PlTreeEntry,\n range?: RangeBytes,\n ): ComputableStableDefined<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getComputableContent`);\n }\n\n return Computable.make((ctx) =>\n this.getDownloadedBlob(res, ctx), {\n postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined\n }\n ).withStableType()\n }\n\n /** Returns all logs and schedules a job that reads remain logs.\n * Notifies when a new portion of the log appeared. */\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r as ResourceSnapshot, lines, callerId);\n if (result == undefined)\n ctx.markUnstable('either a file was not downloaded or logs was not read');\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getLastLogs', rInfo.type);\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToLastLines.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, lines);\n this.idToLastLines.set(blobKey(rInfo.id), newLogGetter);\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getProgressLogNoCtx(\n ctx.watcher,\n r as ResourceSnapshot,\n patternToSearch,\n callerId,\n );\n if (result === undefined)\n ctx.markUnstable('either a file was not downloaded or a progress log was not read');\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getProgressLog', rInfo.type);\n\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToProgressLog.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, 1, patternToSearch);\n this.idToProgressLog.set(blobKey(rInfo.id), newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns an Id of a smart object, that can read logs directly from\n * the platform. */\n public getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<AnyLogHandle>;\n public getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): AnyLogHandle;\n public getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<AnyLogHandle> | AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n return this.getLogHandleNoCtx(r as ResourceSnapshot);\n }\n\n private getLogHandleNoCtx(rInfo: ResourceSnapshot): AnyLogHandle {\n validateDownloadableResourceType('getLogHandle', rInfo.type);\n return newLogHandle(false, rInfo);\n }\n\n public async lastLines(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number, // if 0n, then start from the end.\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n public async readText(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n private async releaseBlob(rInfo: ResourceInfo, callerId: string) {\n const task = this.keyToDownload.get(blobKey(rInfo.id));\n if (task == undefined) {\n return;\n }\n\n if (this.cache.existsFile(blobKey(rInfo.id))) {\n const toDelete = this.cache.removeFile(blobKey(rInfo.id), callerId);\n\n await Promise.all(\n toDelete.map(async (cachedFile) => {\n await fsp.rm(cachedFile.path);\n\n this.cache.removeCache(cachedFile);\n\n this.removeTask(\n mapGet(this.keyToDownload, pathToKey(cachedFile.path)),\n `the task ${stringifyWithResourceId(cachedFile)} was removed`\n + `from cache along with ${stringifyWithResourceId(toDelete.map((d) => d.path))}`,\n );\n }),\n );\n } else {\n // The task is still in a downloading queue.\n const deleted = task.counter.dec(callerId);\n if (deleted) {\n this.removeTask(\n task,\n `the task ${stringifyWithResourceId(task.info())} was removed from cache`,\n );\n }\n }\n }\n\n private removeTask(task: DownloadBlobTask, reason: string) {\n task.abort(reason);\n task.change.markChanged(`download task for ${task.path} removed: ${reason}`);\n this.keyToDownload.delete(pathToKey(task.path));\n this.idToLastLines.delete(blobKey(task.rInfo.id));\n this.idToProgressLog.delete(blobKey(task.rInfo.id));\n }\n\n private async releaseOnDemandBlob(blobId: ResourceId, callerId: string) {\n const deleted = this.keyToOnDemand.get(blobKey(blobId))?.release(callerId) ?? false;\n if (deleted) this.keyToOnDemand.delete(blobKey(blobId));\n }\n\n /** Removes all files from a hard drive. */\n async releaseAll() {\n this.downloadQueue.stop();\n\n this.keyToDownload.forEach((task, key) => {\n this.keyToDownload.delete(key);\n task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);\n });\n }\n}\n\n/** Keeps a counter to the on demand handle. */\nclass OnDemandBlobHolder {\n private readonly counter = new CallersCounter();\n\n constructor(\n private readonly size: number,\n private readonly handle: RemoteBlobHandle,\n ) {}\n\n public getHandle(): RemoteBlobHandleAndSize {\n return { handle: this.handle, size: this.size };\n }\n\n public attach(callerId: string) {\n this.counter.inc(callerId);\n }\n\n public release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n}\n\nclass LastLinesGetter {\n private updater: Updater;\n private log: string | undefined;\n private readonly change: ChangeSource = new ChangeSource();\n private error: any | undefined = undefined;\n\n constructor(\n private readonly path: string,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {\n this.updater = new Updater(async () => this.update());\n }\n\n getOrSchedule(w: Watcher): {\n log: string | undefined;\n error?: any | undefined;\n } {\n this.change.attachWatcher(w);\n\n this.updater.schedule();\n\n return {\n log: this.log,\n error: this.error,\n };\n }\n\n async update(): Promise<void> {\n try {\n const newLogs = await getLastLines(this.path, this.lines, this.patternToSearch);\n\n if (this.log != newLogs) this.change.markChanged(`logs for ${this.path} updated`);\n this.log = newLogs;\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.log = '';\n this.error = e;\n this.change.markChanged(`log update for ${this.path} failed, resource not found`);\n return;\n }\n\n throw e;\n }\n }\n}\n\n/** Gets last lines from a file by reading the file from the top and keeping\n * last N lines in a window queue. */\nasync function getLastLines(fPath: string, nLines: number, patternToSearch?: string): Promise<string> {\n let inStream: fs.ReadStream | undefined;\n let rl: readline.Interface | undefined;\n\n try {\n inStream = fs.createReadStream(fPath);\n rl = readline.createInterface({ input: inStream, crlfDelay: Infinity });\n\n const lines = new Denque();\n\n for await (const line of rl) {\n if (patternToSearch != undefined && !line.includes(patternToSearch)) continue;\n\n lines.push(line);\n if (lines.length > nLines) {\n lines.shift();\n }\n }\n\n // last EOL is for keeping backward compat with platforma implementation.\n return lines.toArray().join(os.EOL) + os.EOL;\n } finally {\n // Cleanup resources in finally block to ensure they're always cleaned up\n try {\n if (rl) {\n rl.close();\n }\n } catch (cleanupError) {\n console.error('Error closing readline interface:', cleanupError);\n }\n\n try {\n if (inStream && !inStream.destroyed) {\n inStream.destroy();\n }\n } catch (cleanupError) {\n console.error('Error destroying read stream:', cleanupError);\n }\n }\n}\n\nfunction validateDownloadableResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('Blob/')) {\n let message = `${methodName}: wrong resource type: ${rType.name}, expected: a resource of type that starts with 'Blob/'.`;\n if (rType.name == 'Blob')\n message += ` If it's called from workflow, should a file be exported with 'file.exportFile' function?`;\n\n throw new WrongResourceTypeError(message);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"download_blob.js","sources":["../../../src/drivers/download_blob/download_blob.ts"],"sourcesContent":["import type {\n ComputableCtx,\n ComputableStableDefined,\n Watcher,\n} from '@milaboratories/computable';\nimport {\n ChangeSource,\n Computable,\n} from '@milaboratories/computable';\nimport type { ResourceId, ResourceType } from '@milaboratories/pl-client';\nimport { resourceIdToString, stringifyWithResourceId } from '@milaboratories/pl-client';\nimport type {\n AnyLogHandle,\n BlobDriver,\n ContentHandler,\n GetContentOptions,\n LocalBlobHandle,\n LocalBlobHandleAndSize,\n ReadyLogHandle,\n RemoteBlobHandle,\n RemoteBlobHandleAndSize,\n StreamingApiResponse,\n} from '@milaboratories/pl-model-common';\nimport { type RangeBytes, validateRangeBytes } from '@milaboratories/pl-model-common';\nimport type {\n PlTreeEntry,\n ResourceInfo,\n ResourceSnapshot\n} from '@milaboratories/pl-tree';\nimport {\n isPlTreeEntry,\n makeResourceSnapshot,\n treeEntryToResourceInfo,\n} from '@milaboratories/pl-tree';\nimport type { MiLogger, Signer } from '@milaboratories/ts-helpers';\nimport { CallersCounter, mapGet, TaskProcessor } from '@milaboratories/ts-helpers';\nimport Denque from 'denque';\nimport * as fs from 'fs';\nimport { randomUUID } from 'node:crypto';\nimport * as fsp from 'node:fs/promises';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport * as readline from 'node:readline/promises';\nimport { buffer } from 'node:stream/consumers';\nimport { Readable } from 'node:stream';\nimport type { ClientDownload } from '../../clients/download';\nimport type { ClientLogs } from '../../clients/logs';\nimport { withFileContent } from '../helpers/read_file';\nimport {\n isLocalBlobHandle,\n newLocalHandle,\n parseLocalHandle,\n} from '../helpers/download_local_handle';\nimport {\n isRemoteBlobHandle,\n newRemoteHandle,\n parseRemoteHandle,\n} from '../helpers/download_remote_handle';\nimport { Updater, WrongResourceTypeError } from '../helpers/helpers';\nimport { getResourceInfoFromLogHandle, newLogHandle } from '../helpers/logs_handle';\nimport { getSize, OnDemandBlobResourceSnapshot } from '../types';\nimport { blobKey, pathToKey } from './blob_key';\nimport { DownloadBlobTask, nonRecoverableError } from './download_blob_task';\nimport { FilesCache } from '../helpers/files_cache';\nimport { SparseCache, SparseCacheFsFile, SparseCacheFsRanges } from './sparse_cache/cache';\nimport { isOffByOneError } from '../../helpers/download_errors';\n\nexport type DownloadDriverOps = {\n /**\n * A soft limit of the amount of blob storage, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one\n * when they become unneeded.\n * */\n cacheSoftSizeBytes: number;\n\n /**\n * A hard limit of the amount of sparse cache, in bytes.\n * Once exceeded, the download driver will start deleting blobs one by one.\n *\n * The sparse cache is used to store ranges of blobs.\n * */\n rangesCacheMaxSizeBytes: number;\n\n /**\n * Max number of concurrent downloads while calculating computable states\n * derived from this driver\n * */\n nConcurrentDownloads: number;\n};\n\n/** DownloadDriver holds a queue of downloading tasks,\n * and notifies every watcher when a file were downloaded. */\nexport class DownloadDriver implements BlobDriver, AsyncDisposable {\n /** Represents a unique key to the path of a blob as a map. */\n private keyToDownload: Map<string, DownloadBlobTask> = new Map();\n\n /** Writes and removes files to a hard drive and holds a counter for every\n * file that should be kept. */\n private cache: FilesCache<DownloadBlobTask>;\n private rangesCache: SparseCache;\n\n /** Downloads files and writes them to the local dir. */\n private downloadQueue: TaskProcessor;\n\n private keyToOnDemand: Map<string, OnDemandBlobHolder> = new Map();\n\n private idToLastLines: Map<string, LastLinesGetter> = new Map();\n private idToProgressLog: Map<string, LastLinesGetter> = new Map();\n\n private readonly saveDir: string;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly clientDownload: ClientDownload,\n private readonly clientLogs: ClientLogs,\n saveDir: string,\n private readonly rangesCacheDir: string,\n private readonly signer: Signer,\n private readonly ops: DownloadDriverOps,\n ) {\n this.cache = new FilesCache(this.ops.cacheSoftSizeBytes);\n\n const fsRanges = new SparseCacheFsRanges(this.logger, this.rangesCacheDir);\n const fsStorage = new SparseCacheFsFile(this.logger, this.rangesCacheDir);\n this.rangesCache = new SparseCache(this.logger, this.ops.rangesCacheMaxSizeBytes, fsRanges, fsStorage);\n\n this.downloadQueue = new TaskProcessor(this.logger, ops.nConcurrentDownloads);\n\n this.saveDir = path.resolve(saveDir);\n }\n\n static async init(\n logger: MiLogger,\n clientDownload: ClientDownload,\n clientLogs: ClientLogs,\n saveDir: string,\n rangesCacheDir: string,\n signer: Signer,\n ops: DownloadDriverOps,\n ): Promise<DownloadDriver> {\n const driver = new DownloadDriver(logger, clientDownload, clientLogs, saveDir, rangesCacheDir, signer, ops);\n await driver.rangesCache.reset();\n\n return driver;\n }\n\n /** Gets a blob or part of the blob by its resource id or downloads a blob and sets it in a cache. */\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx: ComputableCtx,\n ): LocalBlobHandleAndSize | undefined;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ): ComputableStableDefined<LocalBlobHandleAndSize>;\n public getDownloadedBlob(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<LocalBlobHandleAndSize | undefined> | LocalBlobHandleAndSize | undefined {\n if (ctx === undefined) {\n return Computable.make((ctx) => this.getDownloadedBlob(res, ctx));\n }\n\n const rInfo = treeEntryToResourceInfo(res, ctx);\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(rInfo, callerId));\n\n const result = this.getDownloadedBlobNoCtx(ctx.watcher, rInfo as ResourceSnapshot, callerId);\n if (result == undefined) {\n ctx.markUnstable('download blob is still undefined');\n }\n\n return result;\n }\n\n private getDownloadedBlobNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n callerId: string,\n ): LocalBlobHandleAndSize | undefined {\n validateDownloadableResourceType('getDownloadedBlob', rInfo.type);\n\n // We don't need to request files with wider limits,\n // PFrame's engine does it disk-optimally by itself.\n\n const task = this.getOrSetNewTask(rInfo, callerId);\n task.attach(w, callerId);\n\n const result = task.getBlob();\n if (!result.done) {\n return undefined;\n }\n if (result.result.ok) {\n return result.result.value;\n }\n throw result.result.error;\n }\n\n private getOrSetNewTask(\n rInfo: ResourceSnapshot,\n callerId: string,\n ): DownloadBlobTask {\n const key = blobKey(rInfo.id);\n\n const inMemoryTask = this.keyToDownload.get(key);\n if (inMemoryTask) {\n return inMemoryTask;\n }\n\n // schedule the blob downloading, then it'll be added to the cache.\n const fPath = path.resolve(this.saveDir, key);\n\n const newTask = new DownloadBlobTask(\n this.logger,\n this.clientDownload,\n rInfo,\n newLocalHandle(fPath, this.signer),\n fPath,\n );\n this.keyToDownload.set(key, newTask);\n\n this.downloadQueue.push({\n fn: () => this.downloadBlob(newTask, callerId),\n recoverableErrorPredicate: (e) => !nonRecoverableError(e),\n });\n\n return newTask;\n }\n\n private async downloadBlob(task: DownloadBlobTask, callerId: string) {\n await task.download();\n const blob = task.getBlob();\n if (blob.done && blob.result.ok) {\n this.cache.addCache(task, callerId);\n }\n }\n\n /** Gets on demand blob. */\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: undefined,\n fromBytes?: number,\n toBytes?: number,\n ): Computable<RemoteBlobHandleAndSize>;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx: ComputableCtx,\n fromBytes?: number,\n toBytes?: number,\n ): RemoteBlobHandleAndSize;\n public getOnDemandBlob(\n res: OnDemandBlobResourceSnapshot | PlTreeEntry,\n ctx?: ComputableCtx,\n ): ComputableStableDefined<RemoteBlobHandleAndSize> | RemoteBlobHandleAndSize | undefined {\n if (ctx === undefined) return Computable.make((ctx) => this.getOnDemandBlob(res, ctx));\n\n const rInfo: OnDemandBlobResourceSnapshot = isPlTreeEntry(res)\n ? makeResourceSnapshot(res, OnDemandBlobResourceSnapshot, ctx)\n : res;\n\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseOnDemandBlob(rInfo.id, callerId));\n\n // note that the watcher is not needed,\n // the handler never changes.\n const result = this.getOnDemandBlobNoCtx(rInfo, callerId);\n\n return result;\n }\n\n private getOnDemandBlobNoCtx(\n info: OnDemandBlobResourceSnapshot,\n callerId: string,\n ): RemoteBlobHandleAndSize {\n validateDownloadableResourceType('getOnDemandBlob', info.type);\n\n let blob = this.keyToOnDemand.get(blobKey(info.id));\n\n if (blob === undefined) {\n blob = new OnDemandBlobHolder(getSize(info), newRemoteHandle(info, this.signer));\n this.keyToOnDemand.set(blobKey(info.id), blob);\n }\n\n blob.attach(callerId);\n\n return blob.getHandle();\n }\n\n /** Gets a path from a handle. */\n public getLocalPath(handle: LocalBlobHandle): string {\n const { path } = parseLocalHandle(handle, this.signer);\n return path;\n }\n\n /** Gets a content of a blob by a handle. */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options?: GetContentOptions,\n ): Promise<Uint8Array>;\n /** @deprecated Use {@link getContent} with {@link GetContentOptions} instead */\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n range?: RangeBytes,\n ): Promise<Uint8Array>;\n public async getContent(\n handle: LocalBlobHandle | RemoteBlobHandle,\n optionsOrRange?: GetContentOptions | RangeBytes,\n ): Promise<Uint8Array> {\n let options: GetContentOptions = {};\n if (typeof optionsOrRange === 'object' && optionsOrRange !== null) {\n if ('range' in optionsOrRange) {\n options = optionsOrRange;\n } else {\n const range = optionsOrRange as RangeBytes;\n validateRangeBytes(range, `getContent`);\n options = { range };\n }\n }\n\n const request = () => this.withContent(handle, {\n ...options,\n handler: async (content) => {\n const chunks: Uint8Array[] = [];\n for await (const chunk of content) {\n options.signal?.throwIfAborted();\n chunks.push(chunk);\n }\n return Buffer.concat(chunks);\n }\n });\n\n try {\n return await request();\n } catch (error) {\n if (isOffByOneError(error)) {\n return await request();\n }\n throw error;\n }\n }\n\n /** Gets a content stream of a blob by a handle and calls handler with it. */\n public async withContent<T>(\n handle: LocalBlobHandle | RemoteBlobHandle,\n options: GetContentOptions & {\n handler: ContentHandler<T>;\n },\n ): Promise<T> {\n const { range, signal, handler } = options;\n\n if (isLocalBlobHandle(handle)) {\n return await withFileContent({ path: this.getLocalPath(handle), range, signal, handler });\n }\n\n if (isRemoteBlobHandle(handle)) {\n const result = parseRemoteHandle(handle, this.signer);\n\n const key = blobKey(result.info.id);\n const filePath = await this.rangesCache.get(key, range ?? { from: 0, to: result.size });\n signal?.throwIfAborted();\n\n if (filePath) return await withFileContent({ path: filePath, range, signal, handler });\n\n return await this.clientDownload.withBlobContent(\n result.info,\n { signal },\n options,\n async (content, size) => {\n const [handlerStream, cacheStream] = content.tee();\n \n const handlerPromise = handler(handlerStream, size);\n const _cachePromise = buffer(cacheStream)\n .then((data) => this.rangesCache.set(key, range ?? { from: 0, to: result.size }, data))\n .catch(() => {\n // Ignore cache errors - they shouldn't affect the main handler result\n // This prevents unhandled promise rejections when the stream fails\n });\n\n return await handlerPromise;\n }\n );\n }\n\n throw new Error('Malformed remote handle');\n }\n\n /**\n * Creates computable that will return blob content once it is downloaded.\n * Uses downloaded blob handle under the hood, so stores corresponding blob in file system.\n */\n public getComputableContent(\n res: ResourceInfo | PlTreeEntry,\n range?: RangeBytes,\n ): ComputableStableDefined<Uint8Array> {\n if (range) {\n validateRangeBytes(range, `getComputableContent`);\n }\n\n return Computable.make((ctx) =>\n this.getDownloadedBlob(res, ctx), {\n postprocessValue: (v) => v ? this.getContent(v.handle, { range }) : undefined\n }\n ).withStableType()\n }\n\n /** Returns all logs and schedules a job that reads remain logs.\n * Notifies when a new portion of the log appeared. */\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx: ComputableCtx\n ): Computable<string | undefined>;\n public getLastLogs(\n res: ResourceInfo | PlTreeEntry,\n lines: number,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined) return Computable.make((ctx) => this.getLastLogs(res, lines, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getLastLogsNoCtx(ctx.watcher, r as ResourceSnapshot, lines, callerId);\n if (result == undefined)\n ctx.markUnstable('either a file was not downloaded or logs was not read');\n\n return result;\n }\n\n private getLastLogsNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n lines: number,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getLastLogs', rInfo.type);\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToLastLines.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, lines);\n this.idToLastLines.set(blobKey(rInfo.id), newLogGetter);\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns a last line that has patternToSearch.\n * Notifies when a new line appeared or EOF reached. */\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string\n ): Computable<string | undefined>;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx: ComputableCtx\n ): string | undefined;\n public getProgressLog(\n res: ResourceInfo | PlTreeEntry,\n patternToSearch: string,\n ctx?: ComputableCtx,\n ): Computable<string | undefined> | string | undefined {\n if (ctx == undefined)\n return Computable.make((ctx) => this.getProgressLog(res, patternToSearch, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n const callerId = randomUUID();\n ctx.addOnDestroy(() => this.releaseBlob(r, callerId));\n\n const result = this.getProgressLogNoCtx(\n ctx.watcher,\n r as ResourceSnapshot,\n patternToSearch,\n callerId,\n );\n if (result === undefined)\n ctx.markUnstable('either a file was not downloaded or a progress log was not read');\n\n return result;\n }\n\n private getProgressLogNoCtx(\n w: Watcher,\n rInfo: ResourceSnapshot,\n patternToSearch: string,\n callerId: string,\n ): string | undefined {\n validateDownloadableResourceType('getProgressLog', rInfo.type);\n\n const blob = this.getDownloadedBlobNoCtx(w, rInfo, callerId);\n if (blob == undefined) return undefined;\n const { path } = parseLocalHandle(blob.handle, this.signer);\n\n let logGetter = this.idToProgressLog.get(blobKey(rInfo.id));\n\n if (logGetter == undefined) {\n const newLogGetter = new LastLinesGetter(path, 1, patternToSearch);\n this.idToProgressLog.set(blobKey(rInfo.id), newLogGetter);\n\n logGetter = newLogGetter;\n }\n\n const result = logGetter.getOrSchedule(w);\n if (result.error) throw result.error;\n\n return result.log;\n }\n\n /** Returns an Id of a smart object, that can read logs directly from\n * the platform. */\n public getLogHandle(res: ResourceInfo | PlTreeEntry): Computable<AnyLogHandle>;\n public getLogHandle(res: ResourceInfo | PlTreeEntry, ctx: ComputableCtx): AnyLogHandle;\n public getLogHandle(\n res: ResourceInfo | PlTreeEntry,\n ctx?: ComputableCtx,\n ): Computable<AnyLogHandle> | AnyLogHandle {\n if (ctx == undefined) return Computable.make((ctx) => this.getLogHandle(res, ctx));\n\n const r = treeEntryToResourceInfo(res, ctx);\n\n return this.getLogHandleNoCtx(r as ResourceSnapshot);\n }\n\n private getLogHandleNoCtx(rInfo: ResourceSnapshot): AnyLogHandle {\n validateDownloadableResourceType('getLogHandle', rInfo.type);\n return newLogHandle(false, rInfo);\n }\n\n public async lastLines(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number, // if 0n, then start from the end.\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.lastLines(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n public async readText(\n handle: ReadyLogHandle,\n lineCount: number,\n offsetBytes?: number,\n searchStr?: string,\n ): Promise<StreamingApiResponse> {\n const resp = await this.clientLogs.readText(\n getResourceInfoFromLogHandle(handle),\n lineCount,\n BigInt(offsetBytes ?? 0),\n searchStr,\n );\n\n return {\n live: false,\n shouldUpdateHandle: false,\n data: resp.data,\n size: Number(resp.size),\n newOffset: Number(resp.newOffset),\n };\n }\n\n private async releaseBlob(rInfo: ResourceInfo, callerId: string) {\n const task = this.keyToDownload.get(blobKey(rInfo.id));\n if (task == undefined) {\n return;\n }\n\n if (this.cache.existsFile(blobKey(rInfo.id))) {\n const toDelete = this.cache.removeFile(blobKey(rInfo.id), callerId);\n\n await Promise.all(\n toDelete.map(async (cachedFile) => {\n await fsp.rm(cachedFile.path);\n\n this.cache.removeCache(cachedFile);\n\n this.removeTask(\n mapGet(this.keyToDownload, pathToKey(cachedFile.path)),\n `the task ${stringifyWithResourceId(cachedFile)} was removed`\n + `from cache along with ${stringifyWithResourceId(toDelete.map((d) => d.path))}`,\n );\n }),\n );\n } else {\n // The task is still in a downloading queue.\n const deleted = task.counter.dec(callerId);\n if (deleted) {\n this.removeTask(\n task,\n `the task ${stringifyWithResourceId(task.info())} was removed from cache`,\n );\n }\n }\n }\n\n private removeTask(task: DownloadBlobTask, reason: string) {\n task.abort(reason);\n task.change.markChanged(`download task for ${task.path} removed: ${reason}`);\n this.keyToDownload.delete(pathToKey(task.path));\n this.idToLastLines.delete(blobKey(task.rInfo.id));\n this.idToProgressLog.delete(blobKey(task.rInfo.id));\n }\n\n private async releaseOnDemandBlob(blobId: ResourceId, callerId: string) {\n const deleted = this.keyToOnDemand.get(blobKey(blobId))?.release(callerId) ?? false;\n if (deleted) this.keyToOnDemand.delete(blobKey(blobId));\n }\n\n /** Removes all files from a hard drive. */\n async releaseAll() {\n this.downloadQueue.stop();\n\n this.keyToDownload.forEach((task, key) => {\n this.keyToDownload.delete(key);\n task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);\n });\n }\n\n async dispose(): Promise<void> {\n await this.rangesCache.dispose();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\n/** Keeps a counter to the on demand handle. */\nclass OnDemandBlobHolder {\n private readonly counter = new CallersCounter();\n\n constructor(\n private readonly size: number,\n private readonly handle: RemoteBlobHandle,\n ) {}\n\n public getHandle(): RemoteBlobHandleAndSize {\n return { handle: this.handle, size: this.size };\n }\n\n public attach(callerId: string) {\n this.counter.inc(callerId);\n }\n\n public release(callerId: string): boolean {\n return this.counter.dec(callerId);\n }\n}\n\nclass LastLinesGetter {\n private updater: Updater;\n private log: string | undefined;\n private readonly change: ChangeSource = new ChangeSource();\n private error: any | undefined = undefined;\n\n constructor(\n private readonly path: string,\n private readonly lines: number,\n private readonly patternToSearch?: string,\n ) {\n this.updater = new Updater(async () => this.update());\n }\n\n getOrSchedule(w: Watcher): {\n log: string | undefined;\n error?: any | undefined;\n } {\n this.change.attachWatcher(w);\n\n this.updater.schedule();\n\n return {\n log: this.log,\n error: this.error,\n };\n }\n\n async update(): Promise<void> {\n try {\n const newLogs = await getLastLines(this.path, this.lines, this.patternToSearch);\n\n if (this.log != newLogs) this.change.markChanged(`logs for ${this.path} updated`);\n this.log = newLogs;\n } catch (e: any) {\n if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {\n // No resource\n this.log = '';\n this.error = e;\n this.change.markChanged(`log update for ${this.path} failed, resource not found`);\n return;\n }\n\n throw e;\n }\n }\n}\n\n/** Gets last lines from a file by reading the file from the top and keeping\n * last N lines in a window queue. */\nasync function getLastLines(fPath: string, nLines: number, patternToSearch?: string): Promise<string> {\n let inStream: fs.ReadStream | undefined;\n let rl: readline.Interface | undefined;\n\n try {\n inStream = fs.createReadStream(fPath);\n rl = readline.createInterface({ input: inStream, crlfDelay: Infinity });\n\n const lines = new Denque();\n\n for await (const line of rl) {\n if (patternToSearch != undefined && !line.includes(patternToSearch)) continue;\n\n lines.push(line);\n if (lines.length > nLines) {\n lines.shift();\n }\n }\n\n // last EOL is for keeping backward compat with platforma implementation.\n return lines.toArray().join(os.EOL) + os.EOL;\n } finally {\n // Cleanup resources in finally block to ensure they're always cleaned up\n try {\n if (rl) {\n rl.close();\n }\n } catch (cleanupError) {\n console.error('Error closing readline interface:', cleanupError);\n }\n\n try {\n if (inStream && !inStream.destroyed) {\n inStream.destroy();\n }\n } catch (cleanupError) {\n console.error('Error destroying read stream:', cleanupError);\n }\n }\n}\n\nfunction validateDownloadableResourceType(methodName: string, rType: ResourceType) {\n if (!rType.name.startsWith('Blob/')) {\n let message = `${methodName}: wrong resource type: ${rType.name}, expected: a resource of type that starts with 'Blob/'.`;\n if (rType.name == 'Blob')\n message += ` If it's called from workflow, should a file be exported with 'file.exportFile' function?`;\n\n throw new WrongResourceTypeError(message);\n }\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0FA;AAC6D;MAChD,cAAc,CAAA;AAoBN,IAAA,MAAA;AACA,IAAA,cAAA;AACA,IAAA,UAAA;AAEA,IAAA,cAAA;AACA,IAAA,MAAA;AACA,IAAA,GAAA;;AAxBX,IAAA,aAAa,GAAkC,IAAI,GAAG,EAAE;AAEhE;AAC+B;AACvB,IAAA,KAAK;AACL,IAAA,WAAW;;AAGX,IAAA,aAAa;AAEb,IAAA,aAAa,GAAoC,IAAI,GAAG,EAAE;AAE1D,IAAA,aAAa,GAAiC,IAAI,GAAG,EAAE;AACvD,IAAA,eAAe,GAAiC,IAAI,GAAG,EAAE;AAEhD,IAAA,OAAO;AAExB,IAAA,WAAA,CACmB,MAAgB,EAChB,cAA8B,EAC9B,UAAsB,EACvC,OAAe,EACE,cAAsB,EACtB,MAAc,EACd,GAAsB,EAAA;QANtB,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,cAAc,GAAd,cAAc;QACd,IAAA,CAAA,UAAU,GAAV,UAAU;QAEV,IAAA,CAAA,cAAc,GAAd,cAAc;QACd,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,GAAG,GAAH,GAAG;AAEpB,QAAA,IAAI,CAAC,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC;AAExD,QAAA,MAAM,QAAQ,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC;AAC1E,QAAA,MAAM,SAAS,GAAG,IAAI,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC;QACzE,IAAI,CAAC,WAAW,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,uBAAuB,EAAE,QAAQ,EAAE,SAAS,CAAC;AAEtG,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,oBAAoB,CAAC;QAE7E,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;IACtC;AAEA,IAAA,aAAa,IAAI,CACf,MAAgB,EAChB,cAA8B,EAC9B,UAAsB,EACtB,OAAe,EACf,cAAsB,EACtB,MAAc,EACd,GAAsB,EAAA;AAEtB,QAAA,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,CAAC;AAC3G,QAAA,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE;AAEhC,QAAA,OAAO,MAAM;IACf;IAUO,iBAAiB,CACtB,GAA+B,EAC/B,GAAmB,EAAA;AAEnB,QAAA,IAAI,GAAG,KAAK,SAAS,EAAE;AACrB,YAAA,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QACnE;QAEA,MAAM,KAAK,GAAG,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAE/C,QAAA,MAAM,QAAQ,GAAG,UAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAEzD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,OAAO,EAAE,KAAyB,EAAE,QAAQ,CAAC;AAC5F,QAAA,IAAI,MAAM,IAAI,SAAS,EAAE;AACvB,YAAA,GAAG,CAAC,YAAY,CAAC,kCAAkC,CAAC;QACtD;AAEA,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,sBAAsB,CAC5B,CAAU,EACV,KAAuB,EACvB,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,mBAAmB,EAAE,KAAK,CAAC,IAAI,CAAC;;;QAKjE,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC;AAClD,QAAA,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC;AAExB,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE;AAC7B,QAAA,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;AAChB,YAAA,OAAO,SAAS;QAClB;AACA,QAAA,IAAI,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE;AACpB,YAAA,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK;QAC5B;AACA,QAAA,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK;IAC3B;IAEQ,eAAe,CACrB,KAAuB,EACvB,QAAgB,EAAA;QAEhB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAE7B,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC;QAChD,IAAI,YAAY,EAAE;AAChB,YAAA,OAAO,YAAY;QACrB;;AAGA,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC;QAE7C,MAAM,OAAO,GAAG,IAAI,gBAAgB,CAClC,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,cAAc,EACnB,KAAK,EACL,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAClC,KAAK,CACN;QACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC;AAEpC,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC;YACtB,EAAE,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC;YAC9C,yBAAyB,EAAE,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;AAC1D,SAAA,CAAC;AAEF,QAAA,OAAO,OAAO;IAChB;AAEQ,IAAA,MAAM,YAAY,CAAC,IAAsB,EAAE,QAAgB,EAAA;AACjE,QAAA,MAAM,IAAI,CAAC,QAAQ,EAAE;AACrB,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE;QAC3B,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE;YAC/B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;QACrC;IACF;IAkBO,eAAe,CACpB,GAA+C,EAC/C,GAAmB,EAAA;QAEnB,IAAI,GAAG,KAAK,SAAS;AAAE,YAAA,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAEtF,QAAA,MAAM,KAAK,GAAiC,aAAa,CAAC,GAAG;cACzD,oBAAoB,CAAC,GAAG,EAAE,4BAA4B,EAAE,GAAG;cAC3D,GAAG;AAEP,QAAA,MAAM,QAAQ,GAAG,UAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,CAAC,CAAC;;;QAIpE,MAAM,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC;AAEzD,QAAA,OAAO,MAAM;IACf;IAEQ,oBAAoB,CAC1B,IAAkC,EAClC,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,iBAAiB,EAAE,IAAI,CAAC,IAAI,CAAC;AAE9D,QAAA,IAAI,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAEnD,QAAA,IAAI,IAAI,KAAK,SAAS,EAAE;AACtB,YAAA,IAAI,GAAG,IAAI,kBAAkB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;AAChF,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAChD;AAEA,QAAA,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;AAErB,QAAA,OAAO,IAAI,CAAC,SAAS,EAAE;IACzB;;AAGO,IAAA,YAAY,CAAC,MAAuB,EAAA;AACzC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AACtD,QAAA,OAAO,IAAI;IACb;AAYO,IAAA,MAAM,UAAU,CACrB,MAA0C,EAC1C,cAA+C,EAAA;QAE/C,IAAI,OAAO,GAAsB,EAAE;QACnC,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,cAAc,KAAK,IAAI,EAAE;AACjE,YAAA,IAAI,OAAO,IAAI,cAAc,EAAE;gBAC7B,OAAO,GAAG,cAAc;YAC1B;iBAAO;gBACL,MAAM,KAAK,GAAG,cAA4B;AAC1C,gBAAA,kBAAkB,CAAC,KAAK,EAAE,CAAA,UAAA,CAAY,CAAC;AACvC,gBAAA,OAAO,GAAG,EAAE,KAAK,EAAE;YACrB;QACF;QAEA,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE;AAC7C,YAAA,GAAG,OAAO;AACV,YAAA,OAAO,EAAE,OAAO,OAAO,KAAI;gBACzB,MAAM,MAAM,GAAiB,EAAE;AAC/B,gBAAA,WAAW,MAAM,KAAK,IAAI,OAAO,EAAE;AACjC,oBAAA,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE;AAChC,oBAAA,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;gBACpB;AACA,gBAAA,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;YAC9B;AACD,SAAA,CAAC;AAEF,QAAA,IAAI;YACF,OAAO,MAAM,OAAO,EAAE;QACxB;QAAE,OAAO,KAAK,EAAE;AACd,YAAA,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE;gBAC1B,OAAO,MAAM,OAAO,EAAE;YACxB;AACA,YAAA,MAAM,KAAK;QACb;IACF;;AAGO,IAAA,MAAM,WAAW,CACtB,MAA0C,EAC1C,OAEC,EAAA;QAED,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO;AAE1C,QAAA,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE;YAC7B,OAAO,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC3F;AAEA,QAAA,IAAI,kBAAkB,CAAC,MAAM,CAAC,EAAE;YAC9B,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;YAErD,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;YACvF,MAAM,EAAE,cAAc,EAAE;AAExB,YAAA,IAAI,QAAQ;AAAE,gBAAA,OAAO,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAEtF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAC9C,MAAM,CAAC,IAAI,EACX,EAAE,MAAM,EAAE,EACV,OAAO,EACP,OAAO,OAAO,EAAE,IAAI,KAAI;gBACtB,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,GAAG,EAAE;gBAElD,MAAM,cAAc,GAAG,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC;AACnD,gBAAsB,MAAM,CAAC,WAAW;AACrC,qBAAA,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC;qBACrF,KAAK,CAAC,MAAK;;;AAGZ,gBAAA,CAAC;gBAEH,OAAO,MAAM,cAAc;AAC7B,YAAA,CAAC,CACF;QACH;AAEA,QAAA,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;IAC5C;AAEA;;;AAGG;IACI,oBAAoB,CACzB,GAA+B,EAC/B,KAAkB,EAAA;QAElB,IAAI,KAAK,EAAE;AACT,YAAA,kBAAkB,CAAC,KAAK,EAAE,CAAA,oBAAA,CAAsB,CAAC;QACnD;AAEA,QAAA,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KACzB,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE;YAClC,gBAAgB,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,GAAG;SACrE,CACA,CAAC,cAAc,EAAE;IACpB;AAaO,IAAA,WAAW,CAChB,GAA+B,EAC/B,KAAa,EACb,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;YAAE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC;QAExF,MAAM,CAAC,GAAG,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAC3C,QAAA,MAAM,QAAQ,GAAG,UAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,CAAqB,EAAE,KAAK,EAAE,QAAQ,CAAC;QACzF,IAAI,MAAM,IAAI,SAAS;AACrB,YAAA,GAAG,CAAC,YAAY,CAAC,uDAAuD,CAAC;AAE3E,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,gBAAgB,CACtB,CAAU,EACV,KAAuB,EACvB,KAAa,EACb,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,aAAa,EAAE,KAAK,CAAC,IAAI,CAAC;AAC3D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC5D,IAAI,IAAI,IAAI,SAAS;AAAE,YAAA,OAAO,SAAS;AAEvC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AAE3D,QAAA,IAAI,SAAS,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAEzD,QAAA,IAAI,SAAS,IAAI,SAAS,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC;AACrD,YAAA,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;YACvD,SAAS,GAAG,YAAY;QAC1B;QAEA,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,MAAM,MAAM,CAAC,KAAK;QAEpC,OAAO,MAAM,CAAC,GAAG;IACnB;AAaO,IAAA,cAAc,CACnB,GAA+B,EAC/B,eAAuB,EACvB,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;YAClB,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,cAAc,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,CAAC,CAAC;QAEjF,MAAM,CAAC,GAAG,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAC3C,QAAA,MAAM,QAAQ,GAAG,UAAU,EAAE;AAC7B,QAAA,GAAG,CAAC,YAAY,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAErD,QAAA,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CACrC,GAAG,CAAC,OAAO,EACX,CAAqB,EACrB,eAAe,EACf,QAAQ,CACT;QACD,IAAI,MAAM,KAAK,SAAS;AACtB,YAAA,GAAG,CAAC,YAAY,CAAC,iEAAiE,CAAC;AAErF,QAAA,OAAO,MAAM;IACf;AAEQ,IAAA,mBAAmB,CACzB,CAAU,EACV,KAAuB,EACvB,eAAuB,EACvB,QAAgB,EAAA;AAEhB,QAAA,gCAAgC,CAAC,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC;AAE9D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,sBAAsB,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC5D,IAAI,IAAI,IAAI,SAAS;AAAE,YAAA,OAAO,SAAS;AACvC,QAAA,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;AAE3D,QAAA,IAAI,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAE3D,QAAA,IAAI,SAAS,IAAI,SAAS,EAAE;YAC1B,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,EAAE,CAAC,EAAE,eAAe,CAAC;AAClE,YAAA,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC;YAEzD,SAAS,GAAG,YAAY;QAC1B;QAEA,MAAM,MAAM,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,KAAK;YAAE,MAAM,MAAM,CAAC,KAAK;QAEpC,OAAO,MAAM,CAAC,GAAG;IACnB;IAMO,YAAY,CACjB,GAA+B,EAC/B,GAAmB,EAAA;QAEnB,IAAI,GAAG,IAAI,SAAS;AAAE,YAAA,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAElF,MAAM,CAAC,GAAG,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC;AAE3C,QAAA,OAAO,IAAI,CAAC,iBAAiB,CAAC,CAAqB,CAAC;IACtD;AAEQ,IAAA,iBAAiB,CAAC,KAAuB,EAAA;AAC/C,QAAA,gCAAgC,CAAC,cAAc,EAAE,KAAK,CAAC,IAAI,CAAC;AAC5D,QAAA,OAAO,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC;IACnC;IAEO,MAAM,SAAS,CACpB,MAAsB,EACtB,SAAiB,EACjB,WAAoB;IACpB,SAAkB,EAAA;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,SAAS,CAC1C,4BAA4B,CAAC,MAAM,CAAC,EACpC,SAAS,EACT,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,EACxB,SAAS,CACV;QAED,OAAO;AACL,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,YAAA,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAClC;IACH;IAEO,MAAM,QAAQ,CACnB,MAAsB,EACtB,SAAiB,EACjB,WAAoB,EACpB,SAAkB,EAAA;QAElB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,QAAQ,CACzC,4BAA4B,CAAC,MAAM,CAAC,EACpC,SAAS,EACT,MAAM,CAAC,WAAW,IAAI,CAAC,CAAC,EACxB,SAAS,CACV;QAED,OAAO;AACL,YAAA,IAAI,EAAE,KAAK;AACX,YAAA,kBAAkB,EAAE,KAAK;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;AACf,YAAA,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,YAAA,SAAS,EAAE,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;SAClC;IACH;AAEQ,IAAA,MAAM,WAAW,CAAC,KAAmB,EAAE,QAAgB,EAAA;AAC7D,QAAA,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACtD,QAAA,IAAI,IAAI,IAAI,SAAS,EAAE;YACrB;QACF;AAEA,QAAA,IAAI,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE;AAC5C,YAAA,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC;AAEnE,YAAA,MAAM,OAAO,CAAC,GAAG,CACf,QAAQ,CAAC,GAAG,CAAC,OAAO,UAAU,KAAI;gBAChC,MAAM,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;AAE7B,gBAAA,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC;gBAElC,IAAI,CAAC,UAAU,CACb,MAAM,CAAC,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EACtD,YAAY,uBAAuB,CAAC,UAAU,CAAC,CAAA,YAAA;AAC7C,sBAAA,CAAA,sBAAA,EAAyB,uBAAuB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA,CAAE,CAClF;YACH,CAAC,CAAC,CACH;QACH;aAAO;;YAEL,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC1C,IAAI,OAAO,EAAE;AACX,gBAAA,IAAI,CAAC,UAAU,CACb,IAAI,EACJ,CAAA,SAAA,EAAY,uBAAuB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA,uBAAA,CAAyB,CAC1E;YACH;QACF;IACF;IAEQ,UAAU,CAAC,IAAsB,EAAE,MAAc,EAAA;AACvD,QAAA,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;AAClB,QAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,kBAAA,EAAqB,IAAI,CAAC,IAAI,CAAA,UAAA,EAAa,MAAM,CAAA,CAAE,CAAC;AAC5E,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC/C,QAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AACjD,QAAA,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrD;AAEQ,IAAA,MAAM,mBAAmB,CAAC,MAAkB,EAAE,QAAgB,EAAA;QACpE,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK;AACnF,QAAA,IAAI,OAAO;YAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzD;;AAGA,IAAA,MAAM,UAAU,GAAA;AACd,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE;QAEzB,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,GAAG,KAAI;AACvC,YAAA,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC;AAC9B,YAAA,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAQ,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA,SAAA,CAAW,CAAC;AAC/E,QAAA,CAAC,CAAC;IACJ;AAEA,IAAA,MAAM,OAAO,GAAA;AACX,QAAA,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE;IAClC;AAEA,IAAA,OAAO,MAAM,CAAC,YAAY,CAAC,GAAA;AACzB,QAAA,MAAM,IAAI,CAAC,OAAO,EAAE;IACtB;AACD;AAED;AACA,MAAM,kBAAkB,CAAA;AAIH,IAAA,IAAA;AACA,IAAA,MAAA;AAJF,IAAA,OAAO,GAAG,IAAI,cAAc,EAAE;IAE/C,WAAA,CACmB,IAAY,EACZ,MAAwB,EAAA;QADxB,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,MAAM,GAAN,MAAM;IACtB;IAEI,SAAS,GAAA;AACd,QAAA,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE;IACjD;AAEO,IAAA,MAAM,CAAC,QAAgB,EAAA;AAC5B,QAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IAC5B;AAEO,IAAA,OAAO,CAAC,QAAgB,EAAA;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACnC;AACD;AAED,MAAM,eAAe,CAAA;AAOA,IAAA,IAAA;AACA,IAAA,KAAA;AACA,IAAA,eAAA;AARX,IAAA,OAAO;AACP,IAAA,GAAG;AACM,IAAA,MAAM,GAAiB,IAAI,YAAY,EAAE;IAClD,KAAK,GAAoB,SAAS;AAE1C,IAAA,WAAA,CACmB,IAAY,EACZ,KAAa,EACb,eAAwB,EAAA;QAFxB,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,KAAK,GAAL,KAAK;QACL,IAAA,CAAA,eAAe,GAAf,eAAe;AAEhC,QAAA,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,CAAC,YAAY,IAAI,CAAC,MAAM,EAAE,CAAC;IACvD;AAEA,IAAA,aAAa,CAAC,CAAU,EAAA;AAItB,QAAA,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAE5B,QAAA,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;QAEvB,OAAO;YACL,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB;IACH;AAEA,IAAA,MAAM,MAAM,GAAA;AACV,QAAA,IAAI;AACF,YAAA,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,eAAe,CAAC;AAE/E,YAAA,IAAI,IAAI,CAAC,GAAG,IAAI,OAAO;gBAAE,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,SAAA,EAAY,IAAI,CAAC,IAAI,CAAA,QAAA,CAAU,CAAC;AACjF,YAAA,IAAI,CAAC,GAAG,GAAG,OAAO;QACpB;QAAE,OAAO,CAAM,EAAE;AACf,YAAA,IAAI,CAAC,CAAC,IAAI,IAAI,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,WAAW,EAAE;;AAEjD,gBAAA,IAAI,CAAC,GAAG,GAAG,EAAE;AACb,gBAAA,IAAI,CAAC,KAAK,GAAG,CAAC;gBACd,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA,eAAA,EAAkB,IAAI,CAAC,IAAI,CAAA,2BAAA,CAA6B,CAAC;gBACjF;YACF;AAEA,YAAA,MAAM,CAAC;QACT;IACF;AACD;AAED;AACqC;AACrC,eAAe,YAAY,CAAC,KAAa,EAAE,MAAc,EAAE,eAAwB,EAAA;AACjF,IAAA,IAAI,QAAmC;AACvC,IAAA,IAAI,EAAkC;AAEtC,IAAA,IAAI;AACF,QAAA,QAAQ,GAAG,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC;AACrC,QAAA,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAEvE,QAAA,MAAM,KAAK,GAAG,IAAI,MAAM,EAAE;AAE1B,QAAA,WAAW,MAAM,IAAI,IAAI,EAAE,EAAE;YAC3B,IAAI,eAAe,IAAI,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC;gBAAE;AAErE,YAAA,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;AAChB,YAAA,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE;gBACzB,KAAK,CAAC,KAAK,EAAE;YACf;QACF;;AAGA,QAAA,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG;IAC9C;YAAU;;AAER,QAAA,IAAI;YACF,IAAI,EAAE,EAAE;gBACN,EAAE,CAAC,KAAK,EAAE;YACZ;QACF;QAAE,OAAO,YAAY,EAAE;AACrB,YAAA,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,YAAY,CAAC;QAClE;AAEA,QAAA,IAAI;AACF,YAAA,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;gBACnC,QAAQ,CAAC,OAAO,EAAE;YACpB;QACF;QAAE,OAAO,YAAY,EAAE;AACrB,YAAA,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,YAAY,CAAC;QAC9D;IACF;AACF;AAEA,SAAS,gCAAgC,CAAC,UAAkB,EAAE,KAAmB,EAAA;IAC/E,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE;QACnC,IAAI,OAAO,GAAG,CAAA,EAAG,UAAU,0BAA0B,KAAK,CAAC,IAAI,CAAA,wDAAA,CAA0D;AACzH,QAAA,IAAI,KAAK,CAAC,IAAI,IAAI,MAAM;YACtB,OAAO,IAAI,2FAA2F;AAExG,QAAA,MAAM,IAAI,sBAAsB,CAAC,OAAO,CAAC;IAC3C;AACF;;;;"}
|
|
@@ -183,6 +183,12 @@ class SparseCache {
|
|
|
183
183
|
await this.ranges.delete(key);
|
|
184
184
|
this.keyToLastAccessTime.delete(key);
|
|
185
185
|
}
|
|
186
|
+
async dispose() {
|
|
187
|
+
await this.lock.acquireAsync();
|
|
188
|
+
}
|
|
189
|
+
async [Symbol.asyncDispose]() {
|
|
190
|
+
await this.dispose();
|
|
191
|
+
}
|
|
186
192
|
}
|
|
187
193
|
/** Acquires the lock and executes a callback. */
|
|
188
194
|
async function withLock(lock, cb) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.cjs","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"sourcesContent":["import { RangeBytes } from '@milaboratories/pl-model-common';\nimport { ensureDirExists, fileExists, mapEntries, MiLogger } from '@milaboratories/ts-helpers';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { addRange, doesRangeExist, Ranges, rangesFileName, rangesFilePostfix, rangesSize, readRangesFile, writeRangesFile } from './ranges';\nimport { writeToSparseFile } from './file';\nimport { functions } from '@milaboratories/helpers';\n\n/** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */\nexport class CorruptedRangesError extends Error {\n name = 'CorruptedRangesError';\n}\n\n/** Extracted ranges methods to be able to store ranges somewhere else (e.g. in memory for tests). */\nexport interface SparseCacheRanges {\n get(key: string): Promise<Ranges>;\n set(key: string, ranges: Ranges): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores ranges in a directory as JSON files (the default implementation). */\nexport class SparseCacheFsRanges implements SparseCacheRanges {\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n private fPath(key: string): string {\n return path.join(this.cacheDir, rangesFileName(key));\n }\n\n async get(key: string): Promise<Ranges> {\n return await readRangesFile(this.logger, this.fPath(key));\n }\n\n async set(key: string, ranges: Ranges) {\n return await writeRangesFile(this.logger, this.fPath(key), ranges);\n }\n\n async delete(key: string) {\n await fs.rm(this.fPath(key));\n }\n}\n\n/** Extracted interface for storing sparse files. */\nexport interface SparseFileStorage {\n all(): Promise<string[]>;\n exists(key: string): Promise<boolean>;\n path(key: string): string;\n write(key: string, data: Uint8Array, from: number): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores sparse files in a directory (the default implementation). */\nexport class SparseCacheFsFile implements SparseFileStorage {\n private readonly suffix = '.sparse.bin';\n\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n async all(): Promise<string[]> {\n await ensureDirExists(this.cacheDir);\n const files = await fs.readdir(this.cacheDir);\n return files.filter((f) => f.endsWith(this.suffix));\n }\n\n async exists(key: string): Promise<boolean> {\n return await fileExists(this.path(key));\n }\n\n path(key: string): string {\n return path.join(this.cacheDir, key + this.suffix);\n }\n\n async write(key: string, data: Uint8Array, from: number): Promise<void> {\n await ensureDirExists(this.cacheDir);\n await writeToSparseFile(this.logger, process.platform, this.path(key), data, from);\n }\n\n async delete(key: string): Promise<void> {\n await fs.rm(this.path(key));\n }\n}\n\n/** LRU cache for ranges of sparse files. */\nexport class SparseCache {\n /** Fields are public for tests. */\n\n /** The lock to make sure cache requests are done one by one. */\n private lock = new functions.AwaitLock()\n\n public keyToLastAccessTime = new Map<string, Date>();\n public size = 0;\n\n constructor(\n public readonly logger: MiLogger,\n /** The hard limit in bytes. */\n public readonly maxSize: number,\n public readonly ranges: SparseCacheRanges,\n public readonly storage: SparseFileStorage,\n ) {}\n\n /** Resets a cache's size by rereading everything we already store.\n * Safe for concurrent use. */\n async reset() {\n await withLock(this.lock, async () => {\n await this.resetUnsafe();\n })\n }\n\n /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.\n * Safe for concurrent use. */\n async get(key: string, range: RangeBytes): Promise<string | undefined> {\n return await withLock(this.lock, async () => {\n return await this.getUnsafe(key, range);\n });\n }\n\n /** Sets data to the cache's file and clear the cache if it's needed.\n * Safe for concurrent use. */\n async set(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n await withLock(this.lock, async () => {\n await this.setUnsafe(key, range, data);\n })\n }\n\n private async resetUnsafe() {\n this.size = 0;\n this.keyToLastAccessTime = new Map<string, Date>();\n\n const now = new Date();\n // In rmKey method we first deletes the key from a storage and only then from ranges,\n // so if something goes wrong between 2 operations,\n // on reset the logic will be correct.\n for (const key of await this.storage.all()) {\n const ranges = await this.ranges.get(key);\n this.size += rangesSize(ranges);\n this.keyToLastAccessTime.set(key, now);\n }\n }\n\n private async getUnsafe(key: string, range: RangeBytes): Promise<string | undefined> {\n // It first checks the storage, and then the ranges.\n // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,\n // so if we don't have a key in storage but have it in ranges, the logic here is correct.\n // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.\n if (await this.storage.exists(key)) {\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n if (doesRangeExist(ranges, range)) {\n return this.storage.path(key);\n }\n\n return undefined;\n }\n\n return undefined;\n }\n\n private async setUnsafe(key: string, range: { from: number; to: number; }, data: Uint8Array) {\n await this.setWithoutEviction(key, range, data);\n await this.ensureEvicted();\n }\n\n /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */\n async setWithoutEviction(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n if (range.to - range.from !== data.length) {\n throw new Error(\n `SparseCache.set: trying to set ${key} with wrong range length: `\n + `range: ${JSON.stringify(range)}, data: ${data.length}`\n );\n }\n\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n\n await this.storage.write(key, data, range.from);\n\n const newRanges = addRange(ranges, range);\n this.size += rangesSize(newRanges);\n\n await this.ranges.set(key, newRanges);\n }\n\n /** Ensures the size is less than hard limit by deleting the oldest keys. */\n async ensureEvicted(): Promise<void> {\n const byTime = mapEntries(this.keyToLastAccessTime);\n byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());\n\n while (this.size > this.maxSize) {\n const keyAndDate = byTime.pop(); // removes the oldest\n if (!keyAndDate) {\n break;\n }\n const [key, _] = keyAndDate;\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n this.rmKey(key);\n }\n }\n\n /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */\n private async getRanges(key: string) {\n try {\n return await this.ranges.get(key);\n } catch (e: unknown) {\n if (e instanceof CorruptedRangesError) {\n // We need to reset a state of the cache and update current size,\n // it's the only way to calculate the real size when one of the ranges were corrupted.\n await this.rmKey(key);\n await this.resetUnsafe();\n\n return await this.ranges.get(key);\n }\n\n throw e;\n }\n }\n\n /** Removes a key the state of the cache. The size should be updated. */\n private async rmKey(key: string) {\n await this.storage.delete(key);\n await this.ranges.delete(key);\n this.keyToLastAccessTime.delete(key);\n }\n}\n\n/** Acquires the lock and executes a callback. */\nasync function withLock<T>(lock: functions.AwaitLock, cb: () => Promise<T>): Promise<T> {\n try {\n await lock.acquireAsync();\n return await cb();\n } finally {\n lock.release();\n }\n}\n"],"names":["rangesFileName","readRangesFile","ranges","writeRangesFile","fs","ensureDirExists","fileExists","writeToSparseFile","functions","rangesSize","doesRangeExist","addRange","mapEntries"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache.cjs","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"sourcesContent":["import { RangeBytes } from '@milaboratories/pl-model-common';\nimport { ensureDirExists, fileExists, mapEntries, MiLogger } from '@milaboratories/ts-helpers';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { addRange, doesRangeExist, Ranges, rangesFileName, rangesFilePostfix, rangesSize, readRangesFile, writeRangesFile } from './ranges';\nimport { writeToSparseFile } from './file';\nimport { functions } from '@milaboratories/helpers';\n\n/** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */\nexport class CorruptedRangesError extends Error {\n name = 'CorruptedRangesError';\n}\n\n/** Extracted ranges methods to be able to store ranges somewhere else (e.g. in memory for tests). */\nexport interface SparseCacheRanges {\n get(key: string): Promise<Ranges>;\n set(key: string, ranges: Ranges): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores ranges in a directory as JSON files (the default implementation). */\nexport class SparseCacheFsRanges implements SparseCacheRanges {\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n private fPath(key: string): string {\n return path.join(this.cacheDir, rangesFileName(key));\n }\n\n async get(key: string): Promise<Ranges> {\n return await readRangesFile(this.logger, this.fPath(key));\n }\n\n async set(key: string, ranges: Ranges) {\n return await writeRangesFile(this.logger, this.fPath(key), ranges);\n }\n\n async delete(key: string) {\n await fs.rm(this.fPath(key));\n }\n}\n\n/** Extracted interface for storing sparse files. */\nexport interface SparseFileStorage {\n all(): Promise<string[]>;\n exists(key: string): Promise<boolean>;\n path(key: string): string;\n write(key: string, data: Uint8Array, from: number): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores sparse files in a directory (the default implementation). */\nexport class SparseCacheFsFile implements SparseFileStorage {\n private readonly suffix = '.sparse.bin';\n\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n async all(): Promise<string[]> {\n await ensureDirExists(this.cacheDir);\n const files = await fs.readdir(this.cacheDir);\n return files.filter((f) => f.endsWith(this.suffix));\n }\n\n async exists(key: string): Promise<boolean> {\n return await fileExists(this.path(key));\n }\n\n path(key: string): string {\n return path.join(this.cacheDir, key + this.suffix);\n }\n\n async write(key: string, data: Uint8Array, from: number): Promise<void> {\n await ensureDirExists(this.cacheDir);\n await writeToSparseFile(this.logger, process.platform, this.path(key), data, from);\n }\n\n async delete(key: string): Promise<void> {\n await fs.rm(this.path(key));\n }\n}\n\n/** LRU cache for ranges of sparse files. */\nexport class SparseCache implements AsyncDisposable {\n /** Fields are public for tests. */\n\n /** The lock to make sure cache requests are done one by one. */\n private lock = new functions.AwaitLock()\n\n public keyToLastAccessTime = new Map<string, Date>();\n public size = 0;\n\n constructor(\n public readonly logger: MiLogger,\n /** The hard limit in bytes. */\n public readonly maxSize: number,\n public readonly ranges: SparseCacheRanges,\n public readonly storage: SparseFileStorage,\n ) {}\n\n /** Resets a cache's size by rereading everything we already store.\n * Safe for concurrent use. */\n async reset() {\n await withLock(this.lock, async () => {\n await this.resetUnsafe();\n })\n }\n\n /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.\n * Safe for concurrent use. */\n async get(key: string, range: RangeBytes): Promise<string | undefined> {\n return await withLock(this.lock, async () => {\n return await this.getUnsafe(key, range);\n });\n }\n\n /** Sets data to the cache's file and clear the cache if it's needed.\n * Safe for concurrent use. */\n async set(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n await withLock(this.lock, async () => {\n await this.setUnsafe(key, range, data);\n })\n }\n\n private async resetUnsafe() {\n this.size = 0;\n this.keyToLastAccessTime = new Map<string, Date>();\n\n const now = new Date();\n // In rmKey method we first deletes the key from a storage and only then from ranges,\n // so if something goes wrong between 2 operations,\n // on reset the logic will be correct.\n for (const key of await this.storage.all()) {\n const ranges = await this.ranges.get(key);\n this.size += rangesSize(ranges);\n this.keyToLastAccessTime.set(key, now);\n }\n }\n\n private async getUnsafe(key: string, range: RangeBytes): Promise<string | undefined> {\n // It first checks the storage, and then the ranges.\n // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,\n // so if we don't have a key in storage but have it in ranges, the logic here is correct.\n // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.\n if (await this.storage.exists(key)) {\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n if (doesRangeExist(ranges, range)) {\n return this.storage.path(key);\n }\n\n return undefined;\n }\n\n return undefined;\n }\n\n private async setUnsafe(key: string, range: { from: number; to: number; }, data: Uint8Array) {\n await this.setWithoutEviction(key, range, data);\n await this.ensureEvicted();\n }\n\n /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */\n async setWithoutEviction(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n if (range.to - range.from !== data.length) {\n throw new Error(\n `SparseCache.set: trying to set ${key} with wrong range length: `\n + `range: ${JSON.stringify(range)}, data: ${data.length}`\n );\n }\n\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n\n await this.storage.write(key, data, range.from);\n\n const newRanges = addRange(ranges, range);\n this.size += rangesSize(newRanges);\n\n await this.ranges.set(key, newRanges);\n }\n\n /** Ensures the size is less than hard limit by deleting the oldest keys. */\n async ensureEvicted(): Promise<void> {\n const byTime = mapEntries(this.keyToLastAccessTime);\n byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());\n\n while (this.size > this.maxSize) {\n const keyAndDate = byTime.pop(); // removes the oldest\n if (!keyAndDate) {\n break;\n }\n const [key, _] = keyAndDate;\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n this.rmKey(key);\n }\n }\n\n /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */\n private async getRanges(key: string) {\n try {\n return await this.ranges.get(key);\n } catch (e: unknown) {\n if (e instanceof CorruptedRangesError) {\n // We need to reset a state of the cache and update current size,\n // it's the only way to calculate the real size when one of the ranges were corrupted.\n await this.rmKey(key);\n await this.resetUnsafe();\n\n return await this.ranges.get(key);\n }\n\n throw e;\n }\n }\n\n /** Removes a key the state of the cache. The size should be updated. */\n private async rmKey(key: string) {\n await this.storage.delete(key);\n await this.ranges.delete(key);\n this.keyToLastAccessTime.delete(key);\n }\n\n async dispose(): Promise<void> {\n await this.lock.acquireAsync();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\n/** Acquires the lock and executes a callback. */\nasync function withLock<T>(lock: functions.AwaitLock, cb: () => Promise<T>): Promise<T> {\n try {\n await lock.acquireAsync();\n return await cb();\n } finally {\n lock.release();\n }\n}\n"],"names":["rangesFileName","readRangesFile","ranges","writeRangesFile","fs","ensureDirExists","fileExists","writeToSparseFile","functions","rangesSize","doesRangeExist","addRange","mapEntries"],"mappings":";;;;;;;;;AAQA;AACM,MAAO,oBAAqB,SAAQ,KAAK,CAAA;IAC7C,IAAI,GAAG,sBAAsB;AAC9B;AASD;MACa,mBAAmB,CAAA;AAEX,IAAA,MAAA;AACA,IAAA,QAAA;IAFnB,WAAA,CACmB,MAAgB,EAChB,QAAgB,EAAA;QADhB,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,QAAQ,GAAR,QAAQ;IACxB;AAEK,IAAA,KAAK,CAAC,GAAW,EAAA;AACvB,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAEA,qBAAc,CAAC,GAAG,CAAC,CAAC;IACtD;IAEA,MAAM,GAAG,CAAC,GAAW,EAAA;AACnB,QAAA,OAAO,MAAMC,qBAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3D;AAEA,IAAA,MAAM,GAAG,CAAC,GAAW,EAAEC,QAAc,EAAA;AACnC,QAAA,OAAO,MAAMC,sBAAe,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAED,QAAM,CAAC;IACpE;IAEA,MAAM,MAAM,CAAC,GAAW,EAAA;QACtB,MAAME,WAAE,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9B;AACD;AAWD;MACa,iBAAiB,CAAA;AAIT,IAAA,MAAA;AACA,IAAA,QAAA;IAJF,MAAM,GAAG,aAAa;IAEvC,WAAA,CACmB,MAAgB,EAChB,QAAgB,EAAA;QADhB,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,QAAQ,GAAR,QAAQ;IACxB;AAEH,IAAA,MAAM,GAAG,GAAA;AACP,QAAA,MAAMC,yBAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;QACpC,MAAM,KAAK,GAAG,MAAMD,WAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;AAC7C,QAAA,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACrD;IAEA,MAAM,MAAM,CAAC,GAAW,EAAA;QACtB,OAAO,MAAME,oBAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC;AAEA,IAAA,IAAI,CAAC,GAAW,EAAA;AACd,QAAA,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC;IACpD;AAEA,IAAA,MAAM,KAAK,CAAC,GAAW,EAAE,IAAgB,EAAE,IAAY,EAAA;AACrD,QAAA,MAAMD,yBAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;QACpC,MAAME,sBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC;IACpF;IAEA,MAAM,MAAM,CAAC,GAAW,EAAA;QACtB,MAAMH,WAAE,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7B;AACD;AAED;MACa,WAAW,CAAA;AAUJ,IAAA,MAAA;AAEA,IAAA,OAAA;AACA,IAAA,MAAA;AACA,IAAA,OAAA;;;AAVV,IAAA,IAAI,GAAG,IAAII,iBAAS,CAAC,SAAS,EAAE;AAEjC,IAAA,mBAAmB,GAAG,IAAI,GAAG,EAAgB;IAC7C,IAAI,GAAG,CAAC;AAEf,IAAA,WAAA,CACkB,MAAgB;;IAEhB,OAAe,EACf,MAAyB,EACzB,OAA0B,EAAA;QAJ1B,IAAA,CAAA,MAAM,GAAN,MAAM;QAEN,IAAA,CAAA,OAAO,GAAP,OAAO;QACP,IAAA,CAAA,MAAM,GAAN,MAAM;QACN,IAAA,CAAA,OAAO,GAAP,OAAO;IACtB;AAEH;AAC8B;AAC9B,IAAA,MAAM,KAAK,GAAA;QACT,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,YAAW;AACnC,YAAA,MAAM,IAAI,CAAC,WAAW,EAAE;AAC1B,QAAA,CAAC,CAAC;IACJ;AAEA;AAC8B;AAC9B,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAiB,EAAA;QACtC,OAAO,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,YAAW;YAC1C,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC;AACzC,QAAA,CAAC,CAAC;IACJ;AAEA;AAC8B;AAC9B,IAAA,MAAM,GAAG,CAAC,GAAW,EAAE,KAAiB,EAAE,IAAgB,EAAA;QACxD,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,YAAW;YACnC,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC;AACxC,QAAA,CAAC,CAAC;IACJ;AAEQ,IAAA,MAAM,WAAW,GAAA;AACvB,QAAA,IAAI,CAAC,IAAI,GAAG,CAAC;AACb,QAAA,IAAI,CAAC,mBAAmB,GAAG,IAAI,GAAG,EAAgB;AAElD,QAAA,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE;;;;QAItB,KAAK,MAAM,GAAG,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE;YAC1C,MAAMN,QAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;AACzC,YAAA,IAAI,CAAC,IAAI,IAAIO,iBAAU,CAACP,QAAM,CAAC;YAC/B,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC;QACxC;IACF;AAEQ,IAAA,MAAM,SAAS,CAAC,GAAW,EAAE,KAAiB,EAAA;;;;;QAKpD,IAAI,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAClC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;YAE7C,MAAMA,QAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;AACxC,YAAA,IAAIQ,qBAAc,CAACR,QAAM,EAAE,KAAK,CAAC,EAAE;gBACjC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;YAC/B;AAEA,YAAA,OAAO,SAAS;QAClB;AAEA,QAAA,OAAO,SAAS;IAClB;AAEQ,IAAA,MAAM,SAAS,CAAC,GAAW,EAAE,KAAoC,EAAE,IAAgB,EAAA;QACzF,MAAM,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC;AAC/C,QAAA,MAAM,IAAI,CAAC,aAAa,EAAE;IAC5B;;AAGA,IAAA,MAAM,kBAAkB,CAAC,GAAW,EAAE,KAAiB,EAAE,IAAgB,EAAA;AACvE,QAAA,IAAI,KAAK,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE;AACzC,YAAA,MAAM,IAAI,KAAK,CACb,CAAA,+BAAA,EAAkC,GAAG,CAAA,0BAAA;AACnC,kBAAA,CAAA,OAAA,EAAU,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA,QAAA,EAAW,IAAI,CAAC,MAAM,CAAA,CAAE,CAC1D;QACH;QAEA,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,IAAI,EAAE,CAAC;QAE7C,MAAMA,QAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;AACxC,QAAA,IAAI,CAAC,IAAI,IAAIO,iBAAU,CAACP,QAAM,CAAC;AAE/B,QAAA,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC;QAE/C,MAAM,SAAS,GAAGS,eAAQ,CAACT,QAAM,EAAE,KAAK,CAAC;AACzC,QAAA,IAAI,CAAC,IAAI,IAAIO,iBAAU,CAAC,SAAS,CAAC;QAElC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC;IACvC;;AAGA,IAAA,MAAM,aAAa,GAAA;QACjB,MAAM,MAAM,GAAGG,oBAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC;AACnD,QAAA,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;QAE3E,OAAO,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE;YAC/B,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,EAAE,CAAC;YAChC,IAAI,CAAC,UAAU,EAAE;gBACf;YACF;AACA,YAAA,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,UAAU;YAE3B,MAAMV,QAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC;AACxC,YAAA,IAAI,CAAC,IAAI,IAAIO,iBAAU,CAACP,QAAM,CAAC;AAC/B,YAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;QACjB;IACF;;IAGQ,MAAM,SAAS,CAAC,GAAW,EAAA;AACjC,QAAA,IAAI;YACF,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QACnC;QAAE,OAAO,CAAU,EAAE;AACnB,YAAA,IAAI,CAAC,YAAY,oBAAoB,EAAE;;;AAGrC,gBAAA,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;AACrB,gBAAA,MAAM,IAAI,CAAC,WAAW,EAAE;gBAExB,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;YACnC;AAEA,YAAA,MAAM,CAAC;QACT;IACF;;IAGQ,MAAM,KAAK,CAAC,GAAW,EAAA;QAC7B,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC;QAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC;AAC7B,QAAA,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC;IACtC;AAEA,IAAA,MAAM,OAAO,GAAA;AACX,QAAA,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;IAChC;AAEA,IAAA,OAAO,MAAM,CAAC,YAAY,CAAC,GAAA;AACzB,QAAA,MAAM,IAAI,CAAC,OAAO,EAAE;IACtB;AACD;AAED;AACA,eAAe,QAAQ,CAAI,IAAyB,EAAE,EAAoB,EAAA;AACxE,IAAA,IAAI;AACF,QAAA,MAAM,IAAI,CAAC,YAAY,EAAE;QACzB,OAAO,MAAM,EAAE,EAAE;IACnB;YAAU;QACR,IAAI,CAAC,OAAO,EAAE;IAChB;AACF;;;;;;;"}
|
|
@@ -42,7 +42,7 @@ export declare class SparseCacheFsFile implements SparseFileStorage {
|
|
|
42
42
|
delete(key: string): Promise<void>;
|
|
43
43
|
}
|
|
44
44
|
/** LRU cache for ranges of sparse files. */
|
|
45
|
-
export declare class SparseCache {
|
|
45
|
+
export declare class SparseCache implements AsyncDisposable {
|
|
46
46
|
readonly logger: MiLogger;
|
|
47
47
|
/** The hard limit in bytes. */
|
|
48
48
|
readonly maxSize: number;
|
|
@@ -76,5 +76,7 @@ export declare class SparseCache {
|
|
|
76
76
|
private getRanges;
|
|
77
77
|
/** Removes a key the state of the cache. The size should be updated. */
|
|
78
78
|
private rmKey;
|
|
79
|
+
dispose(): Promise<void>;
|
|
80
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
79
81
|
}
|
|
80
82
|
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,EAA2C,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAG/F,OAAO,EAA4B,MAAM,EAAkF,MAAM,UAAU,CAAC;AAI5I,oFAAoF;AACpF,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,IAAI,SAA0B;CAC/B;AAED,qGAAqG;AACrG,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,+EAA+E;AAC/E,qBAAa,mBAAoB,YAAW,iBAAiB;IAEzD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBADR,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,MAAM;IAGnC,OAAO,CAAC,KAAK;IAIP,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAIjC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAI/B,MAAM,CAAC,GAAG,EAAE,MAAM;CAGzB;AAED,oDAAoD;AACpD,MAAM,WAAW,iBAAiB;IAChC,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,uEAAuE;AACvE,qBAAa,iBAAkB,YAAW,iBAAiB;IAIvD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiB;gBAGrB,MAAM,EAAE,QAAQ,EAChB,QAAQ,EAAE,MAAM;IAG7B,GAAG,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAMxB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAInB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGzC;AAED,4CAA4C;AAC5C,qBAAa,
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -181,6 +181,12 @@ class SparseCache {
|
|
|
181
181
|
await this.ranges.delete(key);
|
|
182
182
|
this.keyToLastAccessTime.delete(key);
|
|
183
183
|
}
|
|
184
|
+
async dispose() {
|
|
185
|
+
await this.lock.acquireAsync();
|
|
186
|
+
}
|
|
187
|
+
async [Symbol.asyncDispose]() {
|
|
188
|
+
await this.dispose();
|
|
189
|
+
}
|
|
184
190
|
}
|
|
185
191
|
/** Acquires the lock and executes a callback. */
|
|
186
192
|
async function withLock(lock, cb) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"sourcesContent":["import { RangeBytes } from '@milaboratories/pl-model-common';\nimport { ensureDirExists, fileExists, mapEntries, MiLogger } from '@milaboratories/ts-helpers';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { addRange, doesRangeExist, Ranges, rangesFileName, rangesFilePostfix, rangesSize, readRangesFile, writeRangesFile } from './ranges';\nimport { writeToSparseFile } from './file';\nimport { functions } from '@milaboratories/helpers';\n\n/** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */\nexport class CorruptedRangesError extends Error {\n name = 'CorruptedRangesError';\n}\n\n/** Extracted ranges methods to be able to store ranges somewhere else (e.g. in memory for tests). */\nexport interface SparseCacheRanges {\n get(key: string): Promise<Ranges>;\n set(key: string, ranges: Ranges): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores ranges in a directory as JSON files (the default implementation). */\nexport class SparseCacheFsRanges implements SparseCacheRanges {\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n private fPath(key: string): string {\n return path.join(this.cacheDir, rangesFileName(key));\n }\n\n async get(key: string): Promise<Ranges> {\n return await readRangesFile(this.logger, this.fPath(key));\n }\n\n async set(key: string, ranges: Ranges) {\n return await writeRangesFile(this.logger, this.fPath(key), ranges);\n }\n\n async delete(key: string) {\n await fs.rm(this.fPath(key));\n }\n}\n\n/** Extracted interface for storing sparse files. */\nexport interface SparseFileStorage {\n all(): Promise<string[]>;\n exists(key: string): Promise<boolean>;\n path(key: string): string;\n write(key: string, data: Uint8Array, from: number): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores sparse files in a directory (the default implementation). */\nexport class SparseCacheFsFile implements SparseFileStorage {\n private readonly suffix = '.sparse.bin';\n\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n async all(): Promise<string[]> {\n await ensureDirExists(this.cacheDir);\n const files = await fs.readdir(this.cacheDir);\n return files.filter((f) => f.endsWith(this.suffix));\n }\n\n async exists(key: string): Promise<boolean> {\n return await fileExists(this.path(key));\n }\n\n path(key: string): string {\n return path.join(this.cacheDir, key + this.suffix);\n }\n\n async write(key: string, data: Uint8Array, from: number): Promise<void> {\n await ensureDirExists(this.cacheDir);\n await writeToSparseFile(this.logger, process.platform, this.path(key), data, from);\n }\n\n async delete(key: string): Promise<void> {\n await fs.rm(this.path(key));\n }\n}\n\n/** LRU cache for ranges of sparse files. */\nexport class SparseCache {\n /** Fields are public for tests. */\n\n /** The lock to make sure cache requests are done one by one. */\n private lock = new functions.AwaitLock()\n\n public keyToLastAccessTime = new Map<string, Date>();\n public size = 0;\n\n constructor(\n public readonly logger: MiLogger,\n /** The hard limit in bytes. */\n public readonly maxSize: number,\n public readonly ranges: SparseCacheRanges,\n public readonly storage: SparseFileStorage,\n ) {}\n\n /** Resets a cache's size by rereading everything we already store.\n * Safe for concurrent use. */\n async reset() {\n await withLock(this.lock, async () => {\n await this.resetUnsafe();\n })\n }\n\n /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.\n * Safe for concurrent use. */\n async get(key: string, range: RangeBytes): Promise<string | undefined> {\n return await withLock(this.lock, async () => {\n return await this.getUnsafe(key, range);\n });\n }\n\n /** Sets data to the cache's file and clear the cache if it's needed.\n * Safe for concurrent use. */\n async set(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n await withLock(this.lock, async () => {\n await this.setUnsafe(key, range, data);\n })\n }\n\n private async resetUnsafe() {\n this.size = 0;\n this.keyToLastAccessTime = new Map<string, Date>();\n\n const now = new Date();\n // In rmKey method we first deletes the key from a storage and only then from ranges,\n // so if something goes wrong between 2 operations,\n // on reset the logic will be correct.\n for (const key of await this.storage.all()) {\n const ranges = await this.ranges.get(key);\n this.size += rangesSize(ranges);\n this.keyToLastAccessTime.set(key, now);\n }\n }\n\n private async getUnsafe(key: string, range: RangeBytes): Promise<string | undefined> {\n // It first checks the storage, and then the ranges.\n // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,\n // so if we don't have a key in storage but have it in ranges, the logic here is correct.\n // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.\n if (await this.storage.exists(key)) {\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n if (doesRangeExist(ranges, range)) {\n return this.storage.path(key);\n }\n\n return undefined;\n }\n\n return undefined;\n }\n\n private async setUnsafe(key: string, range: { from: number; to: number; }, data: Uint8Array) {\n await this.setWithoutEviction(key, range, data);\n await this.ensureEvicted();\n }\n\n /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */\n async setWithoutEviction(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n if (range.to - range.from !== data.length) {\n throw new Error(\n `SparseCache.set: trying to set ${key} with wrong range length: `\n + `range: ${JSON.stringify(range)}, data: ${data.length}`\n );\n }\n\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n\n await this.storage.write(key, data, range.from);\n\n const newRanges = addRange(ranges, range);\n this.size += rangesSize(newRanges);\n\n await this.ranges.set(key, newRanges);\n }\n\n /** Ensures the size is less than hard limit by deleting the oldest keys. */\n async ensureEvicted(): Promise<void> {\n const byTime = mapEntries(this.keyToLastAccessTime);\n byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());\n\n while (this.size > this.maxSize) {\n const keyAndDate = byTime.pop(); // removes the oldest\n if (!keyAndDate) {\n break;\n }\n const [key, _] = keyAndDate;\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n this.rmKey(key);\n }\n }\n\n /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */\n private async getRanges(key: string) {\n try {\n return await this.ranges.get(key);\n } catch (e: unknown) {\n if (e instanceof CorruptedRangesError) {\n // We need to reset a state of the cache and update current size,\n // it's the only way to calculate the real size when one of the ranges were corrupted.\n await this.rmKey(key);\n await this.resetUnsafe();\n\n return await this.ranges.get(key);\n }\n\n throw e;\n }\n }\n\n /** Removes a key the state of the cache. The size should be updated. */\n private async rmKey(key: string) {\n await this.storage.delete(key);\n await this.ranges.delete(key);\n this.keyToLastAccessTime.delete(key);\n }\n}\n\n/** Acquires the lock and executes a callback. */\nasync function withLock<T>(lock: functions.AwaitLock, cb: () => Promise<T>): Promise<T> {\n try {\n await lock.acquireAsync();\n return await cb();\n } finally {\n lock.release();\n }\n}\n"],"names":["path","fs"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cache.js","sources":["../../../../src/drivers/download_blob/sparse_cache/cache.ts"],"sourcesContent":["import { RangeBytes } from '@milaboratories/pl-model-common';\nimport { ensureDirExists, fileExists, mapEntries, MiLogger } from '@milaboratories/ts-helpers';\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport { addRange, doesRangeExist, Ranges, rangesFileName, rangesFilePostfix, rangesSize, readRangesFile, writeRangesFile } from './ranges';\nimport { writeToSparseFile } from './file';\nimport { functions } from '@milaboratories/helpers';\n\n/** The implementer of SparseCacheRanges could throw it if ranges were corrupted. */\nexport class CorruptedRangesError extends Error {\n name = 'CorruptedRangesError';\n}\n\n/** Extracted ranges methods to be able to store ranges somewhere else (e.g. in memory for tests). */\nexport interface SparseCacheRanges {\n get(key: string): Promise<Ranges>;\n set(key: string, ranges: Ranges): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores ranges in a directory as JSON files (the default implementation). */\nexport class SparseCacheFsRanges implements SparseCacheRanges {\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n private fPath(key: string): string {\n return path.join(this.cacheDir, rangesFileName(key));\n }\n\n async get(key: string): Promise<Ranges> {\n return await readRangesFile(this.logger, this.fPath(key));\n }\n\n async set(key: string, ranges: Ranges) {\n return await writeRangesFile(this.logger, this.fPath(key), ranges);\n }\n\n async delete(key: string) {\n await fs.rm(this.fPath(key));\n }\n}\n\n/** Extracted interface for storing sparse files. */\nexport interface SparseFileStorage {\n all(): Promise<string[]>;\n exists(key: string): Promise<boolean>;\n path(key: string): string;\n write(key: string, data: Uint8Array, from: number): Promise<void>;\n delete(key: string): Promise<void>;\n}\n\n/** Stores sparse files in a directory (the default implementation). */\nexport class SparseCacheFsFile implements SparseFileStorage {\n private readonly suffix = '.sparse.bin';\n\n constructor(\n private readonly logger: MiLogger,\n private readonly cacheDir: string,\n ) {}\n\n async all(): Promise<string[]> {\n await ensureDirExists(this.cacheDir);\n const files = await fs.readdir(this.cacheDir);\n return files.filter((f) => f.endsWith(this.suffix));\n }\n\n async exists(key: string): Promise<boolean> {\n return await fileExists(this.path(key));\n }\n\n path(key: string): string {\n return path.join(this.cacheDir, key + this.suffix);\n }\n\n async write(key: string, data: Uint8Array, from: number): Promise<void> {\n await ensureDirExists(this.cacheDir);\n await writeToSparseFile(this.logger, process.platform, this.path(key), data, from);\n }\n\n async delete(key: string): Promise<void> {\n await fs.rm(this.path(key));\n }\n}\n\n/** LRU cache for ranges of sparse files. */\nexport class SparseCache implements AsyncDisposable {\n /** Fields are public for tests. */\n\n /** The lock to make sure cache requests are done one by one. */\n private lock = new functions.AwaitLock()\n\n public keyToLastAccessTime = new Map<string, Date>();\n public size = 0;\n\n constructor(\n public readonly logger: MiLogger,\n /** The hard limit in bytes. */\n public readonly maxSize: number,\n public readonly ranges: SparseCacheRanges,\n public readonly storage: SparseFileStorage,\n ) {}\n\n /** Resets a cache's size by rereading everything we already store.\n * Safe for concurrent use. */\n async reset() {\n await withLock(this.lock, async () => {\n await this.resetUnsafe();\n })\n }\n\n /** Returns a path to the key if the range exists in a cache, otherwise returns undefined.\n * Safe for concurrent use. */\n async get(key: string, range: RangeBytes): Promise<string | undefined> {\n return await withLock(this.lock, async () => {\n return await this.getUnsafe(key, range);\n });\n }\n\n /** Sets data to the cache's file and clear the cache if it's needed.\n * Safe for concurrent use. */\n async set(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n await withLock(this.lock, async () => {\n await this.setUnsafe(key, range, data);\n })\n }\n\n private async resetUnsafe() {\n this.size = 0;\n this.keyToLastAccessTime = new Map<string, Date>();\n\n const now = new Date();\n // In rmKey method we first deletes the key from a storage and only then from ranges,\n // so if something goes wrong between 2 operations,\n // on reset the logic will be correct.\n for (const key of await this.storage.all()) {\n const ranges = await this.ranges.get(key);\n this.size += rangesSize(ranges);\n this.keyToLastAccessTime.set(key, now);\n }\n }\n\n private async getUnsafe(key: string, range: RangeBytes): Promise<string | undefined> {\n // It first checks the storage, and then the ranges.\n // In another method, when we remove a key, it first deletes a key from a storage and then from ranges,\n // so if we don't have a key in storage but have it in ranges, the logic here is correct.\n // We probably could reverse the operations here and there, and everywhere we work with both storage and ranges.\n if (await this.storage.exists(key)) {\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n if (doesRangeExist(ranges, range)) {\n return this.storage.path(key);\n }\n\n return undefined;\n }\n\n return undefined;\n }\n\n private async setUnsafe(key: string, range: { from: number; to: number; }, data: Uint8Array) {\n await this.setWithoutEviction(key, range, data);\n await this.ensureEvicted();\n }\n\n /** Sets a key and recalculates a size, but doesn't ensures that the size is less than the hard limit. */\n async setWithoutEviction(key: string, range: RangeBytes, data: Uint8Array): Promise<void> {\n if (range.to - range.from !== data.length) {\n throw new Error(\n `SparseCache.set: trying to set ${key} with wrong range length: `\n + `range: ${JSON.stringify(range)}, data: ${data.length}`\n );\n }\n\n this.keyToLastAccessTime.set(key, new Date());\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n\n await this.storage.write(key, data, range.from);\n\n const newRanges = addRange(ranges, range);\n this.size += rangesSize(newRanges);\n\n await this.ranges.set(key, newRanges);\n }\n\n /** Ensures the size is less than hard limit by deleting the oldest keys. */\n async ensureEvicted(): Promise<void> {\n const byTime = mapEntries(this.keyToLastAccessTime);\n byTime.sort(([_, aDate], [__, bDate]) => bDate.getTime() - aDate.getTime());\n\n while (this.size > this.maxSize) {\n const keyAndDate = byTime.pop(); // removes the oldest\n if (!keyAndDate) {\n break;\n }\n const [key, _] = keyAndDate;\n\n const ranges = await this.getRanges(key);\n this.size -= rangesSize(ranges);\n this.rmKey(key);\n }\n }\n\n /** Gets ranges and if they were corrupted, then remove the file from the cache and reset the cache's size. */\n private async getRanges(key: string) {\n try {\n return await this.ranges.get(key);\n } catch (e: unknown) {\n if (e instanceof CorruptedRangesError) {\n // We need to reset a state of the cache and update current size,\n // it's the only way to calculate the real size when one of the ranges were corrupted.\n await this.rmKey(key);\n await this.resetUnsafe();\n\n return await this.ranges.get(key);\n }\n\n throw e;\n }\n }\n\n /** Removes a key the state of the cache. The size should be updated. */\n private async rmKey(key: string) {\n await this.storage.delete(key);\n await this.ranges.delete(key);\n this.keyToLastAccessTime.delete(key);\n }\n\n async dispose(): Promise<void> {\n await this.lock.acquireAsync();\n }\n\n async [Symbol.asyncDispose](): Promise<void> {\n await this.dispose();\n }\n}\n\n/** Acquires the lock and executes a callback. */\nasync function withLock<T>(lock: functions.AwaitLock, cb: () => Promise<T>): Promise<T> {\n try {\n await lock.acquireAsync();\n return await cb();\n } finally {\n lock.release();\n }\n}\n"],"names":["path","fs"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-drivers",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.29",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=22"
|
|
6
6
|
},
|
|
@@ -32,12 +32,12 @@
|
|
|
32
32
|
"undici": "~7.16.0",
|
|
33
33
|
"upath": "^2.0.1",
|
|
34
34
|
"zod": "~3.23.8",
|
|
35
|
+
"@milaboratories/computable": "2.7.4",
|
|
36
|
+
"@milaboratories/helpers": "1.12.1",
|
|
35
37
|
"@milaboratories/ts-helpers": "1.5.4",
|
|
36
|
-
"@milaboratories/pl-tree": "1.8.
|
|
37
|
-
"@milaboratories/pl-client": "2.16.13",
|
|
38
|
+
"@milaboratories/pl-tree": "1.8.21",
|
|
38
39
|
"@milaboratories/pl-model-common": "1.21.10",
|
|
39
|
-
"@milaboratories/
|
|
40
|
-
"@milaboratories/computable": "2.7.4"
|
|
40
|
+
"@milaboratories/pl-client": "2.16.14"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/decompress": "^4.2.7",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"typescript": "~5.6.3",
|
|
50
50
|
"vitest": "^4.0.7",
|
|
51
51
|
"@milaboratories/eslint-config": "1.0.5",
|
|
52
|
+
"@milaboratories/ts-builder": "1.2.1",
|
|
52
53
|
"@milaboratories/ts-configs": "1.2.0",
|
|
53
|
-
"@milaboratories/
|
|
54
|
-
"@milaboratories/
|
|
55
|
-
"@milaboratories/test-helpers": "1.1.1"
|
|
54
|
+
"@milaboratories/build-configs": "1.2.1",
|
|
55
|
+
"@milaboratories/test-helpers": "1.1.2"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
58
|
"type-check": "ts-builder types --target node",
|
|
@@ -90,7 +90,7 @@ export type DownloadDriverOps = {
|
|
|
90
90
|
|
|
91
91
|
/** DownloadDriver holds a queue of downloading tasks,
|
|
92
92
|
* and notifies every watcher when a file were downloaded. */
|
|
93
|
-
export class DownloadDriver implements BlobDriver {
|
|
93
|
+
export class DownloadDriver implements BlobDriver, AsyncDisposable {
|
|
94
94
|
/** Represents a unique key to the path of a blob as a map. */
|
|
95
95
|
private keyToDownload: Map<string, DownloadBlobTask> = new Map();
|
|
96
96
|
|
|
@@ -643,6 +643,14 @@ export class DownloadDriver implements BlobDriver {
|
|
|
643
643
|
task.change.markChanged(`task ${resourceIdToString(task.rInfo.id)} released`);
|
|
644
644
|
});
|
|
645
645
|
}
|
|
646
|
+
|
|
647
|
+
async dispose(): Promise<void> {
|
|
648
|
+
await this.rangesCache.dispose();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
652
|
+
await this.dispose();
|
|
653
|
+
}
|
|
646
654
|
}
|
|
647
655
|
|
|
648
656
|
/** Keeps a counter to the on demand handle. */
|
|
@@ -85,7 +85,7 @@ export class SparseCacheFsFile implements SparseFileStorage {
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/** LRU cache for ranges of sparse files. */
|
|
88
|
-
export class SparseCache {
|
|
88
|
+
export class SparseCache implements AsyncDisposable {
|
|
89
89
|
/** Fields are public for tests. */
|
|
90
90
|
|
|
91
91
|
/** The lock to make sure cache requests are done one by one. */
|
|
@@ -229,6 +229,14 @@ export class SparseCache {
|
|
|
229
229
|
await this.ranges.delete(key);
|
|
230
230
|
this.keyToLastAccessTime.delete(key);
|
|
231
231
|
}
|
|
232
|
+
|
|
233
|
+
async dispose(): Promise<void> {
|
|
234
|
+
await this.lock.acquireAsync();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async [Symbol.asyncDispose](): Promise<void> {
|
|
238
|
+
await this.dispose();
|
|
239
|
+
}
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
/** Acquires the lock and executes a callback. */
|
|
@@ -65,7 +65,7 @@ test('should show a error when 404 status code', async () => {
|
|
|
65
65
|
expect(url2?.error).not.toBeUndefined();
|
|
66
66
|
expect(url2?.url).toBeUndefined();
|
|
67
67
|
});
|
|
68
|
-
},
|
|
68
|
+
}, 60000);
|
|
69
69
|
|
|
70
70
|
test('should abort a downloading process when we reset a state of a computable', async () => {
|
|
71
71
|
await TestHelpers.withTempRoot(async (client) => {
|