attentive_sidekiq 0.1.0 → 0.2.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
  SHA1:
3
- metadata.gz: 74d0c748009ee5ce4f34769f90fb071a375b6da9
4
- data.tar.gz: bfa38425968b6178b3b7d74d68ee76f745fe0771
3
+ metadata.gz: 77e7b5ed662a75451ebd6432ed1897ed01cb6f55
4
+ data.tar.gz: d26bda1a3c256bf452a35394d6fcf125459fa5ae
5
5
  SHA512:
6
- metadata.gz: f47d3137dc08eb4b83f8768e416fa925d238b13c51a193f87225f7b08fd9bb5bba69d913446a974a0512fc5cb9f506279a91de8190d71415cd49ae4603a1c7b9
7
- data.tar.gz: fa70ba452df6d816d8a05b3ca6accdd00256ba4556085a69000416e6cfa3349956d706a237ee761f8fac1a03c2a8feacf14eaa924e31be369d47390ffe1ee7f7
6
+ metadata.gz: 0f0a0635e8fa0671808a90b0d9f98ab571560019eabc6b1e45f035317ce945955b789923b7f8b472f3bb65143ee76ec13dfd2fa4506b66d2b68064b3831dc288
7
+ data.tar.gz: e2e5e97b3cf9e206773de95ce5166280d043516b9b2c7e027356f0011364009264c7675ebf9624123c65a3570e7b170fa4f347b3b1e1fddf09c8c9fbfc7280b2
@@ -2,20 +2,34 @@ PATH
2
2
  remote: .
3
3
  specs:
4
4
  attentive_sidekiq (0.1.0)
5
+ activesupport
5
6
  concurrent-ruby (~> 1.0)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ activesupport (4.2.5)
12
+ i18n (~> 0.7)
13
+ json (~> 1.7, >= 1.7.7)
14
+ minitest (~> 5.1)
15
+ thread_safe (~> 0.3, >= 0.3.4)
16
+ tzinfo (~> 1.1)
17
+ byebug (9.0.6)
10
18
  coderay (1.1.1)
11
19
  concurrent-ruby (1.0.2)
12
20
  connection_pool (2.2.1)
21
+ i18n (0.7.0)
22
+ json (1.8.3)
13
23
  method_source (0.8.2)
14
24
  minitest (5.9.1)
25
+ minitest-stub_any_instance (1.0.1)
15
26
  pry (0.10.4)
16
27
  coderay (~> 1.1.0)
17
28
  method_source (~> 0.8.1)
18
29
  slop (~> 3.4)
30
+ pry-byebug (3.4.0)
31
+ byebug (~> 9.0)
32
+ pry (~> 0.10)
19
33
  rack (2.0.1)
20
34
  rack-protection (1.5.3)
21
35
  rack
@@ -31,6 +45,9 @@ GEM
31
45
  rack-protection (>= 1.5.0)
32
46
  redis (~> 3.2, >= 3.2.1)
33
47
  slop (3.6.0)
48
+ thread_safe (0.3.5)
49
+ tzinfo (1.2.2)
50
+ thread_safe (~> 0.1)
34
51
 
35
52
  PLATFORMS
36
53
  ruby
@@ -38,7 +55,9 @@ PLATFORMS
38
55
  DEPENDENCIES
39
56
  attentive_sidekiq!
40
57
  minitest (~> 5.0)
58
+ minitest-stub_any_instance
41
59
  pry (~> 0.10)
60
+ pry-byebug
42
61
  rack-test (~> 0.6)
43
62
  rake (~> 11.3)
44
63
  redis-namespace (~> 1.5)
data/README.md CHANGED
@@ -28,7 +28,12 @@ To get only JIDs of lost jobs:
28
28
  AttentiveSidekiq::Disappeared.job_ids
29
29
  ```
30
30
 
31
- To remove a job from disappeared hash (e.g. after manual relaunch):
31
+ To place a disappeared job back into queue:
32
+ ```ruby
33
+ AttentiveSidekiq::Disappeared.requeue(jid)
34
+ ```
35
+
36
+ To remove a job from disappeared hash (e.g. after manual requeue):
32
37
  ```ruby
33
38
  AttentiveSidekiq::Disappeared.remove(jid)
34
39
  ```
@@ -36,10 +41,18 @@ AttentiveSidekiq::Disappeared.remove(jid)
36
41
  ### Sidekiq Web integration
37
42
  You may also watch info about disappeared jobs in a web UI.
38
43
  Simply make sure you have Sidekiq UI enabled, then head right to the Disappeared Jobs tab in the navbar.
44
+ The Web UI uses the API exclusively: anything you can do in the UI can be scripted with the API.
39
45
 
40
46
  ![Web UI](web.png)
41
47
 
48
+ ### Pre-requirements and notes
49
+
50
+ - Attentive Sidekiq assumes you've got Sidekiq installed already.
51
+ - You should make sure sidekiq process is started in order for disappeared jobs updater to work properly.
52
+ - It was tested with Sidekiq version 4. Seamless functionality with lower sidekiq versions is not guaranteed.
53
+
42
54
  ### Installation
55
+
43
56
  Add this line to your application's Gemfile:
44
57
 
45
58
  gem 'attentive_sidekiq'
@@ -5,7 +5,6 @@ require 'attentive_sidekiq/version'
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'attentive_sidekiq'
7
7
  s.version = AttentiveSidekiq::VERSION
8
- s.date = '2016-10-31'
9
8
  s.summary = "Make your sidekiq to be attentive to lost jobs"
10
9
  s.description = "This gem allows you to watch the jobs which suddenly dissappeared from redis without being completed by redis worker"
11
10
  s.authors = ["twonegatives"]
@@ -19,9 +18,12 @@ Gem::Specification.new do |s|
19
18
  s.add_development_dependency 'sidekiq', '~> 4.2'
20
19
  s.add_development_dependency 'rake', '~> 11.3'
21
20
  s.add_development_dependency 'minitest', '~> 5.0'
21
+ s.add_development_dependency 'minitest-stub_any_instance'
22
22
  s.add_development_dependency 'redis-namespace', '~> 1.5'
23
23
  s.add_development_dependency "rack-test", '~> 0.6'
24
24
  s.add_development_dependency 'pry', '~> 0.10'
25
+ s.add_development_dependency "pry-byebug"
25
26
 
27
+ s.add_dependency "activesupport"
26
28
  s.add_dependency 'concurrent-ruby', '~> 1.0'
27
29
  end
@@ -1,4 +1,6 @@
1
1
  require 'set'
2
+ require 'active_support/inflector'
3
+ require 'active_support/core_ext/hash'
2
4
  require 'concurrent'
3
5
  require 'attentive_sidekiq/middleware'
4
6
  require 'attentive_sidekiq/api'
@@ -9,6 +9,10 @@ module AttentiveSidekiq
9
9
  jobs.map{|i| i["jid"]}
10
10
  end
11
11
 
12
+ def get_job jid
13
+ JSON.parse(Sidekiq.redis{|conn| conn.hget(hash_name, jid)})
14
+ end
15
+
12
16
  def add item
13
17
  Sidekiq.redis{ |conn| conn.hset(hash_name, item['jid'], item.to_json) }
14
18
  end
@@ -27,6 +31,23 @@ module AttentiveSidekiq
27
31
 
28
32
  class Disappeared < RedisBasedHash
29
33
  HASH_NAME = AttentiveSidekiq::Middleware::REDIS_DISAPPEARED_KEY
34
+ STATUS_DETECTED = 'detected'
35
+ STATUS_REQUEUED = 'requeued'
36
+
37
+ class << self
38
+ alias_method :base_add, :add
39
+
40
+ def add item
41
+ extended_item = {'noticed_at' => Time.now.to_i, 'status' => STATUS_DETECTED}.merge(item)
42
+ super extended_item
43
+ end
44
+
45
+ def requeue jid
46
+ record = get_job(jid)
47
+ record['class'].constantize.perform_async(*record['args'])
48
+ base_add(record.merge('status' => STATUS_REQUEUED))
49
+ end
50
+ end
30
51
  end
31
52
 
32
53
  class Suspicious < RedisBasedHash
@@ -1,3 +1,3 @@
1
1
  module AttentiveSidekiq
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -3,10 +3,21 @@ module AttentiveSidekiq
3
3
  VIEW_PATH = File.expand_path("../web/views", __FILE__)
4
4
 
5
5
  def self.registered(app)
6
- app.get('/disappeared-jobs') do
7
- @suspicious_jobs = AttentiveSidekiq::Disappeared.jobs
6
+ app.get("/disappeared-jobs") do
7
+ @disappeared_jobs = AttentiveSidekiq::Disappeared.jobs
8
8
  erb File.read(File.join(VIEW_PATH, 'disappeared-list.erb'))
9
- end
9
+ end
10
+
11
+ app.post("/disappeared-jobs/:jid/delete") do
12
+ AttentiveSidekiq::Disappeared.remove(params['jid'])
13
+ redirect "#{root_path}disappeared-jobs"
14
+ end
15
+
16
+ app.post("/disappeared-jobs/:jid/requeue") do
17
+ AttentiveSidekiq::Disappeared.requeue(params['jid'])
18
+ redirect "#{root_path}disappeared-jobs"
19
+ end
20
+
10
21
  end
11
22
  end
12
23
  end
@@ -4,4 +4,14 @@ en:
4
4
  class: Class name
5
5
  arguments: Arguments
6
6
  disappeared_jobs: Disappeared jobs
7
- time: When
7
+ created_at: Created at
8
+ noticed_at: Noticed disappeared at
9
+ noticed_unknown: time unknown
10
+ actions: Actions
11
+ delete: Delete
12
+ delete_confirmation: Are you sure you want to delete this record?
13
+ status: Status
14
+ detected: Detected
15
+ requeued: Requeued
16
+ requeue: Requeue
17
+ requeue_confirmation: Are you sure you want to requeue this job?
@@ -4,4 +4,14 @@ ru:
4
4
  class: Задача
5
5
  arguments: Аргументы
6
6
  disappeared_jobs: Пропавшие задачи
7
- time: Время
7
+ created_at: Создана
8
+ noticed_at: Пропажа замечена
9
+ noticed_unknown: время неизвестно
10
+ actions: Действия
11
+ delete: Удалить
12
+ delete_confirmation: Вы уверены, что хотите удалить эту запись?
13
+ status: Статус
14
+ detected: Обнаружена
15
+ requeued: Поставлена в очередь
16
+ requeue: Перезапустить
17
+ requeue_confirmation: Вы уверены, что хотите перезапустить задачу?
@@ -8,18 +8,35 @@
8
8
  <th><%= t('queue') %></th>
9
9
  <th><%= t('class') %></th>
10
10
  <th><%= t('arguments') %></th>
11
- <th><%= t('time') %></th>
11
+ <th><%= t('created_at') %></th>
12
+ <th><%= t('noticed_at') %></th>
13
+ <th><%= t('status') %></th>
14
+ <th><%= t('actions') %></th>
12
15
  </tr>
13
16
  </thead>
14
17
 
15
18
  <tbody>
16
- <% @suspicious_jobs.each do |job| %>
19
+ <% @disappeared_jobs.each do |job| %>
17
20
  <tr>
18
21
  <td><%= job['jid'] %></td>
19
22
  <td><%= job['queue'] %></td>
20
23
  <td><%= job['class'] %></td>
21
24
  <td><%= job['args'] %></td>
22
25
  <td><%= Time.at(job['created_at']).strftime("%H:%M:%S %d.%m.%Y %z") %></td>
26
+ <td><%= job['noticed_at'] ? Time.at(job['noticed_at']).strftime("%H:%M:%S %d.%m.%Y %z") : t("noticed_unknown") %></td>
27
+ <th><%= job['status'] ? t(job['status']) : t('detected') %></th>
28
+ <td>
29
+ <form action="<%= "#{root_path}disappeared-jobs/#{job['jid']}/delete" %>" method="post">
30
+ <%= csrf_tag %>
31
+ <input class="btn btn-danger btn-xs" type="submit" name="delete" value="<%= t('delete') %>" style="width:100%" data-confirm="<%= t('delete_confirmation') %>" />
32
+ </form>
33
+ <% if job['status'] != AttentiveSidekiq::Disappeared::STATUS_REQUEUED %>
34
+ <form action="<%= "#{root_path}disappeared-jobs/#{job['jid']}/requeue" %>" method="post">
35
+ <%= csrf_tag %>
36
+ <input class="btn btn-xs" type="submit" name="requeue" value="<%= t('requeue') %>" style="width:100%" data-confirm="<%= t('requeue_confirmation') %>" />
37
+ </form>
38
+ <% end %>
39
+ </td>
23
40
  </tr>
24
41
  <% end %>
25
42
  </tbody>
@@ -0,0 +1,73 @@
1
+ require "test_helper"
2
+
3
+ class ApiTest < Minitest::Test
4
+ describe "with real redis" do
5
+ before do
6
+ Sidekiq.redis = REDIS
7
+ Sidekiq.redis{ |c| c.flushdb }
8
+
9
+ simple_hash = {'class' => 'ApiTest::SimpleWorker', 'args' => [], 'created_at' => Time.now.to_i}
10
+ args_hash = {'args' => [1, "boo", "woo"], 'class' => 'ApiTest::WorkerWithArgs', 'created_at' => Time.now.to_i}
11
+
12
+ @item_in_progress = {'jid' => "REDA-257513", 'queue' => 'red_queue'}.merge!(simple_hash)
13
+ @disappeared_wo_args = {'jid' => "YE11OW5-247", 'queue' => 'yellow_queue'}.merge!(simple_hash)
14
+ @disappeared_with_args = {'jid' => "B1UE-441", 'queue' => 'blue_queue'}.merge!(args_hash)
15
+
16
+ AttentiveSidekiq::Suspicious.add(@item_in_progress)
17
+
18
+ AttentiveSidekiq::Disappeared.add(@disappeared_wo_args)
19
+ AttentiveSidekiq::Disappeared.add(@disappeared_with_args)
20
+ end
21
+
22
+ class BaseWorker
23
+ include Sidekiq::Worker
24
+ end
25
+
26
+ class SimpleWorker < BaseWorker
27
+ def perform
28
+ end
29
+ end
30
+
31
+ class WorkerWithArgs < BaseWorker
32
+ def perform a, b, c
33
+ end
34
+ end
35
+
36
+ describe "requeue" do
37
+ it "adds jobs without args to sidekiq queue" do
38
+ item = @disappeared_wo_args
39
+ assert_equal 0, Sidekiq::Queue.new.size
40
+ String.stub_any_instance("constantize", SimpleWorker) do
41
+ AttentiveSidekiq::Disappeared.requeue(item['jid'])
42
+ end
43
+ assert_equal 1, Sidekiq::Queue.new.size
44
+ element = Sidekiq::Queue.new.to_a.first
45
+ assert_equal item['class'], element['class']
46
+ assert_equal item['args'], element['args']
47
+ end
48
+
49
+ it "adds job with args to sidekiq queue" do
50
+ item = @disappeared_with_args
51
+ String.stub_any_instance("constantize", WorkerWithArgs) do
52
+ AttentiveSidekiq::Disappeared.requeue(item['jid'])
53
+ end
54
+ element = Sidekiq::Queue.new.to_a.first
55
+ assert_equal item['class'], element['class']
56
+ assert_equal item['args'], element['args']
57
+ end
58
+
59
+ it "marks jobs as requeued" do
60
+ item = @disappeared_wo_args
61
+ assert_equal job_status(item), AttentiveSidekiq::Disappeared::STATUS_DETECTED
62
+ String.stub_any_instance("constantize", SimpleWorker) do
63
+ AttentiveSidekiq::Disappeared.requeue(item['jid'])
64
+ end
65
+ assert_equal job_status(item), AttentiveSidekiq::Disappeared::STATUS_REQUEUED
66
+ end
67
+
68
+ def job_status(item)
69
+ AttentiveSidekiq::Disappeared.get_job(item['jid'])['status']
70
+ end
71
+ end
72
+ end
73
+ end
@@ -39,7 +39,8 @@ class ManagerTest < Minitest::Test
39
39
  end
40
40
 
41
41
  def disappeared_now
42
- Sidekiq.redis{|conn| conn.hvals(AttentiveSidekiq::Middleware::REDIS_DISAPPEARED_KEY)}.map{|i| JSON.parse(i)}
42
+ from_redis = Sidekiq.redis{|conn| conn.hvals(AttentiveSidekiq::Middleware::REDIS_DISAPPEARED_KEY)}
43
+ from_redis.map{|i| JSON.parse(i).except('noticed_at', 'status')}
43
44
  end
44
45
  end
45
46
  end
@@ -12,6 +12,7 @@ require 'redis-namespace'
12
12
  require 'attentive_sidekiq'
13
13
  require 'minitest/autorun'
14
14
  require 'minitest/pride'
15
+ require 'minitest/stub_any_instance'
15
16
 
16
17
  REDIS_URL = ENV["REDIS_URL"] || "redis://localhost:15"
17
18
  REDIS_NAMESPACE = ENV["REDIS_NAMESPACE"] || 'testy'
@@ -22,3 +23,5 @@ Sidekiq.configure_server do |config|
22
23
  chain.add AttentiveSidekiq::Middleware::Server::Attentionist
23
24
  end
24
25
  end
26
+
27
+ Sidekiq.logger = nil
@@ -13,7 +13,6 @@ class WebTest < Minitest::Test
13
13
  @item_in_progress = {'jid' => "REDA-257513", 'queue' => 'red_queue'}.merge!(common_hash)
14
14
 
15
15
  AttentiveSidekiq::Suspicious.add(@item_in_progress)
16
- AttentiveSidekiq::Suspicious.add(@item_disappeared)
17
16
  AttentiveSidekiq::Disappeared.add(@item_disappeared)
18
17
  end
19
18
 
@@ -29,6 +28,22 @@ class WebTest < Minitest::Test
29
28
  refute_match @item_in_progress['jid'], last_response.body
30
29
  end
31
30
 
31
+ def test_delete_route_functions_fine
32
+ AttentiveSidekiq::Disappeared.stub(:remove, nil) do
33
+ post "/disappeared-jobs/#{@item_disappeared['jid']}/delete"
34
+ follow_redirect!
35
+ assert_equal 200, last_response.status
36
+ end
37
+ end
38
+
39
+ def test_requeue_route_functions_fine
40
+ AttentiveSidekiq::Disappeared.stub(:requeue, nil) do
41
+ post "/disappeared-jobs/#{@item_disappeared['jid']}/requeue"
42
+ follow_redirect!
43
+ assert_equal 200, last_response.status
44
+ end
45
+ end
46
+
32
47
  private
33
48
 
34
49
  def app
data/web.png CHANGED
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attentive_sidekiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - twonegatives
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-31 00:00:00.000000000 Z
11
+ date: 2016-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sidekiq
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-stub_any_instance
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: redis-namespace
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,34 @@ dependencies:
94
108
  - - "~>"
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0.10'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry-byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: activesupport
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  - !ruby/object:Gem::Dependency
98
140
  name: concurrent-ruby
99
141
  requirement: !ruby/object:Gem::Requirement
@@ -133,6 +175,7 @@ files:
133
175
  - lib/attentive_sidekiq/web/locales/en.yml
134
176
  - lib/attentive_sidekiq/web/locales/ru.yml
135
177
  - lib/attentive_sidekiq/web/views/disappeared-list.erb
178
+ - test/api_test.rb
136
179
  - test/manager_test.rb
137
180
  - test/server_middleware_test.rb
138
181
  - test/test_helper.rb
@@ -163,6 +206,7 @@ signing_key:
163
206
  specification_version: 4
164
207
  summary: Make your sidekiq to be attentive to lost jobs
165
208
  test_files:
209
+ - test/api_test.rb
166
210
  - test/manager_test.rb
167
211
  - test/server_middleware_test.rb
168
212
  - test/test_helper.rb