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) => {
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 client.query({
name: 'setObject',
@ -28,11 +41,6 @@ module.exports = function (module) {
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) {
return;
}
// TODO: single query?
await Promise.all(keys.map((k, i) => module.setObject(k, data[i])));
await module.transaction(async (client) => {
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) {
@ -51,7 +87,9 @@ module.exports = function (module) {
await module.transaction(async (client) => {
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 client.query({
name: 'setObjectField',
@ -63,12 +101,6 @@ module.exports = function (module) {
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) {
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({
name: 'deleteObjectFields',
text: `
@ -281,11 +325,6 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
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) {

@ -90,6 +90,13 @@ describe('Hash methods', () => {
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 () => {
const keys = ['bulkKey5'];
const data = [{ }];

Loading…
Cancel
Save