Add transaction level logging and timing information. Add a _simple_delete method

This commit is contained in:
Erik Johnston 2014-10-28 11:18:04 +00:00
parent 967ce43b59
commit da1dda3e1d
9 changed files with 91 additions and 21 deletions

View file

@ -29,15 +29,17 @@ import time
logger = logging.getLogger(__name__)
sql_logger = logging.getLogger("synapse.storage.SQL")
transaction_logger = logging.getLogger("synapse.storage.txn")
class LoggingTransaction(object):
"""An object that almost-transparently proxies for the 'txn' object
passed to the constructor. Adds logging to the .execute() method."""
__slots__ = ["txn"]
__slots__ = ["txn", "name"]
def __init__(self, txn):
def __init__(self, txn, name):
object.__setattr__(self, "txn", txn)
object.__setattr__(self, "name", name)
def __getattr__(self, name):
return getattr(self.txn, name)
@ -47,12 +49,15 @@ class LoggingTransaction(object):
def execute(self, sql, *args, **kwargs):
# TODO(paul): Maybe use 'info' and 'debug' for values?
sql_logger.debug("[SQL] %s", sql)
sql_logger.debug("[SQL] {%s} %s", self.name, sql)
try:
if args and args[0]:
values = args[0]
sql_logger.debug("[SQL values] " +
", ".join(("<%s>",) * len(values)), *values)
sql_logger.debug(
"[SQL values] {%s} " + ", ".join(("<%s>",) * len(values)),
self.name,
*values
)
except:
# Don't let logging failures stop SQL from working
pass
@ -64,10 +69,11 @@ class LoggingTransaction(object):
)
finally:
end = time.clock() * 1000
sql_logger.debug("[SQL time] %f", end - start)
sql_logger.debug("[SQL time] {%s} %f", self.name, end - start)
class SQLBaseStore(object):
_TXN_ID = 0
def __init__(self, hs):
self.hs = hs
@ -75,10 +81,24 @@ class SQLBaseStore(object):
self.event_factory = hs.get_event_factory()
self._clock = hs.get_clock()
def runInteraction(self, func, *args, **kwargs):
def runInteraction(self, desc, func, *args, **kwargs):
"""Wraps the .runInteraction() method on the underlying db_pool."""
def inner_func(txn, *args, **kwargs):
return func(LoggingTransaction(txn), *args, **kwargs)
start = time.clock() * 1000
txn_id = str(SQLBaseStore._TXN_ID)
SQLBaseStore._TXN_ID += 1
name = "%s-%s" % (desc, txn_id, )
transaction_logger.debug("[TXN START] {%s}", name)
try:
return func(LoggingTransaction(txn, name), *args, **kwargs)
finally:
end = time.clock() * 1000
transaction_logger.debug(
"[TXN END] {%s} %f",
name, end - start
)
return self._db_pool.runInteraction(inner_func, *args, **kwargs)
@ -114,7 +134,7 @@ class SQLBaseStore(object):
else:
return cursor.fetchall()
return self.runInteraction(interaction)
return self.runInteraction("_execute", interaction)
def _execute_and_decode(self, query, *args):
return self._execute(self.cursor_to_dict, query, *args)
@ -131,6 +151,7 @@ class SQLBaseStore(object):
or_replace : bool; if True performs an INSERT OR REPLACE
"""
return self.runInteraction(
"_simple_insert",
self._simple_insert_txn, table, values, or_replace=or_replace,
or_ignore=or_ignore,
)
@ -168,6 +189,7 @@ class SQLBaseStore(object):
statement returns no rows
"""
return self._simple_selectupdate_one(
"_simple_select_one",
table, keyvalues, retcols=retcols, allow_none=allow_none
)
@ -217,7 +239,7 @@ class SQLBaseStore(object):
txn.execute(sql, keyvalues.values())
return txn.fetchall()
res = yield self.runInteraction(func)
res = yield self.runInteraction("_simple_select_onecol", func)
defer.returnValue([r[0] for r in res])
@ -240,7 +262,7 @@ class SQLBaseStore(object):
txn.execute(sql, keyvalues.values())
return self.cursor_to_dict(txn)
return self.runInteraction(func)
return self.runInteraction("_simple_select_list", func)
def _simple_update_one(self, table, keyvalues, updatevalues,
retcols=None):
@ -308,7 +330,7 @@ class SQLBaseStore(object):
raise StoreError(500, "More than one row matched")
return ret
return self.runInteraction(func)
return self.runInteraction("_simple_selectupdate_one", func)
def _simple_delete_one(self, table, keyvalues):
"""Executes a DELETE query on the named table, expecting to delete a
@ -320,7 +342,7 @@ class SQLBaseStore(object):
"""
sql = "DELETE FROM %s WHERE %s" % (
table,
" AND ".join("%s = ?" % (k) for k in keyvalues)
" AND ".join("%s = ?" % (k, ) for k in keyvalues)
)
def func(txn):
@ -329,7 +351,25 @@ class SQLBaseStore(object):
raise StoreError(404, "No row found")
if txn.rowcount > 1:
raise StoreError(500, "more than one row matched")
return self.runInteraction(func)
return self.runInteraction("_simple_delete_one", func)
def _simple_delete(self, table, keyvalues):
"""Executes a DELETE query on the named table.
Args:
table : string giving the table name
keyvalues : dict of column names and values to select the row with
"""
return self.runInteraction("_simple_delete", self._simple_delete_txn)
def _simple_delete_txn(self, txn, table, keyvalues):
sql = "DELETE FROM %s WHERE %s" % (
table,
" AND ".join("%s = ?" % (k, ) for k in keyvalues)
)
return txn.execute(sql, keyvalues.values())
def _simple_max_id(self, table):
"""Executes a SELECT query on the named table, expecting to return the
@ -347,7 +387,7 @@ class SQLBaseStore(object):
return 0
return max_id
return self.runInteraction(func)
return self.runInteraction("_simple_max_id", func)
def _parse_event_from_row(self, row_dict):
d = copy.deepcopy({k: v for k, v in row_dict.items()})
@ -371,7 +411,9 @@ class SQLBaseStore(object):
)
def _parse_events(self, rows):
return self.runInteraction(self._parse_events_txn, rows)
return self.runInteraction(
"_parse_events", self._parse_events_txn, rows
)
def _parse_events_txn(self, txn, rows):
events = [self._parse_event_from_row(r) for r in rows]