Fix storage lock contention: reduce lock holds across 6 hot paths

- get_blob_for_post: 3 sequential locks → 1 combined acquisition
- prefetch_blobs_from_peer: lock only for DB reads, blob checks outside lock
- fetch_engagement_from_peer: explicit lock release before next network I/O
- serve_post: 4 locks (2 redundant) → 2
- run_replication_check: 2 locks → 1 combined
- Badge cycle: N+2 IPC calls → 1 (new get_badge_counts command)
- Follow timeout: 15s cap on auto-sync-on-follow to prevent UI hang
- Notification clearing: clear system notifications on conversation read

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Scott Reimers 2026-03-21 13:02:30 -04:00
parent 3cc39590a7
commit 89d6a853f5
5 changed files with 152 additions and 106 deletions

View file

@ -124,8 +124,8 @@ async fn serve_post(stream: &mut TcpStream, path: &str, node: &Arc<Node>, browse
None
};
// Gather all known holders: author + CDN downstream peers
let (holders, local_post) = {
// Single lock: gather holders, local post, AND author name if local
let (holders, local_post, local_author_name) = {
let store = node.storage.lock().await;
let mut holders = Vec::new();
@ -141,7 +141,18 @@ async fn serve_post(stream: &mut TcpStream, path: &str, node: &Arc<Node>, browse
}
let local = store.get_post_with_visibility(&post_id).ok().flatten();
(holders, local)
// If we have the post locally and it's public, get author name now
let author_name = if let Some((ref post, ref vis)) = local {
if matches!(vis, PostVisibility::Public) {
store.get_profile(&post.author).ok().flatten()
.map(|p| p.display_name).unwrap_or_default()
} else {
String::new()
}
} else {
String::new()
};
(holders, local, author_name)
};
// --- Tier 1 & 2: Try direct redirect to an HTTP-capable holder ---
@ -157,15 +168,10 @@ async fn serve_post(stream: &mut TcpStream, path: &str, node: &Arc<Node>, browse
// --- Tier 3: QUIC proxy fallback ---
// Check local storage first
// Check local storage first (author_name already fetched above)
if let Some((post, visibility)) = local_post {
if matches!(visibility, PostVisibility::Public) {
let author_name = {
let store = node.storage.lock().await;
store.get_profile(&post.author).ok().flatten()
.map(|p| p.display_name).unwrap_or_default()
};
let html = render_post_html(&post, &post_id, &author_name);
let html = render_post_html(&post, &post_id, &local_author_name);
let _ = write_http_response(stream, 200, "text/html; charset=utf-8", html.as_bytes()).await;
return;
}
@ -182,14 +188,12 @@ async fn serve_post(stream: &mut TcpStream, path: &str, node: &Arc<Node>, browse
match search_result {
Ok(Ok(Some(sync_post))) => {
{
// Single lock: store post AND get author name
let author_name = {
let store = node.storage.lock().await;
let _ = store.store_post_with_visibility(
&sync_post.id, &sync_post.post, &sync_post.visibility,
);
}
let author_name = {
let store = node.storage.lock().await;
store.get_profile(&sync_post.post.author).ok().flatten()
.map(|p| p.display_name).unwrap_or_default()
};