sidekiq-debouncer 2.0.2 → 3.1.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 +4 -4
- data/CHANGELOG.md +14 -2
- data/README.md +3 -0
- data/lib/sidekiq/debouncer/enq.rb +33 -0
- data/lib/sidekiq/debouncer/job.rb +41 -0
- data/lib/sidekiq/debouncer/launcher.rb +33 -0
- data/lib/sidekiq/debouncer/locales/en.yml +4 -0
- data/lib/sidekiq/debouncer/lua/debounce.lua +4 -16
- data/lib/sidekiq/debouncer/lua/zpopbyscore_multi.lua +5 -0
- data/lib/sidekiq/debouncer/lua/zpopbyscore_withscore.lua +6 -0
- data/lib/sidekiq/debouncer/lua_commands.rb +26 -0
- data/lib/sidekiq/debouncer/middleware/client.rb +12 -26
- data/lib/sidekiq/debouncer/poller.rb +14 -0
- data/lib/sidekiq/debouncer/set.rb +16 -0
- data/lib/sidekiq/debouncer/version.rb +1 -1
- data/lib/sidekiq/debouncer/views/index.html.erb +50 -0
- data/lib/sidekiq/debouncer/views/show.html.erb +40 -0
- data/lib/sidekiq/debouncer/web.rb +26 -0
- data/lib/sidekiq/debouncer/web_extension.rb +57 -0
- data/lib/sidekiq/debouncer.rb +11 -0
- data/sidekiq-debouncer.gemspec +4 -2
- metadata +49 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a48a55ff72c620ec5c777c51d93008502eee9a5a5e6ac5678936a0a937f9300
|
4
|
+
data.tar.gz: a3a99b8171604c90d3e3fb2ee2665e8601d2a53e22cae488d7a3018f298c734a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: caf0fe8240dd387090d730f7421ed6f92897bbbb770410b7f2861f1bf898f261c8cf801519c9f974c50e9a4929fff3da5f275280bca4de4506bf17ea48fbaff4
|
7
|
+
data.tar.gz: 262ca59401d304f301413beda565c40f2557a2352f156ad84e9422e0ab2cf30006e7038e31bcbb67c80ecbe4a575880a3074e6ec74657c74fca4ce250f645c61
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,16 @@
|
|
1
|
-
##
|
1
|
+
## [3.1.0] - 2025-03-28
|
2
|
+
- Drop support for Ruby < 3.2.0
|
3
|
+
- Support for Sidekiq 8 ([#31](https://github.com/paladinsoftware/sidekiq-debouncer/pull/31), [#32](https://github.com/paladinsoftware/sidekiq-debouncer/pull/32))
|
4
|
+
- DragonflyDB support ([#29](https://github.com/paladinsoftware/sidekiq-debouncer/pull/29))
|
5
|
+
|
6
|
+
## [3.0.0] - 2024-10-22
|
7
|
+
- Complete rewrite of the library ([#25](https://github.com/paladinsoftware/sidekiq-debouncer/pull/25))
|
8
|
+
- Read only Web UI ([#26](https://github.com/paladinsoftware/sidekiq-debouncer/pull/26))
|
9
|
+
- Drop support for sidekiq 6.x and ruby 2.7 ([#28](https://github.com/paladinsoftware/sidekiq-debouncer/pull/28))
|
10
|
+
- Respect sidekiq_options overridden by .set ([#27](https://github.com/paladinsoftware/sidekiq-debouncer/pull/27))
|
11
|
+
|
12
|
+
**Upgrade notes:**
|
13
|
+
Since the job format changed, V3 won't debounce jobs enqueued with V2, although they'll still get executed
|
2
14
|
|
3
15
|
## [2.0.2] - 2023-03-13
|
4
16
|
- support Sidekiq::Testing
|
@@ -7,7 +19,7 @@
|
|
7
19
|
- don't remove debounce key in redis to avoid invalid debouncing
|
8
20
|
|
9
21
|
## [2.0.0] - 2023-02-28
|
10
|
-
Complete rewrite of the library:
|
22
|
+
Complete rewrite of the library:
|
11
23
|
- Instead of iterating through whole schedule set, sidekiq-debouncer will now cache debounce key in redis with a reference to the job.
|
12
24
|
Thanks to that there is a huge performance boost compared to V1. With 1k jobs in schedule set it's over 100x faster.
|
13
25
|
The difference is even bigger with larger amount of jobs.
|
data/README.md
CHANGED
@@ -99,6 +99,9 @@ Sidekiq.configure_client do |config|
|
|
99
99
|
end
|
100
100
|
```
|
101
101
|
|
102
|
+
## Web UI
|
103
|
+
Add `require 'sidekiq/debouncer/web'` after `require 'sidekiq/web'`.
|
104
|
+
|
102
105
|
## Testing
|
103
106
|
|
104
107
|
In order to test the behavior of `sidekiq-debouncer` it is necessary to disable testing mode. It is the limitation of internal implementation.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "sidekiq/scheduled"
|
4
|
+
|
5
|
+
module Sidekiq
|
6
|
+
module Debouncer
|
7
|
+
class Enq < ::Sidekiq::Scheduled::Enq
|
8
|
+
extend LuaCommands
|
9
|
+
|
10
|
+
LUA_ZPOPBYSCORE_WITHSCORE = File.read(File.expand_path("../lua/zpopbyscore_withscore.lua", __FILE__))
|
11
|
+
LUA_ZPOPBYSCORE_MULTI = File.read(File.expand_path("../lua/zpopbyscore_multi.lua", __FILE__))
|
12
|
+
|
13
|
+
define_lua_command(:zpopbyscore_withscore, LUA_ZPOPBYSCORE_WITHSCORE)
|
14
|
+
define_lua_command(:zpopbyscore_multi, LUA_ZPOPBYSCORE_MULTI)
|
15
|
+
|
16
|
+
def enqueue_jobs
|
17
|
+
redis do |conn|
|
18
|
+
while !@done && (job, score = zpopbyscore_withscore(conn, [Sidekiq::Debouncer::SET], [Time.now.to_f.to_s]))
|
19
|
+
job_args = zpopbyscore_multi(conn, [job], [score])
|
20
|
+
|
21
|
+
final_args = job_args.map { |elem| Sidekiq.load_json(elem.split("-", 2)[1]) }
|
22
|
+
job_class = job.split("/")[2]
|
23
|
+
klass = Object.const_get(job_class)
|
24
|
+
|
25
|
+
@client.push({"args" => final_args, "class" => klass, "debounce_key" => job})
|
26
|
+
|
27
|
+
logger.debug { "enqueued #{Sidekiq::Debouncer::SET}: #{job}" }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Debouncer
|
5
|
+
class Job
|
6
|
+
include Sidekiq::JobUtil
|
7
|
+
|
8
|
+
attr_reader :key, :score
|
9
|
+
|
10
|
+
def initialize(key, score)
|
11
|
+
@key = key
|
12
|
+
@score = Float(score)
|
13
|
+
end
|
14
|
+
|
15
|
+
def at
|
16
|
+
Time.at(score).utc
|
17
|
+
end
|
18
|
+
|
19
|
+
def args
|
20
|
+
@_args ||= Sidekiq.redis { |conn| conn.call("ZRANGE", key, "-inf", "+inf", "BYSCORE") }
|
21
|
+
.map { |elem| Sidekiq.load_json(elem.split("-", 2)[1]) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def queue
|
25
|
+
normalized["queue"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def klass
|
29
|
+
key.split("/")[2]
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method :display_class, :klass
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def normalized
|
37
|
+
@_normalized ||= normalize_item({"args" => args, "class" => Object.const_get(klass), "debounce_key" => key})
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Debouncer
|
5
|
+
module Launcher
|
6
|
+
def initialize(config, **kwargs)
|
7
|
+
@debounce_poller = Sidekiq::Debouncer::Poller.new(config)
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
super
|
13
|
+
@debounce_poller.start
|
14
|
+
end
|
15
|
+
|
16
|
+
def quiet
|
17
|
+
super
|
18
|
+
@debounce_poller.terminate
|
19
|
+
end
|
20
|
+
|
21
|
+
def stop
|
22
|
+
@debounce_poller.terminate
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Sidekiq.configure_server do
|
30
|
+
require "sidekiq/launcher"
|
31
|
+
|
32
|
+
::Sidekiq::Launcher.prepend(Sidekiq::Debouncer::Launcher)
|
33
|
+
end
|
@@ -1,22 +1,10 @@
|
|
1
1
|
local set = KEYS[1]
|
2
2
|
local debounce_key = KEYS[2]
|
3
3
|
|
4
|
-
local
|
4
|
+
local args = ARGV[1]
|
5
5
|
local time = ARGV[2]
|
6
6
|
local ttl = ARGV[3]
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
redis.call("DEL", debounce_key)
|
12
|
-
-- skip if job wasn't found in schedule set
|
13
|
-
if redis.call("ZREM", set, existing_debounce) > 0 then
|
14
|
-
local new_args = cjson.decode(job)['args'][1]
|
15
|
-
local new_job = cjson.decode(existing_debounce)
|
16
|
-
table.insert(new_job['args'], new_args)
|
17
|
-
job = cjson.encode(new_job)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
redis.call("SET", debounce_key, job, "EX", ttl)
|
22
|
-
redis.call("ZADD", set, time, job)
|
8
|
+
redis.call("ZADD", set, time, debounce_key)
|
9
|
+
redis.call("ZADD", debounce_key, time, args)
|
10
|
+
redis.call("EXPIRE", debounce_key, ttl)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/sha1"
|
4
|
+
|
5
|
+
module Sidekiq
|
6
|
+
module Debouncer
|
7
|
+
module LuaCommands
|
8
|
+
def define_lua_command(command, script)
|
9
|
+
sha = Digest::SHA1.hexdigest(script)
|
10
|
+
define_method(command) do |conn, keys, argv|
|
11
|
+
retryable = true
|
12
|
+
begin
|
13
|
+
conn.call("EVALSHA", sha, keys.size, *keys, *argv)
|
14
|
+
rescue RedisClient::CommandError => e
|
15
|
+
raise if !e.message.start_with?("NOSCRIPT") || !retryable
|
16
|
+
|
17
|
+
# upload script to redis cache and retry
|
18
|
+
conn.call("SCRIPT", "LOAD", script)
|
19
|
+
retryable = false
|
20
|
+
retry
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "securerandom"
|
4
4
|
|
5
5
|
module Sidekiq
|
6
6
|
module Debouncer
|
@@ -11,10 +11,11 @@ module Sidekiq
|
|
11
11
|
# job from schedule set when another debounce occurs.
|
12
12
|
class Client
|
13
13
|
include Sidekiq::ClientMiddleware
|
14
|
+
extend Sidekiq::Debouncer::LuaCommands
|
14
15
|
|
15
16
|
LUA_DEBOUNCE = File.read(File.expand_path("../../lua/debounce.lua", __FILE__))
|
16
|
-
|
17
|
-
|
17
|
+
|
18
|
+
define_lua_command(:redis_debounce, LUA_DEBOUNCE)
|
18
19
|
|
19
20
|
def initialize(options = {})
|
20
21
|
@debounce_key_ttl = options.fetch(:ttl, 60 * 60 * 24) # 24 hours by default
|
@@ -35,18 +36,16 @@ module Sidekiq
|
|
35
36
|
def debounce(klass, job)
|
36
37
|
raise NotSupportedError, "perform_at is not supported with debounce" if job.key?("at")
|
37
38
|
|
38
|
-
options = debounce_options(klass)
|
39
|
+
options = debounce_options(klass, job)
|
39
40
|
key = debounce_key(klass, job, options)
|
40
41
|
time = (options[:time].to_f + Time.now.to_f).to_s
|
41
42
|
|
42
|
-
job["
|
43
|
-
job["args"] = [job["args"]]
|
44
|
-
job.delete("debounce")
|
43
|
+
return job.merge("args" => [job["args"]], "debounce_key" => key) if testing?
|
45
44
|
|
46
|
-
|
45
|
+
args_stringified = "#{SecureRandom.hex(12)}-#{Sidekiq.dump_json(job["args"])}"
|
47
46
|
|
48
47
|
redis do |connection|
|
49
|
-
redis_debounce(connection,
|
48
|
+
redis_debounce(connection, [Sidekiq::Debouncer::SET, key], [args_stringified, time, @debounce_key_ttl])
|
50
49
|
end
|
51
50
|
|
52
51
|
# prevent normal sidekiq flow
|
@@ -56,11 +55,12 @@ module Sidekiq
|
|
56
55
|
def debounce_key(klass, job, options)
|
57
56
|
method = options[:by]
|
58
57
|
result = method.is_a?(Symbol) ? klass.send(method, job["args"]) : method.call(job["args"])
|
59
|
-
"debounce/#{klass.name}/#{result}"
|
58
|
+
"debounce/v3/#{klass.name}/#{result}"
|
60
59
|
end
|
61
60
|
|
62
|
-
def debounce_options(klass)
|
63
|
-
options = klass.get_sidekiq_options["debounce"]
|
61
|
+
def debounce_options(klass, job)
|
62
|
+
options = job.fetch("debounce") { klass.get_sidekiq_options["debounce"] }
|
63
|
+
options = options.transform_keys(&:to_sym)
|
64
64
|
|
65
65
|
raise MissingArgumentError, "'by' attribute not provided" unless options[:by]
|
66
66
|
raise MissingArgumentError, "'time' attribute not provided" unless options[:time]
|
@@ -68,20 +68,6 @@ module Sidekiq
|
|
68
68
|
options
|
69
69
|
end
|
70
70
|
|
71
|
-
def redis_debounce(connection, keys:, argv:)
|
72
|
-
retryable = true
|
73
|
-
begin
|
74
|
-
connection.call("EVALSHA", LUA_DEBOUNCE_SHA, keys.size, *keys, *argv)
|
75
|
-
rescue REDIS_ERROR_CLASS => e
|
76
|
-
raise if !e.message.start_with?("NOSCRIPT") || !retryable
|
77
|
-
|
78
|
-
# upload script to redis cache and retry
|
79
|
-
connection.call("SCRIPT", "LOAD", LUA_DEBOUNCE)
|
80
|
-
retryable = false
|
81
|
-
retry
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
71
|
def testing?
|
86
72
|
defined?(Sidekiq::Testing) && Sidekiq::Testing.enabled?
|
87
73
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Sidekiq
|
4
|
+
module Debouncer
|
5
|
+
class Set < Sidekiq::JobSet
|
6
|
+
def initialize
|
7
|
+
super Sidekiq::Debouncer::SET
|
8
|
+
end
|
9
|
+
|
10
|
+
def fetch_by_key(key)
|
11
|
+
score = Sidekiq.redis { |conn| conn.zscore(Sidekiq::Debouncer::SET, key) }
|
12
|
+
Job.new(key, score)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
<section>
|
2
|
+
<header class='row'>
|
3
|
+
<div class='col-sm-5 pull-left'>
|
4
|
+
<h3><%= t('Debounces') %></h3>
|
5
|
+
</div>
|
6
|
+
<div class='col-sm-7 pull-right'>
|
7
|
+
<%= erb :_paging, locals: { url: "#{root_path}debounces" } %>
|
8
|
+
</div>
|
9
|
+
</header>
|
10
|
+
|
11
|
+
<% if @debounces.size > 0 %>
|
12
|
+
<form action="<%= root_path %>debounces" method="post">
|
13
|
+
<%= csrf_tag %>
|
14
|
+
<div class="table_container">
|
15
|
+
<table class="table table-striped table-bordered table-hover">
|
16
|
+
<thead>
|
17
|
+
<tr>
|
18
|
+
<th class="table-checkbox checkbox-column">
|
19
|
+
<label>
|
20
|
+
<input type="checkbox" class="check_all" />
|
21
|
+
</label>
|
22
|
+
</th>
|
23
|
+
<th><%= t('When') %></th>
|
24
|
+
<th><%= t('DebounceKey') %></th>
|
25
|
+
</tr>
|
26
|
+
</thead>
|
27
|
+
<% @debounces.each do |entry| %>
|
28
|
+
<tr>
|
29
|
+
<td class="table-checkbox">
|
30
|
+
<label>
|
31
|
+
<input type='checkbox' name='key[]' value='<%= entry.key %>' class='shift_clickable' />
|
32
|
+
</label>
|
33
|
+
</td>
|
34
|
+
<td>
|
35
|
+
<a href="<%= root_path %>debounces/<%= ::Base64.urlsafe_encode64(entry.key) %>"><%= relative_time(entry.at) %></a>
|
36
|
+
</td>
|
37
|
+
<td>
|
38
|
+
<a href="<%= root_path %>debounces/<%= ::Base64.urlsafe_encode64(entry.key) %>"><%= entry.key %></a>
|
39
|
+
</td>
|
40
|
+
</tr>
|
41
|
+
<% end %>
|
42
|
+
</table>
|
43
|
+
</div>
|
44
|
+
<!-- <input class="btn btn-danger pull-right flip" type="submit" name="delete" value="<%#= t('Delete') %>" />-->
|
45
|
+
<!-- <input class="btn btn-danger pull-right flip" type="submit" name="add_to_queue" value="<%#= t('AddToQueue') %>" />-->
|
46
|
+
</form>
|
47
|
+
<% else %>
|
48
|
+
<div class="alert alert-success"><%= t('NoDebouncesFound') %></div>
|
49
|
+
<% end %>
|
50
|
+
</section>
|
@@ -0,0 +1,40 @@
|
|
1
|
+
<section>
|
2
|
+
<header class="header-container">
|
3
|
+
<h3><%= t('Job') %></h3>
|
4
|
+
</header>
|
5
|
+
|
6
|
+
<div class="table_container">
|
7
|
+
<table class="table table-bordered table-striped table-hover">
|
8
|
+
<tbody>
|
9
|
+
<tr>
|
10
|
+
<th><%= t('Queue') %></th>
|
11
|
+
<td><%= @job.queue %></td>
|
12
|
+
</tr>
|
13
|
+
<tr>
|
14
|
+
<th><%= t('When') %></th>
|
15
|
+
<td><%= relative_time(@job.at) %></td>
|
16
|
+
</tr>
|
17
|
+
<tr>
|
18
|
+
<th><%= t('DebounceKey') %></th>
|
19
|
+
<td>
|
20
|
+
<%= @job.key %>
|
21
|
+
</td>
|
22
|
+
</tr>
|
23
|
+
<tr>
|
24
|
+
<th><%= t('Job') %></th>
|
25
|
+
<td>
|
26
|
+
<%= @job.display_class %>
|
27
|
+
</td>
|
28
|
+
</tr>
|
29
|
+
<tr>
|
30
|
+
<th><%= t('Arguments') %></th>
|
31
|
+
<td>
|
32
|
+
<code class="code-wrap">
|
33
|
+
<div class="args-extended"><%= display_args(@job.args, nil) %></div>
|
34
|
+
</code>
|
35
|
+
</td>
|
36
|
+
</tr>
|
37
|
+
</tbody>
|
38
|
+
</table>
|
39
|
+
</div>
|
40
|
+
</section>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
if defined?(Sidekiq::Web)
|
4
|
+
require "sidekiq/debouncer/web_extension"
|
5
|
+
|
6
|
+
if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("8.0.0")
|
7
|
+
Sidekiq::Web.configure do |config|
|
8
|
+
config.register(
|
9
|
+
Sidekiq::Debouncer::WebExtension,
|
10
|
+
name: "debounces",
|
11
|
+
tab: "Debounces",
|
12
|
+
index: "debounces"
|
13
|
+
)
|
14
|
+
end
|
15
|
+
elsif Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("7.3.0")
|
16
|
+
Sidekiq::Web.register(
|
17
|
+
Sidekiq::Debouncer::WebExtension,
|
18
|
+
name: "debounces",
|
19
|
+
tab: "Debounces",
|
20
|
+
index: "debounces"
|
21
|
+
)
|
22
|
+
else
|
23
|
+
Sidekiq::Web.register Sidekiq::Debouncer::WebExtension
|
24
|
+
Sidekiq::Web.tabs["Debounces"] = "debounces"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module Sidekiq
|
6
|
+
module Debouncer
|
7
|
+
module WebExtension
|
8
|
+
module Helpers
|
9
|
+
def get_route_param(key)
|
10
|
+
if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("8.0.0")
|
11
|
+
route_params(key)
|
12
|
+
else
|
13
|
+
route_params[key]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_url_param(key)
|
18
|
+
if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("8.0.0")
|
19
|
+
url_params(key)
|
20
|
+
else
|
21
|
+
params[key]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.registered(app)
|
27
|
+
locales = if Gem::Version.new(Sidekiq::VERSION) >= Gem::Version.new("8.0.0")
|
28
|
+
Sidekiq::Web.configure.locales
|
29
|
+
else
|
30
|
+
app.settings.locales
|
31
|
+
end
|
32
|
+
|
33
|
+
locales << File.join(File.expand_path("..", __FILE__), "locales")
|
34
|
+
|
35
|
+
app.helpers(Helpers)
|
36
|
+
|
37
|
+
app.get "/debounces" do
|
38
|
+
view_path = File.join(File.expand_path("..", __FILE__), "views")
|
39
|
+
|
40
|
+
@count = (get_url_param("count") || 25).to_i
|
41
|
+
(@current_page, @total_size, @debounces) = page("debouncer", get_url_param("page"), @count)
|
42
|
+
@debounces = @debounces.map { |key, score| Sidekiq::Debouncer::Job.new(key, score) }
|
43
|
+
|
44
|
+
render(:erb, File.read(File.join(view_path, "index.html.erb")))
|
45
|
+
end
|
46
|
+
|
47
|
+
app.get "/debounces/:key" do
|
48
|
+
view_path = File.join(File.expand_path("..", __FILE__), "views")
|
49
|
+
|
50
|
+
@job = Sidekiq::Debouncer::Set.new.fetch_by_key(Base64.urlsafe_decode64(get_route_param(:key)))
|
51
|
+
|
52
|
+
render(:erb, File.read(File.join(view_path, "show.html.erb")))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/sidekiq/debouncer.rb
CHANGED
@@ -2,11 +2,16 @@
|
|
2
2
|
|
3
3
|
require "sidekiq/debouncer/version"
|
4
4
|
require "sidekiq/debouncer/errors"
|
5
|
+
require "sidekiq/debouncer/lua_commands"
|
5
6
|
require "sidekiq/debouncer/middleware/client"
|
6
7
|
require "sidekiq/debouncer/middleware/server"
|
8
|
+
require "sidekiq/debouncer/job"
|
9
|
+
require "sidekiq/debouncer/set"
|
7
10
|
|
8
11
|
module Sidekiq
|
9
12
|
module Debouncer
|
13
|
+
SET = "debouncer"
|
14
|
+
|
10
15
|
def self.included(base)
|
11
16
|
base.extend ClassMethods
|
12
17
|
end
|
@@ -19,3 +24,9 @@ module Sidekiq
|
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|
27
|
+
|
28
|
+
Sidekiq.configure_server do
|
29
|
+
require "sidekiq/debouncer/enq"
|
30
|
+
require "sidekiq/debouncer/poller"
|
31
|
+
require "sidekiq/debouncer/launcher"
|
32
|
+
end
|
data/sidekiq-debouncer.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
|
|
18
18
|
DESCRIPTION
|
19
19
|
gem.homepage = "https://github.com/paladinsoftware/sidekiq-debouncer"
|
20
20
|
gem.license = "MIT"
|
21
|
-
gem.required_ruby_version = ">=
|
21
|
+
gem.required_ruby_version = ">= 3.0.0"
|
22
22
|
|
23
23
|
gem.files = Dir.glob("lib/**/*") + [
|
24
24
|
"CHANGELOG.md",
|
@@ -27,11 +27,13 @@ Gem::Specification.new do |gem|
|
|
27
27
|
"sidekiq-debouncer.gemspec"
|
28
28
|
]
|
29
29
|
|
30
|
-
gem.add_dependency "sidekiq", ">=
|
30
|
+
gem.add_dependency "sidekiq", ">= 7.0", "< 9.0"
|
31
31
|
|
32
32
|
gem.add_development_dependency "rspec", "~> 3.12.0"
|
33
33
|
gem.add_development_dependency "timecop", "~> 0.9.6"
|
34
34
|
gem.add_development_dependency "simplecov", "~> 0.22.0"
|
35
35
|
gem.add_development_dependency "parallel", "~> 1.22.1"
|
36
36
|
gem.add_development_dependency "standard", "~> 1.24.3"
|
37
|
+
gem.add_development_dependency "rack-test", "~> 2.1.0"
|
38
|
+
gem.add_development_dependency "rack-session", "~> 2.1.0"
|
37
39
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-debouncer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Zuchmański
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2025-03-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sidekiq
|
@@ -17,20 +17,20 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '7.0'
|
21
21
|
- - "<"
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: '
|
23
|
+
version: '9.0'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
28
|
- - ">="
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version: '
|
30
|
+
version: '7.0'
|
31
31
|
- - "<"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '9.0'
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: rspec
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,6 +101,34 @@ dependencies:
|
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
103
|
version: 1.24.3
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: rack-test
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: 2.1.0
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 2.1.0
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: rack-session
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: 2.1.0
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.1.0
|
104
132
|
description: |
|
105
133
|
Sidekiq extension that adds the ability to debounce job execution.
|
106
134
|
Worker will postpone its execution after `wait time` have elapsed since the last time it was invoked.
|
@@ -117,11 +145,24 @@ files:
|
|
117
145
|
- README.md
|
118
146
|
- lib/sidekiq-debouncer.rb
|
119
147
|
- lib/sidekiq/debouncer.rb
|
148
|
+
- lib/sidekiq/debouncer/enq.rb
|
120
149
|
- lib/sidekiq/debouncer/errors.rb
|
150
|
+
- lib/sidekiq/debouncer/job.rb
|
151
|
+
- lib/sidekiq/debouncer/launcher.rb
|
152
|
+
- lib/sidekiq/debouncer/locales/en.yml
|
121
153
|
- lib/sidekiq/debouncer/lua/debounce.lua
|
154
|
+
- lib/sidekiq/debouncer/lua/zpopbyscore_multi.lua
|
155
|
+
- lib/sidekiq/debouncer/lua/zpopbyscore_withscore.lua
|
156
|
+
- lib/sidekiq/debouncer/lua_commands.rb
|
122
157
|
- lib/sidekiq/debouncer/middleware/client.rb
|
123
158
|
- lib/sidekiq/debouncer/middleware/server.rb
|
159
|
+
- lib/sidekiq/debouncer/poller.rb
|
160
|
+
- lib/sidekiq/debouncer/set.rb
|
124
161
|
- lib/sidekiq/debouncer/version.rb
|
162
|
+
- lib/sidekiq/debouncer/views/index.html.erb
|
163
|
+
- lib/sidekiq/debouncer/views/show.html.erb
|
164
|
+
- lib/sidekiq/debouncer/web.rb
|
165
|
+
- lib/sidekiq/debouncer/web_extension.rb
|
125
166
|
- sidekiq-debouncer.gemspec
|
126
167
|
homepage: https://github.com/paladinsoftware/sidekiq-debouncer
|
127
168
|
licenses:
|
@@ -135,14 +176,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
176
|
requirements:
|
136
177
|
- - ">="
|
137
178
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
179
|
+
version: 3.0.0
|
139
180
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
181
|
requirements:
|
141
182
|
- - ">="
|
142
183
|
- !ruby/object:Gem::Version
|
143
184
|
version: '0'
|
144
185
|
requirements: []
|
145
|
-
rubygems_version: 3.
|
186
|
+
rubygems_version: 3.5.22
|
146
187
|
signing_key:
|
147
188
|
specification_version: 4
|
148
189
|
summary: Sidekiq extension that adds the ability to debounce job execution
|