如何在Rails中正确使用Sidekiq处理后台任务及Shopify应用优化问题
Hey there! Let's break down your problem step by step and walk through how to fix this slow API issue and implement proper background processing with Sidekiq in your Shopify Rails app.
The core issue here is that your initial POST request to the external inventory API is taking longer than Rails' default request timeout (usually 15 seconds). We need to move this heavy work out of the web request cycle and into a background job.
First: Set Up Sidekiq in Your Rails App
Start by getting Sidekiq configured properly:
- Add the
sidekiqgem to yourGemfile, then runbundle install - Create a config file at
config/initializers/sidekiq.rbwith basic Redis setup:
Sidekiq.configure_server do |config| config.redis = { url: ENV['REDIS_URL'] || 'redis://localhost:6379/0' } end Sidekiq.configure_client do |config| config.redis = { url: ENV['REDIS_URL'] || 'redis://localhost:6379/0' } end
- Make sure Redis is running locally (or use a managed Redis service for production)
Create a Background Job for Inventory Sync
Generate a Sidekiq job to handle the API call and Shopify updates:
rails generate sidekiq:job InventorySync
Then update the job file at app/jobs/inventory_sync_job.rb with your logic:
class InventorySyncJob include Sidekiq::Job # Customize retries for transient API issues sidekiq_options retry: 3, retry_delay: 60 def perform(shop_domain, shop_token) # 1. Fetch data from external inventory API begin response = HTTParty.post('https://your-external-inventory-api.com/products', headers: { 'Authorization' => 'Bearer YOUR_EXTERNAL_API_KEY' }, body: { shop_identifier: shop_domain }.to_json, timeout: 45 # Extend timeout since we're in the background ) rescue HTTParty::Error, Timeout::Error => e Sidekiq.logger.error("Failed to connect to inventory API: #{e.message}") return # Let Sidekiq handle retries based on our options end if response.success? products_data = response.parsed_response # 2. Sync each product/variant to Shopify products_data.each do |external_product| # Recreate Shopify session for the job (session isn't shared from controller) shopify_session = ShopifyAPI::Session.new(shop_domain, shop_token) ShopifyAPI::Base.activate_session(shopify_session) begin shopify_product = ShopifyAPI::Product.find(external_product['shopify_product_id']) external_product['variants'].each do |variant_data| shopify_variant = shopify_product.variants.find { |v| v.id == variant_data['shopify_variant_id'] } next unless shopify_variant shopify_variant.inventory_quantity = variant_data['quantity'] shopify_variant.save! end rescue ShopifyAPI::Errors::NotFound => e Sidekiq.logger.warn("Shopify product/variant not found: #{e.message}") ensure ShopifyAPI::Base.clear_session end end # Optional: Update your shop's sync status in the database Shop.find_by(domain: shop_domain).update(last_synced_at: Time.now, sync_status: 'success') else Sidekiq.logger.error("Inventory API returned error: #{response.code} - #{response.message}") Shop.find_by(domain: shop_domain).update(sync_status: 'failed') end end end
Trigger the Job from Your Controller
Instead of running the API call directly in your controller, enqueue the job:
class InventorySyncController < ApplicationController before_action :authenticate_shop def start # Grab the current shop's domain and token from your database/session shop = current_shop InventorySyncJob.perform_async(shop.domain, shop.shopify_token) redirect_to dashboard_path, notice: 'Inventory sync started in the background! We’ll update you when it’s done.' end end
Here’s how to handle common problems that come with this workflow:
- Memory bloat from large datasets: If the API returns thousands of products, split the work into smaller chunks. Instead of processing all products in one job, first fetch a list of product IDs, then enqueue separate jobs for batches of 20-50 products.
- Rate limiting: Both Shopify and your external API have rate limits. Add small delays between variant updates (
sleep 0.5inside the variant loop) or use Sidekiq's batch processing to throttle requests. - Tracking sync progress: Add
last_synced_atandsync_statuscolumns to yourShopmodel (as shown in the job) so you can show users the current state of their sync. - Failed job alerts: Integrate Sidekiq with a tool like Sentry to get notified when jobs fail repeatedly, so you can debug issues quickly.
- Isolate Shopify sessions: Never rely on the controller’s Shopify session in background jobs—always recreate the session using the shop’s domain and token stored in your database.
- Secure the Sidekiq web UI: Add authentication to the Sidekiq dashboard (mounted in
routes.rb) to prevent unauthorized access in production:
require 'sidekiq/web' Rails.application.routes.draw do authenticate :user, ->(user) { user.admin? } do mount Sidekiq::Web => '/sidekiq' end end
- Monitor job performance: Use the Sidekiq web UI to track job completion times, retry counts, and failures. This helps you identify bottlenecks (like slow Shopify API calls) quickly.
内容的提问来源于stack exchange,提问作者Marko

