fix: #8954, clear purged replies and toPids (#8959)

* fix: #8954, clear purged replies and toPids

* fix: redis test
v1.18.x
Barış Soner Uşaklı 4 years ago committed by GitHub
parent 39dae0aaff
commit 5bb5ec4618
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -149,7 +149,7 @@ module.exports = function (module) {
};
module.deleteObjectFields = async function (key, fields) {
if (!key || !Array.isArray(fields) || !fields.length) {
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
return;
}
fields = fields.filter(Boolean);
@ -162,8 +162,12 @@ module.exports = function (module) {
field = helpers.fieldToString(field);
data[field] = '';
});
if (Array.isArray(key)) {
await module.client.collection('objects').updateMany({ _key: { $in: key } }, { $unset: data });
} else {
await module.client.collection('objects').updateOne({ _key: key }, { $unset: data });
}
cache.del(key);
};

@ -247,10 +247,10 @@ SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
};
module.deleteObjectFields = async function (key, fields) {
if (!key || !Array.isArray(fields) || !fields.length) {
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
return;
}
async function delKey(key, fields) {
await module.pool.query({
name: 'deleteObjectFields',
text: `
@ -261,6 +261,12 @@ UPDATE "legacy_hash"
WHERE "_key" = $1::TEXT`,
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) {

@ -150,14 +150,21 @@ module.exports = function (module) {
};
module.deleteObjectFields = async function (key, fields) {
if (!key || !Array.isArray(fields) || !fields.length) {
if (!key || (Array.isArray(key) && !key.length) || !Array.isArray(fields) || !fields.length) {
return;
}
fields = fields.filter(Boolean);
if (!fields.length) {
return;
}
if (Array.isArray(key)) {
const batch = module.client.batch();
key.forEach(k => batch.hdel(k, fields));
await helpers.execBatch(batch);
} else {
await module.client.async.hdel(key, fields);
}
cache.del(key);
};

@ -122,13 +122,18 @@ module.exports = function (Posts) {
}
async function deletePostFromReplies(postData) {
if (!parseInt(postData.toPid, 10)) {
return;
const replyPids = await db.getSortedSetMembers('pid:' + postData.pid + ':replies');
const promises = [
db.deleteObjectFields(
replyPids.map(pid => 'post:' + pid), ['toPid']
),
db.delete('pid:' + postData.pid + ':replies'),
];
if (parseInt(postData.toPid, 10)) {
promises.push(db.sortedSetRemove('pid:' + postData.toPid + ':replies', postData.pid));
promises.push(db.decrObjectField('post:' + postData.toPid, 'replies'));
}
await Promise.all([
db.sortedSetRemove('pid:' + postData.toPid + ':replies', postData.pid),
db.decrObjectField('post:' + postData.toPid, 'replies'),
]);
await Promise.all(promises);
}
async function deletePostFromGroups(postData) {

@ -0,0 +1,33 @@
'use strict';
const _ = require('lodash');
const db = require('../../database');
const batch = require('../../batch');
module.exports = {
name: 'Clear purged replies and toPid',
timestamp: Date.UTC(2020, 10, 26),
method: async function () {
const progress = this.progress;
await batch.processSortedSet('posts:pid', async function (pids) {
progress.incr(pids.length);
let postData = await db.getObjects(pids.map(pid => 'post:' + pid));
postData = postData.filter(p => p && parseInt(p.toPid, 10));
if (!postData.length) {
return;
}
const toPids = postData.map(p => p.toPid);
const exists = await db.exists(toPids.map(pid => 'post:' + pid));
const pidsToDelete = postData.filter((p, index) => !exists[index]).map(p => p.pid);
await db.deleteObjectFields(pidsToDelete.map(pid => 'post:' + pid), ['toPid']);
const repliesToDelete = _.uniq(toPids.filter((pid, index) => !exists[index]));
await db.deleteAll(repliesToDelete.map(pid => 'pid:' + pid + ':replies'));
}, {
progress: progress,
batchSize: 500,
});
},
};

@ -415,6 +415,16 @@ describe('Hash methods', function () {
});
});
it('should delete multiple fields of multiple objects', async function () {
await db.setObject('deleteFields1', { foo: 'foo1', baz: '2' });
await db.setObject('deleteFields2', { foo: 'foo2', baz: '3' });
await db.deleteObjectFields(['deleteFields1', 'deleteFields2'], ['baz']);
const obj1 = await db.getObject('deleteFields1');
const obj2 = await db.getObject('deleteFields2');
assert.deepStrictEqual(obj1, { foo: 'foo1' });
assert.deepStrictEqual(obj2, { foo: 'foo2' });
});
it('should not error if fields is empty array', async () => {
await db.deleteObjectFields('someKey', []);
});

@ -190,6 +190,22 @@ describe('Topic\'s', function () {
done();
});
});
it('should delete nested relies properly', async function () {
const result = await topics.post({ uid: fooUid, title: 'nested test', content: 'main post', cid: topic.categoryId });
const reply1 = await topics.reply({ uid: fooUid, content: 'reply post 1', tid: result.topicData.tid });
const reply2 = await topics.reply({ uid: fooUid, content: 'reply post 2', tid: result.topicData.tid, toPid: reply1.pid });
let replies = await socketPosts.getReplies({ uid: fooUid }, reply1.pid);
assert.strictEqual(replies.length, 1);
assert.strictEqual(replies[0].content, 'reply post 2');
let toPid = await posts.getPostField(reply2.pid, 'toPid');
assert.strictEqual(parseInt(toPid, 10), parseInt(reply1.pid, 10));
await posts.purge(reply1.pid, fooUid);
replies = await socketPosts.getReplies({ uid: fooUid }, reply1.pid);
assert.strictEqual(replies.length, 0);
toPid = await posts.getPostField(reply2.pid, 'toPid');
assert.strictEqual(toPid, null);
});
});
describe('Get methods', function () {

Loading…
Cancel
Save