sidekiq_strategies 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +4 -0
- data/lib/airbrake/airbrake.rb +7 -0
- data/lib/dependencies.rb +29 -0
- data/lib/models/account.rb +50 -0
- data/lib/models/provider.rb +11 -0
- data/lib/models/transaction.rb +22 -0
- data/lib/rails/rails.rb +19 -0
- data/lib/sidekiq/sidekiq.rb +14 -0
- data/lib/sidekiq_strategies.rb +10 -0
- data/lib/sidekiq_strategies/account_handler.rb +151 -0
- data/lib/sidekiq_strategies/base.rb +19 -0
- data/lib/sidekiq_strategies/beanstalkd_handler.rb +32 -0
- data/lib/sidekiq_strategies/helper.rb +50 -0
- data/lib/sidekiq_strategies/polling_strategy.rb +252 -0
- data/lib/sidekiq_strategies/version.rb +3 -0
- data/lib/spool/publisher.rb +15 -0
- data/lib/spool/subscribers/events_agent_subscriber.rb +11 -0
- data/lib/spool/subscribers/redis_pubsub_subscriber.rb +11 -0
- data/lib/strategy_runner.rb +26 -0
- data/sidekiq_strategies.gemspec +27 -0
- data/spec/spec_helper.rb +66 -0
- data/spec/strategies/account_handler_spec.rb +182 -0
- data/spec/strategies/beanstalkd_handler_spec.rb +50 -0
- data/spec/strategies/helper_spec.rb +61 -0
- data/spec/strategies/polling_strategy_spec.rb +206 -0
- data/spec/support/seed_helpers.rb +83 -0
- data/tasks/rspec.rake +3 -0
- metadata +169 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 37a73d5707ee07eeed24504034bfc7629d0d7f2f
|
4
|
+
data.tar.gz: d7e1979d4427a24ee68bb0e3115afc0f882334f4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 103f746077d383396643f666d4c170c7b75873afb466879fa096b72d16411a601719dccf9bfa74314622f948a76bffc87978f1d585ba16f9526fe1a65fedc22b
|
7
|
+
data.tar.gz: 12d2c4d6ff59b45a61a7398636ca75e34d126ae64684bfaa363a4de131f2a1b4895ed44f68f4f14d3b455d9ae7a04270d3a370c5c4a457008fc9d40a200661cb
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
worker-strategy-1
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.1
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 abrahamb
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
Sidekiq Worker Strategies
|
2
|
+
=========================
|
3
|
+
|
4
|
+
## Description
|
5
|
+
This project contains various types of strategies that handle communication between the accounts-cc and worker-cc.
|
6
|
+
|
7
|
+
## Polling strategy
|
8
|
+
This strategy is a simple algorithm that basically follow these simple steps:
|
9
|
+
|
10
|
+
1. Load account and transaction information.
|
11
|
+
2. Establish connection with request and reply tubes.
|
12
|
+
3. Validate if the account is in a valid state.
|
13
|
+
4. Validate if there are available watchers (workers ready to process the job).
|
14
|
+
5. Validate if the provider is available (pending implementation).
|
15
|
+
6. Search for data in reply tube in case that previously a worker has processed the job.
|
16
|
+
7. Search for an existing request job in buried state and put it back in the ready queue.
|
17
|
+
8. Put a new request job if steps 6 and 7 are not meet and start polling on request tube to obatin a reply job.
|
18
|
+
9. Process the reply job.
|
19
|
+
|
20
|
+
This strategy needs to be included on accounts-cc to see in the real world how it works, the only necessary files are:
|
21
|
+
|
22
|
+
1. lib/account_handler.rb
|
23
|
+
2. lib/beanstalk_handler.rb
|
24
|
+
3. polling_strategy.rb
|
25
|
+
|
26
|
+
NOTES:
|
27
|
+
- models.rb and models/* are like mocks used to run this strategy stand-alone.
|
28
|
+
- lines commented with double '#' indicates that are lines that should be uncommented in accounts-cc
|
29
|
+
- lines that have 'TODO: remove' indicates that are lines that should be commented or deleted on accounts-cc
|
30
|
+
|
31
|
+
## SPECS
|
32
|
+
|
33
|
+
Pending
|
data/Rakefile
ADDED
data/lib/dependencies.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
libdir = File.dirname(__FILE__)
|
2
|
+
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
|
+
|
4
|
+
require 'bundler'
|
5
|
+
Bundler.require
|
6
|
+
|
7
|
+
require 'beaneater'
|
8
|
+
require 'oj'
|
9
|
+
require 'money'
|
10
|
+
|
11
|
+
# Publisher
|
12
|
+
require 'spool/publisher'
|
13
|
+
|
14
|
+
# Subscriers
|
15
|
+
require 'spool/subscribers/events_agent_subscriber'
|
16
|
+
require 'spool/subscribers/redis_pubsub_subscriber'
|
17
|
+
|
18
|
+
# Rails things
|
19
|
+
require 'sidekiq/sidekiq'
|
20
|
+
require 'rails/rails'
|
21
|
+
require 'airbrake/airbrake'
|
22
|
+
|
23
|
+
# Models
|
24
|
+
require 'models/account'
|
25
|
+
require 'models/provider'
|
26
|
+
require 'models/transaction'
|
27
|
+
|
28
|
+
# Strategies core
|
29
|
+
require 'sidekiq_strategies'
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class Account
|
4
|
+
attr_accessor :state, :id, :number, :service, :country, :response, :currency, :country_code
|
5
|
+
|
6
|
+
def initialize(id)
|
7
|
+
self.state = 'reserved'
|
8
|
+
self.id = id
|
9
|
+
self.number = '55748576'
|
10
|
+
self.service = OpenStruct.new
|
11
|
+
self.country = OpenStruct.new
|
12
|
+
self.service.slug = 'claro-postpay'
|
13
|
+
self.country.code = 'gt'
|
14
|
+
self.currency = "USD"
|
15
|
+
self.country_code = 'gt'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.find(account_id)
|
19
|
+
Account.new(account_id)
|
20
|
+
end
|
21
|
+
|
22
|
+
def transactions
|
23
|
+
Transaction.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def send(action)
|
27
|
+
if action == 'start_query!'
|
28
|
+
self.state = 'querying'
|
29
|
+
else
|
30
|
+
self
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def success
|
35
|
+
self.state = 'ready'
|
36
|
+
end
|
37
|
+
|
38
|
+
def error
|
39
|
+
self.state = 'error'
|
40
|
+
end
|
41
|
+
|
42
|
+
def queue_for_retry
|
43
|
+
self.state = 'awaiting_retry'
|
44
|
+
end
|
45
|
+
|
46
|
+
def queue_for_wait_at_worker
|
47
|
+
self.state = 'waiting_at_worker'
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class Transaction
|
4
|
+
attr_accessor :retry_count, :fields, :provider, :response, :amount, :exception_type, :exception_message, :fields, :beanstalkd_jid, :response_code, :response_message, :sidekiq_jid, :id
|
5
|
+
|
6
|
+
def initialize(id=nil)
|
7
|
+
self.retry_count = 0
|
8
|
+
self.provider = Provider.new
|
9
|
+
self.fields = {reference: "", end_user_id: "", priority: "normal", code:"", end_user_ip_address: "192.168.0.1", monitor_as_url: "http://10.33.33.22:3000/v1/services/gt/claro-postpay/accounts/1", account_number: "55748576", total_cents: ""}
|
10
|
+
self.beanstalkd_jid = nil
|
11
|
+
self.id = id
|
12
|
+
end
|
13
|
+
|
14
|
+
def find(account_id)
|
15
|
+
Transaction.new(account_id)
|
16
|
+
end
|
17
|
+
|
18
|
+
def save!(args = {})
|
19
|
+
puts "save transaction!"
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/lib/rails/rails.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Rails
|
2
|
+
|
3
|
+
def self.application
|
4
|
+
self
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.routes
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.url_helpers
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def v1_account_url(params)
|
16
|
+
"http://api.bluekite.com/v1/services/#{params[:cc]}/#{params[:slug]}/accounts/#{params[:id]}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "sidekiq_strategies/version"
|
2
|
+
require 'sidekiq_strategies/base'
|
3
|
+
require 'sidekiq_strategies/beanstalkd_handler'
|
4
|
+
require 'sidekiq_strategies/helper'
|
5
|
+
require 'sidekiq_strategies/account_handler'
|
6
|
+
require 'sidekiq_strategies/polling_strategy'
|
7
|
+
|
8
|
+
module SidekiqStrategies
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module SidekiqStrategies
|
2
|
+
|
3
|
+
class AccountHandler
|
4
|
+
attr_accessor :account, :transaction, :action, :priority
|
5
|
+
|
6
|
+
def initialize(account_id, transaction_id, action, priority, publisher)
|
7
|
+
self.action = action
|
8
|
+
self.priority = priority
|
9
|
+
load_account(account_id)
|
10
|
+
load_transaction(transaction_id)
|
11
|
+
self.class.send(:include, publisher)
|
12
|
+
end
|
13
|
+
|
14
|
+
def save_sidekiq_jid(jid)
|
15
|
+
self.transaction.sidekiq_jid = jid
|
16
|
+
self.transaction.save!
|
17
|
+
end
|
18
|
+
|
19
|
+
def save_beanstalkd_jid(jid)
|
20
|
+
self.transaction.beanstalkd_jid = jid
|
21
|
+
self.transaction.save!
|
22
|
+
end
|
23
|
+
|
24
|
+
def success
|
25
|
+
previous_account_state = self.account.state
|
26
|
+
self.account.success
|
27
|
+
publish(:state_changed , self.account, previous_account_state, self.account.state)
|
28
|
+
end
|
29
|
+
|
30
|
+
def error
|
31
|
+
previous_account_state = self.account.state
|
32
|
+
self.account.error
|
33
|
+
publish(:state_changed , self.account, previous_account_state, self.account.state)
|
34
|
+
end
|
35
|
+
|
36
|
+
def start_action
|
37
|
+
previous_account_state = self.account.state
|
38
|
+
self.account.send "start_#{self.action}!"
|
39
|
+
publish(:state_changed , self.account, previous_account_state, self.account.state)
|
40
|
+
end
|
41
|
+
|
42
|
+
def queue_for_retry
|
43
|
+
previous_account_state = self.account.state
|
44
|
+
self.transaction.retry_count += 1
|
45
|
+
self.account.queue_for_retry
|
46
|
+
self.transaction.save!(validate: false)
|
47
|
+
publish(:state_changed , self.account, previous_account_state, self.account.state)
|
48
|
+
end
|
49
|
+
|
50
|
+
def queue_for_wait_at_worker
|
51
|
+
previous_account_state = self.account.state
|
52
|
+
self.account.queue_for_wait_at_worker
|
53
|
+
publish(:state_changed , self.account, previous_account_state, self.account.state)
|
54
|
+
end
|
55
|
+
|
56
|
+
def save_success_response(body)
|
57
|
+
self.transaction.response = body
|
58
|
+
self.transaction.amount = body[:amount_due]
|
59
|
+
|
60
|
+
action_response = self.send("#{action_name}_response")
|
61
|
+
self.account.send(self.action).response = action_response
|
62
|
+
self.transaction.save!(validate: false)
|
63
|
+
end
|
64
|
+
|
65
|
+
def save_exception(exception)
|
66
|
+
parsed_exception = parse_exception(exception)
|
67
|
+
|
68
|
+
self.transaction.response = parsed_exception
|
69
|
+
self.transaction.response_code = parsed_exception[:code]
|
70
|
+
self.transaction.response_message = parsed_exception[:message]
|
71
|
+
|
72
|
+
self.transaction.exception_message = parsed_exception[:message]
|
73
|
+
self.transaction.exception_type = parsed_exception[:type]
|
74
|
+
|
75
|
+
self.transaction.save!(validate: false)
|
76
|
+
end
|
77
|
+
|
78
|
+
def action_name
|
79
|
+
action_name = self.action
|
80
|
+
action_name = 'pay' if action_name == 'payment'
|
81
|
+
action_name = 'revert' if action_name == 'reversion'
|
82
|
+
|
83
|
+
action_name
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def load_account(account_id)
|
89
|
+
self.account = Account.find(account_id)
|
90
|
+
end
|
91
|
+
|
92
|
+
def load_transaction(transaction_id)
|
93
|
+
self.transaction = account.transactions.find(transaction_id)
|
94
|
+
end
|
95
|
+
|
96
|
+
def query_response
|
97
|
+
body = self.transaction.response
|
98
|
+
|
99
|
+
# public fields
|
100
|
+
response = {
|
101
|
+
name: ( body[:name] || body["name"] ),
|
102
|
+
address: ( body[:address] || body["address"] ),
|
103
|
+
amount_cents: self.transaction.amount,
|
104
|
+
amount: Money.new(self.transaction.amount, self.account.currency).format }
|
105
|
+
|
106
|
+
# private fields
|
107
|
+
keys = body.keys.select { |k| k.to_s =~ /^_/ }
|
108
|
+
keys.each do |k|
|
109
|
+
response[k] = body[k]
|
110
|
+
end
|
111
|
+
|
112
|
+
response
|
113
|
+
end
|
114
|
+
|
115
|
+
def payment_response
|
116
|
+
{
|
117
|
+
reference: self.transaction.reference
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def authorization_response
|
122
|
+
{
|
123
|
+
validity: (self.transaction.response[:validity] || self.transaction.response["validity"]),
|
124
|
+
message: (self.transaction.response[:message] || self.transaction.response["message"])
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def reversion_response
|
129
|
+
{}
|
130
|
+
end
|
131
|
+
|
132
|
+
def parse_exception(exception)
|
133
|
+
unless exception.kind_of?(StandardError)
|
134
|
+
ex = exception
|
135
|
+
else
|
136
|
+
ex = {
|
137
|
+
type: exception.class.to_s,
|
138
|
+
code: exception.code,
|
139
|
+
message: exception.message,
|
140
|
+
partner_code: ( exception.partner_code rescue 1000 ),
|
141
|
+
partner_message: ( exception.partner_message rescue exception.to_s ),
|
142
|
+
http_code: 500,
|
143
|
+
retry_action: ( exception.retry_action rescue false )
|
144
|
+
}
|
145
|
+
end
|
146
|
+
ex
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SidekiqStrategies
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
def initialize(acc_handler, bns_handler, logger, airbrake_notifier)
|
6
|
+
raise 'Error, no implementation'
|
7
|
+
end
|
8
|
+
|
9
|
+
def retries_exhausted(msg)
|
10
|
+
raise 'Error, no implementation'
|
11
|
+
end
|
12
|
+
|
13
|
+
def perform(sidekiq_jid)
|
14
|
+
raise 'Error, no implementation'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|