Skip to content

Commit db0ccec

Browse files
committed
Prune stale payjoin sessions on DB open
Delete payjoin sessions older than 30 days when the payjoin database is accessed. Remove related event rows in the same cleanup pass.
1 parent 6b06cf7 commit db0ccec

2 files changed

Lines changed: 88 additions & 3 deletions

File tree

src/payjoin/db.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub type Result<T> = std::result::Result<T, Error>;
1616

1717
/// Default filename for the payjoin database
1818
pub const DB_FILENAME: &str = "payjoin.sqlite";
19+
const SESSION_RETENTION_SECS: i64 = 30 * 24 * 60 * 60;
1920

2021
/// Returns the current Unix timestamp in seconds
2122
#[inline]
@@ -109,6 +110,90 @@ impl Database {
109110
Ok(was_seen_before)
110111
}
111112

113+
/// Removes old completed sessions and stale incomplete sessions plus their event logs.
114+
pub fn prune_expired_sessions(&self) -> Result<()> {
115+
let cutoff = now() - SESSION_RETENTION_SECS;
116+
let mut conn = self.conn();
117+
let tx = conn.transaction()?;
118+
let stale_send_session_ids = {
119+
let mut stmt = tx.prepare(
120+
"SELECT session_id FROM send_sessions
121+
WHERE (completed_at IS NOT NULL AND completed_at < ?1)
122+
OR (
123+
completed_at IS NULL
124+
AND session_id IN (
125+
SELECT session_id FROM send_session_events
126+
GROUP BY session_id
127+
HAVING MAX(created_at) < ?1
128+
)
129+
)
130+
OR (
131+
completed_at IS NULL
132+
AND session_id NOT IN (
133+
SELECT session_id FROM send_session_events
134+
)
135+
)",
136+
)?;
137+
let rows = stmt.query_map(params![cutoff], |row| row.get::<_, i64>(0))?;
138+
let mut ids = Vec::new();
139+
for row in rows {
140+
ids.push(row?);
141+
}
142+
ids
143+
};
144+
let stale_receive_session_ids = {
145+
let mut stmt = tx.prepare(
146+
"SELECT session_id FROM receive_sessions
147+
WHERE (completed_at IS NOT NULL AND completed_at < ?1)
148+
OR (
149+
completed_at IS NULL
150+
AND session_id IN (
151+
SELECT session_id FROM receive_session_events
152+
GROUP BY session_id
153+
HAVING MAX(created_at) < ?1
154+
)
155+
)
156+
OR (
157+
completed_at IS NULL
158+
AND session_id NOT IN (
159+
SELECT session_id FROM receive_session_events
160+
)
161+
)",
162+
)?;
163+
let rows = stmt.query_map(params![cutoff], |row| row.get::<_, i64>(0))?;
164+
let mut ids = Vec::new();
165+
for row in rows {
166+
ids.push(row?);
167+
}
168+
ids
169+
};
170+
171+
for session_id in stale_send_session_ids {
172+
tx.execute(
173+
"DELETE FROM send_session_events WHERE session_id = ?1",
174+
params![session_id],
175+
)?;
176+
tx.execute(
177+
"DELETE FROM send_sessions WHERE session_id = ?1",
178+
params![session_id],
179+
)?;
180+
}
181+
182+
for session_id in stale_receive_session_ids {
183+
tx.execute(
184+
"DELETE FROM receive_session_events WHERE session_id = ?1",
185+
params![session_id],
186+
)?;
187+
tx.execute(
188+
"DELETE FROM receive_sessions WHERE session_id = ?1",
189+
params![session_id],
190+
)?;
191+
}
192+
193+
tx.commit()?;
194+
Ok(())
195+
}
196+
112197
/// Returns IDs of all active (incomplete) receive sessions
113198
pub fn get_recv_session_ids(&self) -> Result<Vec<SessionId>> {
114199
let conn = self.conn();

src/utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,9 @@ pub fn open_payjoin_db(
142142
use crate::payjoin::db::{DB_FILENAME, Database};
143143
let wallet_dir = prepare_home_dir(datadir)?.join(wallet_name);
144144
std::fs::create_dir_all(&wallet_dir).map_err(|e| Error::Generic(e.to_string()))?;
145-
Ok(std::sync::Arc::new(Database::create(
146-
wallet_dir.join(DB_FILENAME),
147-
)?))
145+
let db = std::sync::Arc::new(Database::create(wallet_dir.join(DB_FILENAME))?);
146+
db.prune_expired_sessions()?;
147+
Ok(db)
148148
}
149149

150150
#[cfg(any(

0 commit comments

Comments
 (0)