feat!: remove a relay immediately instead of unpublishing it

Removing a relay now takes effect immediately:

- the profile stops fetching and advertising it,

- secondary devices immediately apply the removal through the transport sync,

Upgrading removes unpublished relays and triggers keyupdates.

BREAKING CHANGE: set_transport_unpublished() is removed: UIs call delete_transport() when the user removes a relay.

BREAKING CHANGE: list_transports_ex() and the TransportListEntry type are removed: use list_transports().

BREAKING CHANGE: delete_transport() no longer refuses to remove the primary transport: it refuses only to remove the last one and re-elects the sending transport as needed.

BREAKING CHANGE: TransportsModified is now also emitted on the device modifying the transports, not only on devices applying the synced change.

Deprecated: DC_STR_PHASING_OUT
This commit is contained in:
holger krekel
2026-09-01 14:15:26 +02:00
parent 3d61e0f349
commit 70a01a6813
33 changed files with 473 additions and 827 deletions
-112
View File
@@ -1,6 +1,5 @@
use super::*;
use crate::message::Message;
use crate::tools::SystemTime;
use crate::{EventType, test_utils::TestContext};
#[test]
@@ -370,114 +369,3 @@ async fn test_incremental_vacuum() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_remove_unused_hidden_transports() -> Result<()> {
let t = TestContext::new().await;
let now = time();
let transport_id = t
.sql
.transaction(|t| {
// published transport
t.execute(
"INSERT INTO transports
(addr,
entered_param,
configured_param,
add_timestamp)
VALUES (?, ?, ?, ?)",
("1", "", "", now),
)?;
// not published transport
let transport_id = t.query_row::<u32, _, _>(
"INSERT INTO transports
(addr,
entered_param,
configured_param,
is_published,
add_timestamp)
VALUES (?, ?, ?, ?, ?)
RETURNING id",
("2", "", "", 0, now),
|row| row.get(0),
)?;
// this transport has add_timestamp in the future,
// and should not be removed at all, since we only shift time
// by 90 days and one second.
t.execute(
"INSERT INTO transports
(addr,
entered_param,
configured_param,
is_published,
add_timestamp)
VALUES (?, ?, ?, ?, ?)",
("3", "", "", 0, now + 60),
)?;
Ok(transport_id)
})
.await?;
async fn assert_transport_removed(
context: &Context,
hidden_transport_id: u32,
should_be_removed: bool,
) -> Result<()> {
let transports_count: u32 = context
.sql
.query_get_value("SELECT count(*) FROM transports", ())
.await?
.unwrap();
assert_eq!(transports_count, if should_be_removed { 2 } else { 3 });
let hidden_transport_present: bool = context
.sql
.query_get_value(
"SELECT count(*) FROM transports WHERE id=?",
(hidden_transport_id,),
)
.await?
.unwrap();
assert_eq!(hidden_transport_present, !should_be_removed);
Ok(())
}
assert_eq!(
t.sql
.query_get_value::<i64>(
"SELECT last_rcvd_timestamp FROM transports WHERE id=?",
(transport_id,)
)
.await?
.unwrap(),
0
);
update_transport_last_rcvd_timestamp(&t, transport_id).await?;
// last_rcvd_timestamp should update
assert!(
t.sql
.query_get_value::<i64>(
"SELECT last_rcvd_timestamp FROM transports WHERE id=?",
(transport_id,)
)
.await?
.unwrap()
>= now
);
assert_transport_removed(&t, transport_id, false).await?;
remove_unused_hidden_transports(&t).await?;
// it was recently used, so nothing is deleted
assert_transport_removed(&t, transport_id, false).await?;
SystemTime::shift(Duration::from_secs(
(UNPUBLISHED_TRANSPORT_KEEP_TIME + 1).try_into()?,
));
remove_unused_hidden_transports(&t).await?;
assert_transport_removed(&t, transport_id, true).await?;
Ok(())
}