attentive_sidekiq 0.1.0 → 0.2.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
  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