anway 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 414e59f65eafee592adb08dbe1e5a28c8655e8c13b10d5fbc88597d1df552eb1
4
- data.tar.gz: 59ce7fa161329b4abbafff3a18fc57d55f829b77a806ef39ec19843e3ceb40b3
3
+ metadata.gz: 879b6b41434558306382e3c24155f9fd38bf307af3782a7540fadb7047e6874f
4
+ data.tar.gz: c1daefd209281acc66457927bd76cd5914c1ddabb881e58de993fcb6cdc9652a
5
5
  SHA512:
6
- metadata.gz: 46aa7b2a3c725a6c0d6bdbf3929de6f0c0f038c30bf156dabb6eab1bf462173f0e42b77138f929111fcbeba05129116e04a152f14aae9588c192d870086df4b9
7
- data.tar.gz: b5a597b6a917520cacf61bf2fc12b3f3c267b6032e796b23c5c406f1dc19822c481e87cf0a8e2d8cca77463f3dc8be433a826c0b37a8a8a4dc5d087dae205459
6
+ metadata.gz: e255b3d87fc235bad83961e227598b577588aa8940fb3acce0e499bb8e262a5fb6d070c392013c6e3ca314a747dd0f44e3bf21aa62284bbcadd7c35181a71834
7
+ data.tar.gz: fea336b393f25fee2227dcd9cb6a326df8c847b531e979740978f0001d5d32b0fce2448a9b36455c10775a532b1eb44a4037ea6ce018b0d24e8eec7d6f78cbb6
data/README.md CHANGED
@@ -211,6 +211,20 @@ client.subscribe('chat.message', queue: 'realtime', event: 'BroadcastMessage', r
211
211
  client.unsubscribe('chat.message', queue: 'realtime', event: 'BroadcastMessage')
212
212
  ```
213
213
 
214
+ ### Bulk publish
215
+
216
+ `Anway.publish_bulk` sends many events with one `PUBLISHB` for each 1,000 events. The server saves all copies of the events of one `PUBLISHB` in one write, so an import, an outbox relay, or a replay is much faster than one `publish` for each event. Use `publish` for one event, such as in a request.
217
+
218
+ ```ruby
219
+ results = Anway.publish_bulk(orders.map { |order| { topic: 'order.paid', args: [order.id], id: "order-#{order.id}" } })
220
+
221
+ results.reject { |result| result.error.nil? }.each { |result| Rails.logger.warn("event #{result.event}: #{result.error}") }
222
+ ```
223
+
224
+ - Each event is a hash with `topic:` and `args:`, and the options of `publish`: `id:`, `unique_for:`, and `custom:`.
225
+ - The results are `Anway::Client::PublishResult` values (`event`, `jobs`, `duplicate`, `error`), in the order of the events. A refused event, such as a bad topic, has `error` and does not stop the others.
226
+ - `client.publish_bulk(events)` sends one `PUBLISHB`. It takes at most 1,000 events, and more raise `ArgumentError` before the send. A bulk can make at most 10,000 jobs over all subscriptions.
227
+
214
228
  [docs/topics.md](docs/topics.md) explains the chat example with three apps, the options, the tests, and problems.
215
229
 
216
230
  ## Rails
@@ -327,7 +341,7 @@ Anway::Testing.events.last['topic']
327
341
  Anway::Testing.inline!
328
342
  ```
329
343
 
330
- In inline mode, `Anway.publish` runs one copy in each job class of the app that subscribes to the topic.
344
+ In inline mode, `Anway.publish` runs one copy in each job class of the app that subscribes to the topic. `Anway.publish_bulk` works as one `Anway.publish` for each event.
331
345
 
332
346
  `perform_bulk` and `Anway.push_bulk` work as single pushes do: fake mode adds each job to `Anway::Testing.jobs`, and inline mode runs each job. The result is `{}`.
333
347
 
data/lib/anway/client.rb CHANGED
@@ -10,7 +10,8 @@ module Anway
10
10
  DEFAULT_PORT = 7419
11
11
  FETCH_WAIT = 2
12
12
 
13
- # The most jobs that one PUSHB takes.
13
+ # The most jobs that one PUSHB takes, and the most events that one
14
+ # PUBLISHB takes.
14
15
  MAX_BULK = 1000
15
16
 
16
17
  Endpoint = Data.define(:host, :port, :password, :tls)
@@ -23,6 +24,10 @@ module Anway
23
24
  # server already had an event with this id.
24
25
  Published = Data.define(:event, :jobs, :duplicate)
25
26
 
27
+ # One result of a PUBLISHB: as Published, and the reason in error when the
28
+ # server refused the event.
29
+ PublishResult = Data.define(:event, :jobs, :duplicate, :error)
30
+
26
31
  # A blank URL uses ANWAY_URL, then the local default.
27
32
  def initialize(url = nil, pool_size: 8, timeout: 10, tls: nil, wid: nil, labels: [], identity: {})
28
33
  @endpoint = parse_url(url || ENV.fetch('ANWAY_URL', nil) || DEFAULT_URL)
@@ -135,14 +140,33 @@ module Anway
135
140
  # publish of the same id within unique_for seconds makes no job, and the
136
141
  # reply has duplicate true. Returns a Published.
137
142
  def publish(topic, args = [], id: nil, unique_for: nil, custom: nil)
138
- Job.validate_args!(args)
139
-
140
- event = { topic: topic, args: args, id: id, unique_for: unique_for, custom: custom&.transform_keys(&:to_s) }
141
- data = JSON.parse(call('PUBLISH', event.compact))
143
+ data = JSON.parse(call('PUBLISH', event_payload(topic, args, id: id, unique_for: unique_for, custom: custom)))
142
144
 
143
145
  Published.new(event: data['event'], jobs: data['jobs'], duplicate: data['duplicate'] == true)
144
146
  end
145
147
 
148
+ # Sends up to MAX_BULK events with one PUBLISHB, and the server saves them
149
+ # in one write. Each event is a hash with topic: and args:, and the
150
+ # options of publish (id:, unique_for:, custom:). Returns one
151
+ # PublishResult for each event, in the same order. A refused event, such
152
+ # as a bad topic, has error, and does not stop the others. More than
153
+ # MAX_BULK events raise ArgumentError before the send.
154
+ def publish_bulk(events)
155
+ events = events.to_a
156
+
157
+ raise ArgumentError, "a bulk publish has at most #{MAX_BULK} events, not #{events.size}" if events.size > MAX_BULK
158
+
159
+ return [] if events.empty?
160
+
161
+ payload = events.map do |event|
162
+ event_payload(event.fetch(:topic), event.fetch(:args, []), **event.slice(:id, :unique_for, :custom))
163
+ end
164
+
165
+ JSON.parse(call('PUBLISHB', payload)).map do |data|
166
+ PublishResult.new(event: data['event'], jobs: data['jobs'], duplicate: data['duplicate'] == true, error: data['error'])
167
+ end
168
+ end
169
+
146
170
  # Each event of the topic makes one job in queue, with event as its job
147
171
  # type. A second call with the same topic, queue, and event sets new retry
148
172
  # and reserve_for.
@@ -199,6 +223,13 @@ module Anway
199
223
 
200
224
  private
201
225
 
226
+ # The argument of PUBLISH, and of each event of PUBLISHB.
227
+ def event_payload(topic, args, id: nil, unique_for: nil, custom: nil)
228
+ Job.validate_args!(args)
229
+
230
+ { topic: topic, args: args, id: id, unique_for: unique_for, custom: custom&.transform_keys(&:to_s) }.compact
231
+ end
232
+
202
233
  def queue_command(action, queues)
203
234
  call('QUEUE', "#{action} #{queues.join(' ')}")
204
235
 
data/lib/anway/testing.rb CHANGED
@@ -67,6 +67,15 @@ module Anway
67
67
  Client::Published.new(event: event_id, jobs: subscriptions.size, duplicate: false)
68
68
  end
69
69
 
70
+ # Publishes each event as a single publish does. No event is refused.
71
+ def publish_bulk(events)
72
+ events.map do |event|
73
+ published = publish(event.fetch(:topic), event.fetch(:args, []), **event.slice(:id, :unique_for, :custom))
74
+
75
+ Client::PublishResult.new(event: published.event, jobs: published.jobs, duplicate: false, error: nil)
76
+ end
77
+ end
78
+
70
79
  # Runs and removes all fake jobs. Jobs that they push also run.
71
80
  def drain
72
81
  Executor.call(@jobs.shift) until @jobs.empty?
data/lib/anway/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anway
4
- VERSION = '0.5.1'
4
+ VERSION = '0.5.2'
5
5
 
6
6
  # The anway server that the platform gems carry, and that CI tests against.
7
- SERVER_VERSION = '0.5.1'
7
+ SERVER_VERSION = '0.5.2'
8
8
  end
data/lib/anway.rb CHANGED
@@ -93,6 +93,26 @@ module Anway
93
93
  published
94
94
  end
95
95
 
96
+ # Sends the events with one PUBLISHB for each Client::MAX_BULK events.
97
+ # Each event is a hash with topic: and args:, and the options of publish.
98
+ # Returns one Client::PublishResult for each event, in the same order.
99
+ # Inside a database transaction, the publish waits for the commit and
100
+ # returns nil.
101
+ def publish_bulk(events)
102
+ events = events.to_a
103
+ testing = const_defined?(:Testing) ? Testing : nil
104
+
105
+ return testing.publish_bulk(events) if testing&.mode
106
+
107
+ results = nil
108
+
109
+ config.push_wrapper.call do
110
+ results = events.each_slice(Client::MAX_BULK).flat_map { |slice| client.publish_bulk(slice) }
111
+ end
112
+
113
+ results
114
+ end
115
+
96
116
  def reset!
97
117
  @lock.synchronize do
98
118
  @client&.close
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anway
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Thadeu