Проблемы с памятью в TypeORM, или я просто ее заполняю

#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 });