firebase-tools
Version:
Command-Line Interface for Firebase
115 lines (114 loc) • 3.96 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkQueue = void 0;
const utils = require("../utils");
const error_1 = require("../error");
const emulatorLogger_1 = require("./emulatorLogger");
const types_1 = require("./types");
class WorkQueue {
constructor(mode = types_1.FunctionsExecutionMode.AUTO, maxParallelWork = WorkQueue.DEFAULT_MAX_PARALLEL) {
this.mode = mode;
this.maxParallelWork = maxParallelWork;
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
this.queue = [];
this.running = [];
this.notifyQueue = () => {
};
this.notifyWorkFinish = () => {
};
this.stopped = true;
if (maxParallelWork < 1) {
throw new error_1.FirebaseError(`Cannot run Functions emulator with less than 1 parallel worker (${WorkQueue.MAX_PARALLEL_ENV}=${process.env[WorkQueue.MAX_PARALLEL_ENV]})`);
}
}
submit(entry) {
this.queue.push(entry);
this.notifyQueue();
this.logState();
}
async start() {
if (!this.stopped) {
return;
}
this.stopped = false;
while (!this.stopped) {
if (!this.queue.length) {
await new Promise((res) => {
this.notifyQueue = res;
});
}
if (this.running.length >= this.maxParallelWork) {
this.logger.logLabeled("DEBUG", "work-queue", `waiting for work to finish (running=${this.running})`);
await new Promise((res) => {
this.notifyWorkFinish = res;
});
}
const workPromise = this.runNext();
if (this.mode === types_1.FunctionsExecutionMode.SEQUENTIAL) {
await workPromise;
}
}
}
stop() {
this.stopped = true;
}
async flush(timeoutMs = 60000) {
if (!this.isWorking()) {
return;
}
this.logger.logLabeled("BULLET", "functions", "Waiting for all functions to finish...");
return new Promise((res, rej) => {
const delta = 100;
let elapsed = 0;
const interval = setInterval(() => {
elapsed += delta;
if (elapsed >= timeoutMs) {
rej(new Error(`Functions work queue not empty after ${timeoutMs}ms`));
}
if (!this.isWorking()) {
clearInterval(interval);
res();
}
}, delta);
});
}
getState() {
return {
queuedWork: this.queue.map((work) => work.type),
queueLength: this.queue.length,
runningWork: this.running,
workRunningCount: this.running.length,
};
}
isWorking() {
const state = this.getState();
return state.queueLength > 0 || state.workRunningCount > 0;
}
async runNext() {
const next = this.queue.shift();
if (next) {
this.running.push(next.type || "anonymous");
this.logState();
try {
await next();
}
catch (e) {
this.logger.log("DEBUG", e);
}
finally {
const index = this.running.indexOf(next.type || "anonymous");
if (index !== -1) {
this.running.splice(index, 1);
}
this.notifyWorkFinish();
this.logState();
}
}
}
logState() {
this.logger.logLabeled("DEBUG", "work-queue", JSON.stringify(this.getState()));
}
}
exports.WorkQueue = WorkQueue;
WorkQueue.MAX_PARALLEL_ENV = "FUNCTIONS_EMULATOR_PARALLEL";
WorkQueue.DEFAULT_MAX_PARALLEL = Number.parseInt(utils.envOverride(WorkQueue.MAX_PARALLEL_ENV, "50"));