Skip to content

Commit f458784

Browse files
DanGouldbenalleng
andauthored
Fix flaky test_prune by using deterministic timestamp manipulation (#1338)
* Fix flaky test_prune by using deterministic timestamp manipulation Instead of sleeping to let TTLs elapse (wall-clock dependent and flaky on slow CI), shift timestamps in insert_order/read_order backward to simulate time passing. Uses large TTL values (seconds) so real elapsed time between operations is irrelevant. Co-authored-by: Benalleng <benalleng@gmail.com>
1 parent 52a74cc commit f458784

1 file changed

Lines changed: 32 additions & 22 deletions

File tree

payjoin-directory/src/db/files.rs

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -870,9 +870,9 @@ async fn test_v1_wait() -> std::io::Result<()> {
870870
Ok(())
871871
}
872872

873-
// FIXME test is a bit slow and flakey, how to improve?
874-
// unfortunately tokio::time::pause() can't be used because this uses SystemTime
875-
// as the underlying clock type, due to timestamps originating from disk
873+
// Simulate elapsed time deterministically by shifting stored timestamps
874+
// backward instead of sleeping. tokio::time::pause() can't be used because
875+
// prune compares against SystemTime (timestamps originate from disk).
876876
#[tokio::test]
877877
async fn test_prune() -> std::io::Result<()> {
878878
let dir = tempfile::tempdir()?;
@@ -881,12 +881,16 @@ async fn test_prune() -> std::io::Result<()> {
881881
.await
882882
.expect("initializing mailbox database should succeed");
883883

884+
let read_ttl = Duration::from_secs(60);
885+
let unread_ttl_at_capacity = Duration::from_secs(600);
886+
let unread_ttl_below_capacity = Duration::from_secs(3600);
887+
884888
{
885889
let mut guard = db.mailboxes.lock().await;
886890
guard.capacity = 2;
887-
guard.read_ttl = Duration::from_millis(10);
888-
guard.unread_ttl_at_capacity = Duration::from_millis(100);
889-
guard.unread_ttl_below_capacity = Duration::from_millis(200);
891+
guard.read_ttl = read_ttl;
892+
guard.unread_ttl_at_capacity = unread_ttl_at_capacity;
893+
guard.unread_ttl_below_capacity = unread_ttl_below_capacity;
890894
}
891895

892896
assert_eq!(db.mailboxes.lock().await.len(), 0);
@@ -896,6 +900,7 @@ async fn test_prune() -> std::io::Result<()> {
896900
let id = ShortId([0u8; 8]);
897901
let contents = b"fooo";
898902

903+
// Pending v2 waiter that times out should be cleaned up by prune
899904
let read_task1 = tokio::spawn({
900905
let db = db.clone();
901906
async move { db.wait_for_v2_payload(&id).await }
@@ -906,12 +911,13 @@ async fn test_prune() -> std::io::Result<()> {
906911

907912
match read_task1.await.expect("joining should succeed") {
908913
Err(super::Error::Timeout(_)) => {}
909-
res => panic!("expected timeout, got {:?}", res),
914+
res => panic!("expected timeout, got {res:?}"),
910915
}
911916

912917
db.prune().await.expect("pruning should not fail");
913918
assert_eq!(db.mailboxes.lock().await.len(), 0);
914919

920+
// Post a v2 payload — should survive immediate prune (TTL not elapsed)
915921
db.post_v2_payload(&id, contents.to_vec())
916922
.await
917923
.expect("posting payload should succeed")
@@ -921,42 +927,46 @@ async fn test_prune() -> std::io::Result<()> {
921927
db.prune().await.expect("pruning should not fail");
922928
assert_eq!(db.mailboxes.lock().await.len(), 1);
923929

924-
tokio::time::sleep(Duration::from_millis(200)).await;
930+
// Shift insert timestamps past unread_ttl_below_capacity
931+
{
932+
let mut guard = db.mailboxes.lock().await;
933+
for (ts, _) in guard.insert_order.iter_mut() {
934+
*ts -= unread_ttl_below_capacity + Duration::from_secs(1);
935+
}
936+
}
925937

926938
assert_eq!(db.mailboxes.lock().await.len(), 1);
927939
db.prune().await.expect("pruning should not fail");
928940
assert_eq!(db.mailboxes.lock().await.len(), 0);
929941

942+
// Post again, read it, then verify read TTL pruning
930943
db.post_v2_payload(&id, contents.to_vec())
931944
.await
932945
.expect("posting payload should succeed")
933946
.expect("contents should be accepted");
934947

935948
assert_eq!(db.mailboxes.lock().await.len(), 1);
936-
// FIXME why does this fail?
937-
// db.prune().await.expect("pruning should not fail");
938-
// assert_eq!(db.mailboxes.lock().await.len(), 1);
939-
// mailbox seems to get pruned prematurely
940-
// likely cause is it's in both read and insert queue, and two pruning runs
941-
// are needed to fully clear it?
942-
943-
// mark the mailbox as read
949+
950+
// Mark the mailbox as read
944951
_ = db.wait_for_v2_payload(&id).await.expect("waiting for payload should succeed");
945952

946953
assert_eq!(db.mailboxes.lock().await.len(), 1);
947-
// FIXME db.prune().await.expect("pruning should not fail");
954+
db.prune().await.expect("pruning should not fail");
948955
assert_eq!(db.mailboxes.lock().await.len(), 1);
949956

950-
// allow read TTL to elapse
951-
tokio::time::sleep(Duration::from_millis(10)).await;
957+
// Shift read timestamps past read_ttl
958+
{
959+
let mut guard = db.mailboxes.lock().await;
960+
for (ts, _) in guard.read_order.iter_mut() {
961+
*ts -= read_ttl + Duration::from_secs(1);
962+
}
963+
}
952964

953965
assert_eq!(db.mailboxes.lock().await.len(), 1);
954966
db.prune().await.expect("pruning should not fail");
955967
assert_eq!(db.mailboxes.lock().await.len(), 0);
956968

957-
tokio::time::sleep(Duration::from_millis(200)).await;
958-
959-
assert_eq!(db.mailboxes.lock().await.len(), 0);
969+
// Empty db should remain empty after prune
960970
db.prune().await.expect("pruning should not fail");
961971
assert_eq!(db.mailboxes.lock().await.len(), 0);
962972

0 commit comments

Comments
 (0)