问题
在同一事件上反复触发云功能。 看起来当函数超时时,重试该函数 一遍又一遍?虽然选择"重试失败" gui中没有启用?
问题
你能保证一个函数只在同一个事件上被触发一次吗? 它可能与问题跟踪器中的这些事件有关?:
更多细节
我有一个云功能,可以在云存储桶中上传文件。该函数将音频文件转换为flac文件。
使用小文件全部按预期工作。有了更大的文件,事情变得非常奇怪。多次触发云功能反复对同一存储事件做出反应。我很困惑如何发生这种情况,因为我认为如果一个函数对事件做出反应,它将不再被触发。
该交易是当触发该功能时,该功能需要一段时间才能执行其转换。似乎在那个时候,创建了新的功能来对事件作出反应。
那么可以确保只触发一次云功能吗?
这是stackdriver的日志文件
小文件上传(全部好)
更大的文件(多次调用)
这表明在第一次调用完成后,该函数在一分钟内被触发两次?
搜索该事件,我看到它一次又一次地回来:
这是我对触发器作出反应的代码:
const functions = require("firebase-functions");
const path = require("path");
const fs = require("fs");
const os = require("os");
const gcs = require("@google-cloud/storage")();
const ffmpeg = require("fluent-ffmpeg");
const ffmpeg_static = require("ffmpeg-static");
const speech = require("@google-cloud/speech")();
const Promise = require("bluebird");
//Helper function for changing the ffmpeg command into a promise so that the function will properly wait for it to finish.
//Source: https://github.com/fluent-ffmpeg/node-fluent-ffmpeg/issues/710
function promisifyCommand(command) {
return new Promise(cb => {
command
.on("end", () => {
cb(null);
})
.on("error", error => {
cb(error);
})
.run();
});
}
exports.extractAudio = functions.storage
.bucket("ulc-transcribe.appspot.com")
.object()
.onChange(event => {
const object = event.data;
const filePath = object.name;
const fileName = path.basename(filePath);
const fileBucket = object.bucket;
const bucket = gcs.bucket(fileBucket);
const audioBucket = gcs.bucket("ucl-flac-audio");
const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
console.log("event: ", event);
console.log("path: ", filePath + " name: " + fileName);
// Exit if the file is not an upload
if (!filePath.startsWith("ucl-uploads")) {
console.log("Only uploads need to be converted");
return true;
}
// Exit if the audio is already converted.
if (fileName.endsWith("_output.flac")) {
console.log("Already a converted audio.");
return true;
}
// Exit if this is a move or deletion event.
if (object.resourceState === "not_exists") {
console.log("This is a deletion event.");
return true;
}
// // Exit if file exists but is not new and is only being triggered
// // because of a metadata change.
// if (resourceState === 'exists' && metageneration > 1) {
// console.log('This is a metadata change event.');
// return;
// }
const tempFilePath = path.join(os.tmpdir(), fileName);
const targetTempFileName =
fileName.replace(/\.[^/.]+$/, "") + "_output.flac";
const targetTempFilePath = path.join(os.tmpdir(), targetTempFileName);
const targetStorageFilePath = path.join(targetTempFileName);
console.log("downloading audio file...");
console.log("Filename: " + fileName);
console.log("tempFilePath", tempFilePath);
// Step 1 - Start download
return bucket
.file(filePath)
.download({
destination: tempFilePath
})
.then(() => {
console.log("Audio downloaded locally to", tempFilePath);
//Step 2 - Convert file
console.log("Start conversion:");
command = ffmpeg(tempFilePath)
.setFfmpegPath(ffmpeg_static.path)
.audioChannels(1)
.audioFrequency(16000)
.format("flac")
.output(targetTempFilePath);
command = promisifyCommand(command);
return command
.then(() => {
console.log("Output audio created at", targetTempFilePath);
console.log("Upload file");
return audioBucket
.upload(targetTempFilePath, {
destination: targetStorageFilePath
})
.then(() => {
console.log("Output audio uploaded to", targetStorageFilePath);
// return bucket.upload("/tmp/output.flac", {
// destination: "ucl-flac-audio/test.flac"
// });
//Step 4 - Cleanup fs
console.log("cleanup filesystem");
fs.unlinkSync(tempFilePath);
fs.unlinkSync(targetTempFilePath);
});
})
.catch(err => {
console.log("Error ", +err);
});
});
});