NATS Logo by Example

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.

CLI Go Python Deno Node Rust C# Java Ruby Elixir C
Jump to the output or the recording
$ nbe run use-cases/cross-region-streams-cluster/cli
View the source code or learn how to run this example yourself

Code

#!/bin/bash


set -euo pipefail

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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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
}
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

Network 1d3d8cca_default  Creating
Network 1d3d8cca_default  Created
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    │ IP        │ Version     │ JS  │ Conns │ Subs  │ Routes │ GWs │ Mem     │ CPU │ Slow │ Uptime │ RTT │
├─────────┼────────────┼───────────┼─────────────┼─────┼───────┼───────┼────────┼─────┼─────────┼─────┼──────┼────────┼─────┤
│ rg1-az1 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 1     │ 424   │ 8      │ 0   │ 15 MiB  │ 0.0 │ 0    │ 9.07s  │ 6ms │
│ rg3-az3 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 14 MiB  │ 2.0 │ 0    │ 1.06s  │ 7ms │
│ rg2-az1 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 14 MiB  │ 2.0 │ 0    │ 6.07s  │ 7ms │
│ rg2-az2 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 12 MiB  │ 0.0 │ 0    │ 5.07s  │ 7ms │
│ rg1-az2 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 11 MiB  │ 1.0 │ 0    │ 8.07s  │ 7ms │
│ rg1-az3 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 14 MiB  │ 2.0 │ 0    │ 7.07s  │ 7ms │
│ rg2-az3 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 15 MiB  │ 0.0 │ 0    │ 4.07s  │ 7ms │
│ rg3-az2 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 12 MiB  │ 1.0 │ 0    │ 2.06s  │ 8ms │
│ rg3-az1 │ c1         │ 0.0.0.0   │ 2.9.0-RC.11 │ yes │ 0     │ 424   │ 8      │ 0   │ 12 MiB  │ 1.0 │ 0    │ 3.06s  │ 8ms │
├─────────┼────────────┼───────────┼─────────────┼─────┼───────┼───────┼────────┼─────┼─────────┼─────┼──────┼────────┼─────┤
│         │ 1 Clusters │ 9 Servers │             │ 9   │ 1     │ 3,816 │        │     │ 121 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       │
│ rg3-az2  │ 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       │
│ 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       │
│ rg2-az1  │ 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       │
│ rg1-az3  │ 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       │
├──────────┼─────────┼─────────┼───────────┼──────────┼───────┼────────┼──────┼─────────┼─────────┤
│          │         │ 0       │ 0         │ 0        │ 0 B   │ 0 B    │ 0 B  │ 0       │ 0       │
╰──────────┴─────────┴─────────┴───────────┴──────────┴───────┴────────┴──────┴─────────┴─────────╯

╭────────────────────────────────────────────────────╮
│            RAFT Meta Group Information             │
├─────────┬────────┬─────────┬────────┬────────┬─────┤
│ Name    │ Leader │ Current │ Online │ Active │ Lag │
├─────────┼────────┼─────────┼────────┼────────┼─────┤
│ rg1-az1 │ yes    │ true    │ true   │ 0.00s  │ 0   │
│ rg1-az2 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg1-az3 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg2-az1 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg2-az2 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg2-az3 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg3-az1 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg3-az2 │        │ true    │ true   │ 0.33s  │ 0   │
│ rg3-az3 │        │ true    │ true   │ 0.33s  │ 0   │
╰─────────┴────────┴─────────┴────────┴────────┴─────╯
Stream EVENTS was created

Information for Stream EVENTS created 2022-08-27T09:59:28Z

Configuration:

             Subjects: events.*
     Acknowledgements: true
            Retention: File - Limits
             Replicas: 3
       Discard Policy: Old
     Duplicate Window: 2m0s
    Allows Msg Delete: true
         Allows Purge: true
       Allows Rollups: true
     Maximum Messages: unlimited
        Maximum Bytes: unlimited
          Maximum Age: unlimited
 Maximum Message Size: unlimited
    Maximum Consumers: unlimited
       Placement Tags: xr:123


Cluster Information:

                 Name: c1
               Leader: rg1-az1
              Replica: rg2-az2, current, seen 0.00s ago
              Replica: rg3-az3, current, seen 0.00s ago

State:

             Messages: 0
                Bytes: 0 B
             FirstSeq: 0
              LastSeq: 0
     Active Consumers: 0
Stream ORDERS was created

Information for Stream ORDERS created 2022-08-27T09:59:29Z

Configuration:

             Subjects: orders.*
     Acknowledgements: true
            Retention: File - Limits
             Replicas: 3
       Discard Policy: Old
     Duplicate Window: 2m0s
    Allows Msg Delete: true
         Allows Purge: true
       Allows Rollups: true
     Maximum Messages: unlimited
        Maximum Bytes: unlimited
          Maximum Age: unlimited
 Maximum Message Size: unlimited
    Maximum Consumers: unlimited
       Placement Tags: rg:2


Cluster Information:

                 Name: c1
               Leader: rg2-az1
              Replica: rg2-az2, current, seen 0.00s ago
              Replica: rg2-az3, current, not seen

State:

             Messages: 0
                Bytes: 0 B
             FirstSeq: 0
              LastSeq: 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 │
╰────────┴─────────┴──────────────┴───────────┴──────────┴───────┴──────┴─────────┴────────────────────────────╯

Recording

Note, playback is half speed to make it a bit easier to follow.