/ delayed_job

Delayed job object serialization and deserialization issue

Recently in one of our project there was a requirement to send SMS notification to users and task was to perform this functionality as background job using delayed job service. I wrote following code for sending SMS notifications to user and it was working fine. Then I just did one liner change in my code to process send_sms method asynchronously using handle_asynchronously method of delayed job and thought it will work as expected.

 attr_reader :client, :sender, :recepient, :message

 def initialize(recipient_ph_number, message_details)
   @client = Twilio::REST::Client.new ACCOUNT_SID, AUTH_TOKEN
   @sender = TWILIO_PHONE_NUMBER
   @recepient = recipient_ph_number
   @message = set_message(message_details).gsub(/[\s]{2,}/, ' ')
 end

 def send_sms
   begin
     client.account.messages.create(
       :from => sender,
       :to => "+#{recepient}",
       :body => message
     )
     Rails.logger.info "---------Sent sms to #{recepient}--------"
   rescue Twilio::REST::RequestError => e
     Rails.logger.info "------#{e.message}-----"
   end
 end

 handle_asynchronously :send_sms

After this send_sms method call was getting enqueued as background job in delayed jobs table but somehow delayed job was not able to deserialize it for processing and it was throwing well known error related to deserialization and yml parsing as follows -

Job failed to load: uninitialized constant Syck::Syck. Try to manually require the required file

And I started searching solution for above error and tried different workarounds but nothing was working. I though it is issue related to Syck and YAML parsing and searched in that direction too. At last while debugging send_sms method code I found that moving @client initialization from initialize method to send_sms as follow solves the issue.

  def initialize(recipient_ph_number, message_details)
    @sender = TWILIO_PHONE_NUMBER
    @recepient = recipient_ph_number
    @message = set_message(message_details).gsub(/[\s]{2,}/, ' ')
  end

  def send_sms
    begin
      client = Twilio::REST::Client.new ACCOUNT_SID, AUTH_TOKEN
      client.account.messages.create(
        :from => sender,
        :to => "+#{recepient}",
        :body => message
      )
      Rails.logger.info "---------Sent sms to #{recepient}--------"
    rescue Twilio::REST::RequestError => e
      Rails.logger.info "------#{e.message}-----"
    end
  end

  handle_asynchronously :send_sms

Somehow delayed job was not able to deserialize client object with its exact state and all associations with which it was serialized. So while reading some article I found that it is recommended that either simply avoid serializing of already initialized complex objects (as in first code snippet 'client' is initialized and then serialized) along with job and instead initialize that object in method itself (as done in second code snippet) so that it will be initialized while job is deseriazlied and processed or if you still want to serialize one then you need to use some other serialization-deserialization workaround than delayed_jobs default serialization technique using YAML, so that you will not get unexpected outcome while performing operations using that complex object.

Also I found some information about similar issue with delayed job here . Though post at this link is little old but still depicts the same issue which I faced and its cause.

Let me know here if anybody knows something better and helpful in this context.

Tushar Titame

I am software enthusiast. I like to work on web frameworks, services and trending things in JS world. As a hobby I love visiting beautiful places and capturing them through eyes...

Read More