#node.js #typescript #typeorm
#node.js #typescript #typeorm
Вопрос:
Написал скрипт инициализации базы данных на typescript с использованием typeorm. И, похоже, у меня проблема с памятью, но я не могу найти способ обойти это.
В настоящее время скрипт импортирует три файла профилей (55 записей) Вход пользователя (5306 записей) (1006909 записей)
Переписал вызовы, чтобы во всех случаях скрипт создавал JSON со всеми обновлениями, а затем использовал createQueryBuilder
для выполнения обновления, как показано ниже:
getConnection()
.createQueryBuilder()
.insert()
.into(EULogin)
.values(loginChunk)
.execute()
.catch(error => console.log(error))
Для первых работает очарование, но когда дело доходит до последнего (1 000 000 целых файлов), это не сработает. И у меня проблема с памятью
ВХОД в систему: отправлено 196000/1006909 (размер блока: 500) в базу данных за 155 МС ВХОД в систему: отправлено 196500/1006909 (размер блока: 500) в базу данных за 823 МС
<— Последние несколько GCS —>
[60698:0x110008000] 34328 мс: Очистка 1389.1 (1423.6) -> 1388.6 (1423.6) МБ, 12,1 / 0,0 мс (среднее значение mu = 0,104, текущее значение mu = 0,099) сбой выделения [60698: 0x110008000] 34339 мс: очистка 1389.3 (1423.6) -> 1388.9 (1423.6) МБ, 10,4 / 0,0 мс (среднее значение mu = 0,104, текущее значение mu = 0,099) сбой выделения [60698: 0x110008000] 34361 мс: очистка 1389.4 (1423.6) -> 1389.1 (1424.1) МБ, 12,5 / 0,0 мс (среднее значение mu = 0,104, текущее значение mu = 0,099) сбой выделения
<— JS stacktrace —>
==== Трассировка стека JS =========================================
0: ExitFrame [pc: 0x20ed7445be3d] 1: StubFrame [pc: 0x20ed7440d608] 2: StubFrame [pc: 0x20ed7502c4cc] Security context: 0x0fbfb411e6e9 <JSObject> 3: /* anonymous */(aka /* anonymous */) [0xfbf79c62a41] [/Users/bengtbjorkberg/Development/EUGrapherNode/node_modules/typeorm/query-builder/InsertQueryBuilder.js:348]
[байт-код=0xfbf93b851a1 смещение = 26] (this = 0x0fbfab4826f1
,ValueSet=0x0fbf8846ebc1 <Карта объекта = 0xfbfbf7…НЕУСТРАНИМАЯ ОШИБКА: неэффективная отметка-сжатие вблизи предела выделения кучи не удалось — в куче JavaScript не хватает памяти 1: 0x10003cf99 node::Abort() [/usr/local/bin/node] 2: 0x10003d1a3 node::OnFatalError(char const *, char const *) [/usr/ local/bin / node] 3: 0x1001b7835 v8::internal::V8:: FatalProcessOutOfMemory(v8::internal::Isolate *, char const*, bool) [/usr/local/bin/node] 4: 0x100585682 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/usr/local/bin/node] 5: 0x100588155 v8::internal::Heap::Проверка эффективности метки compact(беззнаковый длинный, двойной) [/usr /local/bin/node] 6: 0x100583fff v8::internal::Heap::PerformGarbageCollection(версия 8::internal::GarbageCollector, версия 8::GCCallbackFlags) [/usr/local/bin/node] 7: 0x1005821d4 v8::internal::Heap::CollectGarbage(версия 8::internal::AllocationSpace, версия 8::internal::GarbageCollectionReason, версия 8:: GCCallbackFlags) [/usr/local/bin/node] 8: 0x10058ea6c v8::internal::Heap::allocaterawithligthretry(int, версия 8::internal::AllocationSpace, версия 8::internal::AllocationAlignment) [/usr/local/bin/node] 9: 0x10058eaef v8::internal::Heap::allocaterawithretry или fail(int, версия 8::internal::AllocationSpace, версия 8::внутренняя::Выравнивание распределения) [/usr/local/bin/node] 10: 0x10055e434 v8:: internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [/usr/local/bin/ node] 11: 0x1007e6714 v8::internal::Runtime_AllocateInNewSpace(int, v8::internal::Object **, v8::internal::Isolate *) [/usr/ local/bin/ node] 12: 0x20ed7445be3d
Process finished with exit code 134 (interrupted by signal 6: SIGABRT)
Я попытался открыть базу данных в синхронном режиме (не асинхронном) Я пытался разделить последнее обновление всего на 50 записей за раз, я даже пытался открывать и закрывать базу данных для каждого блока, но это прекратилось, потому что я не мог заставить ее делать это синхронно.
Открытие базы данных с:
createConnection().then(connection => {
А ниже приведен «Загрузчик фрагментов».
.on('end', () => {
let compTime: number = new Date().getTime()
console.log("LOGIN: Entities read: " loginReadCounter " in " new Date(compTime - loginStartTime).getMilliseconds() " MS")
let currentChunk :number = 0;
let chunkSize : number = 500;
let loginChunk = [];
let loginStartChunkTime : number = compTime;
loginEntries.forEach(entry => {
loginChunk.push(entry);
currentChunk ;
loginCommitCounter ;
if (currentChunk === chunkSize !! ){
getConnection()
.createQueryBuilder()
.insert()
.into(EULogin)
.values(loginChunk)
.execute()
.catch(error => console.log(error))
let compTime: number = new Date().getTime()
console.log("LOGIN: Committed " loginCommitCounter "/" loginReadCounter " (Chunk Size:" loginChunk.length ") to database in " new Date(compTime - loginStartChunkTime).getMilliseconds() " MS");
currentChunk = 0;
loginStartChunkTime = compTime;
loginChunk = [];
}
});
Есть идеи?
========================== ОТРЕДАКТИРУЙТЕ СЛЕДУЮЩИЙ ХОРОШИЙ ВВОД ====================
Чтобы попытаться разобраться в своей голове, я перенес ее в отдельную функцию, я приступил await
к работе, но как мне остановить продолжение процесса после вызова. Потому что await работает внутри createConnection, но не работает в createConnection, поэтому функция сразу вернется
function syncDataWrite(dbEntitiy, dataSet){
console.log("DBLOADER Started for: " dataSet.length);
createConnection().then(async connection => {
console.log("DBLOADER Connected!");
const completion = await createQueryBuilder()
.insert()
.into(dbEntitiy)
.values(dataSet)
.execute()
.catch(error => console.log(error))
console.log("DBLOADER SQL uploaded")
})
// console.log(dbEntity);
}
Ответ №1:
Ваш код предполагает, что, пока вы разбиваете что-то на части, вы выполняете все параллельно, что, вероятно, не то, чего вы хотите.
Я бы рекомендовал вам переписать это, чтобы правильно расположить это в последовательности.
Самым простым на сегодняшний день будет переключиться на async / await и использовать обычный for
цикл. Предполагая, что .execute()
возвращает обещание, это будет выглядеть примерно так:
const connection = getConnection();
for (const entry of loginEntries) {
// [snip]
await createQueryBuilder()
.insert()
.into(EULogin)
.values(loginChunk)
.execute()
// [snip]
}
Я удалил кучу вашего кода, но я надеюсь, что эта общая настройка все еще имеет смысл.
Это можно сделать без async / await, но это будет выглядеть намного сложнее.
Редактируйте на основе вашей правки.
Вот переписанная версия syncDataWrite
:
async function syncDataWrite(dbEntitiy, dataSet){
console.log("DBLOADER Started for: " dataSet.length);
const connection = await createConnection();
console.log("DBLOADER Connected!");
const completion = await createQueryBuilder()
.insert()
.into(dbEntitiy)
.values(dataSet)
.execute();
}
Обратите внимание, что если вы используете syncDataWrite
несколько раз, по 1 для каждого фрагмента, вам все равно нужно await
syncDataWrite при его вызове.
Комментарии:
1. Пробовал это, но не могу заставить это работать. Как только я запускаю его, я получаю
Error:(170, 37) TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
. Не уверен, связано ли это с тем, что я делаю это из потока fs или что-то в этом роде. Но похоже, что он запускает ее синхронно2. В конце добавлена функция, и вы правы, но я не могу заставить await работать даже в этом (я думаю) простом примере
3. @vrghost если
syncDataWrite
обрабатывает только один фрагмент, то у вас все еще точно такая же проблема. Вам нужно использоватьawait syncDataWrite()
. Вы также можете сделатьconst connection = await getConnection()
Ответ №2:
Я приложил немало усилий, чтобы исправить эту проблему с производительностью, в конце концов, я заставил ее работать, используя repository вместо QueryBuilder.
Перед:
ActiveRentalEntity.createQueryBuilder('ar')
.insert()
.values(data)
.execute()
После (с использованием репозитория). Это не из-за проблемы с чанком, я попытался разбить его вручную с помощью query builder и получил ту же проблему, похоже, репозиторий лучше оптимизирован:
getRepository(ActiveRentalEntity).save(data, { chunk: 1000 });