Skip to content

Commit

Permalink
perf(ElasticSearch): simplify IDs handling in SQL queries for sync pe…
Browse files Browse the repository at this point in the history
…rformance
  • Loading branch information
Betree committed Dec 27, 2024
1 parent 0b3a4d2 commit 16c2854
Showing 1 changed file with 40 additions and 2 deletions.
42 changes: 40 additions & 2 deletions server/lib/elastic-search/sync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

import config from 'config';
import { chunk } from 'lodash';
import { chunk, partition } from 'lodash';

import { Op } from '../../models';
import logger from '../logger';
Expand Down Expand Up @@ -53,6 +53,38 @@ async function removeDeletedEntries(indexName: ElasticSearchIndexName, fromDate:
} while (deletedEntries.length === pageSize);
}

/**
* Takes a list like [1,2,3,4,0,165,7,8,9] and simplifies it to [[0,4],[7,9],165]
* to reduce the cost of running SQL queries with large IN clauses.
*/
const simplifyNumbersInRanges = (
ids: number[],
): {
ranges: number[][];
lonelyNumbers: number[];
} => {
// We need the list to be sorted
ids.sort((a, b) => a - b);
const results = ids.reduce(
(ranges, id) => {
const lastRange = ranges[ranges.length - 1];
if (Array.isArray(lastRange) && lastRange[1] + 1 === id) {
lastRange[1] = id; // Extend the range
} else if (typeof lastRange === 'number' && id === lastRange + 1) {
ranges[ranges.length - 1] = [lastRange, id]; // Convert the number to a range
} else {
ranges.push(id); // Add a lonely number
}

return ranges;
},
[] as (number | number[])[],
);

const [lonelyNumbers, ranges] = partition(results, x => typeof x === 'number');
return { ranges, lonelyNumbers };
};

export async function restoreUndeletedEntries(indexName: ElasticSearchIndexName, { log = false } = {}) {
const client = getElasticSearchClient({ throwIfUnavailable: true });
const adapter = ElasticSearchModelsAdapters[indexName];
Expand Down Expand Up @@ -85,10 +117,16 @@ export async function restoreUndeletedEntries(indexName: ElasticSearchIndexName,
/* eslint-enable camelcase */

// Search for entries that are not marked as deleted in the database
const { ranges, lonelyNumbers } = simplifyNumbersInRanges(allIds.map(Number));
const undeletedEntries = (await adapter.getModel().findAll({
attributes: ['id'],
where: { id: { [Op.not]: allIds } },
raw: true,
where: {
[Op.and]: [
{ id: { [Op.notIn]: lonelyNumbers } },
...ranges.map(([start, end]) => ({ id: { [Op.notBetween]: [start, end] } })),
],
},
})) as unknown as Array<{ id: number }>;

if (!undeletedEntries.length) {
Expand Down

0 comments on commit 16c2854

Please sign in to comment.