Class: Cosmo::API::Stream
- Inherits:
-
Object
- Object
- Cosmo::API::Stream
- Includes:
- Enumerable
- Defined in:
- lib/cosmo/api/stream.rb,
sig/cosmo/api/stream.rbs
Constant Summary collapse
- LIMIT =
20
Instance Attribute Summary collapse
-
#name ⇒ ::String
readonly
Returns the value of attribute name.
Class Method Summary collapse
Instance Method Summary collapse
- #client ⇒ Client
- #delete(seq) ⇒ Object
- #each {|arg0| ... } ⇒ void
- #info ⇒ Hash[Symbol, untyped]
-
#initialize(name) ⇒ Stream
constructor
A new instance of Stream.
- #message(seq) ⇒ Job?
- #messages(page: nil, limit: nil) ⇒ Array[Job]
-
#next_candidate(subjects, candidates, current) ⇒ [::String, Job]?
Lowest-seq message at or after current across all job subject filters, caching each subject's next candidate so it isn't re-queried every step.
-
#next_message(subject, seq) ⇒ Job?
Jump straight to the next message on the subject after seq, skipping any acked/deleted gaps.
- #offset(value) ⇒ self
- #pause! ⇒ Object
- #paused? ⇒ Boolean
- #retries ⇒ ::Integer
- #retry(seq) ⇒ void
- #scan_range ⇒ [::Integer, ::Integer, Array[::String]]
- #total ⇒ ::Integer (also: #size)
- #unpause! ⇒ Object
Constructor Details
#initialize(name) ⇒ Stream
Returns a new instance of Stream.
28 29 30 |
# File 'lib/cosmo/api/stream.rb', line 28 def initialize(name) @name = name end |
Instance Attribute Details
#name ⇒ ::String (readonly)
Returns the value of attribute name.
26 27 28 |
# File 'lib/cosmo/api/stream.rb', line 26 def name @name end |
Class Method Details
.all ⇒ Array[Stream]
12 13 14 |
# File 'lib/cosmo/api/stream.rb', line 12 def self.all client.list_streams.map { new(_1.dig("config", "name")) } end |
.client ⇒ Client
22 23 24 |
# File 'lib/cosmo/api/stream.rb', line 22 def self.client @client ||= Client.instance end |
.jobs ⇒ Array[Stream]
16 17 18 19 20 |
# File 'lib/cosmo/api/stream.rb', line 16 def self.jobs client.list_streams.select { _1.dig("config", "metadata", "_cosmo.type") == "jobs" } .reject { %w[scheduled dead].include?(_1.dig("config", "name")) } .map { new(_1.dig("config", "name")) } end |
Instance Method Details
#client ⇒ Client
153 154 155 |
# File 'lib/cosmo/api/stream.rb', line 153 def client self.class.client end |
#delete(seq) ⇒ Object
108 109 110 |
# File 'lib/cosmo/api/stream.rb', line 108 def delete(seq) client.(name, seq) end |
#each {|arg0| ... } ⇒ void
This method returns an undefined value.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/cosmo/api/stream.rb', line 52 def each return if total.zero? candidates = {} current, last, subjects = scan_range loop do break if current > last subject, job = next_candidate(subjects, candidates, current) break unless job candidates.delete(subject) current = job.seq.to_i + 1 yield job end end |
#info ⇒ Hash[Symbol, untyped]
32 33 34 35 |
# File 'lib/cosmo/api/stream.rb', line 32 def info info = client.stream_info(name) { state: info.state, config: info.config } end |
#message(seq) ⇒ Job?
91 92 93 94 95 96 97 98 |
# File 'lib/cosmo/api/stream.rb', line 91 def (seq) job = Job.new(name, client.(name, seq: seq, direct: true)) return if job.subject.to_s.start_with?(Cron::Entry::SUBJECT_PREFIX) job rescue NATS::JetStream::Error::NotFound # nop, acked/nacked end |
#messages(page: nil, limit: nil) ⇒ Array[Job]
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/cosmo/api/stream.rb', line 76 def (page: nil, limit: nil) jobs = [] limit = (limit || LIMIT).to_i state = info[:state] start = state.first_seq.to_i start += (page.to_i - 1) * limit if page offset(start).each do || jobs << break if jobs.size >= limit end jobs end |
#next_candidate(subjects, candidates, current) ⇒ [::String, Job]?
Lowest-seq message at or after current across all job subject filters, caching each subject's next candidate so it isn't re-queried every step.
136 137 138 139 140 141 142 143 144 |
# File 'lib/cosmo/api/stream.rb', line 136 def next_candidate(subjects, candidates, current) subjects.each do |subject| next if candidates.key?(subject) candidates[subject] = (subject, current) end candidates.compact.min_by { |_, msg| msg.seq.to_i } end |
#next_message(subject, seq) ⇒ Job?
Jump straight to the next message on the subject after seq, skipping any acked/deleted gaps
147 148 149 150 151 |
# File 'lib/cosmo/api/stream.rb', line 147 def (subject, seq) Job.new(name, client.(name, next: true, seq: seq, subject: subject, direct: true)) rescue NATS::JetStream::Error::NotFound nil end |
#offset(value) ⇒ self
71 72 73 74 |
# File 'lib/cosmo/api/stream.rb', line 71 def offset(value) @offset = value.to_i self end |
#pause! ⇒ Object
112 113 114 |
# File 'lib/cosmo/api/stream.rb', line 112 def pause! client.pause_stream(name) end |
#paused? ⇒ Boolean
120 121 122 |
# File 'lib/cosmo/api/stream.rb', line 120 def paused? client.stream_paused?(name) end |
#retries ⇒ ::Integer
46 47 48 49 50 |
# File 'lib/cosmo/api/stream.rb', line 46 def retries client.list_consumers(name).sum { _1["num_redelivered"].to_i } rescue NATS::Error 0 end |
#retry(seq) ⇒ void
This method returns an undefined value.
100 101 102 103 104 105 106 |
# File 'lib/cosmo/api/stream.rb', line 100 def retry(seq) job = (seq) return unless job client.publish(job.x_subject, job..data) delete(seq) end |
#scan_range ⇒ [::Integer, ::Integer, Array[::String]]
126 127 128 129 130 131 132 |
# File 'lib/cosmo/api/stream.rb', line 126 def scan_range data = info state = data[:state] current = @offset || state.first_seq.to_i subjects = Array(data[:config].subjects).reject { _1.start_with?(Cron::Entry::SUBJECT_PREFIX) } [current, state.last_seq.to_i, subjects] end |
#total ⇒ ::Integer Also known as: size
37 38 39 40 41 42 43 |
# File 'lib/cosmo/api/stream.rb', line 37 def total all_msgs = info[:state]..to_i cron_count = Client.instance.cron_subjects_in_stream(name, "#{Cron::Entry::SUBJECT_PREFIX}.#{name}.>").size [all_msgs - cron_count, 0].max rescue NATS::Error 0 end |
#unpause! ⇒ Object
116 117 118 |
# File 'lib/cosmo/api/stream.rb', line 116 def unpause! client.unpause_stream(name) end |