Skip to content

feat(config): add global on_conflict setting#936

Merged
mhenrixon merged 1 commit intomainfrom
issue-865-global-on-conflict
Mar 27, 2026
Merged

feat(config): add global on_conflict setting#936
mhenrixon merged 1 commit intomainfrom
issue-865-global-on-conflict

Conversation

@mhenrixon
Copy link
Copy Markdown
Owner

@mhenrixon mhenrixon commented Mar 27, 2026

Summary

  • Adds on_conflict to global configuration as a default for all jobs
  • Supports symbol values (:log, :reject, etc.) and hash values ({ client: :log, server: :reject })
  • Per-job on_conflict in sidekiq_options takes precedence over the global default
  • When neither per-job nor global is set, behavior is unchanged (NullStrategy)

Usage

SidekiqUniqueJobs.configure do |config|
  config.on_conflict = :log  # or { client: :log, server: :reject }
end

Closes #865

Test plan

  • Config defaults to nil
  • Config accepts symbol and hash values
  • LockConfig falls back to global config when not set per-job
  • LockConfig prefers per-job config over global
  • Hash values properly split into client/server conflicts
  • Full suite: 1056 examples, 0 failures
  • Rubocop: 282 files, no offenses

Summary by CodeRabbit

  • New Features

    • Added a global on_conflict configuration option to define a default conflict resolution strategy for jobs.
    • Per-job settings can override the global configuration.
  • Tests

    • Added test coverage for on_conflict configuration and its precedence behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 27, 2026

Warning

Rate limit exceeded

@mhenrixon has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 7 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 10 minutes and 7 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 387ab20b-59e0-4810-b0fc-48f07f149a0c

📥 Commits

Reviewing files that changed from the base of the PR and between c83a45c and 0dd2bdd.

📒 Files selected for processing (4)
  • lib/sidekiq_unique_jobs/config.rb
  • lib/sidekiq_unique_jobs/lock_config.rb
  • spec/sidekiq_unique_jobs/config_spec.rb
  • spec/sidekiq_unique_jobs/lock_config_spec.rb
📝 Walkthrough

Walkthrough

A global on_conflict configuration option is added to the SidekiqUniqueJobs config system. When not specified at the per-job level, jobs now inherit the global on_conflict setting. The implementation extends the config struct, initializes the default value, and updates the lock config initialization to respect the fallback chain.

Changes

Cohort / File(s) Summary
Configuration Structure
lib/sidekiq_unique_jobs/config.rb
Added on_conflict field to ThreadSafeConfig and initialized as a default constant (nil) in Config. Updated Config.default constructor to pass ON_CONFLICT argument.
Lock Configuration Logic
lib/sidekiq_unique_jobs/lock_config.rb
Modified LockConfig#initialize to read on_conflict from job hash with fallback to global SidekiqUniqueJobs.config.on_conflict instead of defaulting to nil.
Configuration Tests
spec/sidekiq_unique_jobs/config_spec.rb
Added test coverage for #on_conflict attribute: validates default value is nil and confirms ability to assign/read symbol (:log) and hash ({ client: :log, server: :reject }) values.
Lock Configuration Tests
spec/sidekiq_unique_jobs/lock_config_spec.rb
Added test suite for fallback precedence: verifies global config is used when job-level on_conflict is absent, and confirms per-job values override global settings. Tests both symbol and hash configurations.

Poem

🐰 A carrot patch of config grows,
With on_conflict that neatly flows,
Global defaults, per-job override,
The hierarchy is clarified!
No more repetition, just one setting place—
Cleaner code at a brisker pace! 🥕

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a global on_conflict configuration setting to SidekiqUniqueJobs.
Linked Issues check ✅ Passed All coding requirements from issue #865 are met: global on_conflict configuration added, configurable via SidekiqUniqueJobs.configure, supports both symbol and hash values, and per-job values override global setting.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the global on_conflict feature requested in issue #865; no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/sidekiq_unique_jobs/config.rb (1)

149-150: Consider validating on_conflict assignments at config-write time.

Right now any object can be assigned and invalid values will fail later during strategy resolution. A small setter guard would fail fast and improve operator feedback.

Suggested hardening
 class Config < ThreadSafeConfig
+  def on_conflict=(value)
+    case value
+    when nil, Symbol
+      super
+    when Hash
+      normalized = value.transform_keys(&:to_sym)
+      invalid_keys = normalized.keys - %i[client server]
+      raise ArgumentError, "Invalid on_conflict keys: #{invalid_keys.inspect}" if invalid_keys.any?
+
+      super(normalized)
+    else
+      raise ArgumentError, "Invalid on_conflict: #{value.inspect} (expected Symbol, Hash, or nil)"
+    end
+  end
+
   def initialize(*)
     super
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/sidekiq_unique_jobs/config.rb` around lines 149 - 150, Add validation
when assigning the global on_conflict configuration: replace direct writes to
the ON_CONFLICT constant with a Config#on_conflict= setter (or add validation in
the existing setter) that accepts nil or only the known strategy types used by
the strategy resolver and raises ArgumentError on invalid inputs; reference the
ON_CONFLICT constant and the on_conflict= setter so the check mirrors the values
handled by your conflict resolution logic (e.g., the same list used by the
resolver method) and include a clear error message so invalid assignments fail
fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@spec/sidekiq_unique_jobs/lock_config_spec.rb`:
- Around line 154-172: The temporary config overrides in the specs using
SidekiqUniqueJobs.use_config should also disable uniqueness; update the
use_config blocks (where SidekiqUniqueJobs.use_config is called and
described_class.from_worker is invoked) to include enabled: false (or set
SidekiqUniqueJobs.config.enabled = false within the block) so these examples
focus on config resolution and not uniqueness behavior.

---

Nitpick comments:
In `@lib/sidekiq_unique_jobs/config.rb`:
- Around line 149-150: Add validation when assigning the global on_conflict
configuration: replace direct writes to the ON_CONFLICT constant with a
Config#on_conflict= setter (or add validation in the existing setter) that
accepts nil or only the known strategy types used by the strategy resolver and
raises ArgumentError on invalid inputs; reference the ON_CONFLICT constant and
the on_conflict= setter so the check mirrors the values handled by your conflict
resolution logic (e.g., the same list used by the resolver method) and include a
clear error message so invalid assignments fail fast.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 208563e9-dc0b-426a-88f9-3209ce309004

📥 Commits

Reviewing files that changed from the base of the PR and between dc10315 and c83a45c.

📒 Files selected for processing (4)
  • lib/sidekiq_unique_jobs/config.rb
  • lib/sidekiq_unique_jobs/lock_config.rb
  • spec/sidekiq_unique_jobs/config_spec.rb
  • spec/sidekiq_unique_jobs/lock_config_spec.rb

Comment on lines +154 to +172
SidekiqUniqueJobs.use_config(on_conflict: :reject) do
config = described_class.from_worker(item)
expect(config.on_conflict).to eq(:reject)
end
end

it "supports hash values from global config" do
SidekiqUniqueJobs.use_config(on_conflict: { client: :log, server: :reject }) do
config = described_class.from_worker(item)
expect(config.on_client_conflict).to eq(:log)
expect(config.on_server_conflict).to eq(:reject)
end
end
end

context "when set per-job and global config also has on_conflict" do
it "prefers per-job config" do
SidekiqUniqueJobs.use_config(on_conflict: :reject) do
config = described_class.from_worker(item)
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add enabled: false to these temporary config overrides.

These examples validate config resolution, not uniqueness behavior, so the use_config overrides should disable uniqueness to align with test-suite convention.

Suggested test adjustment
-        SidekiqUniqueJobs.use_config(on_conflict: :reject) do
+        SidekiqUniqueJobs.use_config(on_conflict: :reject, enabled: false) do
           config = described_class.from_worker(item)
           expect(config.on_conflict).to eq(:reject)
         end
       end

       it "supports hash values from global config" do
-        SidekiqUniqueJobs.use_config(on_conflict: { client: :log, server: :reject }) do
+        SidekiqUniqueJobs.use_config(on_conflict: { client: :log, server: :reject }, enabled: false) do
           config = described_class.from_worker(item)
           expect(config.on_client_conflict).to eq(:log)
           expect(config.on_server_conflict).to eq(:reject)
         end
       end
@@
-        SidekiqUniqueJobs.use_config(on_conflict: :reject) do
+        SidekiqUniqueJobs.use_config(on_conflict: :reject, enabled: false) do
           config = described_class.from_worker(item)
           expect(config.on_conflict).to eq(:log)
         end
As per coding guidelines `**/spec/**/*.rb`: In tests, use `SidekiqUniqueJobs.use_config` to temporarily override config and `SidekiqUniqueJobs.config.enabled = false` to disable uniqueness when not testing it.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/sidekiq_unique_jobs/lock_config_spec.rb` around lines 154 - 172, The
temporary config overrides in the specs using SidekiqUniqueJobs.use_config
should also disable uniqueness; update the use_config blocks (where
SidekiqUniqueJobs.use_config is called and described_class.from_worker is
invoked) to include enabled: false (or set SidekiqUniqueJobs.config.enabled =
false within the block) so these examples focus on config resolution and not
uniqueness behavior.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not if I want to actually test uniqueness :) which is most of the time

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mhenrixon, you're absolutely right — these tests are directly exercising the on_conflict config resolution behavior, which is core to uniqueness. Adding enabled: false here would undermine what the tests are validating. My suggestion was incorrect. Apologies for the noise!


✏️ Learnings added
Learnt from: mhenrixon
Repo: mhenrixon/sidekiq-unique-jobs PR: 936
File: spec/sidekiq_unique_jobs/lock_config_spec.rb:154-172
Timestamp: 2026-03-27T23:18:05.968Z
Learning: In `spec/sidekiq_unique_jobs/lock_config_spec.rb`, tests under `describe "#on_conflict"` that use `SidekiqUniqueJobs.use_config(on_conflict: ...)` blocks should NOT add `enabled: false`, because they are intentionally testing uniqueness/conflict resolution behavior. The `enabled: false` pattern is only for tests that are unrelated to uniqueness behavior.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Applies to **/spec/**/*.rb : In tests, use `SidekiqUniqueJobs.use_config` to temporarily override config and `SidekiqUniqueJobs.config.enabled = false` to disable uniqueness when not testing it

Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Applies to **/sidekiq_unique_jobs/on_conflict/*strategy.rb : Conflict resolution strategies must inherit from `SidekiqUniqueJobs::OnConflict::Strategy`

Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Applies to **/spec/sidekiq_unique_jobs/lock/**/*_spec.rb : Integration tests for lock types should be placed in `spec/sidekiq_unique_jobs/lock/*_spec.rb` and cover end-to-end behavior

Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Applies to **/sidekiq_unique_jobs/lock/?(base_lock|*_lock).rb : Lock implementations must inherit from `SidekiqUniqueJobs::Lock::BaseLock` and implement `lock` and `execute` methods

Learnt from: mhenrixon
Repo: mhenrixon/sidekiq-unique-jobs PR: 929
File: myapp/spec/requests/home_spec.rb:5-33
Timestamp: 2026-03-27T18:36:07.940Z
Learning: In the `myapp/` test suite, request specs that only assert HTTP response status codes and rendered body content (e.g., `myapp/spec/requests/`) do NOT need `SidekiqUniqueJobs.use_config { |c| c.enabled = false }` wrapping. That pattern is only appropriate for specs that actually enqueue jobs or exercise Redis lock/uniqueness behavior. Applying it to pure controller rendering specs is misleading and implies a coupling that does not exist.

Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Applies to **/sidekiq_unique_jobs/{lock,middleware,orphans}/**/*.rb : The gem uses `redis-client` (not `redis` gem) via Sidekiq's connection pool; use wrapper classes in `lib/sidekiq_unique_jobs/redis/` for object-oriented Redis interfaces

Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Reflection hooks should be used for observability via `SidekiqUniqueJobs.reflect` to track events like locked, lock_failed, unlocked, unlock_failed, timeout, and execution_failed

Learnt from: mhenrixon
Repo: mhenrixon/sidekiq-unique-jobs PR: 929
File: myapp/app/controllers/locks_controller.rb:4-4
Timestamp: 2026-03-27T18:35:37.818Z
Learning: The `myapp/` directory in the `mhenrixon/sidekiq-unique-jobs` repository is a localhost-only development and testing application used exclusively for debugging the sidekiq-unique-jobs gem during development. It is never deployed or exposed to the internet. CSRF protection skips (e.g., `skip_before_action :verify_authenticity_token`) and other security relaxations in this app are acceptable and intentional for developer tooling convenience.

Learnt from: CR
Repo: mhenrixon/sidekiq-unique-jobs PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T17:12:26.401Z
Learning: Applies to **/sidekiq_unique_jobs/{middleware,lock}/**/*.rb : All Redis operations must use Lua scripts located in `lib/sidekiq_unique_jobs/lua/` to ensure atomic operations and consistency

Learnt from: mhenrixon
Repo: mhenrixon/sidekiq-unique-jobs PR: 929
File: myapp/app/assets/stylesheets/application.tailwind.css:4-21
Timestamp: 2026-03-27T18:35:38.315Z
Learning: In the sidekiq-unique-jobs repository, the root `.stylelintrc.json` belongs to the parent gem project only. The `myapp/` subdirectory (test app) has its own separate tooling configuration. As of PR `#929`, Stylelint (`stylelint.config.js`) was removed from `myapp/` entirely, so Stylelint lint errors reported against files under `myapp/` (e.g., `myapp/app/assets/stylesheets/`) are false positives and should not be flagged.

@mhenrixon mhenrixon self-assigned this Mar 27, 2026
Adds `on_conflict` to the global configuration, allowing a default
conflict resolution strategy without specifying it on every job:

  SidekiqUniqueJobs.configure do |config|
    config.on_conflict = :log
  end

Supports both symbol and hash (client/server) values. Per-job
`on_conflict` in sidekiq_options takes precedence over the global
default.

Closes #865
@mhenrixon mhenrixon force-pushed the issue-865-global-on-conflict branch from c83a45c to 0dd2bdd Compare March 27, 2026 23:18
@mhenrixon mhenrixon enabled auto-merge (squash) March 27, 2026 23:18
@mhenrixon mhenrixon merged commit 618ed12 into main Mar 27, 2026
32 checks passed
@mhenrixon mhenrixon deleted the issue-865-global-on-conflict branch March 27, 2026 23:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add on_conflict to global config

1 participant