{"action":"created","issue":{"url":"https://api.github.com/repos/odoo/odoo/issues/25252","repository_url":"https://api.github.com/repos/odoo/odoo","labels_url":"https://api.github.com/repos/odoo/odoo/issues/25252/labels{/name}","comments_url":"https://api.github.com/repos/odoo/odoo/issues/25252/comments","events_url":"https://api.github.com/repos/odoo/odoo/issues/25252/events","html_url":"https://github.com/odoo/odoo/issues/25252","id":332445275,"node_id":"MDU6SXNzdWUzMzI0NDUyNzU=","number":25252,"title":"[9.0] Odoo XML RPC and SQL Isolation","user":{"login":"be-cloud-js","id":12003829,"node_id":"MDQ6VXNlcjEyMDAzODI5","avatar_url":"https://avatars0.githubusercontent.com/u/12003829?v=4","gravatar_id":"","url":"https://api.github.com/users/be-cloud-js","html_url":"https://github.com/be-cloud-js","followers_url":"https://api.github.com/users/be-cloud-js/followers","following_url":"https://api.github.com/users/be-cloud-js/following{/other_user}","gists_url":"https://api.github.com/users/be-cloud-js/gists{/gist_id}","starred_url":"https://api.github.com/users/be-cloud-js/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/be-cloud-js/subscriptions","organizations_url":"https://api.github.com/users/be-cloud-js/orgs","repos_url":"https://api.github.com/users/be-cloud-js/repos","events_url":"https://api.github.com/users/be-cloud-js/events{/privacy}","received_events_url":"https://api.github.com/users/be-cloud-js/received_events","type":"User","site_admin":false},"labels":[{"id":712047053,"node_id":"MDU6TGFiZWw3MTIwNDcwNTM=","url":"https://api.github.com/repos/odoo/odoo/labels/9.0","name":"9.0","color":"f03e1e","default":false},{"id":616698546,"node_id":"MDU6TGFiZWw2MTY2OTg1NDY=","url":"https://api.github.com/repos/odoo/odoo/labels/Framework","name":"Framework","color":"E5E970","default":false}],"state":"closed","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2018-06-14T15:09:54Z","updated_at":"2018-07-02T23:01:57Z","closed_at":"2018-07-02T23:01:57Z","author_association":"NONE","body":"Impacted versions: \r\n\r\nat least 9.0\r\n\r\nSteps to reproduce:\r\n\r\nUsing an installation with 4 workers, I have a \"web application\" that uses XML RPC calls to create \"calendar.event\"\r\n\r\n      new Model('calendar.event').call('create', [{\r\n                        'name' : session.partner.name,\r\n                        'start': start.utc().format('YYYY-MM-DD HH:mm:ss'),\r\n                        'stop': stop.utc().format('YYYY-MM-DD HH:mm:ss'),\r\n                        'room_id': roomId,\r\n                        'categ_ids': [[4, categ[1]]],\r\n                    }]).fail(function(error) {\r\n                        if(error.data.exception_type == \"validation_error\"){\r\n                            Materialize.toast(error.data.arguments[0], 4000)\r\n                        }\r\n                    }).then(function (id) {\r\n                        self.trigger_up('newEvent', {'id': id});\r\n                        self.clearAll();\r\n                    });\r\n\r\non my model I have a constraint function that check if a room is already taken and in that case throw an Exception\r\n\r\n    @api.one\r\n    @api.constrains('room_id')\r\n    def _check_room_quota(self):\r\n        \r\n        _logger.info('Check constraints _check_room_quota on record %s' % self.id)\r\n        if self.room_id :\r\n            # Prevent concurrent bookings\r\n\r\n            domain = [('room_id','=',self.room_id.id), ('start', '<', self.stop_datetime), ('stop', '>', self.start_datetime)]\r\n            conflicts_count = self.env['calendar.event'].sudo().with_context({'virtual_id': True}).search_count(domain)\r\n            if conflicts_count > 1:\r\n                raise ValidationError(_(\"Concurrent event detected - %s in %s\") % (self.start_datetime, self.room_id.name))\r\n\r\nCurrent behavior:\r\n\r\nThis solution is working \"most of the time\", ie 99.9% of the time. Problem is once in a while, users achieve to create conflicting calendar.event.\r\n\r\nExpected behavior:\r\n\r\nThe isolation should prevent concurrent event all the time, not 99,9% of the time\r\n\r\nSome input from the code to discuss :\r\n\r\nI get into the sql.py code and my understanding is that the sql isolation should prevent this to happen :\r\n\r\n        In the context of a generic business data management software\r\n        such as OpenERP, we need the best guarantees that no data\r\n        corruption can ever be cause by simply running multiple\r\n        transactions in parallel. Therefore, the preferred level would\r\n        be the *serializable* level, which ensures that a set of\r\n        transactions is guaranteed to produce the same effect as\r\n        running them one at a time in some order.\r\n\r\nBut later\r\n\r\n        \"OpenERP implements its own level of locking protection\r\n        for transactions that are highly likely to provoke concurrent\r\n        updates, such as stock reservations or document sequences updates.\r\n        Therefore we mostly care about the properties of snapshot isolation,\r\n        but we don't really need additional heuristics to trigger transaction\r\n        rollbacks, as we are taking care of triggering instant rollbacks\r\n        ourselves when it matters (and we can save the additional performance\r\n        hit of these heuristics).\"\r\n\r\n**So I am a bit confused if I need to setup a specific locking protection or if I can rely on Odoo to prevent colliding updates and I miss something.**\r\n"},"comment":{"url":"https://api.github.com/repos/odoo/odoo/issues/comments/401962925","html_url":"https://github.com/odoo/odoo/issues/25252#issuecomment-401962925","issue_url":"https://api.github.com/repos/odoo/odoo/issues/25252","id":401962925,"node_id":"MDEyOklzc3VlQ29tbWVudDQwMTk2MjkyNQ==","user":{"login":"pedrobaeza","id":7165771,"node_id":"MDQ6VXNlcjcxNjU3NzE=","avatar_url":"https://avatars2.githubusercontent.com/u/7165771?v=4","gravatar_id":"","url":"https://api.github.com/users/pedrobaeza","html_url":"https://github.com/pedrobaeza","followers_url":"https://api.github.com/users/pedrobaeza/followers","following_url":"https://api.github.com/users/pedrobaeza/following{/other_user}","gists_url":"https://api.github.com/users/pedrobaeza/gists{/gist_id}","starred_url":"https://api.github.com/users/pedrobaeza/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pedrobaeza/subscriptions","organizations_url":"https://api.github.com/users/pedrobaeza/orgs","repos_url":"https://api.github.com/users/pedrobaeza/repos","events_url":"https://api.github.com/users/pedrobaeza/events{/privacy}","received_events_url":"https://api.github.com/users/pedrobaeza/received_events","type":"User","site_admin":false},"created_at":"2018-07-02T23:01:57Z","updated_at":"2018-07-02T23:01:57Z","author_association":"CONTRIBUTOR","body":"The problem comes from the fact that you are checking the constraint on Python, and the running time of that method in concurrency can make that the search is performed on parallel and both results are positive to continue, so the answer is that in these cases, you should either:\r\n\r\n* Put code without these running conditions\r\n* Set a locking/semaphore mechanism for this case.\r\n* Or set an SQL constraint, which doesn't have this problem."}}
{"actor":{"display_login":"pedrobaeza"}}