Improve Album bytes caching, start caching photo count.

More careful uncaching of the summed bytes, to minimize
recalculation. Fewer cases where the album itself
is removed from the photodb's getter cache.

This also helps the download link on album pages disappear if
the child albums don't actually have any photos.
This commit is contained in:
voussoir 2017-11-12 15:21:53 -08:00
parent e1a904da6f
commit a510c7b55c
3 changed files with 56 additions and 28 deletions

View file

@ -85,7 +85,6 @@ class GroupableMixin:
'INSERT INTO %s VALUES(?, ?)' % self.group_table, 'INSERT INTO %s VALUES(?, ?)' % self.group_table,
[self.id, member.id] [self.id, member.id]
) )
self._uncache()
if commit: if commit:
self.photodb.log.debug('Committing - add to group') self.photodb.log.debug('Committing - add to group')
self.photodb.commit() self.photodb.commit()
@ -197,7 +196,6 @@ class GroupableMixin:
'DELETE FROM %s WHERE memberid == ?' % self.group_table, 'DELETE FROM %s WHERE memberid == ?' % self.group_table,
[self.id] [self.id]
) )
self._uncache()
if commit: if commit:
self.photodb.log.debug('Committing - leave group') self.photodb.log.debug('Committing - leave group')
self.photodb.commit() self.photodb.commit()
@ -228,8 +226,10 @@ class Album(ObjectBase, GroupableMixin):
self.description = db_row['description'] or '' self.description = db_row['description'] or ''
self.name = 'Album %s' % self.id self.name = 'Album %s' % self.id
self.group_getter = self.photodb.get_album self.group_getter = self.photodb.get_album
self._sum_bytes_photos = None
self._sum_bytes_albums = None self._sum_bytes_local = None
self._sum_bytes_recursive = None
self._sum_photos_recursive = None
def __hash__(self): def __hash__(self):
return hash(self.id) return hash(self.id)
@ -238,13 +238,24 @@ class Album(ObjectBase, GroupableMixin):
return 'Album:{id}'.format(id=self.id) return 'Album:{id}'.format(id=self.id)
def _uncache(self): def _uncache(self):
self._uncache_sums()
self.photodb.caches['album'].remove(self.id) self.photodb.caches['album'].remove(self.id)
self._sum_bytes_photos = None
self._sum_bytes_albums = None def _uncache_sums(self):
self._sum_photos_recursive = None
self._sum_bytes_local = None
self._sum_bytes_recursive = None
parent = self.parent()
if parent is not None:
parent._sum_photos_recursive = None
parent._sum_bytes_recursive = None
@decorators.required_feature('album.edit') @decorators.required_feature('album.edit')
def add_child(self, *args, **kwargs): def add_child(self, *args, **kwargs):
return super().add_child(*args, **kwargs) result = super().add_child(*args, **kwargs)
self._uncache_sums()
return result
@decorators.required_feature('album.edit') @decorators.required_feature('album.edit')
@decorators.transaction @decorators.transaction
@ -293,7 +304,7 @@ class Album(ObjectBase, GroupableMixin):
self.photodb.log.debug('Adding photo %s to %s' % (photo, self)) self.photodb.log.debug('Adding photo %s to %s' % (photo, self))
cur = self.photodb.sql.cursor() cur = self.photodb.sql.cursor()
cur.execute('INSERT INTO album_photo_rel VALUES(?, ?)', [self.id, photo.id]) cur.execute('INSERT INTO album_photo_rel VALUES(?, ?)', [self.id, photo.id])
self._uncache() self._uncache_sums()
if commit: if commit:
self.photodb.log.debug('Committing - add photo to album') self.photodb.log.debug('Committing - add photo to album')
self.photodb.commit() self.photodb.commit()
@ -369,7 +380,7 @@ class Album(ObjectBase, GroupableMixin):
) )
self.title = title self.title = title
self.description = description self.description = description
self._uncache()
if commit: if commit:
self.photodb.log.debug('Committing - edit album') self.photodb.log.debug('Committing - edit album')
self.photodb.commit() self.photodb.commit()
@ -386,11 +397,15 @@ class Album(ObjectBase, GroupableMixin):
@decorators.required_feature('album.edit') @decorators.required_feature('album.edit')
def join_group(self, *args, **kwargs): def join_group(self, *args, **kwargs):
return super().join_group(*args, **kwargs) result = super().join_group(*args, **kwargs)
return result
@decorators.required_feature('album.edit') @decorators.required_feature('album.edit')
def leave_group(self, *args, **kwargs): def leave_group(self, *args, **kwargs):
return super().leave_group(*args, **kwargs) parent = self.parent()
if parent is not None:
parent._uncache_sums()
result = super().leave_group(*args, **kwargs)
def photos(self): def photos(self):
photos = [] photos = []
@ -416,27 +431,40 @@ class Album(ObjectBase, GroupableMixin):
'DELETE FROM album_photo_rel WHERE albumid == ? AND photoid == ?', 'DELETE FROM album_photo_rel WHERE albumid == ? AND photoid == ?',
[self.id, photo.id] [self.id, photo.id]
) )
self._uncache() self._uncache_sums()
if commit: if commit:
self.photodb.log.debug('Committing - remove photo from album') self.photodb.log.debug('Committing - remove photo from album')
self.photodb.commit() self.photodb.commit()
def sum_bytes(self, recurse=True, string=False): def sum_bytes(self, recurse=True, string=False):
if self._sum_bytes_photos is None: if self._sum_bytes_local is None:
photos = (photo.bytes for photo in self.photos() if photo.bytes is not None) #print(self, 'sumbytes cache miss local')
self._sum_bytes_photos = sum(photos) photos = (photo for photo in self.photos() if photo.bytes is not None)
total = self._sum_bytes_photos self._sum_bytes_local = sum(photo.bytes for photo in photos)
total = self._sum_bytes_local
if recurse: if recurse:
if self._sum_bytes_albums is None: if self._sum_bytes_recursive is None:
self._sum_bytes_albums = sum(a.sum_bytes(recurse=True) for a in self.children()) #print(self, 'sumbytes cache miss recursive')
total += self._sum_bytes_albums child_bytes = sum(child.sum_bytes(recurse=True) for child in self.children())
self._sum_bytes_recursive = self._sum_bytes_local + child_bytes
total = self._sum_bytes_recursive
if string: if string:
return bytestring.bytestring(total) return bytestring.bytestring(total)
else: else:
return total return total
def sum_photos(self):
if self._sum_photos_recursive is None:
#print(self, 'sumphotos cache miss')
total = 0
total += sum(1 for x in self.photos())
total += sum(child.sum_photos() for child in self.children())
self._sum_photos_recursive = total
return self._sum_photos_recursive
def walk_photos(self): def walk_photos(self):
yield from self.photos() yield from self.photos()
children = self.walk_children() children = self.walk_children()

View file

@ -1387,12 +1387,9 @@ class PhotoDB(PDBAlbumMixin, PDBBookmarkMixin, PDBPhotoMixin, PDBTagMixin, PDBUs
if isinstance(thing_id, thing_map['class']): if isinstance(thing_id, thing_map['class']):
thing_id = thing_id.id thing_id = thing_id.id
cache = { cache = self.caches[thing_type]
'album': self._album_cache,
'photo': self._photo_cache,
'tag': self._tag_cache,
}[thing_type]
try: try:
#self.log.debug('Cache hit for %s %s', thing_type, thing_id)
val = cache[thing_id] val = cache[thing_id]
return val return val
except KeyError: except KeyError:

View file

@ -91,16 +91,19 @@ p
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
<p> <p>
{% if photos or sub_albums %} {% set has_local_photos = photos|length > 0 %}
{% set has_child_photos = album.sum_photos() > photos|length %}
{% if has_local_photos or has_child_photos %}
Download: Download:
{% if photos %} {% if has_local_photos %}
<a href="/album/{{album.id}}.zip?recursive=no"> <a href="/album/{{album.id}}.zip?recursive=no">
These files ({{album.sum_bytes(recurse=False, string=True)}}) These files ({{album.sum_bytes(recurse=False, string=True)}})
</a> </a>
{% endif %} {% endif %}
{% if photos and sub_albums %}&mdash;{% endif %} {% if has_local_photos and has_child_photos %}&mdash;{% endif %}
{% if sub_albums %} {% if has_child_photos %}
<a href="/album/{{album.id}}.zip?recursive=yes"> <a href="/album/{{album.id}}.zip?recursive=yes">
Include children ({{album.sum_bytes(recurse=True, string=True)}}) Include children ({{album.sum_bytes(recurse=True, string=True)}})
</a> </a>