def tell(self, key, value, time, ttl, from_client): if value is None: # deletes cannot have a TTL ttl = None send_update = True always_send_update = False # remove no-store flag if key.endswith(FLAG_NO_STORE): key = key[:-len(FLAG_NO_STORE)] always_send_update = True try: category, subkey = key.rsplit('/', 1) except ValueError: category = 'nocat' subkey = key newcats = [category] if category in self._rewrites: newcats.extend(self._rewrites[category]) for newcat in newcats: key = newcat + '/' + subkey with self._db_lock: queue = deque([CacheEntry(None, None, None)], self.maxentries) entries = self._db.setdefault(key, queue) lastent = entries[-1] if lastent.value == value and not lastent.ttl: # not a real update send_update = False entries.append(CacheEntry(time, ttl, value)) if send_update or always_send_update: for client in self._server._connected.values(): if client is not from_client and client.is_active(): client.update(key, OP_TELL, value or '', time, ttl)
def decode(self, buf): try: ns_entry = deserialise_ns10(buf) key = ns_entry.key if ns_entry.key else None ttl = ns_entry.ttl if ns_entry.ttl != 0 else None value = ns_entry.value if ns_entry.value else None entry = CacheEntry(ns_entry.time_stamp, ttl, value) entry.expired = ns_entry.expired return key, entry except Exception as error: self.log.error('Could not decode ns10 cache entry: %s', error) return None, None
def lock(self, key, value, time, ttl): """Lock handling code, common to both subclasses.""" with self._lock_lock: entry = self._locks.get(key) # want to lock? req, client_id = value[0], value[1:] if req == OP_LOCK_LOCK: if entry and entry.value != client_id and \ (not entry.ttl or entry.time + entry.ttl >= currenttime()): # still locked by different client, deny (tell the client # the current client_id though) self.log.debug( 'lock request %s=%s, but still locked by %s', key, client_id, entry.value) return [key + OP_LOCK + entry.value + '\n'] else: # not locked, expired or locked by same client, overwrite ttl = ttl or 600 # set a maximum time to live self.log.debug('lock request %s=%s ttl %s, accepted', key, client_id, ttl) self._locks[key] = CacheEntry(time, ttl, client_id) return [key + OP_LOCK + '\n'] # want to unlock? elif req == OP_LOCK_UNLOCK: if entry and entry.value != client_id: # locked by different client, deny self.log.debug('unlock request %s=%s, but locked by %s', key, client_id, entry.value) return [key + OP_LOCK + entry.value + '\n'] else: # unlocked or locked by same client, allow self.log.debug('unlock request %s=%s, accepted', key, client_id) self._locks.pop(key, None) return [key + OP_LOCK + '\n']
def tell(self, key, value, time, ttl, from_client): # self.log.debug('updating %s %s', key, value) if value is None: # deletes cannot have a TTL ttl = None now = currenttime() if time is None: time = now store_on_disk = True if key.endswith(FLAG_NO_STORE): key = key[:-len(FLAG_NO_STORE)] store_on_disk = False with self._cat_lock: if now > self._nextmidnight: self._rollover() try: category, subkey = key.rsplit('/', 1) except ValueError: category = 'nocat' subkey = key newcats = [category] if category in self._rewrites: newcats.extend(self._rewrites[category]) for newcat in newcats: with self._cat_lock: if newcat not in self._cat: # the first item, fd, is created on demand below self._cat[newcat] = [None, threading.Lock(), {}] fd, lock, db = self._cat[newcat] update = True with lock: if subkey in db: entry = db[subkey] if entry.value == value and not entry.expired: # existing entry with the same value: update the TTL # but don't write an update to the history file entry.time = time entry.ttl = ttl update = not store_on_disk elif value is None and entry.expired: # do not delete old value, it is already expired update = not store_on_disk if update: db[subkey] = CacheEntry(time, ttl, value) if store_on_disk: if fd is None: fd = self._create_fd(newcat) self._cat[newcat][0] = fd fd.write('%s\t%s\t%s\t%s\n' % (subkey, time, ttl and '-' or (value and '+' or '-'), value or '-')) fd.flush() if update and (not ttl or time + ttl > now): key = newcat + '/' + subkey for client in self._server._connected.values(): if client is not from_client: client.update(key, OP_TELL, value or '', time, ttl)
def _read_one_storefile_v2(self, filename, fd): db = {} for line in fd: if '\x00' in line: self.log.warning('found nullbyte in store file %s', filename) continue try: subkey, time, hasttl, value = line.rstrip().split(None, 3) if hasttl == '+': # the value is valid indefinitely, so we can use it db[subkey] = CacheEntry(float(time), None, value) elif value != '-': # the value is not valid indefinitely, add it but mark as expired db[subkey] = CacheEntry(float(time), None, value) db[subkey].expired = True elif subkey in db: # implied: value == '-' # the value is already present, but now explicitly invalidated # => mark it as expired db[subkey].expired = True except Exception: self.log.warning( 'could not interpret line from ' 'cache file %s: %r', filename, line, exc=1) return db
def decode(buf): # Check for the correct file identifier identifier = buf[4:8].decode('utf-8') if identifier != file_identifier: session.log.error('Incorrect file identifier found: %s', identifier) return None, None # Convert the buffer to FB class fb_entry = CacheEntryFB.CacheEntry.GetRootAsCacheEntry(buf, 0) # Capture the default values of key, ttl and set them to None key = fb_entry.Key() if fb_entry.Key() else None ttl = fb_entry.Ttl() if fb_entry.Ttl() != 0 else None # Try to get the value if it was written try: value = fb_entry.Value() except Exception: value = None entry = NicosCacheEntry(fb_entry.Time(), ttl, value) entry.expired = bool(fb_entry.Expired()) return key, entry
def decode(self, coded): edict = self._decoder.decode(coded) entry = CacheEntry(edict['time'], edict['ttl'], edict['value']) entry.expired = edict['expired'] key = edict['key'] return key, entry