@andersbakken/fisk 4.0.57 → 4.0.61
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/builder/VM_runtime.js +27 -23
- package/builder/fisk-builder.js +66 -38
- package/monitor/fisk-monitor.js +15 -10
- package/package.json +1 -1
- package/scheduler/fisk-scheduler.js +132 -40
package/builder/VM_runtime.js
CHANGED
|
@@ -3225,6 +3225,7 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3225
3225
|
proc.on("exit", (exitCode) => {
|
|
3226
3226
|
// try {
|
|
3227
3227
|
const files = [];
|
|
3228
|
+
let addDirError;
|
|
3228
3229
|
const addDir = (directory, prefix) => {
|
|
3229
3230
|
try {
|
|
3230
3231
|
fs.readdirSync(directory).forEach((file) => {
|
|
@@ -3241,21 +3242,21 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3241
3242
|
if (file === outputFileName) {
|
|
3242
3243
|
files.push({ path: output, mapped: path__default["default"].join(prefix, file) });
|
|
3243
3244
|
}
|
|
3244
|
-
else if (path__default["default"].extname(file) === ".gcno") {
|
|
3245
|
-
// console.log("mapping", output, prefix, file);
|
|
3246
|
-
files.push({
|
|
3247
|
-
path: output.substring(0, output.length - 1) + "gcno",
|
|
3248
|
-
mapped: path__default["default"].join(prefix, file)
|
|
3249
|
-
});
|
|
3250
|
-
}
|
|
3251
|
-
else if (path__default["default"].extname(file) === ".gcda") {
|
|
3252
|
-
files.push({
|
|
3253
|
-
path: output.substring(0, output.length - 1) + "gcda",
|
|
3254
|
-
mapped: path__default["default"].join(prefix, file)
|
|
3255
|
-
});
|
|
3256
|
-
}
|
|
3257
3245
|
else {
|
|
3258
|
-
|
|
3246
|
+
const ext = path__default["default"].extname(file);
|
|
3247
|
+
switch (ext) {
|
|
3248
|
+
case ".gcno":
|
|
3249
|
+
case ".gcda":
|
|
3250
|
+
case ".dwo":
|
|
3251
|
+
files.push({
|
|
3252
|
+
path: output.substring(0, output.length - 2) + ext,
|
|
3253
|
+
mapped: path__default["default"].join(prefix, file)
|
|
3254
|
+
});
|
|
3255
|
+
break;
|
|
3256
|
+
default:
|
|
3257
|
+
files.push({ path: path__default["default"].join(prefix, file) });
|
|
3258
|
+
break;
|
|
3259
|
+
}
|
|
3259
3260
|
}
|
|
3260
3261
|
if (debug) {
|
|
3261
3262
|
console.log("Added file", file, files[files.length - 1]);
|
|
@@ -3269,23 +3270,26 @@ class Compile extends EventEmitter__default["default"] {
|
|
|
3269
3270
|
}
|
|
3270
3271
|
catch (err) {
|
|
3271
3272
|
console.error("Got an error processing outputs for", sourcePath, err);
|
|
3272
|
-
|
|
3273
|
-
const errorExitEvent = {
|
|
3274
|
-
exitCode: 110,
|
|
3275
|
-
files: [],
|
|
3276
|
-
error: err.toString(),
|
|
3277
|
-
sourcePath
|
|
3278
|
-
};
|
|
3279
|
-
this.emit("exit", errorExitEvent);
|
|
3273
|
+
addDirError = err;
|
|
3280
3274
|
}
|
|
3281
3275
|
};
|
|
3282
3276
|
if (exitCode === 0) {
|
|
3283
3277
|
addDir(dir, dir);
|
|
3284
3278
|
}
|
|
3279
|
+
assert__default["default"](sourcePath !== undefined, "Must have sourcePath4");
|
|
3280
|
+
if (addDirError) {
|
|
3281
|
+
const errorExitEvent = {
|
|
3282
|
+
exitCode: 110,
|
|
3283
|
+
files: [],
|
|
3284
|
+
error: addDirError.toString(),
|
|
3285
|
+
sourcePath
|
|
3286
|
+
};
|
|
3287
|
+
this.emit("exit", errorExitEvent);
|
|
3288
|
+
return;
|
|
3289
|
+
}
|
|
3285
3290
|
if (exitCode === null) {
|
|
3286
3291
|
exitCode = 111;
|
|
3287
3292
|
}
|
|
3288
|
-
assert__default["default"](sourcePath !== undefined, "Must have sourcePath4");
|
|
3289
3293
|
const exitEvent = { exitCode, files, sourcePath };
|
|
3290
3294
|
this.emit("exit", exitEvent);
|
|
3291
3295
|
});
|
package/builder/fisk-builder.js
CHANGED
|
@@ -7965,7 +7965,10 @@ class ObjectCache extends EventEmitter__default["default"] {
|
|
|
7965
7965
|
}
|
|
7966
7966
|
loadFile(filePath, fileSize) {
|
|
7967
7967
|
const fileName = path__default["default"].basename(filePath);
|
|
7968
|
-
//
|
|
7968
|
+
// Skip if already cached — makes loadFile idempotent
|
|
7969
|
+
if (fileName in this.cache) {
|
|
7970
|
+
return;
|
|
7971
|
+
}
|
|
7969
7972
|
let fd;
|
|
7970
7973
|
let jsonBuffer;
|
|
7971
7974
|
try {
|
|
@@ -7993,6 +7996,9 @@ class ObjectCache extends EventEmitter__default["default"] {
|
|
|
7993
7996
|
this.size += item.fileSize;
|
|
7994
7997
|
this.cache[fileName] = item;
|
|
7995
7998
|
this.emit("added", { sha1: response.sha1, sourcePath: response.sourcePath, fileSize: stat.size });
|
|
7999
|
+
if (this.size > this.maxSize) {
|
|
8000
|
+
this.purge(this.purgeSize);
|
|
8001
|
+
}
|
|
7996
8002
|
}
|
|
7997
8003
|
else {
|
|
7998
8004
|
throw new Error("Unexpected file " + fileName);
|
|
@@ -8010,7 +8016,6 @@ class ObjectCache extends EventEmitter__default["default"] {
|
|
|
8010
8016
|
console.error("Can't even delete this one", doubleError);
|
|
8011
8017
|
}
|
|
8012
8018
|
}
|
|
8013
|
-
return undefined;
|
|
8014
8019
|
}
|
|
8015
8020
|
state(sha1) {
|
|
8016
8021
|
if (sha1 in this.cache) {
|
|
@@ -8068,13 +8073,12 @@ class ObjectCache extends EventEmitter__default["default"] {
|
|
|
8068
8073
|
throw new Error(`Wrong file size for ${path__default["default"].join(this.dir, response.sha1)}, should have been ${cacheItem.fileSize} but ended up being ${stat.size}`);
|
|
8069
8074
|
}
|
|
8070
8075
|
this.cache[response.sha1] = cacheItem;
|
|
8071
|
-
|
|
8076
|
+
this.size += cacheItem.fileSize;
|
|
8072
8077
|
this.emit("added", {
|
|
8073
8078
|
sha1: response.sha1,
|
|
8074
8079
|
sourcePath: response.sourcePath,
|
|
8075
8080
|
fileSize: cacheItem.fileSize
|
|
8076
8081
|
});
|
|
8077
|
-
this.size += cacheItem.fileSize;
|
|
8078
8082
|
if (this.size > this.maxSize) {
|
|
8079
8083
|
this.purge(this.purgeSize);
|
|
8080
8084
|
}
|
|
@@ -55000,7 +55004,7 @@ class CompileJob extends EventEmitter__default["default"] {
|
|
|
55000
55004
|
fs$3.writeSync(this.fd, data);
|
|
55001
55005
|
this.cppSize += data.length;
|
|
55002
55006
|
this.startCompile = Date.now();
|
|
55003
|
-
fs$3.
|
|
55007
|
+
fs$3.closeSync(this.fd);
|
|
55004
55008
|
this.fd = undefined;
|
|
55005
55009
|
this.vm.child.send({ type: "compile", commandLine: this.commandLine, argv0: this.argv0, id: this.id, dir: this.vmDir }, this.sendCallback.bind(this));
|
|
55006
55010
|
}
|
|
@@ -60063,6 +60067,7 @@ process.on("uncaughtException", (err) => {
|
|
|
60063
60067
|
});
|
|
60064
60068
|
let debug = option("debug");
|
|
60065
60069
|
let objectCache;
|
|
60070
|
+
let fetchSequence = 0;
|
|
60066
60071
|
function getFromCache(job, cb) {
|
|
60067
60072
|
// console.log("got job", job.sha1, objectCache ? objectCache.state(job.sha1) : false);
|
|
60068
60073
|
// if (objectCache)
|
|
@@ -60216,71 +60221,88 @@ client.on("fetch_cache_objects", (msg) => {
|
|
|
60216
60221
|
for (let idx = 0; idx < max; ++idx) {
|
|
60217
60222
|
promises.push(Promise.resolve());
|
|
60218
60223
|
}
|
|
60224
|
+
const fetchTimeoutMs = option.int("object-cache-fetch-timeout", 5 * 60 * 1000);
|
|
60219
60225
|
message.objects.forEach((operation, idx) => {
|
|
60220
60226
|
promises[idx % promises.length] = promises[idx % promises.length].then(() => {
|
|
60221
60227
|
return new Promise((resolve) => {
|
|
60222
60228
|
assert__default["default"](objectCache, "Must have objectCache");
|
|
60223
|
-
|
|
60229
|
+
if (objectCache.state(operation.sha1) !== "none") {
|
|
60230
|
+
console.log("Skipping", operation.sha1, "already", objectCache.state(operation.sha1));
|
|
60231
|
+
resolve();
|
|
60232
|
+
return;
|
|
60233
|
+
}
|
|
60234
|
+
const finalPath = path__default["default"].join(objectCache.dir, operation.sha1);
|
|
60235
|
+
const tmpPath = finalPath + ".tmp." + process.pid + "." + ++fetchSequence;
|
|
60224
60236
|
const url = `http://${operation.source}/objectcache/${operation.sha1}`;
|
|
60225
|
-
console.log("Downloading", url, "->",
|
|
60237
|
+
console.log("Downloading", url, "->", tmpPath);
|
|
60226
60238
|
let expectedSize;
|
|
60227
60239
|
let writeStream;
|
|
60228
|
-
|
|
60229
|
-
|
|
60230
|
-
}
|
|
60231
|
-
catch (err) {
|
|
60232
|
-
console.error("Got some error from write stream", err);
|
|
60240
|
+
let resolved = false;
|
|
60241
|
+
const cleanup = () => {
|
|
60233
60242
|
try {
|
|
60234
|
-
fs$3.unlinkSync(
|
|
60243
|
+
fs$3.unlinkSync(tmpPath);
|
|
60235
60244
|
}
|
|
60236
60245
|
catch (e) {
|
|
60237
60246
|
/* */
|
|
60238
60247
|
}
|
|
60239
|
-
|
|
60248
|
+
if (!resolved) {
|
|
60249
|
+
resolved = true;
|
|
60250
|
+
resolve();
|
|
60251
|
+
}
|
|
60252
|
+
};
|
|
60253
|
+
try {
|
|
60254
|
+
writeStream = fs$3.createWriteStream(tmpPath);
|
|
60255
|
+
}
|
|
60256
|
+
catch (err) {
|
|
60257
|
+
console.error("Failed to create write stream for", tmpPath, err);
|
|
60258
|
+
cleanup();
|
|
60240
60259
|
return;
|
|
60241
60260
|
}
|
|
60242
|
-
axios({ method: "get", url: url, responseType: "stream" })
|
|
60261
|
+
axios({ method: "get", url: url, responseType: "stream", timeout: fetchTimeoutMs })
|
|
60243
60262
|
.then((response) => {
|
|
60244
60263
|
expectedSize = parseInt(response.headers["content-length"]);
|
|
60245
60264
|
response.data.pipe(writeStream);
|
|
60246
60265
|
response.data.on("error", (err) => {
|
|
60247
|
-
console.error("Got
|
|
60266
|
+
console.error("Got http stream error for", operation.sha1, err);
|
|
60248
60267
|
writeStream.destroy(new Error("http stream error " + err.toString()));
|
|
60249
60268
|
});
|
|
60250
|
-
// console
|
|
60251
60269
|
})
|
|
60252
60270
|
.catch((err) => {
|
|
60253
|
-
console.error("Got
|
|
60271
|
+
console.error("Got http error fetching", operation.sha1, err);
|
|
60254
60272
|
writeStream.destroy(new Error("http error " + err.toString()));
|
|
60255
60273
|
});
|
|
60256
60274
|
writeStream.on("finish", () => {
|
|
60257
|
-
console.log("Finished writing file", file);
|
|
60258
60275
|
let stat;
|
|
60259
60276
|
try {
|
|
60260
|
-
stat = fs$3.statSync(
|
|
60277
|
+
stat = fs$3.statSync(tmpPath);
|
|
60261
60278
|
}
|
|
60262
60279
|
catch (err) {
|
|
60263
60280
|
/* */
|
|
60264
60281
|
}
|
|
60265
60282
|
if (!stat || stat.size !== expectedSize) {
|
|
60266
|
-
console.log("Got wrong size for",
|
|
60283
|
+
console.log("Got wrong size for", operation.sha1, url, "\nGot", stat ? stat.size : -1, "expected", expectedSize);
|
|
60284
|
+
cleanup();
|
|
60285
|
+
}
|
|
60286
|
+
else {
|
|
60267
60287
|
try {
|
|
60268
|
-
fs$3.
|
|
60288
|
+
fs$3.renameSync(tmpPath, finalPath);
|
|
60289
|
+
++filesReceived;
|
|
60290
|
+
assert__default["default"](objectCache, "Must have objectcache");
|
|
60291
|
+
objectCache.loadFile(finalPath, stat.size);
|
|
60269
60292
|
}
|
|
60270
60293
|
catch (err) {
|
|
60271
|
-
|
|
60294
|
+
console.error("Failed to rename", tmpPath, "to", finalPath, err);
|
|
60295
|
+
cleanup();
|
|
60272
60296
|
}
|
|
60273
60297
|
}
|
|
60274
|
-
|
|
60275
|
-
|
|
60276
|
-
|
|
60277
|
-
objectCache.loadFile(file, stat.size);
|
|
60298
|
+
if (!resolved) {
|
|
60299
|
+
resolved = true;
|
|
60300
|
+
resolve();
|
|
60278
60301
|
}
|
|
60279
|
-
resolve();
|
|
60280
60302
|
});
|
|
60281
60303
|
writeStream.on("error", (err) => {
|
|
60282
|
-
console.error("Got stream error", err);
|
|
60283
|
-
|
|
60304
|
+
console.error("Got stream error for", operation.sha1, err);
|
|
60305
|
+
cleanup();
|
|
60284
60306
|
});
|
|
60285
60307
|
});
|
|
60286
60308
|
});
|
|
@@ -60288,10 +60310,6 @@ client.on("fetch_cache_objects", (msg) => {
|
|
|
60288
60310
|
Promise.all(promises).then(() => {
|
|
60289
60311
|
console.log("got results", filesReceived);
|
|
60290
60312
|
});
|
|
60291
|
-
// chain.then(() => {
|
|
60292
|
-
// console.log("Received", filesReceived, "files. Restarting");
|
|
60293
|
-
// process.exit();
|
|
60294
|
-
// });
|
|
60295
60313
|
});
|
|
60296
60314
|
const environmentsRoot = path__default["default"].join(common.cacheDir(), "environments");
|
|
60297
60315
|
function exec(command, opts) {
|
|
@@ -60454,7 +60472,10 @@ client.on("getEnvironments", (message) => {
|
|
|
60454
60472
|
base += "/environment/";
|
|
60455
60473
|
const work = () => {
|
|
60456
60474
|
if (!message.environments.length) {
|
|
60457
|
-
|
|
60475
|
+
let restart = option("restart-on-new-environments");
|
|
60476
|
+
if (restart === "false" || restart === "0") {
|
|
60477
|
+
restart = false;
|
|
60478
|
+
}
|
|
60458
60479
|
if (!restart) {
|
|
60459
60480
|
setTimeout(() => {
|
|
60460
60481
|
client.send("environments", { environments: Object.keys(environments) });
|
|
@@ -60607,7 +60628,7 @@ server.on("headers", (headers, req) => {
|
|
|
60607
60628
|
wait = true;
|
|
60608
60629
|
}
|
|
60609
60630
|
else if (jobQueue.length >= client.slots) {
|
|
60610
|
-
const priority = parseInt(req.headers["x-fisk-
|
|
60631
|
+
const priority = parseInt(String(req.headers["x-fisk-priority"])) || 0;
|
|
60611
60632
|
let idx = jobQueue.length - 1;
|
|
60612
60633
|
while (idx >= client.slots) {
|
|
60613
60634
|
const job = jobQueue[idx].job;
|
|
@@ -60659,7 +60680,13 @@ server.on("listen", (app) => {
|
|
|
60659
60680
|
const rstream = fs$3.createReadStream(file);
|
|
60660
60681
|
rstream.on("error", (err) => {
|
|
60661
60682
|
console.error("Got read stream error for", file, err);
|
|
60662
|
-
rstream.
|
|
60683
|
+
rstream.destroy();
|
|
60684
|
+
if (!res.headersSent) {
|
|
60685
|
+
res.sendStatus(500);
|
|
60686
|
+
}
|
|
60687
|
+
else {
|
|
60688
|
+
res.destroy();
|
|
60689
|
+
}
|
|
60663
60690
|
});
|
|
60664
60691
|
rstream.pipe(res);
|
|
60665
60692
|
}
|
|
@@ -60818,11 +60845,12 @@ server.on("job", (job) => {
|
|
|
60818
60845
|
const original = contents.find((c) => c.path === item.path);
|
|
60819
60846
|
assert__default["default"](original, "Must have original contents");
|
|
60820
60847
|
assert__default["default"](original.uncompressed, "Must have uncompressed data");
|
|
60821
|
-
|
|
60848
|
+
const ret = {
|
|
60822
60849
|
path: item.path,
|
|
60823
60850
|
bytes: item.contents.length,
|
|
60824
60851
|
uncompressedSize: original.uncompressed.byteLength
|
|
60825
60852
|
};
|
|
60853
|
+
return ret;
|
|
60826
60854
|
}),
|
|
60827
60855
|
success: event.success,
|
|
60828
60856
|
exitCode: event.exitCode,
|
package/monitor/fisk-monitor.js
CHANGED
|
@@ -6143,8 +6143,9 @@ clientBox.on("select", (ev) => {
|
|
|
6143
6143
|
if (jobs) {
|
|
6144
6144
|
// clientBox.current = clientKey;
|
|
6145
6145
|
let str = "";
|
|
6146
|
-
const
|
|
6147
|
-
const
|
|
6146
|
+
const header = ["Source file", "Builder", "Start time"];
|
|
6147
|
+
const rows = [];
|
|
6148
|
+
const widest = [header[0].length + 1, header[1].length + 1];
|
|
6148
6149
|
const now = Date.now();
|
|
6149
6150
|
for (const [jobKey, jobValue] of jobs) {
|
|
6150
6151
|
if (jobKey === "total") {
|
|
@@ -6152,15 +6153,19 @@ clientBox.on("select", (ev) => {
|
|
|
6152
6153
|
}
|
|
6153
6154
|
const sourceBasename = path__default["default"].basename(jobValue.sourcePath || jobValue.sourceFile || "");
|
|
6154
6155
|
widest[0] = Math.max(sourceBasename.length + 1, widest[0]);
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6156
|
+
const ageMs = now - jobValue.time;
|
|
6157
|
+
const row = {
|
|
6158
|
+
source: sourceBasename,
|
|
6159
|
+
builder: jobValue.builder.ip + ":" + jobValue.builder.port,
|
|
6160
|
+
humanizedAge: humanizeDuration(ageMs),
|
|
6161
|
+
ageMs
|
|
6162
|
+
};
|
|
6163
|
+
rows.push(row);
|
|
6164
|
+
widest[1] = Math.max(widest[1], row.builder.length + 1);
|
|
6161
6165
|
}
|
|
6162
|
-
|
|
6163
|
-
|
|
6166
|
+
rows.sort((a, b) => a.ageMs - b.ageMs);
|
|
6167
|
+
const lines = [header, ...rows.map((r) => [r.source, r.builder, r.humanizedAge])];
|
|
6168
|
+
lines.forEach((line, idx) => {
|
|
6164
6169
|
if (!idx) {
|
|
6165
6170
|
str += "{bold}";
|
|
6166
6171
|
}
|
package/package.json
CHANGED
|
@@ -87,11 +87,21 @@ class Database {
|
|
|
87
87
|
records = {};
|
|
88
88
|
}
|
|
89
89
|
records[key] = value;
|
|
90
|
-
fs__default["default"].writeFile(this.path + ".tmp", JSON.stringify(records) + "\n", () => {
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
fs__default["default"].writeFile(this.path + ".tmp", JSON.stringify(records) + "\n", (writeErr) => {
|
|
91
|
+
if (writeErr) {
|
|
92
|
+
reject(writeErr);
|
|
93
|
+
this.finishedOperation();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
fs__default["default"].rename(this.path + ".tmp", this.path, (renameErr) => {
|
|
97
|
+
if (renameErr) {
|
|
98
|
+
reject(renameErr);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
resolve();
|
|
102
|
+
}
|
|
103
|
+
this.finishedOperation();
|
|
93
104
|
});
|
|
94
|
-
this.finishedOperation();
|
|
95
105
|
});
|
|
96
106
|
})
|
|
97
107
|
.catch((err) => {
|
|
@@ -3768,9 +3778,11 @@ class Environments {
|
|
|
3768
3778
|
}
|
|
3769
3779
|
else {
|
|
3770
3780
|
const targets = this._links[srcHash];
|
|
3771
|
-
targets
|
|
3772
|
-
|
|
3773
|
-
|
|
3781
|
+
if (targets) {
|
|
3782
|
+
targets.unset(targetHash);
|
|
3783
|
+
if (!targets.size) {
|
|
3784
|
+
delete this._links[srcHash];
|
|
3785
|
+
}
|
|
3774
3786
|
}
|
|
3775
3787
|
}
|
|
3776
3788
|
return this.syncLinks();
|
|
@@ -3796,15 +3808,19 @@ class Environments {
|
|
|
3796
3808
|
}
|
|
3797
3809
|
remove(hash) {
|
|
3798
3810
|
// ### this should be promisified
|
|
3811
|
+
const env = this._data[hash];
|
|
3812
|
+
if (!env) {
|
|
3813
|
+
return;
|
|
3814
|
+
}
|
|
3799
3815
|
try {
|
|
3800
|
-
fs$4.removeSync(
|
|
3816
|
+
fs$4.removeSync(env.path);
|
|
3801
3817
|
delete this._data[hash];
|
|
3802
3818
|
this.unlink(hash);
|
|
3803
3819
|
this.unlink(undefined, hash);
|
|
3804
3820
|
this.syncLinks();
|
|
3805
3821
|
}
|
|
3806
3822
|
catch (err) {
|
|
3807
|
-
console.error("Failed to remove environment",
|
|
3823
|
+
console.error("Failed to remove environment", env.path, err);
|
|
3808
3824
|
}
|
|
3809
3825
|
}
|
|
3810
3826
|
}
|
|
@@ -3953,7 +3969,9 @@ function prettySize(bytes) {
|
|
|
3953
3969
|
function addToSHA1Map(bySHA1, sha1, fileSize, node) {
|
|
3954
3970
|
const data = bySHA1.get(sha1);
|
|
3955
3971
|
if (data) {
|
|
3956
|
-
data.nodes.
|
|
3972
|
+
if (data.nodes.indexOf(node) === -1) {
|
|
3973
|
+
data.nodes.push(node);
|
|
3974
|
+
}
|
|
3957
3975
|
return data.nodes.length;
|
|
3958
3976
|
}
|
|
3959
3977
|
bySHA1.set(sha1, new SHA1Data(fileSize, node));
|
|
@@ -3977,21 +3995,32 @@ function removeFromSHA1Map(bySHA1, sha1, node) {
|
|
|
3977
3995
|
console.error("We don't have", sha1);
|
|
3978
3996
|
}
|
|
3979
3997
|
}
|
|
3998
|
+
function pendingKey(sha1, node) {
|
|
3999
|
+
return sha1 + ":" + node.ip + ":" + node.port;
|
|
4000
|
+
}
|
|
3980
4001
|
class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
3981
4002
|
constructor(option) {
|
|
3982
4003
|
super();
|
|
3983
4004
|
this.hits = 0;
|
|
3984
4005
|
this.bySHA1 = new Map();
|
|
3985
4006
|
this.byNode = new Map();
|
|
4007
|
+
this.pendingTransfers = new Set();
|
|
4008
|
+
this.pendingTransferTimers = new Map();
|
|
3986
4009
|
this.redundancy = option.int("object-cache-redundancy", 1);
|
|
3987
4010
|
if (this.redundancy <= 0) {
|
|
3988
4011
|
this.redundancy = 1;
|
|
3989
4012
|
}
|
|
4013
|
+
this.pendingTransferTimeoutMs = option.int("object-cache-transfer-timeout", 5 * 60 * 1000);
|
|
3990
4014
|
this.distributeOnInsertion = Boolean(option("distribute-object-cache-on-insertion"));
|
|
3991
4015
|
this.distributeOnCacheHit = Boolean(option("distribute-object-cache-on-cache-hit"));
|
|
3992
4016
|
}
|
|
3993
4017
|
clear() {
|
|
3994
4018
|
this.hits = 0;
|
|
4019
|
+
for (const timer of this.pendingTransferTimers.values()) {
|
|
4020
|
+
clearTimeout(timer);
|
|
4021
|
+
}
|
|
4022
|
+
this.pendingTransfers.clear();
|
|
4023
|
+
this.pendingTransferTimers.clear();
|
|
3995
4024
|
this.emit("cleared");
|
|
3996
4025
|
}
|
|
3997
4026
|
hit(sha1) {
|
|
@@ -4008,7 +4037,10 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4008
4037
|
const nodeData = this.byNode.get(node);
|
|
4009
4038
|
console.log("adding", msg.sourcePath, msg.sha1, "for", node.ip + ":" + node.port, nodeData ? nodeData.sha1s.length : -1);
|
|
4010
4039
|
if (nodeData) {
|
|
4011
|
-
|
|
4040
|
+
this.clearPendingTransfer(msg.sha1, node);
|
|
4041
|
+
if (nodeData.sha1s.indexOf(msg.sha1) === -1) {
|
|
4042
|
+
nodeData.sha1s.push(msg.sha1);
|
|
4043
|
+
}
|
|
4012
4044
|
nodeData.size = msg.cacheSize;
|
|
4013
4045
|
const count = addToSHA1Map(this.bySHA1, msg.sha1, msg.fileSize, node);
|
|
4014
4046
|
if (this.distributeOnInsertion && count - 1 < this.redundancy) {
|
|
@@ -4057,6 +4089,7 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4057
4089
|
return;
|
|
4058
4090
|
}
|
|
4059
4091
|
this.byNode.delete(node);
|
|
4092
|
+
this.clearAllPendingForNode(node);
|
|
4060
4093
|
nodeData.sha1s.forEach((sha1) => {
|
|
4061
4094
|
removeFromSHA1Map(this.bySHA1, sha1, node);
|
|
4062
4095
|
});
|
|
@@ -4081,7 +4114,7 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4081
4114
|
data.name = key.name;
|
|
4082
4115
|
}
|
|
4083
4116
|
if (key.hostname) {
|
|
4084
|
-
data.
|
|
4117
|
+
data.hostname = key.hostname;
|
|
4085
4118
|
}
|
|
4086
4119
|
nodes[key.ip + ":" + key.port] = data;
|
|
4087
4120
|
});
|
|
@@ -4110,11 +4143,12 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4110
4143
|
if (isNaN(max) || max <= 0) {
|
|
4111
4144
|
max = undefined;
|
|
4112
4145
|
}
|
|
4113
|
-
const sha1 =
|
|
4146
|
+
const sha1 = query.sha1 && typeof query.sha1 === "string" ? query.sha1 : undefined;
|
|
4147
|
+
const nodeRestriction = query.node && typeof query.node === "string" ? query.node : undefined;
|
|
4114
4148
|
let ret;
|
|
4115
4149
|
if (res) {
|
|
4116
4150
|
ret = { type: "fetch_cache_objects", dry: dry, commands: {} };
|
|
4117
|
-
console.log("distribute called with redundancy of", redundancy, "and max of", max, "dry", dry, "sha1", sha1);
|
|
4151
|
+
console.log("distribute called with redundancy of", redundancy, "and max of", max, "dry", dry, "sha1", sha1 || "(all)");
|
|
4118
4152
|
}
|
|
4119
4153
|
const nodes = Array.from(this.byNode.keys());
|
|
4120
4154
|
let nodeIdx = 0;
|
|
@@ -4124,23 +4158,20 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4124
4158
|
commands.set(key, { objects: [], available: value.maxSize - value.size });
|
|
4125
4159
|
});
|
|
4126
4160
|
}
|
|
4127
|
-
const nodeRestriction = query.node;
|
|
4128
4161
|
let count = 0;
|
|
4129
|
-
// console.log(commands);
|
|
4130
4162
|
if (this.byNode.size >= 2) {
|
|
4131
|
-
// let max = 1;
|
|
4132
4163
|
let roundRobinIndex = 0;
|
|
4133
4164
|
const processObject = (sha, value) => {
|
|
4134
4165
|
if (max !== undefined && max <= 0) {
|
|
4135
4166
|
return;
|
|
4136
4167
|
}
|
|
4137
|
-
const
|
|
4168
|
+
const pendingCount = this.pendingCountForSha1(sha);
|
|
4169
|
+
const totalCopies = value.nodes.length + pendingCount;
|
|
4170
|
+
const needed = Math.min(redundancy + 1 - totalCopies, this.byNode.size - 1);
|
|
4138
4171
|
if (needed > 0) {
|
|
4139
|
-
const needed2 = redundancy + 1 - value.nodes.length;
|
|
4140
|
-
// console.log("should distribute", key, "to", needed, "nodes");
|
|
4141
4172
|
let firstIdx;
|
|
4142
4173
|
let found = 0;
|
|
4143
|
-
while (found <
|
|
4174
|
+
while (found < needed) {
|
|
4144
4175
|
if (++nodeIdx === nodes.length) {
|
|
4145
4176
|
nodeIdx = 0;
|
|
4146
4177
|
}
|
|
@@ -4154,6 +4185,12 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4154
4185
|
if (value.nodes.indexOf(node) !== -1) {
|
|
4155
4186
|
continue;
|
|
4156
4187
|
}
|
|
4188
|
+
if (this.hasPendingTransfer(sha, node)) {
|
|
4189
|
+
continue;
|
|
4190
|
+
}
|
|
4191
|
+
if (nodeRestriction && node.ip + ":" + node.port !== nodeRestriction) {
|
|
4192
|
+
continue;
|
|
4193
|
+
}
|
|
4157
4194
|
let data = commands.get(node);
|
|
4158
4195
|
let available;
|
|
4159
4196
|
if (data) {
|
|
@@ -4179,7 +4216,6 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4179
4216
|
break;
|
|
4180
4217
|
}
|
|
4181
4218
|
}
|
|
4182
|
-
// console.log("found candidates", candidates.map(node => node.ip + ":" + node.port));
|
|
4183
4219
|
}
|
|
4184
4220
|
};
|
|
4185
4221
|
if (sha1) {
|
|
@@ -4198,14 +4234,16 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4198
4234
|
}
|
|
4199
4235
|
}
|
|
4200
4236
|
commands.forEach((value, key) => {
|
|
4201
|
-
|
|
4202
|
-
if (value.objects.length && (!nodeRestriction || key.ip + ":" + key.port === nodeRestriction)) {
|
|
4237
|
+
if (value.objects.length) {
|
|
4203
4238
|
console.log(`sending ${value.objects.length}/${this.bySHA1.size} fetch_cache_objects to ${key.ip}:${key.port}`);
|
|
4204
4239
|
if (ret) {
|
|
4205
|
-
ret[`${key.ip}:${key.port}`] = value.objects;
|
|
4240
|
+
ret.commands[`${key.ip}:${key.port}`] = value.objects;
|
|
4206
4241
|
}
|
|
4207
4242
|
count += value.objects.length;
|
|
4208
4243
|
if (!dry) {
|
|
4244
|
+
for (const obj of value.objects) {
|
|
4245
|
+
this.addPendingTransfer(obj.sha1, key);
|
|
4246
|
+
}
|
|
4209
4247
|
key.send({ type: "fetch_cache_objects", objects: value.objects });
|
|
4210
4248
|
}
|
|
4211
4249
|
}
|
|
@@ -4213,9 +4251,52 @@ class ObjectCacheManager extends require$$0__default$2["default"] {
|
|
|
4213
4251
|
if (ret && res) {
|
|
4214
4252
|
ret.count = count;
|
|
4215
4253
|
res.send(JSON.stringify(ret, null, 4) + "\n");
|
|
4216
|
-
// } else if (res) {
|
|
4217
4254
|
}
|
|
4218
4255
|
}
|
|
4256
|
+
addPendingTransfer(sha1, node) {
|
|
4257
|
+
const key = pendingKey(sha1, node);
|
|
4258
|
+
this.pendingTransfers.add(key);
|
|
4259
|
+
const timer = setTimeout(() => {
|
|
4260
|
+
this.pendingTransfers.delete(key);
|
|
4261
|
+
this.pendingTransferTimers.delete(key);
|
|
4262
|
+
}, this.pendingTransferTimeoutMs);
|
|
4263
|
+
timer.unref();
|
|
4264
|
+
this.pendingTransferTimers.set(key, timer);
|
|
4265
|
+
}
|
|
4266
|
+
clearPendingTransfer(sha1, node) {
|
|
4267
|
+
const key = pendingKey(sha1, node);
|
|
4268
|
+
this.pendingTransfers.delete(key);
|
|
4269
|
+
const timer = this.pendingTransferTimers.get(key);
|
|
4270
|
+
if (timer) {
|
|
4271
|
+
clearTimeout(timer);
|
|
4272
|
+
this.pendingTransferTimers.delete(key);
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
clearAllPendingForNode(node) {
|
|
4276
|
+
const suffix = ":" + node.ip + ":" + node.port;
|
|
4277
|
+
for (const key of this.pendingTransfers) {
|
|
4278
|
+
if (key.endsWith(suffix)) {
|
|
4279
|
+
this.pendingTransfers.delete(key);
|
|
4280
|
+
const timer = this.pendingTransferTimers.get(key);
|
|
4281
|
+
if (timer) {
|
|
4282
|
+
clearTimeout(timer);
|
|
4283
|
+
this.pendingTransferTimers.delete(key);
|
|
4284
|
+
}
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
pendingCountForSha1(sha1) {
|
|
4289
|
+
let count = 0;
|
|
4290
|
+
for (const key of this.pendingTransfers) {
|
|
4291
|
+
if (key.startsWith(sha1 + ":")) {
|
|
4292
|
+
++count;
|
|
4293
|
+
}
|
|
4294
|
+
}
|
|
4295
|
+
return count;
|
|
4296
|
+
}
|
|
4297
|
+
hasPendingTransfer(sha1, node) {
|
|
4298
|
+
return this.pendingTransfers.has(pendingKey(sha1, node));
|
|
4299
|
+
}
|
|
4219
4300
|
}
|
|
4220
4301
|
|
|
4221
4302
|
class Peak {
|
|
@@ -56039,7 +56120,6 @@ class Server extends require$$0__default$2["default"] {
|
|
|
56039
56120
|
}
|
|
56040
56121
|
this.emit("compile", client);
|
|
56041
56122
|
const remaining = {};
|
|
56042
|
-
client.ws.on("close", (status, reason) => client.emit("close", status, reason));
|
|
56043
56123
|
client.ws.on("error", (err) => client.emit("error", err));
|
|
56044
56124
|
client.ws.on("close", (code, reason) => {
|
|
56045
56125
|
if (remaining.bytes) {
|
|
@@ -57843,7 +57923,7 @@ process.on("uncaughtException", (err) => {
|
|
|
57843
57923
|
});
|
|
57844
57924
|
const monitorsLog = option("monitor-log");
|
|
57845
57925
|
server.on("error", (error) => {
|
|
57846
|
-
throw
|
|
57926
|
+
throw error;
|
|
57847
57927
|
});
|
|
57848
57928
|
let schedulerNpmVersion;
|
|
57849
57929
|
try {
|
|
@@ -58305,7 +58385,6 @@ server.on("listen", (app) => {
|
|
|
58305
58385
|
}
|
|
58306
58386
|
else if (req.query && "distribute" in req.query) {
|
|
58307
58387
|
objectCache.distribute(req.query, res);
|
|
58308
|
-
res.sendStatus(200);
|
|
58309
58388
|
}
|
|
58310
58389
|
else {
|
|
58311
58390
|
const pretty = req.query && req.query.unpretty ? undefined : 4;
|
|
@@ -58605,8 +58684,9 @@ function requestEnvironment(compile) {
|
|
|
58605
58684
|
file = undefined;
|
|
58606
58685
|
// send any new environments to builders
|
|
58607
58686
|
delete pendingEnvironments[hsh];
|
|
58608
|
-
purgeEnvironmentsToMaxSize();
|
|
58687
|
+
return purgeEnvironmentsToMaxSize();
|
|
58609
58688
|
}
|
|
58689
|
+
return undefined;
|
|
58610
58690
|
})
|
|
58611
58691
|
.then(() => {
|
|
58612
58692
|
if (env.last) {
|
|
@@ -58697,20 +58777,24 @@ server.on("compile", (compile) => {
|
|
|
58697
58777
|
if (!filterBuilder(s)) {
|
|
58698
58778
|
return;
|
|
58699
58779
|
}
|
|
58780
|
+
let candidateEnv;
|
|
58781
|
+
for (let i = 0; i < usableEnvs.length; ++i) {
|
|
58782
|
+
if (usableEnvs[i] in s.environments) {
|
|
58783
|
+
candidateEnv = usableEnvs[i];
|
|
58784
|
+
break;
|
|
58785
|
+
}
|
|
58786
|
+
}
|
|
58787
|
+
if (!candidateEnv) {
|
|
58788
|
+
return;
|
|
58789
|
+
}
|
|
58700
58790
|
const builderScore = score(s);
|
|
58701
58791
|
if (!builder ||
|
|
58702
58792
|
builderScore > bestScore ||
|
|
58703
|
-
(builderScore === bestScore &&
|
|
58793
|
+
(builderScore === bestScore && s.lastJob < builder.lastJob)) {
|
|
58704
58794
|
bestScore = builderScore;
|
|
58705
58795
|
builder = s;
|
|
58706
58796
|
foundInCache = true;
|
|
58707
|
-
|
|
58708
|
-
for (let i = 0; i < usableEnvs.length; ++i) {
|
|
58709
|
-
if (usableEnvs[i] in s.environments) {
|
|
58710
|
-
env = usableEnvs[i];
|
|
58711
|
-
break;
|
|
58712
|
-
}
|
|
58713
|
-
}
|
|
58797
|
+
env = candidateEnv;
|
|
58714
58798
|
}
|
|
58715
58799
|
});
|
|
58716
58800
|
}
|
|
@@ -58949,14 +59033,22 @@ server.on("monitor", (client) => {
|
|
|
58949
59033
|
monitors.forEach((monitor) => {
|
|
58950
59034
|
monitor.send(info);
|
|
58951
59035
|
});
|
|
59036
|
+
})
|
|
59037
|
+
.catch((err) => {
|
|
59038
|
+
console.error("Failed to link environments", err);
|
|
58952
59039
|
});
|
|
58953
59040
|
break;
|
|
58954
59041
|
case "unlinkEnvironments":
|
|
58955
|
-
Environments.instance
|
|
59042
|
+
Environments.instance
|
|
59043
|
+
.unlink(message.srcHash, message.targetHash)
|
|
59044
|
+
.then(() => {
|
|
58956
59045
|
const info = { type: "listEnvironments", environments: environmentsInfo() };
|
|
58957
59046
|
monitors.forEach((monitor) => {
|
|
58958
59047
|
monitor.send(info);
|
|
58959
59048
|
});
|
|
59049
|
+
})
|
|
59050
|
+
.catch((err) => {
|
|
59051
|
+
console.error("Failed to unlink environments", err);
|
|
58960
59052
|
});
|
|
58961
59053
|
break;
|
|
58962
59054
|
case "listUsers": {
|
|
@@ -59042,7 +59134,7 @@ server.on("monitor", (client) => {
|
|
|
59042
59134
|
else if (users[message.user].cookieIp !== client.ip) {
|
|
59043
59135
|
throw new Error("Wrong ip address");
|
|
59044
59136
|
}
|
|
59045
|
-
else if (users[message.user].cookieExpiration || 0 <= Date.now()) {
|
|
59137
|
+
else if ((users[message.user].cookieExpiration || 0) <= Date.now()) {
|
|
59046
59138
|
throw new Error("Cookie expired");
|
|
59047
59139
|
}
|
|
59048
59140
|
else {
|