Class: Cosmo::Client
- Inherits:
-
Object
- Object
- Cosmo::Client
- Defined in:
- lib/cosmo/client.rb,
sig/cosmo/client.rbs
Instance Attribute Summary collapse
-
#js ⇒ Object
readonly
Returns the value of attribute js.
-
#nc ⇒ Object
readonly
Returns the value of attribute nc.
Class Method Summary collapse
Instance Method Summary collapse
- #close ⇒ void
- #consumer_info(stream_name, consumer_name) ⇒ NATS::JetStream::API::ConsumerInfo
-
#create_kv_with_msg_ttl(name, **options) ⇒ Object
NOTE: KV manager in nats-pure hardcodes the fields it copies into StreamConfig, so
allow_msg_ttlis never forwarded via create_key_value. - #create_stream(name, config) ⇒ Object
-
#cron_subjects_in_stream(stream_name, filter) ⇒ Array<String>
Return all subjects in
stream_namethat matchfilterusing NATS's subjects_filter on STREAM.INFO (requires NATS ≥ 2.9). - #delete_consumer(stream_name, consumer_name) ⇒ Boolean
- #delete_message(name, seq) ⇒ Hash[::String, untyped]
- #delete_stream(name, params = {}) ⇒ Object
-
#ephemeral_subscribe(subject, config) ⇒ Object
NOTE: nats-pure's #pull_subscribe has no path to a true ephemeral pull consumer.
- #get_message(stream_name, **options) ⇒ NATS::JetStream::API::RawStreamMsg
-
#initialize(nats_url: ENV.fetch("NATS_URL", "nats://localhost:4222")) ⇒ Client
constructor
A new instance of Client.
- #kv(name, allow_msg_ttl: false, **options) ⇒ Object
- #list_consumers(stream_name) ⇒ Array[Hash[::String, untyped]]
- #list_streams ⇒ Array[Hash[::String, untyped]]
- #pause_stream(name) ⇒ void
- #publish(subject, payload, **params) ⇒ Object
- #purge(stream_name, subject) ⇒ ::Integer?
-
#setup_stream(name, config) ⇒ Object
Create/update a stream, falling back to create when there's no stream.
- #stream_info(name) ⇒ Object
- #stream_paused?(name) ⇒ Boolean
-
#subscribe(subject, consumer_name, config) ⇒ Object
Create a pull subscription.
- #unpause_stream(name) ⇒ void
- #update_stream(name, config) ⇒ Object
Constructor Details
#initialize(nats_url: ENV.fetch("NATS_URL", "nats://localhost:4222")) ⇒ Client
Returns a new instance of Client.
14 15 16 17 18 19 |
# File 'lib/cosmo/client.rb', line 14 def initialize(nats_url: ENV.fetch("NATS_URL", "nats://localhost:4222")) Logger.debug "Connecting to NATS server at #{nats_url}..." @nc = NATS.connect(nats_url) Logger.debug "Connection established" @js = @nc.jetstream end |
Instance Attribute Details
#js ⇒ Object (readonly)
Returns the value of attribute js.
12 13 14 |
# File 'lib/cosmo/client.rb', line 12 def js @js end |
#nc ⇒ Object (readonly)
Returns the value of attribute nc.
12 13 14 |
# File 'lib/cosmo/client.rb', line 12 def nc @nc end |
Class Method Details
Instance Method Details
#close ⇒ void
This method returns an undefined value.
139 140 141 |
# File 'lib/cosmo/client.rb', line 139 def close nc.close end |
#consumer_info(stream_name, consumer_name) ⇒ NATS::JetStream::API::ConsumerInfo
107 108 109 |
# File 'lib/cosmo/client.rb', line 107 def consumer_info(stream_name, consumer_name) js.consumer_info(stream_name, consumer_name) end |
#create_kv_with_msg_ttl(name, **options) ⇒ Object
NOTE: KV manager in nats-pure hardcodes the fields it copies into StreamConfig,
so allow_msg_ttl is never forwarded via create_key_value. Send the raw stream-create API request instead.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/cosmo/client.rb', line 173 def create_kv_with_msg_ttl(name, **) payload = Utils::Json.dump({ name: "KV_#{name}", subjects: ["$KV.#{name}.>"], storage: "file", allow_direct: true, allow_msg_ttl: true, allow_rollup_hdrs: true, max_msgs_per_subject: 1 }.merge()) resp = nc.request("$JS.API.STREAM.CREATE.KV_#{name}", payload) result = Utils::Json.parse(resp.data, symbolize_names: false) if result&.dig("error") msg = result.dig("error", "description").to_s # Two worker processes starting simultaneously can both attempt creation. # If another process won the race, fall back to looking up the existing bucket. raise NATS::JetStream::Error, msg unless msg.match?(/already in use|already exists/i) end js.key_value(name) end |
#create_stream(name, config) ⇒ Object
38 39 40 |
# File 'lib/cosmo/client.rb', line 38 def create_stream(name, config) js.add_stream(name: name, **config) end |
#cron_subjects_in_stream(stream_name, filter) ⇒ Array<String>
Return all subjects in stream_name that match filter using NATS's
subjects_filter on STREAM.INFO (requires NATS ≥ 2.9).
62 63 64 65 66 67 68 69 |
# File 'lib/cosmo/client.rb', line 62 def cron_subjects_in_stream(stream_name, filter) payload = Utils::Json.dump({ subjects_filter: filter }) resp = nc.request("$JS.API.STREAM.INFO.#{stream_name}", payload) data = Utils::Json.parse(resp.data, symbolize_names: false) (data&.dig("state", "subjects") || {}).keys rescue StandardError [] end |
#delete_consumer(stream_name, consumer_name) ⇒ Boolean
111 112 113 |
# File 'lib/cosmo/client.rb', line 111 def delete_consumer(stream_name, consumer_name) js.delete_consumer(stream_name, consumer_name) end |
#delete_message(name, seq) ⇒ Hash[::String, untyped]
119 120 121 122 |
# File 'lib/cosmo/client.rb', line 119 def (name, seq) response = nc.request("$JS.API.STREAM.MSG.DELETE.#{name}", JSON.dump({ seq: seq })) Utils::Json.parse(response.data, symbolize_names: false) end |
#delete_stream(name, params = {}) ⇒ Object
42 43 44 |
# File 'lib/cosmo/client.rb', line 42 def delete_stream(name, params = {}) js.delete_stream(name, params) end |
#ephemeral_subscribe(subject, config) ⇒ Object
NOTE: nats-pure's #pull_subscribe has no path to a true ephemeral pull consumer.
Its rescue branch unconditionally sets config[:durable_name] = durable (jetstream.rb).
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/cosmo/client.rb', line 147 def ephemeral_subscribe(subject, config) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity config = config.dup stream = config.delete(:stream) or raise ArgumentError, "stream required for ephemeral consumers" raise ArgumentError, "inactive_threshold required for ephemeral consumers" unless config[:inactive_threshold] subject = subject.first if subject.is_a?(Array) && subject.size == 1 consumer_config = NATS::JetStream::API::ConsumerConfig.new(config) if subject.is_a?(Array) consumer_config[:filter_subjects] ||= subject else consumer_config[:filter_subject] ||= subject end info = js.add_consumer(stream, consumer_config) sub = nc.subscribe(nc.new_inbox) sub.extend(NATS::JetStream.const_get(:PullSubscription)) sub.jsi = NATS::JetStream.const_get(:JS)::Sub.new( js: js, stream: stream, consumer: info.name, nms: "#{js.prefix}.CONSUMER.MSG.NEXT.#{stream}.#{info.name}" ) sub end |
#get_message(stream_name, **options) ⇒ NATS::JetStream::API::RawStreamMsg
115 116 117 |
# File 'lib/cosmo/client.rb', line 115 def (stream_name, **) js.get_msg(stream_name, **) end |
#kv(name, allow_msg_ttl: false, **options) ⇒ Object
133 134 135 136 137 |
# File 'lib/cosmo/client.rb', line 133 def kv(name, allow_msg_ttl: false, **) js.key_value(name) rescue NATS::KeyValue::BucketNotFoundError allow_msg_ttl ? create_kv_with_msg_ttl(name, **) : js.create_key_value({ bucket: name }.merge()) end |
#list_consumers(stream_name) ⇒ Array[Hash[::String, untyped]]
101 102 103 104 105 |
# File 'lib/cosmo/client.rb', line 101 def list_consumers(stream_name) response = nc.request("$JS.API.CONSUMER.LIST.#{stream_name}", "") data = Utils::Json.parse(response.data, default: {}, symbolize_names: false) Array(data["consumers"]) end |
#list_streams ⇒ Array[Hash[::String, untyped]]
71 72 73 74 75 76 77 78 79 |
# File 'lib/cosmo/client.rb', line 71 def list_streams response = nc.request("$JS.API.STREAM.LIST", "") data = Utils::Json.parse(response.data, symbolize_names: false) return [] if data.nil? || data["streams"].nil? data["streams"] rescue NATS::Error [] end |
#pause_stream(name) ⇒ void
This method returns an undefined value.
81 82 83 84 85 86 |
# File 'lib/cosmo/client.rb', line 81 def pause_stream(name) config = stream_info(name).config.to_h config[:metadata] ||= {} config[:metadata][:"_cosmo.paused"] = "true" update_stream(name, config) end |
#publish(subject, payload, **params) ⇒ Object
21 22 23 |
# File 'lib/cosmo/client.rb', line 21 def publish(subject, payload, **params) js.publish(subject, payload, **params) end |
#purge(stream_name, subject) ⇒ ::Integer?
124 125 126 127 128 129 130 131 |
# File 'lib/cosmo/client.rb', line 124 def purge(stream_name, subject) payload = subject ? Utils::Json.dump({ filter: subject }) : "" response = @nc.request("$JS.API.STREAM.PURGE.#{stream_name}", payload) result = Utils::Json.parse(response.data, default: {}, symbolize_names: false) raise NATS::JetStream::Error, result.dig("error", "description") if result["error"] result["purged"] # number of messages purged end |
#setup_stream(name, config) ⇒ Object
Create/update a stream, falling back to create when there's no stream.
53 54 55 56 57 |
# File 'lib/cosmo/client.rb', line 53 def setup_stream(name, config) update_stream(name, config) rescue NATS::JetStream::Error::StreamNotFound create_stream(name, config) end |
#stream_info(name) ⇒ Object
34 35 36 |
# File 'lib/cosmo/client.rb', line 34 def stream_info(name) js.stream_info(name) end |
#stream_paused?(name) ⇒ Boolean
95 96 97 98 99 |
# File 'lib/cosmo/client.rb', line 95 def stream_paused?(name) stream_info(name).config.&.[](:"_cosmo.paused") == "true" rescue NATS::IO::Timeout false end |
#subscribe(subject, consumer_name, config) ⇒ Object
Create a pull subscription. Durable with consumer_name, and ephemeral without.
28 29 30 31 32 |
# File 'lib/cosmo/client.rb', line 28 def subscribe(subject, consumer_name, config) return js.pull_subscribe(subject, consumer_name, config: config) if consumer_name ephemeral_subscribe(subject, config) end |
#unpause_stream(name) ⇒ void
This method returns an undefined value.
88 89 90 91 92 93 |
# File 'lib/cosmo/client.rb', line 88 def unpause_stream(name) config = stream_info(name).config.to_h config[:metadata] ||= {} config[:metadata].delete(:"_cosmo.paused") update_stream(name, config) end |
#update_stream(name, config) ⇒ Object
46 47 48 |
# File 'lib/cosmo/client.rb', line 46 def update_stream(name, config) js.update_stream(name: name, **config) end |