[FIX] res.users: don't delay login() if user row is currently locked

Any running transaction that has created or altered
a record that contains a FK to the user is automatically
holding an SharedRowLock that prevents login() from
updating the last login date. As we cannot delay the
login() until all such transactions are finished,
it's simpler to try to get the lock and if that
fails, we skip the login date update for once,
no big deal.
A typical candidate for this situation is the
Admin user, locked out by long running cron
jobs that touch rows as Admin, hence holding
a ShareRowLock to res.users ID 1 because of the
many create_uid/write_uid foreign keys.

lp bug: https://launchpad.net/bugs/713216 fixed

bzr revid: odo@openerp.com-20120213183207-45ptopqm0szritgx
This commit is contained in:
Olivier Dony 2012-02-13 19:32:07 +01:00
parent e7ca80cdeb
commit f87324e93e
1 changed files with 25 additions and 8 deletions

View File

@ -474,23 +474,40 @@ class users(osv.osv):
return False
cr = pooler.get_db(db).cursor()
try:
# We autocommit: our single request will be performed atomically.
# autocommit: our single request will be performed atomically.
# (In this way, there is no opportunity to have two transactions
# interleaving ther cr.execute()..cr.commit() calls and have one
# of them rollbacked due to a concurrent access.)
# 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() AT TIME ZONE 'UTC'
WHERE login=%s AND password=%s AND active RETURNING id""",
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.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']: