nats-io/natscli

Unable to get a value from a mirror

Kazmirchuk opened this issue · 13 comments

Observed behavior

I have a KV bucket MYKV and its mirror MIRROR_KV that was created as follows:

nats kv add  --history=20 --storage=memory --mirror=MYKV MIRROR_KV

Both buckets exist in the same server and account.

When I put a value into the mirror, I can see in the server log (-DV) that NATS CLI rewrites the subject to the origin bucket as expected:

nats kv put MIRROR_KV key3 val3

[PUB $KV.MYKV.key3 _INBOX.itGcBlv5V82bWxBooq3K4Z.iJvIDoAr 4]
cid:44 - <<- MSG_PAYLOAD: ["val3"]

However, when I try to get a value from the mirror, I get an error "key not found":

nats --trace kv get MIRROR_KV key3

19:25:10 >>> $JS.API.DIRECT.GET.KV_MIRROR_KV.$KV.MIRROR_KV.key3
19:25:10 <<< $JS.API.DIRECT.GET.KV_MIRROR_KV.$KV.MIRROR_KV.key3:
nats: error: nats: key not found

Also, I've checked that MIRROR_KV is in sync with the origin bucket:

nats stream view KV_MIRROR_KV
...
[3] Subject: $KV.MYKV.key3 Received: 2023-11-28T17:14:18+01:00

val3

Expected behavior

I expect to get a key/value from the mirror. NATS CLI should rewrite the subject using the origin bucket name before querying DIRECT.GET

Server and client version

nats --version
0.1.1
nats-server --version
nats-server: v2.10.3

Host environment

No response

Steps to reproduce

No response

Looking at func mapStreamToKVS in nats.go/jetstream/kv.go, I suspect that nats.go might have the same problem, because kv.pre is not overwritten when info.Config.Mirror exists but m.External == nil. But I don't know Go and don't have Go dev.env. I know Python and tried nats.py, and couldn't do either "put" or "get" via a mirror.

Thanks in advance

hmm I see ADR-8 recently has received major updates regarding mirrors... should I read that first?

For mirrors you dont read from them, they are automatically used when reading - the nearest one is used and you cannot use them by name.

If you made a replica by source you could connect to it by name but we have no helpers for that. The only other option is there if you use leafnodes.

Sorry, correction, nats kv does have --source for source based replicas. Mirror as is should only be used with leafnodes and different domains.

thank you! so I should bind to a mirror only when its origin is in another domain.

I'm struggling to understand the test func TestKeyValueMirrorDirectGet in nats.go (and tests in other clients are based on it too of course). Both PUTs and GETs are done on the TEST bucket, so how does it prove that the mirror actually responds to DIRECT.GET? I.e. if I remove js.CreateStream from this test, it will nevertheless pass AFAICS.

sorry for off-topic, and probably questions like these belong to Slack. OTOH people can find answers on Github from googling.

You never can bind to a mirror - its subjects are just wrong. They are there for speeding up reads.

If you make a source based replicas as described in the recent edits to the ADR or using --source in the latest nats release it will be something you can bind to as the messages inside will have been rewritten to have the right subjects.

I suspect the test is just not very good :)

You never can bind to a mirror

but this is exactly what func TestKeyValueMirrorCrossDomains does! 😮

// Bind locally from leafnode and make sure both get and put work.
mkv, err := ljs.KeyValue(ctx, "MIRROR")

(proceeds with testing Put, Get and WatchAll)

Cross domains there's some special handling indeed, personally I'd avoid that. I dont want to support that method for a long time.

The source based method is right for all cases now.

ok, thank you so much for the clarification! I'll send everyone to read the updated ADR-8 :)

sorry for bothering you again, but do you know if any NATS client has (at least draft) implementation of ADR-8 v1.1, i.e. with the latest additions of KVAggregateConfig + KVAggregateOrigin ? I'd like to have some reference while implementing it. I thought that I might find it in some branch in nats.go, but nope =\

No, nothing is at 1.1 yet - feature list not complete either.

Try https://github.com/nats-io/jsm.go/compare/main...ripienaar:jsm.go:kv_topologies?expand=1

Thanks! I've implemented it - quite straightforward and indeed much cleaner than the previous approach with read and write subject prefixes that the client had to figure out based on a mirror configuration.

Do you mind if I provide some feedback about type KVAggregateOrigin? I found it a bit confusing that Stream is mandatory while Bucket is optional, and so users need to remember to prefix the stream name with KV_. What about designing it the other way round, i.e. Bucket is mandatory and Stream is optional (only for the cases when we source from a mirror)? Then users don't need to remember about the "KV_" prefix.

You can see how it looks like here lines 159 (only Bucket) and 198 (both Stream and Bucket). API docs here.

Thanks for the feedback, will check it out. The code I linked was pretty much just me making sure the spec can be implemented :) but will update there thank you!