v0.4.3: Lock contention overhaul, StoragePool, mobile bottom nav, text scaling
Eliminate all conn_mgr lock holds during network I/O across 14 actor commands and bi-stream handlers. PostFetch, TcpPunch, PullFromPeer, FetchEngagement, ResolveAddress, AnchorProbe use brief locks for data gathering only. WormLookup, ContentSearch, WormQuery use connection snapshots for lock-free cascade fan-out. RelayIntroduce extracts forwarding data under brief lock, does I/O outside. BlobRequest, PostFetchRequest, ManifestRefresh use Arc clones instead of conn_mgr lock. ConnectionActor hoists shared Arcs (storage, blob_store, endpoint) for lock-free access. ResolveAddress adds 5s per-query timeout (was unbounded). Initial exchange failure now aborts mesh upgrade (was silently continuing with broken connection). connect_to_peer/connect_to_anchor use consistent 15s timeout. Rebalance connects outside the lock via pending_connects pattern. StoragePool: 8 concurrent SQLite connections in WAL mode replace single Mutex<Storage>. Reads run fully parallel; writes serialize at SQLite level only. PRAGMA busy_timeout=5000 for graceful write contention. Mobile bottom nav bar (<=768px) with icon tabs. Text sizes: XS/S/M/L/XL (75%/100%/125%/150%/200%), default M. localStorage persistence for instant restore. Toast repositioned above mobile nav. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f17535d61d
commit
43adbbdf7d
15 changed files with 1546 additions and 618 deletions
|
|
@ -12,7 +12,7 @@ use tokio::sync::Mutex;
|
|||
use tracing::{debug, info};
|
||||
|
||||
use crate::blob::BlobStore;
|
||||
use crate::storage::Storage;
|
||||
use crate::storage::{Storage, StoragePool};
|
||||
use crate::types::PostVisibility;
|
||||
|
||||
/// Connection budget: 5 content slots, 15 redirect slots, 1 per IP.
|
||||
|
|
@ -104,7 +104,7 @@ impl HttpBudget {
|
|||
/// Run the HTTP server on the given port. Blocks forever.
|
||||
pub async fn run_http_server(
|
||||
port: u16,
|
||||
storage: Arc<Mutex<Storage>>,
|
||||
storage: Arc<StoragePool>,
|
||||
blob_store: Arc<BlobStore>,
|
||||
downstream_addrs: Arc<Mutex<HashMap<[u8; 32], Vec<SocketAddr>>>>,
|
||||
) -> anyhow::Result<()> {
|
||||
|
|
@ -180,7 +180,7 @@ async fn handle_connection(
|
|||
mut stream: TcpStream,
|
||||
_ip: IpAddr,
|
||||
slot: SlotKind,
|
||||
storage: &Arc<Mutex<Storage>>,
|
||||
storage: &Arc<StoragePool>,
|
||||
blob_store: &Arc<BlobStore>,
|
||||
downstream_addrs: &Arc<Mutex<HashMap<[u8; 32], Vec<SocketAddr>>>>,
|
||||
) {
|
||||
|
|
@ -281,12 +281,12 @@ fn validate_hex64(s: &str) -> Option<[u8; 32]> {
|
|||
async fn serve_post(
|
||||
stream: &mut TcpStream,
|
||||
post_id: &[u8; 32],
|
||||
storage: &Arc<Mutex<Storage>>,
|
||||
storage: &Arc<StoragePool>,
|
||||
blob_store: &Arc<BlobStore>,
|
||||
) -> bool {
|
||||
// Look up post + visibility
|
||||
let result = {
|
||||
let store = storage.lock().await;
|
||||
let store = storage.get().await;
|
||||
store.get_post_with_visibility(post_id)
|
||||
};
|
||||
|
||||
|
|
@ -301,7 +301,7 @@ async fn serve_post(
|
|||
|
||||
// Look up author name
|
||||
let author_name = {
|
||||
let store = storage.lock().await;
|
||||
let store = storage.get().await;
|
||||
store
|
||||
.get_profile(&post.author)
|
||||
.ok()
|
||||
|
|
@ -321,12 +321,12 @@ async fn serve_post(
|
|||
async fn serve_blob(
|
||||
stream: &mut TcpStream,
|
||||
blob_id: &[u8; 32],
|
||||
storage: &Arc<Mutex<Storage>>,
|
||||
storage: &Arc<StoragePool>,
|
||||
blob_store: &Arc<BlobStore>,
|
||||
) -> bool {
|
||||
// Verify this blob belongs to a public post
|
||||
let (mime_type, _post_id) = {
|
||||
let store = storage.lock().await;
|
||||
let store = storage.get().await;
|
||||
match find_public_blob_info(&store, blob_id) {
|
||||
Some(info) => info,
|
||||
None => return false, // not found or not public — hard close
|
||||
|
|
@ -367,12 +367,12 @@ fn find_public_blob_info(store: &Storage, blob_id: &[u8; 32]) -> Option<(String,
|
|||
async fn try_redirect(
|
||||
stream: &mut TcpStream,
|
||||
post_id: &[u8; 32],
|
||||
storage: &Arc<Mutex<Storage>>,
|
||||
storage: &Arc<StoragePool>,
|
||||
_downstream_addrs: &Arc<Mutex<HashMap<[u8; 32], Vec<SocketAddr>>>>,
|
||||
) -> bool {
|
||||
// Get downstream peers for this post
|
||||
let downstream_peers = {
|
||||
let store = storage.lock().await;
|
||||
let store = storage.get().await;
|
||||
// Verify post exists and is public first
|
||||
match store.get_post_with_visibility(post_id) {
|
||||
Ok(Some((_, PostVisibility::Public))) => {}
|
||||
|
|
@ -383,7 +383,7 @@ async fn try_redirect(
|
|||
|
||||
// Get addresses for downstream peers
|
||||
let candidates: Vec<SocketAddr> = {
|
||||
let store = storage.lock().await;
|
||||
let store = storage.get().await;
|
||||
let mut addrs = Vec::new();
|
||||
for peer_id in &downstream_peers {
|
||||
if let Ok(Some(peer)) = store.get_peer_record(peer_id) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue