{"action":"created","issue":{"url":"https://api.github.com/repos/rails/rails/issues/27248","repository_url":"https://api.github.com/repos/rails/rails","labels_url":"https://api.github.com/repos/rails/rails/issues/27248/labels{/name}","comments_url":"https://api.github.com/repos/rails/rails/issues/27248/comments","events_url":"https://api.github.com/repos/rails/rails/issues/27248/events","html_url":"https://github.com/rails/rails/pull/27248","id":193005013,"number":27248,"title":"Idempotent option for after_commit :destroy callback","user":{"login":"stefanmb","id":1356314,"avatar_url":"https://avatars.githubusercontent.com/u/1356314?v=3","gravatar_id":"","url":"https://api.github.com/users/stefanmb","html_url":"https://github.com/stefanmb","followers_url":"https://api.github.com/users/stefanmb/followers","following_url":"https://api.github.com/users/stefanmb/following{/other_user}","gists_url":"https://api.github.com/users/stefanmb/gists{/gist_id}","starred_url":"https://api.github.com/users/stefanmb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stefanmb/subscriptions","organizations_url":"https://api.github.com/users/stefanmb/orgs","repos_url":"https://api.github.com/users/stefanmb/repos","events_url":"https://api.github.com/users/stefanmb/events{/privacy}","received_events_url":"https://api.github.com/users/stefanmb/received_events","type":"User","site_admin":false},"labels":[{"id":107191,"url":"https://api.github.com/repos/rails/rails/labels/activerecord","name":"activerecord","color":"0b02e1","default":false}],"state":"open","locked":false,"assignee":{"login":"rafaelfranca","id":47848,"avatar_url":"https://avatars.githubusercontent.com/u/47848?v=3","gravatar_id":"","url":"https://api.github.com/users/rafaelfranca","html_url":"https://github.com/rafaelfranca","followers_url":"https://api.github.com/users/rafaelfranca/followers","following_url":"https://api.github.com/users/rafaelfranca/following{/other_user}","gists_url":"https://api.github.com/users/rafaelfranca/gists{/gist_id}","starred_url":"https://api.github.com/users/rafaelfranca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rafaelfranca/subscriptions","organizations_url":"https://api.github.com/users/rafaelfranca/orgs","repos_url":"https://api.github.com/users/rafaelfranca/repos","events_url":"https://api.github.com/users/rafaelfranca/events{/privacy}","received_events_url":"https://api.github.com/users/rafaelfranca/received_events","type":"User","site_admin":false},"assignees":[{"login":"rafaelfranca","id":47848,"avatar_url":"https://avatars.githubusercontent.com/u/47848?v=3","gravatar_id":"","url":"https://api.github.com/users/rafaelfranca","html_url":"https://github.com/rafaelfranca","followers_url":"https://api.github.com/users/rafaelfranca/followers","following_url":"https://api.github.com/users/rafaelfranca/following{/other_user}","gists_url":"https://api.github.com/users/rafaelfranca/gists{/gist_id}","starred_url":"https://api.github.com/users/rafaelfranca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rafaelfranca/subscriptions","organizations_url":"https://api.github.com/users/rafaelfranca/orgs","repos_url":"https://api.github.com/users/rafaelfranca/repos","events_url":"https://api.github.com/users/rafaelfranca/events{/privacy}","received_events_url":"https://api.github.com/users/rafaelfranca/received_events","type":"User","site_admin":false},{"login":"sgrif","id":1529387,"avatar_url":"https://avatars.githubusercontent.com/u/1529387?v=3","gravatar_id":"","url":"https://api.github.com/users/sgrif","html_url":"https://github.com/sgrif","followers_url":"https://api.github.com/users/sgrif/followers","following_url":"https://api.github.com/users/sgrif/following{/other_user}","gists_url":"https://api.github.com/users/sgrif/gists{/gist_id}","starred_url":"https://api.github.com/users/sgrif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgrif/subscriptions","organizations_url":"https://api.github.com/users/sgrif/orgs","repos_url":"https://api.github.com/users/sgrif/repos","events_url":"https://api.github.com/users/sgrif/events{/privacy}","received_events_url":"https://api.github.com/users/sgrif/received_events","type":"User","site_admin":false}],"milestone":null,"comments":18,"created_at":"2016-12-02T00:53:33Z","updated_at":"2016-12-08T17:59:44Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/rails/rails/pulls/27248","html_url":"https://github.com/rails/rails/pull/27248","diff_url":"https://github.com/rails/rails/pull/27248.diff","patch_url":"https://github.com/rails/rails/pull/27248.patch"},"body":"### Summary\r\n\r\nIn SQL a `DELETE` statement is always idempotent (will always succeed even if the underlying table is only modified once). Consequently the `after_commit` callbacks on `:destroy`will be invoked multiple times for a given model, even if a single deletion occurred. This means that user handlers must also be idempotent, which is not always the case (for example, if counting resources).\r\n\r\nThe issue addressed in this PR is related to https://github.com/rails/rails/pull/14735 and is fixed in a similar fashion:\r\n\r\n- Introduce an optional `:idempotent` flag for the `after_commit` callback.\r\n- Default to `true` to preserve existing behaviour.\r\n- Cache whether the operation affected any rows and provide an `actually_destroyed?` getter.\r\n- Demonstrate problem and solution with new unit tests.\r\n\r\nWe (Shopify) have been running a monkey patched version of this PR since October in production with no ill-effects.\r\n\r\nr: @byroot @sgrif\r\ncc: @tenderlove\r\n\r\nP.S. This is my first Rails PR, so if there are any silly issues or violated conventions please let me know and I will resolve them as soon as possible. Thank you!\r\n"},"comment":{"url":"https://api.github.com/repos/rails/rails/issues/comments/265808793","html_url":"https://github.com/rails/rails/pull/27248#issuecomment-265808793","issue_url":"https://api.github.com/repos/rails/rails/issues/27248","id":265808793,"user":{"login":"sgrif","id":1529387,"avatar_url":"https://avatars.githubusercontent.com/u/1529387?v=3","gravatar_id":"","url":"https://api.github.com/users/sgrif","html_url":"https://github.com/sgrif","followers_url":"https://api.github.com/users/sgrif/followers","following_url":"https://api.github.com/users/sgrif/following{/other_user}","gists_url":"https://api.github.com/users/sgrif/gists{/gist_id}","starred_url":"https://api.github.com/users/sgrif/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sgrif/subscriptions","organizations_url":"https://api.github.com/users/sgrif/orgs","repos_url":"https://api.github.com/users/sgrif/repos","events_url":"https://api.github.com/users/sgrif/events{/privacy}","received_events_url":"https://api.github.com/users/sgrif/received_events","type":"User","site_admin":false},"created_at":"2016-12-08T17:59:44Z","updated_at":"2016-12-08T17:59:44Z","body":"@matthewd It looks like both are problems"}}
{"actor":{"display_login":"sgrif"}}