Преглед на файлове

Оптимизация использования памяти при загрузке маппингов

Book Pauk преди 2 години
родител
ревизия
d6260e3433
променени са 3 файла, в които са добавени 44 реда и са изтрити 56 реда
  1. 1 1
      server/config/base.js
  2. 10 17
      server/core/DbCreator.js
  3. 33 38
      server/core/DbSearcher.js

+ 1 - 1
server/config/base.js

@@ -16,7 +16,7 @@ module.exports = {
 
     //поправить в случае, если были критические изменения в DbCreator или InpxParser
     //иначе будет рассинхронизация между сервером и клиентом на уровне БД
-    dbVersion: '7',
+    dbVersion: '8',
     dbCacheSize: 5,
 
     maxPayloadSize: 500,//in MB

+ 10 - 17
server/core/DbCreator.js

@@ -459,7 +459,6 @@ class DbCreator {
         const config = this.config;
 
         const to = `${from}_book`;
-        const toId = `${from}_id`;
 
         await db.open({table: from});
         await db.create({table: to});
@@ -548,7 +547,7 @@ class DbCreator {
                 await saveChunk(chunk);
 
                 processed += chunk.length;
-                callback({progress: 0.5*processed/fromLength});
+                callback({progress: 0.9*processed/fromLength});
             } else
                 break;
 
@@ -562,24 +561,18 @@ class DbCreator {
         await db.close({table: to});
         await db.close({table: from});
 
-        await db.create({table: toId});
-
-        const chunkSize = 50000;
-        let idRows = [];
-        let proc = 0;
+        const idMap = {arr: [], map: []};
         for (const [id, value] of bookId2RecId) {
-            idRows.push({id, value});
-            if (idRows.length >= chunkSize) {
-                await db.insert({table: toId, rows: idRows});
-                idRows = [];
-
-                proc += chunkSize;
-                callback({progress: 0.5 + 0.5*proc/bookId2RecId.size});
+            if (value.length > 1) {
+                idMap.map.push([id, value]);
+                idMap.arr[id] = 0;
+            } else {
+                idMap.arr[id] = value[0];
             }
         }
-        if (idRows.length)
-            await db.insert({table: toId, rows: idRows});
-        await db.close({table: toId});
+
+        callback({progress: 1});
+        await fs.writeFile(`${this.config.dataDir}/db/${from}_id.map`, JSON.stringify(idMap));
 
         bookId2RecId = null;
         utils.freeMemory();

+ 33 - 38
server/core/DbSearcher.js

@@ -1,3 +1,4 @@
+const fs = require('fs-extra');
 //const _ = require('lodash');
 const LockQueue = require('./LockQueue');
 const utils = require('./utils');
@@ -299,29 +300,13 @@ class DbSearcher {
 
         await this.lock.get();
         try {
-            const db = this.db;
-            const map = new Map();
-            const table = `${from}_id`;
-
-            await db.open({table});
-            let rows = await db.select({table});
-            await db.close({table});
-
-            for (const row of rows) {
-                if (!row.value.length)
-                    continue;
+            const data = await fs.readFile(`${this.config.dataDir}/db/${from}_id.map`, 'utf-8');
 
-                if (row.value.length > 1)
-                    map.set(row.id, row.value);
-                else
-                    map.set(row.id, row.value[0]);
-            }
-
-            this.bookIdMap[from] = map;
+            const idMap = JSON.parse(data);
+            idMap.arr = new Uint32Array(idMap.arr);
+            idMap.map = new Map(idMap.map);
 
-            rows = null;
-            await db.freeMemory();
-            utils.freeMemory();
+            this.bookIdMap[from] = idMap;
 
             return this.bookIdMap[from];
         } finally {
@@ -330,15 +315,21 @@ class DbSearcher {
     }
 
     async fillBookIdMapAll() {
-        await this.fillBookIdMap('author');
-        await this.fillBookIdMap('series');
-        await this.fillBookIdMap('title');
+        try {
+            await this.fillBookIdMap('author');
+            await this.fillBookIdMap('series');
+            await this.fillBookIdMap('title');
+        } catch (e) {
+            //
+        }
     }
 
     async filterTableIds(tableIds, from, query) {
         let result = tableIds;
 
-        //т.к. авторы у книги идут списком, то дополнительно фильтруем
+        //т.к. авторы у книги идут списком (т.е. одна книга относиться сразу к нескольким авторам),
+        //то в выборку по bookId могут попасть авторы, которые отсутствуют в критерии query.author,
+        //поэтому дополнительно фильтруем
         if (from == 'author' && query.author && query.author !== '*') {
             const key = `filter-ids-author-${query.author}`;
             let authorIds = await this.getCached(key);
@@ -381,24 +372,25 @@ class DbSearcher {
                 await this.putCached(bookKey, bookIds);
             }
 
+            //id книг (bookIds) нашли, теперь надо их смаппировать в id таблицы from (авторов, серий, названий)
             if (bookIds) {
                 const tableIdsSet = new Set();
-                const bookIdMap = await this.fillBookIdMap(from);
+                const idMap = await this.fillBookIdMap(from);
                 let proc = 0;
                 let nextProc = 0;
                 for (const bookId of bookIds) {
-                    const tableIdValue = bookIdMap.get(bookId);
-                    if (!tableIdValue)
-                        continue;
-
-                    if (Array.isArray(tableIdValue)) {
-                        for (const tableId of tableIdValue) {
-                            tableIdsSet.add(tableId);
-                            proc++;
-                        }
-                    } else {
-                        tableIdsSet.add(tableIdValue);
+                    const tableId = idMap.arr[bookId];
+                    if (tableId) {
+                        tableIdsSet.add(tableId);
                         proc++;
+                    } else {
+                        const tableIdArr = idMap.map.get(bookId);
+                        if (tableIdArr) {
+                            for (const tableId of tableIdArr) {
+                                tableIdsSet.add(tableId);
+                                proc++;
+                            }
+                        }
                     }
 
                     //прерываемся иногда, чтобы не блокировать Event Loop
@@ -409,7 +401,7 @@ class DbSearcher {
                 }
 
                 tableIds = Array.from(tableIdsSet);
-            } else {
+            } else {//bookIds пустой - критерии не заданы, значит берем все id из from
                 const rows = await db.select({
                     table: from,
                     rawResult: true,
@@ -419,8 +411,11 @@ class DbSearcher {
                 tableIds = rows[0].rawResult;
             }
 
+            //т.к. авторы у книги идут списком, то дополнительно фильтруем
             tableIds = await this.filterTableIds(tableIds, from, query);
 
+            //сортируем по id
+            //порядок id соответствует ASC-сортировке по строковому значению из from (имя автора, назание серии, название книги)
             tableIds.sort((a, b) => a - b);
 
             await this.putCached(tableKey, tableIds);