summaryrefslogtreecommitdiff
path: root/src/leap/soledad/backends/objectstore.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/soledad/backends/objectstore.py')
-rw-r--r--src/leap/soledad/backends/objectstore.py135
1 files changed, 135 insertions, 0 deletions
diff --git a/src/leap/soledad/backends/objectstore.py b/src/leap/soledad/backends/objectstore.py
new file mode 100644
index 00000000..7c5d1177
--- /dev/null
+++ b/src/leap/soledad/backends/objectstore.py
@@ -0,0 +1,135 @@
+"""
+Abstract U1DB backend to handle storage using object stores (like CouchDB, for
+example.
+
+Right now, this is only used by CouchDatabase backend, but can also be
+extended to implement OpenStack or Amazon S3 storage, for example.
+"""
+
+from u1db.backends.inmemory import (
+ InMemoryDatabase,
+ InMemorySyncTarget,
+)
+from u1db import errors
+
+
+class ObjectStoreDatabase(InMemoryDatabase):
+ """
+ A backend for storing u1db data in an object store.
+ """
+
+ @classmethod
+ def open_database(cls, url, create, document_factory=None):
+ raise NotImplementedError(cls.open_database)
+
+ def __init__(self, replica_uid=None, document_factory=None):
+ super(ObjectStoreDatabase, self).__init__(
+ replica_uid,
+ document_factory=document_factory)
+ # sync data in memory with data in object store
+ if not self._get_doc(self.U1DB_DATA_DOC_ID):
+ self._init_u1db_data()
+ self._fetch_u1db_data()
+
+ #-------------------------------------------------------------------------
+ # methods from Database
+ #-------------------------------------------------------------------------
+
+ def _set_replica_uid(self, replica_uid):
+ super(ObjectStoreDatabase, self)._set_replica_uid(replica_uid)
+ self._store_u1db_data()
+
+ def _put_doc(self, doc):
+ raise NotImplementedError(self._put_doc)
+
+ def _get_doc(self, doc):
+ raise NotImplementedError(self._get_doc)
+
+ def get_all_docs(self, include_deleted=False):
+ raise NotImplementedError(self.get_all_docs)
+
+ def delete_doc(self, doc):
+ """Mark a document as deleted."""
+ old_doc = self._get_doc(doc.doc_id, check_for_conflicts=True)
+ if old_doc is None:
+ raise errors.DocumentDoesNotExist
+ if old_doc.rev != doc.rev:
+ raise errors.RevisionConflict()
+ if old_doc.is_tombstone():
+ raise errors.DocumentAlreadyDeleted
+ if old_doc.has_conflicts:
+ raise errors.ConflictedDoc()
+ new_rev = self._allocate_doc_rev(doc.rev)
+ doc.rev = new_rev
+ doc.make_tombstone()
+ self._put_and_update_indexes(old_doc, doc)
+ return new_rev
+
+ # index-related methods
+
+ def create_index(self, index_name, *index_expressions):
+ """
+ Create an named index, which can then be queried for future lookups.
+ """
+ raise NotImplementedError(self.create_index)
+
+ def delete_index(self, index_name):
+ """Remove a named index."""
+ super(ObjectStoreDatabase, self).delete_index(index_name)
+ self._store_u1db_data()
+
+ def _replace_conflicts(self, doc, conflicts):
+ super(ObjectStoreDatabase, self)._replace_conflicts(doc, conflicts)
+ self._store_u1db_data()
+
+ def _do_set_replica_gen_and_trans_id(self, other_replica_uid,
+ other_generation,
+ other_transaction_id):
+ super(ObjectStoreDatabase, self)._do_set_replica_gen_and_trans_id(
+ other_replica_uid,
+ other_generation,
+ other_transaction_id)
+ self._store_u1db_data()
+
+ #-------------------------------------------------------------------------
+ # implemented methods from CommonBackend
+ #-------------------------------------------------------------------------
+
+ def _put_and_update_indexes(self, old_doc, doc):
+ for index in self._indexes.itervalues():
+ if old_doc is not None and not old_doc.is_tombstone():
+ index.remove_json(old_doc.doc_id, old_doc.get_json())
+ if not doc.is_tombstone():
+ index.add_json(doc.doc_id, doc.get_json())
+ trans_id = self._allocate_transaction_id()
+ self._put_doc(doc)
+ self._transaction_log.append((doc.doc_id, trans_id))
+ self._store_u1db_data()
+
+ #-------------------------------------------------------------------------
+ # methods specific for object stores
+ #-------------------------------------------------------------------------
+
+ U1DB_DATA_DOC_ID = 'u1db_data'
+
+ def _fetch_u1db_data(self):
+ """
+ Fetch u1db configuration data from backend storage.
+ """
+ NotImplementedError(self._fetch_u1db_data)
+
+ def _store_u1db_data(self):
+ """
+ Save u1db configuration data on backend storage.
+ """
+ NotImplementedError(self._store_u1db_data)
+
+ def _init_u1db_data(self):
+ """
+ Initialize u1db configuration data on backend storage.
+ """
+ NotImplementedError(self._init_u1db_data)
+
+
+class ObjectStoreSyncTarget(InMemorySyncTarget):
+ pass