Skip to content

Commit

Permalink
Add multi-region example
Browse files Browse the repository at this point in the history
Signed-off-by: Byron Ruth <byron@nats.io>
  • Loading branch information
bruth committed Mar 24, 2023
1 parent fb5cae0 commit 8014c81
Show file tree
Hide file tree
Showing 17 changed files with 788 additions and 0 deletions.
23 changes: 23 additions & 0 deletions examples/topologies/multi-region/cli/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM golang:1.19-alpine3.17 AS build

RUN apk update && apk add git

RUN go install github.com/nats-io/nats-server/v2@v2.9.15
RUN go install github.com/nats-io/natscli/nats@main
RUN go install github.com/nats-io/nsc/v2@v2.7.8

FROM alpine:3.17

RUN apk add bash curl

COPY --from=build /go/bin/nats-server /usr/local/bin/
COPY --from=build /go/bin/nats /usr/local/bin/
COPY --from=build /go/bin/nsc /usr/local/bin/

WORKDIR /app

COPY . .

ENTRYPOINT ["bash"]

CMD ["main.sh"]
23 changes: 23 additions & 0 deletions examples/topologies/multi-region/cli/GLOBAL.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "GLOBAL",
"subjects": [
"js.in.global.>"
],
"retention": "limits",
"max_consumers": -1,
"max_msgs_per_subject": -1,
"max_msgs": -1,
"max_bytes": -1,
"max_age": 0,
"max_msg_size": -1,
"storage": "file",
"discard": "old",
"num_replicas": 3,
"duplicate_window": 120000000000,
"sealed": false,
"deny_delete": false,
"deny_purge": false,
"allow_rollup_hdrs": false,
"allow_direct": false,
"mirror_direct": false
}
39 changes: 39 additions & 0 deletions examples/topologies/multi-region/cli/ORDERS_CENTRAL.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "ORDERS_CENTRAL",
"subjects": [
"js.in.orders_central"
],
"retention": "limits",
"max_consumers": -1,
"max_msgs_per_subject": -1,
"max_msgs": -1,
"max_bytes": -1,
"max_age": 0,
"max_msg_size": -1,
"storage": "file",
"discard": "old",
"num_replicas": 3,
"duplicate_window": 120000000000,
"placement": {
"cluster": "",
"tags": [
"region:central"
]
},
"sources": [
{
"name": "ORDERS_EAST",
"filter_subject": "js.in.orders_east"
},
{
"name": "ORDERS_WEST",
"filter_subject": "js.in.orders_west"
}
],
"sealed": false,
"deny_delete": false,
"deny_purge": false,
"allow_rollup_hdrs": false,
"allow_direct": false,
"mirror_direct": false
}
39 changes: 39 additions & 0 deletions examples/topologies/multi-region/cli/ORDERS_EAST.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "ORDERS_EAST",
"subjects": [
"js.in.orders_east"
],
"retention": "limits",
"max_consumers": -1,
"max_msgs_per_subject": -1,
"max_msgs": -1,
"max_bytes": -1,
"max_age": 0,
"max_msg_size": -1,
"storage": "file",
"discard": "old",
"num_replicas": 3,
"duplicate_window": 120000000000,
"placement": {
"cluster": "",
"tags": [
"region:east"
]
},
"sources": [
{
"name": "ORDERS_WEST",
"filter_subject": "js.in.orders_west"
},
{
"name": "ORDERS_CENTRAL",
"filter_subject": "js.in.orders_central"
}
],
"sealed": false,
"deny_delete": false,
"deny_purge": false,
"allow_rollup_hdrs": false,
"allow_direct": false,
"mirror_direct": false
}
39 changes: 39 additions & 0 deletions examples/topologies/multi-region/cli/ORDERS_WEST.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "ORDERS_WEST",
"subjects": [
"js.in.orders_west"
],
"retention": "limits",
"max_consumers": -1,
"max_msgs_per_subject": -1,
"max_msgs": -1,
"max_bytes": -1,
"max_age": 0,
"max_msg_size": -1,
"storage": "file",
"discard": "old",
"num_replicas": 3,
"duplicate_window": 120000000000,
"placement": {
"cluster": "",
"tags": [
"region:west"
]
},
"sources": [
{
"name": "ORDERS_EAST",
"filter_subject": "js.in.orders_east"
},
{
"name": "ORDERS_CENTRAL",
"filter_subject": "js.in.orders_central"
}
],
"sealed": false,
"deny_delete": false,
"deny_purge": false,
"allow_rollup_hdrs": false,
"allow_direct": false,
"mirror_direct": false
}
107 changes: 107 additions & 0 deletions examples/topologies/multi-region/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Multi Region NATS Cluster

This demonstrates a very basic NATS Cluster using only routes across 3 regions.

This should be used in cases where a multi region setup is needed and one requires a stream to be globally deployed and have globally consistent message deduplication.

## Constraints

* This should be deployed in networks with generally below 100ms latency. For example in GCP using Tier-1 network connectivity in US east, west and central.
* Streams should generally be R3 when stretched out of a single region to mitigate the big exposure they would have to latency if R5
* Multi-region streams should not be the default, ideally these are only used for those cases where global deduplication is needed
* For general streams where eventual consistency is acceptible streams should be bound to a region and replicated into others if desired

## Configuration guidelines

### Server Tags

Each server is tagged with a regional tag, for example, `region:east` or `region:west`. The servers are configured with anti-affinity on this tag using the following configuration:

```
jetstream {
unique_tag: "region:"
}
```

This ensures that streams that are not specifically bound to a single region will be split across the 3 regions.

### Configuring single region streams

```
$ nats stream add --tag region:east --replicas 3 EAST
...
Cluster Information:
Name: c1
Leader: n1-east
Replica: n2-east, current, seen 1ms ago
Replica: n3-east, current, seen 0s ago
```

Note the servers are `n1-east`, `n2-east`, `n3-east`.

### Configuring a global stream

```
$ nats stream add --replicas 3 GLOBAL
...
Cluster Information:
Name: c1
Leader: n1-central
Replica: n2-east, current, seen 0s ago
Replica: n3-west, current, seen 0s ago
```

Note here we give no placement directive so the server will spread it across regions due to the `unique_tag` setting. Servers are `n1-central`, `n2-east` and `n3-west` - 1 per region.

### Configuring replicated streams

To facilitate an eventually consistent but single config set up we show how to create 3 streams:

* `ORDERS_EAST` listening on subjects `js.in.orders_east`
* `ORDERS_WEST` listening on subjects `js.in.orders_west`
* `ORDERS_CENTRAL` listening on subjects `js.in.orders.central`

We then use server mappings in each region to map `js.in.orders` to the in-region subject:

```
accounts {
one: {
jetstream: enabled
mappings: {
js.in.orders: js.in.orders_central
}
}
}
```

We can now publish to one subject and depend on which region we connect to the local stream will handle - then replicate - the data:

```
$ nats --context contexts/central-user.json req js.in.orders 1
{"stream":"ORDERS_CENTRAL", "seq":4}
$ nats --context contexts/east-user.json req js.in.orders 1
{"stream":"ORDERS_EAST", "seq":5}
```

After a short while all streams hold the same data.

```
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Stream Report │
├────────────────┬─────────┬──────────────────────┬───────────┬──────────┬───────┬──────┬─────────┬─────────────────────────────────────┤
│ Stream │ Storage │ Placement │ Consumers │ Messages │ Bytes │ Lost │ Deleted │ Replicas │
├────────────────┼─────────┼──────────────────────┼───────────┼──────────┼───────┼──────┼─────────┼─────────────────────────────────────┤
│ GLOBAL │ File │ │ 0 │ 0 │ 0 B │ 0 │ 0 │ n1-central, n2-west*, n3-east │
│ ORDERS_CENTRAL │ File │ tags: region:central │ 0 │ 5 │ 399 B │ 0 │ 0 │ n1-central, n2-central*, n3-central │
│ ORDERS_EAST │ File │ tags: region:east │ 0 │ 5 │ 405 B │ 0 │ 0 │ n1-east*, n2-east, n3-east │
│ ORDERS_WEST │ File │ tags: region:west │ 0 │ 5 │ 456 B │ 0 │ 0 │ n1-west*, n2-west, n3-west │
╰────────────────┴─────────┴──────────────────────┴───────────┴──────────┴───────┴──────┴─────────┴─────────────────────────────────────╯
```

This allows portable publishers to be built, consumers will need to know the region they are in and bind to the correct stream.


56 changes: 56 additions & 0 deletions examples/topologies/multi-region/cli/configs/cluster-central.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
port: 4222
monitor_port: 8222
server_name: $NAME
client_advertise: $ADVERTISE

server_tags: [$GATEWAY, $REGION]

cluster {
port: 6222

routes = [
nats-route://n1-east:6222
nats-route://n2-east:6222
nats-route://n3-east:6222
nats-route://n1-west:6222
nats-route://n2-west:6222
nats-route://n3-west:6222
nats-route://n1-central:6222
nats-route://n2-central:6222
nats-route://n3-central:6222
]
}

leafnodes {
port: 7422
}

gateway {
name: $GATEWAY
port: 7222
}

jetstream {
store_dir: /data
unique_tag: "region:"
}

accounts {
one: {
jetstream: enabled
users = [
{user: one, password: secret}
]
mappings: {
js.in.orders: js.in.orders_central
}
}

system: {
users = [
{user: system, password: secret}
]
}
}

system_account: system
57 changes: 57 additions & 0 deletions examples/topologies/multi-region/cli/configs/cluster-east.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
port: 4222
monitor_port: 8222
server_name: $NAME
client_advertise: $ADVERTISE

server_tags: [$GATEWAY, $REGION]

cluster {
port: 6222

routes = [
nats-route://n1-east:6222
nats-route://n2-east:6222
nats-route://n3-east:6222
nats-route://n1-west:6222
nats-route://n2-west:6222
nats-route://n3-west:6222
nats-route://n1-central:6222
nats-route://n2-central:6222
nats-route://n3-central:6222
]
}

leafnodes {
port: 7422
}

gateway {
name: $GATEWAY
port: 7222
}

jetstream {
store_dir: /data
unique_tag: "region:"
}

accounts {
one: {
jetstream: enabled
users = [
{user: one, password: secret}
]

mappings: {
js.in.orders: js.in.orders_east
}
}

system: {
users = [
{user: system, password: secret}
]
}
}

system_account: system
Loading

1 comment on commit 8014c81

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for nats-by-example ready!

✅ Preview
https://nats-by-example-ll2nsni3m-connecteverything.vercel.app

Built with commit 8014c81.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.