[FIX] res.users: don't delay login() if user row is currently locked, backport of...
authorRifakat <rha@tinyerp.com>
Tue, 31 Jul 2012 03:56:46 +0000 (09:26 +0530)
committerRifakat <rha@tinyerp.com>
Tue, 31 Jul 2012 03:56:46 +0000 (09:26 +0530)
lp bug: https://launchpad.net/bugs/713216 fixed

bzr revid: rha@tinyerp.com-20120731035646-uyms72swk5btlvqg

bin/addons/base/res/res_user.py

index 0c54f47..c683416 100644 (file)
@@ -440,16 +440,37 @@ class users(osv.osv):
             return False
         cr = pooler.get_db(db).cursor()
         try:
+            # autocommit: our single request will be performed atomically.
+            # (In this way, there is no opportunity to have two transactions
+            # interleaving their cr.execute()..cr.commit() calls and have one
+            # of them rolled back due to a concurrent access.)
+            # We effectively unconditionally write the res_users line.
+            cr.autocommit(True)
+            # Even w/ autocommit there's a chance the user row will be locked,
+            # in which case we can't delay the login just for the purpose of
+            # update the last login date - hence we use FOR UPDATE NOWAIT to
+            # try to get the lock - fail-fast
+            cr.execute("""SELECT id from res_users
+                          WHERE login=%s AND password=%s
+                                AND active FOR UPDATE NOWAIT""",
+                       (tools.ustr(login), tools.ustr(password)))
             cr.execute('UPDATE res_users SET date=now() WHERE login=%s AND password=%s AND active RETURNING id',
                     (tools.ustr(login), tools.ustr(password)))
+        except Exception:
+            # Failing to acquire the lock on the res_users row probably means
+            # another request is holding it. No big deal, we don't want to
+            # prevent/delay login in that case. It will also have been logged
+            # as a SQL error, if anyone cares.
+            cr.execute("""SELECT id from res_users
+                          WHERE login=%s AND password=%s
+                                AND active""",
+                       (tools.ustr(login), tools.ustr(password)))
+        finally:
             res = cr.fetchone()
-            cr.commit()
+            cr.close()
             if res:
                 return res[0]
-            else:
-                return False
-        finally:
-            cr.close()
+        return False
 
     def check_super(self, passwd):
         if passwd == tools.config['admin_passwd']: