|
| 1 | +# source/redis |
| 2 | + |
| 3 | +A [`crucible/source`](../) ingress adapter that consumes a Redis Stream through |
| 4 | +a consumer group. Runtime dependency: |
| 5 | +[`github.com/redis/go-redis/v9`](https://github.com/redis/go-redis). |
| 6 | + |
| 7 | +```go |
| 8 | +in, err := redis.New( |
| 9 | + redis.WithAddr("localhost:6379"), |
| 10 | + redis.WithGroup("orders-svc"), |
| 11 | + redis.WithConsumer("worker-1"), |
| 12 | + redis.WithDLQStream("orders.dlq"), |
| 13 | + redis.WithBlock(5*time.Second), |
| 14 | + redis.WithCount(64), |
| 15 | +) |
| 16 | +sub, _ := in.Subscribe(ctx, source.SubscribeConfig{Topics: []string{"orders"}}) |
| 17 | + |
| 18 | +m, _ := sub.Next(ctx) // or drive sub with a source.Hopper |
| 19 | +_ = sub.Settle(ctx, m, source.Ack()) |
| 20 | +``` |
| 21 | + |
| 22 | +Construct an `Inlet` with `New` and functional options (`WithAddr`/`WithClient`, |
| 23 | +`WithGroup`, `WithConsumer`, `WithDLQStream`, `WithBlock`, `WithCount`, |
| 24 | +`WithMinIdle`). The returned `Subscription` reads entries with `Next` (via |
| 25 | +`XREADGROUP`) and applies a handler `source.Result` with `Settle`. Backpressure |
| 26 | +comes from the `Count` batch size plus the block window. Reach the underlying |
| 27 | +`*redis.Client` through `Inlet.As`, or the per-entry `redis.XMessage` through |
| 28 | +`Message.As`; no vendor type appears in the adapter's own signatures. |
| 29 | + |
| 30 | +A stream entry maps onto `source.Message` as follows: the `value` field becomes |
| 31 | +the raw `Value`, the `crucible-key` field (when set) becomes the routing `Key` |
| 32 | +(otherwise the stream name), every field is exposed as a `Header`, `Subject` is |
| 33 | +the stream, and `Cursor` is the entry ID. |
| 34 | + |
| 35 | +## Settle vocabulary |
| 36 | + |
| 37 | +A consumer group reads with `XREADGROUP`; every delivered entry stays in the |
| 38 | +group's Pending Entries List (PEL) until it is settled. |
| 39 | + |
| 40 | +- **Ack** calls `XACK`, removing the entry from the PEL. |
| 41 | +- **Nak** leaves the entry in the PEL. A consumer reclaims and redelivers it by |
| 42 | + scanning the backlog with `XPENDING` + `XCLAIM` once it has idled past the |
| 43 | + minimum; call `NakRedeliver` (on a timer or between read cycles) to drive that |
| 44 | + pass. The cadence is a deployment choice, so the engine does not force it. |
| 45 | +- **Term** (and **Reject**) append the entry's fields plus dead-letter metadata |
| 46 | + (`crucible-dlq-original-id`, `crucible-dlq-stream`, `crucible-dlq-class`, |
| 47 | + `crucible-dlq-error`) to the configured `WithDLQStream`, then `XACK` the |
| 48 | + original. With no dead-letter stream configured, a terminated entry is acked |
| 49 | + and dropped. |
| 50 | +- **InProgress** is a no-op: Redis has no per-message deadline to extend. |
| 51 | +- **Manual** is a no-op; the handler settled the entry itself through |
| 52 | + `Message.As` and the client. |
| 53 | + |
| 54 | +## Capabilities |
| 55 | + |
| 56 | +The subscription honestly advertises the Redis-shaped capabilities by type |
| 57 | +assertion: `source.SharedDurable` (the consumer group is the competing-consumer |
| 58 | +analog), `source.Seekable` (replay by entry ID via `XRANGE`, with `SeekToTime` |
| 59 | +translating a timestamp into an entry ID), and `source.LagReporter` (group lag |
| 60 | +from `XINFO GROUPS`, falling back to `XLEN`). |
| 61 | + |
| 62 | +## Redis divergences from the source contract |
| 63 | + |
| 64 | +- **No `ConsumerGroups`.** A Redis consumer group has no partitions and no |
| 65 | + assignment lifecycle, so the adapter does not implement |
| 66 | + `source.ConsumerGroups`. The group load-balances across processes instead, |
| 67 | + surfaced as `source.SharedDurable`. `PartitionKey` is always `""`, so the |
| 68 | + Hopper shards by `Key`. |
| 69 | +- **No `Transactional`.** Redis Streams offer no consume-side transaction, so |
| 70 | + the adapter does not implement `source.Transactional`; the capability is |
| 71 | + absent rather than faked. |
| 72 | + |
| 73 | +## Stability |
| 74 | + |
| 75 | +Experimental (pre-v1). The API may change until the suite locks v1.0.0. |
| 76 | + |
| 77 | +## License |
| 78 | + |
| 79 | +Apache-2.0. See [LICENSE](../../LICENSE) and [NOTICE](../../NOTICE). |
0 commit comments