Phase F: NetworkMonitor, offline banner, README & architecture docs
- NetworkMonitor: ConnectivityState Flow using ConnectivityManager.NetworkCallback
- ListsScreen & ViewModel: live offline indicator banner when disconnected
- Documentation:
- docs/ARCHITECTURE.md: system design & tech stack overview
- docs/SYNC.md: HLC timestamping, op_log outbox & LWW projection specification
- docs/API.md: REST API endpoint specification
- README.md: quickstart guide for backend, docker compose & Android app
- Verification: backend & android test suites 100% green ✅
This commit is contained in:
parent
174aad535a
commit
a00db14cba
8 changed files with 482 additions and 96 deletions
58
docs/SYNC.md
Normal file
58
docs/SYNC.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Mitbringsl – Synchronization Protocol & Conflict Resolution
|
||||
|
||||
## 1. Synchronization Model
|
||||
|
||||
Mitbringsl uses an **Append-Only Operation Log (`op_log`)** with **Hybrid Logical Clock (HLC)** timestamps and **Last-Write-Wins (LWW)** projections.
|
||||
|
||||
---
|
||||
|
||||
## 2. Hybrid Logical Clock (HLC)
|
||||
|
||||
Every operation carries a 64-bit integer timestamp `hlc_ts`:
|
||||
|
||||
$$\text{hlc\_ts} = (\text{wall\_ms} \ll 16) \mid \text{counter}$$
|
||||
|
||||
- **`wall_ms`** (48 bits): Unix epoch milliseconds.
|
||||
- **`counter`** (16 bits): Logical counter disambiguating events produced within the same millisecond.
|
||||
|
||||
### Guarantees
|
||||
1. **Strict Monotonicity**: $t_{n+1} > t_n$ for all events generated on the same device.
|
||||
2. **Causal Ordering**: If event $B$ was generated after observing event $A$, then $HLC(B) > HLC(A)$.
|
||||
|
||||
---
|
||||
|
||||
## 3. Operations (`op_log`)
|
||||
|
||||
All state modifications are represented as structured operations:
|
||||
|
||||
| `op_type` | Target | Description | Payload Example |
|
||||
|---|---|---|---|
|
||||
| `list_create` | List UUID | Create list | `{"name":"Wocheneinkauf"}` |
|
||||
| `list_rename` | List UUID | Rename list | `{"name":"Supermarkt"}` |
|
||||
| `list_delete` | List UUID | Delete list | `{}` |
|
||||
| `item_add` | Item UUID | Add item | `{"name":"Milch","quantity":"2L"}` |
|
||||
| `item_update` | Item UUID | Update item | `{"name":"Hafermilch","checked":true}` |
|
||||
| `item_remove` | Item UUID | Delete item | `{}` |
|
||||
|
||||
---
|
||||
|
||||
## 4. Conflict Resolution (LWW & Tombstones)
|
||||
|
||||
1. **Projection Updates**:
|
||||
Database tables (`items`, `lists`) are projections of `op_log`. When an op arrives:
|
||||
```sql
|
||||
UPDATE items
|
||||
SET name = $1, hlc_ts = $2, updated_at = now()
|
||||
WHERE id = $3 AND hlc_ts < $2 AND deleted_at IS NULL;
|
||||
```
|
||||
2. **Tombstones (`deleted_at`)**:
|
||||
When an item or list is deleted (`item_remove`, `list_delete`), a tombstone is set (`deleted_at = now()`). Late-arriving offline `item_update` operations with smaller `hlc_ts` values will **never** revive a tombstoned entity.
|
||||
|
||||
---
|
||||
|
||||
## 5. Idempotent Push & Cursor Pull
|
||||
|
||||
- **Idempotent Push (`POST /api/lists/{id}/ops`)**:
|
||||
Each operation carries a `(client_id, client_seq)` tuple enforced by a `UNIQUE` constraint in Postgres (`op_log`). Re-sent requests return previous `(seq, hlc_ts)` assignments without duplicating side effects.
|
||||
- **Cursor Pull (`GET /api/lists/{id}/ops?since={seq}`)**:
|
||||
Clients track `max(server_seq)` locally. Incremental sync fetches ops where `seq > cursor`, sorted by monotonic server sequence `seq ASC`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue