@milaboratories/pl-drivers 1.3.7 → 1.3.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-drivers",
3
- "version": "1.3.7",
3
+ "version": "1.3.9",
4
4
  "description": "Drivers and a low-level clients for log streaming, downloading and uploading files from and to pl",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",
@@ -27,10 +27,10 @@
27
27
  "undici": "^6.20.1",
28
28
  "zod": "^3.23.8",
29
29
  "@milaboratories/ts-helpers": "^1.1.0",
30
- "@milaboratories/pl-tree": "^1.4.6",
30
+ "@milaboratories/computable": "^2.2.0",
31
+ "@milaboratories/pl-client": "^2.5.6",
31
32
  "@milaboratories/pl-model-common": "^1.5.2",
32
- "@milaboratories/pl-client": "^2.5.5",
33
- "@milaboratories/computable": "^2.2.0"
33
+ "@milaboratories/pl-tree": "^1.4.7"
34
34
  },
35
35
  "devDependencies": {
36
36
  "typescript": "~5.5.4",
@@ -22,6 +22,7 @@ import { DownloadDriver } from './download_and_logs_blob';
22
22
  import { createDownloadClient, createLogsClient } from '../clients/helpers';
23
23
  import { LogsStreamDriver } from './logs_stream';
24
24
  import { LogsDriver } from './logs';
25
+ import { test, expect } from '@jest/globals';
25
26
 
26
27
  test('should get all logs', async () => {
27
28
  await TestHelpers.withTempRoot(async (client) => {
@@ -31,7 +32,7 @@ test('should get all logs', async () => {
31
32
  stopPollingDelay: 10,
32
33
  pollingInterval: 10
33
34
  });
34
- const logsStream = new LogsStreamDriver(createLogsClient(client, logger));
35
+ const logsStream = new LogsStreamDriver(logger, createLogsClient(client, logger));
35
36
  const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test-logs-1-'));
36
37
  const download = new DownloadDriver(
37
38
  logger,
@@ -41,7 +42,7 @@ test('should get all logs', async () => {
41
42
  new HmacSha256Signer(HmacSha256Signer.generateSecret()),
42
43
  { cacheSoftSizeBytes: 700 * 1024, nConcurrentDownloads: 10 }
43
44
  );
44
- const logs = new LogsDriver(logsStream, download);
45
+ const logs = new LogsDriver(logger, logsStream, download);
45
46
 
46
47
  await createRunCommandWithStdoutStream(client, 'bash', ['-c', 'echo 1; sleep 1; echo 2']);
47
48
 
@@ -76,7 +77,7 @@ test('should get last line with a prefix', async () => {
76
77
  stopPollingDelay: 10,
77
78
  pollingInterval: 10
78
79
  });
79
- const logsStream = new LogsStreamDriver(createLogsClient(client, logger));
80
+ const logsStream = new LogsStreamDriver(logger, createLogsClient(client, logger));
80
81
  const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test-logs-2-'));
81
82
  const download = new DownloadDriver(
82
83
  logger,
@@ -86,7 +87,7 @@ test('should get last line with a prefix', async () => {
86
87
  new HmacSha256Signer(HmacSha256Signer.generateSecret()),
87
88
  { cacheSoftSizeBytes: 700 * 1024, nConcurrentDownloads: 10 }
88
89
  );
89
- const logs = new LogsDriver(logsStream, download);
90
+ const logs = new LogsDriver(logger, logsStream, download);
90
91
 
91
92
  const c = Computable.make((ctx) => {
92
93
  const streamManager = ctx.accessor(tree.entry()).node().traverse('result')?.persist();
@@ -126,7 +127,7 @@ test('should get log smart object and get log lines from that', async () => {
126
127
  stopPollingDelay: 10,
127
128
  pollingInterval: 10
128
129
  });
129
- const logsStream = new LogsStreamDriver(createLogsClient(client, logger));
130
+ const logsStream = new LogsStreamDriver(logger, createLogsClient(client, logger));
130
131
  const dir = await fsp.mkdtemp(path.join(os.tmpdir(), 'test-logs-3-'));
131
132
  const download = new DownloadDriver(
132
133
  logger,
@@ -136,7 +137,7 @@ test('should get log smart object and get log lines from that', async () => {
136
137
  new HmacSha256Signer(HmacSha256Signer.generateSecret()),
137
138
  { cacheSoftSizeBytes: 700 * 1024, nConcurrentDownloads: 10 }
138
139
  );
139
- const logs = new LogsDriver(logsStream, download);
140
+ const logs = new LogsDriver(logger, logsStream, download);
140
141
 
141
142
  const c = Computable.make((ctx) => {
142
143
  const streamManager = ctx.accessor(tree.entry()).node().traverse('result')?.persist();
@@ -1,12 +1,14 @@
1
1
  import { Computable, ComputableCtx } from '@milaboratories/computable';
2
2
  import { PlTreeEntry, ResourceInfo } from '@milaboratories/pl-tree';
3
- import { bigintToResourceId } from '@milaboratories/pl-client';
3
+ import { bigintToResourceId, stringifyWithResourceId } from '@milaboratories/pl-client';
4
4
  import { LogsStreamDriver } from './logs_stream';
5
5
  import { DownloadDriver } from './download_and_logs_blob';
6
6
  import * as sdk from '@milaboratories/pl-model-common';
7
+ import { ConsoleLoggerAdapter, MiLogger } from '@milaboratories/ts-helpers';
7
8
 
8
9
  export class LogsDriver implements sdk.LogsDriver {
9
10
  constructor(
11
+ private readonly logger: MiLogger,
10
12
  private readonly logsStreamDriver: LogsStreamDriver,
11
13
  private readonly downloadDriver: DownloadDriver
12
14
  ) {}
@@ -5,8 +5,14 @@ import {
5
5
  PollingComputableHooks,
6
6
  Watcher
7
7
  } from '@milaboratories/computable';
8
- import { ResourceId } from '@milaboratories/pl-client';
9
- import { asyncPool, CallersCounter } from '@milaboratories/ts-helpers';
8
+ import { ResourceId, stringifyWithResourceId } from '@milaboratories/pl-client';
9
+ import {
10
+ asyncPool,
11
+ CallersCounter,
12
+ ConsoleLoggerAdapter,
13
+ MiLogger,
14
+ ValueOrError
15
+ } from '@milaboratories/ts-helpers';
10
16
  import { ClientLogs } from '../clients/logs';
11
17
  import { randomUUID } from 'node:crypto';
12
18
  import { PlTreeEntry, ResourceInfo, treeEntryToResourceInfo } from '@milaboratories/pl-tree';
@@ -15,6 +21,7 @@ import { scheduler } from 'node:timers/promises';
15
21
  import { StreamingAPI_Response } from '../proto/github.com/milaboratory/pl/controllers/shared/grpc/streamingapi/protocol';
16
22
  import * as sdk from '@milaboratories/pl-model-common';
17
23
  import { PollingOps } from './helpers/polling_ops';
24
+ import { RpcError } from '@protobuf-ts/runtime-rpc';
18
25
 
19
26
  export type LogsStreamDriverOps = PollingOps & {
20
27
  /** Max number of concurrent requests to log streaming backend while calculating computable states */
@@ -32,6 +39,7 @@ export class LogsStreamDriver implements sdk.LogsDriver {
32
39
  private readonly hooks: PollingComputableHooks;
33
40
 
34
41
  constructor(
42
+ private readonly logger: MiLogger,
35
43
  private readonly clientLogs: ClientLogs,
36
44
  private readonly opts: LogsStreamDriverOps = {
37
45
  nConcurrentGetLogs: 10,
@@ -82,7 +90,7 @@ export class LogsStreamDriver implements sdk.LogsDriver {
82
90
  let logGetter = this.idToLastLines.get(rInfo.id);
83
91
 
84
92
  if (logGetter == undefined) {
85
- const newLogGetter = new LogGetter(this.clientLogs, rInfo, lines);
93
+ const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, lines);
86
94
  this.idToLastLines.set(rInfo.id, newLogGetter);
87
95
 
88
96
  logGetter = newLogGetter;
@@ -120,7 +128,6 @@ export class LogsStreamDriver implements sdk.LogsDriver {
120
128
  ctx.addOnDestroy(() => this.releaseProgressLog(r.id, callerId));
121
129
 
122
130
  const result = this.getProgressLogNoCtx(ctx.watcher, r, patternToSearch, callerId);
123
-
124
131
  ctx.markUnstable(
125
132
  'The progress log is from the stream, so we consider it unstable. Final value will be got from blobs.'
126
133
  );
@@ -137,7 +144,7 @@ export class LogsStreamDriver implements sdk.LogsDriver {
137
144
  let logGetter = this.idToProgressLog.get(rInfo.id);
138
145
 
139
146
  if (logGetter == undefined) {
140
- const newLogGetter = new LogGetter(this.clientLogs, rInfo, 1, patternToSearch);
147
+ const newLogGetter = new LogGetter(this.logger, this.clientLogs, rInfo, 1, patternToSearch);
141
148
  this.idToProgressLog.set(rInfo.id, newLogGetter);
142
149
 
143
150
  logGetter = newLogGetter;
@@ -264,9 +271,10 @@ export class LogsStreamDriver implements sdk.LogsDriver {
264
271
  this.scheduledOnNextState = [];
265
272
 
266
273
  try {
274
+ const logs = this.getAllLogs();
267
275
  await asyncPool(
268
276
  this.opts.nConcurrentGetLogs,
269
- this.getAllNotDoneLogs().map((getter) => async () => await getter.update())
277
+ logs.map((getter) => async () => await getter.update())
270
278
  );
271
279
 
272
280
  toNotify.forEach((n) => n.resolve());
@@ -282,10 +290,9 @@ export class LogsStreamDriver implements sdk.LogsDriver {
282
290
  this.currentLoop = undefined;
283
291
  }
284
292
 
285
- private getAllNotDoneLogs(): Array<LogGetter> {
293
+ private getAllLogs(): Array<LogGetter> {
286
294
  return Array.from(this.idToLastLines.entries())
287
295
  .concat(Array.from(this.idToProgressLog.entries()))
288
- .filter(([_, getter]) => !getter.getLog().done)
289
296
  .map(([_, getter]) => getter);
290
297
  }
291
298
  }
@@ -294,12 +301,12 @@ export class LogsStreamDriver implements sdk.LogsDriver {
294
301
  class LogGetter {
295
302
  private logs: string | undefined;
296
303
  private error: any | undefined = undefined;
297
- private done = false;
298
304
 
299
305
  private readonly change: ChangeSource = new ChangeSource();
300
306
  private readonly counter: CallersCounter = new CallersCounter();
301
307
 
302
308
  constructor(
309
+ private readonly logger: MiLogger,
303
310
  private readonly clientLogs: ClientLogs,
304
311
  private readonly rInfo: ResourceInfo,
305
312
  private readonly lines: number,
@@ -309,12 +316,10 @@ class LogGetter {
309
316
  getLog(): {
310
317
  log: string | undefined;
311
318
  error?: any | undefined;
312
- done: boolean;
313
319
  } {
314
320
  return {
315
321
  log: this.logs,
316
- error: this.error,
317
- done: this.done
322
+ error: this.error
318
323
  };
319
324
  }
320
325
 
@@ -340,18 +345,22 @@ class LogGetter {
340
345
 
341
346
  if (this.logs != newLogs) this.change.markChanged();
342
347
  this.logs = newLogs;
348
+ this.error = undefined;
343
349
 
344
350
  return;
345
351
  } catch (e: any) {
352
+ e as RpcError;
346
353
  if (e.name == 'RpcError' && e.code == 'NOT_FOUND') {
347
354
  // No resource
348
355
  this.logs = '';
349
356
  this.error = e;
350
- this.done = true;
351
357
  this.change.markChanged();
352
358
  return;
353
359
  }
354
360
 
361
+ this.logger.error(
362
+ `Stream log lines for ${stringifyWithResourceId(this.rInfo.id)} failed, reason: ${e}`
363
+ );
355
364
  throw e;
356
365
  }
357
366
  }
@@ -15,15 +15,9 @@ test('should ok when get all storages from ls driver', async () => {
15
15
  const logger = new ConsoleLoggerAdapter();
16
16
  await TestHelpers.withTempRoot(async (client) => {
17
17
  const lsClient = createLsFilesClient(client, logger);
18
- const driver = await LsDriver.init(
19
- logger,
20
- client,
21
- signer,
22
- [],
23
- () => {
24
- throw Error();
25
- }
26
- );
18
+ const driver = await LsDriver.init(logger, client, signer, [], () => {
19
+ throw Error();
20
+ });
27
21
 
28
22
  const got = await driver.getStorageList();
29
23
 
@@ -41,15 +35,9 @@ test('should ok when list files from remote storage in ls driver', async () => {
41
35
  const signer = new HmacSha256Signer('abc');
42
36
  const logger = new ConsoleLoggerAdapter();
43
37
  await TestHelpers.withTempRoot(async (client) => {
44
- const driver = await LsDriver.init(
45
- logger,
46
- client,
47
- signer,
48
- [],
49
- () => {
50
- throw Error();
51
- }
52
- );
38
+ const driver = await LsDriver.init(logger, client, signer, [], () => {
39
+ throw Error();
40
+ });
53
41
 
54
42
  const storages = await driver.getStorageList();
55
43
  const library = storages.find((se) => se.name == 'library')!.handle;
@@ -82,15 +70,9 @@ test('should ok when list files from local storage in ls driver', async () => {
82
70
  const signer = new HmacSha256Signer('abc');
83
71
  const logger = new ConsoleLoggerAdapter();
84
72
  await TestHelpers.withTempRoot(async (client) => {
85
- const driver = await LsDriver.init(
86
- logger,
87
- client,
88
- signer,
89
- [],
90
- () => {
91
- throw Error();
92
- }
93
- );
73
+ const driver = await LsDriver.init(logger, client, signer, [], () => {
74
+ throw Error();
75
+ });
94
76
 
95
77
  const storages = await driver.getStorageList();
96
78
  const local = storages.find((se) => se.name == 'local')!;
@@ -146,13 +128,9 @@ test('should ok when get file using local dialog, and read its content', async (
146
128
  const signer = new HmacSha256Signer('abc');
147
129
  const logger = new ConsoleLoggerAdapter();
148
130
  await TestHelpers.withTempRoot(async (client) => {
149
- const driver = await LsDriver.init(
150
- logger,
151
- client,
152
- signer,
153
- [],
154
- async () => [path.join(assetsPath, 'answer_to_the_ultimate_question.txt')]
155
- );
131
+ const driver = await LsDriver.init(logger, client, signer, [], async () => [
132
+ path.join(assetsPath, 'answer_to_the_ultimate_question.txt')
133
+ ]);
156
134
 
157
135
  const result = await driver.showOpenSingleFileDialog();
158
136
  expect(result.file).toBeDefined();
@@ -36,14 +36,16 @@ export async function DefaultVirtualLocalStorages(): Promise<VirtualLocalStorage
36
36
  name: `local_disk_${drive}`,
37
37
  root: `${drive}:\\`,
38
38
  initialPath: isHomeDrive ? home : `${drive}:\\`
39
- }
39
+ };
40
40
  });
41
41
  } catch (e: any) {
42
- return [{
43
- name: `local_disk_${homeDrive}`,
44
- root: `${homeDrive}:\\`,
45
- initialPath: home
46
- }]
42
+ return [
43
+ {
44
+ name: `local_disk_${homeDrive}`,
45
+ root: `${homeDrive}:\\`,
46
+ initialPath: home
47
+ }
48
+ ];
47
49
  }
48
50
  }
49
51
  }