sidekiq_solid_fetch 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3f9d49a1edc5ffd25c20302adac6f5fea21eb3aacab3c441d7a8722bb42faeca
4
+ data.tar.gz: 8c137210e8a8febf7409379c7c207270f0c3964ae378898122cc7e1a6f7ae88d
5
+ SHA512:
6
+ metadata.gz: 2df35c34eed28f7f1c3ed78e853da85b80262a21702e4334699a0c7dadc5ba2f62059dba9dea116882ee71edbfa5c503de1f682b4371e021d580409cd8640322
7
+ data.tar.gz: a3ffb8214437b7f4b579465b03268b3a4a4a393d481036f5ac4e90b510174ef7220f5d88a93f54038712f6e6731882889536396c7eed71b08e528163da0670e4
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.1.1"
3
+ }
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ # For available configuration options, see:
2
+ # https://github.com/standardrb/standard
3
+ ruby_version: 3.1
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## [0.1.1](https://github.com/k0va1/sidekiq_solid_fetch/compare/v0.1.0...v0.1.1) (2025-12-04)
4
+
5
+
6
+ ### Features
7
+
8
+ * basic implementation ([08809c1](https://github.com/k0va1/sidekiq_solid_fetch/commit/08809c11ff15a623b592721785f76706bb62557d))
9
+
10
+ ## Changelog
data/CLAUDE.md ADDED
@@ -0,0 +1,45 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ SidekiqSolidFetch is an open-source Ruby gem that provides a reliable fetch strategy for Sidekiq, similar to Sidekiq Pro's `super_fetch`. It uses Redis LMOVE to atomically move jobs from the queue to a processing queue, ensuring jobs aren't lost if a worker crashes.
8
+
9
+ ## Commands
10
+
11
+ - **Install dependencies**: `bundle install`
12
+ - **Run all tests**: `bundle exec rake spec`
13
+ - **Run a single test file**: `bundle exec rspec spec/path/to/file_spec.rb`
14
+ - **Run a single test by line**: `bundle exec rspec spec/path/to/file_spec.rb:LINE`
15
+ - **Lint code**: `bundle exec rake standard`
16
+ - **Auto-fix lint issues**: `bundle exec standardrb --fix`
17
+ - **Run all checks (tests + lint)**: `bundle exec rake`
18
+ - **Interactive console**: `bin/console`
19
+
20
+ ## Architecture
21
+
22
+ ### Core Components
23
+
24
+ - `SidekiqSolidFetch` (lib/sidekiq_solid_fetch.rb) - Main module with `enable!(config)` method to activate the custom fetcher
25
+ - `SidekiqSolidFetch::Fetcher` (lib/sidekiq_solid_fetch/fetcher.rb) - Custom Sidekiq fetcher implementing reliable queue processing
26
+
27
+ ### Fetcher Design
28
+
29
+ The Fetcher uses Redis LMOVE to atomically move jobs from the source queue to a per-worker processing queue (`processing:queue:{name}:{identity}`). This ensures:
30
+ - Jobs are never lost if a worker crashes mid-execution
31
+ - Unfinished jobs can be requeued via `bulk_requeue`
32
+
33
+ Key classes:
34
+ - `Fetcher` - Implements Sidekiq's fetch interface with `retrieve_work` and `bulk_requeue`
35
+ - `UnitOfWork` - Struct wrapping a job with `acknowledge` and `requeue` methods
36
+
37
+ ### Queue Modes
38
+
39
+ - **Strict mode** (`cap.mode == :strict`) - Processes queues in defined order
40
+ - **Weighted mode** (default) - Shuffles queues to distribute load
41
+
42
+ ## Requirements
43
+
44
+ - Ruby >= 3.1.0
45
+ - Sidekiq >= 7.0
data/Makefile ADDED
@@ -0,0 +1,18 @@
1
+ REDIS_URL ?= redis://localhost:6379/0
2
+
3
+ .PHONY: test lint-fix console install
4
+
5
+ install:
6
+ bundle install
7
+
8
+ console:
9
+ bin/console
10
+
11
+ test:
12
+ REDIS_URL=$(REDIS_URL) bundle exec rspec $(filter-out $@,$(MAKECMDGOALS))
13
+
14
+ lint-fix:
15
+ bundle exec standardrb --fix
16
+
17
+ %:
18
+ @:
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # SidekiqSolidFetch
2
+
3
+ A reliable fetch strategy for Sidekiq that ensures jobs are never lost, even if a worker crashes mid-execution. This is an open-source implementation similar to Sidekiq Pro's `super_fetch`.
4
+
5
+ ## How It Works
6
+
7
+ SidekiqSolidFetch uses Redis `LMOVE` command to atomically move jobs from the queue to a per-worker processing queue. This approach ensures:
8
+
9
+ - **No job loss**: Jobs are moved (not copied) to a processing queue before execution. If a worker crashes, the job remains in the processing queue.
10
+ - **Automatic recovery**: On startup, any unfinished jobs from previous runs are automatically requeued.
11
+ - **Graceful shutdown**: When Sidekiq shuts down, in-progress jobs are moved back to their original queues.
12
+
13
+ ### Flow
14
+
15
+ 1. Worker fetches a job → job is atomically moved from `queue:default` to `sidekiq_solid_fetch:processing:queue:default`
16
+ 2. Worker processes the job successfully → job is removed from the processing queue
17
+ 3. Worker crashes → job stays in the processing queue
18
+ 4. On next startup → jobs in processing queues are moved back to their original queues
19
+
20
+ ## Installation
21
+
22
+ Add to your Gemfile:
23
+
24
+ ```ruby
25
+ gem "sidekiq_solid_fetch"
26
+ ```
27
+
28
+ Then run:
29
+
30
+ ```bash
31
+ bundle install
32
+ ```
33
+
34
+ ## Usage
35
+
36
+ Enable SidekiqSolidFetch in your Sidekiq configuration:
37
+
38
+ ```ruby
39
+ # config/initializers/sidekiq.rb (Rails)
40
+ # or wherever you configure Sidekiq
41
+
42
+ require "sidekiq_solid_fetch"
43
+
44
+ Sidekiq.configure_server do |config|
45
+ SidekiqSolidFetch.enable!(config)
46
+ end
47
+ ```
48
+
49
+ That's it! SidekiqSolidFetch will now handle job fetching with crash recovery.
50
+
51
+ ## Requirements
52
+
53
+ - Ruby >= 3.1.0
54
+ - Sidekiq >= 7.0
55
+
56
+ ## Development
57
+
58
+ After checking out the repo, run `bin/setup` to install dependencies.
59
+
60
+ ```bash
61
+ # Run tests
62
+ docker compose up -d
63
+ make test
64
+
65
+ # Run linter
66
+ make lint-fix
67
+ ```
68
+
69
+ ## Contributing
70
+
71
+ Bug reports and pull requests are welcome on GitHub at https://github.com/k0va1/sidekiq_solid_fetch.
72
+
73
+ ## License
74
+
75
+ The gem is available as open source under the terms of the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ require "standard/rake"
7
+
8
+ task default: %i[spec standard]
@@ -0,0 +1,16 @@
1
+ services:
2
+ redis:
3
+ image: redis:8.4-alpine
4
+ network_mode: host
5
+ volumes:
6
+ - redis:/data
7
+ ports:
8
+ - 6379:6379
9
+ healthcheck:
10
+ test: redis-cli ping
11
+ interval: 1s
12
+ timeout: 3s
13
+ retries: 30
14
+
15
+ volumes:
16
+ redis:
@@ -0,0 +1,57 @@
1
+ require "sidekiq"
2
+ require "sidekiq/component"
3
+ require "sidekiq/capsule"
4
+
5
+ module SidekiqSolidFetch
6
+ class Fetcher
7
+ include Sidekiq::Component
8
+
9
+ def initialize(cap)
10
+ raise ArgumentError, "missing queue list" unless cap.queues
11
+ @config = cap
12
+ @strictly_ordered_queues = cap.mode == :strict
13
+ @queues = config.queues.map { |q| "queue:#{q}" }
14
+ @queues.uniq! if @strictly_ordered_queues
15
+ end
16
+
17
+ def retrieve_work
18
+ queues_cmd.each do |queue|
19
+ processing_queue_name = ::SidekiqSolidFetch.processing_queue_name(queue)
20
+ work = redis do |conn|
21
+ conn.lmove(queue, processing_queue_name, "RIGHT", "LEFT")
22
+ end
23
+
24
+ return ::SidekiqSolidFetch::UnitOfWork.new(queue, work, config, processing_queue_name) if work
25
+ end
26
+ nil
27
+ end
28
+
29
+ def bulk_requeue(*)
30
+ logger.info("SidekiqSolidFetch: Re-queueing terminated jobs")
31
+
32
+ count = 0
33
+ redis do |conn|
34
+ queues_cmd.each do |queue|
35
+ processing_queue_name = ::SidekiqSolidFetch.processing_queue_name(queue)
36
+ while conn.lmove(processing_queue_name, queue, "RIGHT", "LEFT")
37
+ count += 1
38
+ logger.info { "SidekiqSolidFetch: Moving job from #{processing_queue_name} back to #{queue}" }
39
+ end
40
+ end
41
+ end
42
+ logger.info("SidekiqSolidFetch: Re-queued #{count} jobs")
43
+ rescue => ex
44
+ logger.warn("SidekiqSolidFetch: Failed to requeue jobs: #{ex.message}")
45
+ end
46
+
47
+ def queues_cmd
48
+ if @strictly_ordered_queues
49
+ @queues
50
+ else
51
+ permute = @queues.shuffle
52
+ permute.uniq!
53
+ permute
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,29 @@
1
+ module SidekiqSolidFetch
2
+ class UnitOfWork
3
+ attr_accessor :queue, :job, :config, :processing_queue
4
+
5
+ def initialize(queue, job, config, processing_queue)
6
+ @queue = queue
7
+ @job = job
8
+ @config = config
9
+ @processing_queue = processing_queue
10
+ end
11
+
12
+ def acknowledge
13
+ config.redis { |conn| conn.lrem(processing_queue, -1, job) }
14
+ end
15
+
16
+ def queue_name
17
+ queue.delete_prefix("queue:")
18
+ end
19
+
20
+ def requeue
21
+ config.redis do |conn|
22
+ conn.multi do |multi|
23
+ multi.lrem(processing_queue, -1, job)
24
+ multi.lpush(queue, job)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module SidekiqSolidFetch
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,39 @@
1
+ require_relative "sidekiq_solid_fetch/version"
2
+ require_relative "sidekiq_solid_fetch/unit_of_work"
3
+ require_relative "sidekiq_solid_fetch/fetcher"
4
+
5
+ module SidekiqSolidFetch
6
+ class Error < StandardError; end
7
+
8
+ PROCESSING_QUEUE_PREFIX = "sidekiq_solid_fetch:processing"
9
+
10
+ def self.enable!(config)
11
+ config[:fetch_class] = SidekiqSolidFetch::Fetcher
12
+
13
+ config.on(:startup) do
14
+ Sidekiq.logger.info("SidekiqSolidFetch enabled")
15
+ requeue_not_finished_jobs(config)
16
+ end
17
+ end
18
+
19
+ def self.requeue_not_finished_jobs(config)
20
+ Sidekiq.logger.info("SidekiqSolidFetch: Re-queueing not finished jobs from previous runs")
21
+
22
+ count = 0
23
+ Sidekiq.redis do |conn|
24
+ config.queues.map { |q| "queue:#{q}" }.uniq.each do |queue|
25
+ processing_queue_name = ::SidekiqSolidFetch.processing_queue_name(queue)
26
+ while conn.lmove(processing_queue_name, queue, "RIGHT", "LEFT")
27
+ count += 1
28
+ Sidekiq.logger.info { "SidekiqSolidFetch: Moved job from #{processing_queue_name} back to #{queue}" }
29
+ end
30
+ end
31
+ end
32
+
33
+ Sidekiq.logger.info("SidekiqSolidFetch: Re-queued #{count} jobs from previous runs")
34
+ end
35
+
36
+ def self.processing_queue_name(queue)
37
+ "#{PROCESSING_QUEUE_PREFIX}:#{queue}"
38
+ end
39
+ end
@@ -0,0 +1,16 @@
1
+ {
2
+ "packages": {
3
+ ".": {
4
+ "package-name": "sidekiq_solid_fetch",
5
+ "include-component-in-tag": false,
6
+ "changelog-path": "CHANGELOG.md",
7
+ "release-type": "ruby",
8
+ "bump-minor-pre-major": true,
9
+ "bump-patch-for-minor-pre-major": true,
10
+ "draft": false,
11
+ "prerelease": false,
12
+ "version-file": "lib/sidekiq_solid_fetch/version.rb"
13
+ }
14
+ },
15
+ "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
16
+ }
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sidekiq_solid_fetch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex Koval
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-12-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sidekiq
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ description: OSS implementation of Sidekiq Pro's `super_fetch`
28
+ email:
29
+ - al3xander.koval@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".release-please-manifest.json"
35
+ - ".rspec"
36
+ - ".standard.yml"
37
+ - CHANGELOG.md
38
+ - CLAUDE.md
39
+ - Makefile
40
+ - README.md
41
+ - Rakefile
42
+ - docker-compose.yml
43
+ - lib/sidekiq_solid_fetch.rb
44
+ - lib/sidekiq_solid_fetch/fetcher.rb
45
+ - lib/sidekiq_solid_fetch/unit_of_work.rb
46
+ - lib/sidekiq_solid_fetch/version.rb
47
+ - release-please-config.json
48
+ homepage: https://github.com/k0va1/sidekiq_solid_fetch
49
+ licenses: []
50
+ metadata:
51
+ homepage_uri: https://github.com/k0va1/sidekiq_solid_fetch
52
+ source_code_uri: https://github.com/k0va1/sidekiq_solid_fetch
53
+ changelog_uri: https://github.com/k0va1/sidekiq_solid_fetch/blob/master/CHANGELOG.md
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 3.1.0
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubygems_version: 3.5.22
70
+ signing_key:
71
+ specification_version: 4
72
+ summary: OSS implementation of Sidekiq Pro's `super_fetch`
73
+ test_files: []