7

I am using the aasm_state gem, along with sidekiq. Here's what customer.rb looks like for the definition of aasm_state:

  aasm do
    state :closed, initial: true
    state :open
    state :claimed

    event :open do
      transitions from: :closed, to: :open
    end

    event :claim do
      transitions from: [:open, :closed], to: :claimed
    end

    event :close do
      transitions from: [:open, :claimed], to: :closed
      before do
        self.user_id = nil
      end
    end
  end

and then I also have in customer.rb:

  def properly_close
    if closed? && user_id
      Rails.logger.info "Bad customer state with customer_id: #{id} at #{Time.now} and the last message was at #{messages.last.created_at}. Aasm_state_was: #{aasm_state_was}"
      self.user_id = nil
    end
  end

Whenever aasm_state == 'closed', there should never be a user_id on customer. However, it still happens, frequently. I'm thinking it has something to do with sidekiq jobs in parallel but i'm just not sure. Even with my two ways of making sure user_id = nil (in the before do and in properly_close), it still ends up getting set with aasm_state == 'closed' && user_id

How is this possible? How do I figure out how it is happening?

Matthew Berman
  • 8,481
  • 10
  • 49
  • 98
  • Do you have *lock_version* column in the table? Try to add it to switch built in optimistic locking mechanism on by adding this column. At least it may help to find out more re concurrent access to that table. – Anatoly Jun 26 '15 at 06:22
  • I'll give that a try. However, just thinking about how activerecord/sidekiq works, if the individual values of each job can never be aasm_state = closed && user_id not nil, i'm struggling to understand how concurrency could be the issue. – Matthew Berman Jun 26 '15 at 16:29
  • Do you use `update_attribute` to set the state directly? `update_attribute` will bypass validations and callbacks. – nathanvda Jun 30 '15 at 11:03

1 Answers1

4

In both cases you need to save the update, i.e:

self.user_id = nil
self.save

Or more concisely (and skipping callbacks):

self.update_attribute :user_id, nil
davetapley
  • 17,000
  • 12
  • 60
  • 86