In a service I'm developing I'm using three cloud services (five if you count additional two streaming services I have to create that are used by those cloud services in turn) and to orchestrate bits and pieces incoming I need a cache for objects relating data from all those services. That cache has to be is safe to access from various Tornado handlers in an atomic manner.
Now, since Tornado is based on I/O loop, theoretically I do not need to use locks or some other synchronization primitives... but that's obviously relying on implementation detail (GIL) and it can reportedly cause problems under some circumstances anyway, so to be rather safe than sorry I try to develop the cache that is all:
- Singleton
- Thread-safe
- Creates a default object available under a key
I'm approaching the subject with some trepidation as so far I have not done such stuff much. This is what I've come up with so far:
UPDATE
I've realized I had a stupid mistake in the previous implementation: the setdefault
method called default_factory
every time. Updated version works but it's kinda ugly:
class ThreadSafeSingletonCache(object):
__me = None
__cache = None
__lock = threading.Lock()
def __new__(cls, _):
if cls.__me is None:
cls.__cache = {}
cls.__me = super(ThreadSafeSingletonCache, cls).__new__(cls)
return cls.__me
def __init__(self, default_factory):
self.default_factory = default_factory
def obj_setattr(self, cache_key, obj_attr, value):
with self.__lock:
item = self.__like_setdefault(cache_key)
setattr(item, obj_attr, value)
def __like_setdefault(self, cache_key):
''' Unfortunately I cannot use .setdefault as default_factory function gets called on each setdefault use'''
item = self.__cache.get(cache_key)
if not item:
item = self.default_factory()
self.__cache[cache_key] = item
return item
def get(self, cache_key):
with self.__lock:
return self.__like_setdefault(cache_key)
def set(self, cache_key, obj):
with self.__lock:
self.__cache[cache_key] = obj
Please point out problems and potential improvements.