mirror of
https://github.com/planetmint/planetmint.git
synced 2025-11-24 22:45:44 +00:00
Fixes #125 - Added Connection Singleton class
This commit is contained in:
parent
109f50bec9
commit
60ae991d70
@ -42,7 +42,7 @@ This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior directed at yourself or another community member may be
|
||||
reported by contacting a project maintainer at [contact@planetmint.com](mailto:contact@planetmint.com). All
|
||||
reported by contacting a project maintainer at [mail@planetmint.io](mailto:contact@planetmint.io). All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is appropriate to the circumstances. Maintainers are
|
||||
obligated to maintain confidentiality with regard to the reporter of an
|
||||
|
||||
@ -13,4 +13,4 @@ configuration or the ``PLANETMINT_DATABASE_BACKEND`` environment variable.
|
||||
|
||||
# Include the backend interfaces
|
||||
from planetmint.backend import schema, query, convert # noqa
|
||||
from planetmint.backend.connection import connect, Connection
|
||||
from planetmint.backend.connection import Connection
|
||||
|
||||
@ -94,9 +94,8 @@ class Connection(metaclass=DBSingleton):
|
||||
from and implements this class.
|
||||
"""
|
||||
|
||||
def __init__(self, host=None, port=None, dbname=None,
|
||||
connection_timeout=None, max_tries=None,
|
||||
**kwargs):
|
||||
def __init__(self, host: str =None, port: int = None, login: str = None, password: str = None, backend: str = None,
|
||||
connection_timeout: int = None, max_tries: int = None, **kwargs):
|
||||
"""Create a new :class:`~.Connection` instance.
|
||||
Args:
|
||||
host (str): the host to connect to.
|
||||
@ -110,23 +109,102 @@ class Connection(metaclass=DBSingleton):
|
||||
**kwargs: arbitrary keyword arguments provided by the
|
||||
configuration's ``database`` settings
|
||||
"""
|
||||
|
||||
dbconf = Config().get()['database']
|
||||
|
||||
self.host = host or dbconf['host']
|
||||
self.port = port or dbconf['port']
|
||||
self.dbname = dbname or dbconf['name']
|
||||
self.connection_timeout = connection_timeout if connection_timeout is not None \
|
||||
else dbconf['connection_timeout']
|
||||
self.connection_timeout = connection_timeout if connection_timeout is not None else Config().get()["database"]
|
||||
self.max_tries = max_tries if max_tries is not None else dbconf['max_tries']
|
||||
self.max_tries_counter = range(self.max_tries) if self.max_tries != 0 else repeat(0)
|
||||
self._conn = None
|
||||
self.conn = None
|
||||
|
||||
@property
|
||||
def conn(self):
|
||||
if self._conn is None:
|
||||
self.connect()
|
||||
return self._conn
|
||||
try:
|
||||
backend = backend
|
||||
if not backend and kwargs and kwargs.get("backend"):
|
||||
backend = kwargs["backend"]
|
||||
|
||||
if backend and backend != Config().get()["database"]["backend"]:
|
||||
Config().init_config(backend)
|
||||
else:
|
||||
backend = Config().get()["database"]["backend"]
|
||||
except KeyError:
|
||||
logger.info("Backend {} not supported".format(backend))
|
||||
raise ConfigurationError
|
||||
|
||||
if(self.conn is None):
|
||||
try:
|
||||
self.connect(host=host, port=port, login=login, password=password, backend=backend, kwargs=kwargs)
|
||||
except tarantool.error.NetworkError as network_err:
|
||||
print(f"Host {host}:{port} can't be reached.\n{network_err}")
|
||||
raise network_err
|
||||
|
||||
def connect(self, host: str = None, port: int = None, login: str = None, password: str = None, backend: str = None, **kwargs):
|
||||
"""Try to connect to the database.
|
||||
Raises:
|
||||
:exc:`~ConnectionError`: If the connection to the database
|
||||
fails.
|
||||
"""
|
||||
for attempt in self.max_tries_counter:
|
||||
if (self.conn is None):
|
||||
try:
|
||||
modulepath, _, class_name = BACKENDS[backend].rpartition('.')
|
||||
Class = getattr(import_module(modulepath), class_name)
|
||||
self.conn = Class(host=host, port=port, login=login, password=password, kwargs=kwargs)
|
||||
break
|
||||
except ConnectionError as exc:
|
||||
logger.warning('Attempt %s/%s. Connection to %s:%s failed after %sms.',
|
||||
attempt, self.max_tries if self.max_tries != 0 else '∞',
|
||||
host, port, self.connection_timeout)
|
||||
if attempt == self.max_tries:
|
||||
logger.critical('Cannot connect to the Database. Giving up.')
|
||||
raise ConnectionError() from exc
|
||||
else:
|
||||
break
|
||||
return self.conn
|
||||
|
||||
def close(self):
|
||||
"""Try to close connection to database.
|
||||
Raises:
|
||||
:exc:`~ConnectionError`: If the closing connection to the database
|
||||
fails.
|
||||
"""
|
||||
for attempt in self.max_tries_counter:
|
||||
if (self.conn is not None):
|
||||
try:
|
||||
self.conn.close()
|
||||
self.conn = None
|
||||
break
|
||||
except ConnectionError as exc:
|
||||
logger.warning('Attempt %s/%s. Close Connection to %s:%s failed after %sms.',
|
||||
attempt, self.max_tries if self.max_tries != 0 else '∞',
|
||||
self.conn.host, self.conn.port, self.connection_timeout)
|
||||
if attempt == self.max_tries:
|
||||
logger.critical('Cannot close connection to the Database. Giving up.')
|
||||
raise ConnectionError() from exc
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
class DBConnection():
|
||||
|
||||
def __init__(self, host: str = None, port: int = None, login: str = None, password: str = None, **kwargs):
|
||||
"""Create a new :class:`~.DBConnection` instance.
|
||||
Args:
|
||||
host (str): the host to connect to.
|
||||
port (int): the port to connect to.
|
||||
dbname (str): the name of the database to use.
|
||||
connection_timeout (int, optional): the milliseconds to wait
|
||||
until timing out the database connection attempt.
|
||||
Defaults to 5000ms.
|
||||
max_tries (int, optional): how many tries before giving up,
|
||||
if 0 then try forever. Defaults to 3.
|
||||
**kwargs: arbitrary keyword arguments provided by the
|
||||
configuration's ``database`` settings
|
||||
"""
|
||||
dbconf = Config().get()['database']
|
||||
|
||||
self.host = host or dbconf["host"] if not kwargs.get("host") else kwargs["host"]
|
||||
self.port = port or dbconf['port'] if not kwargs.get("port") else kwargs["port"]
|
||||
self.login = login or dbconf['login'] if not kwargs.get("login") else kwargs["login"]
|
||||
self.password = password or dbconf['password'] if not kwargs.get("password") else kwargs["password"]
|
||||
|
||||
def run(self, query):
|
||||
"""Run a query.
|
||||
@ -149,18 +227,12 @@ class Connection(metaclass=DBSingleton):
|
||||
:exc:`~ConnectionError`: If the connection to the database
|
||||
fails.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
attempt = 0
|
||||
for i in self.max_tries_counter:
|
||||
attempt += 1
|
||||
try:
|
||||
self._conn = self._connect()
|
||||
except ConnectionError as exc:
|
||||
logger.warning('Attempt %s/%s. Connection to %s:%s failed after %sms.',
|
||||
attempt, self.max_tries if self.max_tries != 0 else '∞',
|
||||
self.host, self.port, self.connection_timeout)
|
||||
if attempt == self.max_tries:
|
||||
logger.critical('Cannot connect to the Database. Giving up.')
|
||||
raise ConnectionError() from exc
|
||||
else:
|
||||
break
|
||||
def close(self):
|
||||
"""Try to close connection to the database.
|
||||
Raises:
|
||||
:exc:`~ConnectionError`: If the connection to the database
|
||||
fails.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@ -13,15 +13,13 @@ from planetmint.backend.exceptions import (DuplicateKeyError,
|
||||
ConnectionError)
|
||||
from planetmint.transactions.common.exceptions import ConfigurationError
|
||||
from planetmint.utils import Lazy
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.backend.connection import DBConnection, _kwargs_parser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class LocalMongoDBConnection(Connection):
|
||||
class LocalMongoDBConnection(DBConnection):
|
||||
|
||||
def __init__(self, replicaset=None, ssl=None, login=None, password=None,
|
||||
ca_cert=None, certfile=None, keyfile=None,
|
||||
keyfile_passphrase=None, crlfile=None, **kwargs):
|
||||
def __init__(self, host: str =None, port: int = None, login: str = None, password: str = None, **kwargs):
|
||||
"""Create a new Connection instance.
|
||||
|
||||
Args:
|
||||
@ -31,16 +29,23 @@ class LocalMongoDBConnection(Connection):
|
||||
configuration's ``database`` settings
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
self.replicaset = replicaset or Config().get()['database']['replicaset']
|
||||
self.ssl = ssl if ssl is not None else Config().get()['database']['ssl']
|
||||
self.login = login or Config().get()['database']['login']
|
||||
self.password = password or Config().get()['database']['password']
|
||||
self.ca_cert = ca_cert or Config().get()['database']['ca_cert']
|
||||
self.certfile = certfile or Config().get()['database']['certfile']
|
||||
self.keyfile = keyfile or Config().get()['database']['keyfile']
|
||||
self.keyfile_passphrase = keyfile_passphrase or Config().get()['database']['keyfile_passphrase']
|
||||
self.crlfile = crlfile or Config().get()['database']['crlfile']
|
||||
super().__init__(host=host, port=port, login=login, password=password, **kwargs)
|
||||
|
||||
dbconf = Config().get()['database']
|
||||
self.dbname = _kwargs_parser(key="name", kwargs=kwargs) or dbconf['name']
|
||||
self.replicaset = _kwargs_parser(key="replicaset", kwargs=kwargs) or dbconf['replicaset']
|
||||
self.ssl = _kwargs_parser(key="ssl", kwargs=kwargs) or dbconf['ssl']
|
||||
|
||||
self.ca_cert = _kwargs_parser(key="ca_cert", kwargs=kwargs) or dbconf['ca_cert']
|
||||
self.certfile = _kwargs_parser(key="certfile", kwargs=kwargs) or dbconf['certfile']
|
||||
self.keyfile = _kwargs_parser(key="keyfile", kwargs=kwargs) or dbconf['keyfile']
|
||||
self.keyfile_passphrase = _kwargs_parser(key="keyfile_passphrase", kwargs=kwargs) or dbconf[
|
||||
'keyfile_passphrase']
|
||||
self.crlfile = _kwargs_parser(key="crlfile", kwargs=kwargs) or dbconf['crlfile']
|
||||
self.max_tries = _kwargs_parser(key="max_tries", kwargs=kwargs)
|
||||
self.connection_timeout = _kwargs_parser(key="connection_timeout", kwargs=kwargs)
|
||||
self.conn = self.connect()
|
||||
|
||||
if not self.ssl:
|
||||
self.ssl = False
|
||||
if not self.keyfile_passphrase:
|
||||
@ -77,7 +82,7 @@ class LocalMongoDBConnection(Connection):
|
||||
print(f'DETAILS: {exc.details}')
|
||||
raise OperationError from exc
|
||||
|
||||
def _connect(self):
|
||||
def connect(self):
|
||||
"""Try to connect to the database.
|
||||
|
||||
Raises:
|
||||
@ -127,11 +132,18 @@ class LocalMongoDBConnection(Connection):
|
||||
|
||||
except (pymongo.errors.ConnectionFailure,
|
||||
pymongo.errors.OperationFailure) as exc:
|
||||
logger.info('Exception in _connect(): {}'.format(exc))
|
||||
logger.info('Exception in connect(): {}'.format(exc))
|
||||
raise ConnectionError(str(exc)) from exc
|
||||
except pymongo.errors.ConfigurationError as exc:
|
||||
raise ConfigurationError from exc
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
self.conn.close()
|
||||
self.conn = None
|
||||
except Exception as exc:
|
||||
logger.info('Exception in planetmint.backend.localmongodb.close(): {}'.format(exc))
|
||||
raise ConnectionError(str(exc)) from exc
|
||||
|
||||
MONGO_OPTS = {
|
||||
'socketTimeoutMS': 20000,
|
||||
|
||||
@ -9,7 +9,7 @@ from functools import singledispatch
|
||||
import logging
|
||||
|
||||
from planetmint.config import Config
|
||||
from planetmint.backend.connection import connect
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.transactions.common.exceptions import ValidationError
|
||||
from planetmint.transactions.common.utils import (
|
||||
validate_all_values_for_key_in_obj, validate_all_values_for_key_in_list)
|
||||
@ -83,7 +83,8 @@ def init_database(connection=None, dbname=None):
|
||||
configuration.
|
||||
"""
|
||||
|
||||
connection = connection or connect()
|
||||
connection = connection or Connection().conn
|
||||
print("=========================================", connection.__class__, "=========================================================")
|
||||
dbname = dbname or Config().get()['database']['name']
|
||||
|
||||
create_database(connection, dbname)
|
||||
|
||||
@ -9,27 +9,27 @@ import tarantool
|
||||
from planetmint.config import Config
|
||||
from planetmint.transactions.common.exceptions import ConfigurationError
|
||||
from planetmint.utils import Lazy
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.backend.connection import DBConnection
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TarantoolDBConnection(Connection):
|
||||
class TarantoolDBConnection(DBConnection):
|
||||
def __init__(
|
||||
self,
|
||||
host: str = "localhost",
|
||||
port: int = 3303,
|
||||
user: str = None,
|
||||
login: str = None,
|
||||
password: str = None,
|
||||
**kwargs,
|
||||
):
|
||||
try:
|
||||
super().__init__(**kwargs)
|
||||
self.host = host
|
||||
self.port = port
|
||||
# TODO add user support later on
|
||||
self.init_path = Config().get()["database"]["init_config"]["absolute_path"]
|
||||
self.drop_path = Config().get()["database"]["drop_config"]["absolute_path"]
|
||||
super().__init__(host=host, port=port, login=login, password=password, **kwargs)
|
||||
|
||||
dbconf = Config().get()["database"]
|
||||
self.init_path = dbconf["init_config"]["absolute_path"]
|
||||
self.drop_path = dbconf["drop_config"]["absolute_path"]
|
||||
self.conn = self.connect()
|
||||
self.SPACE_NAMES = [
|
||||
"abci_chains",
|
||||
"assets",
|
||||
@ -60,9 +60,17 @@ class TarantoolDBConnection(Connection):
|
||||
f.close()
|
||||
return "".join(execute).encode()
|
||||
|
||||
def _connect(self):
|
||||
def connect(self):
|
||||
return tarantool.connect(host=self.host, port=self.port)
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
self.conn.close()
|
||||
self.conn = None
|
||||
except Exception as exc:
|
||||
logger.info('Exception in planetmint.backend.tarantool.close(): {}'.format(exc))
|
||||
raise ConnectionError(str(exc)) from exc
|
||||
|
||||
def get_space(self, space_name: str):
|
||||
return self.conn.space(space_name)
|
||||
|
||||
|
||||
@ -264,8 +264,8 @@ def run_drop(args):
|
||||
if response != 'y':
|
||||
return
|
||||
|
||||
from planetmint.backend.connection import connect
|
||||
conn = connect()
|
||||
from planetmint.backend.connection import Connection
|
||||
conn = Connection().conn
|
||||
try:
|
||||
schema.drop_database(conn)
|
||||
except DatabaseDoesNotExist:
|
||||
|
||||
@ -10,6 +10,7 @@ MongoDB.
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
from uuid import uuid4
|
||||
from planetmint.backend.connection import Connection
|
||||
|
||||
import rapidjson
|
||||
|
||||
@ -74,7 +75,7 @@ class Planetmint(object):
|
||||
self.validation = config_utils.load_validation_plugin(validationPlugin)
|
||||
else:
|
||||
self.validation = BaseValidationRules
|
||||
self.connection = connection if connection is not None else planetmint.backend.connect()
|
||||
self.connection = connection if connection is not None else Connection().conn
|
||||
|
||||
def post_transaction(self, transaction, mode):
|
||||
"""Submit a valid transaction to the mempool."""
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import pytest
|
||||
from planetmint.backend.connection import connect
|
||||
from planetmint.backend.connection import Connection
|
||||
|
||||
|
||||
#
|
||||
@ -27,5 +27,5 @@ from planetmint.backend.connection import connect
|
||||
|
||||
@pytest.fixture
|
||||
def db_conn():
|
||||
conn = connect()
|
||||
conn = Connection().conn
|
||||
return conn
|
||||
|
||||
@ -8,9 +8,9 @@ import pytest
|
||||
|
||||
def test_get_connection_raises_a_configuration_error(monkeypatch):
|
||||
from planetmint.transactions.common.exceptions import ConfigurationError
|
||||
from planetmint.backend.connection import connect
|
||||
from planetmint.backend.connection import Connection
|
||||
with pytest.raises(ConfigurationError):
|
||||
connect('localhost', '1337', 'mydb', 'password', 'msaccess')
|
||||
Connection().connect('localhost', '1337', 'mydb', 'password', 'msaccess')
|
||||
|
||||
with pytest.raises(ConfigurationError):
|
||||
# We need to force a misconfiguration here
|
||||
@ -18,4 +18,4 @@ def test_get_connection_raises_a_configuration_error(monkeypatch):
|
||||
{'catsandra':
|
||||
'planetmint.backend.meowmeow.Catsandra'})
|
||||
|
||||
connect('localhost', '1337', 'mydb', 'password', 'catsandra')
|
||||
Connection().connect('localhost', '1337', 'mydb', 'password', 'catsandra')
|
||||
|
||||
@ -14,6 +14,7 @@ import pytest
|
||||
from planetmint.config import Config
|
||||
from planetmint import ValidatorElection
|
||||
from planetmint.commands.planetmint import run_election_show
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.transactions.types.elections.election import Election
|
||||
from planetmint.lib import Block
|
||||
from planetmint.transactions.types.elections.chain_migration_election import ChainMigrationElection
|
||||
@ -93,9 +94,7 @@ def test__run_init(mocker):
|
||||
init_db_mock = mocker.patch(
|
||||
'planetmint.backend.tarantool.connection.TarantoolDBConnection.init_database')
|
||||
|
||||
from planetmint.backend.connection import connect
|
||||
|
||||
conn = connect()
|
||||
conn = Connection().conn
|
||||
conn.init_database()
|
||||
|
||||
init_db_mock.assert_called_once_with()
|
||||
|
||||
@ -18,7 +18,7 @@ import codecs
|
||||
from collections import namedtuple
|
||||
from logging import getLogger
|
||||
from logging.config import dictConfig
|
||||
from planetmint.backend.connection import connect
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.backend.tarantool.connection import TarantoolDBConnection
|
||||
|
||||
import pytest
|
||||
@ -127,7 +127,7 @@ def _setup_database(_configure_planetmint): # TODO Here is located setup databa
|
||||
|
||||
print('Initializing test db')
|
||||
dbname = Config().get()['database']['name']
|
||||
conn = connect()
|
||||
conn = Connection().conn
|
||||
|
||||
_drop_db(conn, dbname)
|
||||
schema.init_database(conn, dbname)
|
||||
@ -136,7 +136,7 @@ def _setup_database(_configure_planetmint): # TODO Here is located setup databa
|
||||
yield
|
||||
|
||||
print('Deleting `{}` database'.format(dbname))
|
||||
conn = connect()
|
||||
conn = Connection().conn
|
||||
_drop_db(conn, dbname)
|
||||
|
||||
print('Finished deleting `{}`'.format(dbname))
|
||||
@ -148,7 +148,7 @@ def _bdb(_setup_database, _configure_planetmint):
|
||||
from planetmint.models import Transaction
|
||||
from .utils import flush_db
|
||||
from planetmint.config import Config
|
||||
conn = connect()
|
||||
conn = Connection().conn
|
||||
yield
|
||||
dbname = Config().get()['database']['name']
|
||||
flush_db(conn, dbname)
|
||||
@ -389,7 +389,7 @@ def db_name(db_config):
|
||||
|
||||
@pytest.fixture
|
||||
def db_conn():
|
||||
return connect()
|
||||
return Connection().conn
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -93,7 +93,7 @@ def test_filter_unspent_outputs(b, user_pk, user_sk):
|
||||
|
||||
def test_outputs_query_key_order(b, user_pk, user_sk, user2_pk, user2_sk):
|
||||
from planetmint import backend
|
||||
from planetmint.backend.connection import connect
|
||||
from planetmint.backend.connection import Connection
|
||||
from planetmint.backend import query
|
||||
|
||||
tx1 = Create.generate([user_pk],
|
||||
@ -119,7 +119,7 @@ def test_outputs_query_key_order(b, user_pk, user_sk, user2_pk, user2_sk):
|
||||
|
||||
# clean the transaction, metdata and asset collection
|
||||
# conn = connect()
|
||||
connection = connect()
|
||||
connection = Connection().conn
|
||||
# conn.run(conn.collection('transactions').delete_many({}))
|
||||
# conn.run(conn.collection('metadata').delete_many({}))
|
||||
# conn.run(conn.collection('assets').delete_many({}))
|
||||
|
||||
@ -68,15 +68,16 @@ def test_bigchain_class_default_initialization(config):
|
||||
|
||||
def test_bigchain_class_initialization_with_parameters():
|
||||
from planetmint import Planetmint
|
||||
from planetmint.backend import connect
|
||||
from planetmint.backend import Connection
|
||||
from planetmint.validation import BaseValidationRules
|
||||
|
||||
init_db_kwargs = {
|
||||
'backend': 'localmongodb',
|
||||
'host': 'this_is_the_db_host',
|
||||
'port': 12345,
|
||||
'name': 'this_is_the_db_name',
|
||||
}
|
||||
connection = connect(**init_db_kwargs)
|
||||
connection = Connection(**init_db_kwargs).conn
|
||||
planet = Planetmint(connection=connection)
|
||||
assert planet.connection == connection
|
||||
assert planet.connection.host == init_db_kwargs['host']
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user