Skip to content

Commit

Permalink
wip crdt spec
Browse files Browse the repository at this point in the history
Signed-off-by: R.I.Pienaar <[email protected]>
  • Loading branch information
ripienaar committed Jan 9, 2025
1 parent ebb6e32 commit de40992
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ This repository captures Architecture, Design Specifications and Feature Guidanc
|[ADR-42](adr/ADR-42.md)|jetstream, server|Pull Consumer Priority Groups|
|[ADR-43](adr/ADR-43.md)|jetstream, client, server|JetStream Per-Message TTL|
|[ADR-44](adr/ADR-44.md)|jetstream, server|Versioning for JetStream Assets|
|[ADR-48](adr/ADR-48.md)|jetstream|JetStream Distributed Counter CRDT|

## Kv

Expand Down
1 change: 1 addition & 0 deletions adr/ADR-47.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
| 1 | 2024-09-26 | @scottf | Document Initial Design |

## Problem Statement

Have the client support receiving multiple replies from a single request, instead of limiting the client to the first reply,
and support patterns like scatter-gather and sentinel.

Expand Down
69 changes: 69 additions & 0 deletions adr/ADR-48.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# JetStream Distributed Counter CRDT

| Metadata | Value |
|----------|------------|
| Date | 2025-01-09 |
| Author | @ripienaar |
| Status | Approved |
| Tags | jetstream |

| Revision | Date | Author | Info |
|----------|------------|------------|-------------------------|
| 1 | 2025-01-09 | @ripienaar | Document Initial Design |

# Context and Motivation

We wish to provide a distributed counter that will function in Clusters, Super Clusters, through Mirrors and Sources
and any other way that data might reach a Stream.

We will start with basic addition and subtraction primitives which will automatically behave as a CRDT and be order independent.

# Solution Overview

A Stream can opt-in to supporting Counters which will allow any key to be a counter.

Publishing a message to such a Stream will load the most recent message on the subject, increment it's value and save the new message with the latest value.

```bash
$ nats s get COUNTER --last-for counter.hits
Item: COUNTER#22802062 received 2025-01-09 18:05:07.93747413 +0000 UTC on Subject counter.hits

Headers:
Nats-Incr: +2

{"val":100}
$ nats pub counter.hits '' -J -H "Nats-Incr:+1"
$ nats s get COUNTER --last-for counter.hits
Item: COUNTER#22802063 received 2025-01-09 18:06:00 +0000 UTC on Subject counter.hits

Headers:
Nats-Incr: +1

{"val":101}
```

# Design and Behavior

* When publishing a message to the subject the last value is loaded, the body is parsed, incremented and written
into the new message body
* When publishing a message and the previous message do not have a `Nats-Incr` header it means the key is not a
counter, an error is returned to the user and the message is rejected
* When a message with the header is received over a Source processing is done as above
* When a message with the header is received over a Mirror the message is stored verbatim


# Stream Configuration

Weather or not a stream support this behavior should be a configuration opt-in. We want clients to definitely know
when this is supported which the opt-in approach with a boolean on the configuration would make clear.

```golang
type StreamConfig struct {
// AllowMsgCounter enables the feature
AllowMsgCounter bool `json:"allow_msg_counter"`
}
```

This feature can be turned off and on using Stream edits.

A stream with this feature on should require API level 1.

0 comments on commit de40992

Please sign in to comment.