mini-clean-lib 0.0.1
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 +7 -0
- data/mini-clean-lib.gemspec +12 -0
- data/sidekiq-8.1.6/Changes.md +2515 -0
- data/sidekiq-8.1.6/LICENSE.txt +9 -0
- data/sidekiq-8.1.6/README.md +124 -0
- data/sidekiq-8.1.6/bin/kiq +17 -0
- data/sidekiq-8.1.6/bin/lint-herb +13 -0
- data/sidekiq-8.1.6/bin/multi_queue_bench +271 -0
- data/sidekiq-8.1.6/bin/sidekiq +37 -0
- data/sidekiq-8.1.6/bin/sidekiqload +256 -0
- data/sidekiq-8.1.6/bin/sidekiqmon +11 -0
- data/sidekiq-8.1.6/bin/webload +69 -0
- data/sidekiq-8.1.6/lib/active_job/queue_adapters/sidekiq_adapter.rb +124 -0
- data/sidekiq-8.1.6/lib/generators/sidekiq/job_generator.rb +71 -0
- data/sidekiq-8.1.6/lib/generators/sidekiq/templates/job.rb.erb +9 -0
- data/sidekiq-8.1.6/lib/generators/sidekiq/templates/job_spec.rb.erb +6 -0
- data/sidekiq-8.1.6/lib/generators/sidekiq/templates/job_test.rb.erb +8 -0
- data/sidekiq-8.1.6/lib/sidekiq/api.rb +1466 -0
- data/sidekiq-8.1.6/lib/sidekiq/capsule.rb +135 -0
- data/sidekiq-8.1.6/lib/sidekiq/cli.rb +433 -0
- data/sidekiq-8.1.6/lib/sidekiq/client.rb +306 -0
- data/sidekiq-8.1.6/lib/sidekiq/component.rb +132 -0
- data/sidekiq-8.1.6/lib/sidekiq/config.rb +316 -0
- data/sidekiq-8.1.6/lib/sidekiq/deploy.rb +64 -0
- data/sidekiq-8.1.6/lib/sidekiq/embedded.rb +64 -0
- data/sidekiq-8.1.6/lib/sidekiq/fetch.rb +89 -0
- data/sidekiq-8.1.6/lib/sidekiq/iterable_job.rb +56 -0
- data/sidekiq-8.1.6/lib/sidekiq/job/interrupt_handler.rb +24 -0
- data/sidekiq-8.1.6/lib/sidekiq/job/iterable/active_record_enumerator.rb +53 -0
- data/sidekiq-8.1.6/lib/sidekiq/job/iterable/csv_enumerator.rb +47 -0
- data/sidekiq-8.1.6/lib/sidekiq/job/iterable/enumerators.rb +135 -0
- data/sidekiq-8.1.6/lib/sidekiq/job/iterable.rb +322 -0
- data/sidekiq-8.1.6/lib/sidekiq/job.rb +400 -0
- data/sidekiq-8.1.6/lib/sidekiq/job_logger.rb +54 -0
- data/sidekiq-8.1.6/lib/sidekiq/job_retry.rb +332 -0
- data/sidekiq-8.1.6/lib/sidekiq/job_util.rb +113 -0
- data/sidekiq-8.1.6/lib/sidekiq/launcher.rb +283 -0
- data/sidekiq-8.1.6/lib/sidekiq/loader.rb +57 -0
- data/sidekiq-8.1.6/lib/sidekiq/logger.rb +87 -0
- data/sidekiq-8.1.6/lib/sidekiq/manager.rb +134 -0
- data/sidekiq-8.1.6/lib/sidekiq/metrics/query.rb +184 -0
- data/sidekiq-8.1.6/lib/sidekiq/metrics/shared.rb +109 -0
- data/sidekiq-8.1.6/lib/sidekiq/metrics/tracking.rb +153 -0
- data/sidekiq-8.1.6/lib/sidekiq/middleware/chain.rb +207 -0
- data/sidekiq-8.1.6/lib/sidekiq/middleware/current_attributes.rb +120 -0
- data/sidekiq-8.1.6/lib/sidekiq/middleware/i18n.rb +44 -0
- data/sidekiq-8.1.6/lib/sidekiq/middleware/modules.rb +23 -0
- data/sidekiq-8.1.6/lib/sidekiq/monitor.rb +143 -0
- data/sidekiq-8.1.6/lib/sidekiq/paginator.rb +74 -0
- data/sidekiq-8.1.6/lib/sidekiq/processor.rb +298 -0
- data/sidekiq-8.1.6/lib/sidekiq/profiler.rb +73 -0
- data/sidekiq-8.1.6/lib/sidekiq/rails.rb +63 -0
- data/sidekiq-8.1.6/lib/sidekiq/redis_client_adapter.rb +117 -0
- data/sidekiq-8.1.6/lib/sidekiq/redis_connection.rb +115 -0
- data/sidekiq-8.1.6/lib/sidekiq/ring_buffer.rb +32 -0
- data/sidekiq-8.1.6/lib/sidekiq/scheduled.rb +235 -0
- data/sidekiq-8.1.6/lib/sidekiq/sd_notify.rb +149 -0
- data/sidekiq-8.1.6/lib/sidekiq/systemd.rb +26 -0
- data/sidekiq-8.1.6/lib/sidekiq/test_api.rb +331 -0
- data/sidekiq-8.1.6/lib/sidekiq/testing/inline.rb +2 -0
- data/sidekiq-8.1.6/lib/sidekiq/testing.rb +3 -0
- data/sidekiq-8.1.6/lib/sidekiq/transaction_aware_client.rb +59 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/controls.rb +53 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/filtering.rb +53 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/base_tab.rb +204 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/busy.rb +118 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/dead.rb +19 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/home.rb +144 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/metrics.rb +131 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/queues.rb +95 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/retries.rb +19 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/scheduled.rb +19 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs/set_tab.rb +96 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui/tabs.rb +15 -0
- data/sidekiq-8.1.6/lib/sidekiq/tui.rb +382 -0
- data/sidekiq-8.1.6/lib/sidekiq/version.rb +10 -0
- data/sidekiq-8.1.6/lib/sidekiq/web/action.rb +178 -0
- data/sidekiq-8.1.6/lib/sidekiq/web/application.rb +480 -0
- data/sidekiq-8.1.6/lib/sidekiq/web/config.rb +117 -0
- data/sidekiq-8.1.6/lib/sidekiq/web/helpers.rb +474 -0
- data/sidekiq-8.1.6/lib/sidekiq/web/router.rb +91 -0
- data/sidekiq-8.1.6/lib/sidekiq/web.rb +140 -0
- data/sidekiq-8.1.6/lib/sidekiq/worker_compatibility_alias.rb +13 -0
- data/sidekiq-8.1.6/lib/sidekiq.rb +161 -0
- data/sidekiq-8.1.6/sidekiq.gemspec +31 -0
- data/sidekiq-8.1.6/web/assets/images/apple-touch-icon.png +0 -0
- data/sidekiq-8.1.6/web/assets/images/favicon.ico +0 -0
- data/sidekiq-8.1.6/web/assets/images/logo.png +0 -0
- data/sidekiq-8.1.6/web/assets/images/status.png +0 -0
- data/sidekiq-8.1.6/web/assets/javascripts/application.js +204 -0
- data/sidekiq-8.1.6/web/assets/javascripts/base-charts.js +120 -0
- data/sidekiq-8.1.6/web/assets/javascripts/chart.min.js +13 -0
- data/sidekiq-8.1.6/web/assets/javascripts/chartjs-adapter-date-fns.min.js +7 -0
- data/sidekiq-8.1.6/web/assets/javascripts/chartjs-plugin-annotation.min.js +7 -0
- data/sidekiq-8.1.6/web/assets/javascripts/dashboard-charts.js +194 -0
- data/sidekiq-8.1.6/web/assets/javascripts/dashboard.js +63 -0
- data/sidekiq-8.1.6/web/assets/javascripts/metrics.js +280 -0
- data/sidekiq-8.1.6/web/assets/stylesheets/style.css +776 -0
- data/sidekiq-8.1.6/web/locales/ar.yml +89 -0
- data/sidekiq-8.1.6/web/locales/cs.yml +80 -0
- data/sidekiq-8.1.6/web/locales/da.yml +77 -0
- data/sidekiq-8.1.6/web/locales/de.yml +83 -0
- data/sidekiq-8.1.6/web/locales/el.yml +89 -0
- data/sidekiq-8.1.6/web/locales/en.yml +110 -0
- data/sidekiq-8.1.6/web/locales/es.yml +109 -0
- data/sidekiq-8.1.6/web/locales/fa.yml +82 -0
- data/sidekiq-8.1.6/web/locales/fr.yml +100 -0
- data/sidekiq-8.1.6/web/locales/gd.yml +110 -0
- data/sidekiq-8.1.6/web/locales/he.yml +82 -0
- data/sidekiq-8.1.6/web/locales/hi.yml +77 -0
- data/sidekiq-8.1.6/web/locales/it.yml +109 -0
- data/sidekiq-8.1.6/web/locales/ja.yml +92 -0
- data/sidekiq-8.1.6/web/locales/ko.yml +70 -0
- data/sidekiq-8.1.6/web/locales/lt.yml +85 -0
- data/sidekiq-8.1.6/web/locales/nb.yml +79 -0
- data/sidekiq-8.1.6/web/locales/nl.yml +70 -0
- data/sidekiq-8.1.6/web/locales/pl.yml +61 -0
- data/sidekiq-8.1.6/web/locales/pt-BR.yml +97 -0
- data/sidekiq-8.1.6/web/locales/pt.yml +69 -0
- data/sidekiq-8.1.6/web/locales/ru.yml +85 -0
- data/sidekiq-8.1.6/web/locales/sv.yml +70 -0
- data/sidekiq-8.1.6/web/locales/ta.yml +77 -0
- data/sidekiq-8.1.6/web/locales/tr.yml +102 -0
- data/sidekiq-8.1.6/web/locales/uk.yml +102 -0
- data/sidekiq-8.1.6/web/locales/ur.yml +82 -0
- data/sidekiq-8.1.6/web/locales/vi.yml +85 -0
- data/sidekiq-8.1.6/web/locales/zh-CN.yml +107 -0
- data/sidekiq-8.1.6/web/locales/zh-TW.yml +103 -0
- data/sidekiq-8.1.6/web/views/_footer.html.erb +32 -0
- data/sidekiq-8.1.6/web/views/_job_info.html.erb +115 -0
- data/sidekiq-8.1.6/web/views/_metrics_period_select.html.erb +15 -0
- data/sidekiq-8.1.6/web/views/_nav.html.erb +45 -0
- data/sidekiq-8.1.6/web/views/_paging.html.erb +26 -0
- data/sidekiq-8.1.6/web/views/_poll_link.html.erb +4 -0
- data/sidekiq-8.1.6/web/views/_summary.html.erb +40 -0
- data/sidekiq-8.1.6/web/views/busy.html.erb +155 -0
- data/sidekiq-8.1.6/web/views/dashboard.html.erb +104 -0
- data/sidekiq-8.1.6/web/views/dead.html.erb +38 -0
- data/sidekiq-8.1.6/web/views/filtering.html.erb +6 -0
- data/sidekiq-8.1.6/web/views/layout.html.erb +26 -0
- data/sidekiq-8.1.6/web/views/metrics.html.erb +85 -0
- data/sidekiq-8.1.6/web/views/metrics_for_job.html.erb +58 -0
- data/sidekiq-8.1.6/web/views/morgue.html.erb +69 -0
- data/sidekiq-8.1.6/web/views/profiles.html.erb +43 -0
- data/sidekiq-8.1.6/web/views/queue.html.erb +57 -0
- data/sidekiq-8.1.6/web/views/queues.html.erb +46 -0
- data/sidekiq-8.1.6/web/views/retries.html.erb +77 -0
- data/sidekiq-8.1.6/web/views/retry.html.erb +39 -0
- data/sidekiq-8.1.6/web/views/scheduled.html.erb +64 -0
- data/sidekiq-8.1.6/web/views/scheduled_job_info.html.erb +8 -0
- metadata +190 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# The MIT License
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2017, 2018, 2019, 2020 Agis Anastasopoulos
|
|
6
|
+
#
|
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
8
|
+
# this software and associated documentation files (the "Software"), to deal in
|
|
9
|
+
# the Software without restriction, including without limitation the rights to
|
|
10
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
11
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
|
12
|
+
# subject to the following conditions:
|
|
13
|
+
#
|
|
14
|
+
# The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
# copies or substantial portions of the Software.
|
|
16
|
+
#
|
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
19
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
20
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
21
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
22
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
|
|
24
|
+
# This is a copy of https://github.com/agis/ruby-sdnotify as of commit a7d52ee
|
|
25
|
+
# The only changes made was "rehoming" it within the Sidekiq module to avoid
|
|
26
|
+
# namespace collisions and applying standard's code formatting style.
|
|
27
|
+
|
|
28
|
+
require "socket"
|
|
29
|
+
|
|
30
|
+
# SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
|
|
31
|
+
# notify systemd about state changes. Methods of this package are no-op on
|
|
32
|
+
# non-systemd systems (eg. Darwin).
|
|
33
|
+
#
|
|
34
|
+
# The API maps closely to the original implementation of sd_notify(3),
|
|
35
|
+
# therefore be sure to check the official man pages prior to using SdNotify.
|
|
36
|
+
#
|
|
37
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
|
38
|
+
module Sidekiq
|
|
39
|
+
module SdNotify
|
|
40
|
+
# Exception raised when there's an error writing to the notification socket
|
|
41
|
+
class NotifyError < RuntimeError; end
|
|
42
|
+
|
|
43
|
+
READY = "READY=1"
|
|
44
|
+
RELOADING = "RELOADING=1"
|
|
45
|
+
STOPPING = "STOPPING=1"
|
|
46
|
+
STATUS = "STATUS="
|
|
47
|
+
ERRNO = "ERRNO="
|
|
48
|
+
MAINPID = "MAINPID="
|
|
49
|
+
WATCHDOG = "WATCHDOG=1"
|
|
50
|
+
FDSTORE = "FDSTORE=1"
|
|
51
|
+
|
|
52
|
+
def self.ready(unset_env = false)
|
|
53
|
+
notify(READY, unset_env)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def self.reloading(unset_env = false)
|
|
57
|
+
notify(RELOADING, unset_env)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.stopping(unset_env = false)
|
|
61
|
+
notify(STOPPING, unset_env)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param status [String] a custom status string that describes the current
|
|
65
|
+
# state of the service
|
|
66
|
+
def self.status(status, unset_env = false)
|
|
67
|
+
notify("#{STATUS}#{status}", unset_env)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @param errno [Integer]
|
|
71
|
+
def self.errno(errno, unset_env = false)
|
|
72
|
+
notify("#{ERRNO}#{errno}", unset_env)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @param pid [Integer]
|
|
76
|
+
def self.mainpid(pid, unset_env = false)
|
|
77
|
+
notify("#{MAINPID}#{pid}", unset_env)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.watchdog(unset_env = false)
|
|
81
|
+
notify(WATCHDOG, unset_env)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def self.fdstore(unset_env = false)
|
|
85
|
+
notify(FDSTORE, unset_env)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @return [Boolean] true if the service manager expects watchdog keep-alive
|
|
89
|
+
# notification messages to be sent from this process.
|
|
90
|
+
#
|
|
91
|
+
# If the $WATCHDOG_USEC environment variable is set,
|
|
92
|
+
# and the $WATCHDOG_PID variable is unset or set to the PID of the current
|
|
93
|
+
# process
|
|
94
|
+
#
|
|
95
|
+
# @note Unlike sd_watchdog_enabled(3), this method does not mutate the
|
|
96
|
+
# environment.
|
|
97
|
+
def self.watchdog?
|
|
98
|
+
wd_usec = ENV["WATCHDOG_USEC"]
|
|
99
|
+
wd_pid = ENV["WATCHDOG_PID"]
|
|
100
|
+
|
|
101
|
+
return false unless wd_usec
|
|
102
|
+
|
|
103
|
+
begin
|
|
104
|
+
wd_usec = Integer(wd_usec)
|
|
105
|
+
rescue
|
|
106
|
+
return false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
return false if wd_usec <= 0
|
|
110
|
+
return true if !wd_pid || wd_pid == $$.to_s
|
|
111
|
+
|
|
112
|
+
false
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Notify systemd with the provided state, via the notification socket, if
|
|
116
|
+
# any.
|
|
117
|
+
#
|
|
118
|
+
# Generally this method will be used indirectly through the other methods
|
|
119
|
+
# of the library.
|
|
120
|
+
#
|
|
121
|
+
# @param state [String]
|
|
122
|
+
# @param unset_env [Boolean]
|
|
123
|
+
#
|
|
124
|
+
# @return [Fixnum, nil] the number of bytes written to the notification
|
|
125
|
+
# socket or nil if there was no socket to report to (eg. the program wasn't
|
|
126
|
+
# started by systemd)
|
|
127
|
+
#
|
|
128
|
+
# @raise [NotifyError] if there was an error communicating with the systemd
|
|
129
|
+
# socket
|
|
130
|
+
#
|
|
131
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
|
132
|
+
def self.notify(state, unset_env = false)
|
|
133
|
+
sock = ENV["NOTIFY_SOCKET"]
|
|
134
|
+
|
|
135
|
+
return nil unless sock
|
|
136
|
+
|
|
137
|
+
ENV.delete("NOTIFY_SOCKET") if unset_env
|
|
138
|
+
|
|
139
|
+
begin
|
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect do |s|
|
|
141
|
+
s.close_on_exec = true
|
|
142
|
+
s.write(state)
|
|
143
|
+
end
|
|
144
|
+
rescue => e
|
|
145
|
+
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Sidekiq's systemd integration allows Sidekiq to inform systemd:
|
|
5
|
+
# 1. when it has successfully started
|
|
6
|
+
# 2. when it is starting shutdown
|
|
7
|
+
# 3. periodically for a liveness check with a watchdog thread
|
|
8
|
+
#
|
|
9
|
+
module Sidekiq
|
|
10
|
+
def self.start_watchdog
|
|
11
|
+
usec = Integer(ENV["WATCHDOG_USEC"])
|
|
12
|
+
return Sidekiq.logger.error("systemd Watchdog too fast: " + usec) if usec < 1_000_000
|
|
13
|
+
|
|
14
|
+
sec_f = usec / 1_000_000.0
|
|
15
|
+
# "It is recommended that a daemon sends a keep-alive notification message
|
|
16
|
+
# to the service manager every half of the time returned here."
|
|
17
|
+
ping_f = sec_f / 2
|
|
18
|
+
Sidekiq.logger.info "Pinging systemd watchdog every #{ping_f.round(1)} sec"
|
|
19
|
+
Thread.new do
|
|
20
|
+
loop do
|
|
21
|
+
sleep ping_f
|
|
22
|
+
Sidekiq::SdNotify.watchdog
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "sidekiq"
|
|
5
|
+
|
|
6
|
+
module Sidekiq
|
|
7
|
+
class Testing
|
|
8
|
+
class TestModeAlreadySetError < RuntimeError; end
|
|
9
|
+
class << self
|
|
10
|
+
attr_accessor :__global_test_mode
|
|
11
|
+
|
|
12
|
+
# Calling without a block sets the global test mode, affecting
|
|
13
|
+
# all threads. Calling with a block only affects the current Thread.
|
|
14
|
+
def __set_test_mode(mode)
|
|
15
|
+
if block_given?
|
|
16
|
+
# Reentrant testing modes will lead to a rat's nest of code which is
|
|
17
|
+
# hard to reason about. You can set the testing mode once globally and
|
|
18
|
+
# you can override that global setting once per-thread.
|
|
19
|
+
raise TestModeAlreadySetError, "Nesting test modes is not supported" if __local_test_mode
|
|
20
|
+
|
|
21
|
+
self.__local_test_mode = mode
|
|
22
|
+
begin
|
|
23
|
+
yield
|
|
24
|
+
ensure
|
|
25
|
+
self.__local_test_mode = nil
|
|
26
|
+
end
|
|
27
|
+
else
|
|
28
|
+
self.__global_test_mode = mode
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def __test_mode
|
|
33
|
+
__local_test_mode || __global_test_mode
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def __local_test_mode
|
|
37
|
+
Thread.current[:__sidekiq_test_mode]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def __local_test_mode=(value)
|
|
41
|
+
Thread.current[:__sidekiq_test_mode] = value
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def disable!(&block)
|
|
45
|
+
__set_test_mode(:disable, &block)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def fake!(&block)
|
|
49
|
+
__set_test_mode(:fake, &block)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def inline!(&block)
|
|
53
|
+
__set_test_mode(:inline, &block)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def enabled?
|
|
57
|
+
__test_mode != :disable
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def disabled?
|
|
61
|
+
__test_mode == :disable
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def fake?
|
|
65
|
+
__test_mode == :fake
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def inline?
|
|
69
|
+
__test_mode == :inline
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def server_middleware
|
|
73
|
+
@server_chain ||= Middleware::Chain.new(Sidekiq.default_configuration)
|
|
74
|
+
yield @server_chain if block_given?
|
|
75
|
+
@server_chain
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
class EmptyQueueError < RuntimeError; end
|
|
81
|
+
|
|
82
|
+
module TestingClient
|
|
83
|
+
private def atomic_push(conn, payloads)
|
|
84
|
+
if Sidekiq::Testing.fake?
|
|
85
|
+
payloads.each do |job|
|
|
86
|
+
job = Sidekiq.load_json(Sidekiq.dump_json(job))
|
|
87
|
+
job["enqueued_at"] = ::Process.clock_gettime(::Process::CLOCK_REALTIME, :millisecond) unless job["at"]
|
|
88
|
+
Queues.push(job["queue"], job["class"], job)
|
|
89
|
+
end
|
|
90
|
+
true
|
|
91
|
+
elsif Sidekiq::Testing.inline?
|
|
92
|
+
payloads.each do |job|
|
|
93
|
+
klass = Object.const_get(job["class"])
|
|
94
|
+
job["id"] ||= SecureRandom.hex(12)
|
|
95
|
+
job_hash = Sidekiq.load_json(Sidekiq.dump_json(job))
|
|
96
|
+
klass.process_job(job_hash)
|
|
97
|
+
end
|
|
98
|
+
true
|
|
99
|
+
else
|
|
100
|
+
super
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Sidekiq::Client.prepend TestingClient
|
|
106
|
+
|
|
107
|
+
module Queues
|
|
108
|
+
##
|
|
109
|
+
# The Queues class is only for testing the fake queue implementation.
|
|
110
|
+
# There are 2 data structures involved in tandem. This is due to the
|
|
111
|
+
# Rspec syntax of change(HardJob.jobs, :size). It keeps a reference
|
|
112
|
+
# to the array. Because the array was derived from a filter of the total
|
|
113
|
+
# jobs enqueued, it appeared as though the array didn't change.
|
|
114
|
+
#
|
|
115
|
+
# To solve this, we'll keep 2 hashes containing the jobs. One with keys based
|
|
116
|
+
# on the queue, and another with keys of the job type, so the array for
|
|
117
|
+
# HardJob.jobs is a straight reference to a real array.
|
|
118
|
+
#
|
|
119
|
+
# Queue-based hash:
|
|
120
|
+
#
|
|
121
|
+
# {
|
|
122
|
+
# "default"=>[
|
|
123
|
+
# {
|
|
124
|
+
# "class"=>"TestTesting::HardJob",
|
|
125
|
+
# "args"=>[1, 2],
|
|
126
|
+
# "retry"=>true,
|
|
127
|
+
# "queue"=>"default",
|
|
128
|
+
# "jid"=>"abc5b065c5c4b27fc1102833",
|
|
129
|
+
# "created_at"=>1447445554.419934
|
|
130
|
+
# }
|
|
131
|
+
# ]
|
|
132
|
+
# }
|
|
133
|
+
#
|
|
134
|
+
# Job-based hash:
|
|
135
|
+
#
|
|
136
|
+
# {
|
|
137
|
+
# "TestTesting::HardJob"=>[
|
|
138
|
+
# {
|
|
139
|
+
# "class"=>"TestTesting::HardJob",
|
|
140
|
+
# "args"=>[1, 2],
|
|
141
|
+
# "retry"=>true,
|
|
142
|
+
# "queue"=>"default",
|
|
143
|
+
# "jid"=>"abc5b065c5c4b27fc1102833",
|
|
144
|
+
# "created_at"=>1447445554.419934
|
|
145
|
+
# }
|
|
146
|
+
# ]
|
|
147
|
+
# }
|
|
148
|
+
#
|
|
149
|
+
# Example:
|
|
150
|
+
#
|
|
151
|
+
# require 'sidekiq/testing'
|
|
152
|
+
#
|
|
153
|
+
# assert_equal 0, Sidekiq::Queues["default"].size
|
|
154
|
+
# HardJob.perform_async(:something)
|
|
155
|
+
# assert_equal 1, Sidekiq::Queues["default"].size
|
|
156
|
+
# assert_equal :something, Sidekiq::Queues["default"].first['args'][0]
|
|
157
|
+
#
|
|
158
|
+
# You can also clear all jobs:
|
|
159
|
+
#
|
|
160
|
+
# assert_equal 0, Sidekiq::Queues["default"].size
|
|
161
|
+
# HardJob.perform_async(:something)
|
|
162
|
+
# Sidekiq::Queues.clear_all
|
|
163
|
+
# assert_equal 0, Sidekiq::Queues["default"].size
|
|
164
|
+
#
|
|
165
|
+
# This can be useful to make sure jobs don't linger between tests:
|
|
166
|
+
#
|
|
167
|
+
# RSpec.configure do |config|
|
|
168
|
+
# config.before(:each) do
|
|
169
|
+
# Sidekiq::Queues.clear_all
|
|
170
|
+
# end
|
|
171
|
+
# end
|
|
172
|
+
#
|
|
173
|
+
class << self
|
|
174
|
+
def [](queue)
|
|
175
|
+
jobs_by_queue[queue]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def push(queue, klass, job)
|
|
179
|
+
jobs_by_queue[queue] << job
|
|
180
|
+
jobs_by_class[klass] << job
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def jobs_by_queue
|
|
184
|
+
@jobs_by_queue ||= Hash.new { |hash, key| hash[key] = [] }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def jobs_by_class
|
|
188
|
+
@jobs_by_class ||= Hash.new { |hash, key| hash[key] = [] }
|
|
189
|
+
end
|
|
190
|
+
alias_method :jobs_by_worker, :jobs_by_class
|
|
191
|
+
|
|
192
|
+
def delete_for(jid, queue, klass)
|
|
193
|
+
jobs_by_queue[queue.to_s].delete_if { |job| job["jid"] == jid }
|
|
194
|
+
jobs_by_class[klass].delete_if { |job| job["jid"] == jid }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def clear_for(queue, klass)
|
|
198
|
+
jobs_by_queue[queue.to_s].clear
|
|
199
|
+
jobs_by_class[klass].clear
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def clear_all
|
|
203
|
+
jobs_by_queue.clear
|
|
204
|
+
jobs_by_class.clear
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
module Job
|
|
210
|
+
##
|
|
211
|
+
# The Sidekiq testing infrastructure overrides perform_async
|
|
212
|
+
# so that it does not actually touch the network. Instead it
|
|
213
|
+
# stores the asynchronous jobs in a per-class array so that
|
|
214
|
+
# their presence/absence can be asserted by your tests.
|
|
215
|
+
#
|
|
216
|
+
# This is similar to ActionMailer's :test delivery_method and its
|
|
217
|
+
# ActionMailer::Base.deliveries array.
|
|
218
|
+
#
|
|
219
|
+
# Example:
|
|
220
|
+
#
|
|
221
|
+
# require 'sidekiq/testing'
|
|
222
|
+
#
|
|
223
|
+
# assert_equal 0, HardJob.jobs.size
|
|
224
|
+
# HardJob.perform_async(:something)
|
|
225
|
+
# assert_equal 1, HardJob.jobs.size
|
|
226
|
+
# assert_equal :something, HardJob.jobs[0]['args'][0]
|
|
227
|
+
#
|
|
228
|
+
# You can also clear and drain all job types:
|
|
229
|
+
#
|
|
230
|
+
# Sidekiq::Job.clear_all # or .drain_all
|
|
231
|
+
#
|
|
232
|
+
# This can be useful to make sure jobs don't linger between tests:
|
|
233
|
+
#
|
|
234
|
+
# RSpec.configure do |config|
|
|
235
|
+
# config.before(:each) do
|
|
236
|
+
# Sidekiq::Job.clear_all
|
|
237
|
+
# end
|
|
238
|
+
# end
|
|
239
|
+
#
|
|
240
|
+
# or for acceptance testing, i.e. with cucumber:
|
|
241
|
+
#
|
|
242
|
+
# AfterStep do
|
|
243
|
+
# Sidekiq::Job.drain_all
|
|
244
|
+
# end
|
|
245
|
+
#
|
|
246
|
+
# When I sign up as "foo@example.com"
|
|
247
|
+
# Then I should receive a welcome email to "foo@example.com"
|
|
248
|
+
#
|
|
249
|
+
module ClassMethods
|
|
250
|
+
# Queue for this worker
|
|
251
|
+
def queue
|
|
252
|
+
get_sidekiq_options["queue"]
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Jobs queued for this worker
|
|
256
|
+
def jobs
|
|
257
|
+
Queues.jobs_by_class[to_s]
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Clear all jobs for this worker
|
|
261
|
+
def clear
|
|
262
|
+
Queues.clear_for(queue, to_s)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# Drain and run all jobs for this worker
|
|
266
|
+
def drain
|
|
267
|
+
while jobs.any?
|
|
268
|
+
next_job = jobs.first
|
|
269
|
+
Queues.delete_for(next_job["jid"], next_job["queue"], to_s)
|
|
270
|
+
process_job(next_job)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Pop out a single job and perform it
|
|
275
|
+
def perform_one
|
|
276
|
+
raise(EmptyQueueError, "perform_one called with empty job queue") if jobs.empty?
|
|
277
|
+
next_job = jobs.first
|
|
278
|
+
Queues.delete_for(next_job["jid"], next_job["queue"], to_s)
|
|
279
|
+
process_job(next_job)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def process_job(job)
|
|
283
|
+
instance = new
|
|
284
|
+
instance.jid = job["jid"]
|
|
285
|
+
instance.bid = job["bid"] if instance.respond_to?(:bid=)
|
|
286
|
+
Sidekiq::Testing.server_middleware.invoke(instance, job, job["queue"]) do
|
|
287
|
+
execute_job(instance, job["args"])
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def execute_job(worker, args)
|
|
292
|
+
worker.perform(*args)
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
class << self
|
|
297
|
+
def jobs # :nodoc:
|
|
298
|
+
Queues.jobs_by_queue.values.flatten
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Clear all queued jobs
|
|
302
|
+
def clear_all
|
|
303
|
+
Queues.clear_all
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Drain (execute) all queued jobs
|
|
307
|
+
def drain_all
|
|
308
|
+
while jobs.any?
|
|
309
|
+
job_classes = jobs.map { |job| job["class"] }.uniq
|
|
310
|
+
|
|
311
|
+
job_classes.each do |job_class|
|
|
312
|
+
Object.const_get(job_class).drain
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
module TestingExtensions
|
|
320
|
+
def jobs_for(klass)
|
|
321
|
+
jobs.select do |job|
|
|
322
|
+
marshalled = job["args"][0]
|
|
323
|
+
marshalled.index(klass.to_s) && YAML.safe_load(marshalled)[0] == klass
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
if defined?(::Rails) && Rails.respond_to?(:env) && !Rails.env.test? && !$TESTING # rubocop:disable Style/GlobalVars
|
|
330
|
+
warn("⛔️ WARNING: Sidekiq testing API enabled, but this is not the test environment. Your jobs will not go to Redis.", uplevel: 1)
|
|
331
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "sidekiq/client"
|
|
5
|
+
|
|
6
|
+
module Sidekiq
|
|
7
|
+
class TransactionAwareClient
|
|
8
|
+
def initialize(pool: nil, config: nil)
|
|
9
|
+
@redis_client = Client.new(pool: pool, config: config)
|
|
10
|
+
@transaction_backend =
|
|
11
|
+
if ActiveRecord.version >= Gem::Version.new("7.2")
|
|
12
|
+
ActiveRecord.method(:after_all_transactions_commit)
|
|
13
|
+
else
|
|
14
|
+
AfterCommitEverywhere.method(:after_commit)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def batching?
|
|
19
|
+
Thread.current[:sidekiq_batch]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def push(item)
|
|
23
|
+
# 6160 we can't support both Sidekiq::Batch and transactions.
|
|
24
|
+
return @redis_client.push(item) if batching?
|
|
25
|
+
|
|
26
|
+
# pre-allocate the JID so we can return it immediately and
|
|
27
|
+
# save it to the database as part of the transaction.
|
|
28
|
+
item["jid"] ||= SecureRandom.hex(12)
|
|
29
|
+
@transaction_backend.call { @redis_client.push(item) }
|
|
30
|
+
item["jid"]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
##
|
|
34
|
+
# We don't provide transactionality for push_bulk because we don't want
|
|
35
|
+
# to hold potentially hundreds of thousands of job records in memory due to
|
|
36
|
+
# a long running enqueue process.
|
|
37
|
+
def push_bulk(items)
|
|
38
|
+
@redis_client.push_bulk(items)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
##
|
|
44
|
+
# Use `Sidekiq.transactional_push!` in your sidekiq.rb initializer
|
|
45
|
+
module Sidekiq
|
|
46
|
+
def self.transactional_push!
|
|
47
|
+
if ActiveRecord.version < Gem::Version.new("7.2")
|
|
48
|
+
begin
|
|
49
|
+
require "after_commit_everywhere"
|
|
50
|
+
rescue LoadError
|
|
51
|
+
raise %q(You need ActiveRecord >= 7.2 or to add `gem "after_commit_everywhere"` to your Gemfile to use Sidekiq's transactional client)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
Sidekiq.default_job_options["client_class"] = Sidekiq::TransactionAwareClient
|
|
56
|
+
Sidekiq::JobUtil::TRANSIENT_ATTRIBUTES << "client_class"
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
class TUI
|
|
3
|
+
module Controls
|
|
4
|
+
# Defines data for input handling and for displaying controls.
|
|
5
|
+
# :code is the key code for input handling.
|
|
6
|
+
# :display and :description are shown in the controls area, with different
|
|
7
|
+
# styling between them. If :display is omitted, :code is displayed instead.
|
|
8
|
+
# :action is a lambda to execute when the control is triggered.
|
|
9
|
+
# :refresh means the action requires immediate refreshing of data
|
|
10
|
+
#
|
|
11
|
+
# Conventions: dangerous/irreversible actions should use UPPERCASE codes.
|
|
12
|
+
# The Shift button means "I'm sure".
|
|
13
|
+
GLOBAL = [
|
|
14
|
+
{code: "?", display: "?", description: "Help", action: ->(tui, tab) { tui.show_help }},
|
|
15
|
+
{code: "left", display: "←/→", description: "Select Tab", action: ->(tui, tab) { tui.navigate(:left) }, refresh: true},
|
|
16
|
+
{code: "right", action: ->(tui, tab) { tui.navigate(:right) }, refresh: true},
|
|
17
|
+
{code: "q", description: "Quit", action: ->(tui, tab) { :quit }},
|
|
18
|
+
{code: "c", modifiers: ["ctrl"], action: ->(tui, tab) { :quit }}
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
SHARED = {
|
|
22
|
+
pageable: [
|
|
23
|
+
{code: "h", display: "h/l", description: "Prev/Next Page",
|
|
24
|
+
action: ->(tui, tab) { tab.prev_page }, refresh: true},
|
|
25
|
+
{code: "l", action: ->(tui, tab) { tab.next_page }, refresh: true}
|
|
26
|
+
],
|
|
27
|
+
selectable: [
|
|
28
|
+
{code: "k", display: "j/k", description: "Prev/Next Row",
|
|
29
|
+
action: ->(tui, tab) { tab.navigate_row(:up) }},
|
|
30
|
+
{code: "j", action: ->(tui, tab) { tab.navigate_row(:down) }},
|
|
31
|
+
{code: "x", description: "Select", action: ->(tui, tab) { tab.toggle_select }},
|
|
32
|
+
{code: "A", modifiers: ["shift"], display: "A", description: "Select All",
|
|
33
|
+
action: ->(tui, tab) { tab.toggle_select(:all) }}
|
|
34
|
+
],
|
|
35
|
+
filterable: [
|
|
36
|
+
{code: "/", display: "/", description: "Filter", action: ->(tui, tab) { tab.start_filtering }},
|
|
37
|
+
{code: "backspace", action: ->(tui, tab) { tab.remove_last_char_from_filter }, refresh: true},
|
|
38
|
+
{code: "enter", action: ->(tui, tab) { tab.stop_filtering }, refresh: true},
|
|
39
|
+
{code: "esc", action: ->(tui, tab) { tab.stop_and_clear_filtering }, refresh: true}
|
|
40
|
+
]
|
|
41
|
+
}.freeze
|
|
42
|
+
|
|
43
|
+
# Returns an array of symbols for functionality which this tab implements
|
|
44
|
+
def features
|
|
45
|
+
[]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def controls
|
|
49
|
+
GLOBAL + SHARED.slice(*features).values.flatten
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Sidekiq
|
|
2
|
+
class TUI
|
|
3
|
+
module Filtering
|
|
4
|
+
def filtering?
|
|
5
|
+
@data[:filtering]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def current_filter
|
|
9
|
+
@data[:filter]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def start_filtering
|
|
13
|
+
@data[:filtering] = true
|
|
14
|
+
@data[:filter] = ""
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def stop_filtering
|
|
18
|
+
return unless @data[:filtering]
|
|
19
|
+
|
|
20
|
+
@data[:filtering] = false
|
|
21
|
+
@data[:selected] = []
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def stop_and_clear_filtering
|
|
25
|
+
return unless @data[:filtering]
|
|
26
|
+
|
|
27
|
+
@data[:filtering] = false
|
|
28
|
+
@data[:filter] = nil
|
|
29
|
+
@data[:selected] = []
|
|
30
|
+
on_filter_change
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def remove_last_char_from_filter
|
|
34
|
+
return unless @data[:filtering]
|
|
35
|
+
|
|
36
|
+
@data[:filter] = @data[:filter].empty? ? "" : @data[:filter][0..-2]
|
|
37
|
+
on_filter_change
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def append_to_filter(string)
|
|
41
|
+
return unless @data[:filtering]
|
|
42
|
+
|
|
43
|
+
@data[:filter] += string
|
|
44
|
+
@data[:selected] = []
|
|
45
|
+
on_filter_change
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def on_filter_change
|
|
49
|
+
# callback for subclasses
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|