Я запускаю автоматизированный процесс, который загружает большой zip-файл, извлекает из этого zip-файла один XML-файл, анализирует соответствующую информацию из XML и затем использует эту проанализированную информацию в другой области.
Этобыл мой оригинальный процесс для загрузки:
import { ParseOne } from 'unzipper';
import Parser from 'node-xml-stream-parser';
import * as http from 'http';
async function download() {
const stream = await fetchAsStream(url);
const textFileStream = stream.pipe(ParseOne(filenameInsideZip));
const parser = new Parser();
textFileStream.pipe(parser);
// add event handlers to parser
await new Promise(resolve => parser.on('finish', resolve));
}
async function fetchAsStream(url) {
return new Promise((resolve, reject) => {
return http.get(url, res => {
if (res.statusCode === 200) resolve(res);
else reject(new Error(`Failed with status code ${res.statusCode}`));
});
});
}
Файл внутри почтового индекса составляет около 170 МБ.Процесс, описанный выше, работал отлично, хотя и немного медленно (около 3 минут).
Однако позже в процессе я использую проанализированные данные для генерации некоторых сценариев SQL.Я не хотел, чтобы отдельные сценарии были слишком большими, поэтому я записываю в файл каждые 10 КБ, а не один файл для всего сценария:
import * as fs from 'fs';
async function generateSqlScripts(data) {
let script = '...initial stuff...';
let scriptNumber = 0;
for (const key of Object.keys(data) {
script += `Insert Into blah.blah ${key} ${data[key]} ...`;
if (script.length > 10_000) {
await new Promise((resolve, reject) =>
fs.writeFile(scriptPath + scriptNumber, script, err => err ? reject(err) : resolve())
);
scriptNumber++;
script = '';
}
}
}
И сразу после записи первого файлаПрограмма умирает без ошибок.После некоторого исследования у меня появилась теория, что это как-то связано с тем фактом, что загруженный файл буферизуется в памяти во время обработки, что может привести к сбою программы.
Чтобы решить эту проблему, Я решил, что вместо обработки загруженных данных по мере их поступления, я должен сохранить данные непосредственно во временный файл, чтобы избежать переполнения памяти, а затем я могу выполнить обработку нового потока из этого файла, который может быть обработанмедленно без переполнения памяти.
Моя новая реализация делает это:
import { ParseOne } from 'unzipper';
import Parser from 'node-xml-stream-parser';
import * as http from 'http';
import * as os from 'os';
import * as fs from 'fs';
import * as path from 'path';
async function download() {
const stream = await fetchAsStream(url);
const textFileStream = stream.pipe(ParseOne(filenameInsideZip));
// NEW STUFF
const tmpfile = path.join(os.tmpdir(), somePath);
const writeStream = textFileStream.pipe(fs.createWriteStream(tmpfile, { flags: 'w' });
await new Promise((resolve, reject) => {
writeStream.on('end', resolve);
writeStream.on('error', reject);
});
const readStream = fs.createReadStream(tmpfile);
// END NEW STUFF
const parser = new Parser();
readStream.pipe(parser);
// add event handlers to parser
await new Promise(resolve => parser.on('finish', resolve));
}
async function fetchAsStream(url) {
return new Promise((resolve, reject) => {
return http.get(url, res => {
if (res.statusCode === 200) resolve(res);
else reject(new Error(`Failed with status code ${res.statusCode}`));
});
});
}
И по какой-то причине, после того как временный файл закончил запись (я убедился, что это работает нормально), программа завершает работубез кода выхода и сообщения об ошибке.
Я прикрепил к процессу uncaughtException
и unhandledRejection
события и даже добавил хук в process.exit()
, чтобы посмотреть, вызывается ли это где-нибудь.И все же я ничего не получаю.
Я считаю себя примерно на уровне 7/10, когда дело доходит до Node, так что это насколько я знаю.
Кто-нибудь знает, что можетбыть не прав с этим процессом, который может быть причиной этих сбоев, или есть какие-либо советы о том, как отладить эту проблему?Я в полной растерянности.