---
title: "Challenge #4: Grow-Only Counter"
---

# Challenge #4: Grow-Only Counter

In this challenge, you’ll need to implement a stateless, grow-only counter which will run against Maelstrom’s [`g-counter` workload](https://github.com/jepsen-io/maelstrom/blob/main/doc/workloads.md#workload-g-counter). This challenge is different than before in that your nodes will rely on a [sequentially-consistent](https://jepsen.io/consistency/models/sequential) key/value store service provided by Maelstrom.

## Specification

Your node will need to accept two RPC-style message types: `add` & `read`. Your service need only be eventually consistent: given a few seconds without writes, it should converge on the correct counter value.

Please note that the final read from each node should return the final & correct count.

### RPC: `add`

Your node should accept `add` requests and increment the value of a single global counter. Your node will receive a request message body that looks like this:

```
{
  "type": "add",
  "delta": 123
}
```

and it will need to return an `"add_ok"` acknowledgement message:

```
{
  "type": "add_ok"
}
```

### RPC: `read`

Your node should accept `read` requests and return the current value of the global counter. Remember that the counter service is only [sequentially consistent](https://jepsen.io/consistency/models/sequential). Your node will receive a request message body that looks like this:

```
{
  "type": "read"
}
```

and it will need to return a `"read_ok"` message with the current value:

```
{
  "type": "read_ok",
  "value": 1234
}
```

### Service: `seq-kv`

Maelstrom provides a [sequentially-consistent](https://jepsen.io/consistency/models/sequential) key/value store called [`seq-kv`](https://github.com/jepsen-io/maelstrom/blob/main/doc/services.md#seq-kv) which has `read`, `write`, & `cas` operations. The Go library provides a [`KV`](https://pkg.go.dev/github.com/jepsen-io/maelstrom/demo/go#KV) wrapper for this service that you can instantiate with [`NewSeqKV()`](https://pkg.go.dev/github.com/jepsen-io/maelstrom/demo/go#NewSeqKV):

```
node := maelstrom.NewNode()
kv := maelstrom.NewSeqKV(node)
```

The API is as follows:

```
func (kv *KV) Read(ctx context.Context, key string) (any, error)
    Read returns the value for a given key in the key/value store. Returns an
    *RPCError error with a KeyDoesNotExist code if the key does not exist.

func (kv *KV) ReadInt(ctx context.Context, key string) (int, error)
    ReadInt reads the value of a key in the key/value store as an int.

func (kv *KV) Write(ctx context.Context, key string, value any) error
    Write overwrites the value for a given key in the key/value store.

func (kv *KV) CompareAndSwap(ctx context.Context, key string, from, to any, createIfNotExists bool) error
    CompareAndSwap updates the value for a key if its current value matches the
    previous value. Creates the key if createIfNotExists is true.

    Returns an *RPCError with a code of PreconditionFailed if the previous value
    does not match. Return a code of KeyDoesNotExist if the key did not exist.
```

## Evaluation

Build your Go binary as `maelstrom-counter` and run it against Maelstrom with the following command:

```
./maelstrom test -w g-counter --bin ~/go/bin/maelstrom-counter --node-count 3 --rate 100 --time-limit 20 --nemesis partition
```

This will run a 3-node cluster for 20 seconds and increment the counter at the rate of 100 requests per second. It will induce network partitions during the test.

If you’re successful, right on! Continue on to the [Kafka-Style Log challenge](https://fly.io/dist-sys/5a). If you’re having trouble, ask for help on the [Fly.io Community forum](https://community.fly.io/).

1. **Echo**
   Get the hang of working with Maelstrom in Go by creating a node which is a binary that receives JSON messages
   
   [Read More About Echo](https://fly.io/dist-sys/1)
2. **Unique ID Generation**
   Implement a globally-unique ID generation system that runs against Maelstrom's unique-ids workload
   
   [Read More About Unique ID Generation](https://fly.io/dist-sys/2)
3. **Broadcast**
   Implement a broadcast system that gossips messages between all nodes in the cluster. Gossiping is a common way
   
   [Read More About Broadcast](https://fly.io/dist-sys/3a)
4. **Grow-Only Counter**
   Implement a stateless, grow-only counter which will run against Maelstrom's g-counter workload. This
   
   [Read More About Grow-Only Counter](https://fly.io/dist-sys/4)
5. **Kafka-Style Log**
   Implement a replicated log service similar to Kafka. Replicated logs are often used as a message bus or
   
   [Read More About Kafka-Style Log](https://fly.io/dist-sys/5a)
6. **Totally-Available Transactions**
   Implement a key/value store which implements transactions. These transactions contain micro-operations
   
   [Read More About Totally-Available Transactions](https://fly.io/dist-sys/6a)
