Pass object instantiations through a cacher.

I want the system to only have one instance of a particular
object at any time, so that we can effectively cache things
in them.
This commit is contained in:
voussoir 2018-05-06 20:56:09 -07:00
parent 11fda94968
commit 40c255b0d0

View file

@ -106,7 +106,7 @@ class PDBAlbumMixin:
} }
self.sql_insert(table='albums', data=data) self.sql_insert(table='albums', data=data)
album = objects.Album(self, data) album = self.get_cached_instance('album', data)
if associated_directory is not None: if associated_directory is not None:
album.add_associated_directory(associated_directory, commit=False) album.add_associated_directory(associated_directory, commit=False)
@ -151,7 +151,7 @@ class PDBBookmarkMixin:
} }
self.sql_insert(table='bookmarks', data=data) self.sql_insert(table='bookmarks', data=data)
bookmark = objects.Bookmark(self, data) bookmark = self.get_cached_instance('bookmark', data)
if commit: if commit:
self.log.debug('Committing - new Bookmark') self.log.debug('Committing - new Bookmark')
self.commit() self.commit()
@ -180,7 +180,7 @@ class PDBPhotoMixin:
photo_row = self.sql_select_one(query, bindings) photo_row = self.sql_select_one(query, bindings)
if photo_row is None: if photo_row is None:
raise exceptions.NoSuchPhoto(filepath) raise exceptions.NoSuchPhoto(filepath)
photo = objects.Photo(self, photo_row) photo = self.get_cached_instance('photo', photo_row)
return photo return photo
def get_photos_by_id(self, ids): def get_photos_by_id(self, ids):
@ -196,7 +196,7 @@ class PDBPhotoMixin:
query = 'SELECT * FROM photos ORDER BY created DESC' query = 'SELECT * FROM photos ORDER BY created DESC'
photo_rows = self.sql_select(query) photo_rows = self.sql_select(query)
for photo_row in photo_rows: for photo_row in photo_rows:
photo = objects.Photo(self, photo_row) photo = self.get_cached_instance('photo', photo_row)
yield photo yield photo
if count is None: if count is None:
@ -260,7 +260,7 @@ class PDBPhotoMixin:
} }
self.sql_insert(table='photos', data=data) self.sql_insert(table='photos', data=data)
photo = objects.Photo(self, data) photo = self.get_cached_instance('photo', data)
if do_metadata: if do_metadata:
photo.reload_metadata(commit=False) photo.reload_metadata(commit=False)
@ -635,7 +635,7 @@ class PDBPhotoMixin:
generator = self.sql_select(query, bindings) generator = self.sql_select(query, bindings)
photos_received = 0 photos_received = 0
for row in generator: for row in generator:
photo = objects.Photo(self, row) photo = self.get_cached_instance('photo', row)
if mimetype and photo.simple_mimetype not in mimetype: if mimetype and photo.simple_mimetype not in mimetype:
continue continue
@ -827,7 +827,6 @@ class PDBTagMixin:
except (exceptions.TagTooShort, exceptions.TagTooLong): except (exceptions.TagTooShort, exceptions.TagTooLong):
raise exceptions.NoSuchTag(tagname) raise exceptions.NoSuchTag(tagname)
tag_row = None
while True: while True:
# Return if it's a toplevel... # Return if it's a toplevel...
tag_row = self.sql_select_one('SELECT * FROM tags WHERE name == ?', [tagname]) tag_row = self.sql_select_one('SELECT * FROM tags WHERE name == ?', [tagname])
@ -843,11 +842,7 @@ class PDBTagMixin:
raise exceptions.NoSuchTag(tagname) raise exceptions.NoSuchTag(tagname)
tagname = name_row[0] tagname = name_row[0]
tag_id = tag_row[constants.SQL_INDEX['tags']['id']] tag = self.get_cached_instance('tag', tag_row)
tag = self.caches['tag'].get(tag_id, fallback=None)
if tag is None:
tag = objects.Tag(self, tag_row)
self.caches['tag'][tag_id] = tag
return tag return tag
def get_tags(self): def get_tags(self):
@ -885,7 +880,7 @@ class PDBTagMixin:
if commit: if commit:
self.log.debug('Committing - new_tag') self.log.debug('Committing - new_tag')
self.commit() self.commit()
tag = objects.Tag(self, data) tag = self.get_cached_instance('tag', data)
return tag return tag
def normalize_tagname(self, tagname): def normalize_tagname(self, tagname):
@ -959,7 +954,7 @@ class PDBUserMixin:
user_row = self.sql_select_one('SELECT * FROM users WHERE id == ?', [id]) user_row = self.sql_select_one('SELECT * FROM users WHERE id == ?', [id])
if user_row is not None: if user_row is not None:
return objects.User(self, user_row) return self.get_cached_instance('user', user_row)
else: else:
raise exceptions.NoSuchUser(username or id) raise exceptions.NoSuchUser(username or id)
@ -1008,7 +1003,7 @@ class PDBUserMixin:
if not isinstance(password, bytes): if not isinstance(password, bytes):
password = password.encode('utf-8') password = password.encode('utf-8')
user = objects.User(self, user_row) user = self.get_cached_instance('user', user_row)
success = bcrypt.checkpw(password, user.password_hash) success = bcrypt.checkpw(password, user.password_hash)
if not success: if not success:
@ -1045,7 +1040,7 @@ class PDBUserMixin:
self.log.debug('Committing - register user') self.log.debug('Committing - register user')
self.commit() self.commit()
return objects.User(self, data) return self.get_cached_instance('user', data)
class PDBUtilMixin: class PDBUtilMixin:
@ -1406,6 +1401,26 @@ class PhotoDB(
self._cached_frozen_children = tag_export.flat_dict(self.get_tags()) self._cached_frozen_children = tag_export.flat_dict(self.get_tags())
return self._cached_frozen_children return self._cached_frozen_children
def get_cached_instance(self, thing_type, db_row):
thing_map = _THING_CLASSES[thing_type]
thing_table = thing_map['table']
thing_class = thing_map['class']
thing_cache = self.caches[thing_type]
thing_index = constants.SQL_INDEX[thing_table]
if isinstance(db_row, dict):
thing_id = db_row['id']
else:
thing_id = db_row[thing_index['id']]
try:
thing = thing_cache[thing_id]
except KeyError:
thing = thing_class(self, db_row)
thing_cache[thing_id] = thing
return thing
def get_cached_qualname_map(self): def get_cached_qualname_map(self):
if self._cached_qualname_map is None: if self._cached_qualname_map is None:
self._cached_qualname_map = tag_export.qualified_names(self.get_tags()) self._cached_qualname_map = tag_export.qualified_names(self.get_tags())
@ -1415,20 +1430,20 @@ class PhotoDB(
thing_map = _THING_CLASSES[thing_type] thing_map = _THING_CLASSES[thing_type]
thing_class = thing_map['class'] thing_class = thing_map['class']
table = thing_map['table'] thing_table = thing_map['table']
group_table = thing_class.group_table group_table = thing_class.group_table
query = ''' query = f'''
SELECT * FROM {table} SELECT * FROM {thing_table}
WHERE NOT EXISTS ( WHERE NOT EXISTS (
SELECT 1 FROM {group_table} SELECT 1 FROM {group_table}
WHERE memberid == {table}.id WHERE memberid == {thing_table}.id
) )
'''.format(table=table, group_table=group_table) '''
rows = self.sql_select(query) rows = self.sql_select(query)
for row in rows: for row in rows:
thing = thing_class(self, row) thing = self.get_cached_instance(thing_type, row)
yield thing yield thing
def get_thing_by_id(self, thing_type, thing_id): def get_thing_by_id(self, thing_type, thing_id):
@ -1460,7 +1475,7 @@ class PhotoDB(
things = self.sql_select(query) things = self.sql_select(query)
for thing_row in things: for thing_row in things:
thing = thing_map['class'](self, db_row=thing_row) thing = self.get_cached_instance(thing_type, thing_row)
yield thing yield thing
def get_things_by_id(self, thing_type, thing_ids): def get_things_by_id(self, thing_type, thing_ids):
@ -1487,7 +1502,11 @@ class PhotoDB(
query = 'SELECT * FROM %s WHERE id IN %s' % (thing_map['table'], qmarks) query = 'SELECT * FROM %s WHERE id IN %s' % (thing_map['table'], qmarks)
more_things = self.sql_select(query, id_batch) more_things = self.sql_select(query, id_batch)
for thing_row in more_things: for thing_row in more_things:
thing = thing_map['class'](self, db_row=thing_row) # Normally we would call `get_cached_instance` instead of
# constructing here. But we already know for a fact that this
# object is not in the cache because it made it past the
# previous loop.
thing = thing_class(self, db_row=thing_row)
thing_cache[thing.id] = thing thing_cache[thing.id] = thing
yield thing yield thing