NATS Golang

Benifits: 1. Outbound connections typically are easier to control security wise because most scrutiny is on inbound connections. So you can make two services talk securely much easier by just both connecting to an exposed nats service than to expose traffic directly on each service. 1. Easy mode multicasting, the service doesn't have to be aware of all subscribers the subscribers can come and go as they please without the service having to remember them all. 1. Easy mode networking/routing (shift left), all you need to do is connect all the NATs nodes together, the service doesn't need to know where the others services live. All network, and authorization setup (easy mode firewall rules) is for NATs clusters only, services just get to focus on subject names. 1. Easy mode access, one connection is all a service needs to setup to be able to talk to all other relivant services. 1. Easy mode connection management, instead of needing to manage a connection to all interested parties, you just have to connect to NATs local and each NATs local maintains a per node connection with each other. 1. Can keep request / response model if async isn't wanted but still get other benefits. 1. import / export subjects allows a import to have a different name than the export which is helpful for deconflicting nameing convention issues between teams. 1. Help with deconflicting issues between different teams, who's fault is it when an API call fails is pretty clear when the message persists in NATs Jetstream.

Terminology: - Jetstream Domains help individual jetstream consumers distinguish between different jetstream control planes. They don't help with Core NATs routing so generally best to just stick with subject namespacing to help with labeling/routing - Account is a hard boundary around subjects. Cannot cross boundary unless subject exports and imports modified. Prefered over user permission boundaries when deconflicting subject names is expensive like cross department teams. It also keeps user permissions scoped to a single account which can greatly simplify user permissions management across a supercluster. - Subject a unique identifier for a channel that messages travel through. Musticasting is implicit as in a single subscriber means unicast and more than one subscriber means multicast. - Leaf Node is the name you give a cluster if you connect that cluster to another cluster through a leaf node connection. This is helpful for bridging different NATs clusters together to allow traffic from services talking over one cluster to services talking over another cluster - Jetstream (also known as streams) are durable communication paths - Ephemeral Consumer is a Jetstream subscriber that is destroyed when it disconnects. So if the connection is established again a new consumer is created and has to choose where to pick up reading from the stream again. Good for monitoring if you don't care about historical - If you want to have time series and say paginate backwards through the stream, you can use "direct stream reads" - Durable Consumer is a Jetstream subscriber that has a ACK feature that lets you confirm the message once recieved. Ideal for must process at least once pattern. - Exchange Account just a unofficial name for an account that is being used as part of a leaf node link to seperate different cluster accounts that you want to remain segragated regardless of account name and subject name overlap between spoke clusters (assuming hub and spoke model as example). - Benefits over direct link without exchange account: - The service’s complete account namespace remains local. - Only explicitly exported subjects enter LINK_A. - Hub uses one connection to HUB_APP. <- In contrast to the design where hub app owns a dedicated account per spoke SPOKE_ACCOUNT -> SPOKE_EDGE_ACCOUNT - HUB_APP gets service-qualified subjects. - Import prefix is a namespace identifier that can be added to all subjects imported from another account. This is useful if your a hub service and need to distinguish between multiple services from different NATs clusters. - Account mappings is a way to specify what the exported subject name should map to for any consuming entities - WorkQueuePolicy, nats jetstream supports removing the message from jetstream after AckSync(), which is similiar to what GCP Pub/Sub does and keeps retention simple. - Stream Types - Normal stream Your application publishes into it. - Mirror stream Your application cannot publish to it. Its contents come from another stream. - Sourced stream A sourced stream also gets its data from other streams, but it can have multiple origins.

Recommendation when delivery consistency is the priority

Use:

This ensures that successfully acknowledged messages are removed from the stream, while unprocessed messages remain queued.

If the stream reaches its configured storage or message limits, JetStream rejects new publishes rather than deleting unprocessed work. Consumers can continue draining the queue, and producers must retry or otherwise preserve rejected events.

This design is appropriate when:

Recommendation when uninterrupted ingestion is the priority

Use:

This creates a bounded ring buffer. When the stream reaches its configured limits, JetStream removes the oldest messages to make room for new ones.

This design is appropriate for use cases such as:

Publishing remains available even when consumers fall behind, but lagging consumers or mirrors may permanently miss messages that are evicted.

End-to-end consistency is therefore only guaranteed when an external recovery mechanism exists, such as a database snapshot, reconciliation process, CDC source, or historical backfill.

Consumer Configurations

When in doubt, choose Pull Consumer

Recommendation for durable application processing

Use:

Consume() provides continuous, low-latency delivery while retaining the flow-control advantages of a pull consumer.

Because the client controls how many messages it requests and buffers, processing capacity and concurrency can be bounded more naturally than with a push consumer. Traffic spikes accumulate in the stream rather than immediately overwhelming the application, subject to the stream's configured retention and storage limits.

This is generally the preferred configuration for:

Recommendation for lightweight or subject-based delivery

Use:

A push consumer is appropriate when the server should deliver messages immediately to a NATS subject and the receiving application can consistently keep up with the delivery rate.

Typical use cases include:

Push consumers still support durable, acknowledged delivery, but require more careful configuration of MaxAckPending, flow control, heartbeats, and client-side processing capacity to avoid slow-consumer problems.

Security

Accounts

If your team connects your NATs cluster to ours and your using the default $G account we strongly recommend you create explicit account(s) first. Making this decision later will require migrations that scale in complexity to your NATs setup.

The rule of thumb for defining account boundaries is one account per team. This reduces encapsulates management of your NATs resources to your internal team and provides clear exposure contracts with other teams as needed via imports / exports.

Users

Recommended one super user used for creating / migrating streams and consumers.

Recommend one user per service to keep least privileaged decisions obvious.

Disaster Recovery

The original data store (postgres, mysql, etc) should be the source of truth for things like disaster recovery or even just bootstrap, reconcile. NATs Jetstream should really only provide durability and consistency for data in transport, because otherwise we have to worry about our datastore and NATs going out of sync with each other and not having a clear plan to recovery from that.

Traffic Jams

NATs delivers messages at least once so there is a chance a lot of messages end up in the queue

Deduplication

Reference