# 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