- 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
Post a Comment