From 7a6d913fed8fc8332174c7ac5c8a4a5472a478c3 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 19 Dec 2013 21:52:12 -0400 Subject: Do not instantiate the synchronizer each time. This has the nice effect of letting the persistent-connection reuse the existing connection, avoiding the ssl handshake overhead each time we try to synchronize. This can be traced by logging the instantiation of HttpClientBase in u1db.remote I *think* we should be fine with the timeouts as long as we keep the sync period along the 1 min we are doing now. For other cases, we should look into how to override the default timeout in httplib (used by u1db http_client). --- client/changes/bug_reuse-http-connection | 2 ++ client/src/leap/soledad/client/sqlcipher.py | 43 ++++++++++++++++++++++------- client/src/leap/soledad/client/target.py | 1 + 3 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 client/changes/bug_reuse-http-connection diff --git a/client/changes/bug_reuse-http-connection b/client/changes/bug_reuse-http-connection new file mode 100644 index 00000000..c6cdd9b4 --- /dev/null +++ b/client/changes/bug_reuse-http-connection @@ -0,0 +1,2 @@ + o Fix a bug in soledad.client.sqlcipher by which we were creating + a new connection for each sync. diff --git a/client/src/leap/soledad/client/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py index c7aebbe6..5695bf82 100644 --- a/client/src/leap/soledad/client/sqlcipher.py +++ b/client/src/leap/soledad/client/sqlcipher.py @@ -49,10 +49,12 @@ import time import string import threading -from u1db.backends import sqlite_backend -from u1db import errors from pysqlcipher import dbapi2 +from u1db.backends import sqlite_backend +from u1db.sync import Synchronizer from u1db import errors as u1db_errors + +from leap.soledad.client.target import SoledadSyncTarget from leap.soledad.common.document import SoledadDocument logger = logging.getLogger(__name__) @@ -144,6 +146,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): _index_storage_value = 'expand referenced encrypted' k_lock = threading.Lock() + _syncer = None def __init__(self, sqlcipher_file, password, document_factory=None, crypto=None, raw_key=False, cipher='aes-256-cbc', @@ -336,13 +339,33 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): :return: The local generation before the synchronisation was performed. :rtype: int """ - from u1db.sync import Synchronizer - from leap.soledad.client.target import SoledadSyncTarget - return Synchronizer( - self, - SoledadSyncTarget(url, - creds=creds, - crypto=self._crypto)).sync(autocreate=autocreate) + if not self.syncer: + self._create_syncer(url, creds=creds) + return self.syncer.sync(autocreate=autocreate) + + @property + def syncer(self): + """ + Accesor for synchronizer. + """ + return self._syncer + + def _create_syncer(self, url, creds=None): + """ + Creates a synchronizer + + :param url: The url of the target replica to sync with. + :type url: str + :param creds: optional dictionary giving credentials. + to authorize the operation with the server. + :type creds: dict + """ + if self._syncer is None: + self._syncer = Synchronizer( + self, + SoledadSyncTarget(url, + creds=creds, + crypto=self._crypto)) def _extra_schema_init(self, c): """ @@ -719,7 +742,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): definition = self._get_index_definition(index_name) if len(key_values) != len(definition): - raise errors.InvalidValueForIndex() + raise u1db_errors.InvalidValueForIndex() tables = ["document_fields d%d" % i for i in range(len(definition))] novalue_where = ["d.doc_id = d%d.doc_id" " AND d%d.field_name = ?" diff --git a/client/src/leap/soledad/client/target.py b/client/src/leap/soledad/client/target.py index 73f719fb..3b3d6870 100644 --- a/client/src/leap/soledad/client/target.py +++ b/client/src/leap/soledad/client/target.py @@ -63,6 +63,7 @@ logger = logging.getLogger(__name__) # Exceptions # + class DocumentNotEncrypted(Exception): """ Raised for failures in document encryption. -- cgit v1.2.3 From 3a38c1b92e6f1e538f682e4e0f7e99a18f41d7d0 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 19 Dec 2013 21:54:25 -0400 Subject: Set soledad timeout default to 2 min --- client/src/leap/soledad/client/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/leap/soledad/client/__init__.py b/client/src/leap/soledad/client/__init__.py index 61337680..a0b3f45a 100644 --- a/client/src/leap/soledad/client/__init__.py +++ b/client/src/leap/soledad/client/__init__.py @@ -1283,7 +1283,7 @@ class Soledad(object): #----------------------------------------------------------------------------- # We need a more reasonable timeout (in seconds) -SOLEDAD_TIMEOUT = 10 +SOLEDAD_TIMEOUT = 120 class VerifiedHTTPSConnection(httplib.HTTPSConnection): -- cgit v1.2.3 From 01557be5c80833b27df46f4eab17b29c53a7245b Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Fri, 20 Dec 2013 11:33:29 -0400 Subject: catch cannotsendrequest exception --- client/src/leap/soledad/client/sqlcipher.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/client/src/leap/soledad/client/sqlcipher.py b/client/src/leap/soledad/client/sqlcipher.py index 5695bf82..43c871c3 100644 --- a/client/src/leap/soledad/client/sqlcipher.py +++ b/client/src/leap/soledad/client/sqlcipher.py @@ -43,11 +43,12 @@ So, as the statements above were introduced for backwards compatibility with SLCipher 1.1 databases, we do not implement them as all SQLCipher databases handled by Soledad should be created by SQLCipher >= 2.0. """ +import httplib import logging import os -import time import string import threading +import time from pysqlcipher import dbapi2 from u1db.backends import sqlite_backend @@ -341,7 +342,20 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): """ if not self.syncer: self._create_syncer(url, creds=creds) - return self.syncer.sync(autocreate=autocreate) + + try: + res = self.syncer.sync(autocreate=autocreate) + except httplib.CannotSendRequest: + # raised when you reuse httplib.HTTP object for new request + # while you havn't called its getresponse() + # this catch works for the current connclass used + # by our HTTPClientBase, since it uses httplib. + # we will have to replace it if it changes. + logger.info("Replacing connection and trying again...") + self._syncer = None + self._create_syncer(url, creds=creds) + res = self.syncer.sync(autocreate=autocreate) + return res @property def syncer(self): -- cgit v1.2.3