tdb_sql/Thing: Assume data props should be INSERTed on thing creation.

Previously, setting data props on a thing would result in a SELECT to
determine which data properties already existed, and then a sequence of
INSERTs and UPDATEs (with some batching) to get the database in the
right state.

The account master wasn't too happy with those extra selects, so we
made it so that the app would optimistically UPDATE and if no rows were
changed do INSERTs instead (with appropriate batching as above).

Unfortunately, this greatly increases the number of operations done when
adding new data props, such as at thing creation time, and on some
tables that's almost entirely all of the workload.

This patch takes advantage of the fact that we know no data attributes
will exist at thing creation time and therefore we can blindly INSERT
without doing UPDATEs or SELECTs first to try and reduce the number of
extraneous operations we do.
This commit is contained in:
Neil Williams
2012-08-20 11:14:22 -07:00
parent 2c91a2a18e
commit 30885089bd
2 changed files with 34 additions and 5 deletions

View File

@@ -489,7 +489,8 @@ def db2py(val, kind):
return val
def set_data(table, thing_id, **vals):
def update_data(table, thing_id, **vals):
transactions.add_engine(table.bind)
u = table.update(sa.and_(table.c.thing_id == thing_id,
@@ -510,6 +511,22 @@ def set_data(table, thing_id, **vals):
return len(inserts)
def create_data(table, thing_id, **vals):
transactions.add_engine(table.bind)
inserts = []
for key, val in vals.iteritems():
val, kind = py2db(val, return_kind=True)
inserts.append(dict(key=key, value=val, kind=kind))
if inserts:
i = table.insert(values=dict(thing_id=thing_id))
i.execute(*inserts)
return 0
def incr_data_prop(table, type_id, thing_id, prop, amount):
t = table
transactions.add_engine(t.bind)
@@ -554,9 +571,13 @@ def get_data(table, thing_id):
return res
def set_thing_data(type_id, thing_id, **vals):
def set_thing_data(type_id, thing_id, brand_new_thing, **vals):
table = get_thing_table(type_id, action = 'write')[1]
return set_data(table, thing_id, **vals)
if brand_new_thing:
return create_data(table, thing_id, **vals)
else:
return update_data(table, thing_id, **vals)
def incr_thing_data(type_id, thing_id, prop, amount):
table = get_thing_table(type_id, action = 'write')[1]
@@ -589,9 +610,13 @@ def get_thing(type_id, thing_id):
res[row.thing_id] = stor
return res
def set_rel_data(rel_type_id, thing_id, **vals):
def set_rel_data(rel_type_id, thing_id, brand_new_thing, **vals):
table = get_rel_table(rel_type_id, action = 'write')[3]
return set_data(table, thing_id, **vals)
if brand_new_thing:
return create_data(table, thing_id, **vals)
else:
return update_data(table, thing_id, **vals)
def incr_rel_data(rel_type_id, thing_id, prop, amount):
table = get_rel_table(rel_type_id, action = 'write')[3]

View File

@@ -234,6 +234,9 @@ class DataThing(object):
def _commit(self, keys=None):
if not self._created:
self._create()
just_created = True
else:
just_created = False
with g.make_lock('commit_' + self._fullname):
if not self._sync_latest():
@@ -259,6 +262,7 @@ class DataThing(object):
if data_props:
useless_updates = self._set_data(self._type_id,
self._id,
just_created,
**data_props)
else:
useless_updates = 0