sidekiq-status 3.0.3 → 4.0.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/.devcontainer/Dockerfile +2 -0
- data/.devcontainer/README.md +57 -0
- data/.devcontainer/devcontainer.json +55 -0
- data/.devcontainer/docker-compose.yml +19 -0
- data/.github/workflows/ci.yaml +9 -6
- data/Appraisals +14 -6
- data/CHANGELOG.md +12 -0
- data/Dockerfile +5 -0
- data/README.md +756 -41
- data/Rakefile +153 -0
- data/docker-compose.yml +15 -0
- data/gemfiles/{sidekiq_6.1.gemfile → sidekiq_7.0.gemfile} +1 -1
- data/gemfiles/sidekiq_7.3.gemfile +7 -0
- data/gemfiles/sidekiq_8.0.gemfile +7 -0
- data/gemfiles/{sidekiq_6.x.gemfile → sidekiq_8.x.gemfile} +1 -1
- data/lib/sidekiq-status/client_middleware.rb +4 -3
- data/lib/sidekiq-status/helpers.rb +94 -0
- data/lib/sidekiq-status/server_middleware.rb +6 -21
- data/lib/sidekiq-status/storage.rb +12 -3
- data/lib/sidekiq-status/version.rb +1 -1
- data/lib/sidekiq-status/web.rb +67 -93
- data/lib/sidekiq-status/worker.rb +6 -10
- data/lib/sidekiq-status.rb +21 -5
- data/sidekiq-status.gemspec +7 -1
- data/spec/environment.rb +12 -1
- data/spec/lib/sidekiq-status/client_middleware_spec.rb +8 -0
- data/spec/lib/sidekiq-status/server_middleware_spec.rb +13 -0
- data/spec/lib/sidekiq-status/web_spec.rb +72 -3
- data/spec/lib/sidekiq-status/worker_spec.rb +3 -3
- data/spec/lib/sidekiq-status_spec.rb +20 -3
- data/spec/spec_helper.rb +3 -8
- data/spec/support/test_jobs.rb +11 -0
- data/spec/test_environment.rb +1 -0
- data/web/assets/statuses.css +124 -0
- data/web/assets/statuses.js +24 -0
- data/web/views/status.erb +131 -93
- data/web/views/status_not_found.erb +1 -1
- data/web/views/statuses.erb +23 -79
- metadata +93 -14
data/README.md
CHANGED
@@ -4,7 +4,29 @@
|
|
4
4
|
|
5
5
|
Sidekiq-status is an extension to [Sidekiq](https://github.com/mperham/sidekiq) that tracks information about your Sidekiq and provides a UI to that purpose. It was inspired by [resque-status](https://github.com/quirkey/resque-status).
|
6
6
|
|
7
|
-
|
7
|
+
Supports Ruby 3.2+ and Sidekiq 7.0+ or newer.
|
8
|
+
|
9
|
+
## Table of Contents
|
10
|
+
|
11
|
+
- [Installation](#installation)
|
12
|
+
- [Migration Guides](#migration-guides)
|
13
|
+
- [Migrating to Version 4.x from 3.x](#migrating-to-version-4x-from-3x)
|
14
|
+
- [Migrating to Version 3.x from 2.x](#migrating-to-version-3x-from-2x)
|
15
|
+
- [Setup Checklist](#setup-checklist)
|
16
|
+
- [Configuration](#configuration)
|
17
|
+
- [Expiration Times](#expiration-times)
|
18
|
+
- [Retrieving Status](#retrieving-status)
|
19
|
+
- [ActiveJob Support](#activejob-support)
|
20
|
+
- [Tracking Progress and Storing Data](#tracking-progress-and-storing-data)
|
21
|
+
- [Stopping a Running Job](#stopping-a-running-job)
|
22
|
+
- [Unscheduling Jobs](#unscheduling)
|
23
|
+
- [Deleting Job Status](#deleting-job-status-by-job-id)
|
24
|
+
- [Sidekiq Web Integration](#sidekiq-web-integration)
|
25
|
+
- [Testing](#testing)
|
26
|
+
- [Development Environment](#development-environment)
|
27
|
+
- [Testing with Appraisal](#testing-with-appraisal)
|
28
|
+
- [Troubleshooting](#troubleshooting)
|
29
|
+
- [Contributing](#contributing)
|
8
30
|
|
9
31
|
## Installation
|
10
32
|
|
@@ -20,6 +42,14 @@ Or install it yourself as:
|
|
20
42
|
gem install sidekiq-status
|
21
43
|
```
|
22
44
|
|
45
|
+
## Migration Guides
|
46
|
+
|
47
|
+
### Migrating to Version 4.x from 3.x
|
48
|
+
|
49
|
+
Version 4.0.0 adds support for Ruby 3.3, 3.4 and Sidekiq 8.x, but drops support for Sidekiq 6.x and Ruby versions that are now end-of-life (specifically, Ruby 2.7.x - Ruby 3.1.x).
|
50
|
+
|
51
|
+
Version 4.0.0 introduces a breaking change in the way job timestamps are stored in Redis, and also renames `#working_at` to `#updated_at`. Additionally, this version includes major UI improvements with enhanced progress bars and better web interface styling.
|
52
|
+
|
23
53
|
### Migrating to Version 3.x from 2.x
|
24
54
|
|
25
55
|
Version 3.0.0 adds support for Sidekiq 7.x, but drops support for Sidekiq 5.x. **You should be able to upgrade cleanly from version 2.x to 3.x provided you are running Sidekiq 6.x or newer.**
|
@@ -109,21 +139,74 @@ The default expiration time is 30 minutes.
|
|
109
139
|
|
110
140
|
### Retrieving Status
|
111
141
|
|
112
|
-
|
142
|
+
Query for job status at any time up to expiration:
|
113
143
|
|
114
|
-
```
|
144
|
+
```ruby
|
115
145
|
job_id = MyJob.perform_async(*args)
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
Sidekiq::Status
|
123
|
-
|
146
|
+
```
|
147
|
+
|
148
|
+
#### Basic Status Queries
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
# Get current status as symbol
|
152
|
+
status = Sidekiq::Status.status(job_id)
|
153
|
+
# Returns: :queued, :working, :retrying, :complete, :failed, :stopped, :interrupted, or nil after expiry
|
154
|
+
|
155
|
+
# Check specific status with boolean methods
|
156
|
+
Sidekiq::Status.queued?(job_id) # true if job is queued
|
157
|
+
Sidekiq::Status.working?(job_id) # true if job is currently running
|
158
|
+
Sidekiq::Status.retrying?(job_id) # true if job is retrying after failure
|
159
|
+
Sidekiq::Status.complete?(job_id) # true if job completed successfully
|
160
|
+
Sidekiq::Status.failed?(job_id) # true if job failed permanently
|
161
|
+
Sidekiq::Status.interrupted?(job_id) # true if job was interrupted
|
162
|
+
Sidekiq::Status.stopped?(job_id) # true if job was manually stopped
|
163
|
+
```
|
164
|
+
|
165
|
+
#### Progress and Completion
|
124
166
|
|
167
|
+
```ruby
|
168
|
+
# Get progress information
|
169
|
+
Sidekiq::Status.at(job_id) # Current progress (e.g., 42)
|
170
|
+
Sidekiq::Status.total(job_id) # Total items to process (e.g., 100)
|
171
|
+
Sidekiq::Status.pct_complete(job_id) # Percentage complete (e.g., 42)
|
172
|
+
Sidekiq::Status.message(job_id) # Current status message
|
173
|
+
```
|
174
|
+
|
175
|
+
#### Timing Information
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
# Get timing data (returns Unix timestamps as integers, or nil)
|
179
|
+
Sidekiq::Status.enqueued_at(job_id) # When job was enqueued
|
180
|
+
Sidekiq::Status.started_at(job_id) # When job started processing
|
181
|
+
Sidekiq::Status.updated_at(job_id) # Last update time
|
182
|
+
Sidekiq::Status.ended_at(job_id) # When job finished
|
183
|
+
|
184
|
+
# Estimated time to completion (in seconds, or nil)
|
185
|
+
Sidekiq::Status.eta(job_id) # Based on current progress rate
|
186
|
+
```
|
187
|
+
|
188
|
+
#### Custom Data Retrieval
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
# Get specific custom field
|
192
|
+
Sidekiq::Status.get(job_id, :field_name) # Returns string or nil
|
193
|
+
|
194
|
+
# Get all job data as hash
|
195
|
+
data = Sidekiq::Status.get_all(job_id)
|
196
|
+
# Returns: {
|
197
|
+
# "status" => "working",
|
198
|
+
# "updated_at" => "1640995200",
|
199
|
+
# "enqueued_at" => "1640995100",
|
200
|
+
# "started_at" => "1640995150",
|
201
|
+
# "at" => "42",
|
202
|
+
# "total" => "100",
|
203
|
+
# "pct_complete" => "42",
|
204
|
+
# "message" => "Processing...",
|
205
|
+
# "custom_field" => "custom_value"
|
206
|
+
# }
|
125
207
|
```
|
126
|
-
|
208
|
+
|
209
|
+
**Important:** All status methods return `nil` or `false` after the expiration time.
|
127
210
|
|
128
211
|
### ActiveJob Support
|
129
212
|
|
@@ -147,41 +230,166 @@ end
|
|
147
230
|
|
148
231
|
### Tracking Progress and Storing Data
|
149
232
|
|
150
|
-
|
233
|
+
Sidekiq-status provides comprehensive progress tracking and custom data storage capabilities for jobs that include the `Sidekiq::Status::Worker` module.
|
151
234
|
|
152
|
-
|
235
|
+
#### Setting Progress
|
236
|
+
|
237
|
+
```ruby
|
153
238
|
class MyJob
|
154
239
|
include Sidekiq::Worker
|
155
|
-
include Sidekiq::Status::Worker #
|
240
|
+
include Sidekiq::Status::Worker # Required for progress tracking
|
156
241
|
|
157
242
|
def perform(*args)
|
158
|
-
#
|
243
|
+
# Set total number of items to process
|
244
|
+
total 100
|
245
|
+
|
246
|
+
# Update progress throughout your job
|
247
|
+
(1..100).each do |i|
|
248
|
+
# Do some work here...
|
249
|
+
sleep 0.1
|
250
|
+
|
251
|
+
# Update progress with optional message
|
252
|
+
at i, "Processing item #{i}"
|
253
|
+
# This automatically calculates percentage: i/100 * 100
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
```
|
159
258
|
|
160
|
-
|
161
|
-
total 100 # by default
|
162
|
-
at 5, "Almost done" # 5/100 = 5 % completion
|
259
|
+
#### Storing and Retrieving Custom Data
|
163
260
|
|
164
|
-
|
165
|
-
|
261
|
+
```ruby
|
262
|
+
class MyJob
|
263
|
+
include Sidekiq::Worker
|
264
|
+
include Sidekiq::Status::Worker
|
166
265
|
|
167
|
-
|
168
|
-
#
|
169
|
-
|
266
|
+
def perform(user_id, options = {})
|
267
|
+
# Store custom data associated with this job
|
268
|
+
store user_id: user_id
|
269
|
+
store options: options.to_json
|
270
|
+
store phase: 'initialization'
|
271
|
+
|
272
|
+
# Store multiple fields at once
|
273
|
+
store(
|
274
|
+
current_batch: 1,
|
275
|
+
batch_size: 50,
|
276
|
+
errors_count: 0
|
277
|
+
)
|
278
|
+
|
279
|
+
# Retrieve stored data (always returns String or nil)
|
280
|
+
stored_user_id = retrieve(:user_id)
|
281
|
+
stored_options = JSON.parse(retrieve(:options) || '{}')
|
282
|
+
|
283
|
+
# Update progress and custom data together
|
284
|
+
50.times do |i|
|
285
|
+
# Do work...
|
286
|
+
|
287
|
+
# Update progress with custom data
|
288
|
+
at i, "Processing batch #{i}"
|
289
|
+
store current_item: i, last_processed_at: Time.now.to_s
|
290
|
+
end
|
291
|
+
|
292
|
+
# Mark different phases
|
293
|
+
store phase: 'cleanup'
|
294
|
+
at 100, "Job completed successfully"
|
170
295
|
end
|
171
296
|
end
|
172
297
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
Sidekiq::Status
|
178
|
-
Sidekiq::Status
|
179
|
-
Sidekiq::Status
|
180
|
-
|
181
|
-
|
182
|
-
Sidekiq::Status
|
298
|
+
# From outside the job, retrieve custom data
|
299
|
+
job_id = MyJob.perform_async(123, { priority: 'high' })
|
300
|
+
|
301
|
+
# Get specific fields
|
302
|
+
user_id = Sidekiq::Status.get(job_id, :user_id) #=> "123"
|
303
|
+
phase = Sidekiq::Status.get(job_id, :phase) #=> "cleanup"
|
304
|
+
errors = Sidekiq::Status.get(job_id, :errors_count) #=> "0"
|
305
|
+
|
306
|
+
# Get all job data including progress and custom fields
|
307
|
+
all_data = Sidekiq::Status.get_all(job_id)
|
308
|
+
puts all_data['phase'] #=> "cleanup"
|
309
|
+
puts all_data['current_batch'] #=> "1"
|
310
|
+
puts all_data['pct_complete'] #=> "100"
|
311
|
+
```
|
312
|
+
|
313
|
+
#### Progress Tracking Patterns
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
class DataImportJob
|
317
|
+
include Sidekiq::Worker
|
318
|
+
include Sidekiq::Status::Worker
|
319
|
+
|
320
|
+
def perform(file_path)
|
321
|
+
# Example: Processing a CSV file
|
322
|
+
csv_data = CSV.read(file_path)
|
323
|
+
|
324
|
+
# Set total based on data size
|
325
|
+
total csv_data.size
|
326
|
+
|
327
|
+
csv_data.each_with_index do |row, index|
|
328
|
+
begin
|
329
|
+
# Process the row
|
330
|
+
process_row(row)
|
331
|
+
|
332
|
+
# Update progress
|
333
|
+
at index + 1, "Processed row #{index + 1} of #{csv_data.size}"
|
334
|
+
|
335
|
+
# Store running statistics
|
336
|
+
store(
|
337
|
+
processed_count: index + 1,
|
338
|
+
last_processed_id: row['id'],
|
339
|
+
success_rate: calculate_success_rate
|
340
|
+
)
|
341
|
+
|
342
|
+
rescue => e
|
343
|
+
# Log error but continue processing
|
344
|
+
error_count = (retrieve(:error_count) || '0').to_i + 1
|
345
|
+
store error_count: error_count, last_error: e.message
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
# Monitor progress from outside
|
352
|
+
job_id = DataImportJob.perform_async('data.csv')
|
353
|
+
|
354
|
+
# Check progress periodically
|
355
|
+
while !Sidekiq::Status.complete?(job_id) && !Sidekiq::Status.failed?(job_id)
|
356
|
+
progress = Sidekiq::Status.pct_complete(job_id)
|
357
|
+
message = Sidekiq::Status.message(job_id)
|
358
|
+
errors = Sidekiq::Status.get(job_id, :error_count) || '0'
|
359
|
+
|
360
|
+
puts "Progress: #{progress}% - #{message} (#{errors} errors)"
|
361
|
+
sleep 1
|
362
|
+
end
|
363
|
+
```
|
364
|
+
|
365
|
+
#### External Progress Updates
|
366
|
+
|
367
|
+
You can also update job progress from outside the worker:
|
368
|
+
|
369
|
+
```ruby
|
370
|
+
# Update progress for any job by ID
|
371
|
+
job_id = MyJob.perform_async
|
372
|
+
Sidekiq::Status.store_for_id(job_id, {
|
373
|
+
external_update: Time.now.to_s,
|
374
|
+
updated_by: 'external_system'
|
375
|
+
})
|
376
|
+
```
|
377
|
+
|
378
|
+
### Stopping a running job
|
379
|
+
|
380
|
+
You can ask a job to stop execution by calling `.stop!` with its job ID. The
|
381
|
+
next time the job calls `.at` it will raise
|
382
|
+
`Sidekiq::Status::Worker::Stopped`. It will not attempt to retry.
|
383
|
+
|
384
|
+
```ruby
|
385
|
+
job_id = MyJob.perform_async
|
386
|
+
Sidekiq::Status.stop! job_id #=> true
|
387
|
+
Sidekiq::Status.status job_id #=> :stopped
|
183
388
|
```
|
184
389
|
|
390
|
+
Note this will not kill a running job that is stuck. The job must call `.at`
|
391
|
+
for it to be stopped in this way.
|
392
|
+
|
185
393
|
### Unscheduling
|
186
394
|
|
187
395
|
```ruby
|
@@ -210,25 +418,80 @@ Sidekiq::Status.delete(bad_job_id) #=> 0
|
|
210
418
|
|
211
419
|
### Sidekiq Web Integration
|
212
420
|
|
213
|
-
This gem provides
|
421
|
+
This gem provides a comprehensive extension to Sidekiq's web interface that allows you to monitor job statuses, progress, and custom data in real-time.
|
422
|
+
|
423
|
+
#### Features
|
424
|
+
|
425
|
+
- **Job Status Dashboard** at `/statuses` - View all tracked jobs
|
426
|
+
- **Individual Job Details** at `/statuses/:job_id` - Detailed job information
|
427
|
+
- **Real-time Progress Bars** - Visual progress indicators
|
428
|
+
- **Custom Data Display** - View all stored job metadata
|
429
|
+
- **Job Control Actions** - Stop, retry, or delete jobs
|
430
|
+
- **Responsive Design** - Works on desktop and mobile
|
431
|
+
- **Dark Mode Support** - Integrates with Sidekiq's theme
|
214
432
|
|
215
433
|

|
216
434
|
|
217
|
-
|
435
|
+
The main statuses page shows:
|
436
|
+
- Job ID and worker class
|
437
|
+
- Current status with color coding
|
438
|
+
- Progress bar with percentage complete
|
439
|
+
- Elapsed time and ETA
|
440
|
+
- Last updated timestamp
|
441
|
+
- Custom actions (stop, retry, delete)
|
218
442
|
|
219
443
|

|
220
444
|
|
221
|
-
|
445
|
+
The individual job page provides:
|
446
|
+
- Complete job metadata
|
447
|
+
- Custom data fields
|
448
|
+
- Detailed timing information
|
449
|
+
- Full progress history
|
450
|
+
- Error messages (if failed)
|
222
451
|
|
223
452
|
#### Adding the Web Interface
|
224
453
|
|
225
|
-
To
|
454
|
+
To enable the web interface, require the web module after setting up Sidekiq Web:
|
226
455
|
|
227
|
-
```
|
456
|
+
```ruby
|
228
457
|
require 'sidekiq/web'
|
229
458
|
require 'sidekiq-status/web'
|
459
|
+
|
460
|
+
# In Rails, add to config/routes.rb:
|
461
|
+
mount Sidekiq::Web => '/sidekiq'
|
462
|
+
```
|
463
|
+
|
464
|
+
#### Configuration Options
|
465
|
+
|
466
|
+
Customize the web interface behavior:
|
467
|
+
|
468
|
+
```ruby
|
469
|
+
# Configure pagination (default: 25 per page)
|
470
|
+
Sidekiq::Status::Web.default_per_page = 50
|
471
|
+
Sidekiq::Status::Web.per_page_opts = [25, 50, 100, 200]
|
472
|
+
|
473
|
+
# The web interface will show these options in a dropdown
|
230
474
|
```
|
231
475
|
|
476
|
+
#### Web Interface Security
|
477
|
+
|
478
|
+
Since job data may contain sensitive information, secure the web interface:
|
479
|
+
|
480
|
+
```ruby
|
481
|
+
# Example with HTTP Basic Auth
|
482
|
+
Sidekiq::Web.use Rack::Auth::Basic do |username, password|
|
483
|
+
ActiveSupport::SecurityUtils.secure_compare(username, ENV['SIDEKIQ_USERNAME']) &&
|
484
|
+
ActiveSupport::SecurityUtils.secure_compare(password, ENV['SIDEKIQ_PASSWORD'])
|
485
|
+
end
|
486
|
+
|
487
|
+
# Example with devise (Rails)
|
488
|
+
authenticate :user, lambda { |u| u.admin? } do
|
489
|
+
mount Sidekiq::Web => '/sidekiq'
|
490
|
+
end
|
491
|
+
```
|
492
|
+
|
493
|
+
**Note:** Only jobs that include `Sidekiq::Status::Worker` will appear in the web interface.
|
494
|
+
|
232
495
|
### Testing
|
233
496
|
|
234
497
|
Drawing analogy from [sidekiq testing by inlining](https://github.com/mperham/sidekiq/wiki/Testing#testing-workers-inline),
|
@@ -242,11 +505,463 @@ Inlining example:
|
|
242
505
|
|
243
506
|
You can run Sidekiq workers inline in your tests by requiring the `sidekiq/testing/inline` file in your `{test,spec}_helper.rb`:
|
244
507
|
|
245
|
-
|
508
|
+
```ruby
|
509
|
+
require 'sidekiq/testing/inline'
|
510
|
+
```
|
246
511
|
|
247
512
|
To use `sidekiq-status` inlining, require it too in your `{test,spec}_helper.rb`:
|
248
513
|
|
249
|
-
|
514
|
+
```ruby
|
515
|
+
require 'sidekiq-status/testing/inline'
|
516
|
+
```
|
517
|
+
|
518
|
+
## Troubleshooting
|
519
|
+
|
520
|
+
### Common Issues and Solutions
|
521
|
+
|
522
|
+
#### Job Status Always Returns `nil`
|
523
|
+
|
524
|
+
**Problem:** `Sidekiq::Status.status(job_id)` returns `nil` even for recent jobs.
|
525
|
+
|
526
|
+
**Solutions:**
|
527
|
+
1. **Verify middleware configuration:**
|
528
|
+
```ruby
|
529
|
+
# Make sure both client and server middleware are configured
|
530
|
+
Sidekiq.configure_client do |config|
|
531
|
+
Sidekiq::Status.configure_client_middleware config
|
532
|
+
end
|
533
|
+
|
534
|
+
Sidekiq.configure_server do |config|
|
535
|
+
Sidekiq::Status.configure_server_middleware config
|
536
|
+
Sidekiq::Status.configure_client_middleware config # Also needed in server
|
537
|
+
end
|
538
|
+
```
|
539
|
+
|
540
|
+
2. **Check if job includes the Worker module:**
|
541
|
+
```ruby
|
542
|
+
class MyJob
|
543
|
+
include Sidekiq::Worker
|
544
|
+
include Sidekiq::Status::Worker # This is required!
|
545
|
+
end
|
546
|
+
```
|
547
|
+
|
548
|
+
3. **Verify Redis connection:**
|
549
|
+
```ruby
|
550
|
+
# Test Redis connectivity
|
551
|
+
Sidekiq.redis { |conn| conn.ping } # Should return "PONG"
|
552
|
+
```
|
553
|
+
|
554
|
+
#### Jobs Not Appearing in Web Interface
|
555
|
+
|
556
|
+
**Problem:** Jobs are tracked but don't show up in `/sidekiq/statuses`.
|
557
|
+
|
558
|
+
**Solutions:**
|
559
|
+
1. **Include the web module:**
|
560
|
+
```ruby
|
561
|
+
require 'sidekiq/web'
|
562
|
+
require 'sidekiq-status/web' # Must be after sidekiq/web
|
563
|
+
```
|
564
|
+
|
565
|
+
2. **Check job worker includes status module:**
|
566
|
+
```ruby
|
567
|
+
# Only jobs with this module appear in web interface
|
568
|
+
include Sidekiq::Status::Worker
|
569
|
+
```
|
570
|
+
|
571
|
+
3. **Verify Redis key existence:**
|
572
|
+
```ruby
|
573
|
+
# Check if status keys exist in Redis
|
574
|
+
Sidekiq.redis do |conn|
|
575
|
+
keys = conn.scan(match: 'sidekiq:status:*', count: 100)
|
576
|
+
puts "Found #{keys.size} status keys"
|
577
|
+
end
|
578
|
+
```
|
579
|
+
|
580
|
+
#### Progress Not Updating
|
581
|
+
|
582
|
+
**Problem:** Job progress stays at 0% or doesn't update.
|
583
|
+
|
584
|
+
**Solutions:**
|
585
|
+
1. **Call `total` before `at`:**
|
586
|
+
```ruby
|
587
|
+
def perform
|
588
|
+
total 100 # Set total first
|
589
|
+
at 1 # Then update progress
|
590
|
+
end
|
591
|
+
```
|
592
|
+
|
593
|
+
2. **Use numeric values:**
|
594
|
+
```ruby
|
595
|
+
# Correct
|
596
|
+
at 50, "Halfway done"
|
597
|
+
|
598
|
+
# Wrong - will not calculate percentage correctly
|
599
|
+
at "50", "Halfway done"
|
600
|
+
```
|
601
|
+
|
602
|
+
3. **Check for exceptions:**
|
603
|
+
```ruby
|
604
|
+
def perform
|
605
|
+
total 100
|
606
|
+
begin
|
607
|
+
at 50
|
608
|
+
rescue => e
|
609
|
+
puts "Progress update failed: #{e.message}"
|
610
|
+
end
|
611
|
+
end
|
612
|
+
```
|
613
|
+
|
614
|
+
#### Memory Usage Growing Over Time
|
615
|
+
|
616
|
+
**Problem:** Redis memory usage increases continuously.
|
617
|
+
|
618
|
+
**Solutions:**
|
619
|
+
1. **Set appropriate expiration:**
|
620
|
+
```ruby
|
621
|
+
# Configure shorter expiration for high-volume jobs
|
622
|
+
Sidekiq::Status.configure_client_middleware config, expiration: 5.minutes.to_i
|
623
|
+
```
|
624
|
+
|
625
|
+
2. **Clean up manually if needed:**
|
626
|
+
```ruby
|
627
|
+
# Remove old status data
|
628
|
+
Sidekiq.redis do |conn|
|
629
|
+
old_keys = conn.scan(match: 'sidekiq:status:*').select do |key|
|
630
|
+
conn.ttl(key) == -1 # Keys without expiration
|
631
|
+
end
|
632
|
+
conn.del(*old_keys) unless old_keys.empty?
|
633
|
+
end
|
634
|
+
```
|
635
|
+
|
636
|
+
#### Version Compatibility Issues
|
637
|
+
|
638
|
+
**Problem:** Errors after upgrading Sidekiq or Ruby versions.
|
639
|
+
|
640
|
+
**Solutions:**
|
641
|
+
1. **Check version compatibility:**
|
642
|
+
```ruby
|
643
|
+
# sidekiq-status 4.x requirements:
|
644
|
+
# Ruby 3.2+
|
645
|
+
# Sidekiq 7.0+
|
646
|
+
|
647
|
+
puts "Ruby: #{RUBY_VERSION}"
|
648
|
+
puts "Sidekiq: #{Sidekiq::VERSION}"
|
649
|
+
```
|
650
|
+
|
651
|
+
2. **Update gemfile constraints:**
|
652
|
+
```ruby
|
653
|
+
gem 'sidekiq', '~> 8.0' # Use compatible version
|
654
|
+
gem 'sidekiq-status' # Latest version
|
655
|
+
```
|
656
|
+
|
657
|
+
3. **Check for breaking changes:**
|
658
|
+
- Version 4.x renamed `#working_at` to `#updated_at`
|
659
|
+
- Timestamp storage format changed in 4.x
|
660
|
+
|
661
|
+
#### ActiveJob Integration Issues
|
662
|
+
|
663
|
+
**Problem:** ActiveJob jobs not being tracked.
|
664
|
+
|
665
|
+
**Solutions:**
|
666
|
+
1. **Include module in base class:**
|
667
|
+
```ruby
|
668
|
+
class ApplicationJob < ActiveJob::Base
|
669
|
+
include Sidekiq::Status::Worker # Add to base class
|
670
|
+
end
|
671
|
+
```
|
672
|
+
|
673
|
+
2. **Verify Sidekiq adapter:**
|
674
|
+
```ruby
|
675
|
+
# In config/application.rb or config/environments/production.rb
|
676
|
+
config.active_job.queue_adapter = :sidekiq
|
677
|
+
```
|
678
|
+
|
679
|
+
#### Testing Issues
|
680
|
+
|
681
|
+
**Problem:** Tests failing with status-related code.
|
682
|
+
|
683
|
+
**Solutions:**
|
684
|
+
1. **Use testing inline mode:**
|
685
|
+
```ruby
|
686
|
+
# In test helper
|
687
|
+
require 'sidekiq/testing'
|
688
|
+
require 'sidekiq-status/testing/inline'
|
689
|
+
|
690
|
+
Sidekiq::Testing.inline!
|
691
|
+
```
|
692
|
+
|
693
|
+
2. **Mock status calls in tests:**
|
694
|
+
```ruby
|
695
|
+
# RSpec example
|
696
|
+
allow(Sidekiq::Status).to receive(:status).and_return(:complete)
|
697
|
+
allow(Sidekiq::Status).to receive(:pct_complete).and_return(100)
|
698
|
+
```
|
699
|
+
|
700
|
+
### Performance Considerations
|
701
|
+
|
702
|
+
#### High-Volume Job Optimization
|
703
|
+
|
704
|
+
For applications processing thousands of jobs:
|
705
|
+
|
706
|
+
```ruby
|
707
|
+
# Use longer expiration to reduce Redis operations
|
708
|
+
Sidekiq::Status.configure_client_middleware config, expiration: 24.hours.to_i
|
709
|
+
|
710
|
+
# Reduce progress update frequency
|
711
|
+
class HighVolumeJob
|
712
|
+
include Sidekiq::Worker
|
713
|
+
include Sidekiq::Status::Worker
|
714
|
+
|
715
|
+
def perform(items)
|
716
|
+
total items.size
|
717
|
+
|
718
|
+
items.each_with_index do |item, index|
|
719
|
+
process_item(item)
|
720
|
+
|
721
|
+
# Update progress every 100 items instead of every item
|
722
|
+
if (index + 1) % 100 == 0
|
723
|
+
at index + 1, "Processed #{index + 1} items"
|
724
|
+
end
|
725
|
+
end
|
726
|
+
end
|
727
|
+
end
|
728
|
+
```
|
729
|
+
|
730
|
+
#### Redis Optimization
|
731
|
+
|
732
|
+
```ruby
|
733
|
+
# Use Redis pipelining for batch operations
|
734
|
+
def batch_update_status(job_data)
|
735
|
+
Sidekiq.redis do |conn|
|
736
|
+
conn.pipelined do |pipeline|
|
737
|
+
job_data.each do |job_id, data|
|
738
|
+
pipeline.hmset("sidekiq:status:#{job_id}", data.flatten)
|
739
|
+
end
|
740
|
+
end
|
741
|
+
end
|
742
|
+
end
|
743
|
+
```
|
744
|
+
|
745
|
+
### Getting Help
|
746
|
+
|
747
|
+
If you're still experiencing issues:
|
748
|
+
|
749
|
+
1. **Check the logs:** Look for Redis connection errors or middleware loading issues
|
750
|
+
2. **Enable debug logging:** Add `Sidekiq.logger.level = Logger::DEBUG`
|
751
|
+
3. **Test with minimal example:** Create a simple job to isolate the problem
|
752
|
+
4. **Check GitHub issues:** Search for similar problems
|
753
|
+
5. **Create an issue:** Include Ruby/Sidekiq versions, configuration, and error messages
|
754
|
+
|
755
|
+
## Development Environment
|
756
|
+
|
757
|
+
This project provides multiple ways to set up a consistent development environment with all necessary dependencies.
|
758
|
+
|
759
|
+
### Using VS Code Dev Containers (Recommended)
|
760
|
+
|
761
|
+
The easiest way to get started is using VS Code with the Dev Containers extension:
|
762
|
+
|
763
|
+
1. **Prerequisites:**
|
764
|
+
- [VS Code](https://code.visualstudio.com/)
|
765
|
+
- [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
|
766
|
+
- [Docker Desktop](https://www.docker.com/products/docker-desktop)
|
767
|
+
|
768
|
+
2. **Setup:**
|
769
|
+
```bash
|
770
|
+
git clone https://github.com/kenaniah/sidekiq-status.git
|
771
|
+
cd sidekiq-status
|
772
|
+
code . # Open in VS Code
|
773
|
+
```
|
774
|
+
|
775
|
+
3. **Launch Container:**
|
776
|
+
- When prompted, click "Reopen in Container"
|
777
|
+
- Or use Command Palette (`Ctrl+Shift+P`): "Dev Containers: Reopen in Container"
|
778
|
+
|
779
|
+
The devcontainer automatically provides:
|
780
|
+
- **Ruby 3.4** with all required gems
|
781
|
+
- **Redis 7.4.0** server (auto-started)
|
782
|
+
- **VS Code extensions**: Ruby LSP, Endwise, Docker support
|
783
|
+
- **Pre-configured environment** with proper PATH and aliases
|
784
|
+
|
785
|
+
### Manual Development Setup
|
786
|
+
|
787
|
+
If you prefer a local setup:
|
788
|
+
|
789
|
+
1. **Install Dependencies:**
|
790
|
+
```bash
|
791
|
+
# Ruby 3.2+ required
|
792
|
+
ruby --version # Verify version
|
793
|
+
|
794
|
+
# Install Redis (macOS)
|
795
|
+
brew install redis
|
796
|
+
brew services start redis
|
797
|
+
|
798
|
+
# Install Redis (Ubuntu/Debian)
|
799
|
+
sudo apt-get install redis-server
|
800
|
+
sudo systemctl start redis-server
|
801
|
+
```
|
802
|
+
|
803
|
+
2. **Clone and Setup:**
|
804
|
+
```bash
|
805
|
+
git clone https://github.com/kenaniah/sidekiq-status.git
|
806
|
+
cd sidekiq-status
|
807
|
+
bundle install
|
808
|
+
```
|
809
|
+
|
810
|
+
### Docker Compose Setup
|
811
|
+
|
812
|
+
For a containerized development environment without VS Code:
|
813
|
+
|
814
|
+
```bash
|
815
|
+
# Start development environment
|
816
|
+
docker compose -f .devcontainer/docker-compose.yml up -d
|
817
|
+
|
818
|
+
# Enter the container
|
819
|
+
docker compose -f .devcontainer/docker-compose.yml exec app bash
|
820
|
+
|
821
|
+
# Install dependencies
|
822
|
+
bundle install
|
823
|
+
|
824
|
+
# Stop environment
|
825
|
+
docker compose -f .devcontainer/docker-compose.yml down
|
826
|
+
```
|
827
|
+
|
828
|
+
## Testing with Appraisal
|
829
|
+
|
830
|
+
This project uses [Appraisal](https://github.com/thoughtbot/appraisal) to ensure compatibility across multiple Sidekiq versions. This is crucial because Sidekiq has breaking changes between major versions.
|
831
|
+
|
832
|
+
### Supported Versions
|
833
|
+
|
834
|
+
Current test matrix includes:
|
835
|
+
- **Sidekiq 7.0.x** - Stable release
|
836
|
+
- **Sidekiq 7.3.x** - Recent stable
|
837
|
+
- **Sidekiq 7.x** - Latest 7.x
|
838
|
+
- **Sidekiq 8.0.x** - Latest major version
|
839
|
+
- **Sidekiq 8.x** - Bleeding edge
|
840
|
+
|
841
|
+
### Appraisal Workflow
|
842
|
+
|
843
|
+
#### 1. Install All Dependencies
|
844
|
+
|
845
|
+
```bash
|
846
|
+
# Install base dependencies
|
847
|
+
bundle install
|
848
|
+
|
849
|
+
# Generate and install appraisal gemfiles
|
850
|
+
bundle exec appraisal install
|
851
|
+
```
|
852
|
+
|
853
|
+
This creates version-specific Gemfiles in `gemfiles/` directory:
|
854
|
+
```
|
855
|
+
gemfiles/
|
856
|
+
├── sidekiq_7.0.gemfile # Sidekiq ~> 7.0.0
|
857
|
+
├── sidekiq_7.3.gemfile # Sidekiq ~> 7.3.0
|
858
|
+
├── sidekiq_7.x.gemfile # Sidekiq ~> 7
|
859
|
+
├── sidekiq_8.0.gemfile # Sidekiq ~> 8.0.0
|
860
|
+
└── sidekiq_8.x.gemfile # Sidekiq ~> 8
|
861
|
+
```
|
862
|
+
|
863
|
+
#### 2. Running Tests
|
864
|
+
|
865
|
+
**Test all Sidekiq versions:**
|
866
|
+
```bash
|
867
|
+
bundle exec appraisal rake spec
|
868
|
+
```
|
869
|
+
|
870
|
+
**Test specific version:**
|
871
|
+
```bash
|
872
|
+
# Test against Sidekiq 7.0.x
|
873
|
+
bundle exec appraisal sidekiq-7.0 rake spec
|
874
|
+
|
875
|
+
# Test against Sidekiq 7.3.x
|
876
|
+
bundle exec appraisal sidekiq-7.3 rake spec
|
877
|
+
|
878
|
+
# Test against Sidekiq 8.x
|
879
|
+
bundle exec appraisal sidekiq-8.x rake spec
|
880
|
+
```
|
881
|
+
|
882
|
+
**Quick test with current Gemfile:**
|
883
|
+
```bash
|
884
|
+
bundle exec rake spec
|
885
|
+
# or
|
886
|
+
rake spec
|
887
|
+
```
|
888
|
+
|
889
|
+
#### 3. Interactive Debugging
|
890
|
+
|
891
|
+
**Start console with specific Sidekiq version:**
|
892
|
+
```bash
|
893
|
+
# Debug with Sidekiq 7.0.x dependencies
|
894
|
+
bundle exec appraisal sidekiq-7.0 irb
|
895
|
+
```
|
896
|
+
|
897
|
+
**Run individual test files:**
|
898
|
+
```bash
|
899
|
+
# Test specific file with Sidekiq 8.x
|
900
|
+
bundle exec appraisal sidekiq-8.x rspec spec/lib/sidekiq-status/worker_spec.rb
|
901
|
+
|
902
|
+
# Run with verbose output
|
903
|
+
bundle exec appraisal sidekiq-8.x rspec spec/lib/sidekiq-status/worker_spec.rb -v
|
904
|
+
```
|
905
|
+
|
906
|
+
#### 4. Updating Dependencies
|
907
|
+
|
908
|
+
**Regenerate gemfiles after dependency changes:**
|
909
|
+
```bash
|
910
|
+
# Update Appraisals file, then:
|
911
|
+
bundle exec appraisal generate
|
912
|
+
|
913
|
+
# Install new dependencies
|
914
|
+
bundle exec appraisal install
|
915
|
+
```
|
916
|
+
|
917
|
+
**Update specific version:**
|
918
|
+
```bash
|
919
|
+
# Update only Sidekiq 7.x dependencies
|
920
|
+
bundle exec appraisal sidekiq-7.x bundle update
|
921
|
+
```
|
922
|
+
|
923
|
+
### Testing Best Practices
|
924
|
+
|
925
|
+
#### Running Tests in CI/CD Style
|
926
|
+
|
927
|
+
```bash
|
928
|
+
# Full test suite (like GitHub Actions)
|
929
|
+
bundle exec appraisal install
|
930
|
+
bundle exec appraisal rake spec
|
931
|
+
|
932
|
+
# Check for dependency issues
|
933
|
+
bundle exec bundle-audit check --update
|
934
|
+
```
|
935
|
+
|
936
|
+
### Common Development Tasks
|
937
|
+
|
938
|
+
```bash
|
939
|
+
# Start Redis for testing
|
940
|
+
redis-server
|
941
|
+
|
942
|
+
# Run Sidekiq worker with test environment
|
943
|
+
bundle exec sidekiq -r ./spec/environment.rb
|
944
|
+
|
945
|
+
# Start IRB with sidekiq-status loaded
|
946
|
+
bundle exec irb -r ./lib/sidekiq-status
|
947
|
+
|
948
|
+
# Generate test coverage report
|
949
|
+
COVERAGE=true bundle exec rake spec
|
950
|
+
open coverage/index.html
|
951
|
+
```
|
952
|
+
|
953
|
+
### Docker Development Shortcuts
|
954
|
+
|
955
|
+
```bash
|
956
|
+
# Quick test run using Docker
|
957
|
+
docker compose run --rm sidekiq-status bundle exec rake spec
|
958
|
+
|
959
|
+
# Interactive shell in container
|
960
|
+
docker compose run --rm sidekiq-status bash
|
961
|
+
|
962
|
+
# Test specific Sidekiq version in Docker
|
963
|
+
docker compose run --rm sidekiq-status bundle exec appraisal sidekiq-8.x rake spec
|
964
|
+
```
|
250
965
|
|
251
966
|
## Contributing
|
252
967
|
|