Вкратце: у меня есть запланированная функция firebase, которая загружает файл, преобразует его в куски JSON и импортирует его в базу данных в реальном времени. Я дал функции 2 ГБ памяти и 540 секунд (9 минут, максимум), чтобы сделать свою работу, но она все еще заканчивается в тайм-аут около 50% времени. Должна быть утечка, но она, похоже, находится в моем слепом пятне.
Подробно: у меня есть следующая запланированная функция, которая запускается раз в час. Функция вызывает метод updateDatabase
, который, в свою очередь, загружает файл размером около 35 МБ с внешнего сервера (используя метод getNewData
. Этот файл представляет собой разделенный пробелами файл (неважно, ребенок), содержащий 2500 строк и 2500столбцы данных. Мне нужно, чтобы каждая из этих точек данных в базе данных позже быстро их считывала.
Сначала я просто преобразовал это в json и попытался импортировать это в базу данных, так как 35 МБ не показалосьэто большое дело. Однако это заставляло функцию исчерпывать память каждый раз. Поэтому я решил разделить ее на более мелкие части.
Поэтому, когда файл загружен, я сначала разбил его на строки. Затем я перебираю строки, делю их на столбцы и выполняю набор в базе данных реального времени по ссылке /grid/[currentrow]
. Это означает, что я каждый раз вызываю set 2500 раз. Я пытался использовать await
для каждой строки и использовать Promise.all()
(текущая версия, но иногда иногда кажется, что оба зависают. Я не получаю никаких ошибок в журнале, просто тайм-аут, когда 540 сеconds прошли.
Запланированная функция:
exports.scheduledDataUpdate = functions
.region("europe-west1")
.runWith({
timeoutSeconds: 540,
memory: '2GB'
})
.pubsub.schedule("0 * * * *")
.onRun(async () => {
try {
await updateDatabase();
console.log('Database updated');
return true;
} catch(error) {
console.error(error);
return false;
}
});
, которая вызывает метод updateDatabase
:
async function updateDatabase() {
let data;
try {
data = await getNewData().catch(err => console.error(err));
} catch(error) {
console.error(error);
return null;
}
console.log('Data download complete');
const lines = data.split("\n"); // split the data into rows (2500)
lines.forEach((line, r) => {
const cols = line.split(/\s+/); // split the row into columns (2500)
dbUpdates.push(admin.database().ref(`/grid/${r}`).set(cols).then(() => {
if(r > 1 && r % 500 === 0) {
console.log(`updated row ${r}`); // just to get some info in logs whether some rows were processed
}
return true;
}).catch((error) => {
console.error(`Error updating row ${r} -- ${error}`);
}));
return true;
});
return await Promise.all(dbUpdates);
и, если вам интересно, методgetNewData
:
const dataUrl = 'https://some.server/somefile'; // I guess you guess this is different in my code (it is)
async function getNewData() {
console.log('Start download of data');
return await new Promise((resolve, reject) => {
https.get(dataUrl, response => {
if (response.statusCode === 200) {
let data = "";
return response
.on("data", chunk => {
data += chunk;
})
.on("end", () => {
resolve(data);
})
.on("error", error => {
reject(error);
console.error(`error while downloading data: ${data}`);
});
} else {
switch (response.statusCode) {
case 301:
case 302:
case 303:
case 307:
console.warning(`Redirected to ${response.headers.location} [${response.statusCode}]`)
return getNewData(response.headers.location);
default:
return reject(new Error(`Could not download new data (error ${response.statusCode})`));
}
}
})
});
}
Когда я смотрю на квоты, которые предлагает FireBase (я работаю на Blaze, оплачиваю подписку), это действительно не должно быть большой проблемой для запуска,Понятно, что я что-то упускаю или совершаю какую-то глупую ошибку, но для жизни я этого не вижу.
Обновление 1 Пример журнала с тайм-аутом (задано @FrankvanPuffelen)
7:15:03.224 p.m. scheduledDataUpdate Function execution started
7:15:03.546 p.m. scheduledDataUpdate Start download of data
7:15:08.035 p.m. scheduledDataUpdate Data download complete
7:22:28.390 p.m. scheduledDataUpdate updated row 500
7:24:03.244 p.m. scheduledDataUpdate Function execution took 540021 ms, finished with status: 'timeout'