purge: erase only finished outbox rows; cleanup waits for unfinished events
This commit is contained in:
@@ -126,16 +126,16 @@ impl AccountPurger for SqliteAccountPurger {
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
|
||||
// The outbox log: events and signed activities authored as this user.
|
||||
// The `UserDeleted` row (which triggered this run) and the pending
|
||||
// `Delete` deliveries must stay; both go with the tombstone.
|
||||
// The outbox log: finished events and activities authored as, or
|
||||
// mentioning, this user. Unfinished rows are never touched (they
|
||||
// belong to other users' work too and finish or dead-letter on their
|
||||
// own); the tombstone cleanup removes their history later. The
|
||||
// `UserDeleted` row that triggered this run stays until then as well.
|
||||
sqlx::query(
|
||||
"DELETE FROM event_queue
|
||||
WHERE instr(payload, ?1) > 0
|
||||
AND event_type != 'UserDeleted'
|
||||
AND NOT (event_type = 'FederationDeliveryRequested'
|
||||
AND status IN ('pending', 'processing')
|
||||
AND instr(payload, '\"type\":\"Delete\"') > 0)",
|
||||
AND status IN ('done', 'dead_lettered')",
|
||||
)
|
||||
.bind(&id)
|
||||
.execute(&mut *tx)
|
||||
@@ -184,40 +184,42 @@ impl AccountPurger for SqliteAccountPurger {
|
||||
) -> Result<u64, DomainError> {
|
||||
let cutoff = cutoff.format(TIMESTAMP_FORMAT).to_string();
|
||||
let mut tx = self.pool.begin().await.map_err(map_sqlx_error)?;
|
||||
// A tombstone whose id is still named by an unfinished event waits for
|
||||
// the next run: that event may yet be handled, and its history row
|
||||
// must be erased with the rest.
|
||||
let expiring: Vec<String> = sqlx::query_scalar(
|
||||
"SELECT id FROM users WHERE purged_at IS NOT NULL AND purged_at < ?",
|
||||
"SELECT id FROM users u
|
||||
WHERE purged_at IS NOT NULL AND purged_at < ?
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM event_queue q
|
||||
WHERE q.status NOT IN ('done', 'dead_lettered')
|
||||
AND instr(q.payload, u.id) > 0)",
|
||||
)
|
||||
.bind(&cutoff)
|
||||
.fetch_all(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
for id in &expiring {
|
||||
// What the purge had to leave: the `UserDeleted` row and the
|
||||
// `Delete` deliveries.
|
||||
// Includes the `UserDeleted` row and the `Delete` deliveries.
|
||||
sqlx::query("DELETE FROM event_queue WHERE instr(payload, ?) > 0")
|
||||
.bind(id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
}
|
||||
for table in RETAINED_USER_TABLES {
|
||||
sqlx::query(&format!(
|
||||
"DELETE FROM {table} WHERE user_id IN (
|
||||
SELECT id FROM users WHERE purged_at IS NOT NULL AND purged_at < ?)"
|
||||
))
|
||||
.bind(&cutoff)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
}
|
||||
let removed =
|
||||
sqlx::query("DELETE FROM users WHERE purged_at IS NOT NULL AND purged_at < ?")
|
||||
.bind(&cutoff)
|
||||
for table in RETAINED_USER_TABLES {
|
||||
sqlx::query(&format!("DELETE FROM {table} WHERE user_id = ?"))
|
||||
.bind(id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?;
|
||||
}
|
||||
sqlx::query("DELETE FROM users WHERE id = ?")
|
||||
.bind(id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(map_sqlx_error)?
|
||||
.rows_affected();
|
||||
.map_err(map_sqlx_error)?;
|
||||
}
|
||||
tx.commit().await.map_err(map_sqlx_error)?;
|
||||
Ok(removed)
|
||||
Ok(expiring.len() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user