Regional and Cross Region Streams (Cluster) in Use Cases
This example shows and alternative to the [supercluster][1] version. Rather than a supercluster, this relies on a nine node cluster spread out across three regions and three availability zones each.
$ nbe run use-cases/cross-region-streams-cluster/cliView the source code or learn how to run this example yourself
Code
#!/bin/bash
set -euo pipefail
NATS_URL="nats://localhost:4222"
Create a shared configuration which enables JetStream and defines the
unique_tag
option which enforces all replicas for a given stream or
consumer to be placed on nodes with different availability zones (AZ).
cat <<- EOF > shared.conf
jetstream: {
unique_tag: "az:"
}
accounts: {
\$SYS: {
users: [{user: sys, password: sys}]
}
APP: {
jetstream: true
users: [{user: user, password: user}]
}
}
EOF
Create the shared clustered config defining the seed routes.
cat <<- EOF > cluster.conf
name: c1
routes: [
nats-route://127.0.0.1:6222,
nats-route://127.0.0.1:6223,
nats-route://127.0.0.1:6224,
]
EOF
Define nine server configurations modeling a cluster with three nodes
in each region across three AZs. NATS does not currently support
declaring tags with logical OR (or exclusive OR), so valid combinations
can be precomputed as tags and then used when creating streams. In this
case, the xr:
tag encodes the cluster/AZ combination while still adhering
to the unique_tag
requirement.
Each index corresponds to a region (e.g us-east) and the value at the
index corresponds to an AZ, e.g. us-east4.
Thus a tag xr:231
can be read as a cross-region stream where one replica
is placed in region 1 in AZ 2, another in region 2 in AZ 3, and the third
replica in region 3 in AZ 1.
In addition to the xr:
tags, the standard rg:
(region) and az:
(AZ)
tags are set to support regional streams which is demonstrated below.
cat <<- EOF > "rg1-az1.conf"
server_name: rg1-az1
server_tags: [rg:1, az:1, xr:123, xr:132]
port: 4222
http_port: 8222
include shared.conf
cluster: {
port: 6222
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg1-az1
}
EOF
cat <<- EOF > "rg1-az2.conf"
server_name: rg1-az2
server_tags: [rg:1, az:2, xr:213, xr:231]
port: 4223
include shared.conf
cluster: {
port: 6223
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg1-az2
}
EOF
cat <<- EOF > "rg1-az3.conf"
server_name: rg1-az3
server_tags: [rg:1, az:3, xr:312, xr:321]
port: 4224
include shared.conf
cluster: {
port: 6224
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg1-az3
}
EOF
cat <<- EOF > "rg2-az1.conf"
server_name: rg2-az1
server_tags: [rg:2, az:1, xr:213, xr:312]
port: 4225
include shared.conf
cluster: {
port: 6225
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg2-az1
}
EOF
cat <<- EOF > "rg2-az2.conf"
server_name: rg2-az2
server_tags: [rg:2, az:2, xr:123, xr:321]
port: 4226
include shared.conf
cluster: {
port: 6226
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg2-az2
}
EOF
cat <<- EOF > "rg2-az3.conf"
server_name: rg2-az3
server_tags: [rg:2, az:3, xr:132, xr:231]
port: 4227
include shared.conf
cluster: {
port: 6227
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg2-az3
}
EOF
cat <<- EOF > "rg3-az1.conf"
server_name: rg3-az1
server_tags: [rg:3, az:1, xr:231, xr:321]
port: 4228
include shared.conf
cluster: {
port: 6228
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg3-az1
}
EOF
cat <<- EOF > "rg3-az2.conf"
server_name: rg3-az2
server_tags: [rg:3, az:2, xr:132, xr:312]
port: 4229
include shared.conf
cluster: {
port: 6229
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg3-az2
}
EOF
cat <<- EOF > "rg3-az3.conf"
server_name: rg3-az3
server_tags: [rg:3, az:3, xr:123, xr:213]
port: 4230
include shared.conf
cluster: {
port: 6230
include cluster.conf
}
jetstream: {
store_dir: /tmp/nats/storage/rg3-az3
}
EOF
Start a server for each configuration and sleep a second in between so the seeds can startup and get healthy.
for c in $(ls rg*.conf); do
echo "Starting server ${c%.*}"
nats-server -c $c > /dev/null 2>&1 &
sleep 1
done
Wait until the servers up and healthy.
echo 'Healthy?'
curl --fail --silent \
--retry 5 \
--retry-delay 1 \
http://localhost:8222/healthz; echo
Show the server lit and JetStream report.
nats --user sys --password sys server list
nats --user sys --password sys server report jetstream
Create a cross-region stream using one of the pre-defined xr:
tags.
nats --user user --password user stream add \
--tag=xr:123 \
--retention=limits \
--storage=file \
--replicas=3 \
--discard=old \
--dupe-window=2m \
--max-age=-1 \
--max-msgs=-1 \
--max-bytes=-1 \
--max-msg-size=-1 \
--max-msgs-per-subject=-1 \
--max-consumers=-1 \
--allow-rollup \
--no-deny-delete \
--no-deny-purge \
--subjects="events.*" \
EVENTS
Create a regional stream which relies on the unique_tag
to ensure
each replicas in a different AZ. This will create a stream in region 2
due to the rg:2
tag
nats --user user --password user stream add \
--tag=rg:2 \
--retention=limits \
--storage=file \
--replicas=3 \
--discard=old \
--dupe-window=2m \
--max-age=-1 \
--max-msgs=-1 \
--max-bytes=-1 \
--max-msg-size=-1 \
--max-msgs-per-subject=-1 \
--max-consumers=-1 \
--allow-rollup \
--no-deny-delete \
--no-deny-purge \
--subjects="orders.*" \
ORDERS
Report out the streams.
nats --user user --password user stream report
Output
Starting server rg1-az1 Starting server rg1-az2 Starting server rg1-az3 Starting server rg2-az1 Starting server rg2-az2 Starting server rg2-az3 Starting server rg3-az1 Starting server rg3-az2 Starting server rg3-az3 Healthy? {"status":"ok"} ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Server Overview │ ├─────────┬─────────┬──────┬─────────┬─────┬───────┬───────┬────────┬─────┬─────────┬───────┬───────┬──────┬────────┬─────┤ │ Name │ Cluster │ Host │ Version │ JS │ Conns │ Subs │ Routes │ GWs │ Mem │ CPU % │ Cores │ Slow │ Uptime │ RTT │ ├─────────┼─────────┼──────┼─────────┼─────┼───────┼───────┼────────┼─────┼─────────┼───────┼───────┼──────┼────────┼─────┤ │ rg1-az1 │ c1 │ 0 │ 2.10.1 │ yes │ 1 │ 508 │ 32 │ 0 │ 18 MiB │ 3 │ 8 │ 0 │ 9.07s │ 1ms │ │ rg1-az3 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 3 │ 8 │ 0 │ 7.04s │ 2ms │ │ rg3-az2 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 4 │ 8 │ 0 │ 2.03s │ 2ms │ │ rg3-az1 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 2 │ 8 │ 0 │ 3.03s │ 2ms │ │ rg1-az2 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 1 │ 8 │ 0 │ 8.06s │ 2ms │ │ rg2-az1 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 3 │ 8 │ 0 │ 6.05s │ 2ms │ │ rg2-az3 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 2 │ 8 │ 0 │ 4.03s │ 2ms │ │ rg2-az2 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 17 MiB │ 2 │ 8 │ 0 │ 5.04s │ 2ms │ │ rg3-az3 │ c1 │ 0 │ 2.10.1 │ yes │ 0 │ 508 │ 32 │ 0 │ 16 MiB │ 6 │ 8 │ 0 │ 1.02s │ 2ms │ ├─────────┼─────────┼──────┼─────────┼─────┼───────┼───────┼────────┼─────┼─────────┼───────┼───────┼──────┼────────┼─────┤ │ │ 1 │ 9 │ │ 9 │ 1 │ 4,572 │ │ │ 154 MIB │ │ │ 0 │ │ │ ╰─────────┴─────────┴──────┴─────────┴─────┴───────┴───────┴────────┴─────┴─────────┴───────┴───────┴──────┴────────┴─────╯ ╭────────────────────────────────────────────────────────────────────────────╮ │ Cluster Overview │ ├─────────┬────────────┬───────────────────┬───────────────────┬─────────────┤ │ Cluster │ Node Count │ Outgoing Gateways │ Incoming Gateways │ Connections │ ├─────────┼────────────┼───────────────────┼───────────────────┼─────────────┤ │ c1 │ 9 │ 0 │ 0 │ 1 │ ├─────────┼────────────┼───────────────────┼───────────────────┼─────────────┤ │ │ 9 │ 0 │ 0 │ 1 │ ╰─────────┴────────────┴───────────────────┴───────────────────┴─────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────╮ │ JetStream Summary │ ├──────────┬─────────┬─────────┬───────────┬──────────┬───────┬────────┬──────┬─────────┬─────────┤ │ Server │ Cluster │ Streams │ Consumers │ Messages │ Bytes │ Memory │ File │ API Req │ API Err │ ├──────────┼─────────┼─────────┼───────────┼──────────┼───────┼────────┼──────┼─────────┼─────────┤ │ rg1-az1* │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg1-az2 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg1-az3 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg2-az1 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg2-az2 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg2-az3 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg3-az1 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg3-az2 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ │ rg3-az3 │ c1 │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ ├──────────┼─────────┼─────────┼───────────┼──────────┼───────┼────────┼──────┼─────────┼─────────┤ │ │ │ 0 │ 0 │ 0 │ 0 B │ 0 B │ 0 B │ 0 │ 0 │ ╰──────────┴─────────┴─────────┴───────────┴──────────┴───────┴────────┴──────┴─────────┴─────────╯ ╭───────────────────────────────────────────────────────────────╮ │ RAFT Meta Group Information │ ├─────────┬──────────┬────────┬─────────┬────────┬────────┬─────┤ │ Name │ ID │ Leader │ Current │ Online │ Active │ Lag │ ├─────────┼──────────┼────────┼─────────┼────────┼────────┼─────┤ │ rg1-az1 │ 6s4h5MOv │ yes │ true │ true │ 0s │ 0 │ │ rg1-az2 │ X8wPeL6S │ │ true │ true │ 950ms │ 0 │ │ rg1-az3 │ FozYtzby │ │ true │ true │ 949ms │ 0 │ │ rg2-az1 │ 8ShHFKtZ │ │ true │ true │ 946ms │ 0 │ │ rg2-az2 │ Kx5WF0Q6 │ │ true │ true │ 950ms │ 0 │ │ rg2-az3 │ NSUWzTzT │ │ true │ true │ 946ms │ 0 │ │ rg3-az1 │ OEQTh4pP │ │ true │ true │ 950ms │ 0 │ │ rg3-az2 │ LJVb57VO │ │ true │ true │ 950ms │ 0 │ │ rg3-az3 │ uEtIi639 │ │ true │ true │ 950ms │ 0 │ ╰─────────┴──────────┴────────┴─────────┴────────┴────────┴─────╯ Stream EVENTS was created Information for Stream EVENTS created 2023-10-23 19:31:50 Subjects: events.* Replicas: 3 Storage: File Placement Tags: xr:123 Options: Retention: Limits Acknowledgments: true Discard Policy: Old Duplicate Window: 2m0s Direct Get: true Allows Msg Delete: true Allows Purge: true Allows Rollups: true Limits: Maximum Messages: unlimited Maximum Per Subject: unlimited Maximum Bytes: unlimited Maximum Age: unlimited Maximum Message Size: unlimited Maximum Consumers: unlimited Cluster Information: Name: c1 Leader: rg2-az2 Replica: rg1-az1, current, seen 716µs ago Replica: rg3-az3, current, seen 198µs ago State: Messages: 0 Bytes: 0 B First Sequence: 0 Last Sequence: 0 Active Consumers: 0 Stream ORDERS was created Information for Stream ORDERS created 2023-10-23 19:31:50 Subjects: orders.* Replicas: 3 Storage: File Placement Tags: rg:2 Options: Retention: Limits Acknowledgments: true Discard Policy: Old Duplicate Window: 2m0s Direct Get: true Allows Msg Delete: true Allows Purge: true Allows Rollups: true Limits: Maximum Messages: unlimited Maximum Per Subject: unlimited Maximum Bytes: unlimited Maximum Age: unlimited Maximum Message Size: unlimited Maximum Consumers: unlimited Cluster Information: Name: c1 Leader: rg2-az3 Replica: rg2-az1, current, seen 45µs ago Replica: rg2-az2, current, seen 1ms ago State: Messages: 0 Bytes: 0 B First Sequence: 0 Last Sequence: 0 Active Consumers: 0 Obtaining Stream stats ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │ Stream Report │ ├────────┬─────────┬──────────────┬───────────┬──────────┬───────┬──────┬─────────┬────────────────────────────┤ │ Stream │ Storage │ Placement │ Consumers │ Messages │ Bytes │ Lost │ Deleted │ Replicas │ ├────────┼─────────┼──────────────┼───────────┼──────────┼───────┼──────┼─────────┼────────────────────────────┤ │ EVENTS │ File │ tags: xr:123 │ 0 │ 0 │ 0 B │ 0 │ 0 │ rg1-az1, rg2-az2*, rg3-az3 │ │ ORDERS │ File │ tags: rg:2 │ 0 │ 0 │ 0 B │ 0 │ 0 │ rg2-az1, rg2-az2, rg2-az3* │ ╰────────┴─────────┴──────────────┴───────────┴──────────┴───────┴──────┴─────────┴────────────────────────────╯