idempotent event deletion, prevent levId reuse when latest event is replaced or deleted

This commit is contained in:
Doug Hoyte
2023-04-30 13:47:01 -04:00
parent d4290d8a4f
commit 371f95bce3
5 changed files with 47 additions and 33 deletions

View File

@ -57,10 +57,7 @@ void RelayServer::runCron() {
uint64_t numDeleted = 0;
for (auto levId : expiredLevIds) {
auto view = env.lookup_Event(txn, levId);
if (!view) continue; // Deleted in between transactions
deleteEvent(txn, *view);
numDeleted++;
if (deleteEvent(txn, levId)) numDeleted++;
}
txn.commit();
@ -114,10 +111,7 @@ void RelayServer::runCron() {
uint64_t numDeleted = 0;
for (auto levId : expiredLevIds) {
auto view = env.lookup_Event(txn, levId);
if (!view) continue; // Deleted in between transactions
deleteEvent(txn, *view);
numDeleted++;
if (deleteEvent(txn, levId)) numDeleted++;
}
txn.commit();

View File

@ -70,9 +70,7 @@ void cmd_delete(const std::vector<std::string> &subArgs) {
auto txn = env.txn_rw();
for (auto levId : levIds) {
auto view = env.lookup_Event(txn, levId);
if (!view) continue; // Deleted in between transactions
deleteEvent(txn, *view);
deleteEvent(txn, levId);
}
txn.commit();

View File

@ -243,9 +243,10 @@ std::string_view getEventJson(lmdb::txn &txn, Decompressor &decomp, uint64_t lev
void deleteEvent(lmdb::txn &txn, defaultDb::environment::View_Event &ev) {
env.dbi_EventPayload.del(txn, lmdb::to_sv<uint64_t>(ev.primaryKeyId));
env.delete_Event(txn, ev.primaryKeyId);
bool deleteEvent(lmdb::txn &txn, uint64_t levId) {
bool deleted = env.dbi_EventPayload.del(txn, lmdb::to_sv<uint64_t>(levId));
env.delete_Event(txn, levId);
return deleted;
}
@ -258,6 +259,7 @@ void writeEvents(lmdb::txn &txn, std::vector<EventToWrite> &evs, uint64_t logLev
return aC < bC;
});
std::vector<uint64_t> levIdsToDelete;
std::string tmpBuf;
for (size_t i = 0; i < evs.size(); i++) {
@ -296,7 +298,7 @@ void writeEvents(lmdb::txn &txn, std::vector<EventToWrite> &evs, uint64_t logLev
if (otherEv.flat_nested()->created_at() < flat->created_at()) {
if (logLevel >= 1) LI << "Deleting event (d-tag). id=" << to_hex(sv(otherEv.flat_nested()->id()));
deleteEvent(txn, otherEv);
levIdsToDelete.push_back(otherEv.primaryKeyId);
} else {
ev.status = EventWriteStatus::Replaced;
}
@ -314,7 +316,7 @@ void writeEvents(lmdb::txn &txn, std::vector<EventToWrite> &evs, uint64_t logLev
auto otherEv = lookupEventById(txn, sv(tagPair->val()));
if (otherEv && sv(otherEv->flat_nested()->pubkey()) == sv(flat->pubkey())) {
if (logLevel >= 1) LI << "Deleting event (kind 5). id=" << to_hex(sv(tagPair->val()));
deleteEvent(txn, *otherEv);
levIdsToDelete.push_back(otherEv->primaryKeyId);
}
}
}
@ -329,6 +331,13 @@ void writeEvents(lmdb::txn &txn, std::vector<EventToWrite> &evs, uint64_t logLev
env.dbi_EventPayload.put(txn, lmdb::to_sv<uint64_t>(ev.levId), tmpBuf);
ev.status = EventWriteStatus::Written;
// Deletions happen after event was written to ensure levIds are not reused
for (auto levId : levIdsToDelete) deleteEvent(txn, levId);
levIdsToDelete.clear();
}
if (levIdsToDelete.size()) throw herr("unprocessed deletion");
}
}

View File

@ -112,4 +112,4 @@ struct EventToWrite {
void writeEvents(lmdb::txn &txn, std::vector<EventToWrite> &evs, uint64_t logLevel = 1);
void deleteEvent(lmdb::txn &txn, defaultDb::environment::View_Event &ev);
bool deleteEvent(lmdb::txn &txn, uint64_t levId);

View File

@ -22,9 +22,8 @@ my $ids = [
## Basic insert
doTest({
desc => "Basic insert",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 },
qq{--sec $ids->[0]->{sec} --content "hi 2" --kind 1 },
@ -32,9 +31,9 @@ doTest({
verify => [ 0, 1, ],
});
## Replacement, newer timestamp
doTest({
desc => "Replacement, newer timestamp",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 10000 --created-at 5000 },
qq{--sec $ids->[0]->{sec} --content "hi 2" --kind 10000 --created-at 5001 },
@ -43,9 +42,9 @@ doTest({
verify => [ 1, ],
});
## Same, but explicit empty d tag
doTest({
desc => "Same, but explicit empty d tag",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 10000 --created-at 5000 },
qq{--sec $ids->[0]->{sec} --content "hi 2" --kind 10000 --created-at 5001 --tag d '' },
@ -54,9 +53,9 @@ doTest({
verify => [ 1, ],
});
## Replacement is dropped
doTest({
desc => "Replacement is dropped",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 10000 --created-at 5001 },
qq{--sec $ids->[0]->{sec} --content "hi 2" --kind 10000 --created-at 5000 },
@ -64,9 +63,9 @@ doTest({
verify => [ 0, ],
});
## Doesn't replace some else's event
doTest({
desc => "Doesn't replace some else's event",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 10000 --created-at 5000 },
qq{--sec $ids->[1]->{sec} --content "hi 2" --kind 10000 --created-at 5001 },
@ -74,9 +73,9 @@ doTest({
verify => [ 0, 1, ],
});
## Doesn't replace different kind
doTest({
desc => "Doesn't replace different kind",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 10001 --created-at 5000 },
qq{--sec $ids->[1]->{sec} --content "hi 2" --kind 10000 --created-at 5001 },
@ -85,9 +84,9 @@ doTest({
});
## Deletion
doTest({
desc => "Deletion",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5000 },
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5001 },
@ -97,9 +96,21 @@ doTest({
verify => [ 1, 3, ],
});
## Can't delete someone else's event
doTest({
desc => "Deletion, duplicate",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5000 },
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5001 },
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5002 },
qq{--sec $ids->[0]->{sec} --content "blah" --kind 5 --created-at 6000 -e EV_2 -e EV_2 },
],
verify => [ 0, 1, 3, ],
});
doTest({
desc => "Can't delete someone else's event",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5000 },
qq{--sec $ids->[1]->{sec} --content "blah" --kind 5 --created-at 6000 -e EV_0 },
@ -107,9 +118,9 @@ doTest({
verify => [ 0, 1, ],
});
## Deletion prevents re-adding same event
doTest({
desc => "Deletion prevents re-adding same event",
events => [
qq{--sec $ids->[0]->{sec} --content "hi" --kind 1 --created-at 5000 },
qq{--sec $ids->[0]->{sec} --content "blah" --kind 5 --created-at 6000 -e EV_0 },
@ -120,9 +131,9 @@ doTest({
## Parameterized Replaceable Events
doTest({
desc => "Parameterized Replaceable Events",
events => [
qq{--sec $ids->[0]->{sec} --content "hi1" --kind 1 --created-at 5000 --tag d myrepl },
qq{--sec $ids->[0]->{sec} --content "hi2" --kind 1 --created-at 5001 --tag d myrepl },
@ -130,9 +141,9 @@ doTest({
verify => [ 1, ],
});
## d tags have to match
doTest({
desc => "d tags have to match",
events => [
qq{--sec $ids->[0]->{sec} --content "hi1" --kind 1 --created-at 5000 --tag d myrepl },
qq{--sec $ids->[0]->{sec} --content "hi2" --kind 1 --created-at 5001 --tag d myrepl2 },
@ -141,9 +152,9 @@ doTest({
verify => [ 1, 2, ],
});
## Kinds have to match
doTest({
desc => "Kinds have to match",
events => [
qq{--sec $ids->[0]->{sec} --content "hi1" --kind 1 --created-at 5000 --tag d myrepl },
qq{--sec $ids->[0]->{sec} --content "hi2" --kind 2 --created-at 5001 --tag d myrepl },
@ -151,9 +162,9 @@ doTest({
verify => [ 0, 1, ],
});
## Pubkeys have to match
doTest({
desc => "Pubkeys have to match",
events => [
qq{--sec $ids->[0]->{sec} --content "hi1" --kind 1 --created-at 5000 --tag d myrepl },
qq{--sec $ids->[1]->{sec} --content "hi2" --kind 1 --created-at 5001 --tag d myrepl },
@ -161,9 +172,9 @@ doTest({
verify => [ 0, 1, ],
});
## Timestamp
doTest({
desc => "Timestamp",
events => [
qq{--sec $ids->[0]->{sec} --content "hi1" --kind 1 --created-at 5001 --tag d myrepl },
qq{--sec $ids->[0]->{sec} --content "hi2" --kind 1 --created-at 5000 --tag d myrepl },
@ -173,12 +184,14 @@ doTest({
print "OK\n";
print "\nOK\n";
sub doTest {
my $spec = shift;
print "* ", ($spec->{desc} || 'unnamed'), "\n";
cleanDb();
my $eventIds = [];