Implement queue pruning and message rotation (closes #40) #67
12
README.md
12
README.md
@@ -249,8 +249,8 @@ Key properties:
|
|||||||
- **Ordered**: Queue entries have monotonically increasing IDs. Messages are
|
- **Ordered**: Queue entries have monotonically increasing IDs. Messages are
|
||||||
always delivered in order within a client's queue.
|
always delivered in order within a client's queue.
|
||||||
- **No delivery/read receipts** for channel messages. DM receipts are planned.
|
- **No delivery/read receipts** for channel messages. DM receipts are planned.
|
||||||
- **Queue depth**: Server-configurable via `QUEUE_MAX_AGE`. Default is 30
|
- **Client output queue depth**: Server-configurable via `QUEUE_MAX_AGE`.
|
||||||
days. Entries older than this are pruned.
|
Default is 30 days. Entries older than this are pruned.
|
||||||
|
|
||||||
### Long-Polling
|
### Long-Polling
|
||||||
|
|
||||||
@@ -1790,8 +1790,8 @@ skew issues) and simpler than UUIDs (integer comparison vs. string comparison).
|
|||||||
|
|
||||||
- **Messages**: Pruned automatically when older than `MESSAGE_MAX_AGE`
|
- **Messages**: Pruned automatically when older than `MESSAGE_MAX_AGE`
|
||||||
(default 30 days).
|
(default 30 days).
|
||||||
- **Queue entries**: Pruned automatically when older than `QUEUE_MAX_AGE`
|
- **Client output queue entries**: Pruned automatically when older than
|
||||||
(default 30 days).
|
`QUEUE_MAX_AGE` (default 30 days).
|
||||||
- **Channels**: Deleted when the last member leaves (ephemeral).
|
- **Channels**: Deleted when the last member leaves (ephemeral).
|
||||||
- **Users/sessions**: Deleted on `QUIT` or `POST /api/v1/logout`. Idle
|
- **Users/sessions**: Deleted on `QUIT` or `POST /api/v1/logout`. Idle
|
||||||
sessions are automatically expired after `SESSION_IDLE_TIMEOUT` (default
|
sessions are automatically expired after `SESSION_IDLE_TIMEOUT` (default
|
||||||
@@ -1814,7 +1814,7 @@ directory is also loaded automatically via
|
|||||||
| `DEBUG` | bool | `false` | Enable debug logging (verbose request/response logging) |
|
| `DEBUG` | bool | `false` | Enable debug logging (verbose request/response logging) |
|
||||||
| `MESSAGE_MAX_AGE` | string | `720h` | Maximum age of messages as a Go duration string (e.g. `720h`, `24h`). Messages older than this are pruned. Default is 30 days. |
|
| `MESSAGE_MAX_AGE` | string | `720h` | Maximum age of messages as a Go duration string (e.g. `720h`, `24h`). Messages older than this are pruned. Default is 30 days. |
|
||||||
| `SESSION_IDLE_TIMEOUT` | string | `720h` | Session idle timeout as a Go duration string (e.g. `720h`, `24h`). Sessions with no activity for this long are expired and the nick is released. Default is 30 days. |
|
| `SESSION_IDLE_TIMEOUT` | string | `720h` | Session idle timeout as a Go duration string (e.g. `720h`, `24h`). Sessions with no activity for this long are expired and the nick is released. Default is 30 days. |
|
||||||
| `QUEUE_MAX_AGE` | string | `720h` | Maximum age of client queue entries as a Go duration string (e.g. `720h`, `24h`). Entries older than this are pruned. Default is 30 days. |
|
| `QUEUE_MAX_AGE` | string | `720h` | Maximum age of client output queue entries as a Go duration string (e.g. `720h`, `24h`). Entries older than this are pruned. Default is 30 days. |
|
||||||
| `MAX_MESSAGE_SIZE` | int | `4096` | Maximum message body size in bytes (planned enforcement) |
|
| `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) |
|
| `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` |
|
| `MOTD` | string | `""` | Message of the day, shown to clients via `GET /api/v1/server` |
|
||||||
@@ -2228,7 +2228,7 @@ GET /api/v1/challenge
|
|||||||
### Post-MVP (Planned)
|
### Post-MVP (Planned)
|
||||||
|
|
||||||
- [ ] **Hashcash proof-of-work** for session creation (abuse prevention)
|
- [ ] **Hashcash proof-of-work** for session creation (abuse prevention)
|
||||||
- [x] **Queue pruning** — delete old queue entries per `QUEUE_MAX_AGE`
|
- [x] **Client output queue pruning** — delete old client output queue entries per `QUEUE_MAX_AGE`
|
||||||
- [x] **Message rotation** — prune messages older than `MESSAGE_MAX_AGE`
|
- [x] **Message rotation** — prune messages older than `MESSAGE_MAX_AGE`
|
||||||
- [ ] **Channel modes** — enforce `+i`, `+m`, `+s`, `+t`, `+n`
|
- [ ] **Channel modes** — enforce `+i`, `+m`, `+s`, `+t`, `+n`
|
||||||
- [ ] **User channel modes** — `+o` (operator), `+v` (voice)
|
- [ ] **User channel modes** — `+o` (operator), `+v` (voice)
|
||||||
|
|||||||
@@ -1110,8 +1110,8 @@ func (database *Database) GetSessionCreatedAt(
|
|||||||
return createdAt, nil
|
return createdAt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PruneOldQueueEntries deletes client_queues rows older
|
// PruneOldQueueEntries deletes client output queue entries
|
||||||
// than cutoff and returns the number of rows removed.
|
// older than cutoff and returns the number of rows removed.
|
||||||
func (database *Database) PruneOldQueueEntries(
|
func (database *Database) PruneOldQueueEntries(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cutoff time.Time,
|
cutoff time.Time,
|
||||||
@@ -1122,7 +1122,7 @@ func (database *Database) PruneOldQueueEntries(
|
|||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf(
|
return 0, fmt.Errorf(
|
||||||
"prune old queue entries: %w", err,
|
"prune old client output queue entries: %w", err,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -226,8 +226,9 @@ func (hdlr *Handlers) parseDurationConfig(
|
|||||||
return dur
|
return dur
|
||||||
}
|
}
|
||||||
|
|
||||||
// pruneQueuesAndMessages removes old client_queues entries
|
// pruneQueuesAndMessages removes old client output queue
|
||||||
// per QUEUE_MAX_AGE and prunes messages per MESSAGE_MAX_AGE.
|
// entries per QUEUE_MAX_AGE and old messages per
|
||||||
|
// MESSAGE_MAX_AGE.
|
||||||
func (hdlr *Handlers) pruneQueuesAndMessages(
|
func (hdlr *Handlers) pruneQueuesAndMessages(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) {
|
) {
|
||||||
@@ -242,11 +243,11 @@ func (hdlr *Handlers) pruneQueuesAndMessages(
|
|||||||
PruneOldQueueEntries(ctx, queueCutoff)
|
PruneOldQueueEntries(ctx, queueCutoff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
hdlr.log.Error(
|
hdlr.log.Error(
|
||||||
"queue pruning failed", "error", err,
|
"client output queue pruning failed", "error", err,
|
||||||
)
|
)
|
||||||
} else if pruned > 0 {
|
} else if pruned > 0 {
|
||||||
hdlr.log.Info(
|
hdlr.log.Info(
|
||||||
"pruned old queue entries",
|
"pruned old client output queue entries",
|
||||||
"deleted", pruned,
|
"deleted", pruned,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user