feat: implement queue pruning and message rotation

Enforce QUEUE_MAX_AGE and MAX_HISTORY config values that previously
existed but were not applied. The existing cleanup loop now also:

- Prunes client_queues entries older than QUEUE_MAX_AGE (default 48h)
- Rotates messages per target (channel/DM) beyond MAX_HISTORY (default 10000)
- Removes orphaned messages no longer referenced by any client queue

closes #40
This commit is contained in:
user
2026-03-10 03:20:21 -07:00
parent b1fd2f1b96
commit d57d5babf0
4 changed files with 196 additions and 8 deletions

View File

@@ -1788,10 +1788,11 @@ skew issues) and simpler than UUIDs (integer comparison vs. string comparison).
### Data Lifecycle
- **Messages**: Stored indefinitely in the current implementation. Rotation
per `MAX_HISTORY` is planned.
- **Queue entries**: Stored until pruned. Pruning by `QUEUE_MAX_AGE` is
planned.
- **Messages**: Rotated per `MAX_HISTORY` — oldest messages beyond the limit
are pruned periodically per target (channel or DM). Orphaned messages (no
longer referenced by any client queue) are also removed.
- **Queue entries**: Pruned automatically when older than `QUEUE_MAX_AGE`
(default 48h).
- **Channels**: Deleted when the last member leaves (ephemeral).
- **Users/sessions**: Deleted on `QUIT` or `POST /api/v1/logout`. Idle
sessions are automatically expired after `SESSION_IDLE_TIMEOUT` (default
@@ -1812,9 +1813,9 @@ directory is also loaded automatically via
| `PORT` | int | `8080` | HTTP listen port |
| `DBURL` | string | `file:///var/lib/neoirc/state.db?_journal_mode=WAL` | SQLite connection string. For file-based: `file:///path/to/db.db?_journal_mode=WAL`. For in-memory (testing): `file::memory:?cache=shared`. |
| `DEBUG` | bool | `false` | Enable debug logging (verbose request/response logging) |
| `MAX_HISTORY` | int | `10000` | Maximum messages retained per channel before rotation (planned) |
| `MAX_HISTORY` | int | `10000` | Maximum messages retained per target (channel or DM) before rotation |
| `SESSION_IDLE_TIMEOUT` | string | `24h` | Session idle timeout as a Go duration string (e.g. `24h`, `30m`). Sessions with no activity for this long are expired and the nick is released. |
| `QUEUE_MAX_AGE` | int | `172800` | Maximum age of client queue entries in seconds (48h). Entries older than this are pruned (planned). |
| `QUEUE_MAX_AGE` | int | `172800` | Maximum age of client queue entries in seconds (48h). Entries older than this are pruned. |
| `MAX_MESSAGE_SIZE` | int | `4096` | Maximum message body size in bytes (planned enforcement) |
| `LONG_POLL_TIMEOUT`| int | `15` | Default long-poll timeout in seconds (client can override via query param, server caps at 30) |
| `MOTD` | string | `""` | Message of the day, shown to clients via `GET /api/v1/server` |
@@ -2228,8 +2229,8 @@ GET /api/v1/challenge
### Post-MVP (Planned)
- [ ] **Hashcash proof-of-work** for session creation (abuse prevention)
- [ ] **Queue pruning** — delete old queue entries per `QUEUE_MAX_AGE`
- [ ] **Message rotation** — enforce `MAX_HISTORY` per channel
- [x] **Queue pruning** — delete old queue entries per `QUEUE_MAX_AGE`
- [x] **Message rotation** — enforce `MAX_HISTORY` per target
- [ ] **Channel modes** — enforce `+i`, `+m`, `+s`, `+t`, `+n`
- [ ] **User channel modes** — `+o` (operator), `+v` (voice)
- [x] **MODE command** — query channel and user modes (set not yet implemented)