@miso.ai/server-commons 0.6.5-beta.1 → 0.6.5-beta.11
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 +1 -1
- package/src/file.js +22 -1
- package/src/sink/bps.js +9 -2
- package/src/store.js +9 -5
- package/src/stream/buffered-read.js +4 -2
- package/src/stream/buffered-write-state.js +8 -0
- package/src/stream/buffered-write.js +1 -0
- package/src/yargs.js +1 -1
package/package.json
CHANGED
package/src/file.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access } from 'fs/promises';
|
|
2
|
-
import { accessSync, constants } from 'fs';
|
|
2
|
+
import { accessSync, constants, createReadStream } from 'fs';
|
|
3
|
+
import { createInterface } from 'readline';
|
|
3
4
|
|
|
4
5
|
export async function fileExists(file, mode = constants.F_OK) {
|
|
5
6
|
try {
|
|
@@ -18,3 +19,23 @@ export function fileExistsSync(file, mode = constants.F_OK) {
|
|
|
18
19
|
return false;
|
|
19
20
|
}
|
|
20
21
|
}
|
|
22
|
+
|
|
23
|
+
export async function readFileAsLines(file) {
|
|
24
|
+
const fileStream = createReadStream(file, { encoding: 'utf8' });
|
|
25
|
+
const rl = createInterface({
|
|
26
|
+
input: fileStream,
|
|
27
|
+
crlfDelay: Infinity
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const lines = [];
|
|
31
|
+
for await (const line of rl) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
if (trimmed) {
|
|
34
|
+
lines.push(trimmed);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
rl.close();
|
|
39
|
+
|
|
40
|
+
return lines;
|
|
41
|
+
}
|
package/src/sink/bps.js
CHANGED
|
@@ -11,11 +11,13 @@ export default class BpsSink {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
_normalizeOptions({
|
|
14
|
+
writesPerSecond = 5,
|
|
14
15
|
recordsPerSecord = 100000,
|
|
15
|
-
bytesPerSecond =
|
|
16
|
+
bytesPerSecond = 100 * 1024 * 1024,
|
|
16
17
|
...options
|
|
17
18
|
} = {}) {
|
|
18
19
|
return {
|
|
20
|
+
writesPerSecond,
|
|
19
21
|
recordsPerSecord,
|
|
20
22
|
bytesPerSecond,
|
|
21
23
|
...options,
|
|
@@ -62,9 +64,10 @@ export default class BpsSink {
|
|
|
62
64
|
const elapsed = now - this._firstWriteAt;
|
|
63
65
|
const targetBps = this._targetBps(now);
|
|
64
66
|
const targetRps = this._targetRps(now);
|
|
67
|
+
const targetWps = this._targetWps(now);
|
|
65
68
|
|
|
66
69
|
const { started } = this._stats;
|
|
67
|
-
const shallElapsed = Math.max(started.records / targetRps, started.bytes / targetBps) * 1000;
|
|
70
|
+
const shallElapsed = Math.max(started.records / targetRps, started.bytes / targetBps, started.count / targetWps) * 1000;
|
|
68
71
|
|
|
69
72
|
const blockedTime = shallElapsed - elapsed;
|
|
70
73
|
if (blockedTime <= 1000) {
|
|
@@ -86,4 +89,8 @@ export default class BpsSink {
|
|
|
86
89
|
return this._options.recordsPerSecord;
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
_targetWps(timestamp) {
|
|
93
|
+
return this._options.writesPerSecord;
|
|
94
|
+
}
|
|
95
|
+
|
|
89
96
|
}
|
package/src/store.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'path';
|
|
2
2
|
import fs from 'fs/promises';
|
|
3
|
+
import { Transform } from 'stream';
|
|
4
|
+
import { readFileAsLines } from './file.js';
|
|
3
5
|
|
|
4
6
|
const DEFAULT_FLUSH_THRESHOLD = 100;
|
|
5
7
|
|
|
@@ -15,7 +17,7 @@ export default class HashStore {
|
|
|
15
17
|
this._file = file;
|
|
16
18
|
this._hashFn = hashFn;
|
|
17
19
|
this._flushThreshold = flushThreshold;
|
|
18
|
-
this._hashes =
|
|
20
|
+
this._hashes = new Set();
|
|
19
21
|
this._pending = [];
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -38,6 +40,8 @@ export default class HashStore {
|
|
|
38
40
|
|
|
39
41
|
async load() {
|
|
40
42
|
this._hashes = new Set(await this._read());
|
|
43
|
+
this._pending = [];
|
|
44
|
+
return this;
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
get() {
|
|
@@ -65,9 +69,10 @@ export default class HashStore {
|
|
|
65
69
|
if (this._pending.length === 0) {
|
|
66
70
|
return;
|
|
67
71
|
}
|
|
68
|
-
|
|
69
|
-
await fs.appendFile(this._file, this._pending.join('\n') + '\n');
|
|
72
|
+
const pending = this._pending;
|
|
70
73
|
this._pending = [];
|
|
74
|
+
await this._mkdir();
|
|
75
|
+
await fs.appendFile(this._file, pending.join('\n') + '\n');
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
exclusionStream() {
|
|
@@ -85,8 +90,7 @@ export default class HashStore {
|
|
|
85
90
|
|
|
86
91
|
async _read() {
|
|
87
92
|
try {
|
|
88
|
-
|
|
89
|
-
return content.split('\n').map(v => v.trim()).filter(v => v);
|
|
93
|
+
return await readFileAsLines(this._file);
|
|
90
94
|
} catch (err) {
|
|
91
95
|
if (err.code !== 'ENOENT') {
|
|
92
96
|
throw err;
|
|
@@ -85,6 +85,7 @@ export default class BufferedReadStream extends Readable {
|
|
|
85
85
|
const request = this._state.request(this._source.request());
|
|
86
86
|
|
|
87
87
|
this._debug(`[BufferedReadStream] Load request: ${request}`);
|
|
88
|
+
// TODO: racing here! we need to somehow keep the order of loads
|
|
88
89
|
const { data, ...info } = await this._source.get(request);
|
|
89
90
|
const response = new Response(request, info);
|
|
90
91
|
this._debug(`[BufferedReadStream] Load response: ${JSON.stringify(response)} => data = ${data && data.length}`);
|
|
@@ -192,12 +193,13 @@ class Strategy {
|
|
|
192
193
|
|
|
193
194
|
constructor({
|
|
194
195
|
highWatermark = 1000,
|
|
196
|
+
maxPendingLoads = 100,
|
|
195
197
|
eagerLoad = false,
|
|
196
198
|
initialize,
|
|
197
199
|
shallLoad,
|
|
198
200
|
terminate,
|
|
199
201
|
} = {}) {
|
|
200
|
-
this.options = Object.freeze({ highWatermark, eagerLoad });
|
|
202
|
+
this.options = Object.freeze({ highWatermark, maxPendingLoads, eagerLoad });
|
|
201
203
|
// overwrite methods
|
|
202
204
|
Object.assign(this, trimObj({ initialize, shallLoad, terminate }));
|
|
203
205
|
}
|
|
@@ -210,7 +212,7 @@ class Strategy {
|
|
|
210
212
|
|
|
211
213
|
shallLoad(state) {
|
|
212
214
|
// TODO: we can have a slower start
|
|
213
|
-
return state.watermark < this.options.highWatermark;
|
|
215
|
+
return state.pendingLoads < this.options.maxPendingLoads && state.watermark < this.options.highWatermark;
|
|
214
216
|
}
|
|
215
217
|
|
|
216
218
|
terminate(record, state) {
|
|
@@ -120,6 +120,14 @@ export default class State {
|
|
|
120
120
|
category.records += request.records;
|
|
121
121
|
category.bytes += request.bytes;
|
|
122
122
|
|
|
123
|
+
if (response.errors && response.recovered && response.recovered.records > 0) {
|
|
124
|
+
this._failed.records -= response.recovered.records;
|
|
125
|
+
this._failed.bytes -= response.recovered.bytes; // not so accurate, but close enough
|
|
126
|
+
this._successful.requests++;
|
|
127
|
+
this._successful.records += response.recovered.records;
|
|
128
|
+
this._successful.bytes += response.recovered.bytes;
|
|
129
|
+
}
|
|
130
|
+
|
|
123
131
|
this._time.addWrite(response.timestamp - request.timestamp);
|
|
124
132
|
|
|
125
133
|
this._resolutions.get(request).resolve();
|
|
@@ -183,6 +183,7 @@ export default class BufferedWriteStream extends Transform {
|
|
|
183
183
|
result: failed ? 'failed' : 'successful',
|
|
184
184
|
index: request.index,
|
|
185
185
|
records: request.records,
|
|
186
|
+
recovered: response.recovered || { records: 0, bytes: 0 },
|
|
186
187
|
bytes: request.bytes,
|
|
187
188
|
time: response.timestamp - request.timestamp,
|
|
188
189
|
});
|
package/src/yargs.js
CHANGED
|
@@ -73,6 +73,6 @@ export function handleFail(msg, err) {
|
|
|
73
73
|
|
|
74
74
|
export function coerceToArray(arg) {
|
|
75
75
|
return Array.isArray(arg) ? arg :
|
|
76
|
-
typeof arg === 'string' ? arg.split(',') :
|
|
76
|
+
typeof arg === 'string' ? arg.split(',').map(s => s.trim()) :
|
|
77
77
|
arg === undefined || arg === null ? [] : [arg];
|
|
78
78
|
}
|