多年来我一直在使用firebase-queue
来调用作业,但是最近它开始失败,因为交易错误在资源上竞争,并且存在严重的扩展问题。
因此,我将firebase-queue
迁移到了云功能,并且起初它似乎是快速且可扩展的。我写了一个简单的脚本,该脚本由queue/task/{taskId}
上的onWrite连接。但是似乎有时候onWrite不会被随机触发。我根本看不到日志中触发的特定作业。
我们正在使用大火计划,在函数上返回promise,将时限设置为最大值,99.9%的作业占用的时间少于5000ms。
我们每天要调用5万次,并且不会触发约100个工作。这是如此断断续续,对此一无所知。一件奇怪的事是我在日志查看器上没有看到任何错误,但是在调用图上,我可以看到一些加载错误和连接错误,但仍然不知道它们是什么。任何解决该问题的见识将不胜感激。
const functions = require(`firebase-functions`);
const admin = require(`firebase-admin`);
const _ = require(`lodash`);
const specs = require(`./specs.json`);
const queueSpecs = require(`./queue-specs.js`);
const SERVER_TIMESTAMP = { ".sv": `timestamp` };
admin.initializeApp();
const workers = {};
queueSpecs.forEach((spec) => {
let specString = spec;
if (typeof spec === `object`) {
specString = spec.id;
}
workers[specString] = require(`./lib/workers/${specString}`);
});
const taskHandler = (change, context) => {
const data = change.after.val();
const ref = change.after.ref;
const { taskID } = context.params;
if (!data) {
return null;
}
const worker = workers[data._state];
if (!worker) {
console.error(`Worker not found for task ${taskID} - ${data._state}`);
return null;
}
const specID = data._state.replace(`/`, `_`);
const spec = specs[specID];
if (!spec) {
console.error(`Spec not found for task ${taskID} - ${specID}`);
return null;
}
console.log(
`Running ${taskID} - ${data._state} - ${JSON.stringify(data, null, 2)}`
);
return new Promise((resolve, reject) => {
const taskResolve = newTask => {
const updates = _.clone(newTask);
let newState = _.get(newTask, `_new_state`, ``);
newState = newState || spec.finished_state;
if (!updates || !newState) {
console.log(`Exiting task - ${taskID}`);
return ref.set(null).then(resolve, reject);
}
updates._state = newState || spec.finished_state;
updates._state_changed = SERVER_TIMESTAMP;
updates._owner = null;
updates._progress = null;
updates._error_details = null;
console.log(`Resolving`);
return ref.update(updates).then(resolve, reject);
};
const taskReject = error => {
const updates = {};
let errorString;
if (_.isError(error)) {
errorString = error.message;
} else if (_.isString(error)) {
errorString = error;
} else if (!_.isUndefined(error) && !_.isNull(error)) {
errorString = error.toString();
}
if (updates._state === `error`) {
console.log(`Exiting task on reject - ${taskID}`);
return ref.set(null).then(resolve, reject);
}
updates._state = spec.error_state || `error`;
updates._state_changed = SERVER_TIMESTAMP;
updates._owner = null;
updates._progress = null;
updates._error_details = {
previous_state: spec.in_progress_state,
error: errorString,
errorStack: _.get(error, `stack`, null),
attempts: 1
};
console.log(`Rejecting`);
return ref.update(updates).then(resolve, reject);
};
const taskProgress = () => {
// eslint-disable-line
// progress is never used, thus just resolving
const updates = {};
console.log(`Progress ????`);
return ref.update(updates).then(resolve, reject);
};
worker(data, taskProgress, taskResolve, taskReject);
});
};
exports.taskRunner = functions.database
.ref(`/queue/tasks/{taskID}`)
.onWrite(taskHandler);