@forzalabs/remora 1.2.9 → 1.2.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/CHANGELOG.md +7 -0
- package/index.js +517 -301
- package/json_schemas/consumer-schema.json +4 -0
- package/package.json +1 -1
- package/workers/ExecutorWorker.js +513 -294
|
@@ -10292,7 +10292,7 @@ var require_node2 = __commonJS({
|
|
|
10292
10292
|
var require_tail_file = __commonJS({
|
|
10293
10293
|
"../../packages/logger/node_modules/winston/lib/winston/tail-file.js"(exports2, module2) {
|
|
10294
10294
|
"use strict";
|
|
10295
|
-
var
|
|
10295
|
+
var fs18 = require("fs");
|
|
10296
10296
|
var { StringDecoder } = require("string_decoder");
|
|
10297
10297
|
var { Stream } = require_readable();
|
|
10298
10298
|
function noop() {
|
|
@@ -10313,7 +10313,7 @@ var require_tail_file = __commonJS({
|
|
|
10313
10313
|
stream.emit("end");
|
|
10314
10314
|
stream.emit("close");
|
|
10315
10315
|
};
|
|
10316
|
-
|
|
10316
|
+
fs18.open(options.file, "a+", "0644", (err2, fd) => {
|
|
10317
10317
|
if (err2) {
|
|
10318
10318
|
if (!iter) {
|
|
10319
10319
|
stream.emit("error", err2);
|
|
@@ -10325,10 +10325,10 @@ var require_tail_file = __commonJS({
|
|
|
10325
10325
|
}
|
|
10326
10326
|
(function read() {
|
|
10327
10327
|
if (stream.destroyed) {
|
|
10328
|
-
|
|
10328
|
+
fs18.close(fd, noop);
|
|
10329
10329
|
return;
|
|
10330
10330
|
}
|
|
10331
|
-
return
|
|
10331
|
+
return fs18.read(fd, buffer, 0, buffer.length, pos, (error, bytes) => {
|
|
10332
10332
|
if (error) {
|
|
10333
10333
|
if (!iter) {
|
|
10334
10334
|
stream.emit("error", error);
|
|
@@ -10387,7 +10387,7 @@ var require_tail_file = __commonJS({
|
|
|
10387
10387
|
var require_file = __commonJS({
|
|
10388
10388
|
"../../packages/logger/node_modules/winston/lib/winston/transports/file.js"(exports2, module2) {
|
|
10389
10389
|
"use strict";
|
|
10390
|
-
var
|
|
10390
|
+
var fs18 = require("fs");
|
|
10391
10391
|
var path18 = require("path");
|
|
10392
10392
|
var asyncSeries = require_series();
|
|
10393
10393
|
var zlib2 = require("zlib");
|
|
@@ -10592,7 +10592,7 @@ var require_file = __commonJS({
|
|
|
10592
10592
|
let buff = "";
|
|
10593
10593
|
let results = [];
|
|
10594
10594
|
let row = 0;
|
|
10595
|
-
const stream =
|
|
10595
|
+
const stream = fs18.createReadStream(file, {
|
|
10596
10596
|
encoding: "utf8"
|
|
10597
10597
|
});
|
|
10598
10598
|
stream.on("error", (err2) => {
|
|
@@ -10744,7 +10744,7 @@ var require_file = __commonJS({
|
|
|
10744
10744
|
stat(callback) {
|
|
10745
10745
|
const target = this._getFile();
|
|
10746
10746
|
const fullpath = path18.join(this.dirname, target);
|
|
10747
|
-
|
|
10747
|
+
fs18.stat(fullpath, (err2, stat) => {
|
|
10748
10748
|
if (err2 && err2.code === "ENOENT") {
|
|
10749
10749
|
debug("ENOENT\xA0ok", fullpath);
|
|
10750
10750
|
this.filename = target;
|
|
@@ -10849,7 +10849,7 @@ var require_file = __commonJS({
|
|
|
10849
10849
|
_createStream(source) {
|
|
10850
10850
|
const fullpath = path18.join(this.dirname, this.filename);
|
|
10851
10851
|
debug("create stream start", fullpath, this.options);
|
|
10852
|
-
const dest =
|
|
10852
|
+
const dest = fs18.createWriteStream(fullpath, this.options).on("error", (err2) => debug(err2)).on("close", () => debug("close", dest.path, dest.bytesWritten)).on("open", () => {
|
|
10853
10853
|
debug("file open ok", fullpath);
|
|
10854
10854
|
this.emit("open", fullpath);
|
|
10855
10855
|
source.pipe(dest);
|
|
@@ -10928,7 +10928,7 @@ var require_file = __commonJS({
|
|
|
10928
10928
|
const isZipped = this.zippedArchive ? ".gz" : "";
|
|
10929
10929
|
const filePath = `${basename}${isOldest}${ext}${isZipped}`;
|
|
10930
10930
|
const target = path18.join(this.dirname, filePath);
|
|
10931
|
-
|
|
10931
|
+
fs18.unlink(target, callback);
|
|
10932
10932
|
}
|
|
10933
10933
|
/**
|
|
10934
10934
|
* Roll files forward based on integer, up to maxFiles. e.g. if base if
|
|
@@ -10951,17 +10951,17 @@ var require_file = __commonJS({
|
|
|
10951
10951
|
tasks.push(function(i, cb) {
|
|
10952
10952
|
let fileName = `${basename}${i - 1}${ext}${isZipped}`;
|
|
10953
10953
|
const tmppath = path18.join(this.dirname, fileName);
|
|
10954
|
-
|
|
10954
|
+
fs18.exists(tmppath, (exists) => {
|
|
10955
10955
|
if (!exists) {
|
|
10956
10956
|
return cb(null);
|
|
10957
10957
|
}
|
|
10958
10958
|
fileName = `${basename}${i}${ext}${isZipped}`;
|
|
10959
|
-
|
|
10959
|
+
fs18.rename(tmppath, path18.join(this.dirname, fileName), cb);
|
|
10960
10960
|
});
|
|
10961
10961
|
}.bind(this, x));
|
|
10962
10962
|
}
|
|
10963
10963
|
asyncSeries(tasks, () => {
|
|
10964
|
-
|
|
10964
|
+
fs18.rename(
|
|
10965
10965
|
path18.join(this.dirname, `${basename}${ext}${isZipped}`),
|
|
10966
10966
|
path18.join(this.dirname, `${basename}1${ext}${isZipped}`),
|
|
10967
10967
|
callback
|
|
@@ -10977,22 +10977,22 @@ var require_file = __commonJS({
|
|
|
10977
10977
|
* @private
|
|
10978
10978
|
*/
|
|
10979
10979
|
_compressFile(src, dest, callback) {
|
|
10980
|
-
|
|
10980
|
+
fs18.access(src, fs18.F_OK, (err2) => {
|
|
10981
10981
|
if (err2) {
|
|
10982
10982
|
return callback();
|
|
10983
10983
|
}
|
|
10984
10984
|
var gzip = zlib2.createGzip();
|
|
10985
|
-
var inp =
|
|
10986
|
-
var out =
|
|
10985
|
+
var inp = fs18.createReadStream(src);
|
|
10986
|
+
var out = fs18.createWriteStream(dest);
|
|
10987
10987
|
out.on("finish", () => {
|
|
10988
|
-
|
|
10988
|
+
fs18.unlink(src, callback);
|
|
10989
10989
|
});
|
|
10990
10990
|
inp.pipe(gzip).pipe(out);
|
|
10991
10991
|
});
|
|
10992
10992
|
}
|
|
10993
10993
|
_createLogDirIfNotExist(dirPath) {
|
|
10994
|
-
if (!
|
|
10995
|
-
|
|
10994
|
+
if (!fs18.existsSync(dirPath)) {
|
|
10995
|
+
fs18.mkdirSync(dirPath, { recursive: true });
|
|
10996
10996
|
}
|
|
10997
10997
|
}
|
|
10998
10998
|
};
|
|
@@ -18738,25 +18738,6 @@ var ProcessENVManagerClass = class {
|
|
|
18738
18738
|
var ProcessENVManager = new ProcessENVManagerClass();
|
|
18739
18739
|
var ProcessENVManager_default = ProcessENVManager;
|
|
18740
18740
|
|
|
18741
|
-
// ../../packages/common/src/SecretManager.ts
|
|
18742
|
-
var SecretManagerClass = class {
|
|
18743
|
-
constructor() {
|
|
18744
|
-
/**
|
|
18745
|
-
* If the value is a secret (or .env setting), replace it with the value inside the .env configuration file
|
|
18746
|
-
* Starts with "{" and ends with "}".
|
|
18747
|
-
* e.g. {AWS_ID}
|
|
18748
|
-
*/
|
|
18749
|
-
this.replaceSecret = (value) => {
|
|
18750
|
-
if (!value || value.length <= 2 || !value.startsWith("{") || !value.endsWith("}"))
|
|
18751
|
-
return value;
|
|
18752
|
-
const parsedValue = value.slice(1, value.length - 1);
|
|
18753
|
-
return ProcessENVManager_default.getEnvVariable(parsedValue);
|
|
18754
|
-
};
|
|
18755
|
-
}
|
|
18756
|
-
};
|
|
18757
|
-
var SecretManager = new SecretManagerClass();
|
|
18758
|
-
var SecretManager_default = SecretManager;
|
|
18759
|
-
|
|
18760
18741
|
// ../../packages/common/src/ExecutorScope.ts
|
|
18761
18742
|
var import_path3 = __toESM(require("path"), 1);
|
|
18762
18743
|
var import_fs3 = __toESM(require("fs"), 1);
|
|
@@ -18764,7 +18745,7 @@ var import_promises = __toESM(require("fs/promises"), 1);
|
|
|
18764
18745
|
|
|
18765
18746
|
// ../../packages/constants/src/Constants.ts
|
|
18766
18747
|
var CONSTANTS = {
|
|
18767
|
-
cliVersion: "1.2.
|
|
18748
|
+
cliVersion: "1.2.11",
|
|
18768
18749
|
backendVersion: 1,
|
|
18769
18750
|
backendPort: 5088,
|
|
18770
18751
|
workerVersion: 2,
|
|
@@ -18810,10 +18791,10 @@ var ExecutorScopeClass = class {
|
|
|
18810
18791
|
constructor() {
|
|
18811
18792
|
this.WORKERS_FOLDER = "workers";
|
|
18812
18793
|
this.PRODUCERS_FOLDER = "producers";
|
|
18794
|
+
this.getBasePath = () => import_path3.default.join(Constants_default.defaults.REMORA_PATH, Constants_default.defaults.PRODUCER_TEMP_FOLDER);
|
|
18813
18795
|
this.getWorkerPath = (scope, workerId) => {
|
|
18814
18796
|
return import_path3.default.join(
|
|
18815
|
-
|
|
18816
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
18797
|
+
this.getBasePath(),
|
|
18817
18798
|
// A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
|
|
18818
18799
|
scope.folder,
|
|
18819
18800
|
this.WORKERS_FOLDER,
|
|
@@ -18822,8 +18803,7 @@ var ExecutorScopeClass = class {
|
|
|
18822
18803
|
};
|
|
18823
18804
|
this.getProducerPath = (scope, producer, sourceFileKey) => {
|
|
18824
18805
|
return import_path3.default.join(
|
|
18825
|
-
|
|
18826
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
18806
|
+
this.getBasePath(),
|
|
18827
18807
|
// A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
|
|
18828
18808
|
scope.folder,
|
|
18829
18809
|
this.PRODUCERS_FOLDER,
|
|
@@ -18833,22 +18813,30 @@ var ExecutorScopeClass = class {
|
|
|
18833
18813
|
};
|
|
18834
18814
|
this.getMainPath = (scope) => {
|
|
18835
18815
|
return import_path3.default.join(
|
|
18836
|
-
|
|
18837
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
18816
|
+
this.getBasePath(),
|
|
18838
18817
|
scope.folder,
|
|
18839
18818
|
"main.dataset"
|
|
18840
18819
|
);
|
|
18841
18820
|
};
|
|
18842
18821
|
this.clearScope = async (scope) => {
|
|
18843
18822
|
const scopePath = import_path3.default.join(
|
|
18844
|
-
|
|
18845
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
18823
|
+
this.getBasePath(),
|
|
18846
18824
|
scope.folder
|
|
18847
18825
|
);
|
|
18848
18826
|
if (import_fs3.default.existsSync(scopePath)) {
|
|
18849
18827
|
await import_promises.default.rm(scopePath, { recursive: true, force: true });
|
|
18850
18828
|
}
|
|
18851
18829
|
};
|
|
18830
|
+
this.deepClear = () => {
|
|
18831
|
+
const basePath = this.getBasePath();
|
|
18832
|
+
const openScopes = this.getOpenScopes();
|
|
18833
|
+
for (const scopeFolder of openScopes) {
|
|
18834
|
+
const scopePath = import_path3.default.join(basePath, scopeFolder);
|
|
18835
|
+
if (import_fs3.default.existsSync(scopePath)) {
|
|
18836
|
+
import_fs3.default.rmSync(scopePath, { recursive: true, force: true });
|
|
18837
|
+
}
|
|
18838
|
+
}
|
|
18839
|
+
};
|
|
18852
18840
|
this.ensurePath = (fileUri) => {
|
|
18853
18841
|
const dir = import_path3.default.dirname(fileUri);
|
|
18854
18842
|
if (!import_fs3.default.existsSync(dir))
|
|
@@ -18856,11 +18844,105 @@ var ExecutorScopeClass = class {
|
|
|
18856
18844
|
if (!import_fs3.default.existsSync(fileUri))
|
|
18857
18845
|
import_fs3.default.writeFileSync(fileUri, "");
|
|
18858
18846
|
};
|
|
18847
|
+
this.getOpenScopes = () => {
|
|
18848
|
+
const basePath = this.getBasePath();
|
|
18849
|
+
if (!import_fs3.default.existsSync(basePath))
|
|
18850
|
+
return [];
|
|
18851
|
+
return import_fs3.default.readdirSync(basePath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).filter((folder) => folder !== "logs" && folder !== "usage");
|
|
18852
|
+
};
|
|
18859
18853
|
}
|
|
18860
18854
|
};
|
|
18861
18855
|
var ExecutorScope = new ExecutorScopeClass();
|
|
18862
18856
|
var ExecutorScope_default = ExecutorScope;
|
|
18863
18857
|
|
|
18858
|
+
// ../../packages/common/src/ProcessShutdownManager.ts
|
|
18859
|
+
var ProcessShutdownManagerClass = class {
|
|
18860
|
+
constructor() {
|
|
18861
|
+
this._initialized = false;
|
|
18862
|
+
this._cleaned = false;
|
|
18863
|
+
this._runtimeName = "Remora process";
|
|
18864
|
+
this.init = (runtimeName) => {
|
|
18865
|
+
if (this._initialized)
|
|
18866
|
+
return;
|
|
18867
|
+
this._initialized = true;
|
|
18868
|
+
if (runtimeName)
|
|
18869
|
+
this._runtimeName = runtimeName;
|
|
18870
|
+
process.once("SIGINT", () => this.handleSignal("SIGINT", 130));
|
|
18871
|
+
process.once("SIGTERM", () => this.handleSignal("SIGTERM", 143));
|
|
18872
|
+
process.once("uncaughtException", (error) => this.handleUnexpectedShutdown("uncaughtException", error));
|
|
18873
|
+
process.once("unhandledRejection", (reason) => this.handleUnexpectedShutdown("unhandledRejection", reason));
|
|
18874
|
+
process.once("beforeExit", (code) => this.handleBeforeExit(code));
|
|
18875
|
+
process.once("exit", (code) => this.handleExit(code));
|
|
18876
|
+
};
|
|
18877
|
+
this.handleSignal = (signal, exitCode) => {
|
|
18878
|
+
this.setShutdownState("intentional", signal);
|
|
18879
|
+
Logger_default.warn(`Received ${signal}. Shutting down ${this._runtimeName}.`);
|
|
18880
|
+
process.exit(exitCode);
|
|
18881
|
+
};
|
|
18882
|
+
this.handleUnexpectedShutdown = (reason, error) => {
|
|
18883
|
+
this.setShutdownState("unintentional", reason);
|
|
18884
|
+
Logger_default.error(this.asError(reason, error));
|
|
18885
|
+
process.exit(1);
|
|
18886
|
+
};
|
|
18887
|
+
this.handleBeforeExit = (code) => {
|
|
18888
|
+
this.setShutdownState(code === 0 ? "intentional" : "unintentional", `beforeExit:${code}`);
|
|
18889
|
+
};
|
|
18890
|
+
this.handleExit = (code) => {
|
|
18891
|
+
if (!this._shutdownState)
|
|
18892
|
+
this.setShutdownState(code === 0 ? "intentional" : "unintentional", `exit:${code}`);
|
|
18893
|
+
this.cleanupOpenScopes(code);
|
|
18894
|
+
};
|
|
18895
|
+
this.cleanupOpenScopes = (code) => {
|
|
18896
|
+
if (this._cleaned)
|
|
18897
|
+
return;
|
|
18898
|
+
this._cleaned = true;
|
|
18899
|
+
const openScopes = ExecutorScope_default.getOpenScopes();
|
|
18900
|
+
const scopeCount = openScopes.length;
|
|
18901
|
+
const shutdownState = this._shutdownState;
|
|
18902
|
+
const shutdownDescription = `${shutdownState?.type ?? "intentional"} shutdown (${shutdownState?.reason ?? `exit:${code}`})`;
|
|
18903
|
+
if (scopeCount === 0) {
|
|
18904
|
+
Logger_default.info(`Detected ${shutdownDescription} for ${this._runtimeName}. No executor scopes to clean up.`);
|
|
18905
|
+
return;
|
|
18906
|
+
}
|
|
18907
|
+
Logger_default.warn(`Detected ${shutdownDescription} for ${this._runtimeName}. Cleaning up ${scopeCount} executor scope${scopeCount === 1 ? "" : "s"}.`);
|
|
18908
|
+
ExecutorScope_default.deepClear();
|
|
18909
|
+
};
|
|
18910
|
+
this.setShutdownState = (type, reason) => {
|
|
18911
|
+
if (this._shutdownState)
|
|
18912
|
+
return;
|
|
18913
|
+
this._shutdownState = { type, reason };
|
|
18914
|
+
};
|
|
18915
|
+
this.asError = (reason, error) => {
|
|
18916
|
+
if (error instanceof Error) {
|
|
18917
|
+
const contextualError = new Error(`Unexpected ${reason} during ${this._runtimeName} execution: ${error.message}`);
|
|
18918
|
+
contextualError.stack = error.stack;
|
|
18919
|
+
return contextualError;
|
|
18920
|
+
}
|
|
18921
|
+
return new Error(`Unexpected ${reason} during ${this._runtimeName} execution: ${String(error)}`);
|
|
18922
|
+
};
|
|
18923
|
+
}
|
|
18924
|
+
};
|
|
18925
|
+
var ProcessShutdownManager = new ProcessShutdownManagerClass();
|
|
18926
|
+
|
|
18927
|
+
// ../../packages/common/src/SecretManager.ts
|
|
18928
|
+
var SecretManagerClass = class {
|
|
18929
|
+
constructor() {
|
|
18930
|
+
/**
|
|
18931
|
+
* If the value is a secret (or .env setting), replace it with the value inside the .env configuration file
|
|
18932
|
+
* Starts with "{" and ends with "}".
|
|
18933
|
+
* e.g. {AWS_ID}
|
|
18934
|
+
*/
|
|
18935
|
+
this.replaceSecret = (value) => {
|
|
18936
|
+
if (!value || value.length <= 2 || !value.startsWith("{") || !value.endsWith("}"))
|
|
18937
|
+
return value;
|
|
18938
|
+
const parsedValue = value.slice(1, value.length - 1);
|
|
18939
|
+
return ProcessENVManager_default.getEnvVariable(parsedValue);
|
|
18940
|
+
};
|
|
18941
|
+
}
|
|
18942
|
+
};
|
|
18943
|
+
var SecretManager = new SecretManagerClass();
|
|
18944
|
+
var SecretManager_default = SecretManager;
|
|
18945
|
+
|
|
18864
18946
|
// ../../packages/common/src/Environment.ts
|
|
18865
18947
|
var import_fs5 = __toESM(require("fs"), 1);
|
|
18866
18948
|
var import_crypto = __toESM(require("crypto"), 1);
|
|
@@ -19102,7 +19184,16 @@ var ValidatorClass = class {
|
|
|
19102
19184
|
}
|
|
19103
19185
|
const allAvailableFields = [...availableFieldsByProducer.values()].flat();
|
|
19104
19186
|
for (const field of consumer.fields) {
|
|
19105
|
-
|
|
19187
|
+
const syntheticFieldModes = [field.fixed === true, !!field.copyFrom, field.lineCount === true].filter(Boolean);
|
|
19188
|
+
if (syntheticFieldModes.length > 1)
|
|
19189
|
+
errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" can only use one synthetic field mode among "fixed", "copyFrom", and "lineCount".`);
|
|
19190
|
+
if (field.lineCount && field.key === "*")
|
|
19191
|
+
errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "lineCount" with the wildcard key "*".`);
|
|
19192
|
+
if (field.lineCount && field.transform)
|
|
19193
|
+
errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "transform" together with "lineCount".`);
|
|
19194
|
+
if (field.lineCount && field.validate && field.validate.length > 0)
|
|
19195
|
+
errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use "validate" together with "lineCount".`);
|
|
19196
|
+
if (field.key === "*" || field.fixed || field.copyFrom || field.lineCount) continue;
|
|
19106
19197
|
if (field.from) {
|
|
19107
19198
|
const producerFields = availableFieldsByProducer.get(field.from);
|
|
19108
19199
|
if (producerFields && !producerFields.includes(field.key))
|
|
@@ -19123,6 +19214,8 @@ var ValidatorClass = class {
|
|
|
19123
19214
|
const found = precedingFields.find((f) => (f.alias ?? f.key) === field.copyFrom);
|
|
19124
19215
|
if (!found)
|
|
19125
19216
|
errors.push(`Field "${field.alias ?? field.key}" uses copyFrom "${field.copyFrom}" but no field with that name/alias exists before it in consumer "${consumer.name}".`);
|
|
19217
|
+
else if (found.lineCount)
|
|
19218
|
+
errors.push(`Field "${field.alias ?? field.key}" in consumer "${consumer.name}" cannot use copyFrom on a field with "lineCount" because line counts are normalized after worker merge.`);
|
|
19126
19219
|
}
|
|
19127
19220
|
if (consumer.filters && consumer.filters.length > 0) {
|
|
19128
19221
|
if (consumer.filters.some((x) => x.sql && x.rule))
|
|
@@ -19143,10 +19236,14 @@ var ValidatorClass = class {
|
|
|
19143
19236
|
const missingRules = rulesWithMatchingFields.filter((x) => !x.match);
|
|
19144
19237
|
errors.push(`Filter(s) on member(s) "${missingRules.map((x) => x.rule.rule.member).join(", ")}" is invalid since the member specified is not present in the consumer. Check the member value or add the missing field to the consumer.`);
|
|
19145
19238
|
}
|
|
19239
|
+
const lineCountRuleMembers = rulesWithMatchingFields.filter((x) => x.match?.lineCount);
|
|
19240
|
+
if (lineCountRuleMembers.length > 0)
|
|
19241
|
+
errors.push(`Consumer "${consumer.name}" cannot use rule-based filters on field(s) with "lineCount": ${lineCountRuleMembers.map((x) => `"${x.rule.rule.member}"`).join(", ")}.`);
|
|
19146
19242
|
}
|
|
19147
19243
|
const validateTransformations = (fields) => {
|
|
19148
19244
|
const errors2 = [];
|
|
19149
19245
|
const trxsFields = fields.filter((x) => x.transform);
|
|
19246
|
+
const lineCountFieldNames = new Set(fields.filter((x) => x.lineCount).map((x) => x.alias ?? x.key));
|
|
19150
19247
|
for (const field of trxsFields) {
|
|
19151
19248
|
const trxToValidate = [];
|
|
19152
19249
|
if (Array.isArray(field.transform))
|
|
@@ -19154,6 +19251,16 @@ var ValidatorClass = class {
|
|
|
19154
19251
|
else
|
|
19155
19252
|
trxToValidate.push(field.transform);
|
|
19156
19253
|
for (const trans of trxToValidate) {
|
|
19254
|
+
if ("multiplyBy" in trans) {
|
|
19255
|
+
const invalidFields = trans.multiplyBy.fields.filter((x) => lineCountFieldNames.has(x));
|
|
19256
|
+
if (invalidFields.length > 0)
|
|
19257
|
+
errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in multiplyBy: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
|
|
19258
|
+
}
|
|
19259
|
+
if ("addBy" in trans) {
|
|
19260
|
+
const invalidFields = trans.addBy.fields.filter((x) => lineCountFieldNames.has(x));
|
|
19261
|
+
if (invalidFields.length > 0)
|
|
19262
|
+
errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in addBy: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
|
|
19263
|
+
}
|
|
19157
19264
|
if ("combine_fields" in trans) {
|
|
19158
19265
|
const { combine_fields } = trans;
|
|
19159
19266
|
if (!combine_fields.fields || combine_fields.fields.length === 0)
|
|
@@ -19161,6 +19268,9 @@ var ValidatorClass = class {
|
|
|
19161
19268
|
const missingFieldsInConsumer = combine_fields.fields.map((x) => ({ field: x, found: fields.find((k) => (k.alias ?? k.key) === x) })).filter((x) => !x.found);
|
|
19162
19269
|
if (missingFieldsInConsumer.length > 0)
|
|
19163
19270
|
errors2.push(`The requested field(s) for a transformation is missing in the consumer -> missing field(s): "${missingFieldsInConsumer.map((x) => x.field).join(", ")}"; field transformation: "${field.alias ?? field.key}";`);
|
|
19271
|
+
const invalidFields = combine_fields.fields.filter((x) => lineCountFieldNames.has(x));
|
|
19272
|
+
if (invalidFields.length > 0)
|
|
19273
|
+
errors2.push(`Transformation on field "${field.alias ?? field.key}" cannot reference lineCount field(s) in combine_fields: ${invalidFields.map((x) => `"${x}"`).join(", ")}.`);
|
|
19164
19274
|
}
|
|
19165
19275
|
}
|
|
19166
19276
|
}
|
|
@@ -19183,7 +19293,7 @@ var ValidatorClass = class {
|
|
|
19183
19293
|
errors.push(`The export destination "${output.exportDestination}" was not found in the sources.`);
|
|
19184
19294
|
}
|
|
19185
19295
|
}
|
|
19186
|
-
const dimensionFields = consumer.fields.filter((x) => x.key !== "*" && !x.fixed && !x.copyFrom);
|
|
19296
|
+
const dimensionFields = consumer.fields.filter((x) => x.key !== "*" && !x.fixed && !x.copyFrom && !x.lineCount);
|
|
19187
19297
|
const dimensionKeys = dimensionFields.map((x) => `${x.from ?? "_default_"}::${x.key}`);
|
|
19188
19298
|
const duplicateDimensionKeys = dimensionKeys.filter((k, i) => dimensionKeys.indexOf(k) !== i);
|
|
19189
19299
|
if (duplicateDimensionKeys.length > 0) {
|
|
@@ -19621,10 +19731,10 @@ var Environment = new EnvironmentClass();
|
|
|
19621
19731
|
var Environment_default = Environment;
|
|
19622
19732
|
|
|
19623
19733
|
// ../../packages/executors/src/ConsumerExecutor.ts
|
|
19624
|
-
var
|
|
19625
|
-
var
|
|
19734
|
+
var import_path15 = __toESM(require("path"));
|
|
19735
|
+
var import_fs10 = __toESM(require("fs"));
|
|
19626
19736
|
var import_readline6 = __toESM(require("readline"));
|
|
19627
|
-
var
|
|
19737
|
+
var import_promises8 = __toESM(require("fs/promises"));
|
|
19628
19738
|
var import_crypto4 = __toESM(require("crypto"));
|
|
19629
19739
|
|
|
19630
19740
|
// ../../packages/engines/src/CryptoEngine.ts
|
|
@@ -20858,6 +20968,19 @@ var Helper = {
|
|
|
20858
20968
|
};
|
|
20859
20969
|
var Helper_default = Helper;
|
|
20860
20970
|
|
|
20971
|
+
// ../../packages/helper/src/Formatter.ts
|
|
20972
|
+
var Formatter = {
|
|
20973
|
+
bytes: (bytes, decimals = 2) => {
|
|
20974
|
+
if (!+bytes) return "0 Bytes";
|
|
20975
|
+
const k = 1024;
|
|
20976
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
20977
|
+
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
20978
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
20979
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
20980
|
+
}
|
|
20981
|
+
};
|
|
20982
|
+
var Formatter_default = Formatter;
|
|
20983
|
+
|
|
20861
20984
|
// ../../packages/helper/src/Settings.ts
|
|
20862
20985
|
var SETTINGS = {
|
|
20863
20986
|
db: {
|
|
@@ -22688,6 +22811,25 @@ var ConsumerManagerClass = class {
|
|
|
22688
22811
|
const expandedFields = convertedFields.flatMap((x) => this.expandField(consumer, x, availableColumns));
|
|
22689
22812
|
return expandedFields;
|
|
22690
22813
|
};
|
|
22814
|
+
this.getLineCountFields = (consumer, visibleOnly = false) => {
|
|
22815
|
+
Affirm_default(consumer, "Invalid consumer");
|
|
22816
|
+
return this.getLineCountFieldsFromExpanded(this.getExpandedFields(consumer), visibleOnly);
|
|
22817
|
+
};
|
|
22818
|
+
this.getLineCountFieldsFromExpanded = (fields, visibleOnly = false) => {
|
|
22819
|
+
Affirm_default(fields, "Invalid consumer fields");
|
|
22820
|
+
return fields.filter((field) => this.isLineCountField(field.cField) && (!visibleOnly || !field.cField.hidden));
|
|
22821
|
+
};
|
|
22822
|
+
this.hasLineCountFields = (consumer, visibleOnly = false) => {
|
|
22823
|
+
return this.getLineCountFields(consumer, visibleOnly).length > 0;
|
|
22824
|
+
};
|
|
22825
|
+
this.isSyntheticField = (field) => {
|
|
22826
|
+
Affirm_default(field, "Invalid field");
|
|
22827
|
+
return field.fixed === true || !!field.copyFrom || this.isLineCountField(field);
|
|
22828
|
+
};
|
|
22829
|
+
this.isLineCountField = (field) => {
|
|
22830
|
+
Affirm_default(field, "Invalid field");
|
|
22831
|
+
return field.lineCount === true;
|
|
22832
|
+
};
|
|
22691
22833
|
this.getAvailableColumns = (consumer) => {
|
|
22692
22834
|
return ResourcesUtils_default.getAvailableColumns(consumer);
|
|
22693
22835
|
};
|
|
@@ -22740,7 +22882,7 @@ var ConsumerManagerClass = class {
|
|
|
22740
22882
|
column = columns.find((x) => x.owner === field.from && x.nameInProducer === field.key);
|
|
22741
22883
|
} else if (consumer.producers.length === 1 && !field.from) {
|
|
22742
22884
|
column = columns.find((x) => x.nameInProducer === field.key);
|
|
22743
|
-
} else if (!
|
|
22885
|
+
} else if (!this.isSyntheticField(field)) {
|
|
22744
22886
|
const matches = columns.filter((x) => x.nameInProducer === field.key);
|
|
22745
22887
|
Affirm_default(matches.length > 0, `Consumer "${consumer.name}" misconfiguration: the field "${field.key}" is not found in any of the included producers (${consumer.producers.map((x) => x.name).join(", ")})`);
|
|
22746
22888
|
if (matches.length === 1) {
|
|
@@ -22751,14 +22893,14 @@ var ConsumerManagerClass = class {
|
|
|
22751
22893
|
column = matches[0];
|
|
22752
22894
|
}
|
|
22753
22895
|
if (!column) {
|
|
22754
|
-
if (field.fixed === true && Algo_default.hasVal(field.default) || field
|
|
22896
|
+
if (field.fixed === true && Algo_default.hasVal(field.default) || this.isSyntheticField(field)) {
|
|
22755
22897
|
column = {
|
|
22756
22898
|
aliasInProducer: field.key,
|
|
22757
22899
|
nameInProducer: field.alias ?? field.key,
|
|
22758
22900
|
consumerAlias: field.alias ?? field.key,
|
|
22759
22901
|
consumerKey: field.key,
|
|
22760
22902
|
owner: field.from,
|
|
22761
|
-
dimension: { name: field.key, type: typeof field.default }
|
|
22903
|
+
dimension: { name: field.key, type: this.isLineCountField(field) ? "number" : typeof field.default }
|
|
22762
22904
|
};
|
|
22763
22905
|
}
|
|
22764
22906
|
}
|
|
@@ -24197,69 +24339,8 @@ var UsageManager = new UsageManagerClass();
|
|
|
24197
24339
|
var UsageManager_default = UsageManager;
|
|
24198
24340
|
|
|
24199
24341
|
// ../../packages/executors/src/OutputExecutor.ts
|
|
24200
|
-
var
|
|
24201
|
-
|
|
24202
|
-
// ../../packages/executors/src/ExecutorScope.ts
|
|
24342
|
+
var fs13 = __toESM(require("fs"));
|
|
24203
24343
|
var import_path14 = __toESM(require("path"));
|
|
24204
|
-
var import_fs10 = __toESM(require("fs"));
|
|
24205
|
-
var import_promises8 = __toESM(require("fs/promises"));
|
|
24206
|
-
var ExecutorScopeClass2 = class {
|
|
24207
|
-
constructor() {
|
|
24208
|
-
this.WORKERS_FOLDER = "workers";
|
|
24209
|
-
this.PRODUCERS_FOLDER = "producers";
|
|
24210
|
-
this.getWorkerPath = (scope, workerId) => {
|
|
24211
|
-
return import_path14.default.join(
|
|
24212
|
-
Constants_default.defaults.REMORA_PATH,
|
|
24213
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
24214
|
-
// A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
|
|
24215
|
-
scope.folder,
|
|
24216
|
-
this.WORKERS_FOLDER,
|
|
24217
|
-
`${workerId}.dataset`
|
|
24218
|
-
);
|
|
24219
|
-
};
|
|
24220
|
-
this.getProducerPath = (scope, producer, sourceFileKey) => {
|
|
24221
|
-
return import_path14.default.join(
|
|
24222
|
-
Constants_default.defaults.REMORA_PATH,
|
|
24223
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
24224
|
-
// A specific execution sits entirely in this folder, so at the end it's safe to delete it entirely
|
|
24225
|
-
scope.folder,
|
|
24226
|
-
this.PRODUCERS_FOLDER,
|
|
24227
|
-
producer.name,
|
|
24228
|
-
`${sourceFileKey}.dataset`
|
|
24229
|
-
);
|
|
24230
|
-
};
|
|
24231
|
-
this.getMainPath = (scope) => {
|
|
24232
|
-
return import_path14.default.join(
|
|
24233
|
-
Constants_default.defaults.REMORA_PATH,
|
|
24234
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
24235
|
-
scope.folder,
|
|
24236
|
-
"main.dataset"
|
|
24237
|
-
);
|
|
24238
|
-
};
|
|
24239
|
-
this.clearScope = async (scope) => {
|
|
24240
|
-
const scopePath = import_path14.default.join(
|
|
24241
|
-
Constants_default.defaults.REMORA_PATH,
|
|
24242
|
-
Constants_default.defaults.PRODUCER_TEMP_FOLDER,
|
|
24243
|
-
scope.folder
|
|
24244
|
-
);
|
|
24245
|
-
if (import_fs10.default.existsSync(scopePath)) {
|
|
24246
|
-
await import_promises8.default.rm(scopePath, { recursive: true, force: true });
|
|
24247
|
-
}
|
|
24248
|
-
};
|
|
24249
|
-
this.ensurePath = (fileUri) => {
|
|
24250
|
-
const dir = import_path14.default.dirname(fileUri);
|
|
24251
|
-
if (!import_fs10.default.existsSync(dir))
|
|
24252
|
-
import_fs10.default.mkdirSync(dir, { recursive: true });
|
|
24253
|
-
if (!import_fs10.default.existsSync(fileUri))
|
|
24254
|
-
import_fs10.default.writeFileSync(fileUri, "");
|
|
24255
|
-
};
|
|
24256
|
-
}
|
|
24257
|
-
};
|
|
24258
|
-
var ExecutorScope2 = new ExecutorScopeClass2();
|
|
24259
|
-
var ExecutorScope_default2 = ExecutorScope2;
|
|
24260
|
-
|
|
24261
|
-
// ../../packages/executors/src/OutputExecutor.ts
|
|
24262
|
-
var import_path15 = __toESM(require("path"));
|
|
24263
24344
|
var OutputExecutorClass = class {
|
|
24264
24345
|
constructor() {
|
|
24265
24346
|
this._getInternalRecordFormat = (consumer) => {
|
|
@@ -24303,13 +24384,13 @@ var OutputExecutorClass = class {
|
|
|
24303
24384
|
for (const output of consumer.outputs) {
|
|
24304
24385
|
const destination = Environment_default.getSource(output.exportDestination);
|
|
24305
24386
|
const driver = await DriverFactory_default.instantiateDestination(destination);
|
|
24306
|
-
const currentPath =
|
|
24387
|
+
const currentPath = import_path14.default.dirname(ExecutorScope_default.getMainPath(scope));
|
|
24307
24388
|
const destinationName = this._composeFileName(consumer, output, this._getExtension(output));
|
|
24308
24389
|
Logger_default.log(`Exporting consumer "${consumer.name}" to "${output.exportDestination}" as ${output.format} (${destinationName})`);
|
|
24309
|
-
const filenameArray =
|
|
24390
|
+
const filenameArray = fs13.readdirSync(currentPath).filter((filename) => filename.includes(".dataset"));
|
|
24310
24391
|
for (const filename in filenameArray) {
|
|
24311
24392
|
const destinationPath = this.getCompletedPath(destinationName, filename);
|
|
24312
|
-
const startingPath =
|
|
24393
|
+
const startingPath = import_path14.default.join(currentPath, filenameArray[filename]);
|
|
24313
24394
|
if (output.format === internalFormat) {
|
|
24314
24395
|
results.push(await driver.move(startingPath, destinationPath));
|
|
24315
24396
|
} else {
|
|
@@ -24369,47 +24450,54 @@ var OutputExecutor_default = OutputExecutor;
|
|
|
24369
24450
|
var ConsumerExecutorClass = class {
|
|
24370
24451
|
constructor() {
|
|
24371
24452
|
this._getWorkPath = (consumer, executionId) => {
|
|
24372
|
-
const execFolder =
|
|
24373
|
-
const workPath =
|
|
24453
|
+
const execFolder = import_path15.default.join(consumer.name, executionId);
|
|
24454
|
+
const workPath = import_path15.default.join("./remora", Constants_default.defaults.PRODUCER_TEMP_FOLDER, execFolder, ".dataset");
|
|
24374
24455
|
return workPath;
|
|
24375
24456
|
};
|
|
24376
24457
|
this._clearWorkPath = async (workPath) => {
|
|
24377
24458
|
try {
|
|
24378
|
-
if (
|
|
24379
|
-
await
|
|
24459
|
+
if (import_fs10.default.existsSync(workPath)) {
|
|
24460
|
+
await import_promises8.default.unlink(workPath);
|
|
24380
24461
|
}
|
|
24381
24462
|
} catch (error) {
|
|
24382
24463
|
}
|
|
24383
24464
|
try {
|
|
24384
|
-
const dir =
|
|
24385
|
-
if (
|
|
24386
|
-
await
|
|
24465
|
+
const dir = import_path15.default.dirname(workPath);
|
|
24466
|
+
if (import_fs10.default.existsSync(dir)) {
|
|
24467
|
+
await import_promises8.default.rmdir(dir);
|
|
24387
24468
|
}
|
|
24388
24469
|
} catch (error) {
|
|
24389
24470
|
}
|
|
24390
24471
|
};
|
|
24391
24472
|
this._ensurePath = (pathUri) => {
|
|
24392
|
-
const dir =
|
|
24393
|
-
if (!
|
|
24394
|
-
|
|
24395
|
-
if (!
|
|
24396
|
-
|
|
24473
|
+
const dir = import_path15.default.dirname(pathUri);
|
|
24474
|
+
if (!import_fs10.default.existsSync(dir))
|
|
24475
|
+
import_fs10.default.mkdirSync(dir, { recursive: true });
|
|
24476
|
+
if (!import_fs10.default.existsSync(pathUri))
|
|
24477
|
+
import_fs10.default.writeFileSync(pathUri, "");
|
|
24397
24478
|
};
|
|
24398
24479
|
this.processRecord = (options) => {
|
|
24399
24480
|
const { consumer, fields, dimensions, producer, record, requestOptions, index: recordIndex } = options;
|
|
24400
24481
|
for (const field of fields) {
|
|
24401
24482
|
const { cField } = field;
|
|
24402
24483
|
const fieldKey = cField.alias ?? cField.key;
|
|
24484
|
+
if (cField.fixed && Algo_default.hasVal(cField.default)) {
|
|
24485
|
+
record[fieldKey] = cField.default;
|
|
24486
|
+
continue;
|
|
24487
|
+
}
|
|
24488
|
+
if (ConsumerManager_default.isLineCountField(cField)) {
|
|
24489
|
+
record[fieldKey] = recordIndex + 1;
|
|
24490
|
+
continue;
|
|
24491
|
+
}
|
|
24492
|
+
if (cField.copyFrom) {
|
|
24493
|
+
record[fieldKey] = record[cField.copyFrom];
|
|
24494
|
+
continue;
|
|
24495
|
+
}
|
|
24403
24496
|
let dimension;
|
|
24404
24497
|
try {
|
|
24405
24498
|
dimension = dimensions.find((x) => x.name === cField.key);
|
|
24406
24499
|
if (!dimension) {
|
|
24407
|
-
|
|
24408
|
-
record[fieldKey] = cField.default;
|
|
24409
|
-
else if (cField.copyFrom)
|
|
24410
|
-
record[fieldKey] = record[cField.copyFrom];
|
|
24411
|
-
else
|
|
24412
|
-
throw new Error(`The requested field "${cField.key}" from the consumer is not present in the underlying producer "${producer.name}" (${dimensions.map((x) => x.name).join(", ")})`);
|
|
24500
|
+
throw new Error(`The requested field "${cField.key}" from the consumer is not present in the underlying producer "${producer.name}" (${dimensions.map((x) => x.name).join(", ")})`);
|
|
24413
24501
|
}
|
|
24414
24502
|
} catch (error) {
|
|
24415
24503
|
const err2 = new Error(`Resolving dimension for field "${fieldKey}" of producer "${producer.name}" failed (index: ${recordIndex}): ${error.message}`, { cause: error });
|
|
@@ -24522,11 +24610,69 @@ var ConsumerExecutorClass = class {
|
|
|
24522
24610
|
}
|
|
24523
24611
|
return record;
|
|
24524
24612
|
};
|
|
24613
|
+
this.processLineCount = async (consumer, datasetPath, executorResults) => {
|
|
24614
|
+
const internalRecordFormat = OutputExecutor_default._getInternalRecordFormat(consumer);
|
|
24615
|
+
const internalFields = ConsumerManager_default.getExpandedFields(consumer);
|
|
24616
|
+
const lineCountFields = ConsumerManager_default.getLineCountFieldsFromExpanded(internalFields, true);
|
|
24617
|
+
if (lineCountFields.length === 0)
|
|
24618
|
+
return 0;
|
|
24619
|
+
const tempWorkPath = datasetPath + "_tmp";
|
|
24620
|
+
const reader = import_fs10.default.createReadStream(datasetPath);
|
|
24621
|
+
const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
|
|
24622
|
+
const writer = import_fs10.default.createWriteStream(tempWorkPath);
|
|
24623
|
+
const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
|
|
24624
|
+
let workerIndex = 0;
|
|
24625
|
+
let currentWorker = executorResults[workerIndex] ?? null;
|
|
24626
|
+
let remainingLinesInWorker = currentWorker?.outputCount ?? 0;
|
|
24627
|
+
let processedLineCount = 0;
|
|
24628
|
+
const advanceWorker = () => {
|
|
24629
|
+
while (currentWorker && remainingLinesInWorker <= 0) {
|
|
24630
|
+
workerIndex++;
|
|
24631
|
+
currentWorker = executorResults[workerIndex] ?? null;
|
|
24632
|
+
remainingLinesInWorker = currentWorker?.outputCount ?? 0;
|
|
24633
|
+
}
|
|
24634
|
+
};
|
|
24635
|
+
advanceWorker();
|
|
24636
|
+
for await (const line of lineReader) {
|
|
24637
|
+
advanceWorker();
|
|
24638
|
+
Affirm_default(currentWorker, `Unable to resolve worker metadata while processing lineCount for ${datasetPath}`);
|
|
24639
|
+
const rowOffset = currentWorker.rowOffset ?? 0;
|
|
24640
|
+
if (rowOffset > 0) {
|
|
24641
|
+
const record = this._parseLine(line, internalRecordFormat, internalFields);
|
|
24642
|
+
for (const field of lineCountFields) {
|
|
24643
|
+
const currentValue = Number(record[field.finalKey]);
|
|
24644
|
+
Affirm_default(!Number.isNaN(currentValue), `Invalid lineCount value found while processing ${datasetPath}`);
|
|
24645
|
+
record[field.finalKey] = currentValue + rowOffset;
|
|
24646
|
+
}
|
|
24647
|
+
if (!writer.write(OutputExecutor_default.outputRecord(record, consumer, internalFields) + "\n"))
|
|
24648
|
+
await waitForDrain();
|
|
24649
|
+
} else if (!writer.write(line + "\n")) {
|
|
24650
|
+
await waitForDrain();
|
|
24651
|
+
}
|
|
24652
|
+
remainingLinesInWorker--;
|
|
24653
|
+
processedLineCount++;
|
|
24654
|
+
}
|
|
24655
|
+
lineReader.close();
|
|
24656
|
+
await new Promise((resolve, reject) => {
|
|
24657
|
+
writer.on("close", resolve);
|
|
24658
|
+
writer.on("error", reject);
|
|
24659
|
+
writer.end();
|
|
24660
|
+
});
|
|
24661
|
+
if (!reader.destroyed) {
|
|
24662
|
+
await new Promise((resolve) => {
|
|
24663
|
+
reader.once("close", resolve);
|
|
24664
|
+
reader.destroy();
|
|
24665
|
+
});
|
|
24666
|
+
}
|
|
24667
|
+
await import_promises8.default.unlink(datasetPath);
|
|
24668
|
+
await import_promises8.default.rename(tempWorkPath, datasetPath);
|
|
24669
|
+
return processedLineCount;
|
|
24670
|
+
};
|
|
24525
24671
|
this.processDistinct = async (datasetPath) => {
|
|
24526
|
-
const reader =
|
|
24672
|
+
const reader = import_fs10.default.createReadStream(datasetPath);
|
|
24527
24673
|
const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
|
|
24528
24674
|
const tempWorkPath = datasetPath + "_tmp";
|
|
24529
|
-
const writer =
|
|
24675
|
+
const writer = import_fs10.default.createWriteStream(tempWorkPath);
|
|
24530
24676
|
const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
|
|
24531
24677
|
let newLineCount = 0;
|
|
24532
24678
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -24551,12 +24697,12 @@ var ConsumerExecutorClass = class {
|
|
|
24551
24697
|
reader.destroy();
|
|
24552
24698
|
});
|
|
24553
24699
|
}
|
|
24554
|
-
await
|
|
24555
|
-
await
|
|
24700
|
+
await import_promises8.default.unlink(datasetPath);
|
|
24701
|
+
await import_promises8.default.rename(tempWorkPath, datasetPath);
|
|
24556
24702
|
return newLineCount;
|
|
24557
24703
|
};
|
|
24558
24704
|
this.processDistinctOn = async (consumer, datasetPath) => {
|
|
24559
|
-
const reader =
|
|
24705
|
+
const reader = import_fs10.default.createReadStream(datasetPath);
|
|
24560
24706
|
const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
|
|
24561
24707
|
const { distinctOn } = consumer.options;
|
|
24562
24708
|
const { keys, resolution } = distinctOn;
|
|
@@ -24579,7 +24725,7 @@ var ConsumerExecutorClass = class {
|
|
|
24579
24725
|
}
|
|
24580
24726
|
lineReader.close();
|
|
24581
24727
|
const tempWorkPath = datasetPath + "_tmp";
|
|
24582
|
-
const writer =
|
|
24728
|
+
const writer = import_fs10.default.createWriteStream(tempWorkPath);
|
|
24583
24729
|
const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
|
|
24584
24730
|
for (const { line } of winners.values()) {
|
|
24585
24731
|
if (!writer.write(line + "\n"))
|
|
@@ -24596,8 +24742,8 @@ var ConsumerExecutorClass = class {
|
|
|
24596
24742
|
reader.destroy();
|
|
24597
24743
|
});
|
|
24598
24744
|
}
|
|
24599
|
-
await
|
|
24600
|
-
await
|
|
24745
|
+
await import_promises8.default.unlink(datasetPath);
|
|
24746
|
+
await import_promises8.default.rename(tempWorkPath, datasetPath);
|
|
24601
24747
|
return winners.size;
|
|
24602
24748
|
};
|
|
24603
24749
|
this.processPivot = async (consumer, datasetPath) => {
|
|
@@ -24609,7 +24755,7 @@ var ConsumerExecutorClass = class {
|
|
|
24609
24755
|
if (!pivotValues) {
|
|
24610
24756
|
pivotValues = [];
|
|
24611
24757
|
const discoverySet = /* @__PURE__ */ new Set();
|
|
24612
|
-
const discoverReader =
|
|
24758
|
+
const discoverReader = import_fs10.default.createReadStream(datasetPath);
|
|
24613
24759
|
const discoverLineReader = import_readline6.default.createInterface({ input: discoverReader, crlfDelay: Infinity });
|
|
24614
24760
|
for await (const line of discoverLineReader) {
|
|
24615
24761
|
const record = this._parseLine(line, internalRecordFormat, internalFields);
|
|
@@ -24628,7 +24774,7 @@ var ConsumerExecutorClass = class {
|
|
|
24628
24774
|
}
|
|
24629
24775
|
}
|
|
24630
24776
|
const groups = /* @__PURE__ */ new Map();
|
|
24631
|
-
const reader =
|
|
24777
|
+
const reader = import_fs10.default.createReadStream(datasetPath);
|
|
24632
24778
|
const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
|
|
24633
24779
|
for await (const line of lineReader) {
|
|
24634
24780
|
const record = this._parseLine(line, internalRecordFormat, internalFields);
|
|
@@ -24656,7 +24802,7 @@ var ConsumerExecutorClass = class {
|
|
|
24656
24802
|
...pivotValues.map((pv) => ({ cField: { key: columnPrefix + pv }, finalKey: columnPrefix + pv }))
|
|
24657
24803
|
];
|
|
24658
24804
|
const tempWorkPath = datasetPath + "_tmp";
|
|
24659
|
-
const writer =
|
|
24805
|
+
const writer = import_fs10.default.createWriteStream(tempWorkPath);
|
|
24660
24806
|
const waitForDrain = () => new Promise((resolve) => writer.once("drain", resolve));
|
|
24661
24807
|
let outputCount = 0;
|
|
24662
24808
|
for (const { rowRecord, cells } of groups.values()) {
|
|
@@ -24702,8 +24848,8 @@ var ConsumerExecutorClass = class {
|
|
|
24702
24848
|
reader.destroy();
|
|
24703
24849
|
});
|
|
24704
24850
|
}
|
|
24705
|
-
await
|
|
24706
|
-
await
|
|
24851
|
+
await import_promises8.default.unlink(datasetPath);
|
|
24852
|
+
await import_promises8.default.rename(tempWorkPath, datasetPath);
|
|
24707
24853
|
return outputCount;
|
|
24708
24854
|
};
|
|
24709
24855
|
this._parseLine = (line, format2, fields) => {
|
|
@@ -24749,7 +24895,7 @@ var ConsumerExecutorClass = class {
|
|
|
24749
24895
|
for (const fieldKey of uniqueFieldKeys) {
|
|
24750
24896
|
fieldValueSets.set(fieldKey, /* @__PURE__ */ new Set());
|
|
24751
24897
|
}
|
|
24752
|
-
const reader =
|
|
24898
|
+
const reader = import_fs10.default.createReadStream(datasetPath);
|
|
24753
24899
|
const lineReader = import_readline6.default.createInterface({ input: reader, crlfDelay: Infinity });
|
|
24754
24900
|
for await (const line of lineReader) {
|
|
24755
24901
|
rowCount++;
|
|
@@ -24797,11 +24943,11 @@ var ConsumerExecutor = new ConsumerExecutorClass();
|
|
|
24797
24943
|
var ConsumerExecutor_default = ConsumerExecutor;
|
|
24798
24944
|
|
|
24799
24945
|
// ../../packages/executors/src/Executor.ts
|
|
24800
|
-
var
|
|
24946
|
+
var import_fs11 = __toESM(require("fs"));
|
|
24801
24947
|
var import_readline7 = __toESM(require("readline"));
|
|
24802
24948
|
|
|
24803
24949
|
// ../../packages/executors/src/ProducerExecutor.ts
|
|
24804
|
-
var
|
|
24950
|
+
var import_path16 = __toESM(require("path"));
|
|
24805
24951
|
var ProducerExecutorClass = class {
|
|
24806
24952
|
constructor() {
|
|
24807
24953
|
this.ready = async (producer, scope) => {
|
|
@@ -24827,7 +24973,7 @@ var ProducerExecutorClass = class {
|
|
|
24827
24973
|
counter = performance.now();
|
|
24828
24974
|
for (const dimension of dimensions) {
|
|
24829
24975
|
if (dimension.prodDimension.sourceFilename === true)
|
|
24830
|
-
record[dimension.name] =
|
|
24976
|
+
record[dimension.name] = import_path16.default.basename(chunk.fileUri);
|
|
24831
24977
|
const maskType = ProducerManager_default.getMask(dimension.prodDimension);
|
|
24832
24978
|
if (Algo_default.hasVal(maskType))
|
|
24833
24979
|
record[dimension.name] = CryptoEngine_default.hashValue(maskType, record[dimension.name]?.toString(), dimension.prodDimension.type);
|
|
@@ -24878,10 +25024,14 @@ var Executor = class {
|
|
|
24878
25024
|
elapsedMS: -1,
|
|
24879
25025
|
inputCount: -1,
|
|
24880
25026
|
outputCount: -1,
|
|
24881
|
-
|
|
25027
|
+
chunkIndex: chunk.index,
|
|
25028
|
+
fileUri: chunk.fileUri,
|
|
25029
|
+
rowOffset: chunk.rowOffset ?? -1,
|
|
25030
|
+
rowCount: -1,
|
|
25031
|
+
resultUri: ExecutorScope_default.getWorkerPath(scope, workerId),
|
|
24882
25032
|
operations: {}
|
|
24883
25033
|
};
|
|
24884
|
-
|
|
25034
|
+
ExecutorScope_default.ensurePath(result.resultUri);
|
|
24885
25035
|
Logger_default.log(`[${workerId}] Starting execution for producer "${producer.name}" \u2192 consumer "${consumer.name}" (file: ${chunk.fileUri})${recordLimit ? ` (limit: ${recordLimit})` : ""}`);
|
|
24886
25036
|
let totalOutputCount = 0, totalCycles = 1, perf = 0, lineIndex = 0;
|
|
24887
25037
|
const readStream = this.openReadStream(chunk);
|
|
@@ -24987,17 +25137,18 @@ var Executor = class {
|
|
|
24987
25137
|
result.cycles = totalCycles;
|
|
24988
25138
|
result.inputCount = lineIndex;
|
|
24989
25139
|
result.outputCount = totalOutputCount;
|
|
25140
|
+
result.rowCount = lineIndex;
|
|
24990
25141
|
result.operations = this._performance.getOperations();
|
|
24991
25142
|
Logger_default.log(`[${workerId}] Finished: ${lineIndex} input \u2192 ${totalOutputCount} output in ${Math.round(result.elapsedMS)}ms (${totalCycles} cycle(s))`);
|
|
24992
25143
|
return result;
|
|
24993
25144
|
};
|
|
24994
25145
|
this.openReadStream = (chunk) => {
|
|
24995
25146
|
const { end, fileUri, start } = chunk;
|
|
24996
|
-
return
|
|
25147
|
+
return import_fs11.default.createReadStream(fileUri, { start, end: end - 1 });
|
|
24997
25148
|
};
|
|
24998
25149
|
this.openWriteStream = (scope, workerId) => {
|
|
24999
|
-
const workerPath =
|
|
25000
|
-
return
|
|
25150
|
+
const workerPath = ExecutorScope_default.getWorkerPath(scope, workerId);
|
|
25151
|
+
return import_fs11.default.createWriteStream(workerPath);
|
|
25001
25152
|
};
|
|
25002
25153
|
this.shouldProcessFirstLine = (producer) => {
|
|
25003
25154
|
Affirm_default(producer, "Invalid producer");
|
|
@@ -25028,14 +25179,13 @@ var Executor = class {
|
|
|
25028
25179
|
var Executor_default = Executor;
|
|
25029
25180
|
|
|
25030
25181
|
// ../../packages/executors/src/ExecutorOrchestrator.ts
|
|
25031
|
-
var
|
|
25032
|
-
var
|
|
25033
|
-
var import_promises10 = __toESM(require("fs/promises"));
|
|
25182
|
+
var import_fs12 = __toESM(require("fs"));
|
|
25183
|
+
var import_promises9 = __toESM(require("fs/promises"));
|
|
25034
25184
|
var import_path18 = __toESM(require("path"));
|
|
25035
25185
|
var import_workerpool = __toESM(require("workerpool"));
|
|
25036
25186
|
|
|
25037
25187
|
// ../../packages/executors/src/ExecutorWriter.ts
|
|
25038
|
-
var
|
|
25188
|
+
var fs16 = __toESM(require("fs"));
|
|
25039
25189
|
var import_readline8 = __toESM(require("readline"));
|
|
25040
25190
|
var ExecutorWriter = class {
|
|
25041
25191
|
constructor() {
|
|
@@ -25052,11 +25202,11 @@ var ExecutorWriter = class {
|
|
|
25052
25202
|
};
|
|
25053
25203
|
this.splitBySize = async (scope, sourcePath) => {
|
|
25054
25204
|
const maxOutputFileSize = scope.limitFileSize * this.FAKE_GB;
|
|
25055
|
-
const readStream =
|
|
25205
|
+
const readStream = fs16.createReadStream(sourcePath);
|
|
25056
25206
|
const reader = import_readline8.default.createInterface({ input: readStream, crlfDelay: Infinity });
|
|
25057
25207
|
let writerIndex = 0;
|
|
25058
25208
|
let destPath = this.getCompletedPath(sourcePath, writerIndex);
|
|
25059
|
-
let writeStream =
|
|
25209
|
+
let writeStream = fs16.createWriteStream(destPath, { flags: "a" });
|
|
25060
25210
|
const waitForDrain = () => new Promise((resolve) => writeStream.once("drain", resolve));
|
|
25061
25211
|
for await (const line of reader) {
|
|
25062
25212
|
if (readStream.bytesRead > maxOutputFileSize * (writerIndex + 1)) {
|
|
@@ -25067,7 +25217,7 @@ var ExecutorWriter = class {
|
|
|
25067
25217
|
});
|
|
25068
25218
|
writerIndex++;
|
|
25069
25219
|
destPath = this.getCompletedPath(sourcePath, writerIndex);
|
|
25070
|
-
writeStream =
|
|
25220
|
+
writeStream = fs16.createWriteStream(destPath, { flags: "a" });
|
|
25071
25221
|
}
|
|
25072
25222
|
if (!writeStream.write(line + "\n"))
|
|
25073
25223
|
await waitForDrain();
|
|
@@ -25077,7 +25227,7 @@ var ExecutorWriter = class {
|
|
|
25077
25227
|
writeStream.on("finish", resolve);
|
|
25078
25228
|
writeStream.on("error", reject);
|
|
25079
25229
|
});
|
|
25080
|
-
await
|
|
25230
|
+
await fs16.promises.unlink(sourcePath);
|
|
25081
25231
|
};
|
|
25082
25232
|
/**
|
|
25083
25233
|
* Manage the Writestream for main.dataset
|
|
@@ -25134,7 +25284,7 @@ var ExecutorWriter = class {
|
|
|
25134
25284
|
var ExecutorWriter_default = ExecutorWriter;
|
|
25135
25285
|
|
|
25136
25286
|
// ../../packages/executors/src/ExecutorOrchestrator.ts
|
|
25137
|
-
var
|
|
25287
|
+
var import_promises10 = require("stream/promises");
|
|
25138
25288
|
|
|
25139
25289
|
// ../../packages/executors/src/cli_progress/ExecutorProgress2.ts
|
|
25140
25290
|
var ExecutorProgress2 = class {
|
|
@@ -25170,19 +25320,111 @@ var ExecutorProgress2 = class {
|
|
|
25170
25320
|
};
|
|
25171
25321
|
var ExecutorProgress2_default = ExecutorProgress2;
|
|
25172
25322
|
|
|
25323
|
+
// ../../packages/executors/src/OrchestratorHelper.ts
|
|
25324
|
+
var import_os = __toESM(require("os"));
|
|
25325
|
+
var import_path17 = __toESM(require("path"));
|
|
25326
|
+
var OrchestratorHelper = {
|
|
25327
|
+
getMemoryUsage: () => {
|
|
25328
|
+
const processMemory = process.memoryUsage();
|
|
25329
|
+
const freeSystemMemory = import_os.default.freemem();
|
|
25330
|
+
return {
|
|
25331
|
+
/**
|
|
25332
|
+
* resident set size (heap + code + stack)
|
|
25333
|
+
*/
|
|
25334
|
+
rss: Formatter_default.bytes(processMemory.rss),
|
|
25335
|
+
heapUsed: Formatter_default.bytes(processMemory.heapUsed),
|
|
25336
|
+
heapTotal: Formatter_default.bytes(processMemory.heapTotal),
|
|
25337
|
+
heapPercent: Algo_default.round(processMemory.heapUsed / processMemory.heapTotal, 1),
|
|
25338
|
+
external: Formatter_default.bytes(processMemory.external),
|
|
25339
|
+
free: Formatter_default.bytes(freeSystemMemory)
|
|
25340
|
+
};
|
|
25341
|
+
},
|
|
25342
|
+
formatMemoryUsage: () => {
|
|
25343
|
+
return `Memory [total: ${OrchestratorHelper.getMemoryUsage().rss} - heap: ${OrchestratorHelper.getMemoryUsage().heapPercent}%]`;
|
|
25344
|
+
},
|
|
25345
|
+
computeFinalResult: (tracker, executorResults, executionId, resultUri) => {
|
|
25346
|
+
const result = {
|
|
25347
|
+
cycles: Algo_default.max(executorResults.map((x) => x.cycles)),
|
|
25348
|
+
elapsedMS: Algo_default.sum(executorResults.map((x) => x.elapsedMS)),
|
|
25349
|
+
inputCount: Algo_default.sum(executorResults.map((x) => x.inputCount)),
|
|
25350
|
+
outputCount: Algo_default.sum(executorResults.map((x) => x.outputCount)),
|
|
25351
|
+
workerCount: executorResults.length,
|
|
25352
|
+
executionId,
|
|
25353
|
+
resultUri,
|
|
25354
|
+
operations: {}
|
|
25355
|
+
};
|
|
25356
|
+
for (const res of executorResults) {
|
|
25357
|
+
for (const opKey of Object.keys(res.operations)) {
|
|
25358
|
+
const op = res.operations[opKey];
|
|
25359
|
+
let label = result.operations[opKey];
|
|
25360
|
+
if (!label) {
|
|
25361
|
+
result.operations[opKey] = { avg: -1, max: -1, min: -1, elapsedMS: [] };
|
|
25362
|
+
label = result.operations[opKey];
|
|
25363
|
+
}
|
|
25364
|
+
label.elapsedMS.push(op.elapsedMS);
|
|
25365
|
+
}
|
|
25366
|
+
for (const opKey of Object.keys(result.operations)) {
|
|
25367
|
+
const operation = result.operations[opKey];
|
|
25368
|
+
if (operation.elapsedMS.length > 0) {
|
|
25369
|
+
operation.min = Math.min(...operation.elapsedMS);
|
|
25370
|
+
operation.max = Math.max(...operation.elapsedMS);
|
|
25371
|
+
operation.avg = Algo_default.mean(operation.elapsedMS);
|
|
25372
|
+
}
|
|
25373
|
+
}
|
|
25374
|
+
}
|
|
25375
|
+
const trackerOperations = tracker.getOperations();
|
|
25376
|
+
for (const opKey of Object.keys(trackerOperations)) {
|
|
25377
|
+
const trackerOp = trackerOperations[opKey];
|
|
25378
|
+
const value = trackerOp.elapsedMS;
|
|
25379
|
+
if (!result.operations[opKey]) {
|
|
25380
|
+
result.operations[opKey] = { avg: value, max: value, min: value, elapsedMS: [] };
|
|
25381
|
+
}
|
|
25382
|
+
result.operations[opKey].elapsedMS.push(value);
|
|
25383
|
+
}
|
|
25384
|
+
return result;
|
|
25385
|
+
},
|
|
25386
|
+
/**
|
|
25387
|
+
* Returns the path to the worker thread file in the build (different between dev, cli and prod (docker)).
|
|
25388
|
+
* IMPORTANT!: when moving this (OrchestratorHelper.ts) file, or the workers output, you have to make sure to update these paths
|
|
25389
|
+
*/
|
|
25390
|
+
getPhysicalWorkerPath: () => {
|
|
25391
|
+
const currentDir = __dirname;
|
|
25392
|
+
if (ProcessENVManager_default.getEnvVariable("NODE_ENV") === "dev" || ProcessENVManager_default.getEnvVariable("NODE_ENV") === "development")
|
|
25393
|
+
return import_path17.default.resolve("./.build/workers");
|
|
25394
|
+
const forcedPath = ProcessENVManager_default.getEnvVariable("REMORA_WORKERS_PATH");
|
|
25395
|
+
if (forcedPath && forcedPath.length > 0)
|
|
25396
|
+
return import_path17.default.join(__dirname, forcedPath);
|
|
25397
|
+
if (!currentDir.includes(".build")) {
|
|
25398
|
+
return import_path17.default.join(__dirname, "../workers");
|
|
25399
|
+
} else {
|
|
25400
|
+
return import_path17.default.resolve("./.build/workers");
|
|
25401
|
+
}
|
|
25402
|
+
},
|
|
25403
|
+
getParallelWorkerCount: () => {
|
|
25404
|
+
const cpuBoundWorkers = Math.max(1, Math.floor(import_os.default.cpus().length * 0.7));
|
|
25405
|
+
const totalMemoryMB = Math.floor(import_os.default.totalmem() / (1024 * 1024));
|
|
25406
|
+
const reservedMemoryMB = Constants_default.defaults.MIN_RUNTIME_HEAP_MB * 2;
|
|
25407
|
+
const availableMemoryForWorkersMB = Math.max(Constants_default.defaults.MIN_RUNTIME_HEAP_MB, totalMemoryMB - reservedMemoryMB);
|
|
25408
|
+
const memoryBoundWorkers = Math.max(1, Math.floor(availableMemoryForWorkersMB / Constants_default.defaults.MIN_RUNTIME_HEAP_MB));
|
|
25409
|
+
return Math.min(cpuBoundWorkers, Constants_default.defaults.MAX_THREAD_COUNT, memoryBoundWorkers);
|
|
25410
|
+
}
|
|
25411
|
+
};
|
|
25412
|
+
var OrchestratorHelper_default = OrchestratorHelper;
|
|
25413
|
+
|
|
25173
25414
|
// ../../packages/executors/src/ExecutorOrchestrator.ts
|
|
25174
25415
|
var ExecutorOrchestratorClass = class {
|
|
25175
25416
|
constructor() {
|
|
25176
|
-
this.createPool = () => {
|
|
25417
|
+
this.createPool = (maxWorkers) => {
|
|
25177
25418
|
const options = {
|
|
25419
|
+
maxWorkers,
|
|
25178
25420
|
workerThreadOpts: {
|
|
25179
25421
|
resourceLimits: {
|
|
25180
25422
|
maxOldGenerationSizeMb: Constants_default.defaults.MIN_RUNTIME_HEAP_MB
|
|
25181
25423
|
}
|
|
25182
25424
|
}
|
|
25183
25425
|
};
|
|
25184
|
-
const workerPath =
|
|
25185
|
-
Logger_default.log(`Initializing worker pool from ${workerPath} (heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB)`);
|
|
25426
|
+
const workerPath = OrchestratorHelper_default.getPhysicalWorkerPath();
|
|
25427
|
+
Logger_default.log(`Initializing worker pool from ${workerPath} (workers: ${maxWorkers}, heap limit: ${Constants_default.defaults.MIN_RUNTIME_HEAP_MB}MB) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25186
25428
|
return import_workerpool.default.pool(import_path18.default.join(workerPath, "ExecutorWorker.js"), options);
|
|
25187
25429
|
};
|
|
25188
25430
|
this.launch = async (request) => {
|
|
@@ -25196,11 +25438,12 @@ var ExecutorOrchestratorClass = class {
|
|
|
25196
25438
|
const _progress = new ExecutorProgress2_default(logProgress);
|
|
25197
25439
|
const { usageId } = UsageManager_default.startUsage(consumer, details);
|
|
25198
25440
|
const scope = { id: usageId, folder: `${consumer.name}_${usageId}`, workersId: [], limitFileSize: consumer.maximumFileSize };
|
|
25199
|
-
const
|
|
25441
|
+
const expandedFields = ConsumerManager_default.getExpandedFields(consumer);
|
|
25442
|
+
let activePool = null;
|
|
25200
25443
|
try {
|
|
25201
25444
|
const start = performance.now();
|
|
25202
25445
|
const executorResults = [];
|
|
25203
|
-
Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length})`);
|
|
25446
|
+
Logger_default.log(`[${usageId}] Launching consumer "${consumer.name}" (invoked by: ${details.invokedBy}, user: ${details.user?.name ?? "unknown"}, producer(s): ${consumer.producers.length}) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25204
25447
|
let counter = performance.now();
|
|
25205
25448
|
_progress.update({ phase: "Preparing source data", progress: 0 });
|
|
25206
25449
|
let sourceFilesByProducer = await this.readySourceFiles(consumer, scope);
|
|
@@ -25216,10 +25459,10 @@ var ExecutorOrchestratorClass = class {
|
|
|
25216
25459
|
let globalWorkerIndex = 0;
|
|
25217
25460
|
for (const pair of sourceFilesByProducer) {
|
|
25218
25461
|
const { prod, cProd, response } = pair;
|
|
25219
|
-
if (!
|
|
25220
|
-
if (!cProd.isOptional)
|
|
25462
|
+
if (!import_fs12.default.existsSync(response.files[0].fullUri)) {
|
|
25463
|
+
if (!cProd.isOptional) {
|
|
25221
25464
|
throw new Error(`Expected data file ${response.files[0].fullUri} of producer ${prod.name} in consumer ${consumer.name} is missing.`);
|
|
25222
|
-
else if (cProd.isOptional === true) {
|
|
25465
|
+
} else if (cProd.isOptional === true) {
|
|
25223
25466
|
Logger_default.log(`[${usageId}] Producer "${prod.name}": data file missing but marked optional, skipping`);
|
|
25224
25467
|
continue;
|
|
25225
25468
|
}
|
|
@@ -25232,35 +25475,42 @@ var ExecutorOrchestratorClass = class {
|
|
|
25232
25475
|
for (const [fileIndex, file] of response.files.entries()) {
|
|
25233
25476
|
const chunks = ExecutorOrchestrator.scopeWork(file.fullUri);
|
|
25234
25477
|
const workerThreads = [];
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25478
|
+
activePool = this.createPool(chunks.length);
|
|
25479
|
+
Logger_default.log(`[${usageId}] Producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}: split into ${chunks.length} chunk(s) | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25480
|
+
try {
|
|
25481
|
+
for (const chunk of chunks) {
|
|
25482
|
+
const workerId = `${usageId}_${globalWorkerIndex}`;
|
|
25483
|
+
globalWorkerIndex++;
|
|
25484
|
+
const workerData = {
|
|
25485
|
+
producer: prod,
|
|
25486
|
+
chunk,
|
|
25487
|
+
consumer,
|
|
25488
|
+
prodDimensions,
|
|
25489
|
+
workerId,
|
|
25490
|
+
scope,
|
|
25491
|
+
options,
|
|
25492
|
+
loggerConfig: Logger_default.getConfig()
|
|
25493
|
+
};
|
|
25494
|
+
scope.workersId.push(workerId);
|
|
25495
|
+
Logger_default.log(`[${usageId}] Spawning worker ${workerId} for producer "${prod.name}" \u2014 chunk ${chunk.start}-${chunk.end} (${Math.round((chunk.end - chunk.start) / 1024)}KB)`);
|
|
25496
|
+
workerThreads.push(activePool.exec("executor", [workerData], {
|
|
25497
|
+
on: (payload) => this.onWorkAdvanced(payload, workerId, _progress, totalBytesToProcess, bytesProcessedByWorker)
|
|
25498
|
+
}).catch((error) => {
|
|
25499
|
+
Logger_default.error(error);
|
|
25500
|
+
return null;
|
|
25501
|
+
}));
|
|
25502
|
+
}
|
|
25503
|
+
Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25504
|
+
const fileExecutorResults = await Promise.all(workerThreads);
|
|
25505
|
+
this.assignChunkRowMetadata(chunks, fileExecutorResults);
|
|
25506
|
+
executorResults.push(...fileExecutorResults);
|
|
25507
|
+
Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25508
|
+
} finally {
|
|
25509
|
+
await activePool.terminate();
|
|
25510
|
+
activePool = null;
|
|
25257
25511
|
}
|
|
25258
|
-
Logger_default.log(`[${usageId}] Waiting for ${workerThreads.length} worker(s) to complete`);
|
|
25259
|
-
executorResults.push(...await Promise.all(workerThreads));
|
|
25260
|
-
Logger_default.log(`[${usageId}] All ${workerThreads.length} worker(s) finished for producer "${prod.name}" file ${fileIndex + 1}/${totalFiles}`);
|
|
25261
25512
|
}
|
|
25262
25513
|
}
|
|
25263
|
-
await pool.terminate();
|
|
25264
25514
|
_progress.update({ phase: "Processing data", progress: 1 });
|
|
25265
25515
|
if (executorResults.some((x) => !Algo_default.hasVal(x)))
|
|
25266
25516
|
throw new Error(`${executorResults.filter((x) => !Algo_default.hasVal(x)).length} worker(s) failed to produce valid results`);
|
|
@@ -25275,7 +25525,7 @@ var ExecutorOrchestratorClass = class {
|
|
|
25275
25525
|
if (consumer.options?.distinct === true) {
|
|
25276
25526
|
Logger_default.log(`[${usageId}] Running unified distinct pass across merged workers`);
|
|
25277
25527
|
counter = performance.now();
|
|
25278
|
-
const unifiedOutputCount = await ConsumerExecutor_default.processDistinct(
|
|
25528
|
+
const unifiedOutputCount = await ConsumerExecutor_default.processDistinct(ExecutorScope_default.getMainPath(scope));
|
|
25279
25529
|
tracker.measure("process-distinct:main", performance.now() - counter);
|
|
25280
25530
|
postOperation.totalOutputCount = unifiedOutputCount;
|
|
25281
25531
|
Logger_default.log(`[${usageId}] Distinct pass complete: ${unifiedOutputCount} unique rows in ${Math.round(performance.now() - counter)}ms`);
|
|
@@ -25283,24 +25533,31 @@ var ExecutorOrchestratorClass = class {
|
|
|
25283
25533
|
if (consumer.options?.distinctOn) {
|
|
25284
25534
|
Logger_default.log(`[${usageId}] Running unified distinctOn pass (${consumer.options.distinctOn})`);
|
|
25285
25535
|
counter = performance.now();
|
|
25286
|
-
const unifiedOutputCount = await ConsumerExecutor_default.processDistinctOn(consumer,
|
|
25536
|
+
const unifiedOutputCount = await ConsumerExecutor_default.processDistinctOn(consumer, ExecutorScope_default.getMainPath(scope));
|
|
25287
25537
|
tracker.measure("process-distinct-on:main", performance.now() - counter);
|
|
25288
25538
|
postOperation.totalOutputCount = unifiedOutputCount;
|
|
25289
25539
|
Logger_default.log(`[${usageId}] DistinctOn pass complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
|
|
25290
25540
|
}
|
|
25291
|
-
|
|
25292
|
-
|
|
25293
|
-
|
|
25294
|
-
|
|
25295
|
-
|
|
25296
|
-
|
|
25297
|
-
|
|
25298
|
-
|
|
25541
|
+
if (consumer.options?.pivot) {
|
|
25542
|
+
Logger_default.log(`[${usageId}] Running pivot operation`);
|
|
25543
|
+
counter = performance.now();
|
|
25544
|
+
const unifiedOutputCount = await ConsumerExecutor_default.processPivot(consumer, ExecutorScope_default.getMainPath(scope));
|
|
25545
|
+
tracker.measure("process-pivot:main", performance.now() - counter);
|
|
25546
|
+
postOperation.totalOutputCount = unifiedOutputCount;
|
|
25547
|
+
Logger_default.log(`[${usageId}] Pivot complete: ${unifiedOutputCount} rows in ${Math.round(performance.now() - counter)}ms`);
|
|
25548
|
+
}
|
|
25549
|
+
if (ConsumerManager_default.hasLineCountFields(consumer, true)) {
|
|
25550
|
+
Logger_default.log(`[${usageId}] Running unified lineCount pass across merged workers`);
|
|
25551
|
+
counter = performance.now();
|
|
25552
|
+
const processedLineCount = await ConsumerExecutor_default.processLineCount(consumer, ExecutorScope_default.getMainPath(scope), executorResults);
|
|
25553
|
+
tracker.measure("process-line-count:main", performance.now() - counter);
|
|
25554
|
+
Logger_default.log(`[${usageId}] LineCount pass complete: normalized ${processedLineCount} rows in ${Math.round(performance.now() - counter)}ms`);
|
|
25555
|
+
}
|
|
25299
25556
|
}
|
|
25300
25557
|
if (consumer.validate && consumer.validate.length > 0) {
|
|
25301
25558
|
Logger_default.log(`[${usageId}] Running dataset-level validations`);
|
|
25302
25559
|
counter = performance.now();
|
|
25303
|
-
const validationResults = await ConsumerExecutor_default.processDatasetValidation(consumer,
|
|
25560
|
+
const validationResults = await ConsumerExecutor_default.processDatasetValidation(consumer, ExecutorScope_default.getMainPath(scope));
|
|
25304
25561
|
tracker.measure("dataset-validation", performance.now() - counter);
|
|
25305
25562
|
for (const result of validationResults) {
|
|
25306
25563
|
if (result.onFail === "fail") {
|
|
@@ -25317,7 +25574,7 @@ var ExecutorOrchestratorClass = class {
|
|
|
25317
25574
|
Logger_default.log(`[${usageId}] Splitting output by size limit (${scope.limitFileSize})`);
|
|
25318
25575
|
counter = performance.now();
|
|
25319
25576
|
const writer = new ExecutorWriter_default();
|
|
25320
|
-
await writer.splitBySize(scope,
|
|
25577
|
+
await writer.splitBySize(scope, ExecutorScope_default.getMainPath(scope));
|
|
25321
25578
|
tracker.measure("split-by-size", performance.now() - counter);
|
|
25322
25579
|
Logger_default.log(`[${usageId}] Split complete in ${Math.round(performance.now() - counter)}ms`);
|
|
25323
25580
|
}
|
|
@@ -25325,7 +25582,7 @@ var ExecutorOrchestratorClass = class {
|
|
|
25325
25582
|
_progress.update({ phase: "Exporting result", progress: 0 });
|
|
25326
25583
|
counter = performance.now();
|
|
25327
25584
|
Logger_default.log(`[${usageId}] Exporting results to ${consumer.outputs.length} output(s)`);
|
|
25328
|
-
const exportRes = await OutputExecutor_default.exportResult(consumer,
|
|
25585
|
+
const exportRes = await OutputExecutor_default.exportResult(consumer, expandedFields, scope);
|
|
25329
25586
|
tracker.measure("export-result", performance.now() - counter);
|
|
25330
25587
|
Logger_default.log(`[${usageId}] Export complete in ${Math.round(performance.now() - counter)}ms (key: ${exportRes.key})`);
|
|
25331
25588
|
_progress.update({ phase: "Exporting result", progress: 1 });
|
|
@@ -25336,9 +25593,9 @@ var ExecutorOrchestratorClass = class {
|
|
|
25336
25593
|
tracker.measure("on-success-actions", performance.now() - counter);
|
|
25337
25594
|
Logger_default.log(`[${usageId}] On-success actions complete in ${Math.round(performance.now() - counter)}ms`);
|
|
25338
25595
|
}
|
|
25339
|
-
Logger_default.log(`[${usageId}] Starting cleanup operations`);
|
|
25596
|
+
Logger_default.log(`[${usageId}] Starting cleanup operations | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25340
25597
|
await this.performCleanupOperations(scope, tracker);
|
|
25341
|
-
const finalResult =
|
|
25598
|
+
const finalResult = OrchestratorHelper_default.computeFinalResult(tracker, executorResults, usageId, exportRes.key);
|
|
25342
25599
|
finalResult.elapsedMS = performance.now() - start;
|
|
25343
25600
|
if (Algo_default.hasVal(postOperation.totalOutputCount))
|
|
25344
25601
|
finalResult.outputCount = postOperation.totalOutputCount;
|
|
@@ -25347,9 +25604,10 @@ var ExecutorOrchestratorClass = class {
|
|
|
25347
25604
|
await Logger_default.flush();
|
|
25348
25605
|
return finalResult;
|
|
25349
25606
|
} catch (error) {
|
|
25350
|
-
Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message}`);
|
|
25607
|
+
Logger_default.log(`[${usageId}] Consumer "${consumer.name}" failed: ${Helper_default.asError(error).message} | ${OrchestratorHelper_default.formatMemoryUsage()}`);
|
|
25351
25608
|
Logger_default.error(error);
|
|
25352
|
-
|
|
25609
|
+
if (activePool)
|
|
25610
|
+
await activePool.terminate();
|
|
25353
25611
|
await ConsumerOnFinishManager_default.onConsumerError(consumer, usageId);
|
|
25354
25612
|
Logger_default.log(`[${usageId}] Running cleanup after failure`);
|
|
25355
25613
|
await this.performCleanupOperations(scope, tracker);
|
|
@@ -25364,18 +25622,17 @@ var ExecutorOrchestratorClass = class {
|
|
|
25364
25622
|
* Returns a single chunk for small files where parallelism overhead isn't worth it.
|
|
25365
25623
|
*/
|
|
25366
25624
|
this.scopeWork = (fileUri, numChunks) => {
|
|
25367
|
-
const fileSize =
|
|
25625
|
+
const fileSize = import_fs12.default.statSync(fileUri).size;
|
|
25368
25626
|
if (fileSize === 0) return [];
|
|
25369
25627
|
if (fileSize < Constants_default.defaults.MIN_FILE_SIZE_FOR_PARALLEL) {
|
|
25370
25628
|
return [{ start: 0, end: fileSize, isFirstChunk: true, fileUri, index: 0 }];
|
|
25371
25629
|
}
|
|
25372
|
-
const
|
|
25373
|
-
const cpus = numChunks ?? Math.min(availableCores, Constants_default.defaults.MAX_THREAD_COUNT);
|
|
25630
|
+
const targetWorkers = numChunks ?? OrchestratorHelper_default.getParallelWorkerCount();
|
|
25374
25631
|
const maxChunksBySize = Math.floor(fileSize / Constants_default.defaults.MIN_CHUNK_SIZE);
|
|
25375
|
-
const effectiveChunks = Math.min(
|
|
25632
|
+
const effectiveChunks = Math.min(targetWorkers, maxChunksBySize);
|
|
25376
25633
|
if (effectiveChunks <= 1) return [{ start: 0, end: fileSize, isFirstChunk: true, fileUri, index: 0 }];
|
|
25377
25634
|
const targetChunkSize = Math.floor(fileSize / effectiveChunks);
|
|
25378
|
-
const fd =
|
|
25635
|
+
const fd = import_fs12.default.openSync(fileUri, "r");
|
|
25379
25636
|
try {
|
|
25380
25637
|
const offsets = [];
|
|
25381
25638
|
let currentStart = 0;
|
|
@@ -25393,7 +25650,23 @@ var ExecutorOrchestratorClass = class {
|
|
|
25393
25650
|
}
|
|
25394
25651
|
return offsets;
|
|
25395
25652
|
} finally {
|
|
25396
|
-
|
|
25653
|
+
import_fs12.default.closeSync(fd);
|
|
25654
|
+
}
|
|
25655
|
+
};
|
|
25656
|
+
this.assignChunkRowMetadata = (chunks, executorResults) => {
|
|
25657
|
+
const validResults = executorResults.filter((result) => Algo_default.hasVal(result));
|
|
25658
|
+
const resultsByChunkIndex = new Map(validResults.map((result) => [result.chunkIndex, result]));
|
|
25659
|
+
let nextRowOffset = 0;
|
|
25660
|
+
for (const chunk of chunks) {
|
|
25661
|
+
const result = resultsByChunkIndex.get(chunk.index);
|
|
25662
|
+
if (!result)
|
|
25663
|
+
continue;
|
|
25664
|
+
const rowCount = result.inputCount;
|
|
25665
|
+
chunk.rowOffset = nextRowOffset;
|
|
25666
|
+
chunk.rowCount = rowCount;
|
|
25667
|
+
result.rowOffset = nextRowOffset;
|
|
25668
|
+
result.rowCount = rowCount;
|
|
25669
|
+
nextRowOffset += rowCount;
|
|
25397
25670
|
}
|
|
25398
25671
|
};
|
|
25399
25672
|
/**
|
|
@@ -25406,7 +25679,7 @@ var ExecutorOrchestratorClass = class {
|
|
|
25406
25679
|
let currentPos = position;
|
|
25407
25680
|
while (currentPos < fileSize) {
|
|
25408
25681
|
const bytesToRead = Math.min(BUFFER_SIZE, fileSize - currentPos);
|
|
25409
|
-
const bytesRead =
|
|
25682
|
+
const bytesRead = import_fs12.default.readSync(fd, buffer, 0, bytesToRead, currentPos);
|
|
25410
25683
|
if (bytesRead === 0) break;
|
|
25411
25684
|
for (let i = 0; i < bytesRead; i++) {
|
|
25412
25685
|
if (buffer[i] === 10) {
|
|
@@ -25470,21 +25743,21 @@ var ExecutorOrchestratorClass = class {
|
|
|
25470
25743
|
startRow: prod.settings.startRow,
|
|
25471
25744
|
startColumn: prod.settings.startColumn
|
|
25472
25745
|
});
|
|
25473
|
-
await (0,
|
|
25746
|
+
await (0, import_promises10.pipeline)(
|
|
25474
25747
|
xlsCsvStream,
|
|
25475
|
-
|
|
25748
|
+
import_fs12.default.createWriteStream(decodedPath)
|
|
25476
25749
|
);
|
|
25477
|
-
const fileStats = await
|
|
25750
|
+
const fileStats = await import_promises9.default.stat(decodedPath);
|
|
25478
25751
|
decodedFiles.push({ fullUri: decodedPath, fileSize: fileStats.size });
|
|
25479
25752
|
decodedCount++;
|
|
25480
25753
|
continue;
|
|
25481
25754
|
}
|
|
25482
25755
|
if (inferredType === "XML") {
|
|
25483
|
-
const fileContent = await
|
|
25756
|
+
const fileContent = await import_promises9.default.readFile(file.fullUri, "utf-8");
|
|
25484
25757
|
const jsonData = XMLParser_default.xmlToJson(fileContent);
|
|
25485
25758
|
const records = normalizeXmlRows(jsonData);
|
|
25486
25759
|
if (records.length === 0) {
|
|
25487
|
-
await
|
|
25760
|
+
await import_promises9.default.writeFile(decodedPath, "", "utf-8");
|
|
25488
25761
|
} else {
|
|
25489
25762
|
const columns = [];
|
|
25490
25763
|
for (const record of records) {
|
|
@@ -25500,9 +25773,9 @@ var ExecutorOrchestratorClass = class {
|
|
|
25500
25773
|
const row = columns.map((column) => csvSafeValue(record[column]));
|
|
25501
25774
|
lines.push(CSVParser_default.stringifyRow(row));
|
|
25502
25775
|
}
|
|
25503
|
-
await
|
|
25776
|
+
await import_promises9.default.writeFile(decodedPath, lines.join("\n"), "utf-8");
|
|
25504
25777
|
}
|
|
25505
|
-
const fileStats = await
|
|
25778
|
+
const fileStats = await import_promises9.default.stat(decodedPath);
|
|
25506
25779
|
decodedFiles.push({ fullUri: decodedPath, fileSize: fileStats.size });
|
|
25507
25780
|
decodedCount++;
|
|
25508
25781
|
continue;
|
|
@@ -25521,86 +25794,32 @@ var ExecutorOrchestratorClass = class {
|
|
|
25521
25794
|
}));
|
|
25522
25795
|
return decodedResults;
|
|
25523
25796
|
};
|
|
25524
|
-
this._getWorkerPath = () => {
|
|
25525
|
-
const currentDir = __dirname;
|
|
25526
|
-
if (ProcessENVManager_default.getEnvVariable("NODE_ENV") === "dev" || ProcessENVManager_default.getEnvVariable("NODE_ENV") === "development")
|
|
25527
|
-
return import_path18.default.resolve("./.build/workers");
|
|
25528
|
-
const forcedPath = ProcessENVManager_default.getEnvVariable("REMORA_WORKERS_PATH");
|
|
25529
|
-
if (forcedPath && forcedPath.length > 0)
|
|
25530
|
-
return import_path18.default.join(__dirname, forcedPath);
|
|
25531
|
-
if (!currentDir.includes(".build")) {
|
|
25532
|
-
return import_path18.default.join(__dirname, "../workers");
|
|
25533
|
-
} else {
|
|
25534
|
-
return import_path18.default.resolve("./.build/workers");
|
|
25535
|
-
}
|
|
25536
|
-
};
|
|
25537
25797
|
this.reconcileExecutorThreadsResults = async (scope, executorResults, tracker) => {
|
|
25538
|
-
const mainPath =
|
|
25798
|
+
const mainPath = ExecutorScope_default.getMainPath(scope);
|
|
25539
25799
|
ConsumerExecutor_default._ensurePath(mainPath);
|
|
25540
25800
|
if (executorResults.length > 1) {
|
|
25541
25801
|
Logger_default.log(`[${scope.id}] Merging ${executorResults.length} worker output files into ${mainPath}`);
|
|
25542
25802
|
const perf = performance.now();
|
|
25543
25803
|
for (const workerResult of executorResults) {
|
|
25544
|
-
await (0,
|
|
25545
|
-
|
|
25546
|
-
|
|
25804
|
+
await (0, import_promises10.pipeline)(
|
|
25805
|
+
import_fs12.default.createReadStream(workerResult.resultUri),
|
|
25806
|
+
import_fs12.default.createWriteStream(mainPath, { flags: "a" })
|
|
25547
25807
|
);
|
|
25548
|
-
await
|
|
25808
|
+
await import_promises9.default.unlink(workerResult.resultUri);
|
|
25549
25809
|
}
|
|
25550
25810
|
tracker.measure("merge-workers", performance.now() - perf);
|
|
25551
25811
|
Logger_default.log(`[${scope.id}] Merge complete in ${Math.round(performance.now() - perf)}ms`);
|
|
25552
25812
|
} else if (executorResults.length === 1) {
|
|
25553
25813
|
Logger_default.log(`[${scope.id}] Single worker \u2014 renaming output to ${mainPath}`);
|
|
25554
|
-
await
|
|
25814
|
+
await import_promises9.default.rename(executorResults[0].resultUri, mainPath);
|
|
25555
25815
|
}
|
|
25556
25816
|
return mainPath;
|
|
25557
25817
|
};
|
|
25558
25818
|
this.performCleanupOperations = async (scope, tracker) => {
|
|
25559
25819
|
const start = performance.now();
|
|
25560
|
-
await
|
|
25820
|
+
await ExecutorScope_default.clearScope(scope);
|
|
25561
25821
|
tracker.measure("cleanup-operations", performance.now() - start);
|
|
25562
25822
|
};
|
|
25563
|
-
this.computeFinalResult = (tracker, executorResults, executionId, resultUri) => {
|
|
25564
|
-
const result = {
|
|
25565
|
-
cycles: Algo_default.max(executorResults.map((x) => x.cycles)),
|
|
25566
|
-
elapsedMS: Algo_default.sum(executorResults.map((x) => x.elapsedMS)),
|
|
25567
|
-
inputCount: Algo_default.sum(executorResults.map((x) => x.inputCount)),
|
|
25568
|
-
outputCount: Algo_default.sum(executorResults.map((x) => x.outputCount)),
|
|
25569
|
-
workerCount: executorResults.length,
|
|
25570
|
-
executionId,
|
|
25571
|
-
resultUri,
|
|
25572
|
-
operations: {}
|
|
25573
|
-
};
|
|
25574
|
-
for (const res of executorResults) {
|
|
25575
|
-
for (const opKey of Object.keys(res.operations)) {
|
|
25576
|
-
const op = res.operations[opKey];
|
|
25577
|
-
let label = result.operations[opKey];
|
|
25578
|
-
if (!label) {
|
|
25579
|
-
result.operations[opKey] = { avg: -1, max: -1, min: -1, elapsedMS: [] };
|
|
25580
|
-
label = result.operations[opKey];
|
|
25581
|
-
}
|
|
25582
|
-
label.elapsedMS.push(op.elapsedMS);
|
|
25583
|
-
}
|
|
25584
|
-
for (const opKey of Object.keys(result.operations)) {
|
|
25585
|
-
const operation = result.operations[opKey];
|
|
25586
|
-
if (operation.elapsedMS.length > 0) {
|
|
25587
|
-
operation.min = Math.min(...operation.elapsedMS);
|
|
25588
|
-
operation.max = Math.max(...operation.elapsedMS);
|
|
25589
|
-
operation.avg = Algo_default.mean(operation.elapsedMS);
|
|
25590
|
-
}
|
|
25591
|
-
}
|
|
25592
|
-
}
|
|
25593
|
-
const trackerOperations = tracker.getOperations();
|
|
25594
|
-
for (const opKey of Object.keys(trackerOperations)) {
|
|
25595
|
-
const trackerOp = trackerOperations[opKey];
|
|
25596
|
-
const value = trackerOp.elapsedMS;
|
|
25597
|
-
if (!result.operations[opKey]) {
|
|
25598
|
-
result.operations[opKey] = { avg: value, max: value, min: value, elapsedMS: [] };
|
|
25599
|
-
}
|
|
25600
|
-
result.operations[opKey].elapsedMS.push(value);
|
|
25601
|
-
}
|
|
25602
|
-
return result;
|
|
25603
|
-
};
|
|
25604
25823
|
this.onWorkAdvanced = (packet, workerId, progress, totalBytesToProcess, bytesProcessedByWorker) => {
|
|
25605
25824
|
const { processed } = packet;
|
|
25606
25825
|
bytesProcessedByWorker[workerId] = processed;
|