Skip to content

Pessimistic locking via add_lock! adds lock clause on nested selects and can cause deadlock #1

Closed
@metaskills

Description

@metaskills

Reported by Joe Rafaniello | June 12th, 2009 @ 05:28 PM

I am using the 2.2.16 version of the adapter and noticed that I occasionally receive deadlocks while using the :lock option, like this:

Account.find(:first,
  :conditions => [cond, *cond_param],
  :order => "priority, id",
  :lock => "WITH(UPDLOCK, ROWLOCK)"
)

The error shows itself as a process being chosen as a deadlock victim. Notice that we have nested selects and that each one has a WITH(UPDLOCK,ROWLOCK) and they are on the same table:

An error has occurred during work processing: DBI::DatabaseError: 37000 (1205) [unixODBC][FreeTDS][SQL Server]Transaction (Process ID 62) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: SELECT TOP 1 * FROM [accounts] WITH(UPDLOCK, ROWLOCK) WHERE (state = 'active' and acct_type = 'priority' and (created_on is null or created_on <= '2009-06-04 23:06:27.404') and (task_id is NULL or task_id not in ( select task_id FROM accounts WITH(UPDLOCK, ROWLOCK) where state = 'expired' and task_id is not NULL )) and (region = 'NorthEast' or region = '' or region IS NULL) and (role in ('administrator','operator') or role = '' or role IS NULL) ORDER BY priority, id /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb:188:in log' /var/www/miq/vmdb/vendor/gems/rails-sqlserver-2000-2005-adapter-2.2.16/lib/active_record/connection_adapters/sqlserver_adapter.rb:811:inraw_execute' /var/www/miq/vmdb/vendor/gems/rails-sqlserver-2000-2005-adapter-2.2.16/lib/active_record/connection_adapters/sqlserver_adapter.rb:834:in raw_select' /var/www/miq/vmdb/vendor/gems/rails-sqlserver-2000-2005-adapter-2.2.16/lib/active_record/connection_adapters/sqlserver_adapter.rb:787:inselect' /var/www/miq/vmdb/vendor/gems/rails-sqlserver-2000-2005-adapter-2.2.16/lib/active_record/connection_adapters/sqlserver_adapter.rb:787:in select' /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb:7:inselect_all_without_query_cache' /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb:62:in select_all' /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/base.rb:635:infind_by_sql' /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/base.rb:1490:in find_every' /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/base.rb:1452:infind_initial' /var/www/miq/vmdb/config/../vendor/gems/activerecord-2.2.2/lib/active_record/base.rb:587:in find'

I found that the sql.gsub! line in the add_lock! method of the adapter is adding the LOCK phrase to each of the FROM clauses. I have a hack to the adapter to only add the lock for the first select we find. I'm not sure this is really the correct way of handling it. I'm not sure how to handle the JOIN gsub! either. Any ideas?

Attached is a svn diff of my local patch.
http://rails-sqlserver.lighthouseapp.com/attachments/132867/add_lock_deadlock.patch

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions