135 lines
5.3 KiB
YAML
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
|