notes/Design choices/Rationale/Backend > Connection pooling and caching.md
···11+## Overview
22+- PostgreSQL and Redis are pooled with `bb8` (see `server/src/database.rs`).
33+- Redis is used only for performance: bloom filters and timeline caches; PostgreSQL remains source of truth.
44+- Background maintainer (`database::maintain`) periodically deletes old sessions and prunes timeline caches.
55+66+## PostgreSQL pool (bb8-postgres)
77+- Built from `tokio_postgres::Config` with `NoTls`.
88+- Pools are cloned everywhere via `DatabaseConnections::get_postgres_pool` to keep acquisition cheap.
99+- Avoid `unwrap()` on `.get()`; surface bb8 run errors via `LuminaError::Bb8RunErrorPg`.
1010+1111+Example (simplified):
1212+```rust
1313+let pg_pool = PgConn { postgres_pool, redis_pool };
1414+let client = pg_pool.postgres_pool.get().await?; // ? maps to LuminaError::Bb8RunErrorPg
1515+```
1616+1717+## Redis pool (bb8-redis)
1818+- Configured pool builder (currently max_size 50, 5s timeout, 5m idle timeout).
1919+- Connection type is `MultiplexedConnection`; use `redis::cmd(...).query_async(&mut **conn)`. Pool errors surface as `LuminaError::Bb8RunErrorRedis`.
2020+2121+### Bloom filters
2222+- Keys: `bloom:email`, `bloom:username`.
2323+- Populated at startup from Postgres (`database::setup`).
2424+- Checked in `user::register_validitycheck` before DB uniqueness queries.
2525+2626+Example add/check:
2727+```rust
2828+let mut conn = redis_pool.get().await?;
2929+redis::cmd("BF.ADD").arg("bloom:email").arg(email).query_async(&mut *conn).await?;
3030+let exists: bool = redis::cmd("BF.EXISTS").arg("bloom:email").arg(email).query_async(&mut *conn).await?;
3131+```
3232+3333+### Timeline cache
3434+- Cache keys: `timeline_cache:{tlid}:page:{page}`; metadata key: `timeline_cache:{tlid}:meta`.
3535+- Cache TTL: 3600s; high-traffic threshold: 100 lookups (global always high-traffic).
3636+- Write path (`cache_timeline_page`) stores page JSON and total count; read path (`get_cached_timeline_page`) returns `CachedTimelinePage`.
3737+- Invalidation: `invalidate_timeline_cache` SCANs matching keys and DELs; called after timeline writes and from the maintainer loop when timelines change.
3838+- Background invalidation cursor uses `timeline_cache_last_check` stored in Redis.
3939+4040+Example invalidate:
4141+```rust
4242+let mut conn = redis_pool.get().await?;
4343+timeline::invalidate_timeline_cache(&mut conn, tlid).await?;
4444+```
4545+4646+## Background maintainer
4747+- `database::maintain` (spawned at setup) runs two intervals:
4848+ - Every 60s: delete sessions older than 20 days.
4949+ - Every 300s: prune expired timeline cache entries; check timeline invalidations based on latest timestamps.
5050+5151+## Tests
5252+- `src/tests.rs` covers: pool setup, bloom filter add/exists, timeline cache invalidation.
5353+5454+## Operational cautions
5555+- Pool exhaustion: handle `.get()` errors; avoid panics from `unwrap()`.
5656+- Redis is non-authoritative; always fall back to Postgres on cache miss or bloom filter hit.
5757+- Keep TTLs and thresholds in sync if tuning (`timeline.rs` constants).
+20
notes/Design choices/Rationale/Backend > Error handling and logging.md
···11+## LuminaError
22+- Defined in `server/src/errors.rs`.
33+- Key variants: `DbError(LuminaDbError)`, `Bb8RunErrorPg(bb8::RunError<postgres::Error>)`, `Bb8RunErrorRedis(bb8::RunError<redis::RedisError>)`, auth/registration errors, `SerializationError(String)`, `RocketFaillure(Box<rocket::Error>)`.
44+- Conversions implemented for Rocket, Postgres, Redis, and bb8 run errors.
55+- Guidance: propagate the source error (`?`) to keep context; avoid lossy `to_string()` unless necessary.
66+77+## Logging
88+- Event logging macros `info_elog!`, `warn_elog!`, `error_elog!`, `success_elog!` are used across DB/timeline flows.
99+- `EventLogger` can log to stdout and (optionally) Postgres `logs` table (see `helpers/events.rs`).
1010+- When logging DB failures, prefer structured context (timeline id, page, user) to aid diagnosis.
1111+1212+## Failure-handling patterns
1313+- Pool acquisition: use `?` so `.get()` maps to `LuminaError::Bb8RunErrorPg/Redis`; avoid panics.
1414+- Redis is non-authoritative: on Redis errors, proceed with Postgres path to avoid request failure where possible.
1515+- Bloom filters: treat `BF.EXISTS` positives as hints; always confirm with Postgres.
1616+- Timeline cache: cache misses/failures should fall back to DB fetch; invalidation is best-effort.
1717+1818+## Operational notes
1919+- If Rocket state is missing (e.g., limiter), guards fail-open; verify state wiring at startup.
2020+- Add tracing/metrics around pool usage and cache hit/miss for production readiness.
···11+## Overview
22+- Token-bucket limiter in `server/src/rate_limiter.rs` using in-memory `HashMap` protected by `tokio::sync::Mutex`.
33+- Rocket request guard `RateLimit` pulls `State<GeneralRateLimiter>`; missing state = allow (fail-open).
44+- Separate wrapper types: `GeneralRateLimiter` and `AuthRateLimiter` so Rocket can manage both independently.
55+66+## Defaults / Tuning
77+- Constructor requires `refill_per_second` and `capacity`; no hardcoded defaults. Decide per endpoint.
88+- In-memory only: resets on process restart; not distributed. For multi-node, replace with shared store (e.g., Redis token bucket) or IP hash partitioning.
99+- Keyed by client IP (`Request::client_ip()`); missing IP maps to key "unknown".
1010+1111+## Usage pattern
1212+```rust
1313+// Configure and mount in Rocket managed state
1414+let limiter = GeneralRateLimiter::new(refill_per_second, capacity);
1515+rocket::build().manage(limiter);
1616+1717+// Handler signature adds guard
1818+#[get("/protected")]
1919+async fn protected(_rate: RateLimit) -> &'static str {
2020+ "ok"
2121+}
2222+```
2323+2424+## Gotchas
2525+- Fail-open if the guard cannot fetch state; ensure the limiter is registered in Rocket.
2626+- No per-route tuning baked in; provide distinct limiters via type wrappers if needed.
2727+- Single-threaded bottleneck: Mutex over HashMap is fine for moderate QPS; consider sharding or lock-free structure if contention grows.