-
Notifications
You must be signed in to change notification settings - Fork 34
JetStream Simplification #167
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
base: main
Are you sure you want to change the base?
Conversation
108e261
to
8227b62
Compare
174b8fe
to
04cc96d
Compare
max = send("max_#{type}") | ||
|
||
if threshold.nil? | ||
instance_variable_set("@threshold_#{type}", max / 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it needs to be possible to also be able to make a batch request with both max msgs and max bytes limits, in the Go client it is an option called: PullMaxMessagesWithBytesLimit
that always makes fetch requests using the same max_bytes https://github.com/nats-io/nats.go/pull/1789/files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the consume config, so now consume
can accept both max_messages
and max_bytes
parameters.
df550c3
to
7195367
Compare
4a58ad4
to
595189b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ran into a few import issues running the examples, this should be ok usage right?
require 'nats'
nc = NATS.connect
jsctx = NATS::JetStream::Context.new(nc)
@@ -0,0 +1,45 @@ | |||
# frozen_string_literal: true | |||
|
|||
require "nats" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried running this example and got the following, maybe there is a missing require?
# bundle exec ruby examples/jetstream/consume.rb
nats-pure.rb/lib/nats/io/client.rb:869:in 'NATS::Client#js': uninitialized constant NATS::JetStream::Context (NameError)
::NATS::JetStream::Context.new(self, options)
^^^^^^^^^
from examples/jetstream/consume.rb:6:in '<main>'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed requires in nats.rb
that caused the issue.
Please note that if you run examples with ruby path/to/example.rb
you need to build and install the gem locally:
gem build
gem install nats-pure-2.5.0.gem
Or you can run bundle console
and copy the code there.
end | ||
alias_method :JetStream, :jetstream | ||
alias_method :jsm, :jetstream | ||
|
||
# Simplified JetStream API | ||
def js(options = {}) | ||
::NATS::JetStream::Context.new(self, options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Running into this when running an example, maybe there is a missing require?
bundle exec ruby examples/jetstream/next.rb
...nats-pure.rb/lib/nats/io/client.rb:869:in 'NATS::Client#js': uninitialized constant NATS::JetStream::Context (NameError)
::NATS::JetStream::Context.new(self, options)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. See #167 (comment)
```ruby | ||
client = NATS.connect | ||
|
||
js = NATS::JetStream::Context.new(client) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you include the requires in the examples as well? I tried this but ran into loading issues
require 'nats'
nc = NATS.connect
jsctx = NATS::JetStream::Context.new(nc)
lib/nats/jetstream/stream/schemas.rb:7:in '<class:Stream>': uninitialized constant NATS::Utils::Config (NameError)
class SubjectTransform < NATS::Utils::Config
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. See #167 (comment)
class JetStream | ||
class Stream | ||
# Subject transform to apply to matching messages going into the stream | ||
class SubjectTransform < NATS::Utils::Config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could it be that this file missing some requires?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. See #167 (comment)
This is an implementation of JetStream Simplification.
Structure
The new API consists of several key objects:
API
The purpose of
Api
object is to simplify working with JetStream API:nats-pure.rb/lib/nats/jetstream/api.rb
Lines 11 to 29 in f548e3f
API endpoints are defined with custom dsl methods
group
andendpoint
. The dsl magic is implemented inDSL
,Group
andEndpoint
classes and uses Ruby meta programming.The
group
method helps to create an endpoints hierarchy. Theendpoint
method defines an endpoint using:request
that defines what options can be sent to the endpoint,response
that describes the structure of the endpoint response,subject
defines whether or not the endpoint accepts a subject as its first field.Different types of requests and responses are described in
api/requests
andapi/response
directories.With the api object, making a request looks just like the request subject, which greatly simplifies the code that uses it. For example, to make an API request to
$JS.API.STREAM.MSG.GET.*
you just writeContext
Since the Legacy JetStream API "occupies" the
JetStream
object, I made a new class,Context
, to be an entry point to the simplified API.It initializes an
Api
instance underneath, provides methods for accessinginfo
andstreams
, and publishing messages withpublish
.Stream
A single stream is represented by the
Stream
class. Most of the Stream subclasses define different internal parts of a stream, such as:Stream::Config
defines a stream config.Stream::Info
defines data that is returned byapi.stream.info
.Stream::State
defines a stream state object.stream/schemas.rb
contains definitions for Info sub-objects, such as SubjectTransfrom, ExternalStream, and StreamSource.Each stream has methods to manage it:
info
,update
,delete
, andpurge
.List
Stream::List
encapsulates the logic of finding, creating and iterating over streams/names. The iteration is implemented via a Ruby Enumerator object that makes subsequentapi.stream.list/names
requests.Consumer
The
Consumer
class represents a consumer. It has a set of sub-classes similar to a stream:Consumer::Config
defines a consumer config.Consumer::Info
defines data that is returned byapi.consumer.info
.consumer/schemas.rb
contains definitions for Info sub-objects, such as Limits and SequenceInfo.A consumer has
info
,update
anddelete
methods that uses api requests underneath.List
Consumer::List
encapsulates the logic of finding, creating and iterating over consumers/names. The iteration is implemented in the same way as for streams: via a Ruby Enumerator object that makes subsequentapi.consumer.list/names
requests.Fetch
The
Consumer::Fetch
wraps up aFetch
pull and adds iterator like behaviour to it. The fetch operation blocks until all the messages are received or the pull expires. For more onFetch
see the Pull section.Message
The
Message
class is a base for JetStream messages that can be divided into 4 groups:stream.messages.find
method, and it has only one methoddelete
.ack
,nack
,term
andin_progress
.Message.build
method builds a JetStream message from a NATS message by checking its "Status" and "Description" headers.Pull
Pull
is the basis for the fetch and consume operations. A pull consists ofEach pull has a status field: pending, processing, draining or closed.
The workflow of the pull has only 2 steps:
start
that starts the pull execution,drain
that starts draining the pull.You can wait until a pull is closed with
#wait
method that blocks until the pull is closed or timeout seconds pass.Fetch
Fetch::Handler
handles messages in the following way:ConsumerMessage
- resets heartbeats, stores the message in the buffer, initiates drain if the buffer is full.IdleHeartbeatMessage
- resets heartbeats.WarningMessage
- stores the message description inlast_error
, drains the pull.ErrorMessager
- stores the message description inlast_error
, drains the pull.Fetch::Buffer
has three methods:fetched(message)
stores a message and tracks the number of messages/bytes fetched so far,full?
returns true if the number of messages/bytes fetched,messages
- an array of fetched messages.Fetch::Heartbeats
sets aConcurrent::ScheduledTask
task for monitoring heartbeats. Every time a consumer or idle heartbeat message is received, the heartbeats task is rescheduled. If no messages are received or the pull request expires, the pull initiates drain.Fetch::Timeout
sets aConcurrent::ScheduledTask
task for monitoring timeouts that will be executed after a fetch requests expires.Consume
Consume::Handler
handles messages in the following way:ConsumerMessage
- resets heartbeats, calls the user defined block, tracks the message in the buffer, requests new messages if the buffer is depleting.IdleHeartbeatMessage
- resets heartbeats.WarningMessage
- resets heartbeats, update the buffer if the message contains "Nats-Pending" headers, requests new messages if the buffer is depleting.ErrorMessager
- stores the message description inlast_error
, drains the pull.Consume::Buffer
has four methods:consumed(message)
tracks the number of messages/bytes pending,depleting?
returns true if the number of messages/bytes pending falls below the threshold,refill
- refills the number of messages/bytes pending indicating that a new batch of messages has been requested,trim(message)
- decrease the number of messages/bytes pending by "Nats-Pending" headers.Consume::Heartbeats
sets aConcurrent::ScheduledTask
task for monitoring heartbeats. The heartbeats task is rescheduled whenever a consumer or idle heartbeat message is received. If no messages are received, the pull requests new messages.Fetch::Connection
registers a new status listener that listens to reconnecting/connected statuses (the idea is inspired by the Go client) and creates a separate thread that monitors the client connection via the status listener and stops/reschedules heartbeats depending on the client status.Publish
A
Publisher
object implements publishing messages. It definespublish
method that:Publisher::Options
,Publisher::Ack
object.nats-pure.rb/lib/nats/jetstream/publisher.rb
Lines 15 to 30 in f548e3f
Legacy vs Simplified API
The Legacy JetStream API is available as usual via
jetstream
andjsm
methodsThe Simplified JetStream API can be accessed through the new
js
method or by creating a JetStream context object: