-
Notifications
You must be signed in to change notification settings - Fork 482
Description
When upgrading to resque-scheduler 4.9.0 and above with redis-rb 4.x, we still get the deprecation notices related to resque/resque#1794 and #745.
Repro:
require "bundler/inline"
gemfile do
source "https://rubygems.org"
gem "resque", "~> 2.6"
gem "resque-scheduler", "~> 4.9"
gem "redis", "~> 4.0"
end
Resque.redis = Redis.new # NOTE: need a fresh redis-server running locally
class Job
@queue = "jobs"
def self.perform
puts "performed job"
end
end
Resque.enqueue_in(1, Job)
sleep(1)
Resque::Scheduler.handle_delayed_items
puts "enqueued delayed items"
Resque::Worker.new("jobs").work(0)
$ ruby repro.rb
resque-scheduler: [INFO] 2023-12-27T11:11:35-08:00: Processing Delayed Items
Redis#srem will always return an Integer in Redis 5.0.0. Use Redis#srem? instead.(called from: /Users/alex.vondrak/.local/share/rtx/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/redis-namespace-1.11.0/lib/redis/namespace.rb:564:in `wrapped_send')
Pipelining commands on a Redis instance is deprecated and will be removed in Redis 5.0.0.
redis.multi do
redis.get("key")
end
should be replaced by
redis.multi do |pipeline|
pipeline.get("key")
end
(called from /Users/alex.vondrak/.local/share/rtx/installs/ruby/3.2.2/lib/ruby/gems/3.2.0/gems/redis-namespace-1.11.0/lib/redis/namespace.rb:564:in `wrapped_send'}
enqueued delayed items
performed job
After enough poking around through stack traces, I believe I've discovered the issue, and it's a subtle interaction between the resque & resque-scheduler gems:
- resque-scheduler calls
Resque.redis.multi
here:resque-scheduler/lib/resque/scheduler.rb
Lines 253 to 259 in 462e33e
Resque.redis.multi do |pipeline| encoded_jobs_to_requeue.each do |encoded_job| pipeline.srem("timestamps:#{encoded_job}", timestamp_bucket_key) decoded_job = Resque.decode(encoded_job) enqueue(decoded_job) end - through a chain of calls, this hits
Resque::Job.create
:resque-scheduler/lib/resque/scheduler.rb
Line 327 in 462e33e
Resque::Job.create(queue, job_klass, *params) - resque defines
Job.create
in such a way that we effectively make a call toResque.redis.pipelined
: https://github.com/resque/resque/blob/2f9d080ce86eb2e3f1f3d47599a21c576124c6f3/lib/resque/data_store.rb#L105-L108
Copying the shape of the above into a direct repro, we're basically doing
# Causes deprecation warning
Resque.redis.multi do |m|
Resque.redis.pipelined do |p|
# ...
end
end
instead of what the redis gem wants, which is
# Does not cause deprecation warning
Resque.redis.multi do |m|
m.pipelined do |p|
# ...
end
end
It might be hard to actually achieve the latter, though, since the code is straddling two gems. 😕
We were not getting this warning prior to resque-scheduler 4.9 since the outer multi
was added by #767 (which also causes other nested transaction issues, like in #773).
It seems like this still works circa redis 5.x. At least when switching the version in the above repro, I get:
$ ruby repro.rb
resque-scheduler: [INFO] 2023-12-27T11:14:06-08:00: Processing Delayed Items
enqueued delayed items
performed job
I think the deprecation may just kind of be firing in a wonky way due to how redis 4.x tries to juggle state.
Circa 4.x:
Redis#multi
replaces@client
with aDeprecatedMulti
: https://github.com/redis/redis-rb/blob/0afcf1cad6f4fc74213f2b4c7a20819581c36897/lib/redis.rb#L225Redis#pipelined
replaces@client
with aDeprecatedPipeline
: https://github.com/redis/redis-rb/blob/0afcf1cad6f4fc74213f2b4c7a20819581c36897/lib/redis.rb#L174- Both
DeprecatedMulti
andDeprecatedPipeline
are defined such that any call to the underlyingPipeline::Multi
orPipeline
methods will issue deprecation warnings: https://github.com/redis/redis-rb/blob/0afcf1cad6f4fc74213f2b4c7a20819581c36897/lib/redis/pipeline.rb#L208-L236 - thus, because the nested
pipelined
call happens onResque.redis
, it winds up delegating to the@client
, which issues the deprecation warning
Circa 5.x:
Redis#multi
will just yield a newMultiConnection
instance: https://github.com/redis/redis-rb/blob/667b0007191cb5c9ed6f3b1ea7fc97a40f8f8c93/lib/redis/commands/transactions.rb#L23-L29Redis#pipelined
will just yield a newPipelinedConnection
instance: https://github.com/redis/redis-rb/blob/935f64f18d7c37386e9b01db71683f8f3b868cd2/lib/redis.rb#L102-L108- those objects handle nested calls to
multi
/pipelined
in their respective ways: https://github.com/redis/redis-rb/blob/667b0007191cb5c9ed6f3b1ea7fc97a40f8f8c93/lib/redis/pipeline.rb#L16-L31 + https://github.com/redis/redis-rb/blob/667b0007191cb5c9ed6f3b1ea7fc97a40f8f8c93/lib/redis/pipeline.rb#L59-L61
However, it's unclear to me what the interaction will be between two "top level" calls to these transaction commands. I.e., will the futures fire with the desired timing, or do they go out of sync in some way that may cause an issue? EDIT: per investigation below, it seems that the "nested" pipeline actually silently fires outside of the new MULTI
/EXEC
transaction around the batch; see #787 (comment) for details.