purge: erase only finished outbox rows; cleanup waits for unfinished events
This commit is contained in:
@@ -296,6 +296,21 @@ async fn seed(pool: &SqlitePool, parts: &sqlite::SqliteWireOutput) -> Seeded {
|
||||
"done",
|
||||
format!(r#"{{"user_id":"{b_str}"}}"#),
|
||||
),
|
||||
(
|
||||
"FollowRequested",
|
||||
"pending",
|
||||
format!(r#"{{"follower_id":"{b_str}","target":"{a_str}"}}"#),
|
||||
),
|
||||
(
|
||||
"ActorBlocked",
|
||||
"processing",
|
||||
format!(r#"{{"user_id":"{b_str}","actor":"{a_str}"}}"#),
|
||||
),
|
||||
(
|
||||
"Unfollowed",
|
||||
"done",
|
||||
format!(r#"{{"follower_id":"{b_str}","target":"{a_str}"}}"#),
|
||||
),
|
||||
] {
|
||||
sqlx::query("INSERT INTO event_queue (event_type, payload, status) VALUES (?, ?, ?)")
|
||||
.bind(event_type)
|
||||
@@ -385,8 +400,10 @@ async fn purge_removes_every_user_owned_row() {
|
||||
),
|
||||
("UserDeleted".to_string(), "processing".to_string()),
|
||||
("ReviewLogged".to_string(), "done".to_string()),
|
||||
("FollowRequested".to_string(), "pending".to_string()),
|
||||
("ActorBlocked".to_string(), "processing".to_string()),
|
||||
],
|
||||
"the user's authored events and activities are gone; the pending Delete, the trigger and bob's event stay"
|
||||
"finished rows mentioning the user are gone; pending/processing rows (incl. other users' events about her) and bob's own rows stay"
|
||||
);
|
||||
|
||||
// The other account is untouched.
|
||||
@@ -657,12 +674,44 @@ async fn tombstone_cleanup_removes_rows_older_than_retention_and_keeps_younger_o
|
||||
assert_eq!(removed, 0);
|
||||
assert_eq!(count_for(&pool, "users", "id", &s.user).await, 1);
|
||||
|
||||
// Unfinished events still name the user (the pending Delete, bob's
|
||||
// pending FollowRequested, a processing row): the tombstone waits and no
|
||||
// unfinished row is ever deleted.
|
||||
let alice = s.user.value().to_string();
|
||||
let unfinished = || async {
|
||||
sqlx::query_scalar::<_, i64>(
|
||||
"SELECT COUNT(*) FROM event_queue WHERE status IN ('pending', 'processing')",
|
||||
)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.unwrap()
|
||||
};
|
||||
let before = unfinished().await;
|
||||
assert!(before >= 3);
|
||||
let removed = p
|
||||
.purge_expired_tombstones(now - chrono::Duration::days(7))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(removed, 0, "an unfinished event still names the user");
|
||||
assert_eq!(count_for(&pool, "users", "id", &s.user).await, 1);
|
||||
assert_eq!(unfinished().await, before);
|
||||
|
||||
// Once they finish, the cleanup takes the tombstone and the history.
|
||||
exec(&pool, "UPDATE event_queue SET status = 'done'", &[]).await;
|
||||
let removed = p
|
||||
.purge_expired_tombstones(now - chrono::Duration::days(7))
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(removed, 1);
|
||||
assert_eq!(count_for(&pool, "users", "id", &s.user).await, 0);
|
||||
let left: Vec<String> = sqlx::query_scalar("SELECT payload FROM event_queue")
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(
|
||||
left.iter().all(|p| !p.contains(&alice)),
|
||||
"no history of the user remains: {left:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
count_for(&pool, "ap_local_actors", "user_id", &s.user).await,
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user