Test Failed
Pull Request — master (#202)
by Vinicius
08:03
created

kytos.core.db.mongo_client()   A

Complexity

Conditions 1

Size

Total Lines 38
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 29
nop 13
dl 0
loc 38
ccs 2
cts 2
cp 1
crap 1
rs 9.184
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
"""DB client."""
2
# pylint: disable=invalid-name,redefined-outer-name,too-many-arguments
3
# pylint: disable=unsubscriptable-object,inconsistent-return-statements
4
5 2
import logging
6 2
import os
7 2
import sys
8 2
import time
9 2
from typing import Optional
10
11 2
import pymongo.helpers
12 2
from pymongo import MongoClient
13 2
from pymongo.errors import AutoReconnect, OperationFailure
14
15 2
from kytos.core.exceptions import KytosDBInitException
16
17 2
LOG = logging.getLogger(__name__)
18
19
20 2
def _log_pymongo_thread_traceback() -> None:
21
    """Log pymongo thread traceback, originally it printed out to sys.stderr
22
    poluting it when handling certain asynchronous errors that can happen,
23
    with this patched function it logs to LOG.error instead."""
24 2
    if sys.stderr:
25 2
        einfo = sys.exc_info()
26 2
        try:
27 2
            LOG.error(einfo)
28
        except IOError:
29
            pass
30
        finally:
31 2
            del einfo
32
33
34 2
if hasattr(pymongo.helpers, "_handle_exception"):
35 2
    pymongo.helpers._handle_exception = _log_pymongo_thread_traceback
36
37
38 2
def mongo_client(
39
    host_seeds=os.environ.get(
40
        "MONGO_HOST_SEEDS", "mongo1:27017,mongo2:27018,mongo3:27019"
41
    ),
42
    username=os.environ.get("MONGO_USERNAME") or "invalid_user",
43
    password=os.environ.get("MONGO_PASSWORD") or "invalid_password",
44
    database=os.environ.get("MONGO_DBNAME") or "napps",
45
    connect=False,
46
    retrywrites=True,
47
    retryreads=True,
48
    readpreference="primaryPreferred",
49
    readconcernlevel="majority",
50
    maxpoolsize=int(os.environ.get("MONGO_MAX_POOLSIZE") or 300),
51
    minpoolsize=int(os.environ.get("MONGO_MIN_POOLSIZE") or 30),
52
    serverselectiontimeoutms=30000,
53
    **kwargs,
54
) -> MongoClient:
55
    """Instantiate a MongoClient instance. MongoClient is thread-safe
56
    and has connection-pooling built in.
57
58
    NApps are supposed to use the Mongo class that wraps a MongoClient. NApps
59
    might use a new MongoClient instance for exceptional cases where a NApp
60
    needs to parametrize differently.
61
    """
62 2
    return MongoClient(
63
        host_seeds.split(","),
64
        username=username,
65
        password=password,
66
        connect=connect,
67
        authsource=database,
68
        retrywrites=retrywrites,
69
        retryreads=retryreads,
70
        readpreference=readpreference,
71
        maxpoolsize=maxpoolsize,
72
        minpoolsize=minpoolsize,
73
        readconcernlevel=readconcernlevel,
74
        serverselectiontimeoutms=serverselectiontimeoutms,
75
        **kwargs,
76
    )
77
78
79 2
class Mongo:
80
    """MongoClient instance for NApps."""
81
82 2
    client = mongo_client(connect=False)
83 2
    db_name = os.environ.get("MONGO_DBNAME") or "napps"
84
85 2
    @classmethod
86 2
    def bootstrap_index(
87
        cls,
88
        collection: str,
89
        index: str,
90
        direction: int,
91
        **kwargs,
92
    ) -> Optional[str]:
93
        """Bootstrap index."""
94 2
        db = cls.client[cls.db_name]
95 2
        indexes = set()
96
97 2
        for value in db[collection].index_information().values():
98
            if "key" in value and isinstance(value["key"], list):
99
                indexes.add(value["key"][0])
100
101 2
        if (index, direction) not in indexes:
102 2
            return db[collection].create_index([(index, direction)], **kwargs)
103
104
        return None
105
106
107 2
def _mongo_conn_wait(mongo_client=mongo_client, retries=10,
108
                     timeout_ms=10000) -> None:
109
    """Try to run 'hello' command on MongoDB and wait for it with retries."""
110 2
    try:
111 2
        client = mongo_client(maxpoolsize=6, minpoolsize=3)
112 2
        LOG.info("Trying to run 'hello' command on MongoDB...")
113 2
        client.db.command("hello")
114 2
        LOG.info("Ran 'hello' command on MongoDB successfully. It's ready!")
115 2
    except (OperationFailure, AutoReconnect) as exc:
116 2
        retries -= 1
117 2
        if retries > 0:
118 2
            time.sleep(max(timeout_ms / 1000, 1))
119 2
            return _mongo_conn_wait(mongo_client, retries, timeout_ms)
120 2
        LOG.error("Maximum retries reached when waiting for MongoDB")
121 2
        raise KytosDBInitException(str(exc), exc)
122
123
124 2
def db_conn_wait(db_backend="mongodb", retries=12, timeout_ms=10000) -> None:
125
    """DB conn wait."""
126 2
    try:
127 2
        LOG.info("Starting DB connection")
128 2
        conn_wait_funcs = {"mongodb": _mongo_conn_wait}
129 2
        return conn_wait_funcs[db_backend](retries=retries,
130
                                           timeout_ms=timeout_ms)
131 2
    except KeyError:
132 2
        client_names = ",".join(list(conn_wait_funcs.keys()))
0 ignored issues
show
introduced by
The variable conn_wait_funcs does not seem to be defined for all execution paths.
Loading history...
133 2
        raise KytosDBInitException(
134
            f"DB backend '{db_backend}' isn't supported."
135
            f" Current supported databases: {client_names}"
136
        )
137