def __init__(self, database): log.Logger.__init__(self, database) self.database = IDatabaseDriver(database) self.serializer = json.Serializer() self.unserializer = json.PaisleyUnserializer() self.listener_id = None self.change_cb = None # rev -> doc_id self.known_revisions = container.ExpDict(self)
def __init__(self, database): log.Logger.__init__(self, database) log.LogProxy.__init__(self, database) self._database = IDatabaseDriver(database) self._serializer = json.Serializer() self._unserializer = json.PaisleyUnserializer() # listner_id -> doc_ids self._listeners = dict() self._change_cb = None # Changed to use a normal dictionary. # It will grow boundless up to the number of documents # modified by the connection. It is a kind of memory leak # done to temporarily resolve the problem of notifications # received after the expiration time due to reconnection # killing agents. self._known_revisions = {} # {DOC_ID: (REV_INDEX, REV_HASH)}
class Connection(log.Logger, log.LogProxy): '''API for agency to call against the database.''' implements(IDatabaseClient, ITimeProvider, IRevisionStore) def __init__(self, database): log.Logger.__init__(self, database) log.LogProxy.__init__(self, database) self._database = IDatabaseDriver(database) self._serializer = json.Serializer() self._unserializer = json.PaisleyUnserializer() # listner_id -> doc_ids self._listeners = dict() self._change_cb = None # Changed to use a normal dictionary. # It will grow boundless up to the number of documents # modified by the connection. It is a kind of memory leak # done to temporarily resolve the problem of notifications # received after the expiration time due to reconnection # killing agents. self._known_revisions = {} # {DOC_ID: (REV_INDEX, REV_HASH)} ### IRevisionStore ### @property def known_revisions(self): return self._known_revisions ### ITimeProvider ### def get_time(self): return time.time() ### IDatabaseClient ### def create_database(self): return self._database.create_db() def save_document(self, doc): serialized = self._serializer.convert(doc) d = self._database.save_doc(serialized, doc.doc_id) d.addCallback(self._update_id_and_rev, doc) return d def get_document(self, doc_id): d = self._database.open_doc(doc_id) d.addCallback(self._unserializer.convert) d.addCallback(self._notice_doc_revision) return d def reload_document(self, doc): assert isinstance(doc, document.Document) return self.get_document(doc.doc_id) def delete_document(self, doc): assert isinstance(doc, document.Document) d = self._database.delete_doc(doc.doc_id, doc.rev) d.addCallback(self._update_id_and_rev, doc) return d def changes_listener(self, filter_, callback, **kwargs): assert callable(callback) r = RevisionAnalytic(self, callback) d = self._database.listen_changes(filter_, r.on_change, kwargs) def set_listener_id(l_id, filter_): self._listeners[l_id] = filter_ d.addCallback(set_listener_id, filter_) return d def cancel_listener(self, doc_id): for l_id, doc_ids in self._listeners.items(): if doc_id in doc_ids: self._cancel_listener(l_id) def query_view(self, factory, **options): factory = IViewFactory(factory) d = self._database.query_view(factory, **options) d.addCallback(self._parse_view_results, factory, options) return d def disconnect(self): for l_id in self._listeners.keys(): self._cancel_listener(l_id) ### private def _cancel_listener(self, lister_id): self._database.cancel_listener(lister_id) try: del(self._listeners[lister_id]) except KeyError: self.warning('Tried to remove nonexistining listener id %r.', lister_id) def _parse_view_results(self, rows, factory, options): ''' rows here should be a list of tuples (key, value) rendered by the view ''' reduced = factory.use_reduce and options.get('reduce', True) return map(lambda row: factory.parse(row[0], row[1], reduced), rows) def _update_id_and_rev(self, resp, doc): doc.doc_id = unicode(resp.get('id', None)) doc.rev = unicode(resp.get('rev', None)) self._notice_doc_revision(doc) return doc def _notice_doc_revision(self, doc): self.log('Storing knowledge about doc rev. ID: %r, REV: %r', doc.doc_id, doc.rev) self._known_revisions[doc.doc_id] = _parse_doc_revision(doc.rev) return doc
class Connection(log.Logger): """API for agency to call against the database.""" implements(IDatabaseClient, ITimeProvider) def __init__(self, database): log.Logger.__init__(self, database) self.database = IDatabaseDriver(database) self.serializer = json.Serializer() self.unserializer = json.PaisleyUnserializer() self.listener_id = None self.change_cb = None # rev -> doc_id self.known_revisions = container.ExpDict(self) ### ITimeProvider def get_time(self): return time.time() ### IDatabaseClient def create_database(self): return self.database.create_db() def save_document(self, doc): serialized = self.serializer.convert(doc) d = self.database.save_doc(serialized, doc.doc_id) d.addCallback(self._update_id_and_rev, doc) return d def get_document(self, id): d = self.database.open_doc(id) d.addCallback(self.unserializer.convert) d.addCallback(self._notice_doc_revision) return d def reload_document(self, doc): assert isinstance(doc, document.Document) return self.get_document(doc.doc_id) def delete_document(self, doc): assert isinstance(doc, document.Document) d = self.database.delete_doc(doc.doc_id, doc.rev) d.addCallback(self._update_id_and_rev, doc) return d def changes_listener(self, doc_ids, callback): assert isinstance(doc_ids, (tuple, list)) assert callable(callback) self.change_cb = callback d = self.database.listen_changes(doc_ids, self._on_change) def set_listener_id(l_id): self.listener_id = l_id d.addCallback(set_listener_id) return d def query_view(self, factory, **options): factory = IViewFactory(factory) d = self.database.query_view(factory, **options) d.addCallback(self._parse_view_results, factory, options) return d def disconnect(self): if self.listener_id: self.database.cancel_listener(self.listener_id) self.listener_id = None self.change_cb = None ### private def _parse_view_results(self, rows, factory, options): """ rows here should be a list of tuples (key, value) rendered by the view """ reduced = factory.use_reduce and options.get("reduce", True) return map(lambda row: factory.parse(row[0], row[1], reduced), rows) def _on_change(self, doc_id, rev): self.log("Change notification received doc_id: %r, rev: %r", doc_id, rev) key = (doc_id, rev) known = self.known_revisions.get(key, False) if known: self.log("Ignoring change notification, it is ours.") elif callable(self.change_cb): self.change_cb(doc_id, rev) def _update_id_and_rev(self, resp, doc): doc.doc_id = unicode(resp.get("id", None)) doc.rev = unicode(resp.get("rev", None)) # store information about rev and doc_id in ExpDict for 1 second # so that we can ignore change callback which we trigger self._notice_doc_revision(doc) return doc def _notice_doc_revision(self, doc): self.log("Storing knowledge about doc rev. ID: %r, REV: %r", doc.doc_id, doc.rev) self.known_revisions.set((doc.doc_id, doc.rev), True, expiration=5, relative=True) return doc