|
@@ -24,13 +24,12 @@ caching (Caching Module)
|
24
|
24
|
"""
|
25
|
25
|
__DEPENDENCIES__ = []
|
26
|
26
|
|
27
|
|
-import hashlib
|
28
|
|
-import hmac
|
29
|
27
|
import json
|
30
|
28
|
import logging
|
31
|
29
|
import os
|
32
|
30
|
import pickle
|
33
|
31
|
import sys
|
|
32
|
+import time
|
34
|
33
|
|
35
|
34
|
try:
|
36
|
35
|
from config import APP_NAME as ROOT_LOGGER_NAME
|
|
@@ -81,13 +80,20 @@ class property_cache_pickle(object):
|
81
|
80
|
"""
|
82
|
81
|
LOG_PREFIX = 'PickCache:'
|
83
|
82
|
DATA_VERSION_TAG = '_property_cache_data_version_'
|
|
83
|
+ STORAGE_VERSION_TAG = '_storage_version_'
|
84
|
84
|
UID_TAG = '_property_cache_uid_'
|
|
85
|
+ DATA_TAG = '_data_'
|
|
86
|
+ AGE_TAG = '_age_'
|
|
87
|
+ #
|
|
88
|
+ STORAGE_VERSION = 1
|
85
|
89
|
|
86
|
|
- def __init__(self, source_instance, cache_filename, load_all_on_init=False, callback_on_data_storage=None):
|
|
90
|
+ def __init__(self, source_instance, cache_filename, load_all_on_init=False, callback_on_data_storage=None, max_age=None, store_none_value=False):
|
87
|
91
|
self._source_instance = source_instance
|
88
|
92
|
self._cache_filename = cache_filename
|
89
|
93
|
self._load_all_on_init = load_all_on_init
|
90
|
94
|
self._callback_on_data_storage = callback_on_data_storage
|
|
95
|
+ self._max_age = max_age
|
|
96
|
+ self._store_none_value = store_none_value
|
91
|
97
|
self._cached_props = None
|
92
|
98
|
|
93
|
99
|
def get(self, key, default=None):
|
|
@@ -101,14 +107,27 @@ class property_cache_pickle(object):
|
101
|
107
|
if key in self.keys():
|
102
|
108
|
if self._cached_props is None:
|
103
|
109
|
self._init_cache()
|
104
|
|
- if self._key_filter(key) not in self._cached_props:
|
|
110
|
+ if self._max_age is None:
|
|
111
|
+ cache_old = False
|
|
112
|
+ else:
|
|
113
|
+ cache_old = time.time() - self._cached_props[self.AGE_TAG].get(self._key_filter(key), 0) > self._max_age
|
|
114
|
+ if cache_old:
|
|
115
|
+ logger.debug("The cached value is old, cached value will be ignored")
|
|
116
|
+ if self._key_filter(key) not in self._cached_props[self.DATA_TAG] or cache_old:
|
105
|
117
|
val = self._source_instance.get(key, None)
|
106
|
118
|
logger.debug("%s Loading property for '%s' from source instance (%s)", self.LOG_PREFIX, key, repr(val))
|
107
|
|
- self._cached_props[self._key_filter(key)] = val
|
108
|
|
- self._save_cache()
|
|
119
|
+ if val or self._store_none_value:
|
|
120
|
+ tm = int(time.time())
|
|
121
|
+ logger.debug("Storing value=%s with timestamp=%d to chache", val, tm)
|
|
122
|
+ self._cached_props[self.DATA_TAG][self._key_filter(key)] = val
|
|
123
|
+ self._cached_props[self.AGE_TAG][self._key_filter(key)] = tm
|
|
124
|
+ self._save_cache()
|
|
125
|
+ else:
|
|
126
|
+ return val
|
109
|
127
|
else:
|
110
|
|
- logger.debug("%s Providing property for '%s' from cache (%s)", self.LOG_PREFIX, key, repr(self._cached_props.get(self._key_filter(key), default)))
|
111
|
|
- return self._cached_props.get(self._key_filter(key), default)
|
|
128
|
+ logger.debug("%s Providing property for '%s' from cache (%s)", self.LOG_PREFIX,
|
|
129
|
+ key, repr(self._cached_props[self.DATA_TAG].get(self._key_filter(key), default)))
|
|
130
|
+ return self._cached_props[self.DATA_TAG].get(self._key_filter(key), default)
|
112
|
131
|
else:
|
113
|
132
|
logger.info("%s Key '%s' is not in cached_keys. Uncached data will be returned.", self.LOG_PREFIX, key)
|
114
|
133
|
return self._source_instance.get(key, default)
|
|
@@ -125,17 +144,31 @@ class property_cache_pickle(object):
|
125
|
144
|
else:
|
126
|
145
|
return self._cached_props.get(self.DATA_VERSION_TAG, None)
|
127
|
146
|
|
|
147
|
+ def _storage_version(self):
|
|
148
|
+ if self._cached_props is None:
|
|
149
|
+ return None
|
|
150
|
+ else:
|
|
151
|
+ return self._cached_props.get(self.STORAGE_VERSION_TAG, None)
|
|
152
|
+
|
128
|
153
|
def _init_cache(self):
|
129
|
|
- if not self._load_cache() or self._source_instance.uid() != self._uid() or self._source_instance.data_version() > self._data_version():
|
130
|
|
- if self._uid() is not None and self._source_instance.uid() != self._uid():
|
|
154
|
+ load_cache = self._load_cache()
|
|
155
|
+ uid = self._source_instance.uid() != self._uid()
|
|
156
|
+ data_version = self._source_instance.data_version() > self._data_version()
|
|
157
|
+ storage_version = self._storage_version() != self.STORAGE_VERSION
|
|
158
|
+ #
|
|
159
|
+ if not load_cache or uid or data_version or storage_version:
|
|
160
|
+ if self._uid() is not None and uid:
|
131
|
161
|
logger.debug("%s Source uid changed, ignoring previous cache data", self.LOG_PREFIX)
|
132
|
|
- if self._data_version() is not None and self._source_instance.data_version() > self._data_version():
|
|
162
|
+ if self._data_version() is not None and data_version:
|
133
|
163
|
logger.debug("%s Data version increased, ignoring previous cache data", self.LOG_PREFIX)
|
134
|
|
- self._cached_props = dict()
|
|
164
|
+ if storage_version:
|
|
165
|
+ logger.debug("%s Storage version changed, ignoring previous cache data", self.LOG_PREFIX)
|
|
166
|
+ self._cached_props = {self.AGE_TAG: {}, self.DATA_TAG: {}}
|
135
|
167
|
if self._load_all_on_init:
|
136
|
168
|
self._load_source()
|
137
|
169
|
self._cached_props[self.UID_TAG] = self._source_instance.uid()
|
138
|
170
|
self._cached_props[self.DATA_VERSION_TAG] = self._source_instance.data_version()
|
|
171
|
+ self._cached_props[self.STORAGE_VERSION_TAG] = self.STORAGE_VERSION
|
139
|
172
|
self._save_cache()
|
140
|
173
|
|
141
|
174
|
def _load_cache(self):
|
|
@@ -149,13 +182,6 @@ class property_cache_pickle(object):
|
149
|
182
|
return False
|
150
|
183
|
|
151
|
184
|
def _key_filter(self, key):
|
152
|
|
- if sys.version_info >= (3, 0):
|
153
|
|
- tps = [str]
|
154
|
|
- else:
|
155
|
|
- tps = [str, unicode]
|
156
|
|
- if type(key) in tps:
|
157
|
|
- if key.endswith(self.DATA_VERSION_TAG) or key.endswith(self.UID_TAG):
|
158
|
|
- return '_' + key
|
159
|
185
|
return key
|
160
|
186
|
|
161
|
187
|
def _load_source(self):
|