From 9830bd00964a1c83cfc92151446adda6f14fbc54 Mon Sep 17 00:00:00 2001 From: Victor Shyba Date: Tue, 20 Oct 2015 15:31:09 -0300 Subject: [refactor] CouchDocument is now ServerDocument As SoledadBackend is intended to be database agnostic, a new generic document is now used instead of the old one made for CouchDB. The only attribute that really relates to couch was couch_rev, removed on this commit as it can be set on CouchDatabase implementation when needed. --- common/src/leap/soledad/common/backend.py | 43 ++++++++++------------ common/src/leap/soledad/common/couch/__init__.py | 24 ++++++------ common/src/leap/soledad/common/document.py | 25 ++++--------- common/src/leap/soledad/common/tests/test_couch.py | 6 +-- scripts/profiling/mail/mx.py | 5 ++- 5 files changed, 44 insertions(+), 59 deletions(-) diff --git a/common/src/leap/soledad/common/backend.py b/common/src/leap/soledad/common/backend.py index cb37b4ac..1cbf3d57 100644 --- a/common/src/leap/soledad/common/backend.py +++ b/common/src/leap/soledad/common/backend.py @@ -29,7 +29,7 @@ from u1db.errors import ( ) from u1db.backends import CommonBackend from u1db.backends import CommonSyncTarget -from leap.soledad.common.document import CouchDocument +from leap.soledad.common.document import ServerDocument class SoledadBackend(CommonBackend): @@ -48,7 +48,7 @@ class SoledadBackend(CommonBackend): :type replica_uid: str """ # save params - self._factory = CouchDocument + self._factory = ServerDocument self._real_replica_uid = None self._cache = None self._dbname = database._dbname @@ -83,7 +83,7 @@ class SoledadBackend(CommonBackend): def delete_database(self): """ - Delete a U1DB CouchDB database. + Delete a U1DB database. """ self._database.delete_database() @@ -221,7 +221,7 @@ class SoledadBackend(CommonBackend): :type check_for_conflicts: bool :return: The document. - :rtype: CouchDocument + :rtype: ServerDocument """ return self._database._get_doc(doc_id, check_for_conflicts) @@ -237,7 +237,7 @@ class SoledadBackend(CommonBackend): :type include_deleted: bool :return: A document object. - :rtype: CouchDocument. + :rtype: ServerDocument. """ doc = self._get_doc(doc_id, check_for_conflicts=True) if doc is None: @@ -255,25 +255,25 @@ class SoledadBackend(CommonBackend): documents will not be included in the results. :type include_deleted: bool - :return: (generation, [CouchDocument]) + :return: (generation, [ServerDocument]) The current generation of the database, followed by a list of all the documents in the database. - :rtype: (int, [CouchDocument]) + :rtype: (int, [ServerDocument]) """ return self._database.get_all_docs(include_deleted) def _put_doc(self, old_doc, doc): """ - Put the document in the Couch backend database. + Put the document in the backend database. Note that C{old_doc} must have been fetched with the parameter C{check_for_conflicts} equal to True, so we can properly update the new document using the conflict information from the old one. :param old_doc: The old document version. - :type old_doc: CouchDocument + :type old_doc: ServerDocument :param doc: The document to be put. - :type doc: CouchDocument + :type doc: ServerDocument """ last_transaction =\ self._database.save_document(old_doc, doc, @@ -348,7 +348,7 @@ class SoledadBackend(CommonBackend): This will also set doc.content to None. :param doc: The document to mark as deleted. - :type doc: CouchDocument. + :type doc: ServerDocument. :raise DocumentDoesNotExist: Raised if the document does not exist. @@ -372,20 +372,17 @@ class SoledadBackend(CommonBackend): self._put_doc(old_doc, doc) return new_rev - def get_doc_conflicts(self, doc_id, couch_rev=None): + def get_doc_conflicts(self, doc_id): """ Get the conflicted versions of a document. - If the C{couch_rev} parameter is not None, conflicts for a specific - document's couch revision are returned. - - :param couch_rev: The couch document revision. - :type couch_rev: str + :param doc_id: The document id. + :type doc_id: str :return: A list of conflicted versions of the document. :rtype: list """ - return self._database.get_doc_conflicts(doc_id, couch_rev) + return self._database.get_doc_conflicts(doc_id) def _get_replica_gen_and_trans_id(self, other_replica_uid): """ @@ -478,7 +475,7 @@ class SoledadBackend(CommonBackend): Add a conflict and force a document put. :param doc: The document to be put. - :type doc: CouchDocument + :type doc: ServerDocument """ my_doc = self._get_doc(doc.doc_id) self._prune_conflicts(doc, vectorclock.VectorClockRev(doc.rev)) @@ -499,7 +496,7 @@ class SoledadBackend(CommonBackend): the time you GET_DOC_CONFLICTS until the point where you RESOLVE) :param doc: A Document with the new content to be inserted. - :type doc: CouchDocument + :type doc: ServerDocument :param conflicted_doc_revs: A list of revisions that the new content supersedes. :type conflicted_doc_revs: [str] @@ -564,7 +561,7 @@ class SoledadBackend(CommonBackend): remote copy is never updated again.) :param doc: A document object - :type doc: CouchDocument + :type doc: ServerDocument :param save_conflict: If this document is a conflict, do you want to save it as a conflict, or just ignore it. :type save_conflict: bool @@ -596,7 +593,7 @@ class SoledadBackend(CommonBackend): 'converged', at_gen is the insertion/current generation. :rtype: (str, int) """ - if not isinstance(doc, CouchDocument): + if not isinstance(doc, ServerDocument): doc = self._factory(doc.doc_id, doc.rev, doc.get_json()) my_doc = self._get_doc(doc.doc_id, check_for_conflicts=True) if my_doc: @@ -636,7 +633,7 @@ class SoledadBackend(CommonBackend): Originally in u1db.CommonBackend :param doc: The document to have conflicts pruned. - :type doc: CouchDocument + :type doc: ServerDocument :param doc_vcr: A vector clock representing the current document's revision. :type doc_vcr: u1db.vectorclock.VectorClock diff --git a/common/src/leap/soledad/common/couch/__init__.py b/common/src/leap/soledad/common/couch/__init__.py index 727f033f..1780cecd 100644 --- a/common/src/leap/soledad/common/couch/__init__.py +++ b/common/src/leap/soledad/common/couch/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# couch.py +# __init__.py # Copyright (C) 2013 LEAP # # This program is free software: you can redistribute it and/or modify @@ -54,7 +54,7 @@ from leap.soledad.common import ddocs from leap.soledad.common.errors import raise_server_error from leap.soledad.common.errors import raise_missing_design_doc_error from leap.soledad.common.errors import InvalidURLError -from leap.soledad.common.document import CouchDocument +from leap.soledad.common.document import ServerDocument from leap.soledad.common.backend import SoledadBackend @@ -78,8 +78,8 @@ def list_users_dbs(couch_url): return users -# monkey-patch the u1db http app to use CouchDocument -http_app.Document = CouchDocument +# monkey-patch the u1db http app to use ServerDocument +http_app.Document = ServerDocument @contextmanager @@ -254,10 +254,10 @@ class CouchDatabase(object): documents will not be included in the results. :type include_deleted: bool - :return: (generation, [CouchDocument]) + :return: (generation, [ServerDocument]) The current generation of the database, followed by a list of all the documents in the database. - :rtype: (int, [CouchDocument]) + :rtype: (int, [ServerDocument]) """ generation, _ = self._get_generation_info() @@ -316,7 +316,7 @@ class CouchDatabase(object): :type check_for_conflicts: bool :return: The document. - :rtype: CouchDocument + :rtype: ServerDocument """ # get document with all attachments (u1db content and eventual # conflicts) @@ -333,7 +333,7 @@ class CouchDatabase(object): # restrict to u1db documents if 'u1db_rev' not in result: return None - doc = CouchDocument(doc_id, result['u1db_rev']) + doc = ServerDocument(doc_id, result['u1db_rev']) # set contents or make tombstone if '_attachments' not in result \ or 'u1db_content' not in result['_attachments']: @@ -368,7 +368,7 @@ class CouchDatabase(object): """ conflicts = [] for doc_rev, content in attached_conflicts: - doc = CouchDocument(doc_id, doc_rev) + doc = ServerDocument(doc_id, doc_rev) if content is None: doc.make_tombstone() else: @@ -652,9 +652,9 @@ class CouchDatabase(object): new document using the conflict information from the old one. :param old_doc: The old document version. - :type old_doc: CouchDocument + :type old_doc: ServerDocument :param doc: The document to be put. - :type doc: CouchDocument + :type doc: ServerDocument :raise RevisionConflict: Raised when trying to update a document but couch revisions mismatch. @@ -712,7 +712,7 @@ class CouchDatabase(object): '_attachments': attachments, } # if we are updating a doc we have to add the couch doc revision - if old_doc is not None: + if old_doc is not None and hasattr(old_doc, 'couch_rev'): couch_doc['_rev'] = old_doc.couch_rev # prepare the multipart PUT buf = StringIO() diff --git a/common/src/leap/soledad/common/document.py b/common/src/leap/soledad/common/document.py index 05458906..20510544 100644 --- a/common/src/leap/soledad/common/document.py +++ b/common/src/leap/soledad/common/document.py @@ -110,18 +110,17 @@ class SoledadDocument(Document): doc="Wrapper to ensure `doc.rev` is always returned as bytes.") -class CouchDocument(SoledadDocument): +class ServerDocument(SoledadDocument): """ - This is the document used for maintaining the Couch backend. + This is the document used by server to hold conflicts and transactions + on a database. - A CouchDocument can fetch and manipulate conflicts and also holds a - reference to the couch document revision. This data is used to ensure an - atomic and consistent update of the database. + The goal is to ensure an atomic and consistent update of the database. """ def __init__(self, doc_id=None, rev=None, json='{}', has_conflicts=False): """ - Container for handling a document that is stored in couch backend. + Container for handling a document that stored on server. :param doc_id: The unique document identifier. :type doc_id: str @@ -133,7 +132,6 @@ class CouchDocument(SoledadDocument): :type has_conflicts: bool """ SoledadDocument.__init__(self, doc_id, rev, json, has_conflicts) - self.couch_rev = None self.transactions = None self._conflicts = None @@ -142,7 +140,7 @@ class CouchDocument(SoledadDocument): Get the conflicted versions of the document. :return: The conflicted versions of the document. - :rtype: [CouchDocument] + :rtype: [ServerDocument] """ return self._conflicts or [] @@ -161,7 +159,7 @@ class CouchDocument(SoledadDocument): Add a conflict to this document. :param doc: The conflicted version to be added. - :type doc: CouchDocument + :type doc: Document """ if self._conflicts is None: raise Exception("Fetch conflicts first!") @@ -181,12 +179,3 @@ class CouchDocument(SoledadDocument): lambda doc: doc.rev not in conflict_revs, self._conflicts) self.has_conflicts = len(self._conflicts) > 0 - - def update(self, new_doc): - # update info - self.rev = new_doc.rev - if new_doc.is_tombstone(): - self.is_tombstone() - else: - self.content = new_doc.content - self.has_conflicts = new_doc.has_conflicts diff --git a/common/src/leap/soledad/common/tests/test_couch.py b/common/src/leap/soledad/common/tests/test_couch.py index 1634d30c..01f0587b 100644 --- a/common/src/leap/soledad/common/tests/test_couch.py +++ b/common/src/leap/soledad/common/tests/test_couch.py @@ -36,7 +36,7 @@ from u1db import SyncTarget from u1db import vectorclock from leap.soledad.common import couch -from leap.soledad.common import backend +from leap.soledad.common.document import ServerDocument from leap.soledad.common import errors from leap.soledad.common.tests import u1db_tests as tests @@ -47,8 +47,6 @@ from leap.soledad.common.tests.util import sync_via_synchronizer from leap.soledad.common.tests.u1db_tests import test_backends from leap.soledad.common.tests.u1db_tests import DatabaseBaseTests -from u1db.backends.inmemory import InMemoryIndex - # ----------------------------------------------------------------------------- # The following tests come from `u1db.tests.test_common_backend`. @@ -134,7 +132,7 @@ def copy_couch_database_for_test(test, db): def make_document_for_test(test, doc_id, rev, content, has_conflicts=False): - return couch.CouchDocument( + return ServerDocument( doc_id, rev, content, has_conflicts=has_conflicts) diff --git a/scripts/profiling/mail/mx.py b/scripts/profiling/mail/mx.py index b6a1e5cf..55084a40 100644 --- a/scripts/profiling/mail/mx.py +++ b/scripts/profiling/mail/mx.py @@ -5,7 +5,7 @@ import timeit from leap.keymanager import openpgp -from leap.soledad.common.couch import CouchDocument +from leap.soledad.common.document import ServerDocument from leap.soledad.common.crypto import ( EncryptionSchemes, ENC_JSON_KEY, @@ -47,7 +47,7 @@ def get_enc_json(pubkey, message): def get_new_doc(enc_json): - doc = CouchDocument(doc_id=str(uuid.uuid4())) + doc = ServerDocument(doc_id=str(uuid.uuid4())) doc.content = { 'incoming': True, ENC_SCHEME_KEY: EncryptionSchemes.PUBKEY, @@ -71,6 +71,7 @@ def put_lots_of_messages(db, number): log("Populating database with %d encrypted messages... " % number, line_break=False) pubkey = get_pubkey() + def _put_one_message(): put_one_message(pubkey, db) time = timeit.timeit(_put_one_message, number=number) -- cgit v1.2.3