Welcome back again!
Welcome to my adventure series on the NATS JetStream Key/Value Store! This is Part 3 of our series exploring the NATS JetStream KV Store! Let's just get to it and keep exploring!
Where did we leave off??
We're were just about to start messing around with lockers… I mean keys! I gotta stop doing that!
Creating Keys
Ok - let's add some keys to these Buckets. For this, we're going to have a Pop Quiz! I want you to delete any Buckets that exist.
What's that? We don't like Pop Quizzes? Ok fine. No Pop Quiz. Just run these commands.
$ nats kv ls # list your buckets
$ nats kv del <bucket> # delete them
Now starting fresh let's make a new Bucket.
$ nats kv add Bucket1
Information for Key-Value Store Bucket Bucket1 created 2025-02-07T12:37:39-05:00
Configuration:
Bucket Name: Bucket1
History Kept: 1
Values Stored: 0
Compressed: false
Backing Store Kind: JetStream
Bucket Size: 0 B
Maximum Bucket Size: unlimited
Maximum Value Size: unlimited
Maximum Age: unlimited
JetStream Stream: KV_Bucket1
Storage: File
Cluster Information:
Name:
Leader: NAHBJSFROLS2WSIXPPDBIUZXWJX6XIAKK36PTRF72ONLECU22XPPNV23
As we saw earlier from nats kv -h
, we can use the create
command to create keys.
Let's try it!
$ nats kv create Bucket1 Key1
Empty output from the response? Let's try again!
$ nats kv create Bucket1 Key1
nats: error: nats: wrong last sequence: 1: key exists
Hmm, what's going on? Let's check the create
command again.
$ nats kv
nats kv
error: a subcommand from the list below is required, use --help for full help including flags and arguments
usage: nats kv <command> [<args> ...]
Interacts with a JetStream based Key-Value store
The JetStream Key-Value store uses streams to store key-value pairs for an indefinite period
or a per-bucket configured TTL.
Subcommands:
kv add Adds a new KV Store Bucket
kv put Puts a value into a key
kv get Gets a value for a key
kv create Puts a value into a key only if the key is new or it's last operation was a
delete
kv update Updates a key with a new value if the previous value matches the given revision
kv del Deletes a key or the entire bucket
kv purge Deletes a key from the bucket, clearing history before creating a delete marker
kv history Shows the full history for a key
kv revert Reverts a value to a previous revision using put
kv info View the status of a KV store
kv watch Watch the bucket or a specific key for updated
kv ls List available buckets or the keys in a bucket
kv compact Reclaim space used by deleted keys
Pass --help to see global flags applicable to this command.
Ok so, it will only create a new key if:
- The key is new.
- The Key's last operation was a delete.
Ok the key isn't new since we had created it before and it also wasn't previously deleted. This makes sense. It was weird though because we had an empty output upon creation.
Let's check the Bucket.
$ nats kv ls Bucket1
Key1
Ok so the key was definitely created! Let's get it's value.
Reading Keys
Let's check the keys value. For this we can use get
.
$ nats kv get Bucket1 Key1
Bucket1 > Key1 revision: 1 created @ 19 Jan 25 03:54 UTC
Once again, we can see that the key is indeed present, along with extra details like its revision and the time it was created.
We’ll cover those details soon, but if you just want the value itself, you can use the raw
flag.
$ nats kv get --raw Bucket1 Key1
Nothing, and we had gotten an empty response when we created it. I think a picture is starting to emerge.
Let's try something else to be sure. We can check the history
of a key like this.
$ nats kv history Bucket1 Key1
╭──────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬───────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼───────┤
│ Key1 │ 1 │ PUT │ 2025-01-18 22:54:59 │ 0 │ │
╰──────┴──────────┴─────┴─────────────────────┴────────┴───────╯
Ohh ok! The Value was empty when we created the key, remember?
You can see the length of the value is 0. If you recall we did nats kv create Bucket1 Key1
, not nats kv create Bucket1 Key1 Value1
.
We never specified a Value!
Let's thank the history command for solving this mystery. We should probably talk about it!
Key History
So far, we’ve been busy creating keys and checking out their history—but let’s be honest, we’ve been winging it without really understanding.
To clear things up, we need to take a little step back. Let’s start with history (no, not that kind of history, the history of keys!).
When you create a Bucket, you get to decide how it handles keys and keeps track of their changes. Up until now, we’ve been rolling with the default setting, which, by the way, is set to 1.
But what does that even mean? Let’s dig in.
Start fresh again with no Buckets. Delete all the buckets! nats kv del <Bucket>
$ nats kv add --history=3 Bucket1
Information for Key-Value Store Bucket Bucket1 created 2025-02-07T13:01:15-05:00
Configuration:
Bucket Name: Bucket1
History Kept: 3
Values Stored: 0
Compressed: false
Backing Store Kind: JetStream
Bucket Size: 0 B
Maximum Bucket Size: unlimited
Maximum Value Size: unlimited
Maximum Age: unlimited
JetStream Stream: KV_Bucket1
Storage: File
Cluster Information:
Name:
Leader: NAHBJSFROLS2WSIXPPDBIUZXWJX6XIAKK36PTRF72ONLECU22XPPNV23
Notice History Kept: 3
rather than History Kept: 1
. If you backtrack you'll see it was 1 when we made a Bucket without using the history
flag. Go ahead and scroll up and check!
Ok so we have created a Bucket with History Kept: 3
. Let's create a key again and check the history.
$ nats kv create Bucket1 Key1 Value1
Value1
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 1 │ PUT │ 2025-02-07 13:03:56 │ 6 │ Value1 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
Ok this makes sense. We only have one entry in the history because we only just created a key. How do we update the value? I want to see my history!
We can use update
!
Updating a Key
Let's try updating the key.
$ nats kv update Bucket1 Key1 Value2
nats: error: nats: wrong last sequence: 1
Wait what? Maybe we should check what update
does?
Subcommands:
kv add Adds a new KV Store Bucket
kv put Puts a value into a key
kv get Gets a value for a key
kv create Puts a value into a key only if the key is new or it's last operation was a delete
kv update Updates a key with a new value if the previous value matches the given revision
kv del Deletes a key or the entire bucket
kv purge Deletes a key from the bucket, clearing history before creating a delete marker
kv history Shows the full history for a key
kv revert Reverts a value to a previous revision using put
kv info View the status of a KV store
kv watch Watch the bucket or a specific key for updated
kv ls List available buckets or the keys in a bucket
kv compact Reclaim space used by deleted keys
Ahh ok! It will only update the value if the previous value matches the given revision. We didn't give a revision.
What was the revision again?
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 1 │ PUT │ 2025-02-07 13:03:56 │ 6 │ Value1 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
Duh! It was Revision 1
. The first revision! Since we had only created the key. Let's supply the revision!
$ nats kv update Bucket1 Key1 Value2 1
Value2
Bingo Bango Bongo! We got it!
For fun let's check the history again!
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 1 │ PUT │ 2025-02-07 13:03:56 │ 6 │ Value1 │
│ Key1 │ 2 │ PUT │ 2025-02-07 13:09:54 │ 6 │ Value2 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
Oh neat! We have another revision and another entry! This is because we set history=3
! If you had created the Bucket with history=1
or omitted the history flag and went with default, you wouldn't see two entries!
Let's experiment. Create another Bucket with history=1
. Then we will add a key and then update that key and check the history.
$ nats kv add Bucket2
Information for Key-Value Store Bucket Bucket2 created 2025-02-07T13:14:22-05:00
Configuration:
Bucket Name: Bucket2
History Kept: 1
Values Stored: 0
Compressed: false
Backing Store Kind: JetStream
Bucket Size: 0 B
Maximum Bucket Size: unlimited
Maximum Value Size: unlimited
Maximum Age: unlimited
JetStream Stream: KV_Bucket2
Storage: File
Cluster Information:
Name:
Leader: NAHBJSFROLS2WSIXPPDBIUZXWJX6XIAKK36PTRF72ONLECU22XPPNV23
$ nats kv create Bucket2 Key1 Value1
Value1
$ nats kv update Bucket2 Key1 Value2 1
Value2
$ nats kv history Bucket2 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket2 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 2 │ PUT │ 2025-02-07 13:14:32 │ 6 │ Value2 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
See! There's still only 1 entry, whereas Bucket1 has 2 entries! You can see the revision number is 2 but there's still just 1 entry!
Let's mess with Bucket1 again. Let's update the key twice and see what happens.
nats kv update Bucket1 Key1 Value3 2
Value3
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 1 │ PUT │ 2025-02-07 13:03:56 │ 6 │ Value1 │
│ Key1 │ 2 │ PUT │ 2025-02-07 13:09:54 │ 6 │ Value2 │
│ Key1 │ 3 │ PUT │ 2025-02-07 13:16:45 │ 6 │ Value3 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
$ nats kv update Bucket1 Key1 Value4 3
Value4
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 2 │ PUT │ 2025-02-07 13:09:54 │ 6 │ Value2 │
│ Key1 │ 3 │ PUT │ 2025-02-07 13:16:45 │ 6 │ Value3 │
│ Key1 │ 4 │ PUT │ 2025-02-07 13:17:05 │ 6 │ Value4 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
See! 3 entries! We no longer have Revision 1. It's sort of like a stack but revision 1 was replaced when the revisions went over the configured history number.
- Bucket1 has a history of 3 and keeps track of up to 3 revisions.
- Bucket2 has a history of 1 and keeps track of up to 1 revision.
Makes sense.
Ok updating each time and having to provide a revision number can be annoying. What if we just want to change the value and not worry about a revision number?
We can use put
!
Updating a key with put
Like Bernie Sanders, once again I am going to ask you to check our commands.
Subcommands:
kv add Adds a new KV Store Bucket
kv put Puts a value into a key
kv get Gets a value for a key
kv create Puts a value into a key only if the key is new or it's last operation was a delete
kv update Updates a key with a new value if the previous value matches the given revision
kv del Deletes a key or the entire bucket
kv purge Deletes a key from the bucket, clearing history before creating a delete marker
kv history Shows the full history for a key
kv revert Reverts a value to a previous revision using put
kv info View the status of a KV store
kv watch Watch the bucket or a specific key for updated
kv ls List available buckets or the keys in a bucket
kv compact Reclaim space used by deleted keys
Ok so put
just puts a value into our key, nothing else is necessary. Let's try it.
$ nats kv put Bucket1 Key1 Value5
Value5
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 3 │ PUT │ 2025-02-07 13:16:45 │ 6 │ Value3 │
│ Key1 │ 4 │ PUT │ 2025-02-07 13:17:05 │ 6 │ Value4 │
│ Key1 │ 5 │ PUT │ 2025-02-07 13:33:51 │ 6 │ Value5 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
There we go. We changed the value of Key1 to Value5 now. We also increased the revision number in the history which still has 3 entries.
put
makes things easy when you need it to be!
Let's go over the write commands for the keys.
Ok so, what’s the difference between Create, Put and Update?
Create
Ensures a new key is added only if it doesn’t already exist, failing if the key is present. Use when you want to ensure a key is added without risk of overwriting.
Put
Sets a value for a key, creating it if it doesn’t exist or overwriting it if it does. Use for general-purpose operations where overwriting is acceptable.
Update
Updates a key’s value only if its current revision matches a specified version, ensuring safe conditional updates. Use for conditional updates where revision conflicts need to be avoided.
Sweet! Easy-peasy!
There was also another command in there you might have noticed called revert
.
Let's check it out.
Revert
What if you want to revert to a previous value. Well-- it's a great thing we have history! NATS is great man!
$ nats kv history Bucket1 Key1
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 3 │ PUT │ 2025-02-07 13:16:45 │ 6 │ Value3 │
│ Key1 │ 4 │ PUT │ 2025-02-07 13:17:05 │ 6 │ Value4 │
│ Key1 │ 5 │ PUT │ 2025-02-07 13:33:51 │ 6 │ Value5 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
$ nats kv revert Bucket1 Key1 3
Revision: 3
Value3
? Really revert to revision 3 Yes
╭───────────────────────────────────────────────────────────────╮
│ History for Bucket1 > Key1 │
├──────┬──────────┬─────┬─────────────────────┬────────┬────────┤
│ Key │ Revision │ Op │ Created │ Length │ Value │
├──────┼──────────┼─────┼─────────────────────┼────────┼────────┤
│ Key1 │ 4 │ PUT │ 2025-02-07 13:17:05 │ 6 │ Value4 │
│ Key1 │ 5 │ PUT │ 2025-02-07 13:33:51 │ 6 │ Value5 │
│ Key1 │ 6 │ PUT │ 2025-02-07 13:38:09 │ 6 │ Value3 │
╰──────┴──────────┴─────┴─────────────────────┴────────┴────────╯
Sweet! You can see the current Revision 6 has the value of Revision 3 like we asked!
Ok this was a doozy. We're not finished yet. Continue on to Part 4 of our journey as we continue our adventures with the NATS JetStream KV Store CLI!
Top comments (0)