diff --git a/Dockerfile b/Dockerfile index 04e60ea..46e3169 100644 --- a/Dockerfile +++ b/Dockerfile @@ -78,6 +78,11 @@ RUN chown -R routewatch:routewatch /app ENV XDG_DATA_HOME=/var/lib +# Cap the Go heap at 1.5 GiB so the runtime collects harder before the +# container's memory limit is reached. runuser preserves this the way it does +# XDG_DATA_HOME above. +ENV GOMEMLIMIT=1536MiB + # Expose HTTP port EXPOSE 8080 diff --git a/README.md b/README.md index 5bfe36c..e856eb4 100644 --- a/README.md +++ b/README.md @@ -165,14 +165,51 @@ bgp_peers(id, peer_ip, peer_asn, last_message_type, last_seen) Configuration is handled via environment variables and OS-specific paths: | Variable | Default | Description | -|----------|---------|-------------| +|----------|----------|-------------| | `PORT` | `8080` | HTTP server port | | `DEBUG` | (empty) | Set to `routewatch` for debug logging | +| `GOMEMLIMIT` | `1536MiB` (in the Docker image) | Go soft memory limit; see Memory | State directory (database location): - macOS: `~/Library/Application Support/routewatch/` - Linux: `/var/lib/routewatch/` or `~/.local/share/routewatch/` +## Memory + +The daemon holds a live routing table, so its memory grows with the size of the +data it tracks. The image sets ceilings that keep it inside a 5 GiB container. + +Budget: +- Go heap: a 1.5 GiB soft limit (`GOMEMLIMIT=1536MiB`, set in the image). +- SQLite: at most 640 MiB of page cache across the connection pool (64 MiB per + connection, 10 connections) and a 1.5 GiB hard heap limit for the C library. +- About 0.2 GiB for everything else in the runtime. + +Run the container with a memory limit of 5 GiB and swap disabled: + +```bash +docker run --memory=5g --memory-swap=5g ... +``` + +or the equivalent in your deployment tool. This leaves headroom above the +ceilings for spikes and the kernel page cache. + +Override the Go soft limit by setting `GOMEMLIMIT` in the environment (for +example `-e GOMEMLIMIT=1GiB`); this replaces the image default. + +What happens at each limit: +- Go soft limit: as the heap approaches `GOMEMLIMIT`, the runtime runs garbage + collection more aggressively rather than growing further. +- SQLite: at a 1 GiB soft heap limit it recycles its page cache instead of + allocating more; at the 1.5 GiB hard heap limit a statement fails with an + out-of-memory error, and the handler logs it and drops that batch. The process + keeps running. +- Handler queues: each of the four handler queues holds at most 20,000 messages. + When a queue fills, the streamer drops messages instead of blocking. + +With `DEBUG=routewatch` the daemon logs a `System stats` line every 60 seconds +with the goroutine count and Go memory figures. + ## Development ```bash