totoro 0.6.1 → 1.0.0

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: aa6dd8c493beb2f3161254c4d0c207969183d3cbe0da84dc8782c63acba3f073
4
- data.tar.gz: 50e0437780093c06a2c928b49fff7c8d09a094cf906a5c9ea4cd6cf54a070132
3
+ metadata.gz: 46971dbec9f717c43e7cd0886b94b4391374786557286ee0e0854e424999ded6
4
+ data.tar.gz: 3b512d046ed53091adc36a4809e128d8d436a24e0e49b2ad1086031b45ae7b41
5
5
  SHA512:
6
- metadata.gz: '08d3b721c6d06504d37d6ebf153377df3354e446d79852b090e1d93920eb301fbb65cf925eba8adf10d1c0303e44230e91d5bdb89418f6e186f40b0b4ca716b0'
7
- data.tar.gz: 883d9a3d0ceabd7bff3a1eebe6eda1b9700f384dea8895fa2ba628d36c587d5a29f365d8a43f5dffd8b85796f2eb1f9515fd8a5767bb81ff38beedadd83f22a4
6
+ metadata.gz: 61e5a11bb89c80a98d46ee66ba20c6410da252fb3c5030d65ef3b944719ffa3feee561f0227a2e4cf953cea8b5c70a0d826de0655d95e0e81ea5a5840e0f7c61
7
+ data.tar.gz: 5a77beb59c2f8521c3b42738d212ef66c76b14d62fb4f04782684b9ad6c62761f55be4edad078e4e701119c9e4eea9378ba416d1a91147fdc72494a6d07a5f0d
@@ -5,6 +5,7 @@ class CreateTotoroFailedMessages < ActiveRecord::Migration[5.1]
5
5
  t.string :class_name
6
6
  t.string :queue_id
7
7
  t.jsonb :payload
8
+ t.string :group
8
9
  t.timestamps
9
10
  end
10
11
  end
@@ -0,0 +1,6 @@
1
+ class UpdateTotoroFailedMessages < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :totoro_failed_messages, :group, :string
4
+ Totoro::TotoroFailedMessage.all.update(group: 'enqueue')
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Totoro
4
+ class UpdateGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('templates', __dir__)
6
+ desc 'Update totoro to 1.0.0'
7
+
8
+ def copy_config_file
9
+ template 'update_totoro_failed_messages.rb', File.join('db/migrate', "#{Time.now.strftime('%Y%m%d%H%M%S')}_update_totoro_failed_messages.rb")
10
+ end
11
+ end
12
+ end
@@ -10,59 +10,35 @@ module Totoro
10
10
  end
11
11
 
12
12
  def connection
13
- @connection ||= Bunny.new(config.connect.merge(threaded: false)).tap(&:start)
13
+ @connection ||= Bunny.new(config.connect.merge(threaded: false))
14
14
  end
15
15
 
16
- def subscribe_channel
17
- @subscribe_channel ||= Bunny.new(config.connect).tap(&:start).create_channel
16
+ def broadcast(id, payload)
17
+ Totoro::BroadcastService.new(connection, config).broadcast(id, payload)
18
+ rescue Totoro::ConnectionBreakError => error
19
+ handle_failed_msg(id, payload, error, :broadcast)
18
20
  end
19
21
 
20
22
  def enqueue(id, payload)
21
- channel = connection.create_channel
22
- queue = channel.queue(*config.queue(id))
23
- payload = JSON.dump payload
24
- channel.default_exchange.publish(payload, routing_key: queue.name)
25
- Rails.logger.info "send message to #{queue.name}"
26
- STDOUT.flush
27
- channel.close
28
- rescue Bunny::TCPConnectionFailedForAllHosts,
29
- Bunny::NetworkErrorWrapper,
30
- Bunny::ChannelAlreadyClosed,
31
- AMQ::Protocol::EmptyResponseError => error
32
- handle_failed_msg(id, payload, error)
33
- end
34
-
35
- def subscribe(id)
36
- queue = subscribe_channel.queue(*config.queue(id))
37
- queue.subscribe do |delivery_info, metadata, payload|
38
- yield(delivery_info, metadata, payload)
39
- end
40
- end
41
-
42
- def get_worker(worker_class)
43
- ::Worker.const_get(worker_class.to_s.camelize).new
23
+ Totoro::EnqueueService.new(connection, config).enqueue(id, payload)
24
+ rescue Totoro::ConnectionBreakError => error
25
+ handle_failed_msg(id, payload, error, :enqueue)
44
26
  end
45
27
 
46
28
  private
47
29
 
48
- def handle_failed_msg(id, payload, error)
30
+ def handle_failed_msg(id, payload, error, group)
49
31
  Rails.logger.error error.message
50
32
  Rails.logger.info 'Add failed message to resend list'
51
33
  STDOUT.flush
52
- clear_connection
34
+ @connection = nil
53
35
  Totoro::TotoroFailedMessage.create(
54
36
  class_name: to_s,
55
37
  queue_id: id,
38
+ group: group,
56
39
  payload: payload
57
40
  )
58
41
  end
59
-
60
- def clear_connection
61
- @exchange = nil
62
- @channel = nil
63
- @subscribe_channel = nil
64
- @connection = nil
65
- end
66
42
  end
67
43
  end
68
44
  end
@@ -7,6 +7,7 @@ module Totoro
7
7
  queue_name = attrs[:queue_name]
8
8
  define_method('setup') do
9
9
  raise(Totoro::NeedQueueNameError) if queue_name.nil?
10
+
10
11
  @prefix = prefix
11
12
  @queue_name = queue_name
12
13
  end
@@ -14,22 +15,21 @@ module Totoro
14
15
 
15
16
  def initialize
16
17
  setup
17
- @queue_class = queue_class
18
18
  end
19
19
 
20
20
  def execute
21
- @queue_class.subscribe(@queue_name) do |delivery_info, metadata, payload|
21
+ Rails.logger.info 'Listening to the Rabbitmq'
22
+ STDOUT.flush
23
+ subscribe_service.subscribe(@queue_name) do |delivery_info, metadata, payload|
22
24
  Rails.logger.info "#{@queue_name} received message"
23
25
  STDOUT.flush
24
26
  payload_hash = JSON.parse(payload).with_indifferent_access
25
27
  process(payload_hash, metadata, delivery_info)
26
28
  end
27
- Rails.logger.info 'Listening to the Rabbitmq'
28
- STDOUT.flush
29
- @queue_class.subscribe_channel.work_pool.join
29
+ subscribe_service.channel.work_pool.join
30
30
  rescue SignalException
31
31
  puts 'Terminating process ..'
32
- @queue_class.subscribe_channel.work_pool.shutdown(true)
32
+ subscribe_service.channel.work_pool.shutdown(true)
33
33
  puts 'Stopped.'
34
34
  end
35
35
 
@@ -37,14 +37,12 @@ module Totoro
37
37
 
38
38
  private
39
39
 
40
- def queue_class
41
- if @prefix == :default
42
- Totoro::Queue
43
- else
44
- "Totoro::#{@prefix.to_s.camelize}::Queue".constantize
45
- end
40
+ def config
41
+ @config ||= Totoro::Config.new(@prefix)
46
42
  end
47
- end
48
43
 
49
- class NeedQueueNameError < RuntimeError; end
44
+ def subscribe_service
45
+ @subscribe_service ||= Totoro::SubscribeService.new(config)
46
+ end
47
+ end
50
48
  end
data/lib/totoro/config.rb CHANGED
@@ -15,6 +15,14 @@ module Totoro
15
15
  @data[:connect]
16
16
  end
17
17
 
18
+ def exchange(id)
19
+ @data[:exchange][id][:name]
20
+ end
21
+
22
+ def exchange_name_for_queue(queue_id)
23
+ @data[:queue][queue_id][:exchange]
24
+ end
25
+
18
26
  def queue(id)
19
27
  name = @data[:queue][id][:name]
20
28
  settings = { durable: @data[:queue][id][:durable] }
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Totoro
4
+ class ConnectionBreakError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Totoro
4
+ class NeedQueueNameError < RuntimeError; end
5
+ end
@@ -8,13 +8,7 @@ module Totoro
8
8
  run_every 10.second
9
9
  queue 'totoro'
10
10
  def perform
11
- Totoro::Queue.connection
12
- Totoro::TotoroFailedMessage.find_in_batches(batch_size: 100) do |message_group|
13
- message_group.each do |m|
14
- m.class_name.constantize.enqueue(m.queue_id, m.payload)
15
- m.destroy
16
- end
17
- end
11
+ Totoro::ResendService.new.resend_all_messages
18
12
  rescue StandardError => error
19
13
  Rails.logger.error error.message
20
14
  STDOUT.flush
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Totoro
4
+ class BroadcastService
5
+ def initialize(connection, config)
6
+ @connection = connection
7
+ @config = config
8
+ end
9
+
10
+ def broadcast(exchange_id, payload)
11
+ @connection.start unless @connection.connected?
12
+ exchange = channel.fanout(@config.exchange(exchange_id))
13
+ payload = JSON.dump payload
14
+ exchange.publish(payload)
15
+ Rails.logger.info "send message to exchange #{@config.exchange(exchange_id)}"
16
+ STDOUT.flush
17
+ channel.close
18
+ rescue Bunny::TCPConnectionFailedForAllHosts,
19
+ Bunny::NetworkErrorWrapper,
20
+ Bunny::ChannelAlreadyClosed,
21
+ Bunny::ConnectionAlreadyClosed,
22
+ AMQ::Protocol::EmptyResponseError => error
23
+ @channel.close if @channel.present?
24
+ raise(Totoro::ConnectionBreakError, "type: #{error.class}, message: #{error.message}")
25
+ end
26
+
27
+ private
28
+
29
+ def channel
30
+ @channel ||= @connection.create_channel
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Totoro
4
+ class EnqueueService
5
+ def initialize(connection, config)
6
+ @connection = connection
7
+ @config = config
8
+ end
9
+
10
+ def enqueue(id, payload)
11
+ @connection.start unless @connection.connected?
12
+ queue = channel.queue(*@config.queue(id))
13
+ payload = JSON.dump payload
14
+ exchange.publish(payload, routing_key: queue.name)
15
+ Rails.logger.info "send message to #{queue.name}"
16
+ STDOUT.flush
17
+ channel.close
18
+ rescue Bunny::TCPConnectionFailedForAllHosts,
19
+ Bunny::NetworkErrorWrapper,
20
+ Bunny::ChannelAlreadyClosed,
21
+ Bunny::ConnectionAlreadyClosed,
22
+ AMQ::Protocol::EmptyResponseError => error
23
+ @channel.close if @channel.present?
24
+ raise(Totoro::ConnectionBreakError, "type: #{error.class}, message: #{error.message}")
25
+ end
26
+
27
+ private
28
+
29
+ def channel
30
+ @channel ||= @connection.create_channel
31
+ end
32
+
33
+ # default exchange is a direct exchange
34
+ def exchange
35
+ @exchange ||= channel.default_exchange
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ module Totoro
2
+ class ResendService
3
+ def resend_all_messages
4
+ Totoro::TotoroFailedMessage.find_in_batches(batch_size: 100) do |message_group|
5
+ message_group.each { |m| resend_message(m) }
6
+ end
7
+ end
8
+
9
+ private
10
+
11
+ def resend_message(failed_message)
12
+ queue_class = failed_message.class_name.constantize
13
+ queue_class.method(failed_message.group).call(failed_message.queue_id, failed_message.payload)
14
+ failed_message.destroy
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Totoro
4
+ class SubscribeService
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def subscribe(id)
10
+ queue = bind_queue(id)
11
+ queue.subscribe do |delivery_info, metadata, payload|
12
+ yield(delivery_info, metadata, payload)
13
+ end
14
+ end
15
+
16
+ def channel
17
+ @channel ||= Bunny.new(@config.connect).tap(&:start).create_channel
18
+ end
19
+
20
+ private
21
+
22
+ def bind_queue(id)
23
+ exchange_name = @config.exchange_name_for_queue(id)
24
+ if exchange_name.nil?
25
+ channel.queue(*@config.queue(id))
26
+ else
27
+ channel.queue(*@config.queue(id)).bind(exchange_name)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Totoro
4
- VERSION = '0.6.1'
4
+ VERSION = '1.0.0'
5
5
  end
data/lib/totoro.rb CHANGED
@@ -4,9 +4,15 @@ require 'totoro/version'
4
4
  require 'totoro/config'
5
5
  require 'totoro/base_queue'
6
6
  require 'totoro/base_worker'
7
+ require 'totoro/services/enqueue_service'
8
+ require 'totoro/services/broadcast_service'
9
+ require 'totoro/services/subscribe_service'
10
+ require 'totoro/services/resend_service'
11
+ require 'totoro/errors/connection_break_error'
12
+ require 'totoro/errors/need_queue_name_error'
7
13
  require 'totoro/initializer'
8
14
  require 'totoro/message_resender'
9
- require 'totoro/totoro_failed_message'
15
+ require 'totoro/models/totoro_failed_message'
10
16
  require 'totoro/railtie' if defined?(Rails)
11
17
 
12
18
  module Totoro
Binary file
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /totoro
3
3
  specs:
4
- totoro (0.6.0)
4
+ totoro (0.6.1)
5
5
  bunny (~> 2.10)
6
6
  delayed_job_active_record (~> 4.1.3)
7
7
  delayed_job_recurring (~> 0.3.7)
@@ -49,8 +49,8 @@ GEM
49
49
  amq-protocol (2.3.0)
50
50
  arel (8.0.0)
51
51
  builder (3.2.3)
52
- bunny (2.11.0)
53
- amq-protocol (~> 2.3.0)
52
+ bunny (2.13.0)
53
+ amq-protocol (~> 2.3, >= 2.3.0)
54
54
  byebug (10.0.2)
55
55
  concurrent-ruby (1.0.5)
56
56
  crass (1.0.4)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Worker
4
+ class ExchangeQueue < Totoro::BaseWorker
5
+ setup queue_name: 'exchange_queue'
6
+ def process(payload, metadata, delivery_info)
7
+ p payload
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Worker
4
+ class ShuQueue < Totoro::BaseWorker
5
+ setup queue_name: 'shu_queue'
6
+ def process(payload, metadata, delivery_info)
7
+ p payload
8
+ end
9
+ end
10
+ end
@@ -9,6 +9,17 @@ default: &default
9
9
  example_queue:
10
10
  name: real.queue.name
11
11
  durable: true
12
+ exchange_queue:
13
+ name: real.exchange.queue
14
+ durable: true
15
+ exchange: real.exchange.name
16
+ shu_queue:
17
+ name: real.shu.queue
18
+ durable: true
19
+ exchange: real.exchange.name
20
+ exchange:
21
+ example_exchange:
22
+ name: real.exchange.name
12
23
 
13
24
  custom:
14
25
  connect:
@@ -0,0 +1,5 @@
1
+ class UpdateTotoroFailedMessages < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :totoro_failed_messages, :group, :string
4
+ end
5
+ end
@@ -10,30 +10,33 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20_181_019_070_846) do
13
+ ActiveRecord::Schema.define(version: 20181228033422) do
14
+
14
15
  # These are extensions that must be enabled in order to support this database
15
- enable_extension 'plpgsql'
16
+ enable_extension "plpgsql"
16
17
 
17
- create_table 'delayed_jobs', force: :cascade do |t|
18
- t.integer 'priority', default: 0, null: false
19
- t.integer 'attempts', default: 0, null: false
20
- t.text 'handler', null: false
21
- t.text 'last_error'
22
- t.datetime 'run_at'
23
- t.datetime 'locked_at'
24
- t.datetime 'failed_at'
25
- t.string 'locked_by'
26
- t.string 'queue'
27
- t.datetime 'created_at'
28
- t.datetime 'updated_at'
29
- t.index %w[priority run_at], name: 'delayed_jobs_priority'
18
+ create_table "delayed_jobs", force: :cascade do |t|
19
+ t.integer "priority", default: 0, null: false
20
+ t.integer "attempts", default: 0, null: false
21
+ t.text "handler", null: false
22
+ t.text "last_error"
23
+ t.datetime "run_at"
24
+ t.datetime "locked_at"
25
+ t.datetime "failed_at"
26
+ t.string "locked_by"
27
+ t.string "queue"
28
+ t.datetime "created_at"
29
+ t.datetime "updated_at"
30
+ t.index ["priority", "run_at"], name: "delayed_jobs_priority"
30
31
  end
31
32
 
32
- create_table 'totoro_failed_messages', force: :cascade do |t|
33
- t.string 'class_name'
34
- t.string 'queue_id'
35
- t.jsonb 'payload'
36
- t.datetime 'created_at', null: false
37
- t.datetime 'updated_at', null: false
33
+ create_table "totoro_failed_messages", force: :cascade do |t|
34
+ t.string "class_name"
35
+ t.string "queue_id"
36
+ t.jsonb "payload"
37
+ t.datetime "created_at", null: false
38
+ t.datetime "updated_at", null: false
39
+ t.string "group"
38
40
  end
41
+
39
42
  end
data/totoro.gemspec CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  # frozen_string_literal: true
3
2
 
4
3
  lib = File.expand_path('lib', __dir__)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: totoro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ShuHui18
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-08 00:00:00.000000000 Z
11
+ date: 2018-12-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -109,7 +109,6 @@ files:
109
109
  - LICENSE.txt
110
110
  - README.md
111
111
  - Rakefile
112
- - TODO
113
112
  - bin/console
114
113
  - bin/setup
115
114
  - bin/totoro
@@ -119,19 +118,28 @@ files:
119
118
  - lib/generators/totoro/templates/create_totoro_failed_messages.rb
120
119
  - lib/generators/totoro/templates/initializer.rb
121
120
  - lib/generators/totoro/templates/totoro.yml
121
+ - lib/generators/totoro/templates/update_totoro_failed_messages.rb
122
122
  - lib/generators/totoro/templates/worker.rb.erb
123
+ - lib/generators/totoro/update_generator.rb
123
124
  - lib/generators/totoro/worker_generator.rb
124
125
  - lib/totoro.rb
125
126
  - lib/totoro/base_queue.rb
126
127
  - lib/totoro/base_worker.rb
127
128
  - lib/totoro/config.rb
129
+ - lib/totoro/errors/connection_break_error.rb
130
+ - lib/totoro/errors/need_queue_name_error.rb
128
131
  - lib/totoro/initializer.rb
129
132
  - lib/totoro/message_resender.rb
133
+ - lib/totoro/models/totoro_failed_message.rb
130
134
  - lib/totoro/railtie.rb
135
+ - lib/totoro/services/broadcast_service.rb
136
+ - lib/totoro/services/enqueue_service.rb
137
+ - lib/totoro/services/resend_service.rb
138
+ - lib/totoro/services/subscribe_service.rb
131
139
  - lib/totoro/tasks/resend_message.rake
132
- - lib/totoro/totoro_failed_message.rb
133
140
  - lib/totoro/version.rb
134
141
  - pkg/totoro-0.6.0.gem
142
+ - pkg/totoro-0.6.1.gem
135
143
  - spec/spec_helper.rb
136
144
  - spec/totoro_spec.rb
137
145
  - test/rabbitmq_commands.txt
@@ -146,6 +154,8 @@ files:
146
154
  - test/totoro_test/app/mailers/application_mailer.rb
147
155
  - test/totoro_test/app/models/application_record.rb
148
156
  - test/totoro_test/app/models/worker/example_queue.rb
157
+ - test/totoro_test/app/models/worker/exchange_queue.rb
158
+ - test/totoro_test/app/models/worker/shu_queue.rb
149
159
  - test/totoro_test/app/services/bench_mark.txt
150
160
  - test/totoro_test/app/services/load_test.rb
151
161
  - test/totoro_test/app/views/layouts/mailer.html.erb
@@ -183,6 +193,7 @@ files:
183
193
  - test/totoro_test/db/development.sqlite3
184
194
  - test/totoro_test/db/migrate/20181019041208_create_delayed_jobs.rb
185
195
  - test/totoro_test/db/migrate/20181019070846_create_totoro_failed_messages.rb
196
+ - test/totoro_test/db/migrate/20181228033422_update_totoro_failed_messages.rb
186
197
  - test/totoro_test/db/schema.rb
187
198
  - test/totoro_test/db/seeds.rb
188
199
  - test/totoro_test/db/test.sqlite3
data/TODO DELETED
@@ -1,4 +0,0 @@
1
-
2
- Totoro:
3
- ☐ Add log
4
- ☐ flush log