mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00
Initialize replica set for mongodb when running bigchaindb init
This commit is contained in:
parent
83d37d699f
commit
4a14c7c8f1
@ -1,8 +1,10 @@
|
||||
"""Utils to initialize and drop the database."""
|
||||
|
||||
import time
|
||||
import logging
|
||||
|
||||
from pymongo import ASCENDING, DESCENDING
|
||||
from pymongo import errors
|
||||
|
||||
from bigchaindb import backend
|
||||
from bigchaindb.common import exceptions
|
||||
@ -24,6 +26,9 @@ def create_database(conn, dbname):
|
||||
# TODO: read and write concerns can be declared here
|
||||
conn.conn.get_database(dbname)
|
||||
|
||||
# initialize the replica set
|
||||
initialize_replica_set(conn)
|
||||
|
||||
|
||||
@register_schema(MongoDBConnection)
|
||||
def create_tables(conn, dbname):
|
||||
@ -85,3 +90,68 @@ def create_votes_secondary_index(conn, dbname):
|
||||
('node_pubkey',
|
||||
ASCENDING)],
|
||||
name='block_and_voter')
|
||||
|
||||
|
||||
def initialize_replica_set(conn):
|
||||
"""Initialize a replica set. If already initialized skip."""
|
||||
replica_set_name = _get_replica_set_name(conn)
|
||||
config = {'_id': replica_set_name,
|
||||
'members': [{'_id': 0, 'host': 'localhost:27017'}]}
|
||||
|
||||
try:
|
||||
conn.conn.admin.command('replSetInitiate', config)
|
||||
except errors.OperationFailure as exc_info:
|
||||
if exc_info.details['codeName'] == 'AlreadyInitialized':
|
||||
logger.info('Replica set already initialized')
|
||||
return
|
||||
else:
|
||||
raise
|
||||
|
||||
_wait_for_replica_set_initialization(conn)
|
||||
logger.info('Initialized replica set')
|
||||
|
||||
|
||||
def _get_replica_set_name(conn):
|
||||
"""Checks if the replSet option was enabled either through the command
|
||||
line option or config file.
|
||||
|
||||
Note:
|
||||
The setting we are looking for will have a different name depending
|
||||
if it was set by the config file (`replSetName`) or by command
|
||||
line arguments (`replSet`).
|
||||
|
||||
Returns:
|
||||
The replica set name if enabled.
|
||||
|
||||
Raise:
|
||||
:exc:`~ConfigurationError`: If mongod was not started with the
|
||||
replSet option.
|
||||
"""
|
||||
options = conn.conn.admin.command('getCmdLineOpts')
|
||||
try:
|
||||
repl_opts = options['parsed']['replication']
|
||||
return repl_opts.get('replSetName', None) or repl_opts['replSet']
|
||||
except KeyError:
|
||||
raise exceptions.ConfigurationError('mongod was not started with'
|
||||
' the replSet option.')
|
||||
|
||||
|
||||
def _wait_for_replica_set_initialization(conn):
|
||||
"""Wait for a replica set to finish initialization.
|
||||
|
||||
If a replica set is being initialized for the first time it takes some
|
||||
time. Nodes need to discover each other and an election needs to take
|
||||
place. During this time the database is not writable so we need to wait
|
||||
before continuing with the rest of the initialization
|
||||
"""
|
||||
|
||||
# I did not find a better way to do this for now.
|
||||
# To check if the database is ready we will poll the mongodb logs until
|
||||
# we find the line that says the database is ready
|
||||
logger.info('Waiting for mongodb replica set initialization')
|
||||
while True:
|
||||
logs = conn.conn.admin.command('getLog', 'rs')['log']
|
||||
for line in logs:
|
||||
if 'database writes are now permitted' in line:
|
||||
return
|
||||
time.sleep(0.1)
|
||||
|
@ -1,4 +1,5 @@
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('setup_database')
|
||||
@ -107,3 +108,37 @@ def test_drop():
|
||||
|
||||
schema.drop_database(conn, dbname)
|
||||
assert dbname not in conn.conn.database_names()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('setup_database')
|
||||
def test_get_replica_set_name():
|
||||
from pymongo.database import Database
|
||||
from bigchaindb import backend
|
||||
from bigchaindb.backend.mongodb.schema import _get_replica_set_name
|
||||
from bigchaindb.common.exceptions import ConfigurationError
|
||||
|
||||
conn = backend.connect()
|
||||
|
||||
# no replSet option set
|
||||
cmd_line_opts = {'argv': ['mongod', '--dbpath=/data'],
|
||||
'ok': 1.0,
|
||||
'parsed': {'storage': {'dbPath': '/data'}}}
|
||||
with patch.object(Database, 'command', return_value=cmd_line_opts):
|
||||
with pytest.raises(ConfigurationError):
|
||||
_get_replica_set_name(conn)
|
||||
|
||||
# replSet option set through the command line
|
||||
cmd_line_opts = {'argv': ['mongod', '--dbpath=/data', '--replSet=rs0'],
|
||||
'ok': 1.0,
|
||||
'parsed': {'replication': {'replSet': 'rs0'},
|
||||
'storage': {'dbPath': '/data'}}}
|
||||
with patch.object(Database, 'command', return_value=cmd_line_opts):
|
||||
assert _get_replica_set_name(conn) == 'rs0'
|
||||
|
||||
# replSet option set through the config file
|
||||
cmd_line_opts = {'argv': ['mongod', '--dbpath=/data', '--replSet=rs0'],
|
||||
'ok': 1.0,
|
||||
'parsed': {'replication': {'replSetName': 'rs0'},
|
||||
'storage': {'dbPath': '/data'}}}
|
||||
with patch.object(Database, 'command', return_value=cmd_line_opts):
|
||||
assert _get_replica_set_name(conn) == 'rs0'
|
||||
|
Loading…
x
Reference in New Issue
Block a user