Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Message Enhancement (Direct API) #115

Closed
12 tasks done
aricart opened this issue May 31, 2022 · 24 comments
Closed
12 tasks done

Get Message Enhancement (Direct API) #115

aricart opened this issue May 31, 2022 · 24 comments
Assignees
Labels
client Client related work enhancement New feature or request kv

Comments

@aricart
Copy link
Member

aricart commented May 31, 2022

Overview

Retrieve messages from a stream in a more efficient way than the get message API ($JS.API.STREAM.MSG.GET.<stream>)

allow_direct

  • Stream configuration adds a new boolean property: allow_direct and mirror_direct
  • allow_direct should be set to true for newly created KVs if the server version allows it.

Direct Get Api

When the property is enabled, clients can lookup messages using the direct JetStream API: $JS.API.DIRECT.GET.<stream> with a payload the same as the get message API:

Seq     uint64 `json:"seq,omitempty"`
LastFor string `json:"last_by_subj,omitempty"`
NextFor string `json:"next_by_subj,omitempty"`
  • A regular message is returned but will not contain the JS meta data (the reply-to will be empty).

  • Examples
    {last_by_subj: string} - get the last message having the subject
    {seq: number} - get a message by sequence
    {next_by_subj: string} - get the first message (lowest seq) having the specified subject
    {seq: number, next_by_subj: string} - get the first message with a seq >= to the input seq that has the specified subject

  • There is a shortcut form for last_by_subj $JS.API.DIRECT.GET.<stream>.<last_by_subj> This for is preferred over the payload version as it can be secured and the subject level.

  • If not an error (404, 408, etc), the request will return the value.
    404 is returned if the request is valid but cannot resolve a message
    408 is returned if the request is empty or invalid.

Headers constants

When getting a message directly, Nats specific headers are added:

  • stream name under Nats-Stream
  • the sequence of the message under Nats-Sequence
  • msg timestamp under Nats-Time-Stamp
  • subject (key in the KV sense): Nats-Subject
  • An additional Nats-Last-Sequence is also possible

Client Behavior

  • The client is expected to be aware of the stream configuration and to use the direct get when it can and use the regular get message when it cannot. This should be done automatically for the user.
  • Both direct and regular should support the next_by_subj field.
  • When creating a KV stream, if the user does not supply max-history-per-key (which is backed by max_msgs_per_subject), the stream should be created with max_msgs_per_subject of 1. Also as a reminder, 64 is currently the current maximum allowed history per key that clients should allow.

References

Clients and Tools

Other Tasks

  • docs.nats.io updated
  • Update ADR to Implemented
  • Update client features spreadsheet

Client authors please update with your progress. If you open issues in your own repositories as a result of this request, please link them to this one by pasting the issue URL in a comment or main issue description.

Example unit test

  1. Create a stream 'stream' with subjects 's1' and 's2', allow_direct set to true
  2. Publish 6 messages, 3 to each subject, alternating subjects. So publish to s1, s2, s1, s2, s1, s2

Then test and validate headers for expected subject and sequence. I checked stream also and that a timestamp was present.

Request Expected Subject Expected Sequence
seq = 1 s1 1
last_by_subj = s1 s1 5
last_by_subj = s2 s2 6
seq <= 0 , next_by_subj = s1 s1 1
seq <= 0 , next_by_subj = s2 s2 2
seq = 1 , next_by_subj = s1 s1 1
seq = 1 , next_by_subj = s2 s2 2
seq = 2 , next_by_subj = s1 s1 3
seq = 2 , next_by_subj = s2 s2 2
seq = 5 , next_by_subj = s1 s1 5
seq = 5 , next_by_subj = s2 s2 6
Regular Request Expected Status
{} bad request [10003]
{"seq":0} bad request [10003]
{"seq":9} no message found [10037]
{"last_by_subj":"not-a-subject"} no message found [10037]
{"next_by_subj":"not-a-subject"} no message found [10037]
{"seq":9,"next_by_subj":"s1"} no message found [10037]
{"seq":1,"next_by_subj":"not-a-subject"} no message found [10037]
Direct Request Expected Status
{} {408, 'Empty Request'}
{"seq":0} {408, 'Empty Request'}
{"seq":9} {404, 'Message Not Found'}
DIRECT.GET.stream.not-a-subject {404, 'Message Not Found'}
{"next_by_subj":"not-a-subject"} {404, 'Message Not Found'}
{"seq":9,"next_by_subj":"s1"} {404, 'Message Not Found'}
{"seq":1,"next_by_subj":"not-a-subject"} {404, 'Message Not Found'}
@derekcollison
Copy link
Member

The value is not json encoded either, nor does it do any API pass through or accounting. So much more efficient.

@aricart
Copy link
Member Author

aricart commented Jun 10, 2022

Feature landed in the server!

@aricart aricart changed the title [PRELIMINARY] Get Message Enhancement Get Message Enhancement Jun 10, 2022
@ripienaar
Copy link
Contributor

While I opted not to add direct get as a feature in the CLI for getting messages I did add --allow-direct as a option when adding streams, but since its so niche its not something being prompted for.

Will report such a stream in s info though

@derekcollison
Copy link
Member

ok for now, but once settled this will be the way for KV etc and possible individual message retrieval directly from a stream.

@aricart
Copy link
Member Author

aricart commented Jun 28, 2022

Note that subject has changed to $JS.API.DIRECT.GET.${stream}

@aricart
Copy link
Member Author

aricart commented Jul 7, 2022

Note that there's a next_by_subj option

@kozlovic
Copy link
Member

kozlovic commented Jul 7, 2022

@derekcollison @aricart From a client library perspective, should we return a "raw" NATS message with the incoming headers, or should we make it a "JS" message where users can access Sequence, Time, etc..?

If the latter, are the headers copied over as-is (including all the Nats-xxx ones)? I am guessing that the original message could have its own headers, so we can't simply strip the whole headers section...

@scottf
Copy link
Collaborator

scottf commented Jul 7, 2022

Are we re-using the existing schema response, stream_msg_get_response, the same as the current get messages? That message object could be expanded to have a reply-to field which is where we pull the js meta data, or just have a js-meta data field.

@derekcollison
Copy link
Member

These are delivered on INBOX (or reply) and are friendly to Request Muxing. They have headers that denote stream, subject, sequence and timestamp.

These are raw messages, not JSON encoded.

@derekcollison
Copy link
Member

These also participate in a queue group so all members, optionally including mirrors, can participate.

@kozlovic
Copy link
Member

kozlovic commented Jul 7, 2022

@derekcollison But regarding my question: should the library return them as-is (that is a raw NATS message) or as a JetStream message? For instance in Go the normal GetMsg() returns a RawStreamMsg that looks like this:

type RawStreamMsg struct {
	Subject  string
	Sequence uint64
	Header   Header
	Data     []byte
	Time     time.Time
}

As I was asking if we return as a RawStreamMsg, do we need to suppress the Nats-xxx headers that were return by the "direct get" API response?

@derekcollison
Copy link
Member

I think JetStream message. Apologies for the confusion. But these can not be ack'd and are not part of a consumer of course.

@aricart
Copy link
Member Author

aricart commented Jul 7, 2022

Likely they shouldn't be a JetStream message as the API shouldn't introduce ambiguity on how it was obtained. If the client provides a different "type" of msg, it should do so if it can. It is possible that in the current Go client these are the same as the Go client doesn't differentiate.

@derekcollison
Copy link
Member

But it is a JetStream message, you got it out of a stream. I think client libs should present a consistent view upwards regardless of low level mechanism to retrieve, e.g. consumer vs stream get vs direct stream get.

@kozlovic
Copy link
Member

kozlovic commented Jul 7, 2022

But it is a JetStream message, you got it out of a stream

I would agree here. Of course, it can't be ack'ed (since it was not received through a subscription), but it is still a JetStream message.

@aricart
Copy link
Member Author

aricart commented Jul 7, 2022

in the case of Go, external functions like ack,etc are not bound to the type. In other languages they are, so typing it as a "JetStreamMsg" would also imply functions that are not applicable, thus the comment that languages should do what is appropriate.

@aricart
Copy link
Member Author

aricart commented Aug 4, 2022

when doing a last_by_subject you can now simply append this subject to the request for the message without any payload. This allows things like KV to be clamped for permissions on GET when using the Direct API. See nats-io/nats.deno#341

@marthaCP marthaCP changed the title Get Message Enhancement Get Message Enhancement (Direct API) Aug 24, 2022
@marthaCP marthaCP added the kv label Aug 25, 2022
@aricart
Copy link
Member Author

aricart commented Sep 6, 2022

Note that the original description of this issue omittedmirror_direct configuration property on the stream, which should also be available in order to enable mirror to use the direct get api

@derekcollison
Copy link
Member

Right now it gets set to true if the origin stream has AllowDirect, just FYI.

@kozlovic
Copy link
Member

kozlovic commented Sep 8, 2022

@aricart We should update the description that the server does NOT set allow_direct to true when max_msgs_per_subject is > 0, this change in the server has been reverted here: nats-io/nats-server#3441

@aricart
Copy link
Member Author

aricart commented Sep 9, 2022

Yes. I marked that on @bruth release worksheet

@aricart
Copy link
Member Author

aricart commented Sep 9, 2022

Removed initial comment where the initial server behaviour of setting allow_direct was set to true by the server by default

@Jarema Jarema removed their assignment Oct 1, 2022
@bruth
Copy link
Member

bruth commented Nov 22, 2022

Stream docs were updated to document the AllowDirect and MirrorDirect options as of nats-io/nats.docs#489.

@bruth
Copy link
Member

bruth commented Nov 22, 2022

Per ADR-31, this is implemented.

@bruth bruth closed this as completed Nov 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
client Client related work enhancement New feature or request kv
Projects
None yet
Development

No branches or pull requests