totoro 0.6.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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