Skip to content

KeyValue.Update does not properly reset TTL #1994

@zluudg

Description

@zluudg

Observed behavior

Calling KeyValue.Update() on a key created with KeyValue.Create() and a custom TTL does not seem to properly reset the TTL. Instead, the key+value stays in the bucket forever.

Expected behavior

I'm expecting a KeyValue.Update() to reset the TTL to the value that was passed when KeyValue.Create() set the key.

Server and client version

Server: 2.12.3
Client: 1.48.0

Host environment

I run the official nats docker image on a Ubuntu 24.04.3 machine. Go version is 1.24.6.

Steps to reproduce

Start a nats server with docker run -p4222:4222 nats:2.12.3 -js.

Then, reproduce the issue with the following snippet:

package main


import (
    "context"
    "fmt"
    "time"
    "sync"

    "github.com/nats-io/nats.go"
    "github.com/nats-io/nats.go/jetstream"
)


func main() {
    ctx := context.Background()
    conn, _ := nats.Connect("nats://localhost:4222")
    js, _ := jetstream.New(conn)

    kv, _ := js.CreateKeyValue(ctx,
    jetstream.KeyValueConfig{
        Bucket: "testbucket",
        LimitMarkerTTL: 20*time.Second,
    })

    kv.Create(
        ctx,
        "foo.bar",
        []byte("bar!"),
        jetstream.KeyTTL(20*time.Second))
    fmt.Println("foo.bar created")

    bazRev, _ := kv.Create(
        ctx,
        "foo.baz",
        []byte("baz!"),
        jetstream.KeyTTL(20*time.Second))
    fmt.Println("foo.baz created")

    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        w, _ := kv.Watch(ctx, "foo.bar")
        for entry := range w.Updates() {
            if entry == nil {
            } else {
                if len(entry.Value()) == 0 {
                    break
                }
                fmt.Printf("foo.bar gave: %s\n", string(entry.Value()))
            }
        }
        fmt.Println("foo.bar value deleted!")
        wg.Done()
    }()

    wg.Add(1)
    go func() {
        w, _ := kv.Watch(ctx, "foo.baz")
        for entry := range w.Updates() {
            if entry == nil {
            } else {
                if len(entry.Value()) == 0 {
                    break
                }
                fmt.Printf("foo.baz gave: %s\n", string(entry.Value()))
            }
        }
        fmt.Println("foo.baz value deleted!")
        wg.Done()
    }()

    time.Sleep(5*time.Second)

    kv.Update(ctx, "foo.baz", []byte("changed!"), bazRev)
    fmt.Println("foo.baz updated from main routine")

    wg.Wait()
}

It gives the output but never terminates:

foo.bar created
foo.baz created
foo.baz gave: baz!
foo.bar gave: bar!
foo.baz updated from main routine
foo.baz gave: changed!
foo.bar value deleted!

I'm expecting a foo.baz value deleted! and program exit after a 5 second delay (approximately).

Metadata

Metadata

Assignees

No one assigned

    Labels

    defectSuspected defect such as a bug or regression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions