fix(connectivity): drive reachability from real repository traffic
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 3m32s
Traceability Validation / Check Requirement Traces (push) Successful in 20s
🏗️ Build and Test JellyTau / Build Android APK (push) Successful in 18m27s
Build & Release / Run Tests (push) Successful in 3m36s
Build & Release / Build Linux (push) Successful in 15m43s
Build & Release / Build Android (push) Successful in 18m40s
Build & Release / Create Release (push) Failing after 22s
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 3m32s
Traceability Validation / Check Requirement Traces (push) Successful in 20s
🏗️ Build and Test JellyTau / Build Android APK (push) Successful in 18m27s
Build & Release / Run Tests (push) Successful in 3m36s
Build & Release / Build Linux (push) Successful in 15m43s
Build & Release / Build Android (push) Successful in 18m40s
Build & Release / Create Release (push) Failing after 22s
The offline/online switch was janky because two independent systems decided "online" and never communicated: - ConnectivityMonitor owned is_server_reachable (drove the UI banner) but learned reachability only from a standalone /System/Info/Public ping loop and from auth/login calls. - HybridRepository served all real data by racing cache-vs-server but never read or wrote reachability. So the banner reflected a side-channel poller, not the system the user actually experienced: a successful ping could read "online" while authenticated data calls 401'd or timed out, and three different timeout regimes (5s ping / 30s data / 100ms cache race) flapped against each other. Unify into a single source of truth: - Extract a cheap, cloneable ConnectivityReporter that owns all reachability transitions and event emission. - OnlineRepository reports the outcome of every server request to the reporter, classified via RepoError: Ok/Authentication/NotFound/Server => reachable (the server answered), Network => offline candidate, Database/Offline => ignored (not a server signal). - Time-window debounce (OFFLINE_CONFIRM_WINDOW = 5s): flip offline only after sustained network failure; recover instantly on the first success. - Demote the ping loop to an offline-only recovery probe (no online polling; real traffic is the signal when online). - Frontend: navigator.onLine is now advisory (triggers a recheck instead of forcing offline); removed the dead markReachable/markUnreachable store methods. Docs updated (README, 07-connectivity, 03-data-flow, 02-svelte-frontend) to describe the new model and fix pre-existing drift (HTTP client is 30s timeout + 5s ping, not the documented 10s/base_url). Tests: 12 connectivity tests (debounce, instant recovery, RepoError classification through report_outcome). Full suite: 398 Rust + 384 frontend passing, svelte-check clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ JellyTau uses a client-server architecture: business logic lives in a comprehens
|
||||
- **Events + Polling hybrid**: Rust emits events the frontend listens to, and the UI also polls status on short intervals in a few hot spots (e.g. queue status in `library/+layout.svelte`, playback progress in `VideoPlayer.svelte`).
|
||||
- **Handle-Based Resources**: UUID handles for stateful Rust objects.
|
||||
- **Cache-First**: Parallel queries with intelligent fallback.
|
||||
- **Single source of truth for reachability**: Server reachability is derived from the outcome of *real repository traffic*, not a side-channel poller. The `OnlineRepository` reports each server result to the `ConnectivityMonitor` (classified via `RepoError`), which applies a time-window debounce before declaring the server offline and recovers instantly on the first success. The standalone `/System/Info/Public` probe runs *only while offline*, as a recovery detector for idle sessions.
|
||||
- **Poison-tolerant locking**: Shared `std::sync` state is accessed via the `MutexSafe`/`RwLockSafe` helpers in `utils/lock.rs`, which recover a poisoned lock instead of cascading a panic across the player.
|
||||
- **Graceful backend init**: If a native player backend (MPV/ExoPlayer) fails to initialize, the app falls back to a no-op backend and emits a `backend-init-failed` event rather than crashing.
|
||||
|
||||
@@ -79,9 +80,12 @@ flowchart TB
|
||||
Core --> Storage
|
||||
Repository --> HttpClient
|
||||
Repository --> DatabaseService
|
||||
Repository -->|"reports server outcome<br/>(success / RepoError)"| ConnectivityMonitor
|
||||
end
|
||||
```
|
||||
|
||||
> The `Repository --> ConnectivityMonitor` edge is the source of truth for the offline/online banner: every server request the user actually makes updates reachability. The monitor's own polling is now an offline-only recovery probe (see [07-connectivity.md](07-connectivity.md)).
|
||||
|
||||
---
|
||||
|
||||
## Detailed Documentation
|
||||
@@ -187,7 +191,7 @@ src/lib/
|
||||
|
||||
**What moved to Rust (~3,500 lines of business logic):**
|
||||
1. **HTTP Client** (338 lines) - Retry logic with exponential backoff
|
||||
2. **Connectivity Monitor** (301 lines) - Adaptive polling, event emission
|
||||
2. **Connectivity Monitor** (301 lines) - Reachability derived from real repository traffic, time-window debounce, offline-only recovery probe, event emission
|
||||
3. **Repository Pattern** (1061 lines) - Cache-first hybrid with parallel racing
|
||||
4. **Database Service** - Async wrapper preventing UI freezing
|
||||
5. **Playback Mode** (303 lines) - Local/remote transfer coordination
|
||||
|
||||
Reference in New Issue
Block a user