[#199 state:fixed] Added a mtime column to PE's cache DB so that we can purge outdated caches.

This commit is contained in:
Virgil Dupras 2012-08-10 15:58:37 -04:00
parent 24771af955
commit 20320f539f
3 changed files with 42 additions and 15 deletions

View File

@ -7,6 +7,7 @@
# http://www.hardcoded.net/licenses/bsd_license # http://www.hardcoded.net/licenses/bsd_license
import os import os
import os.path as op
import logging import logging
import sqlite3 as sqlite import sqlite3 as sqlite
@ -30,7 +31,7 @@ def colors_to_string(colors):
# result.append((number >> 16, (number >> 8) & 0xff, number & 0xff)) # result.append((number >> 16, (number >> 8) & 0xff, number & 0xff))
# return result # return result
class Cache(object): class Cache:
"""A class to cache picture blocks. """A class to cache picture blocks.
""" """
def __init__(self, db=':memory:'): def __init__(self, db=':memory:'):
@ -72,29 +73,34 @@ class Cache(object):
result = self.con.execute(sql).fetchall() result = self.con.execute(sql).fetchall()
return result[0][0] return result[0][0]
def __setitem__(self, key, value): def __setitem__(self, path_str, blocks):
value = colors_to_string(value) blocks = colors_to_string(blocks)
if key in self: if op.exists(path_str):
sql = "update pictures set blocks = ? where path = ?" mtime = int(os.stat(path_str).st_mtime)
else: else:
sql = "insert into pictures(blocks,path) values(?,?)" mtime = 0
if path_str in self:
sql = "update pictures set blocks = ?, mtime = ? where path = ?"
else:
sql = "insert into pictures(blocks,mtime,path) values(?,?,?)"
try: try:
self.con.execute(sql, [value, key]) self.con.execute(sql, [blocks, mtime, path_str])
except sqlite.OperationalError: except sqlite.OperationalError:
logging.warning('Picture cache could not set value for key %r', key) logging.warning('Picture cache could not set value for key %r', path_str)
except sqlite.DatabaseError as e: except sqlite.DatabaseError as e:
logging.warning('DatabaseError while setting value for key %r: %s', key, str(e)) logging.warning('DatabaseError while setting value for key %r: %s', path_str, str(e))
def _create_con(self, second_try=False): def _create_con(self, second_try=False):
def create_tables(): def create_tables():
sql = "create table pictures(path TEXT, blocks TEXT)" logging.debug("Creating picture cache tables.")
self.con.execute(sql); self.con.execute("drop table if exists pictures");
sql = "create index idx_path on pictures (path)" self.con.execute("drop index if exists idx_path");
self.con.execute(sql) self.con.execute("create table pictures(path TEXT, mtime INTEGER, blocks TEXT)");
self.con.execute("create index idx_path on pictures (path)")
self.con = sqlite.connect(self.dbname, isolation_level=None) self.con = sqlite.connect(self.dbname, isolation_level=None)
try: try:
self.con.execute("select * from pictures where 1=2") self.con.execute("select path, mtime, blocks from pictures where 1=2")
except sqlite.OperationalError: # new db except sqlite.OperationalError: # new db
create_tables() create_tables()
except sqlite.DatabaseError as e: # corrupted db except sqlite.DatabaseError as e: # corrupted db
@ -134,3 +140,23 @@ class Cache(object):
cur = self.con.execute(sql) cur = self.con.execute(sql)
return ((rowid, string_to_colors(blocks)) for rowid, blocks in cur) return ((rowid, string_to_colors(blocks)) for rowid, blocks in cur)
def purge_outdated(self):
"""Go through the cache and purge outdated records.
A record is outdated if the picture doesn't exist or if its mtime is greater than the one in
the db.
"""
todelete = []
sql = "select rowid, path, mtime from pictures"
cur = self.con.execute(sql)
for rowid, path_str, mtime in cur:
if mtime and op.exists(path_str):
picture_mtime = os.stat(path_str).st_mtime
if int(picture_mtime) <= mtime:
# not outdated
continue
todelete.append(rowid)
if todelete:
sql = "delete from pictures where rowid in (%s)" % ','.join(map(str, todelete))
self.con.execute(sql)

View File

@ -55,6 +55,7 @@ def prepare_pictures(pictures, cache_path, with_dimensions, j=job.nulljob):
# MemoryError happens when trying to read an image file, which is freed from memory by the # MemoryError happens when trying to read an image file, which is freed from memory by the
# time that MemoryError is raised. # time that MemoryError is raised.
cache = Cache(cache_path) cache = Cache(cache_path)
cache.purge_outdated()
prepared = [] # only pictures for which there was no error getting blocks prepared = [] # only pictures for which there was no error getting blocks
try: try:
for picture in j.iter_with_progress(pictures, tr("Analyzed %d/%d pictures")): for picture in j.iter_with_progress(pictures, tr("Analyzed %d/%d pictures")):

View File

@ -141,5 +141,5 @@ class TestCaseCacheSQLEscape:
try: try:
del c["foo'bar"] del c["foo'bar"]
except KeyError: except KeyError:
self.fail() assert False