Оптимизация использования памяти при загрузке маппингов
This commit is contained in:
@@ -16,7 +16,7 @@ module.exports = {
|
|||||||
|
|
||||||
//поправить в случае, если были критические изменения в DbCreator или InpxParser
|
//поправить в случае, если были критические изменения в DbCreator или InpxParser
|
||||||
//иначе будет рассинхронизация между сервером и клиентом на уровне БД
|
//иначе будет рассинхронизация между сервером и клиентом на уровне БД
|
||||||
dbVersion: '7',
|
dbVersion: '8',
|
||||||
dbCacheSize: 5,
|
dbCacheSize: 5,
|
||||||
|
|
||||||
maxPayloadSize: 500,//in MB
|
maxPayloadSize: 500,//in MB
|
||||||
|
|||||||
@@ -459,7 +459,6 @@ class DbCreator {
|
|||||||
const config = this.config;
|
const config = this.config;
|
||||||
|
|
||||||
const to = `${from}_book`;
|
const to = `${from}_book`;
|
||||||
const toId = `${from}_id`;
|
|
||||||
|
|
||||||
await db.open({table: from});
|
await db.open({table: from});
|
||||||
await db.create({table: to});
|
await db.create({table: to});
|
||||||
@@ -548,7 +547,7 @@ class DbCreator {
|
|||||||
await saveChunk(chunk);
|
await saveChunk(chunk);
|
||||||
|
|
||||||
processed += chunk.length;
|
processed += chunk.length;
|
||||||
callback({progress: 0.5*processed/fromLength});
|
callback({progress: 0.9*processed/fromLength});
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -562,24 +561,18 @@ class DbCreator {
|
|||||||
await db.close({table: to});
|
await db.close({table: to});
|
||||||
await db.close({table: from});
|
await db.close({table: from});
|
||||||
|
|
||||||
await db.create({table: toId});
|
const idMap = {arr: [], map: []};
|
||||||
|
|
||||||
const chunkSize = 50000;
|
|
||||||
let idRows = [];
|
|
||||||
let proc = 0;
|
|
||||||
for (const [id, value] of bookId2RecId) {
|
for (const [id, value] of bookId2RecId) {
|
||||||
idRows.push({id, value});
|
if (value.length > 1) {
|
||||||
if (idRows.length >= chunkSize) {
|
idMap.map.push([id, value]);
|
||||||
await db.insert({table: toId, rows: idRows});
|
idMap.arr[id] = 0;
|
||||||
idRows = [];
|
} else {
|
||||||
|
idMap.arr[id] = value[0];
|
||||||
proc += chunkSize;
|
|
||||||
callback({progress: 0.5 + 0.5*proc/bookId2RecId.size});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (idRows.length)
|
|
||||||
await db.insert({table: toId, rows: idRows});
|
callback({progress: 1});
|
||||||
await db.close({table: toId});
|
await fs.writeFile(`${this.config.dataDir}/db/${from}_id.map`, JSON.stringify(idMap));
|
||||||
|
|
||||||
bookId2RecId = null;
|
bookId2RecId = null;
|
||||||
utils.freeMemory();
|
utils.freeMemory();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
const fs = require('fs-extra');
|
||||||
//const _ = require('lodash');
|
//const _ = require('lodash');
|
||||||
const LockQueue = require('./LockQueue');
|
const LockQueue = require('./LockQueue');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
@@ -299,29 +300,13 @@ class DbSearcher {
|
|||||||
|
|
||||||
await this.lock.get();
|
await this.lock.get();
|
||||||
try {
|
try {
|
||||||
const db = this.db;
|
const data = await fs.readFile(`${this.config.dataDir}/db/${from}_id.map`, 'utf-8');
|
||||||
const map = new Map();
|
|
||||||
const table = `${from}_id`;
|
|
||||||
|
|
||||||
await db.open({table});
|
const idMap = JSON.parse(data);
|
||||||
let rows = await db.select({table});
|
idMap.arr = new Uint32Array(idMap.arr);
|
||||||
await db.close({table});
|
idMap.map = new Map(idMap.map);
|
||||||
|
|
||||||
for (const row of rows) {
|
this.bookIdMap[from] = idMap;
|
||||||
if (!row.value.length)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (row.value.length > 1)
|
|
||||||
map.set(row.id, row.value);
|
|
||||||
else
|
|
||||||
map.set(row.id, row.value[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bookIdMap[from] = map;
|
|
||||||
|
|
||||||
rows = null;
|
|
||||||
await db.freeMemory();
|
|
||||||
utils.freeMemory();
|
|
||||||
|
|
||||||
return this.bookIdMap[from];
|
return this.bookIdMap[from];
|
||||||
} finally {
|
} finally {
|
||||||
@@ -330,15 +315,21 @@ class DbSearcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fillBookIdMapAll() {
|
async fillBookIdMapAll() {
|
||||||
await this.fillBookIdMap('author');
|
try {
|
||||||
await this.fillBookIdMap('series');
|
await this.fillBookIdMap('author');
|
||||||
await this.fillBookIdMap('title');
|
await this.fillBookIdMap('series');
|
||||||
|
await this.fillBookIdMap('title');
|
||||||
|
} catch (e) {
|
||||||
|
//
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async filterTableIds(tableIds, from, query) {
|
async filterTableIds(tableIds, from, query) {
|
||||||
let result = tableIds;
|
let result = tableIds;
|
||||||
|
|
||||||
//т.к. авторы у книги идут списком, то дополнительно фильтруем
|
//т.к. авторы у книги идут списком (т.е. одна книга относиться сразу к нескольким авторам),
|
||||||
|
//то в выборку по bookId могут попасть авторы, которые отсутствуют в критерии query.author,
|
||||||
|
//поэтому дополнительно фильтруем
|
||||||
if (from == 'author' && query.author && query.author !== '*') {
|
if (from == 'author' && query.author && query.author !== '*') {
|
||||||
const key = `filter-ids-author-${query.author}`;
|
const key = `filter-ids-author-${query.author}`;
|
||||||
let authorIds = await this.getCached(key);
|
let authorIds = await this.getCached(key);
|
||||||
@@ -381,24 +372,25 @@ class DbSearcher {
|
|||||||
await this.putCached(bookKey, bookIds);
|
await this.putCached(bookKey, bookIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//id книг (bookIds) нашли, теперь надо их смаппировать в id таблицы from (авторов, серий, названий)
|
||||||
if (bookIds) {
|
if (bookIds) {
|
||||||
const tableIdsSet = new Set();
|
const tableIdsSet = new Set();
|
||||||
const bookIdMap = await this.fillBookIdMap(from);
|
const idMap = await this.fillBookIdMap(from);
|
||||||
let proc = 0;
|
let proc = 0;
|
||||||
let nextProc = 0;
|
let nextProc = 0;
|
||||||
for (const bookId of bookIds) {
|
for (const bookId of bookIds) {
|
||||||
const tableIdValue = bookIdMap.get(bookId);
|
const tableId = idMap.arr[bookId];
|
||||||
if (!tableIdValue)
|
if (tableId) {
|
||||||
continue;
|
tableIdsSet.add(tableId);
|
||||||
|
|
||||||
if (Array.isArray(tableIdValue)) {
|
|
||||||
for (const tableId of tableIdValue) {
|
|
||||||
tableIdsSet.add(tableId);
|
|
||||||
proc++;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tableIdsSet.add(tableIdValue);
|
|
||||||
proc++;
|
proc++;
|
||||||
|
} else {
|
||||||
|
const tableIdArr = idMap.map.get(bookId);
|
||||||
|
if (tableIdArr) {
|
||||||
|
for (const tableId of tableIdArr) {
|
||||||
|
tableIdsSet.add(tableId);
|
||||||
|
proc++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//прерываемся иногда, чтобы не блокировать Event Loop
|
//прерываемся иногда, чтобы не блокировать Event Loop
|
||||||
@@ -409,7 +401,7 @@ class DbSearcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tableIds = Array.from(tableIdsSet);
|
tableIds = Array.from(tableIdsSet);
|
||||||
} else {
|
} else {//bookIds пустой - критерии не заданы, значит берем все id из from
|
||||||
const rows = await db.select({
|
const rows = await db.select({
|
||||||
table: from,
|
table: from,
|
||||||
rawResult: true,
|
rawResult: true,
|
||||||
@@ -419,8 +411,11 @@ class DbSearcher {
|
|||||||
tableIds = rows[0].rawResult;
|
tableIds = rows[0].rawResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//т.к. авторы у книги идут списком, то дополнительно фильтруем
|
||||||
tableIds = await this.filterTableIds(tableIds, from, query);
|
tableIds = await this.filterTableIds(tableIds, from, query);
|
||||||
|
|
||||||
|
//сортируем по id
|
||||||
|
//порядок id соответствует ASC-сортировке по строковому значению из from (имя автора, назание серии, название книги)
|
||||||
tableIds.sort((a, b) => a - b);
|
tableIds.sort((a, b) => a - b);
|
||||||
|
|
||||||
await this.putCached(tableKey, tableIds);
|
await this.putCached(tableKey, tableIds);
|
||||||
|
|||||||
Reference in New Issue
Block a user