Skip to content

Improved exception handling#895

Merged
mhenrixon merged 24 commits intomainfrom
improved-exception-handling
Nov 26, 2025
Merged

Improved exception handling#895
mhenrixon merged 24 commits intomainfrom
improved-exception-handling

Conversation

@mhenrixon
Copy link
Copy Markdown
Owner

@mhenrixon mhenrixon commented Nov 26, 2025

=============================================================   =============================================================
SIDEKIQ UNIQUE JOBS - PERFORMANCE IMPROVEMENTS BENCHMARK        SIDEKIQ UNIQUE JOBS - PERFORMANCE IMPROVEMENTS BENCHMARK
=============================================================   =============================================================
Ruby Version: 3.4.7                                             Ruby Version: 3.4.7
Redis Version: 7.2.4                                            Redis Version: 7.2.4
Sidekiq Version: 8.0.9                                          Sidekiq Version: 8.0.9
SidekiqUniqueJobs Version: 8.0.12                               SidekiqUniqueJobs Version: 8.0.12
Git Branch: main                                              | Git Branch: improved-exception-handling
Git Commit: fb3be830                                          | Git Commit: 7b297e01
=============================================================   =============================================================

=============================================================   =============================================================
BENCHMARK 1: Non-Blocking Lock Acquisition (until_executing)    BENCHMARK 1: Non-Blocking Lock Acquisition (until_executing)
Improvement: Changed from wait:1 to wait:0 in exception handl   Improvement: Changed from wait:1 to wait:0 in exception handl
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
lock with failure simulation                                    lock with failure simulation
                       120.000 i/100ms                        |                        137.000 i/100ms
lock and unlock - normal flow                                   lock and unlock - normal flow
                       189.000 i/100ms                        |                        203.000 i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
lock with failure simulation                                    lock with failure simulation
                          1.129k (±12.7%) i/s  (885.44 μs/i |                             1.101k (±28.3%) i/s  (908.32 μs/i
lock and unlock - normal flow                                   lock and unlock - normal flow
                          1.578k (±16.3%) i/s  (633.55 μs/i |                             1.653k (±20.8%) i/s  (605.06 μs/i

=============================================================   =============================================================
BENCHMARK 2: Orphan Reaper Performance (Lua Script Optimizati   BENCHMARK 2: Orphan Reaper Performance (Lua Script Optimizati
Improvements: Cached digest transformations, short-circuit br   Improvements: Cached digest transformations, short-circuit br
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
 orphan reaper (lua)   334.000 i/100ms                        |  orphan reaper (lua)   385.000 i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
 orphan reaper (lua)      3.675k (±23.7%) i/s  (272.13 μs/i |    orphan reaper (lua)      3.909k (±19.3%) i/s  (255.81 μs/i

=============================================================   =============================================================
BENCHMARK 3: Lock TTL Calculation (Scheduled Jobs)              BENCHMARK 3: Lock TTL Calculation (Scheduled Jobs)
Improvement: Prevent negative TTL values with clamping          Improvement: Prevent negative TTL values with clamping
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
TTL calc - future job                                           TTL calc - future job
                        25.235k i/100ms                       |                         27.721k i/100ms
TTL calc - overdue job                                          TTL calc - overdue job
                        26.656k i/100ms                       |                         27.061k i/100ms
TTL calc - immediate job                                        TTL calc - immediate job
                        30.398k i/100ms                       |                         23.874k i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
TTL calc - future job                                           TTL calc - future job
                        277.946k (± 8.3%) i/s    (3.60 μs/i |                           264.418k (± 8.1%) i/s    (3.78 μs/i
TTL calc - overdue job                                          TTL calc - overdue job
                        289.865k (± 4.5%) i/s    (3.45 μs/i |                           262.069k (±16.0%) i/s    (3.82 μs/i
TTL calc - immediate job                                        TTL calc - immediate job
                        314.652k (± 5.5%) i/s    (3.18 μs/i |                           195.715k (±26.5%) i/s    (5.11 μs/i

Comparison:                                                     Comparison:
TTL calc - immediate job:   314652.0 i/s                      | TTL calc - future job:   264417.9 i/s
TTL calc - overdue job:   289864.7 i/s - same-ish: difference | TTL calc - overdue job:   262068.6 i/s - same-ish: difference
TTL calc - future job:   277946.1 i/s - same-ish: difference  | TTL calc - immediate job:   195715.4 i/s - same-ish: differen


=============================================================   =============================================================
BENCHMARK 4: Scripts Class Operations                           BENCHMARK 4: Scripts Class Operations
Improvement: Simplified using Concurrent::Map#fetch_or_store    Improvement: Simplified using Concurrent::Map#fetch_or_store
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
script fetch (cached)                                           script fetch (cached)
:
Improvement: Prevent negative TTL values with clamping          Improvement: Prevent negative TTL values with clamping
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
TTL calc - future job                                           TTL calc - future job
                        25.235k i/100ms                       |                         27.721k i/100ms
TTL calc - overdue job                                          TTL calc - overdue job
                        26.656k i/100ms                       |                         27.061k i/100ms
TTL calc - immediate job                                        TTL calc - immediate job
                        30.398k i/100ms                       |                         23.874k i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
TTL calc - future job                                           TTL calc - future job
                        277.946k (± 8.3%) i/s    (3.60 μs/i |                           264.418k (± 8.1%) i/s    (3.78 μs/i
TTL calc - overdue job                                          TTL calc - overdue job
                        289.865k (± 4.5%) i/s    (3.45 μs/i |                           262.069k (±16.0%) i/s    (3.82 μs/i
TTL calc - immediate job                                        TTL calc - immediate job
                        314.652k (± 5.5%) i/s    (3.18 μs/i |                           195.715k (±26.5%) i/s    (5.11 μs/i

Comparison:                                                     Comparison:
TTL calc - immediate job:   314652.0 i/s                      | TTL calc - future job:   264417.9 i/s
TTL calc - overdue job:   289864.7 i/s - same-ish: difference | TTL calc - overdue job:   262068.6 i/s - same-ish: difference
TTL calc - future job:   277946.1 i/s - same-ish: difference  | TTL calc - immediate job:   195715.4 i/s - same-ish: differen


=============================================================   =============================================================
BENCHMARK 4: Scripts Class Operations                           BENCHMARK 4: Scripts Class Operations
Improvement: Simplified using Concurrent::Map#fetch_or_store    Improvement: Simplified using Concurrent::Map#fetch_or_store
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
script fetch (cached)                                           script fetch (cached)
                        67.284k i/100ms                       |                         25.788k i/100ms
locksmith lock/unlock                                           locksmith lock/unlock
                        79.000 i/100ms                        |                          1.000 i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
script fetch (cached)                                           script fetch (cached)
                        610.068k (± 9.3%) i/s    (1.64 μs/i |                           628.917k (± 9.8%) i/s    (1.59 μs/i
locksmith lock/unlock                                           locksmith lock/unlock
                        726.410 (±19.0%) i/s    (1.38 ms/i)  |                            2.084k (±24.6%) i/s  (479.89 μs/i

=============================================================   =============================================================
BENCHMARK 5: Concurrent Lock Operations (Thread Safety)         BENCHMARK 5: Concurrent Lock Operations (Thread Safety)
Improvement: Thread-safe config access with mutex               Improvement: Thread-safe config access with mutex
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
100 locks - 10 threads                                          100 locks - 10 threads
                         1.000 i/100ms                                                   1.000 i/100ms
500 locks - 20 threads                                          500 locks - 20 threads
                         1.000 i/100ms                                                   1.000 i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
100 locks - 10 threads                                          100 locks - 10 threads
                          8.665 (±23.1%) i/s  (115.41 ms/i)  |                            9.583 (± 0.0%) i/s  (104.35 ms/i)
500 locks - 20 threads                                          500 locks - 20 threads
                          1.785 (± 0.0%) i/s  (560.12 ms/i)  |                            3.036 (±32.9%) i/s  (329.41 ms/i)

=============================================================   =============================================================
BENCHMARK 6: Callback Exception Handling                        BENCHMARK 6: Callback Exception Handling
Improvement: Don't re-raise callback exceptions after unlock    Improvement: Don't re-raise callback exceptions after unlock
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
  callback - success     5.158k i/100ms                       |   callback - success     6.661k i/100ms
callback - with exception                                       callback - with exception
                         4.361k i/100ms                       |                          6.192k i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
  callback - success     57.200k (± 9.3%) i/s   (17.48 μs/i |     callback - success     65.679k (± 3.7%) i/s   (15.23 μs/i
callback - with exception                                       callback - with exception
                         42.136k (± 9.2%) i/s   (23.73 μs/i |                            61.897k (± 5.5%) i/s   (16.16 μs/i

Comparison:                                                     Comparison:
  callback - success:    57200.3 i/s                          |   callback - success:    65679.1 i/s
callback - with exception:    42136.0 i/s - 1.36x  slower     | callback - with exception:    61897.1 i/s - same-ish: differe


=============================================================   =============================================================
BENCHMARK 7: Blocking Redis Operations                          BENCHMARK 7: Blocking Redis Operations
Improvement: Cap blocking wait at MAX_BLOCKING_WAIT (5 second   Improvement: Cap blocking wait at MAX_BLOCKING_WAIT (5 second
=============================================================   =============================================================
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm   ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +YJIT +PRISM [arm
Warming up --------------------------------------               Warming up --------------------------------------
   lock with wait: 0    24.000 i/100ms                        |    lock with wait: 0   199.000 i/100ms
   lock with wait: 1    51.000 i/100ms                        |    lock with wait: 1   163.000 i/100ms
Calculating -------------------------------------               Calculating -------------------------------------
   lock with wait: 0    506.836 (±18.2%) i/s    (1.97 ms/i)  |     lock with wait: 0      1.190k (±15.9%) i/s  (840.18 μs/i
   lock with wait: 1    488.120 (±13.7%) i/s    (2.05 ms/i)  |     lock with wait: 1    683.966 (± 7.9%) i/s    (1.46 ms/i)

=============================================================   =============================================================
MEMORY BENCHMARK: Lock Operations                               MEMORY BENCHMARK: Lock Operations
=============================================================   =============================================================
Calculating -------------------------------------               Calculating -------------------------------------
100 lock/unlock cycles                                          100 lock/unlock cycles
                         3.054M memsize (    40.000  retained                            3.054M memsize (    40.000  retained
                        31.001k objects (     1.000  retained                           31.001k objects (     1.000  retained
                        50.000  strings (     0.000  retained                           50.000  strings (     0.000  retained
100 lock with execute                                           100 lock with execute
                         2.456M memsize (    80.000  retained                            2.456M memsize (    80.000  retained
                        26.901k objects (     2.000  retained                           26.901k objects (     2.000  retained
                        50.000  strings (     0.000  retained                           50.000  strings (     0.000  retained

Comparison:                                                     Comparison:
100 lock with execute:    2455976 allocated                   | 100 lock with execute:    2455880 allocated
100 lock/unlock cycles:    3054408 allocated - 1.24x more     | 100 lock/unlock cycles:    3054280 allocated - 1.24x more

=============================================================   =============================================================
BENCHMARK COMPLETE!                                             BENCHMARK COMPLETE!
=============================================================   =============================================================

To compare branches:                                            To compare branches:
1. Run this script on main branch: git checkout main && bin/b   1. Run this script on main branch: git checkout main && bin/b
2. Run this script on your branch: git checkout <branch> && b   2. Run this script on your branch: git checkout <branch> && b
3. Compare the results!                                         3. Compare the results!
=============================================================   =============================================================

- Replaced manual get/create/store pattern with Concurrent::Map#fetch_or_store
- Simplified class-level fetch method for thread-safe lazy initialization
- Simplified instance fetch method using same pattern
- Improved delete method with ternary operator
- Improved kill method to extract redis connection logic
Changed wait time from 1 second to 0 (non-blocking) when re-acquiring
lock after job failure. This prevents worker threads from being blocked
unnecessarily while still maintaining the lock to prevent duplicate jobs
during retry.

Consistent with fix in until_and_while_executing.
@mhenrixon mhenrixon force-pushed the improved-exception-handling branch from 029b22d to dddd982 Compare November 26, 2025 17:12
Added comprehensive benchmarking infrastructure:
- bin/benchmark_improvements: Targeted benchmarks for specific improvements
- bin/compare_performance: Automated branch comparison tool
- BENCHMARKING.md: Complete guide for running and interpreting benchmarks

Key features:
- Tests all 7 major performance improvements
- Automatic branch comparison with metrics summary
- Memory allocation tracking
- IPS (iterations per second) measurements
- Results saved to tmp/benchmark_results/

Usage:
  bin/compare_performance main improved-exception-handling
Instead of manually calling scripts with argv, use locksmith
which handles all the proper formatting. This is more reliable
and matches how the gem is actually used.
Use Gem.clear_paths to reload gem paths after installation
so newly installed gems are immediately available.
Move benchmark gem requires before bundler/setup so they can be
loaded from system gems (not Gemfile). This allows users to install
the benchmark gems separately without adding them to the project.
Wrap callback exception test in rescue block so it works on both
main (re-raises) and improved (doesn't re-raise) branches.
@mhenrixon mhenrixon merged commit b2fb54a into main Nov 26, 2025
31 checks passed
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.

1 participant