{"action":"created","comment":{"url":"https://api.github.com/repos/aasm/aasm/pulls/comments/50994135","id":50994135,"diff_hunk":"@@ -399,6 +399,39 @@\n     expect(persistor).not_to be_sleeping\n   end\n \n+  describe 'pessimistic locking' do\n+    let(:worker) { Worker.create!(:name => 'worker', :status => 'sleeping') }\n+\n+    subject { transactor.run! }\n+\n+    context 'no lock' do\n+      let(:transactor) { NoLockTransactor.create!(:name => 'no_lock_transactor', :worker => worker) }\n+\n+      it 'should invoke pessimsitic lock with true' do","path":"spec/unit/persistence/active_record_persistence_spec.rb","position":null,"original_position":12,"commit_id":"de224f3f4a768c03b8f8ab31ef708f9c310b3c23","original_commit_id":"990736d6d67fc29dd4614ba56ef7fd5ce88f9b83","user":{"login":"HoyaBoya","id":134368,"avatar_url":"https://avatars.githubusercontent.com/u/134368?v=3","gravatar_id":"","url":"https://api.github.com/users/HoyaBoya","html_url":"https://github.com/HoyaBoya","followers_url":"https://api.github.com/users/HoyaBoya/followers","following_url":"https://api.github.com/users/HoyaBoya/following{/other_user}","gists_url":"https://api.github.com/users/HoyaBoya/gists{/gist_id}","starred_url":"https://api.github.com/users/HoyaBoya/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HoyaBoya/subscriptions","organizations_url":"https://api.github.com/users/HoyaBoya/orgs","repos_url":"https://api.github.com/users/HoyaBoya/repos","events_url":"https://api.github.com/users/HoyaBoya/events{/privacy}","received_events_url":"https://api.github.com/users/HoyaBoya/received_events","type":"User","site_admin":false},"body":"@TheDudeWithTheThing and @alto, I've corrected the spec descriptions in a  follow up commit. Thanks for the feedback.","created_at":"2016-01-27T14:59:23Z","updated_at":"2016-01-27T14:59:23Z","html_url":"https://github.com/aasm/aasm/pull/283#discussion_r50994135","pull_request_url":"https://api.github.com/repos/aasm/aasm/pulls/283","_links":{"self":{"href":"https://api.github.com/repos/aasm/aasm/pulls/comments/50994135"},"html":{"href":"https://github.com/aasm/aasm/pull/283#discussion_r50994135"},"pull_request":{"href":"https://api.github.com/repos/aasm/aasm/pulls/283"}}},"pull_request":{"url":"https://api.github.com/repos/aasm/aasm/pulls/283","id":49987092,"html_url":"https://github.com/aasm/aasm/pull/283","diff_url":"https://github.com/aasm/aasm/pull/283.diff","patch_url":"https://github.com/aasm/aasm/pull/283.patch","issue_url":"https://api.github.com/repos/aasm/aasm/issues/283","number":283,"state":"open","locked":false,"title":"Introduce Pessimistic Locking For AR Persistence","user":{"login":"HoyaBoya","id":134368,"avatar_url":"https://avatars.githubusercontent.com/u/134368?v=3","gravatar_id":"","url":"https://api.github.com/users/HoyaBoya","html_url":"https://github.com/HoyaBoya","followers_url":"https://api.github.com/users/HoyaBoya/followers","following_url":"https://api.github.com/users/HoyaBoya/following{/other_user}","gists_url":"https://api.github.com/users/HoyaBoya/gists{/gist_id}","starred_url":"https://api.github.com/users/HoyaBoya/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HoyaBoya/subscriptions","organizations_url":"https://api.github.com/users/HoyaBoya/orgs","repos_url":"https://api.github.com/users/HoyaBoya/repos","events_url":"https://api.github.com/users/HoyaBoya/events{/privacy}","received_events_url":"https://api.github.com/users/HoyaBoya/received_events","type":"User","site_admin":false},"body":"I understand there has been a previous conversation where we felt that it wasn't appropriate to have pessimistic locking within AASM.\r\n\r\nhttps://github.com/aasm/aasm/issues/230\r\n\r\nI offer this PR to revisit the conversation. It adds a ```requires_lock``` configuration to AASM. When set to true or some SQL string i.e. ```FOR UPDATE NOWAIT```, it will be passed through to ActiveRecord's ```lock!``` method. This will allow our AASM models to transition knowing that it owns the row lock and not in contention with another resource that is also accessing the model. It also allows any ```guard``` callbacks we have on the AASM model to operate on the latest data, vs potentially un-committed data if in a PostGres MVCC type of environment.\r\n\r\nOur AASM project has fallen into the same pattern as this article by Engine Yard, https://blog.engineyard.com/2010/concurrency-and-the-aasm-gem; we are constantly locking an AASM model in a controller, and potentially in a lower level facade/service layer as we introduce non HTTP touch points to our AASM models (Jobs, Console, other POROs, other AASM models). I'm wondering if this pattern gets ameliorated if we tried to offer locking within AASM.\r\n\r\nIn summary, it's becoming slightly untenable for us to continue to lock at the controller entry point when thee are so many points of contention.\r\n\r\n#### An Example\r\n\r\nUsing the simple Job example from the README with the lock\r\n\r\n```\r\nclass Job < ActiveRecord::Base\r\n  include AASM\r\n\r\n  aasm requires_lock: 'FOR UPDATE NOWAIT' do\r\n    state :sleeping, :initial => true\r\n    state :running\r\n    state :cleaning\r\n\r\n    event :run do\r\n      transitions :from => :sleeping, :to => :running\r\n    end\r\n\r\n    event :clean do\r\n      transitions :from => :running, :to => :cleaning\r\n    end\r\n\r\n    event :sleep do\r\n      transitions :from => [:running, :cleaning], :to => :sleeping\r\n    end\r\n  end\r\nend\r\n```\r\n\r\n####  Locking With No Conflict\r\n\r\nNote the FOR UPDATE NOWAIT query reload of the model.\r\n\r\n```\r\nirb(main):002:0> j.run!\r\n   (0.8ms)  BEGIN\r\n  Job Load (0.9ms)  SELECT  \"jobs\".* FROM \"jobs\"  WHERE \"jobs\".\"id\" = $1 LIMIT 1 FOR UPDATE NOWAIT  [[\"id\", 3]]\r\n  SQL (0.3ms)  UPDATE \"jobs\" SET \"aasm_state\" = $1, \"updated_at\" = $2 WHERE \"jobs\".\"id\" = 3  [[\"aasm_state\", \"running\"], [\"updated_at\", \"2015-11-06 17:57:33.939470\"]]\r\n   (6.1ms)  COMMIT\r\n=> true\r\n```\r\n\r\n#### With A Conflict\r\n\r\nconsole 1\r\n```\r\nirb(main):001:0> Job.find(3).with_lock { sleep 10000000 } \r\n```\r\n\r\nconsole 2\r\n```\r\nrb(main):005:0> j.run!\r\n   (0.3ms)  BEGIN\r\n  Job Load (1.8ms)  SELECT  \"jobs\".* FROM \"jobs\"  WHERE \"jobs\".\"id\" = $1 LIMIT 1 FOR UPDATE NOWAIT  [[\"id\", 3]]\r\nPG::Error: ERROR:  could not obtain lock on row in relation \"jobs\"\r\n: SELECT  \"jobs\".* FROM \"jobs\"  WHERE \"jobs\".\"id\" = $1 LIMIT 1 FOR UPDATE NOWAIT\r\n   (0.3ms)  ROLLBACK\r\nActiveRecord::StatementInvalid: PG::Error: ERROR:  could not obtain lock on row in relation \"jobs\"\r\n: SELECT  \"jobs\".* FROM \"jobs\"  WHERE \"jobs\".\"id\" = $1 LIMIT 1 FOR UPDATE NOWAIT\r\n```\r\n\r\nRelated Documents\r\nhttp://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html","created_at":"2015-11-06T18:21:04Z","updated_at":"2016-01-27T14:59:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"634ba240269d3e5241459f0716b81a92ca67e9f8","assignee":{"login":"alto","id":3209,"avatar_url":"https://avatars.githubusercontent.com/u/3209?v=3","gravatar_id":"","url":"https://api.github.com/users/alto","html_url":"https://github.com/alto","followers_url":"https://api.github.com/users/alto/followers","following_url":"https://api.github.com/users/alto/following{/other_user}","gists_url":"https://api.github.com/users/alto/gists{/gist_id}","starred_url":"https://api.github.com/users/alto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/alto/subscriptions","organizations_url":"https://api.github.com/users/alto/orgs","repos_url":"https://api.github.com/users/alto/repos","events_url":"https://api.github.com/users/alto/events{/privacy}","received_events_url":"https://api.github.com/users/alto/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/aasm/aasm/pulls/283/commits","review_comments_url":"https://api.github.com/repos/aasm/aasm/pulls/283/comments","review_comment_url":"https://api.github.com/repos/aasm/aasm/pulls/comments{/number}","comments_url":"https://api.github.com/repos/aasm/aasm/issues/283/comments","statuses_url":"https://api.github.com/repos/aasm/aasm/statuses/de224f3f4a768c03b8f8ab31ef708f9c310b3c23","head":{"label":"HoyaBoya:feature/pessimstic_locking_on_ar_persistence","ref":"feature/pessimstic_locking_on_ar_persistence","sha":"de224f3f4a768c03b8f8ab31ef708f9c310b3c23","user":{"login":"HoyaBoya","id":134368,"avatar_url":"https://avatars.githubusercontent.com/u/134368?v=3","gravatar_id":"","url":"https://api.github.com/users/HoyaBoya","html_url":"https://github.com/HoyaBoya","followers_url":"https://api.github.com/users/HoyaBoya/followers","following_url":"https://api.github.com/users/HoyaBoya/following{/other_user}","gists_url":"https://api.github.com/users/HoyaBoya/gists{/gist_id}","starred_url":"https://api.github.com/users/HoyaBoya/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HoyaBoya/subscriptions","organizations_url":"https://api.github.com/users/HoyaBoya/orgs","repos_url":"https://api.github.com/users/HoyaBoya/repos","events_url":"https://api.github.com/users/HoyaBoya/events{/privacy}","received_events_url":"https://api.github.com/users/HoyaBoya/received_events","type":"User","site_admin":false},"repo":{"id":45560339,"name":"aasm","full_name":"HoyaBoya/aasm","owner":{"login":"HoyaBoya","id":134368,"avatar_url":"https://avatars.githubusercontent.com/u/134368?v=3","gravatar_id":"","url":"https://api.github.com/users/HoyaBoya","html_url":"https://github.com/HoyaBoya","followers_url":"https://api.github.com/users/HoyaBoya/followers","following_url":"https://api.github.com/users/HoyaBoya/following{/other_user}","gists_url":"https://api.github.com/users/HoyaBoya/gists{/gist_id}","starred_url":"https://api.github.com/users/HoyaBoya/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/HoyaBoya/subscriptions","organizations_url":"https://api.github.com/users/HoyaBoya/orgs","repos_url":"https://api.github.com/users/HoyaBoya/repos","events_url":"https://api.github.com/users/HoyaBoya/events{/privacy}","received_events_url":"https://api.github.com/users/HoyaBoya/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/HoyaBoya/aasm","description":"AASM - State machines for Ruby classes (plain Ruby, ActiveRecord, Mongoid, MongoMapper)","fork":true,"url":"https://api.github.com/repos/HoyaBoya/aasm","forks_url":"https://api.github.com/repos/HoyaBoya/aasm/forks","keys_url":"https://api.github.com/repos/HoyaBoya/aasm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/HoyaBoya/aasm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/HoyaBoya/aasm/teams","hooks_url":"https://api.github.com/repos/HoyaBoya/aasm/hooks","issue_events_url":"https://api.github.com/repos/HoyaBoya/aasm/issues/events{/number}","events_url":"https://api.github.com/repos/HoyaBoya/aasm/events","assignees_url":"https://api.github.com/repos/HoyaBoya/aasm/assignees{/user}","branches_url":"https://api.github.com/repos/HoyaBoya/aasm/branches{/branch}","tags_url":"https://api.github.com/repos/HoyaBoya/aasm/tags","blobs_url":"https://api.github.com/repos/HoyaBoya/aasm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/HoyaBoya/aasm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/HoyaBoya/aasm/git/refs{/sha}","trees_url":"https://api.github.com/repos/HoyaBoya/aasm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/HoyaBoya/aasm/statuses/{sha}","languages_url":"https://api.github.com/repos/HoyaBoya/aasm/languages","stargazers_url":"https://api.github.com/repos/HoyaBoya/aasm/stargazers","contributors_url":"https://api.github.com/repos/HoyaBoya/aasm/contributors","subscribers_url":"https://api.github.com/repos/HoyaBoya/aasm/subscribers","subscription_url":"https://api.github.com/repos/HoyaBoya/aasm/subscription","commits_url":"https://api.github.com/repos/HoyaBoya/aasm/commits{/sha}","git_commits_url":"https://api.github.com/repos/HoyaBoya/aasm/git/commits{/sha}","comments_url":"https://api.github.com/repos/HoyaBoya/aasm/comments{/number}","issue_comment_url":"https://api.github.com/repos/HoyaBoya/aasm/issues/comments{/number}","contents_url":"https://api.github.com/repos/HoyaBoya/aasm/contents/{+path}","compare_url":"https://api.github.com/repos/HoyaBoya/aasm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/HoyaBoya/aasm/merges","archive_url":"https://api.github.com/repos/HoyaBoya/aasm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/HoyaBoya/aasm/downloads","issues_url":"https://api.github.com/repos/HoyaBoya/aasm/issues{/number}","pulls_url":"https://api.github.com/repos/HoyaBoya/aasm/pulls{/number}","milestones_url":"https://api.github.com/repos/HoyaBoya/aasm/milestones{/number}","notifications_url":"https://api.github.com/repos/HoyaBoya/aasm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/HoyaBoya/aasm/labels{/name}","releases_url":"https://api.github.com/repos/HoyaBoya/aasm/releases{/id}","deployments_url":"https://api.github.com/repos/HoyaBoya/aasm/deployments","created_at":"2015-11-04T18:54:01Z","updated_at":"2015-11-04T18:54:03Z","pushed_at":"2016-01-27T14:59:03Z","git_url":"git://github.com/HoyaBoya/aasm.git","ssh_url":"git@github.com:HoyaBoya/aasm.git","clone_url":"https://github.com/HoyaBoya/aasm.git","svn_url":"https://github.com/HoyaBoya/aasm","homepage":"","size":1344,"stargazers_count":0,"watchers_count":0,"language":"Ruby","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"aasm:master","ref":"master","sha":"a31b02cc2b1c2cf36658c8a21acad1ee2b0d6623","user":{"login":"aasm","id":1910612,"avatar_url":"https://avatars.githubusercontent.com/u/1910612?v=3","gravatar_id":"","url":"https://api.github.com/users/aasm","html_url":"https://github.com/aasm","followers_url":"https://api.github.com/users/aasm/followers","following_url":"https://api.github.com/users/aasm/following{/other_user}","gists_url":"https://api.github.com/users/aasm/gists{/gist_id}","starred_url":"https://api.github.com/users/aasm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aasm/subscriptions","organizations_url":"https://api.github.com/users/aasm/orgs","repos_url":"https://api.github.com/users/aasm/repos","events_url":"https://api.github.com/users/aasm/events{/privacy}","received_events_url":"https://api.github.com/users/aasm/received_events","type":"Organization","site_admin":false},"repo":{"id":1734,"name":"aasm","full_name":"aasm/aasm","owner":{"login":"aasm","id":1910612,"avatar_url":"https://avatars.githubusercontent.com/u/1910612?v=3","gravatar_id":"","url":"https://api.github.com/users/aasm","html_url":"https://github.com/aasm","followers_url":"https://api.github.com/users/aasm/followers","following_url":"https://api.github.com/users/aasm/following{/other_user}","gists_url":"https://api.github.com/users/aasm/gists{/gist_id}","starred_url":"https://api.github.com/users/aasm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aasm/subscriptions","organizations_url":"https://api.github.com/users/aasm/orgs","repos_url":"https://api.github.com/users/aasm/repos","events_url":"https://api.github.com/users/aasm/events{/privacy}","received_events_url":"https://api.github.com/users/aasm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aasm/aasm","description":"AASM - State machines for Ruby classes (plain Ruby, ActiveRecord, Mongoid, MongoMapper)","fork":false,"url":"https://api.github.com/repos/aasm/aasm","forks_url":"https://api.github.com/repos/aasm/aasm/forks","keys_url":"https://api.github.com/repos/aasm/aasm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aasm/aasm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aasm/aasm/teams","hooks_url":"https://api.github.com/repos/aasm/aasm/hooks","issue_events_url":"https://api.github.com/repos/aasm/aasm/issues/events{/number}","events_url":"https://api.github.com/repos/aasm/aasm/events","assignees_url":"https://api.github.com/repos/aasm/aasm/assignees{/user}","branches_url":"https://api.github.com/repos/aasm/aasm/branches{/branch}","tags_url":"https://api.github.com/repos/aasm/aasm/tags","blobs_url":"https://api.github.com/repos/aasm/aasm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aasm/aasm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aasm/aasm/git/refs{/sha}","trees_url":"https://api.github.com/repos/aasm/aasm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aasm/aasm/statuses/{sha}","languages_url":"https://api.github.com/repos/aasm/aasm/languages","stargazers_url":"https://api.github.com/repos/aasm/aasm/stargazers","contributors_url":"https://api.github.com/repos/aasm/aasm/contributors","subscribers_url":"https://api.github.com/repos/aasm/aasm/subscribers","subscription_url":"https://api.github.com/repos/aasm/aasm/subscription","commits_url":"https://api.github.com/repos/aasm/aasm/commits{/sha}","git_commits_url":"https://api.github.com/repos/aasm/aasm/git/commits{/sha}","comments_url":"https://api.github.com/repos/aasm/aasm/comments{/number}","issue_comment_url":"https://api.github.com/repos/aasm/aasm/issues/comments{/number}","contents_url":"https://api.github.com/repos/aasm/aasm/contents/{+path}","compare_url":"https://api.github.com/repos/aasm/aasm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aasm/aasm/merges","archive_url":"https://api.github.com/repos/aasm/aasm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aasm/aasm/downloads","issues_url":"https://api.github.com/repos/aasm/aasm/issues{/number}","pulls_url":"https://api.github.com/repos/aasm/aasm/pulls{/number}","milestones_url":"https://api.github.com/repos/aasm/aasm/milestones{/number}","notifications_url":"https://api.github.com/repos/aasm/aasm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aasm/aasm/labels{/name}","releases_url":"https://api.github.com/repos/aasm/aasm/releases{/id}","deployments_url":"https://api.github.com/repos/aasm/aasm/deployments","created_at":"2008-02-28T20:40:04Z","updated_at":"2016-01-27T08:03:15Z","pushed_at":"2016-01-27T14:59:03Z","git_url":"git://github.com/aasm/aasm.git","ssh_url":"git@github.com:aasm/aasm.git","clone_url":"https://github.com/aasm/aasm.git","svn_url":"https://github.com/aasm/aasm","homepage":"","size":1655,"stargazers_count":2199,"watchers_count":2199,"language":"Ruby","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":349,"mirror_url":null,"open_issues_count":50,"forks":349,"open_issues":50,"watchers":2199,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/aasm/aasm/pulls/283"},"html":{"href":"https://github.com/aasm/aasm/pull/283"},"issue":{"href":"https://api.github.com/repos/aasm/aasm/issues/283"},"comments":{"href":"https://api.github.com/repos/aasm/aasm/issues/283/comments"},"review_comments":{"href":"https://api.github.com/repos/aasm/aasm/pulls/283/comments"},"review_comment":{"href":"https://api.github.com/repos/aasm/aasm/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/aasm/aasm/pulls/283/commits"},"statuses":{"href":"https://api.github.com/repos/aasm/aasm/statuses/de224f3f4a768c03b8f8ab31ef708f9c310b3c23"}}}}