Python self.cache using sqlite3 db

  • Make caching part of your class, easiest way is to use a sqlite3 db to store cache.
  • Make member variables (which you don't want to cache like the db connection, etc) private (names starts with double underscores like self.__iam_private_variable)
  • And while caching the object (self) make sure you ignore all private variables
  • Below is a simple class with self caching

import os,sys
from timeit import default_timer as timer
import concurrent.futures
import threading
from threading import Lock
from pydal import DAL, Field
import pickle class myclass: def __setupCache(self):
self.__db = DAL('sqlite://object_cache_for_myclass.sqlite',driver_args={'check_same_thread':False,'timeout': 2000})
self.__db.define_table('obj_cache',Field('cache_id',type='double',required=True,unique=True),
Field('obj',type='blob'))
self.__insert = False def __setupAttribsFromCache(self,obj):
self.print("INFO: setting up attrs from cache")
for attr in obj:
#self.print(f"reseting from cache attr {attr} to: {obj[attr]}")
setattr(self,attr,obj[attr]) def __cache_insert(self):
db = self.__db
obj = {}
for attr in vars(self):
if attr.startswith('_myclass__'):
continue
#self.print(f"setting cache attr {attr}")
obj[attr] = getattr(self,attr)
self.print(f"cache_insert() called cache_id {self.cache_id}")
db.obj_cache.insert(cache_id=self.cache_id,
obj=pickle.dumps(obj))
db.commit() def cache_evict(self,cache_id):
db = self.__db
n = db(db.obj_cache.cache_id == cache_id).delete()
self.print(f"num records removed from cache {n} for cache_id {cache_id}") def __cache_get(self,cache_id):
db = self.__db self.cache_id = cache_id # db_record = db(db.mytable.id == id).select().first()
db_record = db(db.obj_cache.cache_id == cache_id).select().first()
#self.print(f"myclass cache record: {db_record}") if db_record is None:
# no record for this cache_id in db
self.__insert = True
return None obj = pickle.loads(db_record.obj) self.print(f"myclass Cache HIT!! {cache_id}")
return obj def __init__(self,cache_id,logger_inst): self.__loggy = logger_inst
self.__MAX_THREADS = min(32, os.cpu_count() + 4)
self.__lock = Lock()
self.__executor = concurrent.futures.ThreadPoolExecutor(max_workers=self.__MAX_THREADS) # get cache obj, if obj is not None set all attrs from obj to self and return
self.__setupCache()
cache_obj = self.__cache_get(cache_id)
if cache_obj is not None:
start_sa = timer()
self.__setupAttribsFromCache(cache_obj)
self.print(f"time taken to set-attribs = {str(datetime.timedelta(seconds=timer() - start_sa))} secs")
return self.cache_id = cache_id
self.compute_intensive_results = {}
self.computeIntensiveFuncSetsMemberVars() # computes and sets values for self.compute_intensive_results def computeIntensiveFuncSetsMemberVars(self):
# some intense computation here & sets self.compute_intensive_results
pass

def print(self, msg):
if self.__loggy:
self.__loggy.debug(msg)
else:
print(msg) def __del__(self):
if self.__insert:
self.__cache_insert()
try:
self.__executor.shutdown()
except RuntimeError:
pass
try:
self.__db.close()
except:
pass
self.print("INFO: myclass destr!!")

Comments

Popular posts from this blog

Sqlite3 in Python