Example #1
0
def mongod_program(logger, job_num, executable, process_kwargs,
                   mongod_options):
    """
    Return a Process instance that starts mongod arguments constructed from 'mongod_options'.

    @param logger - The logger to pass into the process.
    @param executable - The mongod executable to run.
    @param process_kwargs - A dict of key-value pairs to pass to the process.
    @param mongod_options - A HistoryDict describing the various options to pass to the mongod.
    """

    args = [executable]
    mongod_options = mongod_options.copy()

    if "port" not in mongod_options:
        mongod_options["port"] = network.PortAllocator.next_fixture_port(
            job_num)
    suite_set_parameters = mongod_options.get("set_parameters",
                                              make_historic({}))
    _apply_set_parameters(args, suite_set_parameters)
    mongod_options.pop("set_parameters")

    # Apply the rest of the command line arguments.
    _apply_kwargs(args, mongod_options)

    _set_keyfile_permissions(mongod_options)

    process_kwargs = make_historic(utils.default_if_none(process_kwargs, {}))
    process_kwargs["job_num"] = job_num
    if config.EXPORT_MONGOD_CONFIG == "regular":
        mongod_options.dump_history(f"{logger.name}_config.yml")
    elif config.EXPORT_MONGOD_CONFIG == "detailed":
        mongod_options.dump_history(f"{logger.name}_config.yml",
                                    include_location=True)
    return make_process(logger, args, **process_kwargs), mongod_options["port"]
Example #2
0
    def _new_rs_shard(self, index, num_rs_nodes_per_shard):
        """Return a replicaset.ReplicaSetFixture configured as a shard in a sharded cluster."""

        shard_logging_prefix = f"shard{index}"
        mongod_logger = logging.loggers.new_fixture_node_logger(self.__class__.__name__,
                                                                self.job_num, shard_logging_prefix)

        shard_options = self.shard_options.copy()

        auth_options = shard_options.pop("auth_options", self.auth_options)
        preserve_dbpath = shard_options.pop("preserve_dbpath", self.preserve_dbpath)

        replset_config_options = make_historic(shard_options.pop("replset_config_options", {}))
        replset_config_options["configsvr"] = False

        mixed_bin_versions = self.mixed_bin_versions
        if mixed_bin_versions is not None:
            start_index = index * num_rs_nodes_per_shard
            mixed_bin_versions = mixed_bin_versions[start_index:start_index +
                                                    num_rs_nodes_per_shard]

        mongod_options = self.mongod_options.copy()
        mongod_options.update(make_historic(shard_options.pop("mongod_options", {})))
        mongod_options["shardsvr"] = ""
        mongod_options["dbpath"] = os.path.join(self._dbpath_prefix, "shard{}".format(index))
        mongod_options["replSet"] = ShardedClusterFixture._SHARD_REPLSET_NAME_PREFIX + str(index)

        return replicaset.ReplicaSetFixture(
            mongod_logger, self.job_num, mongod_executable=self.mongod_executable,
            mongod_options=mongod_options, preserve_dbpath=preserve_dbpath,
            num_nodes=num_rs_nodes_per_shard, auth_options=auth_options,
            replset_config_options=replset_config_options, mixed_bin_versions=mixed_bin_versions,
            shard_logging_prefix=shard_logging_prefix, **shard_options)
Example #3
0
    def __init__(  # pylint: disable=too-many-arguments,too-many-locals
            self, logger, job_num, mongos_executable=None, mongos_options=None,
            mongod_executable=None, mongod_options=None, dbpath_prefix=None, preserve_dbpath=False,
            num_shards=1, num_rs_nodes_per_shard=1, num_mongos=1, enable_sharding=None,
            enable_balancer=True, enable_autosplit=True, auth_options=None, configsvr_options=None,
            shard_options=None, mixed_bin_versions=None):
        """Initialize ShardedClusterFixture with different options for the cluster processes."""

        interface.Fixture.__init__(self, logger, job_num, dbpath_prefix=dbpath_prefix)

        if "dbpath" in mongod_options:
            raise ValueError("Cannot specify mongod_options.dbpath")

        self.mongos_executable = mongos_executable
        self.mongos_options = make_historic(utils.default_if_none(mongos_options, {}))
        self.mongod_options = make_historic(utils.default_if_none(mongod_options, {}))
        self.mongod_executable = mongod_executable
        self.mongod_options["set_parameters"] = make_historic(
            mongod_options.get("set_parameters", {})).copy()
        self.mongod_options["set_parameters"]["migrationLockAcquisitionMaxWaitMS"] = \
                self.mongod_options["set_parameters"].get("migrationLockAcquisitionMaxWaitMS", 30000)
        self.preserve_dbpath = preserve_dbpath
        # Use 'num_shards' and 'num_rs_nodes_per_shard' values from the command line if they exist.
        num_shards_option = config.NUM_SHARDS
        self.num_shards = num_shards if not num_shards_option else num_shards_option
        num_rs_nodes_per_shard_option = config.NUM_REPLSET_NODES
        self.num_rs_nodes_per_shard = num_rs_nodes_per_shard if not num_rs_nodes_per_shard_option else num_rs_nodes_per_shard_option
        self.num_mongos = num_mongos
        self.enable_sharding = utils.default_if_none(enable_sharding, [])
        self.enable_balancer = enable_balancer
        self.enable_autosplit = enable_autosplit
        self.auth_options = auth_options
        self.configsvr_options = make_historic(utils.default_if_none(configsvr_options, {}))
        self.shard_options = make_historic(utils.default_if_none(shard_options, {}))
        self.mixed_bin_versions = utils.default_if_none(mixed_bin_versions,
                                                        config.MIXED_BIN_VERSIONS)

        if self.num_rs_nodes_per_shard is None:
            raise TypeError("num_rs_nodes_per_shard must be an integer but found None")
        elif isinstance(self.num_rs_nodes_per_shard, int):
            if self.num_rs_nodes_per_shard <= 0:
                raise ValueError("num_rs_nodes_per_shard must be a positive integer")

        if self.mixed_bin_versions is not None:
            num_mongods = self.num_shards * self.num_rs_nodes_per_shard
            if len(self.mixed_bin_versions) != num_mongods:
                msg = (("The number of binary versions specified: {} do not match the number of"\
                        " nodes in the sharded cluster: {}.")).format(len(self.mixed_bin_versions), num_mongods)
                raise errors.ServerFailure(msg)

        self._dbpath_prefix = os.path.join(self._dbpath_prefix, config.FIXTURE_SUBDIR)

        self.configsvr = None
        self.mongos = []
        self.shards = []
Example #4
0
def mongos_program(  # pylint: disable=too-many-arguments
        logger,
        job_num,
        test_id=None,
        executable=None,
        process_kwargs=None,
        mongos_options=None):
    """Return a Process instance that starts a mongos with arguments constructed from 'kwargs'."""
    args = [executable]

    mongos_options = mongos_options.copy()

    if "port" not in mongos_options:
        mongos_options["port"] = network.PortAllocator.next_fixture_port(
            job_num)
    suite_set_parameters = mongos_options.get("set_parameters", {})
    _apply_set_parameters(args, suite_set_parameters)
    mongos_options.pop("set_parameters")

    # Apply the rest of the command line arguments.
    _apply_kwargs(args, mongos_options)

    _set_keyfile_permissions(mongos_options)

    process_kwargs = make_historic(utils.default_if_none(process_kwargs, {}))
    process_kwargs["job_num"] = job_num
    process_kwargs["test_id"] = test_id
    return make_process(logger, args, **process_kwargs), mongos_options["port"]
Example #5
0
    def _new_configsvr(self):
        """Return a replicaset.ReplicaSetFixture configured as the config server."""

        shard_logging_prefix = "configsvr"
        mongod_logger = logging.loggers.new_fixture_node_logger(self.__class__.__name__,
                                                                self.job_num, shard_logging_prefix)

        configsvr_options = self.configsvr_options.copy()

        auth_options = configsvr_options.pop("auth_options", self.auth_options)
        preserve_dbpath = configsvr_options.pop("preserve_dbpath", self.preserve_dbpath)
        num_nodes = configsvr_options.pop("num_nodes", 1)

        replset_config_options = configsvr_options.pop("replset_config_options", {})
        replset_config_options["configsvr"] = True

        mongod_options = self.mongod_options.copy()
        mongod_options.update(make_historic(configsvr_options.pop("mongod_options", {})))
        mongod_options["configsvr"] = ""
        mongod_options["dbpath"] = os.path.join(self._dbpath_prefix, "config")
        mongod_options["replSet"] = ShardedClusterFixture._CONFIGSVR_REPLSET_NAME
        mongod_options["storageEngine"] = "wiredTiger"

        return replicaset.ReplicaSetFixture(
            mongod_logger, self.job_num, mongod_options=mongod_options,
            mongod_executable=self.mongod_executable, preserve_dbpath=preserve_dbpath,
            num_nodes=num_nodes, auth_options=auth_options, mixed_bin_versions=None,
            replset_config_options=replset_config_options,
            shard_logging_prefix=shard_logging_prefix, **configsvr_options)
Example #6
0
    def __init__(  # pylint: disable=too-many-arguments
            self,
            logger,
            job_num,
            mongod_executable=None,
            mongod_options=None,
            dbpath_prefix=None,
            preserve_dbpath=False):
        """Initialize MongoDFixture with different options for the mongod process."""

        self.mongod_options = make_historic(
            utils.default_if_none(mongod_options, {}))
        interface.Fixture.__init__(self,
                                   logger,
                                   job_num,
                                   dbpath_prefix=dbpath_prefix)

        if "dbpath" in self.mongod_options and dbpath_prefix is not None:
            raise ValueError(
                "Cannot specify both mongod_options.dbpath and dbpath_prefix")

        # Default to command line options if the YAML configuration is not passed in.
        self.mongod_executable = utils.default_if_none(
            mongod_executable, config.MONGOD_EXECUTABLE)

        self.mongod_options = make_historic(
            utils.default_if_none(mongod_options, {})).copy()

        # The dbpath in mongod_options takes precedence over other settings to make it easier for
        # users to specify a dbpath containing data to test against.
        if "dbpath" not in self.mongod_options:
            self.mongod_options["dbpath"] = os.path.join(
                self._dbpath_prefix, config.FIXTURE_SUBDIR)
        self._dbpath = self.mongod_options["dbpath"]

        if config.ALWAYS_USE_LOG_FILES:
            self.mongod_options["logpath"] = self._dbpath + "/mongod.log"
            self.mongod_options["logappend"] = ""
            self.preserve_dbpath = True
        else:
            self.preserve_dbpath = preserve_dbpath

        self.mongod = None
        self.port = None
Example #7
0
    def test_make_historic(self):
        actual_dict = {
            "foo": "bar",
            "a": "b",
            "innerdict": {
                "innerkey": "innerval"
            }
        }
        test_dict = make_historic(actual_dict)
        test_dict["a"] = "c"

        # Updating actual_dict doesn't affect test_dict (it copied).
        actual_dict["foo"] = "za"
        # Similarly, updating the inner dict doesn't either.
        actual_dict["innerdict"]["innerkey"] = "innerval2"

        # However, updating the inner dict on the test_dict does.
        test_dict["innerdict"]["innerkey"] = "secondinnerval"
        expected_test_dict = """SchemaVersion: "0.1"


History:


  a:
  - time: 1
    type: WRITE
    value_written: b
  - time: 3
    type: WRITE
    value_written: c


  foo:
  - time: 0
    type: WRITE
    value_written: bar


  innerdict:
  - time: 2
    type: WRITE
    value_written:
      object_class: dict
      object_value:
        innerkey: innerval
  - time: 4
    type: WRITE
    value_written:
      object_class: dict
      object_value:
        innerkey: secondinnerval"""

        self.assertEqual(test_dict.dump_history(), expected_test_dict)
Example #8
0
def mongos_program(logger,
                   job_num,
                   test_id=None,
                   executable=None,
                   process_kwargs=None,
                   **kwargs):
    """Return a Process instance that starts a mongos with arguments constructed from 'kwargs'."""

    executable = utils.default_if_none(executable,
                                       config.DEFAULT_MONGOS_EXECUTABLE)
    args = [executable]

    # Apply the --setParameter command line argument. Command line options to resmoke.py override
    # the YAML configuration.
    suite_set_parameters = make_historic(kwargs.pop("set_parameters", {}))

    if config.MONGOS_SET_PARAMETERS is not None:
        suite_set_parameters.update(
            utils.load_yaml(config.MONGOS_SET_PARAMETERS))

    # Set default log verbosity levels if none were specified.
    if "logComponentVerbosity" not in suite_set_parameters:
        suite_set_parameters[
            "logComponentVerbosity"] = default_mongos_log_component_verbosity(
            )

    _add_testing_set_parameters(suite_set_parameters)

    _apply_set_parameters(args, suite_set_parameters)

    # Apply the rest of the command line arguments.
    _apply_kwargs(args, kwargs)

    _set_keyfile_permissions(kwargs)

    process_kwargs = make_historic(utils.default_if_none(process_kwargs, {}))
    process_kwargs["job_num"] = job_num
    process_kwargs["test_id"] = test_id
    return make_process(logger, args, **process_kwargs)
Example #9
0
    def __init__(self, logger, job_num, dbpath_prefix, mongos_executable=None, mongos_options=None):
        """Initialize _MongoSFixture."""

        interface.Fixture.__init__(self, logger, job_num)

        # Default to command line options if the YAML configuration is not passed in.
        self.mongos_executable = utils.default_if_none(mongos_executable, config.MONGOS_EXECUTABLE)

        self.mongos_options = make_historic(utils.default_if_none(mongos_options, {})).copy()

        self.mongos = None
        self.port = None
        self._dbpath_prefix = dbpath_prefix
Example #10
0
    def test_write_equality(self):
        test_dict = HistoryDict()
        test_dict["foo"] = "bar"
        test_dict["myint"] = 1
        test_dict["foo"] = "za"
        test_dict["innerdict"] = make_historic({"a": "b"})

        second_dict = HistoryDict()
        second_dict["foo"] = "bar"
        second_dict["myint"] = 1
        second_dict["foo"] = "za"
        second_dict["innerdict"] = make_historic({"a": "b"})

        self.assertTrue(test_dict.write_equals(second_dict))

        second_dict["another"] = "write"
        self.assertFalse(test_dict.write_equals(second_dict))
        test_dict["another"] = "write"
        self.assertTrue(test_dict.write_equals(second_dict))

        # Reads aren't counted
        gotten_value = second_dict["foo"]  # pylint: disable=unused-variable
        self.assertTrue(test_dict.write_equals(second_dict))
Example #11
0
def mongos_program(logger,
                   job_num,
                   test_id=None,
                   executable=None,
                   process_kwargs=None,
                   **kwargs):
    """Return a Process instance that starts a mongos with arguments constructed from 'kwargs'."""
    args = [executable]

    suite_set_parameters = kwargs.get("set_parameters", {})
    _apply_set_parameters(args, suite_set_parameters)
    kwargs.pop("set_parameters")

    # Apply the rest of the command line arguments.
    _apply_kwargs(args, kwargs)

    _set_keyfile_permissions(kwargs)

    process_kwargs = make_historic(utils.default_if_none(process_kwargs, {}))
    process_kwargs["job_num"] = job_num
    process_kwargs["test_id"] = test_id
    return make_process(logger, args, **process_kwargs)
Example #12
0
    def __init__(  # pylint: disable=too-many-arguments, too-many-locals
            self,
            logger,
            job_num,
            mongod_executable=None,
            mongod_options=None,
            dbpath_prefix=None,
            preserve_dbpath=False,
            num_nodes=2,
            start_initial_sync_node=False,
            write_concern_majority_journal_default=None,
            auth_options=None,
            replset_config_options=None,
            voting_secondaries=True,
            all_nodes_electable=False,
            use_replica_set_connection_string=None,
            linear_chain=False,
            mixed_bin_versions=None,
            default_read_concern=None,
            default_write_concern=None,
            shard_logging_prefix=None,
            replicaset_logging_prefix=None):
        """Initialize ReplicaSetFixture."""

        interface.ReplFixture.__init__(self,
                                       logger,
                                       job_num,
                                       dbpath_prefix=dbpath_prefix)

        self.mongod_executable = mongod_executable
        self.mongod_options = make_historic(
            utils.default_if_none(mongod_options, {}))
        self.preserve_dbpath = preserve_dbpath
        self.start_initial_sync_node = start_initial_sync_node
        self.write_concern_majority_journal_default = write_concern_majority_journal_default
        self.auth_options = auth_options
        self.replset_config_options = make_historic(
            utils.default_if_none(replset_config_options, {}))
        self.voting_secondaries = voting_secondaries
        self.all_nodes_electable = all_nodes_electable
        self.use_replica_set_connection_string = use_replica_set_connection_string
        self.default_read_concern = default_read_concern
        self.default_write_concern = default_write_concern
        self.mixed_bin_versions = utils.default_if_none(
            mixed_bin_versions, config.MIXED_BIN_VERSIONS)
        self.mixed_bin_versions_config = self.mixed_bin_versions
        self.shard_logging_prefix = shard_logging_prefix
        self.replicaset_logging_prefix = replicaset_logging_prefix

        # Use the values given from the command line if they exist for linear_chain and num_nodes.
        linear_chain_option = utils.default_if_none(config.LINEAR_CHAIN,
                                                    linear_chain)
        self.linear_chain = linear_chain_option if linear_chain_option else linear_chain
        num_replset_nodes = config.NUM_REPLSET_NODES
        self.num_nodes = num_replset_nodes if num_replset_nodes else num_nodes

        if self.mixed_bin_versions is not None:
            mongod_executable = utils.default_if_none(
                self.mongod_executable, config.MONGOD_EXECUTABLE,
                config.DEFAULT_MONGOD_EXECUTABLE)
            latest_mongod = mongod_executable
            # The last-lts binary is currently expected to live in '/data/multiversion', which is
            # part of the PATH.
            is_config_svr = "configsvr" in self.replset_config_options and self.replset_config_options[
                "configsvr"]
            if not is_config_svr:
                self.mixed_bin_versions = [
                    latest_mongod if (x == "new") else LAST_LTS_MONGOD_BINARY
                    for x in self.mixed_bin_versions
                ]
            else:
                # Our documented recommended path for upgrading shards lets us assume that config
                # server nodes will always be fully upgraded before shard nodes.
                self.mixed_bin_versions = [latest_mongod, latest_mongod]
            num_versions = len(self.mixed_bin_versions)
            if num_versions != self.num_nodes and not is_config_svr:
                msg = (("The number of binary versions specified: {} do not match the number of"\
                        " nodes in the replica set: {}.")).format(num_versions, self.num_nodes)
                raise errors.ServerFailure(msg)

        # By default, we only use a replica set connection string if all nodes are capable of being
        # elected primary.
        if self.use_replica_set_connection_string is None:
            self.use_replica_set_connection_string = self.all_nodes_electable

        if self.default_write_concern is True:
            self.default_write_concern = make_historic({
                "w":
                "majority",
                # Use a "signature" value that won't typically match a value assigned in normal use.
                # This way the wtimeout set by this override is distinguishable in the server logs.
                "wtimeout":
                5 * 60 * 1000 + 321,  # 300321ms
            })

        # Set the default oplogSize to 511MB.
        self.mongod_options.setdefault("oplogSize", 511)

        # The dbpath in mongod_options is used as the dbpath prefix for replica set members and
        # takes precedence over other settings. The ShardedClusterFixture uses this parameter to
        # create replica sets and assign their dbpath structure explicitly.
        if "dbpath" in self.mongod_options:
            self._dbpath_prefix = self.mongod_options.pop("dbpath")
        else:
            self._dbpath_prefix = os.path.join(self._dbpath_prefix,
                                               config.FIXTURE_SUBDIR)

        self.nodes = []
        self.replset_name = None
        self.initial_sync_node = None
        self.initial_sync_node_idx = -1
Example #13
0
    def setup(self):  # pylint: disable=too-many-branches,too-many-statements,too-many-locals
        """Set up the replica set."""
        self.replset_name = self.mongod_options.get("replSet", "rs")
        if not self.nodes:
            for i in range(self.num_nodes):
                node = self._new_mongod(i, self.replset_name)
                self.nodes.append(node)

        for i in range(self.num_nodes):
            steady_state_constraint_param = "oplogApplicationEnforcesSteadyStateConstraints"
            # TODO (SERVER-52985): Set steady state constraint parameters on last-lts nodes.
            if (steady_state_constraint_param
                    not in self.nodes[i].mongod_options["set_parameters"]
                    and self.mixed_bin_versions is not None
                    and self.mixed_bin_versions[i] == "new"):
                self.nodes[i].mongod_options["set_parameters"][
                    steady_state_constraint_param] = True
            if self.linear_chain and i > 0:
                self.nodes[i].mongod_options["set_parameters"][
                    "failpoint.forceSyncSourceCandidate"] = make_historic({
                        "mode":
                        "alwaysOn",
                        "data": {
                            "hostAndPort":
                            self.nodes[i - 1].get_internal_connection_string()
                        }
                    })
            self.nodes[i].setup()

        if self.start_initial_sync_node:
            if not self.initial_sync_node:
                self.initial_sync_node_idx = len(self.nodes)
                self.initial_sync_node = self._new_mongod(
                    self.initial_sync_node_idx, self.replset_name)
            self.initial_sync_node.setup()
            self.initial_sync_node.await_ready()

        if self.mixed_bin_versions:
            for i in range(self.num_nodes):
                if self.nodes[i].mongod_executable != self.mixed_bin_versions[
                        i]:
                    msg = (
                        f"Executable of node{i}: {self.nodes[i].mongod_executable} does not "
                        f"match the executable assigned by mixedBinVersions: "
                        f"{self.mixed_bin_versions[i]}.")
                    raise errors.ServerFailure(msg)

        # We need only to wait to connect to the first node of the replica set because we first
        # initiate it as a single node replica set.
        self.nodes[0].await_ready()

        # Initiate the replica set.
        members = []
        for (i, node) in enumerate(self.nodes):
            member_info = {
                "_id": i,
                "host": node.get_internal_connection_string()
            }
            if i > 0:
                if not self.all_nodes_electable:
                    member_info["priority"] = 0
                if i >= 7 or not self.voting_secondaries:
                    # Only 7 nodes in a replica set can vote, so the other members must still be
                    # non-voting when this fixture is configured to have voting secondaries.
                    member_info["votes"] = 0
            members.append(member_info)
        if self.initial_sync_node:
            members.append({
                "_id":
                self.initial_sync_node_idx,
                "host":
                self.initial_sync_node.get_internal_connection_string(),
                "priority":
                0,
                "hidden":
                1,
                "votes":
                0
            })

        repl_config = {"_id": self.replset_name, "protocolVersion": 1}
        client = self.nodes[0].mongo_client()

        self.auth(client, self.auth_options)

        if client.local.system.replset.count():
            # Skip initializing the replset if there is an existing configuration.
            return

        if self.write_concern_majority_journal_default is not None:
            repl_config[
                "writeConcernMajorityJournalDefault"] = self.write_concern_majority_journal_default
        else:
            server_status = client.admin.command({"serverStatus": 1})
            cmd_line_opts = client.admin.command({"getCmdLineOpts": 1})
            if not (server_status["storageEngine"]["persistent"]
                    and cmd_line_opts["parsed"].get("storage", {}).get(
                        "journal", {}).get("enabled", True)):
                repl_config["writeConcernMajorityJournalDefault"] = False

        if self.replset_config_options.get("configsvr", False):
            repl_config["configsvr"] = True
        if self.replset_config_options.get("settings"):
            replset_settings = self.replset_config_options["settings"]
            repl_config["settings"] = replset_settings

        # Increase the election timeout to 24 hours to prevent spurious elections.
        repl_config.setdefault("settings", {})
        if "electionTimeoutMillis" not in repl_config["settings"]:
            repl_config["settings"][
                "electionTimeoutMillis"] = 24 * 60 * 60 * 1000

        # Start up a single node replica set then reconfigure to the correct size (if the config
        # contains more than 1 node), so the primary is elected more quickly.
        repl_config["members"] = [members[0]]
        self.logger.info("Issuing replSetInitiate command: %s", repl_config)
        self._initiate_repl_set(client, repl_config)
        self._await_primary()

        if self.mixed_bin_versions is not None:
            if self.mixed_bin_versions[0] == "new":
                fcv_response = client.admin.command({
                    "getParameter":
                    1,
                    "featureCompatibilityVersion":
                    1
                })
                fcv = fcv_response["featureCompatibilityVersion"]["version"]
                if fcv != ReplicaSetFixture._LATEST_FCV:
                    msg = (("Server returned FCV{} when we expected FCV{}."
                            ).format(fcv, ReplicaSetFixture._LATEST_FCV))
                    raise errors.ServerFailure(msg)

            # Initiating a replica set with a single node will use "latest" FCV. This will
            # cause IncompatibleServerVersion errors if additional "last-lts" binary version
            # nodes are subsequently added to the set, since such nodes cannot set their FCV to
            # "latest". Therefore, we make sure the primary is "last-lts" FCV before adding in
            # nodes of different binary versions to the replica set.
            client.admin.command({
                "setFeatureCompatibilityVersion":
                ReplicaSetFixture._LAST_LTS_FCV
            })

        if self.nodes[1:]:
            # Wait to connect to each of the secondaries before running the replSetReconfig
            # command.
            for node in self.nodes[1:]:
                node.await_ready()
            # Add in the members one at a time, since non force reconfigs can only add/remove a
            # single voting member at a time.
            for ind in range(2, len(members) + 1):
                self._add_node_to_repl_set(client, repl_config, ind, members)

        self._await_secondaries()
        self._await_newly_added_removals()
Example #14
0
from buildscripts.resmokelib import config
from buildscripts.resmokelib import core
from buildscripts.resmokelib import errors
from buildscripts.resmokelib import logging
from buildscripts.resmokelib import utils
from buildscripts.resmokelib.utils.history import make_historic
from buildscripts.resmokelib.multiversionconstants import LAST_LTS_MONGOS_BINARY
from buildscripts.resmokelib.testing.fixtures import interface
from buildscripts.resmokelib.testing.fixtures import replicaset
from buildscripts.resmokelib.testing.fixtures import standalone
from buildscripts.resmokelib.utils import registry

# The default verbosity setting for any tests that are not started with an Evergreen task id. This
# will apply to any tests run locally.
DEFAULT_MONGOS_LOG_COMPONENT_VERBOSITY = make_historic({"transaction": 3})

# The default verbosity setting for any tests running in Evergreen i.e. started with an Evergreen
# task id.
DEFAULT_EVERGREEN_MONGOS_LOG_COMPONENT_VERBOSITY = make_historic(
    {"transaction": 3})


class ShardedClusterFixture(interface.Fixture):  # pylint: disable=too-many-instance-attributes
    """Fixture which provides JSTests with a sharded cluster to run against."""

    _CONFIGSVR_REPLSET_NAME = "config-rs"
    _SHARD_REPLSET_NAME_PREFIX = "shard-rs"
    AWAIT_SHARDING_INITIALIZATION_TIMEOUT_SECS = 60

    def __init__(  # pylint: disable=too-many-arguments,too-many-locals
Example #15
0
def mongod_program(  # pylint: disable=too-many-branches,too-many-statements
        logger,
        job_num,
        executable=None,
        process_kwargs=None,
        mongod_options=None):
    """
    Return a Process instance that starts mongod arguments constructed from 'mongod_options'.

    @param logger - The logger to pass into the process.
    @param executable - The mongod executable to run.
    @param process_kwargs - A dict of key-value pairs to pass to the process.
    @param mongod_options - A HistoryDict describing the various options to pass to the mongod.
    """

    executable = utils.default_if_none(executable,
                                       config.DEFAULT_MONGOD_EXECUTABLE)
    mongod_options = utils.default_if_none(mongod_options,
                                           make_historic({})).copy()
    args = [executable]

    # Apply the --setParameter command line argument. Command line options to resmoke.py override
    # the YAML configuration.
    # We leave the parameters attached for now so the top-level dict tracks its history.
    suite_set_parameters = mongod_options.setdefault("set_parameters",
                                                     make_historic({}))

    if config.MONGOD_SET_PARAMETERS is not None:
        suite_set_parameters.update(
            make_historic(utils.load_yaml(config.MONGOD_SET_PARAMETERS)))

    # Set default log verbosity levels if none were specified.
    if "logComponentVerbosity" not in suite_set_parameters:
        suite_set_parameters[
            "logComponentVerbosity"] = get_default_log_component_verbosity_for_mongod(
                executable)

    # minNumChunksForSessionsCollection controls the minimum number of chunks the balancer will
    # enforce for the sessions collection. If the actual number of chunks is less, the balancer will
    # issue split commands to create more chunks. As a result, the balancer will also end up moving
    # chunks for the sessions collection to balance the chunks across shards. Unless the suite is
    # explicitly prepared to handle these background migrations, set the parameter to 1.
    if "configsvr" in mongod_options and "minNumChunksForSessionsCollection" not in suite_set_parameters:
        suite_set_parameters["minNumChunksForSessionsCollection"] = 1

    # orphanCleanupDelaySecs controls an artificial delay before cleaning up an orphaned chunk
    # that has migrated off of a shard, meant to allow most dependent queries on secondaries to
    # complete first. It defaults to 900, or 15 minutes, which is prohibitively long for tests.
    # Setting it in the .yml file overrides this.
    if "shardsvr" in mongod_options and "orphanCleanupDelaySecs" not in suite_set_parameters:
        suite_set_parameters["orphanCleanupDelaySecs"] = 1

    # The LogicalSessionCache does automatic background refreshes in the server. This is
    # race-y for tests, since tests trigger their own immediate refreshes instead. Turn off
    # background refreshing for tests. Set in the .yml file to override this.
    if "disableLogicalSessionCacheRefresh" not in suite_set_parameters:
        suite_set_parameters["disableLogicalSessionCacheRefresh"] = True

    # Set coordinateCommitReturnImmediatelyAfterPersistingDecision to false so that tests do
    # not need to rely on causal consistency or explicitly wait for the transaction to finish
    # committing. If we are running LAST_LTS mongoD and the test suite has explicitly set the
    # coordinateCommitReturnImmediatelyAfterPersistingDecision parameter, we remove it from
    # the setParameter list, since coordinateCommitReturnImmediatelyAfterPersistingDecision
    # does not exist prior to 4.7.
    # TODO(SERVER-51682): remove the 'elif' clause on master when 5.0 becomes LAST_LTS.
    if executable != LAST_LTS_MONGOD_BINARY and \
        "coordinateCommitReturnImmediatelyAfterPersistingDecision" not in suite_set_parameters:
        suite_set_parameters[
            "coordinateCommitReturnImmediatelyAfterPersistingDecision"] = False
    elif executable == LAST_LTS_MONGOD_BINARY and \
        "coordinateCommitReturnImmediatelyAfterPersistingDecision" in suite_set_parameters:
        del suite_set_parameters[
            "coordinateCommitReturnImmediatelyAfterPersistingDecision"]

    # TODO SERVER-54593 to remove the special-case handling when 5.0 becomes LAST_LTS.
    if "reshardingMinimumOperationDurationMillis" in suite_set_parameters:
        if executable == LAST_LTS_MONGOD_BINARY:
            del suite_set_parameters[
                "reshardingMinimumOperationDurationMillis"]
    elif executable != LAST_LTS_MONGOD_BINARY:
        suite_set_parameters["reshardingMinimumOperationDurationMillis"] = 5000

    # There's a periodic background thread that checks for and aborts expired transactions.
    # "transactionLifetimeLimitSeconds" specifies for how long a transaction can run before expiring
    # and being aborted by the background thread. It defaults to 60 seconds, which is too short to
    # be reliable for our tests. Setting it to 24 hours, so that it is longer than the Evergreen
    # execution timeout.
    if "transactionLifetimeLimitSeconds" not in suite_set_parameters:
        suite_set_parameters["transactionLifetimeLimitSeconds"] = 24 * 60 * 60

    # Hybrid index builds drain writes received during the build process in batches of 1000 writes
    # by default. Not all tests perform enough writes to exercise the code path where multiple
    # batches are applied, which means certain bugs are harder to encounter. Set this level lower
    # so there are more opportunities to drain writes in multiple batches.
    if "maxIndexBuildDrainBatchSize" not in suite_set_parameters:
        suite_set_parameters["maxIndexBuildDrainBatchSize"] = 10

    # The periodic no-op writer writes an oplog entry of type='n' once every 10 seconds. This has
    # the potential to mask issues such as SERVER-31609 because it allows the operationTime of
    # cluster to advance even if the client is blocked for other reasons. We should disable the
    # periodic no-op writer. Set in the .yml file to override this.
    if "replSet" in mongod_options and "writePeriodicNoops" not in suite_set_parameters:
        suite_set_parameters["writePeriodicNoops"] = False

    # The default time for stepdown and quiesce mode in response to SIGTERM is 15 seconds. Reduce
    # this to 100ms for faster shutdown. On branches 4.4 and earlier, there is no quiesce mode, but
    # the default time for stepdown is 10 seconds.
    # TODO(SERVER-47797): Remove reference to waitForStepDownOnNonCommandShutdown.
    if ("replSet" in mongod_options and "waitForStepDownOnNonCommandShutdown"
            not in suite_set_parameters
            and "shutdownTimeoutMillisForSignaledShutdown"
            not in suite_set_parameters):
        if executable == LAST_LTS_MONGOD_BINARY:
            suite_set_parameters["waitForStepDownOnNonCommandShutdown"] = False
        else:
            suite_set_parameters[
                "shutdownTimeoutMillisForSignaledShutdown"] = 100

    if "enableFlowControl" not in suite_set_parameters and config.FLOW_CONTROL is not None:
        suite_set_parameters["enableFlowControl"] = (
            config.FLOW_CONTROL == "on")

    if ("failpoint.flowControlTicketOverride" not in suite_set_parameters
            and config.FLOW_CONTROL_TICKETS is not None):
        suite_set_parameters[
            "failpoint.flowControlTicketOverride"] = make_historic({
                "mode": "alwaysOn",
                "data": {
                    "numTickets": config.FLOW_CONTROL_TICKETS
                }
            })

    _add_testing_set_parameters(suite_set_parameters)

    _apply_set_parameters(args, suite_set_parameters)

    shortcut_opts = {
        "enableMajorityReadConcern": config.MAJORITY_READ_CONCERN,
        "nojournal": config.NO_JOURNAL,
        "storageEngine": config.STORAGE_ENGINE,
        "transportLayer": config.TRANSPORT_LAYER,
        "wiredTigerCollectionConfigString": config.WT_COLL_CONFIG,
        "wiredTigerEngineConfigString": config.WT_ENGINE_CONFIG,
        "wiredTigerIndexConfigString": config.WT_INDEX_CONFIG,
    }

    if config.STORAGE_ENGINE == "inMemory":
        shortcut_opts["inMemorySizeGB"] = config.STORAGE_ENGINE_CACHE_SIZE
    elif config.STORAGE_ENGINE == "rocksdb":
        shortcut_opts["rocksdbCacheSizeGB"] = config.STORAGE_ENGINE_CACHE_SIZE
    elif config.STORAGE_ENGINE == "wiredTiger" or config.STORAGE_ENGINE is None:
        shortcut_opts[
            "wiredTigerCacheSizeGB"] = config.STORAGE_ENGINE_CACHE_SIZE

    # These options are just flags, so they should not take a value.
    opts_without_vals = ("nojournal", "logappend")

    # Have the --nojournal command line argument to resmoke.py unset the journal option.
    if shortcut_opts["nojournal"] and "journal" in mongod_options:
        del mongod_options["journal"]

    # Ensure that config servers run with journaling enabled.
    if "configsvr" in mongod_options:
        shortcut_opts["nojournal"] = False
        mongod_options["journal"] = ""

    # Command line options override the YAML configuration.
    for opt_name in shortcut_opts:
        opt_value = shortcut_opts[opt_name]
        if opt_name in opts_without_vals:
            # Options that are specified as --flag on the command line are represented by a boolean
            # value where True indicates that the flag should be included in 'kwargs'.
            if opt_value:
                mongod_options[opt_name] = ""
        else:
            # Options that are specified as --key=value on the command line are represented by a
            # value where None indicates that the key-value pair shouldn't be included in 'kwargs'.
            if opt_value is not None:
                mongod_options[opt_name] = opt_value

    # Override the storage engine specified on the command line with "wiredTiger" if running a
    # config server replica set.
    if "replSet" in mongod_options and "configsvr" in mongod_options:
        mongod_options["storageEngine"] = "wiredTiger"

    # set_parameters has its own logic above
    mongod_options.pop("set_parameters")

    # Apply the rest of the command line arguments.
    _apply_kwargs(args, mongod_options)

    _set_keyfile_permissions(mongod_options)

    process_kwargs = make_historic(utils.default_if_none(process_kwargs, {}))
    process_kwargs["job_num"] = job_num
    if config.EXPORT_MONGOD_CONFIG == "regular":
        mongod_options.dump_history(f"{logger.name}_config.yml")
    elif config.EXPORT_MONGOD_CONFIG == "detailed":
        mongod_options.dump_history(f"{logger.name}_config.yml",
                                    include_location=True)
    return make_process(logger, args, **process_kwargs)
Example #16
0
from buildscripts.resmokelib.multiversionconstants import LAST_LTS_MONGOD_BINARY
from buildscripts.resmokelib.multiversionconstants import LAST_LTS_MONGOS_BINARY

# The below parameters define the default 'logComponentVerbosity' object passed to mongod processes
# started either directly via resmoke or those that will get started by the mongo shell. We allow
# this default to be different for tests run locally and tests run in Evergreen. This allows us, for
# example, to keep log verbosity high in Evergreen test runs without polluting the logs for
# developers running local tests.

# The default verbosity setting for any tests that are not started with an Evergreen task id. This
# will apply to any tests run locally.
DEFAULT_MONGOD_LOG_COMPONENT_VERBOSITY = make_historic({
    "replication": {
        "rollback": 2
    },
    "sharding": {
        "migration": 2
    },
    "transaction": 4,
    "tenantMigration": 4
})

DEFAULT_LAST_LTS_MONGOD_LOG_COMPONENT_VERBOSITY = make_historic({
    "replication": {
        "rollback": 2
    },
    "transaction":
    4
})

# The default verbosity setting for any mongod processes running in Evergreen i.e. started with an
# Evergreen task id.