You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In some systems, you'll have to do something very slow. Maybe you need to export a large dataset to a CSV file, or need to generate a thousand row Exel file that takes some minutes to complete?
Instead of try building these kinds of reports in a normal Rails controller action, this task should be moved into a background job for processing outside the life cycle of a single HTTP request.
But when you move work to a background job, code execution does not stop and you need a way to “connect” back to the user that caused the job to be created. It’s outside of a single request-response life cycle so you cannot rely on normal Rails controllers.
We often need a way to report back progress and send back information once the job is completed.
Example: Export large xlsx file
This sample uses Sidekiq for Backgroud Job and a Stimulus controller to handle client-side polling/updating.
First, we use this command to generate new job rails generate sidekiq:job ExportUser.
Now we move the export file logic to the export_job.rb file, and call the job inside the controller instead.
So our controller will now be like this, the csv generation will now be handle in the background instead of in our main app
classExportUserJobincludeSidekiq::JobincludeSidekiq::Status::Workerdefperform(*args)users=User.pluck:id,:name,:email,:address,:phonetotalusers.size# Create Axlsx package and workbookxlsx_package=Axlsx::Package.newxlsx_workbook=xlsx_package.workbookxlsx_workbook.add_worksheet(name: "Users")do |worksheet|
worksheet.add_row%w(IDNameEmailAddressPhone)users.each.with_index(1)do |user,idx|
worksheet.add_rowuser# Caculate percentage atidxendend# Save file into tmp with suffix is jobIdxlsx_package.serializeRails.root.join("tmp","users_export_#{self.jid}.xlsx")endend
However, there was a problem with using Sidekiq, the job is executed in the background so it doesn’t inform the main server when it is completed. So we executed the job, got the exported file, and now what? We cannot give the file back to the server to return it to our client.
Polling + Progress
It is a repeating AJAX call to check the job status and a block of text
The first approach is to combine polling (periodically checking on the job via AJAX calls) with progress reporting. This approach is good in case the user wants to export large reports and wants to monitor progress.
In UserController, We need 2 methods, one for tracking job's status and one for downloading file when job is completed:
classUsersController < ApplicationController
...
defexport_statusrespond_todo |format|
format.jsondojob_id=params[:job_id]# Check job status and percentage using JobIdjob_status=Sidekiq::Status.get_all(job_id).symbolize_keysrenderjson: {status: job_status[:status],percentage: job_status[:pct_complete]}endendenddefexport_downloadjob_id=params[:id]exported_file_name="users_export_#{job_id}.xlsx"filename="UserData_#{DateTime.now.strftime("%Y%m%d_%H%M%S")}.xlsx"respond_todo |format|
format.xlsxdosend_fileRails.root.join("tmp",exported_file_name),type: :xlsx,filename: filenameendendendend
This approach is extendable and flexible. You can implement the progress reporting in a variety of ways: a numeric progress bar, a single “status” message, or even a detailed log of progress.
Action Cable to broadcast
In order to return the csv file to the client, we will create a channel using Action Cable and broadcast the file back when it’s ready.
Use rails g channel VideoConversion to generate the new channel
Then we will add the code to broadcast our file in the job so it will return the csv when the job is performed.
classExportUserJobincludeSidekiq::Jobdefperform(*args)
....# Save file into tmp with suffix is jobIdxlsx_package.serializeRails.root.join("tmp","users_export_#{self.jid}.xlsx")ActionCable.server.broadcast"export_user",self.jidendend
Next, let’s add the Stimulus controller that will listen for events from this channel and update the UI as it receives them. The important parts are the channel we subscribe to in the export method and the received method
In some systems, you'll have to do something very slow. Maybe you need to export a large dataset to a CSV file, or need to generate a thousand row Exel file that takes some minutes to complete?
Instead of try building these kinds of reports in a normal Rails controller action, this task should be moved into a background job for processing outside the life cycle of a single HTTP request.
But when you move work to a background job, code execution does not stop and you need a way to “connect” back to the user that caused the job to be created. It’s outside of a single request-response life cycle so you cannot rely on normal Rails controllers.
We often need a way to report back progress and send back information once the job is completed.
Example: Export large xlsx file
This sample uses Sidekiq for Backgroud Job and a Stimulus controller to handle client-side polling/updating.
First, we use this command to generate new job
rails generate sidekiq:job ExportUser
.Now we move the export file logic to the export_job.rb file, and call the job inside the controller instead.
So our controller will now be like this, the csv generation will now be handle in the background instead of in our main app
However, there was a problem with using Sidekiq, the job is executed in the background so it doesn’t inform the main server when it is completed. So we executed the job, got the exported file, and now what? We cannot give the file back to the server to return it to our client.
Polling + Progress
The first approach is to combine polling (periodically checking on the job via AJAX calls) with progress reporting. This approach is good in case the user wants to export large reports and wants to monitor progress.
In
UserController
, We need 2 methods, one for tracking job's status and one for downloading file when job is completed:Our Stimulus controller will be looks like this:
This approach is extendable and flexible. You can implement the progress reporting in a variety of ways: a numeric progress bar, a single “status” message, or even a detailed log of progress.
Action Cable to broadcast
In order to return the csv file to the client, we will create a channel using Action Cable and broadcast the file back when it’s ready.
Use
rails g channel VideoConversion
to generate the new channelThen we will add the code to broadcast our file in the job so it will return the csv when the job is performed.
In controller, we just need 2 methods:
Next, let’s add the Stimulus controller that will listen for events from this channel and update the UI as it receives them. The important parts are the channel we subscribe to in the export method and the received method
You can find the full source code for this guide on this repo, or for action cable approach in branch export_action-cablel
References
The text was updated successfully, but these errors were encountered: