'Write changes to db and write the full object to cache.
When writing to postgres we write only the changes. The data in postgres
is the canonical version.
For a few reasons (speed, decreased load on postgres, postgres
replication lag) we want to keep a perfectly consistent copy of the
thing in cache.
To achieve this we read the current value of the thing from cache to
pull in any changes made by other processes, apply our changes to the
thing, and finally set it in cache. This is done under lock to ensure
read/write safety.
If the cached thing is evicted or expires we must read from postgres.
Failure cases:
* Write to cache fails. The cache now contains stale/incorrect data. To
ensure we recover quickly TTLs should be set as low as possible
without overloading postgres.
* There is long replication lag and high cache pressure. When an object
is modified it is written to cache, but quickly evicted, The next
lookup might read from a postgres secondary before the changes have
been replicated there. To protect against this replication lag and
cache pressure should be monitored and kept at acceptable levels.
* Near simultaneous writes that create a logical inconsistency. Say
request 1 and request 2 both read state 0 of a Thing. Request 1
changes Thing.prop from True to False and writes to cache and
postgres. Request 2 examines the value of Thing.prop, sees that it is
True, and due to logic in the app sets Thing.prop_is_true to True and
writes to cache and postgres. Request 2 didn\'t clobber the change
made by request 1, but it made a logically incorrect change--the
resulting state is Thing.prop = False and Thing.prop_is_true = True.
Logic like this should be identified and avoided wherever possible, or
protected against using locks.'

def _commit(self):

    if (not self._created):
        with TdbTransactionContext():
            _id = self.write_new_thing_to_db()
            self._id = _id
            self._created = True
            changes = self._dirties.copy()
            self.write_changes_to_db(changes, brand_new_thing=True)
            self._dirties.clear()
        self.write_thing_to_cache(lock=None, brand_new_thing=True)
        self.record_cache_write(event='create')
    else:
        with self.get_read_modify_write_lock() as lock:
            self.update_from_cache(lock)
            if (not self._dirty):
                return
            with TdbTransactionContext():
                changes = self._dirties.copy()
                self.write_changes_to_db(changes, brand_new_thing=False)
                self._dirties.clear()
            self.write_thing_to_cache(lock)
            self.record_cache_write(event='modify')
    hooks.get_hook('thing.commit').call(thing=self, changes=changes)
