Support bulk enqueuing of jobs which differ only in args/kwargs#340
Support bulk enqueuing of jobs which differ only in args/kwargs#340ZimbiX merged 25 commits intoque-rb:masterfrom
Conversation
|
How about making the method take an option for whether to disable the notify while enqueueing the jobs? |
Interesting idea. How would we do that without also impacting jobs enqueued using the normal |
I was thinking something like this - disable and re-enable the trigger in the call to |
|
Note that disabling/enabling triggers is a schema change, meaning it will require a I’ve found adding a trigger condition which checks a custom setting works well to temporarily stop row triggers on bulk operations. Something like this: CREATE TRIGGER que_job_notify
AFTER INSERT ON public.que_jobs FOR EACH ROW
WHEN (NOT coalesce(current_setting('que.skip_notify', true)::boolean, false))
EXECUTE FUNCTION public.que_job_notify();Then use |
|
@jasoncodes Good to know! Thanks for the tip <3 Edit: The docs for disabling a trigger confirm this |
|
Rebased (via cherry-picking just the latest commit) on master now that the Ruby 3 PR has been merged |
|
@jasoncodes: @AndrewColbeck and I have had to adjust the query you provided, as we found it unfortunately errors after a transaction which sets the variable. It seems that Here's a working alternative that avoids the need to cast: |
|
Good catch. I can confirm this empty string behaviour on at least PG 14.x. Here’s an alternate solution to consider: |
Thanks for that idea. We tried it out, but it doesn't work - I think because casting null to boolean doesn't: Sorry, I thought casting null to boolean was working before 😅 We've found these two options to be working: WHEN (nullif(current_setting('que.skip_notify', true), '') is null)
WHEN (NOT coalesce(current_setting('que.skip_notify', true), '') = 'true')We think we prefer the second form, given it's checking the value is set to something truthy rather than just set to something. |
…mance Implementation is based on [a tip from @jasoncodes](que-rb#340 (comment))! Co-Authored-By: Andrew Colbeck <andrewcolbeck@gmail.com>
…ormance Implementation is based on [a tip from @jasoncodes](que-rb#340 (comment))! Co-Authored-By: Andrew Colbeck <andrewcolbeck@gmail.com>
|
I've just realised there's a second trigger, I think I'd still like to add an option in bulk enqueueing for whether to disable the notify (disabling it by default, I reckon). The MyJob.bulk_enqueue(
[
{ args: ['job1_arg1'], kwargs: { job1_kwarg1: 'x' } },
{ args: ['job2_arg1'], kwargs: { job2_kwarg1: 'x' } },
],
)It could be good to improve that here or subsequently. I was wondering about something like: Que::Job.with_bulk_enqueue do
MyJob.enqueue('job1_arg1', job1_kwarg1: 'x')
MyJob.enqueue('job2_arg1', job2_kwarg1: 'x')
end |
Ah, true. The trigger condition must return true and I missed the
Agreed entirely. Comparing to WHEN (nullif(current_setting('que.skip_notify', true), '')::boolean IS DISTINCT FROM true)I read this one as “run the trigger when when skip_notify is not true”. |
I think disabled by default makes sense.
Agreed. Not being able to use the normal Job
Yes, please! This would be is a much nicer interface and would allow users to adopt bulk enqueuing without a lot of changes to application code. One could divert the The main API difference I can think of is |
|
I've reworked the interface to use basically what I proposed, but still called To keep track of everything that's left, I've just added a todo list to the first comment. |
Yeah, that's what I'd been thinking too 😉
Ah, I hadn't thought of that! Cheers; done. I'm not really familiar with the ActiveJob part of Que - do you reckon we should add a test for that here? |
Yeah, but the settings are strings, so we can't really avoid string stuff afaik, having to use either string parsing or string comparison, of which I presume the performance is similar.
Interesting alternative. I think what we went with is slightly more readable, personally, so I'll stick with that. Thanks though |
To avoid a ShareRowExclusiveLock, which would prevent row modifications while the transaction is open (que-rb#340 (comment))
…ormance Implementation is based on [a tip from @jasoncodes](que-rb#340 (comment))! Co-Authored-By: Andrew Colbeck <andrewcolbeck@gmail.com>
To avoid a ShareRowExclusiveLock, which would prevent row modifications while the transaction is open (que-rb#340 (comment))
|
Rebased to avoid changelog conflict |
Co-Authored-By: Andrew Colbeck <andrewcolbeck@gmail.com>
…s used It was consistently failing when run with: ```bash DATABASE_URL=postgres://que:que@localhost:5697/que-test TESTOPTS="--seed=50554 -v" USE_RAILS=true be rake spec ``` Co-Authored-By: Andrew Colbeck <andrewcolbeck@gmail.com>
…ormance Implementation is based on [a tip from @jasoncodes](que-rb#340 (comment))! Co-Authored-By: Andrew Colbeck <andrewcolbeck@gmail.com>
… 'PROCEDURE' for compatibility with older PostgreSQL
Allows fast-failing a test run using: ```bash TESTOPTS=--fail-fast bundle exec rake spec ```
…calls to .enqueue
It disables notify by default for performance
To avoid a ShareRowExclusiveLock, which would prevent row modifications while the transaction is open (que-rb#340 (comment))
This does not seem to be used
…queue rather than .bulk_enqueue I added a matcher, `assert_raises_with_message` to make the expectation cleaner. I reckon this should have been part of minitest, like it is in RSpec
… args, and do so only at the end of the .bulk_enqueue block
…ecessary serialisation, making it ~19% faster
Co-Authored-By: Jerome Paul <jerome.paul@greensync.com.au>
Co-Authored-By: Jerome Paul <jerome.paul@greensync.com.au>
Co-authored-by: Brendan Weibrecht <brendan@weibrecht.net.au>
[ActiveJob always adds job options to the Que.enqueue call](https://github.com/rails/rails/blob/main/activejob/lib/active_job/queue_adapters/que_adapter.rb#L25) but that is not supported. Furthermore ActiveJob does not have a dedicated bulk enqueue interface. Co-authored-by: Brendan Weibrecht <brendan@weibrecht.net.au>
Using Que to manually enqueue an ActiveJob job is not just as simple being able to specify your job class in the `job_class` job option in `Que.enqueue` - there's a lot more to it, e.g.: ``` [2] pry(#<running jobs via ActiveJob>)> TestJobClass.perform_later(1, 2) => #<TestJobClass:0x00007fb081f4bcb0 @arguments=[1, 2], @exception_executions={}, @Executions=0, @job_id="227ffa28-57b2-4517-99ef-a53a4136c01a", @priority=nil, @provider_job_id=136557356, @queue_name="default", @Timezone=nil> [3] pry(#<running jobs via ActiveJob>)> jobs_dataset.all => [{:priority=>100, :run_at=>2022-08-25 06:25:15.406266 +0000, :id=>136557356, :job_class=>"ActiveJob::QueueAdapters::QueAdapter::JobWrapper", :error_count=>0, :last_error_message=>nil, :queue=>"default", :last_error_backtrace=>nil, :finished_at=>nil, :expired_at=>nil, :args=> [{:job_id=>"227ffa28-57b2-4517-99ef-a53a4136c01a", :locale=>"en", :priority=>nil, :timezone=>nil, :arguments=>[1, 2], :job_class=>"TestJobClass", :executions=>0, :queue_name=>"default", :enqueued_at=>"2022-08-25T06:25:15Z", :provider_job_id=>nil, :exception_executions=>{}}], :data=>{}, :job_schema_version=>2, :kwargs=>{}}] ``` Co-Authored-By: Jerome Paul <jerome.paul@greensync.com.au>
Co-Authored-By: Jerome Paul <jerome.paul@greensync.com.au>
|
🚀 |
Resolves: #333
Todo:
.bulk_enqueue.enqueueque_job_notifytrigger in.bulk_enqueuefor performanceque_state_notifytrigger in.bulk_enqueuefor performance /completely remove it(removal deferred)que_state_notifytrigger in the changelog.bulk_enqueue.bulk_enqueuein synchronous modeque.skip_notifyAdd a test for enqueueing a job with ActiveJob within a.bulk_enqueueblockConfirm that the alternate instructions in the ActiveJob error message actually work for ActiveJob jobs- it didn't: removedTo PR and release subsequently:
.bulk_enqueueblock.bulk_enqueueblock