perf: convert promise.all to single query (#9851)

isekai-main
Barış Soner Uşaklı 3 years ago committed by GitHub
parent 7d1c7e0a7b
commit ea04aeded4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,7 +16,20 @@ module.exports = function (module) {
} }
await module.transaction(async (client) => { await module.transaction(async (client) => {
const dataString = JSON.stringify(data); const dataString = JSON.stringify(data);
async function setOne(key) {
if (Array.isArray(key)) {
await helpers.ensureLegacyObjectsType(client, key, 'hash');
await client.query({
name: 'setObjectKeys',
text: `
INSERT INTO "legacy_hash" ("_key", "data")
SELECT k, $2::TEXT::JSONB
FROM UNNEST($1::TEXT[]) vs(k)
ON CONFLICT ("_key")
DO UPDATE SET "data" = "legacy_hash"."data" || $2::TEXT::JSONB`,
values: [key, dataString],
});
} else {
await helpers.ensureLegacyObjectType(client, key, 'hash'); await helpers.ensureLegacyObjectType(client, key, 'hash');
await client.query({ await client.query({
name: 'setObject', name: 'setObject',
@ -28,11 +41,6 @@ module.exports = function (module) {
values: [key, dataString], values: [key, dataString],
}); });
} }
if (Array.isArray(key)) {
await Promise.all(key.map(k => setOne(k)));
} else {
await setOne(key);
}
}); });
}; };
@ -40,8 +48,36 @@ module.exports = function (module) {
if (!keys.length || !data.length) { if (!keys.length || !data.length) {
return; return;
} }
// TODO: single query? await module.transaction(async (client) => {
await Promise.all(keys.map((k, i) => module.setObject(k, data[i]))); keys = keys.slice();
data = data.filter((d, i) => {
if (d.hasOwnProperty('')) {
delete d[''];
}
const keep = !!Object.keys(d).length;
if (!keep) {
keys.splice(i, 1);
}
return keep;
});
if (!keys.length) {
return;
}
await helpers.ensureLegacyObjectsType(client, keys, 'hash');
const dataStrings = data.map(JSON.stringify);
await client.query({
name: 'setObjectBulk',
text: `
INSERT INTO "legacy_hash" ("_key", "data")
SELECT k, d
FROM UNNEST($1::TEXT[], $2::TEXT::JSONB[]) vs(k, d)
ON CONFLICT ("_key")
DO UPDATE SET "data" = "legacy_hash"."data" || EXCLUDED.data`,
values: [keys, dataStrings],
});
});
}; };
module.setObjectField = async function (key, field, value) { module.setObjectField = async function (key, field, value) {
@ -51,7 +87,9 @@ module.exports = function (module) {
await module.transaction(async (client) => { await module.transaction(async (client) => {
const valueString = JSON.stringify(value); const valueString = JSON.stringify(value);
async function setOne(key) { if (Array.isArray(key)) {
await module.setObject(key, { [field]: value });
} else {
await helpers.ensureLegacyObjectType(client, key, 'hash'); await helpers.ensureLegacyObjectType(client, key, 'hash');
await client.query({ await client.query({
name: 'setObjectField', name: 'setObjectField',
@ -63,12 +101,6 @@ module.exports = function (module) {
values: [key, field, valueString], values: [key, field, valueString],
}); });
} }
if (Array.isArray(key)) {
await Promise.all(key.map(k => setOne(k)));
} else {
await setOne(key);
}
}); });
}; };
@ -269,7 +301,19 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) { if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
return; return;
} }
async function delKey(key, fields) {
if (Array.isArray(key)) {
await module.pool.query({
name: 'deleteObjectFieldsKeys',
text: `
UPDATE "legacy_hash"
SET "data" = COALESCE((SELECT jsonb_object_agg("key", "value")
FROM jsonb_each("data")
WHERE "key" <> ALL ($2::TEXT[])), '{}')
WHERE "_key" = ANY($1::TEXT[])`,
values: [key, fields],
});
} else {
await module.pool.query({ await module.pool.query({
name: 'deleteObjectFields', name: 'deleteObjectFields',
text: ` text: `
@ -281,11 +325,6 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
values: [key, fields], values: [key, fields],
}); });
} }
if (Array.isArray(key)) {
await Promise.all(key.map(k => delKey(k, fields)));
} else {
await delKey(key, fields);
}
}; };
module.incrObjectField = async function (key, field) { module.incrObjectField = async function (key, field) {

@ -90,6 +90,13 @@ describe('Hash methods', () => {
assert.deepStrictEqual(result, [{ foo: '1' }, null]); assert.deepStrictEqual(result, [{ foo: '1' }, null]);
}); });
it('should update existing object on second call', async () => {
await db.setObjectBulk(['bulkKey3.5'], [{ foo: '1' }]);
await db.setObjectBulk(['bulkKey3.5'], [{ baz: '2' }]);
const result = await db.getObject('bulkKey3.5');
assert.deepStrictEqual(result, { foo: '1', baz: '2' });
});
it('should not error if object is empty', async () => { it('should not error if object is empty', async () => {
const keys = ['bulkKey5']; const keys = ['bulkKey5'];
const data = [{ }]; const data = [{ }];

Loading…
Cancel
Save