Files
tech-log-backend/infra/redis-sdk/cluster/compose.yml
T

135 lines
5.3 KiB
YAML

# Cluster lane. Six nodes: three primaries so cross-slot behaviour is observable at all, and three
# replicas so a promotion can be forced without losing a shard.
#
# Host networking for the same reason as the Sentinel lane, and a sharper one. A cluster client does
# not talk to one address: it reads `CLUSTER SHARDS`, learns every node's address, and connects to
# each of them itself. On a bridge those addresses are container-internal, so a client on the host
# resolves a topology it cannot dial and every redirect points somewhere unreachable. Sharing the
# host network namespace makes the addresses the cluster advertises the addresses the client can
# use, which is the difference between testing the SDK and testing Docker's network.
#
# Ports are fixed because they are written into the cluster's own configuration at creation time:
# the node identity a redirect names has to be an address the client can dial.
#
# nodes 7100..7105 · cluster bus 17100..17105
#
# The ACL file is loaded on every node. The accounts are the deployment's last enforcement boundary
# and a cluster enforces them per node, so "they exist on one node" is not evidence.
#
# min-replicas-to-write is set here for the same reason as on the Sentinel lane. A cluster promotes
# a replica without asking the client too, so a superseded primary keeps acknowledging writes it
# will discard on resync — the Sentinel lane measured 2,086 of them in one eleven-second window.
# Nothing about slot ownership changes that, and this lane was written without the setting at first
# precisely because the failure mode is easy to think of as Sentinel-specific. It is not.
x-node: &node
image: "redis:${REDIS_VERSION:-7.4}"
network_mode: host
volumes:
- ../acl:/etc/redis/acl:ro
entrypoint:
- /bin/sh
- -c
- |
exec redis-server \
--port $$NODE_PORT \
--cluster-enabled yes \
--cluster-config-file /tmp/nodes.conf \
--cluster-node-timeout 2000 \
--cluster-announce-ip 127.0.0.1 \
--appendonly no \
--save '' \
--min-replicas-to-write 1 \
--min-replicas-max-lag 1 \
--masteruser ca-skeleton-replication \
--masterauth fixture-replication \
--aclfile /etc/redis/acl/all-accounts.acl
healthcheck:
test: ["CMD-SHELL", "[ \"$$(redis-cli -p $$NODE_PORT --user ca-skeleton-application --pass fixture-application --no-auth-warning ping)\" = PONG ]"]
interval: 2s
timeout: 2s
retries: 15
services:
node-1:
<<: *node
environment:
NODE_PORT: "7100"
node-2:
<<: *node
environment:
NODE_PORT: "7101"
node-3:
<<: *node
environment:
NODE_PORT: "7102"
node-4:
<<: *node
environment:
NODE_PORT: "7103"
node-5:
<<: *node
environment:
NODE_PORT: "7104"
node-6:
<<: *node
environment:
NODE_PORT: "7105"
# The cluster is created after every node reports healthy, and the lane is not "up" until every
# slot is covered. A test that starts before slot assignment finishes sees MOVED and CLUSTERDOWN
# for reasons that have nothing to do with the SDK.
init:
image: "redis:${REDIS_VERSION:-7.4}"
network_mode: host
depends_on:
node-1: {condition: service_healthy}
node-2: {condition: service_healthy}
node-3: {condition: service_healthy}
node-4: {condition: service_healthy}
node-5: {condition: service_healthy}
node-6: {condition: service_healthy}
entrypoint:
- /bin/sh
- -c
- |
redis-cli --user ca-skeleton-cluster-bootstrap --pass fixture-bootstrap --no-auth-warning \
--cluster create \
127.0.0.1:7100 127.0.0.1:7101 127.0.0.1:7102 \
127.0.0.1:7103 127.0.0.1:7104 127.0.0.1:7105 \
--cluster-replicas 1 --cluster-yes
# Authenticated, like every other command against this fixture. The `default` user is off,
# so an unauthenticated CLUSTER INFO answers NOAUTH — which never matches, so this loop
# never ended, the helper never exited, and `up --wait` returned on the nodes' own health
# while slot assignment was still in flight. A lane that reports ready before it can serve
# a key produces failures that look like SDK defects and are not.
until redis-cli -p 7100 \
--user ca-skeleton-cluster-bootstrap --pass fixture-bootstrap --no-auth-warning \
cluster info | grep -q 'cluster_state:ok'; do sleep 1; done
echo "cluster ready"
# `up --wait` returns when every service is running or healthy, and a one-shot helper is neither
# for as long as it runs — so the wait ended while slots were still being assigned, and whichever
# test connected first saw a cluster that could not serve its keys. This gate is a service the
# wait can see: it cannot become healthy until the cluster reports a fully covered keyspace.
ready:
image: "redis:${REDIS_VERSION:-7.4}"
network_mode: host
depends_on:
init: {condition: service_completed_successfully}
command: ["sleep", "infinity"]
healthcheck:
test:
- CMD-SHELL
- >-
[ "$$(redis-cli -p 7100 --user ca-skeleton-cluster-bootstrap
--pass fixture-bootstrap --no-auth-warning cluster info
| tr -d '\r' | grep -c '^cluster_state:ok$$')" = 1 ]
interval: 1s
timeout: 3s
retries: 60