Skip to content

Commit dc237f3

Browse files
committed
Preserve peers on failed removal
Persist the prospective peer set before publishing the in-memory removal. This keeps failed storage updates retryable and prevents the runtime view from diverging from durable state. AI tools were used in preparing this commit. Co-Authored-By: HAL 9000
1 parent 1cc60c8 commit dc237f3

1 file changed

Lines changed: 66 additions & 4 deletions

File tree

src/peer_store.rs

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ where
5858
pub(crate) async fn remove_peer(&self, node_id: &PublicKey) -> Result<(), Error> {
5959
let _guard = self.mutation_lock.lock().await;
6060
let data = {
61-
let mut locked_peers = self.peers.write().expect("lock");
62-
locked_peers.remove(node_id);
63-
PeerStoreSerWrapper(&locked_peers).encode()
61+
let locked_peers = self.peers.read().expect("lock");
62+
let mut updated_peers = locked_peers.clone();
63+
updated_peers.remove(node_id);
64+
PeerStoreSerWrapper(&updated_peers).encode()
6465
};
65-
self.persist_peers(data).await
66+
self.persist_peers(data).await?;
67+
self.peers.write().expect("lock").remove(node_id);
68+
Ok(())
6669
}
6770

6871
/// Returns the current in-memory peer set.
@@ -170,12 +173,52 @@ mod tests {
170173
use std::str::FromStr;
171174
use std::sync::Arc;
172175

176+
use bitcoin::io;
177+
use lightning::util::persist::{PageToken, PaginatedKVStore, PaginatedListResponse};
173178
use lightning::util::test_utils::TestLogger;
174179

175180
use super::*;
176181
use crate::io::test_utils::InMemoryStore;
177182
use crate::types::DynStoreWrapper;
178183

184+
struct FailingStore;
185+
186+
impl KVStore for FailingStore {
187+
fn read(
188+
&self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str,
189+
) -> impl std::future::Future<Output = Result<Vec<u8>, io::Error>> + 'static + Send {
190+
async { Err(io::Error::new(io::ErrorKind::Other, "read failed")) }
191+
}
192+
193+
fn write(
194+
&self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, _buf: Vec<u8>,
195+
) -> impl std::future::Future<Output = Result<(), io::Error>> + 'static + Send {
196+
async { Err(io::Error::new(io::ErrorKind::Other, "write failed")) }
197+
}
198+
199+
fn remove(
200+
&self, _primary_namespace: &str, _secondary_namespace: &str, _key: &str, _lazy: bool,
201+
) -> impl std::future::Future<Output = Result<(), io::Error>> + 'static + Send {
202+
async { Err(io::Error::new(io::ErrorKind::Other, "remove failed")) }
203+
}
204+
205+
fn list(
206+
&self, _primary_namespace: &str, _secondary_namespace: &str,
207+
) -> impl std::future::Future<Output = Result<Vec<String>, io::Error>> + 'static + Send {
208+
async { Err(io::Error::new(io::ErrorKind::Other, "list failed")) }
209+
}
210+
}
211+
212+
impl PaginatedKVStore for FailingStore {
213+
fn list_paginated(
214+
&self, _primary_namespace: &str, _secondary_namespace: &str,
215+
_page_token: Option<PageToken>,
216+
) -> impl std::future::Future<Output = Result<PaginatedListResponse, io::Error>> + 'static + Send
217+
{
218+
async { Err(io::Error::new(io::ErrorKind::Other, "list_paginated failed")) }
219+
}
220+
}
221+
179222
#[tokio::test]
180223
async fn peer_info_persistence() {
181224
let store: Arc<DynStore> = Arc::new(DynStoreWrapper(InMemoryStore::new()));
@@ -215,4 +258,23 @@ mod tests {
215258
assert_eq!(peers[0], expected_peer_info);
216259
assert_eq!(deser_peer_store.get_peer(&node_id), Some(expected_peer_info));
217260
}
261+
262+
#[tokio::test]
263+
async fn remove_peer_does_not_mutate_memory_if_persist_fails() {
264+
let store: Arc<DynStore> = Arc::new(DynStoreWrapper(FailingStore));
265+
let logger = Arc::new(TestLogger::new());
266+
let node_id = PublicKey::from_str(
267+
"0276607124ebe6a6c9338517b6f485825b27c2dcc0b9fc2aa6a4c0df91194e5993",
268+
)
269+
.unwrap();
270+
let peer_info =
271+
PeerInfo { node_id, address: SocketAddress::from_str("127.0.0.1:9738").unwrap() };
272+
let mut peers = HashMap::new();
273+
peers.insert(node_id, peer_info.clone());
274+
let persisted_bytes = PeerStoreSerWrapper(&peers).encode();
275+
let peer_store = PeerStore::read(&mut &persisted_bytes[..], (store, logger)).unwrap();
276+
277+
assert_eq!(Err(Error::PersistenceFailed), peer_store.remove_peer(&node_id).await);
278+
assert_eq!(Some(peer_info), peer_store.get_peer(&node_id));
279+
}
218280
}

0 commit comments

Comments
 (0)