4.6 KiB
Chanora Server Prefetch Crate Design
Date: 2026-05-28
Goal
Move server-resolution prefetch policy out of chanora_core into a focused Rust crate named chanora_prefetch, without changing connection behavior, Flutter APIs, or protocol dialing semantics.
Non-Goals
- Do not change resolver behavior or DNS/SRV/TSDNS ordering.
- Do not change
ConnectConfig.resolved_addresssemantics inchanora_protocol. - Do not expose prefetch state in the UI.
- Do not prefetch bookmarks or additional hosts.
- Do not persist prefetched addresses.
Architecture
Add a workspace member at crates/chanora_prefetch.
Responsibilities:
- Normalize server host keys by trimming and lowercasing.
- Track one active prefetch generation.
- Store at most one successful prefetched socket address.
- Reject stale async completions by generation.
- Return a prefetched address only for an exact normalized host match.
- Enforce the 2-minute freshness TTL.
- Resolve server addresses by calling
chanora_resolver::ChanoraResolver::resolve_client_address. - Log prefetch start, success, miss/stale, and failure diagnostics.
Dependencies:
chanora_resolverfor actual server address resolution.tokioforMutexand spawned prefetch tasks.tracingfor diagnostics.thiserrorfor a narrowServerPrefetchErrorpublic error type.
Public API
The crate exposes this small async owner type:
pub struct ServerPrefetcher { ... }
impl ServerPrefetcher {
pub fn new() -> Self;
pub async fn prefetch(&self, host: String) -> Result<(), ServerPrefetchError>;
pub async fn fresh_match(&self, host: &str) -> Option<std::net::SocketAddr>;
}
prefetch returns after scheduling work, preserving the current invisible, non-blocking behavior. Empty normalized hosts are ignored successfully. Resolution failures are stored only as diagnostics and do not affect connect semantics.
Core Integration
chanora_core replaces its private prefetch cache fields and helpers with ServerPrefetcher.
Core remains the trust boundary for connection config:
- It clears any caller-provided
ConnectConfig.resolved_addressbefore lookup. - It asks
ServerPrefetcher::fresh_matchfor the current host. - It sets
dial_cfg.resolved_addressonly from a fresh exact cache hit. - It stores supervisor/reconnect config with
resolved_address: None.
The Flutter bridge keeps calling the same core API, prefetch_server_resolution(host). No Dart API change is intended.
Data Flow
- Flutter host editing schedules
ChanoraSession::prefetch_server_resolution(host). - Core delegates to
ServerPrefetcher::prefetch(host). - The prefetcher normalizes the host, increments generation, and spawns resolver work.
- On success, the prefetcher stores the resolved socket address if the generation is still current.
- On connect, core prepares
(stored_cfg, dial_cfg). - Core clears untrusted
resolved_address, asks the prefetcher for a fresh exact match, and applies the result only todial_cfg. - Protocol uses
dial_cfg.resolved_addressif present; otherwise it resolves normally.
Error Handling
- Prefetch failures remain invisible to users.
- Prefetch failures are logged through
tracing. - If prefetch misses, is stale, or fails, connect falls back to normal protocol resolution.
- A caller-supplied
resolved_addressis never trusted by core.
Testing
Move cache-policy tests from chanora_core into chanora_prefetch:
- fresh exact match returns the socket address.
- stale entries are ignored.
- different hosts are ignored.
- stale generation completions are ignored.
- blank normalized hosts return
Ok(())and do not update generation or spawn resolver work.
Keep core tests for connection trust-boundary behavior:
- untrusted
resolved_addressis cleared on cache miss. - prepared stored/supervisor config has
resolved_address: None. - prepared dial config can receive a fresh prefetched address.
Run at minimum:
cargo test -p chanora_prefetch --libcargo test -p chanora_core --libcargo test -p chanora_protocol --lib
For final confidence, rerun the Android server connect smoke path that verifies prefetch logs and connected UI.
Acceptance Criteria
- Workspace builds with the new crate member.
chanora_coreno longer owns the prefetch cache implementation.chanora_prefetchowns prefetch normalization, TTL, generation, storage, and resolver-backed warming.- Public Flutter and Rust protocol behavior is unchanged.
- Existing server connect and reconnect safety tests pass.
- Android connect still reaches the connected server UI and does not get stuck in
ConnectingorSynchronizing.