OK for posterity, those that follow and my ageing memory (in case I need to remember how I did it)…
The problem was that the Wizard plugin was timing out because I was trying to create up to 77 topics synchronously. I needed to enqueue a series of jobs that would create topics asynchronously so that I could return control to the Wizard plugin to stop it timing out.
In looking at the various Job
class definitions that I found in the source, I couldn’t figure out how to instantiate the class and put it on a sidekiq queue.
After some digging, this appears to come from Jobs.enqueue(:job_name, args)
. (I believe there are variations of this that schedule jobs and enqueue repeating jobs.
Anyway, the steps I took to defer the creation of topics…
Create the job that will create the topics
It appears to be a convention with plugins to create a plugin-name\jobs
directory and inside this folder, I created a Ruby script called deferred_topic_creation.rb
. It looks like this…
# school-points\jobs\deferred_topic_creation.rb
module Jobs
class DeferredTopicCreation < Jobs::Base
def execute(args)
creator_user_id = args[:creator_user_id]
custom_fields = {
event_start: args[:event_start],
event_end: args[:event_end]
}
creator_user = User.find(creator_user_id) || User.find(Discourse::SYSTEM_USER_ID)
post_creator = PostCreator.new(
creator_user,
args.except!(:creator_user_id, :event_start, :event_end)
)
post = post_creator.create
topic = Topic.find(post.topic_id)
topic.custom_fields = custom_fields
topic.save!
end
end
end
The code you want to execute when sidekiq finally processes the jobs seems to go inside the execute
method, and, at least with an ordinary job (not scheduled or repeated) it is simply passed the args
as a hash as the first parameter.
Gotcha! In keeping with other queueing systems I’ve used (in Python), you can’t pass Discourse / Rails objects through to the job in that args
hash. If you do, the object is just serialised as a string. I don’t know how to instantiate the object from the string so my solution was to pass IDs of any discourse objects I needed (the user that would create the topics in my case) and then to .find()
the object in the job execute
method.
load
the job code in your plugin - inside the after_initialize do
block like so
after_initialize do
.
.
.
load File.expand_path('../jobs/deferred_topic_creation.rb', __FILE__)
.
.
.
I don’t know if where it is placed in the block is important but the path must resolve to our job definition in the jobs
directory.
Enqueue the jobs to create the topics
With that setup in place, you are then able to enqueue the job using a command like this
opts = {
title: title,
raw: content,
category: child_category[:id],
creator_user_id: creator_user_id,
event_start: event_start.to_time.to_i,
event_end: event_end.to_time.to_i
}
Jobs.enqueue(:deferred_topic_creation, opts)
Note that the name of the job is passed as a symbol here.
In development mode remember to stop your server and kill sidekiq before deleting the tmp
directory contents and starting the server and sidekiq again.