Compare commits
117 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a20978382 | ||
|
|
eb23c1b873 | ||
|
|
e3c738c4da | ||
|
|
faaa2c6964 | ||
|
|
8b2023a654 | ||
|
|
3668b2376b | ||
|
|
cea384d8e7 | ||
|
|
af4f6f1457 | ||
|
|
10fd84b685 | ||
|
|
78ba288ddc | ||
|
|
5a20629d20 | ||
|
|
f1c2fc508f | ||
|
|
4852124956 | ||
|
|
1239081e30 | ||
|
|
a2cc20bf8b | ||
|
|
a1b048ca66 | ||
|
|
f3ff7049ba | ||
|
|
4fa7a897ed | ||
|
|
290112ef13 | ||
|
|
153ecfe425 | ||
|
|
7676045db7 | ||
|
|
35832bd3cb | ||
|
|
0f8ed3b2ae | ||
|
|
ef0e913ce7 | ||
|
|
d90c776681 | ||
|
|
a31c4c6c4b | ||
|
|
718bf390c8 | ||
|
|
eb8910b86a | ||
|
|
2db2ccc12b | ||
|
|
3be9114472 | ||
|
|
465b6878c2 | ||
|
|
3dc6cd2613 | ||
|
|
f058a17e0c | ||
|
|
7fbabf32d9 | ||
|
|
e86b8c099e | ||
|
|
f037f034bb | ||
|
|
4b44c24078 | ||
|
|
5f3ed635a7 | ||
|
|
d4108459eb | ||
|
|
ae174ad93c | ||
|
|
d8c83636e6 | ||
|
|
be31f3772c | ||
|
|
194a588a57 | ||
|
|
84cb06c4db | ||
|
|
6b55bbebb1 | ||
|
|
135dee6512 | ||
|
|
6f196e05ce | ||
|
|
6161d31622 | ||
|
|
f2a5b9e9bf | ||
|
|
0bb9d60967 | ||
|
|
490ac76661 | ||
|
|
9b96ed3f33 | ||
|
|
9d78ef337b | ||
|
|
89b2e659a2 | ||
|
|
d3fab9c982 | ||
|
|
a7c27ed890 | ||
|
|
ba23c18841 | ||
|
|
cb651c8d8e | ||
|
|
7e8d18a464 | ||
|
|
9ca8158531 | ||
|
|
f90b35cf6c | ||
|
|
7382e1a20f | ||
|
|
63d6350c8a | ||
|
|
e62af19b57 | ||
|
|
e7b8e61a29 | ||
|
|
8045c4a777 | ||
|
|
923b5fd65a | ||
|
|
b6ff8ad7ad | ||
|
|
833d66db10 | ||
|
|
7cc81eb7c8 | ||
|
|
67ecf18ff9 | ||
|
|
ad2b7e93f1 | ||
|
|
b75695e4e1 | ||
|
|
71c42004e0 | ||
|
|
141ff826fd | ||
|
|
bb0c1608a1 | ||
|
|
fa551f7ae3 | ||
|
|
94323d60eb | ||
|
|
0743b9dbdb | ||
|
|
b57d0f80d3 | ||
|
|
8b1d7be7ab | ||
|
|
7726d54b47 | ||
|
|
61620c0d61 | ||
|
|
4f8c870d1e | ||
|
|
abcdf5a80b | ||
|
|
d21a749138 | ||
|
|
eff237c48a | ||
|
|
80003a2abe | ||
|
|
866c9351be | ||
|
|
77dd7f8d28 | ||
|
|
3c16cf3c39 | ||
|
|
5a1c2ba98e | ||
|
|
ac43d305a8 | ||
|
|
901d618860 | ||
|
|
14c4127600 | ||
|
|
dc25f5f142 | ||
|
|
cc020142b7 | ||
|
|
1255f157c1 | ||
|
|
ddbe563959 | ||
|
|
168b40f0ff | ||
|
|
39881664f2 | ||
|
|
fc05e34ea4 | ||
|
|
efa7a19284 | ||
|
|
ee271f84ba | ||
|
|
24ea1d6cd7 | ||
|
|
29296c1ed9 | ||
|
|
931838789d | ||
|
|
e0c820476e | ||
|
|
ac930ff992 | ||
|
|
f1c4d43ce0 | ||
|
|
0df7825a8b | ||
|
|
3e7effc73a | ||
|
|
c3cec02457 | ||
|
|
dfa1355e07 | ||
|
|
6507c32ebc | ||
|
|
1840ac24bb | ||
|
|
28d3fe9a1a |
@ -90,3 +90,4 @@ Welcome to MongoDB!
|
||||
October 16, 2018, including patch fixes for prior versions, are published
|
||||
under the [Server Side Public License (SSPL) v1](LICENSE-Community.txt).
|
||||
See individual files for details.
|
||||
|
||||
|
||||
21
SConstruct
21
SConstruct
@ -22,6 +22,10 @@ from pkg_resources import parse_version
|
||||
|
||||
import SCons
|
||||
import SCons.Script
|
||||
from buildscripts.metrics.metrics_datatypes import SConsToolingMetrics
|
||||
from buildscripts.metrics.tooling_exit_hook import initialize_exit_hook
|
||||
from buildscripts.metrics.tooling_metrics_utils import register_metrics_collection_atexit
|
||||
from site_scons.mongo import build_profiles
|
||||
|
||||
# This must be first, even before EnsureSConsVersion, if
|
||||
# we are to avoid bulk loading all tools in the DefaultEnvironment.
|
||||
@ -52,8 +56,6 @@ SCons.Node.FS.File.release_target_info = release_target_info_noop
|
||||
|
||||
from buildscripts import utils
|
||||
from buildscripts import moduleconfig
|
||||
from buildscripts.metrics.scons_tooling_metrics import setup_scons_metrics_collection_atexit
|
||||
|
||||
import psutil
|
||||
|
||||
scons_invocation = '{} {}'.format(sys.executable, ' '.join(sys.argv))
|
||||
@ -1551,10 +1553,17 @@ env = Environment(variables=env_vars, **envDict)
|
||||
del envDict
|
||||
env.AddMethod(lambda env, name, **kwargs: add_option(name, **kwargs), 'AddOption')
|
||||
|
||||
# Setup atexit method to store tooling metrics
|
||||
# The placement of this is intentional. We should only register this function atexit after
|
||||
# env, env_vars and the parser have been properly initialized.
|
||||
setup_scons_metrics_collection_atexit(utc_starttime, env_vars, env, _parser, sys.argv)
|
||||
# The placement of this is intentional. Here we setup an atexit method to store tooling metrics.
|
||||
# We should only register this function after env, env_vars and the parser have been properly initialized.
|
||||
register_metrics_collection_atexit(
|
||||
SConsToolingMetrics.generate_metrics, {
|
||||
"utc_starttime": datetime.utcnow(),
|
||||
"env_vars": env_vars,
|
||||
"env": env,
|
||||
"parser": _parser,
|
||||
"args": sys.argv,
|
||||
"exit_hook": initialize_exit_hook(),
|
||||
})
|
||||
|
||||
if get_option('build-metrics'):
|
||||
env['BUILD_METRICS_ARTIFACTS_DIR'] = '$BUILD_ROOT/$VARIANT_DIR'
|
||||
|
||||
@ -7,7 +7,7 @@ import math
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
@ -19,7 +19,7 @@ from evergreen import EvergreenApi, RetryingEvergreenApi
|
||||
|
||||
from buildscripts.ciconfig.evergreen import (EvergreenProjectConfig, parse_evergreen_file)
|
||||
from buildscripts.resmoke_proxy.resmoke_proxy import ResmokeProxyService
|
||||
from buildscripts.timeouts.timeout_service import (TimeoutParams, TimeoutService, TimeoutSettings)
|
||||
from buildscripts.timeouts.timeout_service import (TimeoutParams, TimeoutService)
|
||||
from buildscripts.util.cmdutils import enable_logging
|
||||
from buildscripts.util.taskname import determine_task_base_name
|
||||
|
||||
@ -372,9 +372,6 @@ def main():
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - HISTORY_LOOKBACK
|
||||
|
||||
timeout_override = timedelta(seconds=options.timeout) if options.timeout else None
|
||||
exec_timeout_override = timedelta(
|
||||
seconds=options.exec_timeout) if options.exec_timeout else None
|
||||
@ -389,7 +386,6 @@ def main():
|
||||
binder.bind(
|
||||
EvergreenApi,
|
||||
RetryingEvergreenApi.get_api(config_file=os.path.expanduser(options.evg_api_config)))
|
||||
binder.bind(TimeoutSettings, TimeoutSettings(start_date=start_date, end_date=end_date))
|
||||
binder.bind(TimeoutOverrides, timeout_overrides)
|
||||
binder.bind(EvergreenProjectConfig,
|
||||
parse_evergreen_file(os.path.expanduser(options.evg_project_config)))
|
||||
|
||||
@ -61,8 +61,8 @@ def generate_version_expansions():
|
||||
raise ValueError("Unable to parse version from stdin and no version.json provided")
|
||||
|
||||
if version_parts[0]:
|
||||
expansions["suffix"] = "latest"
|
||||
expansions["src_suffix"] = "latest"
|
||||
expansions["suffix"] = "v6.2-latest"
|
||||
expansions["src_suffix"] = "v6.2-latest"
|
||||
expansions["is_release"] = "false"
|
||||
else:
|
||||
expansions["suffix"] = version_line
|
||||
|
||||
@ -30,7 +30,6 @@ Generate a file containing a list of disabled feature flags.
|
||||
Used by resmoke.py to run only feature flag tests.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
@ -43,6 +42,7 @@ sys.path.append(os.path.normpath(os.path.join(os.path.abspath(__file__), '../../
|
||||
|
||||
# pylint: disable=wrong-import-position
|
||||
import buildscripts.idl.lib as lib
|
||||
from buildscripts.idl.idl import parser
|
||||
|
||||
|
||||
def is_third_party_idl(idl_path: str) -> bool:
|
||||
@ -56,13 +56,14 @@ def is_third_party_idl(idl_path: str) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def gen_all_feature_flags(idl_dir: str, import_dirs: List[str]):
|
||||
def gen_all_feature_flags(idl_dir: str = os.getcwd()):
|
||||
"""Generate a list of all feature flags."""
|
||||
all_flags = []
|
||||
for idl_path in sorted(lib.list_idls(idl_dir)):
|
||||
if is_third_party_idl(idl_path):
|
||||
continue
|
||||
for feature_flag in lib.parse_idl(idl_path, import_dirs).spec.feature_flags:
|
||||
doc = parser.parse_file(open(idl_path), idl_path)
|
||||
for feature_flag in doc.spec.feature_flags:
|
||||
if feature_flag.default.literal != "true":
|
||||
all_flags.append(feature_flag.name)
|
||||
|
||||
@ -72,18 +73,16 @@ def gen_all_feature_flags(idl_dir: str, import_dirs: List[str]):
|
||||
return list(set(all_flags) - set(force_disabled_flags))
|
||||
|
||||
|
||||
def gen_all_feature_flags_file(filename: str = lib.ALL_FEATURE_FLAG_FILE):
|
||||
flags = gen_all_feature_flags()
|
||||
with open(filename, "w") as output_file:
|
||||
output_file.write("\n".join(flags))
|
||||
print("Generated: ", os.path.realpath(output_file.name))
|
||||
|
||||
|
||||
def main():
|
||||
"""Run the main function."""
|
||||
arg_parser = argparse.ArgumentParser(description=__doc__)
|
||||
arg_parser.add_argument("--import-dir", dest="import_dirs", type=str, action="append",
|
||||
help="Directory to search for IDL import files")
|
||||
|
||||
args = arg_parser.parse_args()
|
||||
|
||||
flags = gen_all_feature_flags(os.getcwd(), args.import_dirs)
|
||||
with open(lib.ALL_FEATURE_FLAG_FILE, "w") as output_file:
|
||||
for flag in flags:
|
||||
output_file.write("%s\n" % flag)
|
||||
gen_all_feature_flags_file()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@ -1023,7 +1023,7 @@ def _propagate_globals(spec):
|
||||
idltype.cpp_type = _prefix_with_namespace(cpp_namespace, idltype.cpp_type)
|
||||
|
||||
|
||||
def _parse(stream, error_file_name):
|
||||
def parse_file(stream, error_file_name):
|
||||
# type: (Any, str) -> syntax.IDLParsedSpec
|
||||
"""
|
||||
Parse a YAML document into an idl.syntax tree.
|
||||
@ -1125,7 +1125,7 @@ def parse(stream, input_file_name, resolver):
|
||||
input_file_name: a file name for error messages to use, and to help resolve imported files.
|
||||
"""
|
||||
|
||||
root_doc = _parse(stream, input_file_name)
|
||||
root_doc = parse_file(stream, input_file_name)
|
||||
|
||||
if root_doc.errors:
|
||||
return root_doc
|
||||
@ -1162,7 +1162,7 @@ def parse(stream, input_file_name, resolver):
|
||||
|
||||
# Parse imported file
|
||||
with resolver.open(resolved_file_name) as file_stream:
|
||||
parsed_doc = _parse(file_stream, resolved_file_name)
|
||||
parsed_doc = parse_file(file_stream, resolved_file_name)
|
||||
|
||||
# Check for errors
|
||||
if parsed_doc.errors:
|
||||
|
||||
@ -5,12 +5,13 @@ import multiprocessing
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import traceback
|
||||
from typing import Any, Dict, List, Optional
|
||||
import distro
|
||||
import git
|
||||
from pydantic import BaseModel
|
||||
|
||||
from buildscripts.metrics.tooling_exit_hook import _ExitHook
|
||||
|
||||
# pylint: disable=bare-except
|
||||
|
||||
SCONS_ENV_FILE = "scons_env.env"
|
||||
@ -20,6 +21,12 @@ SCONS_SECTION_HEADER = "SCONS_ENV"
|
||||
class BaseMetrics(BaseModel):
|
||||
"""Base class for an metrics object."""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def generate_metrics(cls, **kwargs):
|
||||
"""Generate metrics."""
|
||||
raise NotImplementedError
|
||||
|
||||
@abstractmethod
|
||||
def is_malformed(self) -> bool:
|
||||
"""Confirm whether this instance has all expected fields."""
|
||||
@ -35,14 +42,14 @@ class BuildInfo(BaseMetrics):
|
||||
artifact_dir: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def get_scons_build_info(
|
||||
def generate_metrics(
|
||||
cls,
|
||||
utc_starttime: datetime,
|
||||
env_vars: "SCons.Variables.Variables",
|
||||
env: "SCons.Script.SConscript.SConsEnvironment",
|
||||
parser: "SCons.Script.SConsOptions.SConsOptionParser",
|
||||
args: List[str],
|
||||
):
|
||||
): # pylint: disable=arguments-differ
|
||||
"""Get SCons build info to the best of our ability."""
|
||||
artifact_dir = cls._get_scons_artifact_dir(env)
|
||||
return cls(
|
||||
@ -59,19 +66,22 @@ class BuildInfo(BaseMetrics):
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Get the environment variables options that can be set by users."""
|
||||
|
||||
artifact_dir = BuildInfo._get_scons_artifact_dir(env)
|
||||
artifact_dir = artifact_dir if artifact_dir else '.'
|
||||
scons_env_filepath = f'{artifact_dir}/{SCONS_ENV_FILE}'
|
||||
try:
|
||||
# Use SCons built-in method to save environment variables to a file
|
||||
env_vars.Save(SCONS_ENV_FILE, env)
|
||||
env_vars.Save(scons_env_filepath, env)
|
||||
|
||||
# Add a section header to the file so we can easily parse with ConfigParser
|
||||
with open(SCONS_ENV_FILE, 'r') as original:
|
||||
with open(scons_env_filepath, 'r') as original:
|
||||
data = original.read()
|
||||
with open(SCONS_ENV_FILE, 'w') as modified:
|
||||
with open(scons_env_filepath, 'w') as modified:
|
||||
modified.write(f"[{SCONS_SECTION_HEADER}]\n" + data)
|
||||
|
||||
# Parse file using config parser
|
||||
config = configparser.ConfigParser()
|
||||
config.read(SCONS_ENV_FILE)
|
||||
config.read(scons_env_filepath)
|
||||
str_dict = dict(config[SCONS_SECTION_HEADER])
|
||||
return {key: eval(val) for key, val in str_dict.items()} # pylint: disable=eval-used
|
||||
except:
|
||||
@ -118,52 +128,27 @@ class BuildInfo(BaseMetrics):
|
||||
return None in [self.artifact_dir, self.env, self.options, self.build_artifacts]
|
||||
|
||||
|
||||
class ExitInfo(BaseMetrics):
|
||||
"""Class to store tooling exit information."""
|
||||
|
||||
exit_code: Optional[int]
|
||||
exception: Optional[str]
|
||||
stacktrace: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def get_resmoke_exit_info(cls):
|
||||
"""Get the current exit info."""
|
||||
exc = sys.exc_info()[1]
|
||||
return cls(
|
||||
exit_code=0 if not exc else exc.code if exc.__class__ == SystemExit else 1,
|
||||
exception=exc.__class__.__name__ if exc else None,
|
||||
stacktrace=traceback.format_exc() if exc else None,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_scons_exit_info(cls, exit_code):
|
||||
"""Get the current exit info using the given exit code."""
|
||||
return cls(
|
||||
exit_code=exit_code if isinstance(exit_code, int) else None,
|
||||
exception=None,
|
||||
stacktrace=None,
|
||||
)
|
||||
|
||||
def is_malformed(self):
|
||||
"""Return True if this object is missing an exit code."""
|
||||
return self.exit_code is None
|
||||
|
||||
|
||||
class HostInfo(BaseMetrics):
|
||||
"""Class to store host information."""
|
||||
|
||||
ip_address: Optional[str]
|
||||
host_os: str
|
||||
num_cores: int
|
||||
memory: Optional[float]
|
||||
|
||||
@classmethod
|
||||
def get_host_info(cls):
|
||||
def generate_metrics(cls): # pylint: disable=arguments-differ
|
||||
"""Get the host info to the best of our ability."""
|
||||
try:
|
||||
ip_address = socket.gethostbyname(socket.gethostname())
|
||||
except:
|
||||
ip_address = None
|
||||
try:
|
||||
memory = cls._get_memory()
|
||||
except:
|
||||
memory = None
|
||||
return cls(
|
||||
ip_address=ip_address,
|
||||
host_os=distro.name(pretty=True),
|
||||
num_cores=multiprocessing.cpu_count(),
|
||||
memory=memory,
|
||||
@ -174,9 +159,9 @@ class HostInfo(BaseMetrics):
|
||||
"""Get total memory of the host system."""
|
||||
return os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') / (1024.**3)
|
||||
|
||||
def is_malformed(self):
|
||||
def is_malformed(self) -> bool:
|
||||
"""Confirm whether this instance has all expected fields."""
|
||||
return self.memory is None
|
||||
return None in [self.memory, self.ip_address]
|
||||
|
||||
|
||||
class GitInfo(BaseMetrics):
|
||||
@ -188,7 +173,7 @@ class GitInfo(BaseMetrics):
|
||||
repo_name: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def get_git_info(cls, filepath: str):
|
||||
def generate_metrics(cls, filepath: str): # pylint: disable=arguments-differ
|
||||
"""Get the git info for a repo to the best of our ability."""
|
||||
try:
|
||||
commit_hash = git.Repo(filepath).head.commit.hexsha
|
||||
@ -212,7 +197,7 @@ class GitInfo(BaseMetrics):
|
||||
repo_name=repo_name,
|
||||
)
|
||||
|
||||
def is_malformed(self):
|
||||
def is_malformed(self) -> bool:
|
||||
"""Confirm whether this instance has all expected fields."""
|
||||
return None in [self.commit_hash, self.branch_name, self.repo_name]
|
||||
|
||||
@ -225,7 +210,7 @@ def _get_modules_git_info():
|
||||
module_git_info = []
|
||||
try:
|
||||
module_git_info = [
|
||||
GitInfo.get_git_info(os.path.join(MODULES_FILEPATH, module))
|
||||
GitInfo.generate_metrics(os.path.join(MODULES_FILEPATH, module))
|
||||
for module in os.listdir(MODULES_FILEPATH)
|
||||
if os.path.isdir(os.path.join(MODULES_FILEPATH, module))
|
||||
]
|
||||
@ -234,73 +219,79 @@ def _get_modules_git_info():
|
||||
return module_git_info
|
||||
|
||||
|
||||
class ToolingMetrics(BaseMetrics):
|
||||
"""Class to store tooling metrics."""
|
||||
class ResmokeToolingMetrics(BaseMetrics):
|
||||
"""Class to store resmoke tooling metrics."""
|
||||
|
||||
source: str
|
||||
utc_starttime: datetime
|
||||
utc_endtime: datetime
|
||||
host_info: HostInfo
|
||||
git_info: GitInfo
|
||||
exit_info: ExitInfo
|
||||
build_info: Optional[BuildInfo]
|
||||
exit_code: Optional[int]
|
||||
command: List[str]
|
||||
module_info: List[GitInfo]
|
||||
ip_address: Optional[str]
|
||||
|
||||
@classmethod
|
||||
def get_resmoke_metrics(
|
||||
def generate_metrics(
|
||||
cls,
|
||||
utc_starttime: datetime,
|
||||
):
|
||||
exit_hook: _ExitHook,
|
||||
): # pylint: disable=arguments-differ
|
||||
"""Get resmoke metrics to the best of our ability."""
|
||||
try:
|
||||
ip_address = socket.gethostbyname(socket.gethostname())
|
||||
except:
|
||||
ip_address = None
|
||||
return cls(
|
||||
source='resmoke',
|
||||
utc_starttime=utc_starttime,
|
||||
utc_endtime=datetime.utcnow(),
|
||||
host_info=HostInfo.get_host_info(),
|
||||
git_info=GitInfo.get_git_info('.'),
|
||||
exit_info=ExitInfo.get_resmoke_exit_info(),
|
||||
build_info=None,
|
||||
module_info=_get_modules_git_info(),
|
||||
host_info=HostInfo.generate_metrics(),
|
||||
git_info=GitInfo.generate_metrics('.'),
|
||||
exit_code=exit_hook.exit_code if isinstance(exit_hook.exit_code, int) else None,
|
||||
command=sys.argv,
|
||||
ip_address=ip_address,
|
||||
module_info=_get_modules_git_info(),
|
||||
)
|
||||
|
||||
def is_malformed(self) -> bool:
|
||||
"""Confirm whether this instance has all expected fields."""
|
||||
sub_metrics = self.module_info + [self.git_info] + [self.host_info]
|
||||
return self.exit_code is None or any(metrics.is_malformed() for metrics in sub_metrics)
|
||||
|
||||
|
||||
class SConsToolingMetrics(BaseMetrics):
|
||||
"""Class to store scons tooling metrics."""
|
||||
|
||||
source: str
|
||||
utc_starttime: datetime
|
||||
utc_endtime: datetime
|
||||
host_info: HostInfo
|
||||
git_info: GitInfo
|
||||
exit_code: Optional[int]
|
||||
build_info: BuildInfo
|
||||
command: List[str]
|
||||
module_info: List[GitInfo]
|
||||
|
||||
@classmethod
|
||||
def get_scons_metrics(
|
||||
def generate_metrics(
|
||||
cls,
|
||||
utc_starttime: datetime,
|
||||
env_vars: "SCons.Variables.Variables",
|
||||
env: "SCons.Script.SConscript.SConsEnvironment",
|
||||
parser: "SCons.Script.SConsOptions.SConsOptionParser",
|
||||
args: List[str],
|
||||
exit_code: int,
|
||||
):
|
||||
exit_hook: _ExitHook,
|
||||
): # pylint: disable=arguments-differ
|
||||
"""Get scons metrics to the best of our ability."""
|
||||
try:
|
||||
ip_address = socket.gethostbyname(socket.gethostname())
|
||||
except:
|
||||
ip_address = None
|
||||
return cls(
|
||||
source='scons',
|
||||
utc_starttime=utc_starttime,
|
||||
utc_endtime=datetime.utcnow(),
|
||||
host_info=HostInfo.get_host_info(),
|
||||
git_info=GitInfo.get_git_info('.'),
|
||||
exit_info=ExitInfo.get_scons_exit_info(exit_code),
|
||||
build_info=BuildInfo.get_scons_build_info(utc_starttime, env_vars, env, parser, args),
|
||||
module_info=_get_modules_git_info(),
|
||||
host_info=HostInfo.generate_metrics(),
|
||||
git_info=GitInfo.generate_metrics('.'),
|
||||
build_info=BuildInfo.generate_metrics(utc_starttime, env_vars, env, parser, args),
|
||||
exit_code=exit_hook.exit_code if isinstance(exit_hook.exit_code, int) else None,
|
||||
command=sys.argv,
|
||||
ip_address=ip_address,
|
||||
module_info=_get_modules_git_info(),
|
||||
)
|
||||
|
||||
def is_malformed(self):
|
||||
def is_malformed(self) -> bool:
|
||||
"""Confirm whether this instance has all expected fields."""
|
||||
sub_metrics = [self.build_info] if self.source == 'scons' else []
|
||||
sub_metrics += self.module_info + [self.git_info] + [self.host_info] + [self.exit_info]
|
||||
return self.ip_address is None or any(metrics.is_malformed() for metrics in sub_metrics)
|
||||
sub_metrics = self.module_info + [self.git_info] + [self.host_info] + [self.build_info]
|
||||
return self.exit_code is None or any(metrics.is_malformed() for metrics in sub_metrics)
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
from buildscripts.metrics.metrics_datatypes import ToolingMetrics
|
||||
from buildscripts.metrics.tooling_metrics_utils import save_tooling_metrics, should_collect_metrics
|
||||
|
||||
logger = logging.getLogger('resmoke_tooling_metrics')
|
||||
|
||||
|
||||
def save_resmoke_tooling_metrics(utc_starttime: datetime):
|
||||
try:
|
||||
if not should_collect_metrics():
|
||||
return
|
||||
tooling_metrics = ToolingMetrics.get_resmoke_metrics(utc_starttime)
|
||||
save_tooling_metrics(tooling_metrics)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
logger.warning(
|
||||
"%s\nResmoke Metrics Collection Failed -- this is a non-issue.\nIf this message persists, feel free to reach out to #server-development-platform",
|
||||
exc)
|
||||
@ -1,71 +0,0 @@
|
||||
import atexit
|
||||
import datetime
|
||||
import logging
|
||||
import sys
|
||||
from typing import List
|
||||
|
||||
from buildscripts.metrics.metrics_datatypes import ToolingMetrics
|
||||
from buildscripts.metrics.tooling_metrics_utils import save_tooling_metrics, should_collect_metrics
|
||||
|
||||
logger = logging.getLogger('scons_tooling_metrics')
|
||||
|
||||
|
||||
class SConsExitHook(object):
|
||||
"""Plumb all sys.exit through this object so that we can access the exit code in atexit."""
|
||||
|
||||
def __init__(self):
|
||||
self.exit_code = None
|
||||
self._orig_exit = sys.exit
|
||||
|
||||
def __del__(self):
|
||||
sys.exit = self._orig_exit
|
||||
|
||||
def initialize(self):
|
||||
sys.exit = self.exit
|
||||
|
||||
def exit(self, code=0):
|
||||
self.exit_code = code
|
||||
self._orig_exit(code)
|
||||
|
||||
|
||||
# This method should only be used when registered on atexit
|
||||
def _save_scons_tooling_metrics(
|
||||
utc_starttime: datetime,
|
||||
env_vars: "SCons.Variables.Variables",
|
||||
env: "SCons.Script.SConscript.SConsEnvironment",
|
||||
parser: "SCons.Script.SConsOptions.SConsOptionParser",
|
||||
args: List[str],
|
||||
exit_hook: SConsExitHook,
|
||||
):
|
||||
"""Save SCons tooling metrics to atlas cluster."""
|
||||
try:
|
||||
if not should_collect_metrics():
|
||||
return
|
||||
tooling_metrics = ToolingMetrics.get_scons_metrics(utc_starttime, env_vars, env, parser,
|
||||
args, exit_hook.exit_code)
|
||||
save_tooling_metrics(tooling_metrics)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
logger.warning(
|
||||
"%sSCons Metrics Collection Failed -- this is a non-issue.\nIf this message persists, feel free to reach out to #server-development-platform",
|
||||
exc)
|
||||
|
||||
|
||||
def setup_scons_metrics_collection_atexit(
|
||||
utc_starttime: datetime,
|
||||
env_vars: "SCons.Variables.Variables",
|
||||
env: "SCons.Script.SConscript.SConsEnvironment",
|
||||
parser: "SCons.Script.SConsOptions.SConsOptionParser",
|
||||
args: List[str],
|
||||
) -> None:
|
||||
"""Register an atexit method for scons metrics collection."""
|
||||
scons_exit_hook = SConsExitHook()
|
||||
scons_exit_hook.initialize()
|
||||
atexit.register(
|
||||
_save_scons_tooling_metrics,
|
||||
utc_starttime,
|
||||
env_vars,
|
||||
env,
|
||||
parser,
|
||||
args,
|
||||
scons_exit_hook,
|
||||
)
|
||||
36
buildscripts/metrics/tooling_exit_hook.py
Normal file
36
buildscripts/metrics/tooling_exit_hook.py
Normal file
@ -0,0 +1,36 @@
|
||||
import sys
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
# pylint: disable=redefined-outer-name
|
||||
|
||||
|
||||
# DO NOT INITIALIZE DIRECTLY -- This is intended to be a singleton.
|
||||
class _ExitHook(object):
|
||||
"""Plumb all sys.exit through this object so that we can access the exit code in atexit."""
|
||||
|
||||
def __init__(self):
|
||||
self.exit_code = 0
|
||||
self._orig_exit = sys.exit
|
||||
sys.exit = self.exit
|
||||
|
||||
def __del__(self):
|
||||
sys.exit = self._orig_exit
|
||||
|
||||
def exit(self, code=0):
|
||||
self.exit_code = code
|
||||
self._orig_exit(code)
|
||||
|
||||
|
||||
SINGLETON_TOOLING_METRICS_EXIT_HOOK = None
|
||||
|
||||
|
||||
# Always use this method when initializing _ExitHook -- This guarantees you are using the singleton
|
||||
# initialize the exit hook as early as possible to ensure we capture the error.
|
||||
def initialize_exit_hook() -> None:
|
||||
"""Initialize the exit hook."""
|
||||
try:
|
||||
if not SINGLETON_TOOLING_METRICS_EXIT_HOOK:
|
||||
SINGLETON_TOOLING_METRICS_EXIT_HOOK = _ExitHook()
|
||||
except UnboundLocalError as _:
|
||||
SINGLETON_TOOLING_METRICS_EXIT_HOOK = _ExitHook()
|
||||
return SINGLETON_TOOLING_METRICS_EXIT_HOOK
|
||||
@ -1,20 +1,17 @@
|
||||
import atexit
|
||||
import logging
|
||||
import os
|
||||
import asyncio
|
||||
from typing import Optional
|
||||
from git import Repo
|
||||
from typing import Any, Callable, Dict
|
||||
import pymongo
|
||||
|
||||
from buildscripts.metrics.metrics_datatypes import ToolingMetrics
|
||||
|
||||
logger = logging.getLogger('tooling_metrics_utils')
|
||||
logger = logging.getLogger('tooling_metrics')
|
||||
|
||||
INTERNAL_TOOLING_METRICS_HOSTNAME = "mongodb+srv://dev-metrics-pl-0.kewhj.mongodb.net"
|
||||
INTERNAL_TOOLING_METRICS_USERNAME = "internal_tooling_user"
|
||||
INTERNAL_TOOLING_METRICS_PASSWORD = "internal_tooling_user"
|
||||
|
||||
|
||||
def _get_internal_tooling_metrics_client():
|
||||
def _get_internal_tooling_metrics_client() -> pymongo.MongoClient:
|
||||
"""Retrieve client for internal MongoDB tooling metrics cluster."""
|
||||
return pymongo.MongoClient(
|
||||
host=INTERNAL_TOOLING_METRICS_HOSTNAME,
|
||||
@ -24,28 +21,20 @@ def _get_internal_tooling_metrics_client():
|
||||
serverSelectionTimeoutMS=1000,
|
||||
connectTimeoutMS=1000,
|
||||
waitQueueTimeoutMS=1000,
|
||||
retryWrites=False,
|
||||
)
|
||||
|
||||
|
||||
EXPECTED_TOOLCHAIN_LOCATION = "/opt/mongodbtoolchain"
|
||||
|
||||
|
||||
def _toolchain_exists() -> bool:
|
||||
"""Check if the internal MongoDB toolchain exists."""
|
||||
return os.path.exists(EXPECTED_TOOLCHAIN_LOCATION)
|
||||
|
||||
|
||||
def _git_user_exists() -> Optional[str]:
|
||||
"""Check if a git user email exists."""
|
||||
try:
|
||||
return Repo('.').config_reader().get_value("user", "email", None)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
MONGOD_INTENRAL_DISTRO_FILEPATH = '/etc/mongodb-distro-name'
|
||||
|
||||
|
||||
def _is_virtual_workstation() -> bool:
|
||||
"""Detect whether this is a MongoDB internal virtual workstation."""
|
||||
return _toolchain_exists() and _git_user_exists()
|
||||
try:
|
||||
with open(MONGOD_INTENRAL_DISTRO_FILEPATH, 'r') as file:
|
||||
return file.read().strip() == 'ubuntu1804-workstation'
|
||||
except Exception as _: # pylint: disable=broad-except
|
||||
return False
|
||||
|
||||
|
||||
TOOLING_METRICS_OPT_OUT = "TOOLING_METRICS_OPT_OUT"
|
||||
@ -56,26 +45,32 @@ def _has_metrics_opt_out() -> bool:
|
||||
return os.environ.get(TOOLING_METRICS_OPT_OUT, None) == '1'
|
||||
|
||||
|
||||
def should_collect_metrics() -> bool:
|
||||
def _should_collect_metrics() -> bool:
|
||||
"""Determine whether to collect tooling metrics."""
|
||||
return _is_virtual_workstation() and not _has_metrics_opt_out()
|
||||
|
||||
|
||||
async def _save_metrics(metrics: ToolingMetrics) -> None:
|
||||
"""Save tooling metrics data."""
|
||||
client = _get_internal_tooling_metrics_client()
|
||||
client.metrics.tooling_metrics.insert_one(metrics.dict())
|
||||
|
||||
|
||||
def save_tooling_metrics(tooling_metrics: ToolingMetrics) -> None:
|
||||
"""Persist tooling metrics data to MongoDB Internal Atlas Cluster."""
|
||||
# DO NOT USE DIRECTLY -- This is only to be used when metrics collection is registered atexit
|
||||
def _save_metrics(
|
||||
generate_metrics_function: Callable,
|
||||
generate_metrics_args: Dict[str, Any],
|
||||
) -> None:
|
||||
"""Save metrics to the atlas cluster."""
|
||||
try:
|
||||
asyncio.run(asyncio.wait_for(_save_metrics(tooling_metrics), timeout=1.0))
|
||||
except asyncio.TimeoutError as exc:
|
||||
logger.warning(
|
||||
"%s\nTimeout: Tooling metrics collection is not available -- this is a non-issue.\nIf this message persists, feel free to reach out to #server-development-platform",
|
||||
exc)
|
||||
client = _get_internal_tooling_metrics_client()
|
||||
metrics = generate_metrics_function(**generate_metrics_args)
|
||||
client.metrics.tooling_metrics.insert_one(metrics.dict())
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
logger.warning(
|
||||
"%s\nUnexpected: Tooling metrics collection is not available -- this is a non-issue.\nIf this message persists, feel free to reach out to #server-development-platform",
|
||||
"%s\n\nInternal Metrics Collection Failed -- this is a non-issue.\nIf this message persists, feel free to reach out to #server-dev-platform",
|
||||
exc)
|
||||
|
||||
|
||||
# This is the only util that should be used externally
|
||||
def register_metrics_collection_atexit(
|
||||
generate_metrics_function: Callable,
|
||||
generate_metrics_args: Dict[str, Any],
|
||||
) -> None:
|
||||
"""Register metrics collection on atexit."""
|
||||
if _should_collect_metrics():
|
||||
atexit.register(_save_metrics, generate_metrics_function, generate_metrics_args)
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
import json
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
from statistics import mean
|
||||
from typing import Dict, List
|
||||
|
||||
@ -13,7 +12,8 @@ import structlog
|
||||
from buildscripts.resmokelib.testing.report import TestInfo, TestReport
|
||||
from buildscripts.resmokelib.utils import get_task_name_without_suffix
|
||||
from buildscripts.util.cmdutils import enable_logging
|
||||
from evergreen import RetryingEvergreenApi, TestStats
|
||||
|
||||
from buildscripts.util.teststats import HistoricTaskData, HistoricalTestInformation
|
||||
|
||||
LOGGER = structlog.get_logger("buildscripts.resmoke_tests_runtime_validate")
|
||||
|
||||
@ -34,17 +34,12 @@ def parse_resmoke_report(report_file: str) -> List[TestInfo]:
|
||||
return [test_info for test_info in test_report.test_infos if "jstests" in test_info.test_file]
|
||||
|
||||
|
||||
def get_historic_stats(evg_api_config: str, project_id: str, test_files: List[str], task_name: str,
|
||||
build_variant: str) -> List[TestStats]:
|
||||
def get_historic_stats(project_id: str, task_name: str,
|
||||
build_variant: str) -> List[HistoricalTestInformation]:
|
||||
"""Get historic test stats."""
|
||||
evg_api = RetryingEvergreenApi.get_api(config_file=evg_api_config)
|
||||
before_date = datetime.today()
|
||||
after_date = before_date - timedelta(days=LOOK_BACK_NUM_DAYS)
|
||||
base_task_name = get_task_name_without_suffix(task_name, build_variant).replace(
|
||||
BURN_IN_PREFIX, "")
|
||||
return evg_api.test_stats_by_project(project_id=project_id, after_date=after_date,
|
||||
before_date=before_date, tests=test_files,
|
||||
tasks=[base_task_name], variants=[build_variant])
|
||||
return HistoricTaskData.get_stats_from_s3(project_id, base_task_name, build_variant)
|
||||
|
||||
|
||||
def make_stats_map(stats: List[_TestData]) -> Dict[str, List[float]]:
|
||||
@ -63,13 +58,10 @@ def make_stats_map(stats: List[_TestData]) -> Dict[str, List[float]]:
|
||||
@click.command()
|
||||
@click.option("--resmoke-report-file", type=str, required=True,
|
||||
help="Location of resmoke's report JSON file.")
|
||||
@click.option("--evg-api-config", type=str, required=True,
|
||||
help="Location of evergreen api configuration.")
|
||||
@click.option("--project-id", type=str, required=True, help="Evergreen project id.")
|
||||
@click.option("--build-variant", type=str, required=True, help="Evergreen build variant name.")
|
||||
@click.option("--task-name", type=str, required=True, help="Evergreen task name.")
|
||||
def main(resmoke_report_file: str, evg_api_config: str, project_id: str, build_variant: str,
|
||||
task_name: str) -> None:
|
||||
def main(resmoke_report_file: str, project_id: str, build_variant: str, task_name: str) -> None:
|
||||
"""Compare resmoke tests runtime with historic stats."""
|
||||
enable_logging(verbose=False)
|
||||
|
||||
@ -79,10 +71,9 @@ def main(resmoke_report_file: str, evg_api_config: str, project_id: str, build_v
|
||||
for test_info in current_test_infos
|
||||
])
|
||||
|
||||
historic_stats = get_historic_stats(evg_api_config, project_id, list(current_stats_map.keys()),
|
||||
task_name, build_variant)
|
||||
historic_stats = get_historic_stats(project_id, task_name, build_variant)
|
||||
historic_stats_map = make_stats_map([
|
||||
_TestData(test_stats.test_file, test_stats.avg_duration_pass)
|
||||
_TestData(test_stats.test_name, test_stats.avg_duration_pass)
|
||||
for test_stats in historic_stats
|
||||
])
|
||||
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
- jstests/core/find_and_modify.js
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
@ -82,6 +83,7 @@
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_pipeline_update.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# These test run commands using legacy queries, which are not supported on sessions.
|
||||
- jstests/core/comment_field.js
|
||||
|
||||
@ -222,6 +222,7 @@ selector:
|
||||
- jstests/core/find_and_modify.js
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Does not support tojson of command objects.
|
||||
- jstests/core/SERVER-23626.js
|
||||
|
||||
@ -30,6 +30,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_pipeline_update.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# These test run commands using legacy queries, which are not supported on sessions.
|
||||
- jstests/core/comment_field.js
|
||||
|
||||
@ -23,6 +23,7 @@ selector:
|
||||
- jstests/core/find_and_modify.js
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -29,6 +29,7 @@ selector:
|
||||
- jstests/core/find_and_modify_pipeline_update.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# These tests rely on the assumption that an update command is run only once.
|
||||
- jstests/core/find_and_modify_metrics.js
|
||||
|
||||
@ -22,6 +22,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -43,6 +43,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -43,6 +43,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -43,6 +43,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -23,6 +23,7 @@ selector:
|
||||
- jstests/core/find_and_modify.js
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -42,6 +42,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -41,6 +41,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -41,6 +41,7 @@ selector:
|
||||
- jstests/core/find_and_modify2.js
|
||||
- jstests/core/find_and_modify_server6865.js
|
||||
- jstests/core/fts_find_and_modify.js
|
||||
- jstests/core/project_with_collation.js
|
||||
|
||||
# Stepdown commands during fsync lock will fail.
|
||||
- jstests/core/currentop.js
|
||||
|
||||
@ -4,7 +4,9 @@ from datetime import datetime
|
||||
import time
|
||||
import os
|
||||
import psutil
|
||||
from buildscripts.metrics.resmoke_tooling_metrics import save_resmoke_tooling_metrics
|
||||
from buildscripts.metrics.metrics_datatypes import ResmokeToolingMetrics
|
||||
from buildscripts.metrics.tooling_exit_hook import initialize_exit_hook
|
||||
from buildscripts.metrics.tooling_metrics_utils import register_metrics_collection_atexit
|
||||
from buildscripts.resmokelib import parser
|
||||
|
||||
|
||||
@ -25,7 +27,8 @@ def main(argv):
|
||||
"For example: resmoke.py run -h\n"
|
||||
"Note: bisect and setup-multiversion subcommands have been moved to db-contrib-tool (https://github.com/10gen/db-contrib-tool#readme).\n"
|
||||
)
|
||||
try:
|
||||
subcommand.execute()
|
||||
finally:
|
||||
save_resmoke_tooling_metrics(datetime.utcfromtimestamp(__start_time))
|
||||
register_metrics_collection_atexit(ResmokeToolingMetrics.generate_metrics, {
|
||||
"utc_starttime": datetime.utcfromtimestamp(__start_time),
|
||||
"exit_hook": initialize_exit_hook()
|
||||
})
|
||||
subcommand.execute()
|
||||
|
||||
@ -92,8 +92,9 @@ DEFAULTS = {
|
||||
"report_failure_status": "fail",
|
||||
"report_file": None,
|
||||
"run_all_feature_flag_tests": False,
|
||||
"run_all_feature_flags_no_tests": False,
|
||||
"run_no_feature_flag_tests": False,
|
||||
"additional_feature_flags": None,
|
||||
"additional_feature_flags_file": None,
|
||||
"seed": int(time.time() * 256), # Taken from random.py code in Python 2.7.
|
||||
"service_executor": None,
|
||||
"shell_conn_string": None,
|
||||
@ -379,8 +380,11 @@ INSTALL_DIR = None
|
||||
# Whether to run tests for feature flags.
|
||||
RUN_ALL_FEATURE_FLAG_TESTS = None
|
||||
|
||||
# Whether to run the server with feature flags. Defaults to true if `RUN_ALL_FEATURE_FLAG_TESTS` is true.
|
||||
RUN_ALL_FEATURE_FLAGS = None
|
||||
# Whether to run the tests with enabled feature flags
|
||||
RUN_NO_FEATURE_FLAG_TESTS = None
|
||||
|
||||
# the path to a file containing feature flags
|
||||
ADDITIONAL_FEATURE_FLAGS_FILE = None
|
||||
|
||||
# List of enabled feature flags.
|
||||
ENABLED_FEATURE_FLAGS = []
|
||||
|
||||
@ -15,6 +15,7 @@ import shlex
|
||||
|
||||
import pymongo.uri_parser
|
||||
|
||||
from buildscripts.idl import gen_all_feature_flag_list
|
||||
from buildscripts.idl.lib import ALL_FEATURE_FLAG_FILE
|
||||
|
||||
from buildscripts.resmokelib import config as _config
|
||||
@ -52,14 +53,9 @@ def _validate_options(parser, args):
|
||||
"Cannot use --replayFile with additional test files listed on the command line invocation."
|
||||
)
|
||||
|
||||
if args.run_all_feature_flag_tests or args.run_all_feature_flags_no_tests:
|
||||
if not os.path.isfile(ALL_FEATURE_FLAG_FILE):
|
||||
parser.error(
|
||||
"To run tests with all feature flags, the %s file must exist and be placed in"
|
||||
" your working directory. The file can be downloaded from the artifacts tarball"
|
||||
" in Evergreen. Alternatively, if you know which feature flags you want to enable,"
|
||||
" you can use the --additionalFeatureFlags command line argument" %
|
||||
ALL_FEATURE_FLAG_FILE)
|
||||
if args.additional_feature_flags_file and not os.path.isfile(
|
||||
args.additional_feature_flags_file):
|
||||
parser.error("The specified additional feature flags file does not exist.")
|
||||
|
||||
def get_set_param_errors(process_params):
|
||||
agg_set_params = collections.defaultdict(list)
|
||||
@ -185,28 +181,36 @@ be invoked as either:
|
||||
- buildscripts/resmoke.py --installDir {shlex.quote(user_config['install_dir'])}""")
|
||||
raise RuntimeError(err)
|
||||
|
||||
def process_feature_flag_file(path):
|
||||
with open(path) as fd:
|
||||
return fd.read().split()
|
||||
|
||||
def setup_feature_flags():
|
||||
_config.RUN_ALL_FEATURE_FLAG_TESTS = config.pop("run_all_feature_flag_tests")
|
||||
_config.RUN_ALL_FEATURE_FLAGS = config.pop("run_all_feature_flags_no_tests")
|
||||
_config.RUN_NO_FEATURE_FLAG_TESTS = config.pop("run_no_feature_flag_tests")
|
||||
_config.ADDITIONAL_FEATURE_FLAGS_FILE = config.pop("additional_feature_flags_file")
|
||||
|
||||
# Running all feature flag tests implies running the fixtures with feature flags.
|
||||
if _config.RUN_ALL_FEATURE_FLAG_TESTS:
|
||||
_config.RUN_ALL_FEATURE_FLAGS = True
|
||||
print("Generating: ", ALL_FEATURE_FLAG_FILE)
|
||||
gen_all_feature_flag_list.gen_all_feature_flags_file()
|
||||
|
||||
all_ff = []
|
||||
enabled_feature_flags = []
|
||||
try:
|
||||
with open(ALL_FEATURE_FLAG_FILE) as fd:
|
||||
all_ff = fd.read().split()
|
||||
all_ff = process_feature_flag_file(ALL_FEATURE_FLAG_FILE)
|
||||
except FileNotFoundError:
|
||||
# If we ask resmoke to run with all feature flags, the feature flags file
|
||||
# needs to exist.
|
||||
if _config.RUN_ALL_FEATURE_FLAGS:
|
||||
if _config.RUN_ALL_FEATURE_FLAG_TESTS or _config.RUN_NO_FEATURE_FLAG_TESTS:
|
||||
raise
|
||||
|
||||
if _config.RUN_ALL_FEATURE_FLAGS:
|
||||
if _config.RUN_ALL_FEATURE_FLAG_TESTS:
|
||||
enabled_feature_flags = all_ff[:]
|
||||
|
||||
if _config.ADDITIONAL_FEATURE_FLAGS_FILE:
|
||||
enabled_feature_flags.extend(
|
||||
process_feature_flag_file(_config.ADDITIONAL_FEATURE_FLAGS_FILE))
|
||||
|
||||
# Specify additional feature flags from the command line.
|
||||
# Set running all feature flag tests to True if this options is specified.
|
||||
additional_feature_flags = _tags_from_list(config.pop("additional_feature_flags"))
|
||||
@ -231,7 +235,7 @@ be invoked as either:
|
||||
_config.EXCLUDE_WITH_ANY_TAGS.extend(
|
||||
utils.default_if_none(_tags_from_list(config.pop("exclude_with_any_tags")), []))
|
||||
|
||||
if _config.RUN_ALL_FEATURE_FLAGS and not _config.RUN_ALL_FEATURE_FLAG_TESTS:
|
||||
if _config.RUN_NO_FEATURE_FLAG_TESTS:
|
||||
# Don't run any feature flag tests.
|
||||
_config.EXCLUDE_WITH_ANY_TAGS.extend(all_feature_flags)
|
||||
else:
|
||||
|
||||
@ -832,15 +832,19 @@ class RunPlugin(PluginInterface):
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--runAllFeatureFlagsNoTests", dest="run_all_feature_flags_no_tests",
|
||||
action="store_true", help=
|
||||
"Run MongoDB servers with all feature flags enabled but don't run any tests tagged with these feature flags; used for multiversion suites"
|
||||
)
|
||||
"--runNoFeatureFlagTests", dest="run_no_feature_flag_tests", action="store_true",
|
||||
help=("Do not run any tests tagged with enabled feature flags."
|
||||
" This argument has precedence over --runAllFeatureFlagTests"
|
||||
"; used for multiversion suites"))
|
||||
|
||||
parser.add_argument("--additionalFeatureFlags", dest="additional_feature_flags",
|
||||
action="append", metavar="featureFlag1, featureFlag2, ...",
|
||||
help="Additional feature flags")
|
||||
|
||||
parser.add_argument("--additionalFeatureFlagsFile", dest="additional_feature_flags_file",
|
||||
action="store", metavar="FILE",
|
||||
help="The path to a file with feature flags, delimited by newlines.")
|
||||
|
||||
parser.add_argument("--maxTestQueueSize", type=int, dest="max_test_queue_size",
|
||||
help=argparse.SUPPRESS)
|
||||
|
||||
@ -1188,15 +1192,6 @@ def to_local_args(input_args=None):
|
||||
if origin_suite is not None:
|
||||
setattr(parsed_args, "suite_files", origin_suite)
|
||||
|
||||
# Replace --runAllFeatureFlagTests with an explicit list of feature flags. The former relies on
|
||||
# all_feature_flags.txt which may not exist in the local dev environment.
|
||||
run_all_feature_flag_tests = getattr(parsed_args, "run_all_feature_flag_tests", None)
|
||||
if run_all_feature_flag_tests is not None:
|
||||
setattr(parsed_args, "additional_feature_flags", config.ENABLED_FEATURE_FLAGS)
|
||||
del parsed_args.run_all_feature_flag_tests
|
||||
|
||||
del parsed_args.run_all_feature_flags_no_tests
|
||||
|
||||
# The top-level parser has one subparser that contains all subcommand parsers.
|
||||
command_subparser = [
|
||||
action for action in parser._actions # pylint: disable=protected-access
|
||||
|
||||
@ -181,7 +181,7 @@ class MongoDFixture(interface.Fixture):
|
||||
|
||||
def get_driver_connection_url(self):
|
||||
"""Return the driver connection URL."""
|
||||
return "mongodb://" + self.get_internal_connection_string()
|
||||
return "mongodb://" + self.get_internal_connection_string() + "/?directConnection=true"
|
||||
|
||||
|
||||
# The below parameters define the default 'logComponentVerbosity' object passed to mongod processes
|
||||
@ -193,15 +193,16 @@ class MongoDFixture(interface.Fixture):
|
||||
# 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 = {
|
||||
"replication": {"rollback": 2}, "sharding": {"migration": 2}, "transaction": 4,
|
||||
"tenantMigration": 4
|
||||
"replication": {"rollback": 2}, "sharding": {"migration": 2, "rangeDeleter": 2},
|
||||
"transaction": 4, "tenantMigration": 4
|
||||
}
|
||||
|
||||
# The default verbosity setting for any mongod processes running in Evergreen i.e. started with an
|
||||
# Evergreen task id.
|
||||
DEFAULT_EVERGREEN_MONGOD_LOG_COMPONENT_VERBOSITY = {
|
||||
"replication": {"election": 4, "heartbeats": 2, "initialSync": 2, "rollback": 2},
|
||||
"sharding": {"migration": 2}, "storage": {"recovery": 2}, "transaction": 4, "tenantMigration": 4
|
||||
"replication": {"election": 4, "heartbeats": 2, "initialSync": 2,
|
||||
"rollback": 2}, "sharding": {"migration": 2, "rangeDeleter": 2},
|
||||
"storage": {"recovery": 2}, "transaction": 4, "tenantMigration": 4
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,41 +1,42 @@
|
||||
"""Unit tests for timeout_service.py."""
|
||||
import random
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import MagicMock
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from requests.exceptions import HTTPError
|
||||
from evergreen import EvergreenApi
|
||||
|
||||
import buildscripts.timeouts.timeout_service as under_test
|
||||
from buildscripts.resmoke_proxy.resmoke_proxy import ResmokeProxyService
|
||||
from buildscripts.util.teststats import HistoricTaskData
|
||||
from buildscripts.util.teststats import HistoricTaskData, HistoricTestInfo
|
||||
|
||||
# pylint: disable=invalid-name,protected-access
|
||||
|
||||
NS = "buildscripts.timeouts.timeout_service"
|
||||
|
||||
def build_mock_service(evg_api=None, resmoke_proxy=None):
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(weeks=2)
|
||||
timeout_settings = under_test.TimeoutSettings(
|
||||
end_date=end_date,
|
||||
start_date=start_date,
|
||||
)
|
||||
|
||||
def ns(relative_name): # pylint: disable=invalid-name
|
||||
"""Return a full name from a name relative to the test module"s name space."""
|
||||
return NS + "." + relative_name
|
||||
|
||||
|
||||
def build_mock_service(resmoke_proxy=None):
|
||||
return under_test.TimeoutService(
|
||||
evg_api=evg_api if evg_api else MagicMock(spec_set=EvergreenApi),
|
||||
resmoke_proxy=resmoke_proxy if resmoke_proxy else MagicMock(spec_set=ResmokeProxyService),
|
||||
timeout_settings=timeout_settings)
|
||||
resmoke_proxy=resmoke_proxy if resmoke_proxy else MagicMock(spec_set=ResmokeProxyService))
|
||||
|
||||
|
||||
def tst_stat_mock(file, duration, pass_count):
|
||||
return MagicMock(test_file=file, avg_duration_pass=duration, num_pass=pass_count)
|
||||
return MagicMock(test_name=file, avg_duration_pass=duration, num_pass=pass_count, hooks=[])
|
||||
|
||||
|
||||
def tst_runtime_mock(file, duration, pass_count):
|
||||
return MagicMock(test_name=file, avg_duration_pass=duration, num_pass=pass_count)
|
||||
|
||||
|
||||
class TestGetTimeoutEstimate(unittest.TestCase):
|
||||
def test_no_stats_should_return_default_timeout(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
mock_evg_api.test_stats_by_project.return_value = []
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api)
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_no_stats_should_return_default_timeout(self, from_s3_mock: MagicMock):
|
||||
timeout_service = build_mock_service()
|
||||
from_s3_mock.return_value = []
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
@ -48,13 +49,17 @@ class TestGetTimeoutEstimate(unittest.TestCase):
|
||||
|
||||
self.assertFalse(timeout.is_specified())
|
||||
|
||||
def test_a_test_with_missing_history_should_cause_a_default_timeout(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
test_stats = [tst_stat_mock(f"test_{i}.js", 60, 1) for i in range(30)]
|
||||
mock_evg_api.test_stats_by_project.return_value = test_stats
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_a_test_with_missing_history_should_cause_a_default_timeout(
|
||||
self, from_s3_mock: MagicMock):
|
||||
test_stats = [
|
||||
HistoricTestInfo(test_name=f"test_{i}.js", avg_duration=60, num_pass=1, hooks=[])
|
||||
for i in range(30)
|
||||
]
|
||||
from_s3_mock.return_value = HistoricTaskData(test_stats)
|
||||
mock_resmoke_proxy = MagicMock(spec_set=ResmokeProxyService)
|
||||
mock_resmoke_proxy.list_tests.return_value = ["test_with_no_stats.js"]
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api, resmoke_proxy=mock_resmoke_proxy)
|
||||
timeout_service = build_mock_service(resmoke_proxy=mock_resmoke_proxy)
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
@ -67,14 +72,19 @@ class TestGetTimeoutEstimate(unittest.TestCase):
|
||||
|
||||
self.assertFalse(timeout.is_specified())
|
||||
|
||||
def test_a_test_with_zero_runtime_history_should_cause_a_default_timeout(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
test_stats = [tst_stat_mock(f"test_{i}.js", 60, 1) for i in range(30)]
|
||||
test_stats.append(tst_stat_mock("zero.js", 0.0, 1))
|
||||
mock_evg_api.test_stats_by_project.return_value = test_stats
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_a_test_with_zero_runtime_history_should_cause_a_default_timeout(
|
||||
self, from_s3_mock: MagicMock):
|
||||
test_stats = [
|
||||
HistoricTestInfo(test_name=f"test_{i}.js", avg_duration=60, num_pass=1, hooks=[])
|
||||
for i in range(30)
|
||||
]
|
||||
test_stats.append(
|
||||
HistoricTestInfo(test_name="zero.js", avg_duration=0.0, num_pass=1, hooks=[]))
|
||||
from_s3_mock.return_value = HistoricTaskData(test_stats)
|
||||
mock_resmoke_proxy = MagicMock(spec_set=ResmokeProxyService)
|
||||
mock_resmoke_proxy.list_tests.return_value = [ts.test_file for ts in test_stats]
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api, resmoke_proxy=mock_resmoke_proxy)
|
||||
mock_resmoke_proxy.list_tests.return_value = [ts.test_name for ts in test_stats]
|
||||
timeout_service = build_mock_service(resmoke_proxy=mock_resmoke_proxy)
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
@ -87,15 +97,19 @@ class TestGetTimeoutEstimate(unittest.TestCase):
|
||||
|
||||
self.assertFalse(timeout.is_specified())
|
||||
|
||||
def test_all_tests_with_runtime_history_should_use_custom_timeout(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_all_tests_with_runtime_history_should_use_custom_timeout(self,
|
||||
from_s3_mock: MagicMock):
|
||||
n_tests = 30
|
||||
test_runtime = 600
|
||||
test_stats = [tst_stat_mock(f"test_{i}.js", test_runtime, 1) for i in range(n_tests)]
|
||||
mock_evg_api.test_stats_by_project.return_value = test_stats
|
||||
test_stats = [
|
||||
HistoricTestInfo(test_name=f"test_{i}.js", avg_duration=test_runtime, num_pass=1,
|
||||
hooks=[]) for i in range(n_tests)
|
||||
]
|
||||
from_s3_mock.return_value = HistoricTaskData(test_stats)
|
||||
mock_resmoke_proxy = MagicMock(spec_set=ResmokeProxyService)
|
||||
mock_resmoke_proxy.list_tests.return_value = [ts.test_file for ts in test_stats]
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api, resmoke_proxy=mock_resmoke_proxy)
|
||||
mock_resmoke_proxy.list_tests.return_value = [ts.test_name for ts in test_stats]
|
||||
timeout_service = build_mock_service(resmoke_proxy=mock_resmoke_proxy)
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
@ -149,10 +163,10 @@ class TestGetTaskHookOverhead(unittest.TestCase):
|
||||
|
||||
|
||||
class TestLookupHistoricStats(unittest.TestCase):
|
||||
def test_no_stats_from_evergreen_should_return_none(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
mock_evg_api.test_stats_by_project.return_value = []
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api)
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_no_stats_from_evergreen_should_return_none(self, from_s3_mock: MagicMock):
|
||||
from_s3_mock.return_value = None
|
||||
timeout_service = build_mock_service()
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
@ -165,10 +179,10 @@ class TestLookupHistoricStats(unittest.TestCase):
|
||||
|
||||
self.assertIsNone(stats)
|
||||
|
||||
def test_errors_from_evergreen_should_return_none(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
mock_evg_api.test_stats_by_project.side_effect = HTTPError("failed to connect")
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api)
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_errors_from_evergreen_should_return_none(self, from_s3_mock: MagicMock):
|
||||
from_s3_mock.side_effect = HTTPError("failed to connect")
|
||||
timeout_service = build_mock_service()
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
@ -181,11 +195,11 @@ class TestLookupHistoricStats(unittest.TestCase):
|
||||
|
||||
self.assertIsNone(stats)
|
||||
|
||||
def test_stats_from_evergreen_should_return_the_stats(self):
|
||||
mock_evg_api = MagicMock(spec_set=EvergreenApi)
|
||||
@patch(ns("HistoricTaskData.from_s3"))
|
||||
def test_stats_from_evergreen_should_return_the_stats(self, from_s3_mock: MagicMock):
|
||||
test_stats = [tst_stat_mock(f"test_{i}.js", 60, 1) for i in range(100)]
|
||||
mock_evg_api.test_stats_by_project.return_value = test_stats
|
||||
timeout_service = build_mock_service(evg_api=mock_evg_api)
|
||||
from_s3_mock.return_value = HistoricTaskData(test_stats)
|
||||
timeout_service = build_mock_service()
|
||||
timeout_params = under_test.TimeoutParams(
|
||||
evg_project="my project",
|
||||
build_variant="bv",
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
"""Unit tests for metrics_datatypes.py."""
|
||||
from datetime import datetime
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
|
||||
@ -9,6 +11,12 @@ import buildscripts.metrics.metrics_datatypes as under_test
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
|
||||
# Metrics collection is not supported for Windows
|
||||
if os.name == "nt":
|
||||
sys.exit()
|
||||
|
||||
MOCK_EXIT_HOOK = MagicMock(exit_code=0)
|
||||
|
||||
|
||||
@patch("buildscripts.metrics.metrics_datatypes.BuildInfo._get_scons_artifact_dir",
|
||||
return_value='/test')
|
||||
@ -18,94 +26,89 @@ class TestBuildInfo(unittest.TestCase):
|
||||
@patch("buildscripts.metrics.metrics_datatypes.BuildInfo._get_scons_options_dict",
|
||||
return_value={'opt': 'opt'})
|
||||
def test_build_info_valid(self, mock_env, mock_options, mock_artifact_dir):
|
||||
build_info = under_test.BuildInfo.get_scons_build_info(datetime.utcnow(), MagicMock(),
|
||||
MagicMock(), MagicMock(),
|
||||
MagicMock())
|
||||
build_info = under_test.BuildInfo.generate_metrics(datetime.utcnow(), MagicMock(),
|
||||
MagicMock(), MagicMock(), MagicMock())
|
||||
assert not build_info.is_malformed()
|
||||
|
||||
def test_build_info_malformed(self, mock_artifact_dir):
|
||||
build_info = under_test.BuildInfo.get_scons_build_info(datetime.utcnow(), MagicMock(),
|
||||
MagicMock(), MagicMock(),
|
||||
MagicMock())
|
||||
build_info = under_test.BuildInfo.generate_metrics(datetime.utcnow(), MagicMock(),
|
||||
MagicMock(), MagicMock(), MagicMock())
|
||||
assert build_info.is_malformed()
|
||||
|
||||
|
||||
class TestExitInfo(unittest.TestCase):
|
||||
@patch("sys.exc_info", return_value=(None, None, None))
|
||||
def test_resmoke_no_exc_info(self, mock_exc_info):
|
||||
exit_info = under_test.ExitInfo.get_resmoke_exit_info()
|
||||
assert not exit_info.is_malformed()
|
||||
|
||||
@patch("sys.exc_info", return_value=(None, ValueError(), None))
|
||||
def test_resmoke_with_exc_info(self, mock_exc_info):
|
||||
exit_info = under_test.ExitInfo.get_resmoke_exit_info()
|
||||
assert not exit_info.is_malformed()
|
||||
|
||||
def test_scons_exit_info_valid(self):
|
||||
exit_info = under_test.ExitInfo.get_scons_exit_info(0)
|
||||
assert not exit_info.is_malformed()
|
||||
|
||||
def test_scons_exit_info_malformed(self):
|
||||
exit_info = under_test.ExitInfo.get_scons_exit_info('string')
|
||||
assert exit_info.is_malformed()
|
||||
|
||||
|
||||
class TestHostInfo(unittest.TestCase):
|
||||
@patch("buildscripts.metrics.metrics_datatypes.HostInfo._get_memory", side_effect=Exception())
|
||||
def test_host_info_with_exc(self, mock_get_memory):
|
||||
host_info = under_test.HostInfo.get_host_info()
|
||||
host_info = under_test.HostInfo.generate_metrics()
|
||||
assert host_info.is_malformed()
|
||||
|
||||
# Mock this so that it passes when running the 'buildscripts_test' suite on Windows
|
||||
@patch("buildscripts.metrics.metrics_datatypes.HostInfo._get_memory", return_value=30)
|
||||
def test_host_info_no_exc(self, mock_get_memory):
|
||||
host_info = under_test.HostInfo.get_host_info()
|
||||
host_info = under_test.HostInfo.generate_metrics()
|
||||
assert not host_info.is_malformed()
|
||||
|
||||
|
||||
class TestGitInfo(unittest.TestCase):
|
||||
@patch("git.Repo", side_effect=Exception())
|
||||
def test_git_info_with_exc(self, mock_repo):
|
||||
git_info = under_test.GitInfo.get_git_info('.')
|
||||
git_info = under_test.GitInfo.generate_metrics('.')
|
||||
assert git_info.is_malformed()
|
||||
|
||||
def test_git_info_no_exc(self):
|
||||
git_info = under_test.GitInfo.get_git_info('.')
|
||||
git_info = under_test.GitInfo.generate_metrics('.')
|
||||
assert not git_info.is_malformed()
|
||||
|
||||
@patch("git.refs.symbolic.SymbolicReference.is_detached", True)
|
||||
def test_git_info_detached_head(self):
|
||||
git_info = under_test.GitInfo.get_git_info('.')
|
||||
git_info = under_test.GitInfo.generate_metrics('.')
|
||||
assert not git_info.is_malformed()
|
||||
|
||||
|
||||
# Mock this so that it passes when running the 'buildscripts_test' suite on Windows
|
||||
@patch("buildscripts.metrics.metrics_datatypes.HostInfo._get_memory", return_value=30)
|
||||
class TestToolingMetrics(unittest.TestCase):
|
||||
class TestResmokeToolingMetrics(unittest.TestCase):
|
||||
@patch("socket.gethostname", side_effect=Exception())
|
||||
def test_resmoke_tooling_metrics_with_exc(self, mock_gethostname, mock_get_memory):
|
||||
tooling_metrics = under_test.ToolingMetrics.get_resmoke_metrics(datetime.utcnow())
|
||||
def test_resmoke_tooling_metrics_valid(self, mock_gethostname):
|
||||
tooling_metrics = under_test.ResmokeToolingMetrics.generate_metrics(
|
||||
datetime.utcnow(),
|
||||
MOCK_EXIT_HOOK,
|
||||
)
|
||||
assert tooling_metrics.is_malformed()
|
||||
|
||||
def test_resmoke_tooling_metrics_no_exc(self, mock_get_memory):
|
||||
tooling_metrics = under_test.ToolingMetrics.get_resmoke_metrics(datetime.utcnow())
|
||||
def test_resmoke_tooling_metrics_malformed(self):
|
||||
tooling_metrics = under_test.ResmokeToolingMetrics.generate_metrics(
|
||||
datetime.utcnow(),
|
||||
MOCK_EXIT_HOOK,
|
||||
)
|
||||
assert not tooling_metrics.is_malformed()
|
||||
|
||||
|
||||
class TestSConsToolingMetrics(unittest.TestCase):
|
||||
@patch("buildscripts.metrics.metrics_datatypes.BuildInfo._get_scons_artifact_dir",
|
||||
return_value='/test')
|
||||
@patch("buildscripts.metrics.metrics_datatypes.BuildInfo._get_scons_env_vars_dict",
|
||||
return_value={'env': 'env'})
|
||||
@patch("buildscripts.metrics.metrics_datatypes.BuildInfo._get_scons_options_dict",
|
||||
return_value={'opt': 'opt'})
|
||||
def test_scons_tooling_metrics_valid(self, mock_options, mock_env, mock_artifact_dir,
|
||||
mock_get_memory):
|
||||
def test_scons_tooling_metrics_valid(self, mock_options, mock_env, mock_artifact_dir):
|
||||
parser = MagicMock()
|
||||
parser.parse_args = MagicMock(return_value={"opt1": "val1"})
|
||||
tooling_metrics = under_test.ToolingMetrics.get_scons_metrics(
|
||||
datetime.utcnow(), {'env': 'env'}, {'opts': 'opts'}, parser, ['test1', 'test2'], 0)
|
||||
tooling_metrics = under_test.SConsToolingMetrics.generate_metrics(
|
||||
datetime.utcnow(),
|
||||
{'env': 'env'},
|
||||
{'opts': 'opts'},
|
||||
parser,
|
||||
['test1', 'test2'],
|
||||
MOCK_EXIT_HOOK,
|
||||
)
|
||||
assert not tooling_metrics.is_malformed()
|
||||
|
||||
def test_scons_tooling_metrics_malformed(self, mock_get_memory):
|
||||
tooling_metrics = under_test.ToolingMetrics.get_scons_metrics(
|
||||
datetime.utcnow(), {'env': 'env'}, {'opts': 'opts'}, None, [], 0)
|
||||
def test_scons_tooling_metrics_malformed(self):
|
||||
tooling_metrics = under_test.SConsToolingMetrics.generate_metrics(
|
||||
datetime.utcnow(),
|
||||
{'env': 'env'},
|
||||
{'opts': 'opts'},
|
||||
None,
|
||||
[],
|
||||
MOCK_EXIT_HOOK,
|
||||
)
|
||||
assert tooling_metrics.is_malformed()
|
||||
|
||||
@ -3,11 +3,8 @@ import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
import mongomock
|
||||
import pymongo
|
||||
|
||||
import buildscripts.metrics.resmoke_tooling_metrics as under_test
|
||||
from buildscripts.resmoke import entrypoint as resmoke_entrypoint
|
||||
import buildscripts.resmoke as under_test
|
||||
|
||||
TEST_INTERNAL_TOOLING_METRICS_HOSTNAME = 'mongodb://testing:27017'
|
||||
CURRENT_DATE_TIME = datetime(2022, 10, 4)
|
||||
@ -19,37 +16,32 @@ if os.name == "nt":
|
||||
sys.exit()
|
||||
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils.INTERNAL_TOOLING_METRICS_HOSTNAME",
|
||||
TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
@patch("buildscripts.resmokelib.logging.flush._FLUSH_THREAD", None)
|
||||
class TestResmokeMetricsCollection(unittest.TestCase):
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
@patch("buildscripts.metrics.resmoke_tooling_metrics.should_collect_metrics", return_value=True)
|
||||
@patch("atexit.register")
|
||||
class TestResmokeAtExitMetricsCollection(unittest.TestCase):
|
||||
@patch("sys.argv", ['buildscripts/resmoke.py', 'list-suites'])
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=True)
|
||||
def test_resmoke_at_exit_metrics_collection(self, mock_should_collect_metrics,
|
||||
mock_atexit_register):
|
||||
under_test.entrypoint()
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit_register.call_args_list]
|
||||
assert "_save_metrics" in atexit_functions
|
||||
|
||||
@patch("sys.argv", ['buildscripts/resmoke.py', 'list-suites'])
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=False)
|
||||
def test_no_resmoke_at_exit_metrics_collection(self, mock_should_collect_metrics,
|
||||
mock_atexit_register):
|
||||
under_test.entrypoint()
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit_register.call_args_list]
|
||||
assert "_save_metrics" not in atexit_functions
|
||||
|
||||
@patch("sys.argv", ['buildscripts/resmoke.py', 'run', '--suite', 'buildscripts_test'])
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=True)
|
||||
@patch("buildscripts.resmokelib.testing.executor.TestSuiteExecutor._run_tests",
|
||||
side_effect=Exception())
|
||||
def test_resmoke_metrics_collection_exc(self, mock_executor_run, mock_should_collect_metrics):
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
with self.assertRaises(SystemExit):
|
||||
resmoke_entrypoint()
|
||||
assert client.metrics.tooling_metrics.find_one()
|
||||
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
@patch("buildscripts.metrics.resmoke_tooling_metrics.should_collect_metrics", return_value=True)
|
||||
@patch("sys.argv", ['buildscripts/resmoke.py', 'list-suites'])
|
||||
def test_resmoke_metrics_collection(self, mock_should_collect_metrics):
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
resmoke_entrypoint()
|
||||
assert client.metrics.tooling_metrics.find_one()
|
||||
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
@patch("buildscripts.metrics.resmoke_tooling_metrics.should_collect_metrics",
|
||||
return_value=False)
|
||||
@patch("sys.argv", ['buildscripts/resmoke.py', 'list-suites'])
|
||||
def test_no_resmoke_metrics_collection(self, mock_should_collect_metrics):
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
resmoke_entrypoint()
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
def test_resmoke_at_exit_metrics_collection_exc(
|
||||
self, mock_exc_method, mock_should_collect_metrics, mock_atexit_register):
|
||||
with self.assertRaises(SystemExit) as _:
|
||||
under_test.entrypoint()
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit_register.call_args_list]
|
||||
assert "_save_metrics" in atexit_functions
|
||||
|
||||
@ -1,15 +1,8 @@
|
||||
from datetime import datetime
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import mongomock
|
||||
import pymongo
|
||||
import buildscripts.metrics.scons_tooling_metrics as under_test
|
||||
from buildscripts.scons import entrypoint as scons_entrypoint
|
||||
|
||||
TEST_INTERNAL_TOOLING_METRICS_HOSTNAME = 'mongodb://testing:27017'
|
||||
CURRENT_DATE_TIME = datetime(2022, 10, 4)
|
||||
from unittest.mock import patch
|
||||
import buildscripts.scons as under_test
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
# pylint: disable=protected-access
|
||||
@ -23,52 +16,29 @@ if os.name == "nt":
|
||||
'buildscripts/scons.py', "CC=/opt/mongodbtoolchain/v3/bin/gcc",
|
||||
"CXX=/opt/mongodbtoolchain/v3/bin/g++", "NINJA_PREFIX=test_success", "--ninja"
|
||||
])
|
||||
@patch("buildscripts.metrics.scons_tooling_metrics.should_collect_metrics", return_value=True)
|
||||
@patch("atexit.register")
|
||||
class TestSconsAtExitMetricsCollection(unittest.TestCase):
|
||||
def test_scons_at_exit_metrics_collection(self, mock_atexit_register,
|
||||
mock_should_collect_metrics):
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
scons_entrypoint()
|
||||
assert context.exception.code == 0
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=True)
|
||||
def test_scons_at_exit_metrics_collection(self, mock_should_collect_metrics,
|
||||
mock_atexit_register):
|
||||
with self.assertRaises(SystemExit) as _:
|
||||
under_test.entrypoint()
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit_register.call_args_list]
|
||||
assert "_save_scons_tooling_metrics" in atexit_functions
|
||||
assert "_save_metrics" in atexit_functions
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=False)
|
||||
def test_no_scons_at_exit_metrics_collection(self, mock_should_collect_metrics,
|
||||
mock_atexit_register):
|
||||
with self.assertRaises(SystemExit) as _:
|
||||
under_test.entrypoint()
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit_register.call_args_list]
|
||||
assert "_save_metrics" not in atexit_functions
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=True)
|
||||
@patch("buildscripts.moduleconfig.get_module_sconscripts", side_effect=Exception())
|
||||
def test_scons_at_exit_metrics_collection_exc(self, mock_method, mock_atexit_register,
|
||||
mock_should_collect_metrics):
|
||||
with self.assertRaises(SystemExit) as context:
|
||||
scons_entrypoint()
|
||||
assert context.exception.code == 2
|
||||
def test_scons_at_exit_metrics_collection_exc(
|
||||
self, mock_exc_method, mock_should_collect_metrics, mock_atexit_register):
|
||||
with self.assertRaises(SystemExit) as _:
|
||||
under_test.entrypoint()
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit_register.call_args_list]
|
||||
assert "_save_scons_tooling_metrics" in atexit_functions
|
||||
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils.INTERNAL_TOOLING_METRICS_HOSTNAME",
|
||||
TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
class TestSconsMetricsCollection(unittest.TestCase):
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
@patch("buildscripts.metrics.scons_tooling_metrics.should_collect_metrics", return_value=True)
|
||||
def test_scons_metrics_collection_success(self, mock_should_collect_metrics):
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
under_test._save_scons_tooling_metrics(CURRENT_DATE_TIME, None, None, None, None,
|
||||
MagicMock(exit_code=0))
|
||||
assert client.metrics.tooling_metrics.find_one()
|
||||
|
||||
@patch("buildscripts.metrics.scons_tooling_metrics.should_collect_metrics", return_value=True)
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
def test_scons_metrics_collection_fail(self, mock_should_collect_metrics):
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
under_test._save_scons_tooling_metrics(None, None, None, None, None, None)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
|
||||
@patch("buildscripts.metrics.scons_tooling_metrics.should_collect_metrics", return_value=False)
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
def test_no_scons_metrics_collection(self, mock_should_collect_metrics):
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
under_test._save_scons_tooling_metrics(CURRENT_DATE_TIME, None, None, None, None,
|
||||
MagicMock(exit_code=0))
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
assert "_save_metrics" in atexit_functions
|
||||
|
||||
@ -1,81 +1,75 @@
|
||||
"""Unit tests for tooling_metrics.py."""
|
||||
import asyncio
|
||||
from datetime import datetime
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import mock_open, patch
|
||||
from mock import MagicMock
|
||||
import mongomock
|
||||
import pymongo
|
||||
from buildscripts.metrics.metrics_datatypes import ToolingMetrics
|
||||
from buildscripts.metrics.metrics_datatypes import ResmokeToolingMetrics, SConsToolingMetrics
|
||||
import buildscripts.metrics.tooling_metrics_utils as under_test
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
# pylint: disable=protected-access
|
||||
|
||||
TEST_INTERNAL_TOOLING_METRICS_HOSTNAME = 'mongodb://testing:27017'
|
||||
CURRENT_DATE_TIME = datetime(2022, 10, 4)
|
||||
|
||||
|
||||
async def extended_sleep(arg):
|
||||
await asyncio.sleep(2)
|
||||
|
||||
RESMOKE_METRICS_ARGS = {
|
||||
"utc_starttime": datetime(2022, 10, 4),
|
||||
"exit_hook": MagicMock(exit_code=0),
|
||||
}
|
||||
|
||||
# Metrics collection is not supported for Windows
|
||||
if os.name == "nt":
|
||||
sys.exit()
|
||||
|
||||
|
||||
@patch("atexit.register")
|
||||
class TestRegisterMetricsCollectionAtExit(unittest.TestCase):
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=True)
|
||||
def test_register_metrics_collection(self, mock_should_collect_metrics, mock_atexit):
|
||||
under_test.register_metrics_collection_atexit(ResmokeToolingMetrics.generate_metrics,
|
||||
RESMOKE_METRICS_ARGS)
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit.call_args_list]
|
||||
assert "_save_metrics" in atexit_functions
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._should_collect_metrics", return_value=False)
|
||||
def test_no_register_metrics_collection(self, mock_should_collect_metrics, mock_atexit):
|
||||
under_test.register_metrics_collection_atexit(ResmokeToolingMetrics.generate_metrics,
|
||||
RESMOKE_METRICS_ARGS)
|
||||
atexit_functions = [call[0][0].__name__ for call in mock_atexit.call_args_list]
|
||||
assert "_save_metrics" not in atexit_functions
|
||||
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils.INTERNAL_TOOLING_METRICS_HOSTNAME",
|
||||
TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
class TestSaveToolingMetrics(unittest.TestCase):
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
def test_on_virtual_workstation(self):
|
||||
under_test.save_tooling_metrics(ToolingMetrics.get_resmoke_metrics(CURRENT_DATE_TIME))
|
||||
def test_save_resmoke_metrics(self):
|
||||
under_test._save_metrics(ResmokeToolingMetrics.generate_metrics, RESMOKE_METRICS_ARGS)
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert client.metrics.tooling_metrics.find_one()
|
||||
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._save_metrics",
|
||||
side_effect=pymongo.errors.WriteError(error="Error Information"))
|
||||
def test_exception_caught(self, mock_save_metrics):
|
||||
with self.assertLogs('tooling_metrics_utils') as cm:
|
||||
under_test.save_tooling_metrics(ToolingMetrics.get_resmoke_metrics(CURRENT_DATE_TIME))
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._get_internal_tooling_metrics_client",
|
||||
side_effect=pymongo.errors.ServerSelectionTimeoutError(message="Error Information"))
|
||||
def test_save_metrics_with_exc(self, mock_save_metrics):
|
||||
with self.assertLogs('tooling_metrics') as cm:
|
||||
under_test._save_metrics(ResmokeToolingMetrics.generate_metrics, RESMOKE_METRICS_ARGS)
|
||||
assert "Error Information" in cm.output[0]
|
||||
assert "Unexpected: Tooling metrics collection is not available" in cm.output[0]
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
|
||||
@mongomock.patch(servers=((TEST_INTERNAL_TOOLING_METRICS_HOSTNAME), ))
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._save_metrics", side_effect=extended_sleep)
|
||||
def test_timeout_caught(self, mock_save_metrics):
|
||||
with self.assertLogs('tooling_metrics_utils') as cm:
|
||||
under_test.save_tooling_metrics(ToolingMetrics.get_resmoke_metrics(CURRENT_DATE_TIME))
|
||||
assert "Timeout: Tooling metrics collection is not available" in cm.output[0]
|
||||
assert "Internal Metrics Collection Failed" in cm.output[0]
|
||||
client = pymongo.MongoClient(host=TEST_INTERNAL_TOOLING_METRICS_HOSTNAME)
|
||||
assert not client.metrics.tooling_metrics.find_one()
|
||||
|
||||
|
||||
class TestIsVirtualWorkstation(unittest.TestCase):
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._toolchain_exists", return_value=False)
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._git_user_exists", return_value=True)
|
||||
def test_no_toolchain_has_email(self, mock_git_user_exists, mock_toolchain_exists):
|
||||
assert not under_test._is_virtual_workstation()
|
||||
@patch("builtins.open", mock_open(read_data="ubuntu1804-workstation"))
|
||||
def test_is_virtual_workstation(self):
|
||||
assert under_test._is_virtual_workstation() is True
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._toolchain_exists", return_value=True)
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._git_user_exists", return_value=True)
|
||||
def test_has_toolchain_has_email(self, mock_git_user_exists, mock_toolchain_exists):
|
||||
assert under_test._is_virtual_workstation()
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._toolchain_exists", return_value=True)
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._git_user_exists", return_value=False)
|
||||
def test_has_toolchain_no_email(self, mock_git_user_exists, mock_toolchain_exists):
|
||||
assert not under_test._is_virtual_workstation()
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._toolchain_exists", return_value=False)
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._git_user_exists", return_value=False)
|
||||
def test_no_toolchain_no_email(self, mock_git_user_exists, mock_toolchain_exists):
|
||||
assert not under_test._is_virtual_workstation()
|
||||
@patch("builtins.open", mock_open(read_data="test"))
|
||||
def test_is_not_virtual_workstation(self):
|
||||
assert under_test._is_virtual_workstation() is False
|
||||
|
||||
|
||||
class TestHasMetricsOptOut(unittest.TestCase):
|
||||
@ -92,9 +86,9 @@ class TestShouldCollectMetrics(unittest.TestCase):
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._is_virtual_workstation", return_value=True)
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._has_metrics_opt_out", return_value=False)
|
||||
def test_should_collect_metrics(self, mock_opt_out, mock_is_virtual_env):
|
||||
assert under_test.should_collect_metrics()
|
||||
assert under_test._should_collect_metrics()
|
||||
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._is_virtual_workstation", return_value=True)
|
||||
@patch("buildscripts.metrics.tooling_metrics_utils._has_metrics_opt_out", return_value=True)
|
||||
def test_no_collect_metrics_opt_out(self, mock_opt_out, mock_is_virtual_env):
|
||||
assert not under_test.should_collect_metrics()
|
||||
assert not under_test._should_collect_metrics()
|
||||
|
||||
@ -78,7 +78,7 @@ class TestHistoricTaskData(unittest.TestCase):
|
||||
@staticmethod
|
||||
def _make_evg_result(test_file="dir/test1.js", num_pass=0, duration=0):
|
||||
return Mock(
|
||||
test_file=test_file,
|
||||
test_name=test_file,
|
||||
task_name="task1",
|
||||
variant="variant1",
|
||||
distro="distro1",
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
"""Service for determining task timeouts."""
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, NamedTuple, Optional
|
||||
|
||||
import inject
|
||||
@ -31,29 +30,17 @@ class TimeoutParams(NamedTuple):
|
||||
is_asan: bool
|
||||
|
||||
|
||||
class TimeoutSettings(NamedTuple):
|
||||
"""Settings for determining timeouts."""
|
||||
|
||||
start_date: datetime
|
||||
end_date: datetime
|
||||
|
||||
|
||||
class TimeoutService:
|
||||
"""A service for determining task timeouts."""
|
||||
|
||||
@inject.autoparams()
|
||||
def __init__(self, evg_api: EvergreenApi, resmoke_proxy: ResmokeProxyService,
|
||||
timeout_settings: TimeoutSettings) -> None:
|
||||
def __init__(self, resmoke_proxy: ResmokeProxyService) -> None:
|
||||
"""
|
||||
Initialize the service.
|
||||
|
||||
:param evg_api: Evergreen API client.
|
||||
:param resmoke_proxy: Proxy to query resmoke.
|
||||
:param timeout_settings: Settings for how timeouts are calculated.
|
||||
"""
|
||||
self.evg_api = evg_api
|
||||
self.resmoke_proxy = resmoke_proxy
|
||||
self.timeout_settings = timeout_settings
|
||||
|
||||
def get_timeout_estimate(self, timeout_params: TimeoutParams) -> TimeoutEstimate:
|
||||
"""
|
||||
@ -137,10 +124,8 @@ class TimeoutService:
|
||||
:return: Historic test results if they exist.
|
||||
"""
|
||||
try:
|
||||
evg_stats = HistoricTaskData.from_evg(
|
||||
self.evg_api, timeout_params.evg_project, self.timeout_settings.start_date,
|
||||
self.timeout_settings.end_date, timeout_params.task_name,
|
||||
timeout_params.build_variant)
|
||||
evg_stats = HistoricTaskData.from_s3(
|
||||
timeout_params.evg_project, timeout_params.task_name, timeout_params.build_variant)
|
||||
if not evg_stats:
|
||||
LOGGER.warning("No historic runtime information available")
|
||||
return None
|
||||
|
||||
@ -1,15 +1,31 @@
|
||||
"""Utility to support parsing a TestStat."""
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from itertools import chain
|
||||
from typing import NamedTuple, List, Callable, Optional
|
||||
|
||||
from evergreen import EvergreenApi, TestStats
|
||||
import requests
|
||||
from requests.adapters import HTTPAdapter, Retry
|
||||
|
||||
from buildscripts.util.testname import split_test_hook_name, is_resmoke_hook, get_short_name_from_test_file
|
||||
|
||||
TASK_LEVEL_HOOKS = {"CleanEveryN"}
|
||||
TESTS_STATS_S3_LOCATION = "https://mongo-test-stats.s3.amazonaws.com"
|
||||
|
||||
|
||||
class HistoricalTestInformation(NamedTuple):
|
||||
"""
|
||||
Container for information about the historical runtime of a test.
|
||||
|
||||
test_name: Name of test.
|
||||
avg_duration_pass: Average of runtime of test that passed.
|
||||
num_pass: Number of times the test has passed.
|
||||
num_fail: Number of times the test has failed.
|
||||
"""
|
||||
|
||||
test_name: str
|
||||
num_pass: int
|
||||
num_fail: int
|
||||
avg_duration_pass: float
|
||||
|
||||
|
||||
class TestRuntime(NamedTuple):
|
||||
@ -74,9 +90,9 @@ class HistoricHookInfo(NamedTuple):
|
||||
avg_duration: float
|
||||
|
||||
@classmethod
|
||||
def from_test_stats(cls, test_stats: TestStats) -> "HistoricHookInfo":
|
||||
def from_test_stats(cls, test_stats: HistoricalTestInformation) -> "HistoricHookInfo":
|
||||
"""Create an instance from a test_stats object."""
|
||||
return cls(hook_id=test_stats.test_file, num_pass=test_stats.num_pass,
|
||||
return cls(hook_id=test_stats.test_name, num_pass=test_stats.num_pass,
|
||||
avg_duration=test_stats.avg_duration_pass)
|
||||
|
||||
def test_name(self) -> str:
|
||||
@ -101,10 +117,10 @@ class HistoricTestInfo(NamedTuple):
|
||||
hooks: List[HistoricHookInfo]
|
||||
|
||||
@classmethod
|
||||
def from_test_stats(cls, test_stats: TestStats,
|
||||
def from_test_stats(cls, test_stats: HistoricalTestInformation,
|
||||
hooks: List[HistoricHookInfo]) -> "HistoricTestInfo":
|
||||
"""Create an instance from a test_stats object."""
|
||||
return cls(test_name=test_stats.test_file, num_pass=test_stats.num_pass,
|
||||
return cls(test_name=test_stats.test_name, num_pass=test_stats.num_pass,
|
||||
avg_duration=test_stats.avg_duration_pass, hooks=hooks)
|
||||
|
||||
def normalized_test_name(self) -> str:
|
||||
@ -137,45 +153,56 @@ class HistoricTaskData(object):
|
||||
"""Initialize the TestStats with raw results from the Evergreen API."""
|
||||
self.historic_test_results = historic_test_results
|
||||
|
||||
@classmethod
|
||||
def from_evg(cls, evg_api: EvergreenApi, project: str, start_date: datetime, end_date: datetime,
|
||||
task: str, variant: str) -> "HistoricTaskData":
|
||||
@staticmethod
|
||||
def get_stats_from_s3(project: str, task: str, variant: str) -> List[HistoricalTestInformation]:
|
||||
"""
|
||||
Retrieve test stats from evergreen for a given task.
|
||||
Retrieve test stats from s3 for a given task.
|
||||
|
||||
:param project: Project to query.
|
||||
:param task: Task to query.
|
||||
:param variant: Build variant to query.
|
||||
:return: A list of the Test stats for the specified task.
|
||||
"""
|
||||
session = requests.Session()
|
||||
retries = Retry(total=5, backoff_factor=1, status_forcelist=[502, 503, 504])
|
||||
session.mount('https://', HTTPAdapter(max_retries=retries))
|
||||
|
||||
response = session.get(f"{TESTS_STATS_S3_LOCATION}/{project}/{variant}/{task}")
|
||||
data = response.json()
|
||||
|
||||
return [HistoricalTestInformation(**item) for item in data]
|
||||
|
||||
@classmethod
|
||||
def from_s3(cls, project: str, task: str, variant: str) -> "HistoricTaskData":
|
||||
"""
|
||||
Retrieve test stats from s3 for a given task.
|
||||
|
||||
:param evg_api: Evergreen API client.
|
||||
:param project: Project to query.
|
||||
:param start_date: Start date to query.
|
||||
:param end_date: End date to query.
|
||||
:param task: Task to query.
|
||||
:param variant: Build variant to query.
|
||||
:return: Test stats for the specified task.
|
||||
"""
|
||||
days = (end_date - start_date).days
|
||||
historic_stats = evg_api.test_stats_by_project(
|
||||
project, after_date=start_date, before_date=end_date, tasks=[task], variants=[variant],
|
||||
group_by="test", group_num_days=days)
|
||||
|
||||
return cls.from_stats_list(historic_stats)
|
||||
historical_test_data = cls.get_stats_from_s3(project, task, variant)
|
||||
return cls.from_stats_list(historical_test_data)
|
||||
|
||||
@classmethod
|
||||
def from_stats_list(cls, historic_stats: List[TestStats]) -> "HistoricTaskData":
|
||||
def from_stats_list(
|
||||
cls, historical_test_data: List[HistoricalTestInformation]) -> "HistoricTaskData":
|
||||
"""
|
||||
Build historic task data from a list of historic stats.
|
||||
|
||||
:param historic_stats: List of historic stats to build from.
|
||||
:param historical_test_data: A list of information about the runtime of a test.
|
||||
:return: Historic task data from the list of stats.
|
||||
"""
|
||||
|
||||
hooks = defaultdict(list)
|
||||
for hook in [stat for stat in historic_stats if is_resmoke_hook(stat.test_file)]:
|
||||
for hook in [stat for stat in historical_test_data if is_resmoke_hook(stat.test_name)]:
|
||||
historical_hook = HistoricHookInfo.from_test_stats(hook)
|
||||
hooks[historical_hook.test_name()].append(historical_hook)
|
||||
|
||||
return cls([
|
||||
HistoricTestInfo.from_test_stats(stat,
|
||||
hooks[get_short_name_from_test_file(stat.test_file)])
|
||||
for stat in historic_stats if not is_resmoke_hook(stat.test_file)
|
||||
hooks[get_short_name_from_test_file(stat.test_name)])
|
||||
for stat in historical_test_data if not is_resmoke_hook(stat.test_name)
|
||||
])
|
||||
|
||||
def get_tests_runtimes(self) -> List[TestRuntime]:
|
||||
|
||||
@ -272,8 +272,18 @@ last-continuous:
|
||||
ticket: SERVER-70190
|
||||
- test_file: jstests/core/cover_null_queries.js
|
||||
ticket: SERVER-70436
|
||||
- test_file: jstests/sharding/prepare_transaction_then_migrate.js
|
||||
ticket: SERVER-68361
|
||||
- test_file: jstests/sharding/all_collection_stats.js
|
||||
ticket: SERVER-71477
|
||||
- test_file: jstests/core/project_with_collation.js
|
||||
ticket: SERVER-72416
|
||||
- test_file: jstests/core/collation.js
|
||||
ticket: SERVER-72416
|
||||
- test_file: jstests/core/or_to_in.js
|
||||
ticket: SERVER-72416
|
||||
- test_file: src/mongo/db/modules/enterprise/jstests/fcbis/oplog_rollover.js
|
||||
ticket: SERVER-72422
|
||||
- test_file: jstests/core/timeseries/timeseries_groupby_reorder.js
|
||||
ticket: SERVER-73822
|
||||
suites: null
|
||||
last-lts:
|
||||
all:
|
||||
@ -621,6 +631,16 @@ last-lts:
|
||||
ticket: SERVER-70190
|
||||
- test_file: jstests/core/cover_null_queries.js
|
||||
ticket: SERVER-70436
|
||||
- test_file: jstests/sharding/prepare_transaction_then_migrate.js
|
||||
ticket: SERVER-68361
|
||||
- test_file: jstests/sharding/all_collection_stats.js
|
||||
ticket: SERVER-71477
|
||||
- test_file: jstests/core/project_with_collation.js
|
||||
ticket: SERVER-72416
|
||||
- test_file: jstests/core/collation.js
|
||||
ticket: SERVER-72416
|
||||
- test_file: jstests/core/or_to_in.js
|
||||
ticket: SERVER-72416
|
||||
- test_file: src/mongo/db/modules/enterprise/jstests/fcbis/oplog_rollover.js
|
||||
ticket: SERVER-72422
|
||||
- test_file: jstests/core/timeseries/timeseries_groupby_reorder.js
|
||||
ticket: SERVER-73822
|
||||
suites: null
|
||||
|
||||
@ -248,7 +248,6 @@ variables:
|
||||
- ubuntu2004-package
|
||||
- name: selinux_rhel7_enterprise
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
|
||||
@ -310,34 +309,6 @@ buildvariants:
|
||||
- name: sharding_gen
|
||||
- name: sharding_opportunistic_secondary_targeting_gen
|
||||
|
||||
- <<: *linux-64-debug-required-template
|
||||
name: &linux-64-debug-wtdevelop linux-64-debug-wtdevelop
|
||||
display_name: "~ Linux DEBUG WiredTiger develop"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- wtdevelop
|
||||
depends_on:
|
||||
- name: archive_dist_test
|
||||
variant: &linux_x86_dynamic_debug_wtdevelop_compile_variant_name linux-x86-dynamic-debug-wtdevelop-compile
|
||||
- name: version_gen
|
||||
variant: generate-tasks-for-version
|
||||
expansions:
|
||||
use_wt_develop: true
|
||||
resmoke_jobs_factor: 0.5 # Avoid starting too many mongod's
|
||||
compile_flags: >-
|
||||
--dbg=on
|
||||
--opt=on
|
||||
-j$(grep -c ^processor /proc/cpuinfo)
|
||||
--variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars
|
||||
--enable-free-mon=on
|
||||
--enable-http-client=on
|
||||
--link-model=dynamic
|
||||
scons_cache_mode: all
|
||||
scons_cache_scope: shared
|
||||
num_scons_link_jobs_available: 0.99
|
||||
test_flags: --excludeWithAnyTags=requires_http_client
|
||||
compile_variant: *linux_x86_dynamic_debug_wtdevelop_compile_variant_name
|
||||
|
||||
- name: &tla-plus tla-plus
|
||||
display_name: TLA+
|
||||
run_on:
|
||||
@ -987,7 +958,7 @@ buildvariants:
|
||||
# To force disable feature flags even on the all feature flags variant, please use this file:
|
||||
# buildscripts/resmokeconfig/fully_disabled_feature_flags.yml
|
||||
test_flags: >-
|
||||
--runAllFeatureFlagTests
|
||||
--additionalFeatureFlagsFile all_feature_flags.txt
|
||||
--excludeWithAnyTags=incompatible_with_windows_tls
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
external_auth_jobs_max: 1
|
||||
@ -1577,7 +1548,7 @@ buildvariants:
|
||||
# To force disable feature flags even on the all feature flags variant, please use this file:
|
||||
# buildscripts/resmokeconfig/fully_disabled_feature_flags.yml
|
||||
test_flags: >-
|
||||
--runAllFeatureFlagTests
|
||||
--additionalFeatureFlagsFile all_feature_flags.txt
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
tasks:
|
||||
- name: cqf
|
||||
@ -1873,7 +1844,7 @@ buildvariants:
|
||||
<<: *linux-x86-multiversion-expansions-template
|
||||
# No feature flag tests since they aren't compatible with the older binaries.
|
||||
test_flags: >-
|
||||
--runAllFeatureFlagsNoTests
|
||||
--runNoFeatureFlagTests
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
|
||||
- <<: *linux-x86-multiversion-template
|
||||
@ -2251,7 +2222,7 @@ buildvariants:
|
||||
# buildscripts/resmokeconfig/fully_disabled_feature_flags.yml
|
||||
large_distro_name: rhel80-build
|
||||
test_flags: >-
|
||||
--runAllFeatureFlagTests
|
||||
--additionalFeatureFlagsFile all_feature_flags.txt
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
separate_debug: off
|
||||
tasks:
|
||||
@ -2433,7 +2404,7 @@ buildvariants:
|
||||
test_flags: >-
|
||||
--excludeWithAnyTags=requires_ocsp_stapling
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
--runAllFeatureFlagTests
|
||||
--additionalFeatureFlagsFile all_feature_flags.txt
|
||||
multiversion_platform: rhel80
|
||||
multiversion_edition: enterprise
|
||||
resmoke_jobs_factor: 0.3 # Avoid starting too many mongod's under UBSAN build.
|
||||
@ -2616,7 +2587,7 @@ buildvariants:
|
||||
test_flags: >-
|
||||
--excludeWithAnyTags=requires_ocsp_stapling
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
--runAllFeatureFlagTests
|
||||
--additionalFeatureFlagsFile all_feature_flags.txt
|
||||
tasks:
|
||||
- name: jsCore
|
||||
- name: jsCore_txns
|
||||
@ -3328,7 +3299,7 @@ buildvariants:
|
||||
target_resmoke_time: 30
|
||||
max_sub_suites: 3
|
||||
test_flags: >-
|
||||
--runAllFeatureFlagTests
|
||||
--additionalFeatureFlagsFile all_feature_flags.txt
|
||||
--excludeWithAnyTags=resource_intensive
|
||||
--excludeWithAnyTags=incompatible_with_shard_merge
|
||||
|
||||
@ -3388,18 +3359,6 @@ buildvariants:
|
||||
- windows-vsCurrent-large
|
||||
- name: .benchmarks !benchmarks_orphaned
|
||||
|
||||
- <<: *enterprise-windows-nopush-template
|
||||
name: &enterprise-windows-wtdevelop enterprise-windows-wtdevelop
|
||||
display_name: "~ Enterprise Windows WiredTiger develop"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- enterprise
|
||||
- wtdevelop
|
||||
expansions:
|
||||
<<: *enterprise-windows-nopush-expansions-template
|
||||
use_wt_develop: true
|
||||
compile_variant: *enterprise-windows-wtdevelop
|
||||
|
||||
### QO & QE Patch-Specific Build Variants ###
|
||||
- <<: *enterprise-windows-nopush-template
|
||||
name: &windows-compile-query-patch-only windows-compile-query-patch-only
|
||||
|
||||
@ -6,16 +6,336 @@ include:
|
||||
- filename: etc/evergreen_yml_components/variants/atlas.yml
|
||||
- filename: etc/evergreen_yml_components/variants/misc_release.yml
|
||||
### Comment out when using this file for a LTS or Rapid release branch. ###
|
||||
- filename: etc/evergreen_yml_components/variants/ibm.yml
|
||||
# - filename: etc/evergreen_yml_components/variants/ibm.yml
|
||||
### Uncomment when using this file for a LTS release branch. ###
|
||||
# - filename: etc/evergreen_yml_components/variants/in_memory.yml
|
||||
### Uncomment when using this file for a LTS or Rapid release branch. ###
|
||||
# - filename: etc/evergreen_yml_components/variants/sanitizer.yml
|
||||
- filename: etc/evergreen_yml_components/variants/sanitizer.yml
|
||||
### Uncomment when using this file for a LTS or Rapid release branch. ###
|
||||
# - filename: etc/evergreen_yml_components/variants/ninja.yml
|
||||
- filename: etc/evergreen_yml_components/variants/ninja.yml
|
||||
- filename: etc/evergreen_yml_components/variants/compile_static_analysis.yml
|
||||
|
||||
|
||||
parameters:
|
||||
- key: evergreen_config_file_path
|
||||
value: "etc/evergreen_nightly.yml"
|
||||
description: "path to this file"
|
||||
|
||||
|
||||
variables:
|
||||
# Common compile variant dependency specifications.
|
||||
- &linux_x86_dynamic_compile_variant_dependency
|
||||
depends_on:
|
||||
- name: archive_dist_test_debug
|
||||
variant: &linux_x86_dynamic_compile_variant_name linux-x86-dynamic-compile-required
|
||||
- name: version_gen
|
||||
variant: generate-tasks-for-version
|
||||
|
||||
- &linux_x86_dynamic_debug_compile_variant_dependency
|
||||
depends_on:
|
||||
- name: archive_dist_test_debug
|
||||
variant: &linux_x86_dynamic_debug_compile_variant_name linux-x86-dynamic-debug-compile-required
|
||||
- name: version_gen
|
||||
variant: generate-tasks-for-version
|
||||
|
||||
- &linux_debug_aubsan_compile_variant_dependency
|
||||
depends_on:
|
||||
- name: archive_dist_test_debug
|
||||
variant: &linux_debug_aubsan_compile_variant_name linux-debug-aubsan-compile-required
|
||||
- name: version_gen
|
||||
variant: generate-tasks-for-version
|
||||
|
||||
- &windows_compile_variant_dependency
|
||||
depends_on:
|
||||
- name: archive_dist_test_debug
|
||||
variant: &windows_compile_variant_name windows-compile-required
|
||||
- name: version_gen
|
||||
variant: generate-tasks-for-version
|
||||
|
||||
- &linux_x86_generic_expansions
|
||||
multiversion_platform: rhel80
|
||||
multiversion_edition: enterprise
|
||||
repo_edition: enterprise
|
||||
large_distro_name: rhel80-medium
|
||||
num_scons_link_jobs_available: 0.99
|
||||
compile_variant: *linux_x86_dynamic_compile_variant_name
|
||||
|
||||
|
||||
buildvariants:
|
||||
- &linux-64-debug-required-template
|
||||
<<: *linux_x86_dynamic_debug_compile_variant_dependency
|
||||
name: &linux-64-debug-required linux-64-debug-required
|
||||
display_name: "! Linux x86 Shared Library DEBUG"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
run_on:
|
||||
- rhel80-medium
|
||||
expansions:
|
||||
resmoke_jobs_factor: 0.5 # Avoid starting too many mongod's
|
||||
test_flags: --excludeWithAnyTags=requires_http_client
|
||||
target_resmoke_time: 15
|
||||
max_sub_suites: 5
|
||||
large_distro_name: rhel80-medium
|
||||
compile_variant: *linux_x86_dynamic_debug_compile_variant_name
|
||||
tasks:
|
||||
- name: .aggregation !.encrypt !.feature_flag_guarded
|
||||
- name: .auth !.audit !.multiversion
|
||||
- name: .causally_consistent !.wo_snapshot
|
||||
- name: .change_streams !.secondary_reads
|
||||
- name: .clustered_collections
|
||||
- name: .misc_js
|
||||
- name: disk_wiredtiger
|
||||
- name: free_monitoring
|
||||
- name: .jscore .common
|
||||
- name: jsCore_txns_large_txns_format
|
||||
- name: json_schema
|
||||
- name: query_golden_classic
|
||||
- name: libunwind_tests
|
||||
- name: .multi_shard
|
||||
- name: multi_stmt_txn_jscore_passthrough_with_migration_gen
|
||||
- name: .ocsp
|
||||
- name: .read_write_concern
|
||||
- name: .replica_sets !.encrypt !.ignore_non_generated_replica_sets_jscore_passthrough !.fcbis
|
||||
- name: replica_sets_jscore_passthrough_gen
|
||||
- name: replica_sets_reconfig_jscore_passthrough_gen
|
||||
- name: replica_sets_reconfig_jscore_stepdown_passthrough_gen
|
||||
- name: .retry
|
||||
- name: .read_only
|
||||
- name: session_jscore_passthrough
|
||||
- name: sharded_multi_stmt_txn_jscore_passthrough
|
||||
- name: .sharding .jscore !.wo_snapshot
|
||||
- name: sharding_gen
|
||||
- name: sharding_opportunistic_secondary_targeting_gen
|
||||
|
||||
- &enterprise-windows-required-template
|
||||
<<: *windows_compile_variant_dependency
|
||||
name: &enterprise-windows-required enterprise-windows-required
|
||||
display_name: "! Enterprise Windows"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- windows-vsCurrent-small
|
||||
expansions: &windows_required_expansions
|
||||
compile_variant: *windows_compile_variant_name
|
||||
burn_in_tests_build_variant: enterprise-windows-required
|
||||
exe: ".exe"
|
||||
content_type: application/zip
|
||||
python: '/cygdrive/c/python/python37/python.exe'
|
||||
ext: zip
|
||||
multiversion_platform: windows
|
||||
multiversion_edition: enterprise
|
||||
jstestfuzz_num_generated_files: 35
|
||||
target_resmoke_time: 20
|
||||
max_sub_suites: 5
|
||||
large_distro_name: windows-vsCurrent-large
|
||||
push_path: windows
|
||||
push_bucket: downloads.10gen.com
|
||||
push_name: windows
|
||||
push_arch: x86_64-enterprise
|
||||
test_flags: &windows_common_test_excludes --excludeWithAnyTags=incompatible_with_windows_tls
|
||||
external_auth_jobs_max: 1
|
||||
tasks:
|
||||
# - name: burn_in_tests_gen
|
||||
- name: audit
|
||||
- name: auth_audit_gen
|
||||
- name: causally_consistent_jscore_txns_passthrough
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
- name: .encrypt !.aggregation !.replica_sets !.sharding !.jscore
|
||||
- name: external_auth
|
||||
- name: external_auth_aws
|
||||
- name: external_auth_windows
|
||||
distros:
|
||||
- windows-2016-dc
|
||||
- name: .jscore .common !.sharding
|
||||
- name: jsCore_auth
|
||||
- name: jsCore_ese
|
||||
- name: jsCore_txns_large_txns_format
|
||||
- name: .jstestfuzz .common
|
||||
- name: mqlrun
|
||||
- name: noPassthrough_gen
|
||||
- name: noPassthroughWithMongod_gen
|
||||
- name: .replica_sets .common !.ignore_non_generated_replica_sets_jscore_passthrough
|
||||
- name: .replica_sets .multi_oplog !.ignore_non_generated_replica_sets_jscore_passthrough
|
||||
- name: replica_sets_jscore_passthrough_gen
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
- name: replica_sets_ese_gen
|
||||
- name: sasl
|
||||
- name: .sharding .txns
|
||||
- name: sharding_auth_gen
|
||||
- name: sharding_auth_audit_gen
|
||||
- name: sharding_ese_gen
|
||||
|
||||
- &enterprise-rhel-80-64-bit-dynamic-required-template
|
||||
<<: *linux_x86_dynamic_compile_variant_dependency
|
||||
name: &enterprise-rhel-80-64-bit-dynamic-required enterprise-rhel-80-64-bit-dynamic-required
|
||||
display_name: "! Shared Library Enterprise RHEL 8.0"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- rhel80-small
|
||||
expansions: &enterprise-rhel-80-64-bit-dynamic-expansions
|
||||
<<: *linux_x86_generic_expansions
|
||||
scons_cache_scope: shared
|
||||
scons_cache_mode: all
|
||||
has_packages: false
|
||||
jstestfuzz_num_generated_files: 40
|
||||
jstestfuzz_concurrent_num_files: 10
|
||||
target_resmoke_time: 10
|
||||
max_sub_suites: 5
|
||||
idle_timeout_factor: 1.5
|
||||
exec_timeout_factor: 1.5
|
||||
large_distro_name: rhel80-medium
|
||||
burn_in_tag_buildvariants: >-
|
||||
enterprise-rhel-80-64-bit-inmem
|
||||
enterprise-rhel-80-64-bit-multiversion
|
||||
burn_in_tag_compile_task_group_name: compile_and_archive_dist_test_TG
|
||||
burn_in_tag_compile_distro: rhel80-xlarge
|
||||
depends_on:
|
||||
- name: archive_dist_test
|
||||
variant: *linux_x86_dynamic_compile_variant_name
|
||||
- name: version_gen
|
||||
variant: generate-tasks-for-version
|
||||
- name: version_burn_in_gen
|
||||
variant: generate-tasks-for-version
|
||||
tasks:
|
||||
# - name: burn_in_tests_gen
|
||||
- name: .aggfuzzer !.feature_flag_guarded
|
||||
- name: .aggregation !.feature_flag_guarded
|
||||
- name: audit
|
||||
- name: .auth
|
||||
- name: unittest_shell_hang_analyzer_gen
|
||||
- name: .causally_consistent !.sharding
|
||||
- name: .change_streams
|
||||
- name: .change_stream_fuzzer
|
||||
- name: .misc_js
|
||||
- name: .concurrency !.large !.ubsan !.no_txns !.debug_only
|
||||
- name: .concurrency .large !.ubsan !.no_txns !.debug_only
|
||||
distros:
|
||||
- rhel80-medium
|
||||
- name: disk_wiredtiger
|
||||
- name: .encrypt
|
||||
- name: idl_tests
|
||||
- name: initial_sync_fuzzer_gen
|
||||
- name: jsCore
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: .jscore .common !jsCore
|
||||
- name: jsCore_minimum_batch_size
|
||||
- name: jsCore_txns_large_txns_format
|
||||
- name: json_schema
|
||||
- name: .jstestfuzz !.flow_control # Flow control jstestfuzz take longer.
|
||||
- name: libunwind_tests
|
||||
- name: mqlrun
|
||||
- name: .multi_shard
|
||||
- name: multi_stmt_txn_jscore_passthrough_with_migration_gen
|
||||
- name: multiversion_gen
|
||||
- name: .query_fuzzer
|
||||
- name: .random_multiversion_ds
|
||||
- name: .read_write_concern .large
|
||||
distros:
|
||||
- rhel80-medium
|
||||
- name: .read_write_concern !.large
|
||||
- name: .replica_sets !.encrypt !.auth
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: replica_sets_api_version_jscore_passthrough_gen
|
||||
- name: replica_sets_reconfig_jscore_passthrough_gen
|
||||
- name: replica_sets_reconfig_jscore_stepdown_passthrough_gen
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: replica_sets_reconfig_kill_primary_jscore_passthrough_gen
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: retryable_writes_jscore_passthrough_gen
|
||||
- name: retryable_writes_jscore_stepdown_passthrough_gen
|
||||
distros:
|
||||
- rhel80-medium
|
||||
- name: .read_only
|
||||
- name: .rollbackfuzzer
|
||||
- name: sasl
|
||||
- name: search
|
||||
- name: search_auth
|
||||
- name: search_ssl
|
||||
- name: session_jscore_passthrough
|
||||
- name: .sharding .jscore !.wo_snapshot !.multi_stmt
|
||||
- name: sharding_api_version_jscore_passthrough_gen
|
||||
- name: .sharding .txns
|
||||
- name: .sharding .common
|
||||
- name: .updatefuzzer
|
||||
- name: secondary_reads_passthrough_gen
|
||||
- name: .serverless
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
|
||||
- &rhel80-debug-aubsan-lite-required-template
|
||||
<<: *linux_debug_aubsan_compile_variant_dependency
|
||||
name: &rhel80-debug-aubsan-lite-required rhel80-debug-aubsan-lite-required
|
||||
display_name: "! Shared Library {A,UB}SAN Enterprise RHEL 8.0 DEBUG"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- rhel80-build
|
||||
expansions: &aubsan-lite-required-expansions
|
||||
compile_variant: *linux_debug_aubsan_compile_variant_name
|
||||
lang_environment: LANG=C
|
||||
# If you add anything to san_options, make sure the appropriate changes are
|
||||
# also made to SConstruct.
|
||||
san_options: >-
|
||||
UBSAN_OPTIONS="print_stacktrace=1:external_symbolizer_path=/opt/mongodbtoolchain/v3/bin/llvm-symbolizer"
|
||||
LSAN_OPTIONS="suppressions=etc/lsan.suppressions:report_objects=1"
|
||||
ASAN_OPTIONS="detect_leaks=1:check_initialization_order=true:strict_init_order=true:abort_on_error=1:disable_coredump=0:handle_abort=1:strict_string_checks=true:detect_invalid_pointer_pairs=1:external_symbolizer_path=/opt/mongodbtoolchain/v3/bin/llvm-symbolizer"
|
||||
test_flags: --excludeWithAnyTags=requires_ocsp_stapling
|
||||
resmoke_jobs_factor: 0.3 # Avoid starting too many mongod's under {A,UB}SAN build.
|
||||
hang_analyzer_dump_core: false
|
||||
max_sub_suites: 3
|
||||
num_scons_link_jobs_available: 0.99
|
||||
large_distro_name: rhel80-build
|
||||
tasks:
|
||||
- name: jsCore
|
||||
- name: jsCore_txns
|
||||
|
||||
- <<: *enterprise-rhel-80-64-bit-dynamic-required-template
|
||||
name: &commit-queue commit-queue
|
||||
display_name: "~ Commit Queue"
|
||||
cron: "0 4 * * 0" # From the ${project_weekly_cron} parameter
|
||||
stepback: false
|
||||
expansions:
|
||||
<<: *linux_x86_generic_expansions
|
||||
scons_cache_scope: shared
|
||||
scons_cache_mode: all
|
||||
has_packages: false
|
||||
compile_flags: >-
|
||||
--ssl
|
||||
MONGO_DISTMOD=rhel80
|
||||
-j$(grep -c ^processor /proc/cpuinfo)
|
||||
--variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars
|
||||
--link-model=dynamic
|
||||
crypt_task_compile_flags: >-
|
||||
SHLINKFLAGS_EXTRA="-Wl,-Bsymbolic
|
||||
-Wl,--no-gnu-unique"
|
||||
CCFLAGS="-fno-gnu-unique"
|
||||
clang_tidy_toolchain: v3
|
||||
compile_variant: *commit-queue
|
||||
depends_on: []
|
||||
tasks:
|
||||
- name: compile_test_and_package_parallel_core_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge-commitqueue
|
||||
- name: compile_test_and_package_parallel_unittest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge-commitqueue
|
||||
- name: compile_test_and_package_parallel_dbtest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge-commitqueue
|
||||
- name: jsCore
|
||||
distros:
|
||||
- rhel80-xlarge-commitqueue
|
||||
- name: .lint
|
||||
- name: test_api_version_compatibility
|
||||
- name: validate_commit_message
|
||||
- name: check_feature_flag_tags
|
||||
- name: compile_venv_deps_check
|
||||
|
||||
@ -10,6 +10,10 @@
|
||||
# in alphabetical order.
|
||||
|
||||
overrides:
|
||||
amazon-linux2-arm64-compile:
|
||||
- task: server_discovery_and_monitoring_json_test
|
||||
exec_timeout: 30 # 30 min.
|
||||
|
||||
enterprise-amazon2-arm64:
|
||||
- task: config_fuzzer_replica_sets_jscore_passthrough
|
||||
exec_timeout: 150 # 2.5 hours.
|
||||
@ -68,6 +72,10 @@ overrides:
|
||||
- task: auth
|
||||
exec_timeout: 60 # 1 hour.
|
||||
|
||||
linux-x86-dynamic-compile-required:
|
||||
- task: server_discovery_and_monitoring_json_test
|
||||
exec_timeout: 30 # 30 min.
|
||||
|
||||
linux-64-debug-repeated-execution:
|
||||
- task: run_unittests
|
||||
exec_timeout: 120 # 2 hours.
|
||||
|
||||
@ -66,7 +66,7 @@ modules:
|
||||
- name: enterprise
|
||||
repo: git@github.com:10gen/mongo-enterprise-modules.git
|
||||
prefix: src/mongo/db/modules
|
||||
branch: master
|
||||
branch: v6.2
|
||||
|
||||
- name: wtdevelop
|
||||
repo: git@github.com:wiredtiger/wiredtiger.git
|
||||
@ -608,7 +608,7 @@ functions:
|
||||
"get buildnumber": &get_buildnumber
|
||||
command: keyval.inc
|
||||
params:
|
||||
key: "${build_variant}_master"
|
||||
key: "${build_variant}_v6.2"
|
||||
destination: "builder_num"
|
||||
|
||||
"run diskstats": &run_diskstats
|
||||
@ -5017,7 +5017,9 @@ tasks:
|
||||
- func: "run tests"
|
||||
vars:
|
||||
suite: core
|
||||
resmoke_args: --fuzzMongodConfigs
|
||||
resmoke_args: >-
|
||||
--fuzzMongodConfigs
|
||||
--excludeWithAnyTags=does_not_support_config_fuzzer
|
||||
|
||||
- <<: *task_template
|
||||
name: config_fuzzer_concurrency
|
||||
@ -5027,7 +5029,9 @@ tasks:
|
||||
- func: "run tests"
|
||||
vars:
|
||||
suite: concurrency
|
||||
resmoke_args: --fuzzMongodConfigs
|
||||
resmoke_args: >-
|
||||
--fuzzMongodConfigs
|
||||
--excludeWithAnyTags=does_not_support_config_fuzzer
|
||||
|
||||
- <<: *task_template
|
||||
name: config_fuzzer_simulate_crash_concurrency_replication
|
||||
@ -5037,7 +5041,9 @@ tasks:
|
||||
- func: "run tests"
|
||||
vars:
|
||||
suite: simulate_crash_concurrency_replication
|
||||
resmoke_args: --fuzzMongodConfigs
|
||||
resmoke_args: >-
|
||||
--fuzzMongodConfigs
|
||||
--excludeWithAnyTags=does_not_support_config_fuzzer
|
||||
|
||||
- <<: *task_template
|
||||
name: config_fuzzer_concurrency_replication
|
||||
@ -5047,7 +5053,9 @@ tasks:
|
||||
- func: "run tests"
|
||||
vars:
|
||||
suite: concurrency_replication
|
||||
resmoke_args: --fuzzMongodConfigs
|
||||
resmoke_args: >-
|
||||
--fuzzMongodConfigs
|
||||
--excludeWithAnyTags=does_not_support_config_fuzzer
|
||||
|
||||
- <<: *task_template
|
||||
name: config_fuzzer_replica_sets_jscore_passthrough
|
||||
@ -5057,7 +5065,9 @@ tasks:
|
||||
- func: "run tests"
|
||||
vars:
|
||||
suite: replica_sets_jscore_passthrough
|
||||
resmoke_args: --fuzzMongodConfigs
|
||||
resmoke_args: >-
|
||||
--fuzzMongodConfigs
|
||||
--excludeWithAnyTags=does_not_support_config_fuzzer
|
||||
|
||||
- <<: *task_template
|
||||
name: jsCore_ese
|
||||
@ -6173,6 +6183,7 @@ tasks:
|
||||
- func: "generate resmoke tasks"
|
||||
vars:
|
||||
use_large_distro: "true"
|
||||
multiversion_exclude_tags_version: last_lts
|
||||
|
||||
- <<: *gen_task_template
|
||||
name: sharding_opportunistic_secondary_targeting_gen
|
||||
@ -6827,19 +6838,6 @@ tasks:
|
||||
- *set_up_venv
|
||||
- func: "run package test"
|
||||
|
||||
- name: test_packages_complete
|
||||
tags: []
|
||||
depends_on:
|
||||
- name: package
|
||||
commands:
|
||||
- command: manifest.load
|
||||
- func: "git get project and add git tag"
|
||||
- *f_expansions_write
|
||||
- *kill_processes
|
||||
- *cleanup_environment
|
||||
- *set_up_venv
|
||||
- func: "run complete package test"
|
||||
|
||||
- name: package
|
||||
tags: []
|
||||
depends_on:
|
||||
@ -7019,42 +7017,6 @@ tasks:
|
||||
args:
|
||||
- "./src/evergreen/packages_publish.sh"
|
||||
|
||||
- name: publish_packages_test
|
||||
run_on: rhel80-small
|
||||
tags: ["publish_test"]
|
||||
# This should prevent this task from running in patch builds, where we
|
||||
# don't want to publish packages.
|
||||
patchable: false
|
||||
stepback: false
|
||||
# Same dependencies as "push" below
|
||||
depends_on:
|
||||
- name: package
|
||||
- name: jsCore
|
||||
- name: run_dbtest
|
||||
- name: replica_sets_jscore_passthrough
|
||||
commands:
|
||||
- command: manifest.load
|
||||
- func: "git get project and add git tag"
|
||||
- func: "get and apply version expansions"
|
||||
- func: "f_expansions_write"
|
||||
- func: "kill processes"
|
||||
- func: "cleanup environment"
|
||||
- func: "set up venv"
|
||||
- func: "fetch packages"
|
||||
- func: "generate compile expansions"
|
||||
- func: "apply compile expansions"
|
||||
- func: "set up remote credentials"
|
||||
vars:
|
||||
aws_key_remote: ${repo_aws_key}
|
||||
aws_secret_remote: ${repo_aws_secret}
|
||||
- func: "set up notary client credentials"
|
||||
- *f_expansions_write
|
||||
- command: subprocess.exec
|
||||
params:
|
||||
binary: bash
|
||||
args:
|
||||
- "./src/evergreen/packages_publish_test.sh"
|
||||
|
||||
- name: push
|
||||
run_on: rhel80-small
|
||||
tags: ["publish"]
|
||||
|
||||
@ -88,17 +88,6 @@ buildvariants:
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
- name: .integration !.audit
|
||||
|
||||
- <<: *generic_linux_compile_params
|
||||
name: &linux-x86-dynamic-debug-wtdevelop-compile linux-x86-dynamic-debug-wtdevelop-compile
|
||||
display_name: "~ Linux WiredTiger develop DEBUG Compile"
|
||||
activate: false
|
||||
modules:
|
||||
- wtdevelop
|
||||
expansions:
|
||||
<<: *linux_debug_compile_expansions
|
||||
use_wt_develop: true
|
||||
compile_variant: *linux-x86-dynamic-debug-wtdevelop-compile
|
||||
|
||||
- <<: *generic_linux_compile_params
|
||||
name: &linux-debug-aubsan-compile-required linux-debug-aubsan-compile-required
|
||||
display_name: "! Linux x86 Shared Library {A,UB}SAN Compile"
|
||||
|
||||
@ -177,7 +177,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-amazon2
|
||||
@ -244,7 +243,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: amazon2-arm64
|
||||
@ -298,7 +296,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-amazon2-arm64
|
||||
@ -427,7 +424,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: amazon2022-arm64
|
||||
@ -477,7 +473,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2204-arm64-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-amazon2022-arm64
|
||||
@ -590,8 +585,6 @@ buildvariants:
|
||||
distros:
|
||||
- amazon2022-arm64-large
|
||||
- name: .publish_crypt
|
||||
distros:
|
||||
- amazon2022-arm64-large
|
||||
- name: secondary_reads_passthrough_gen
|
||||
- name: server_discovery_and_monitoring_json_test_TG
|
||||
- name: .serverless
|
||||
@ -604,7 +597,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2204-arm64-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: debian10
|
||||
@ -660,7 +652,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-debian10-64
|
||||
@ -724,7 +715,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: debian11
|
||||
@ -780,7 +770,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2204-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-debian11-64
|
||||
@ -842,7 +831,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2204-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: rhel70
|
||||
@ -898,7 +886,6 @@ buildvariants:
|
||||
- ubuntu2004-package
|
||||
- name: selinux_rhel7_org
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
# This variant compiles on RHEL 7.0 and runs tests on RHEL 7.6
|
||||
@ -999,7 +986,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-rhel-80-64-bit
|
||||
@ -1125,7 +1111,6 @@ buildvariants:
|
||||
- ubuntu2004-package
|
||||
- name: selinux_rhel8_enterprise
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: rhel-82-arm64
|
||||
@ -1177,7 +1162,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-rhel-82-arm64
|
||||
@ -1246,7 +1230,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: rhel90
|
||||
@ -1298,7 +1281,6 @@ buildvariants:
|
||||
- ubuntu2204-large
|
||||
- name: selinux_rhel9_org
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-rhel-90-64-bit
|
||||
@ -1375,7 +1357,6 @@ buildvariants:
|
||||
- ubuntu2204-large
|
||||
- name: selinux_rhel9_enterprise
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: suse12
|
||||
@ -1433,7 +1414,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-suse12-64
|
||||
@ -1485,12 +1465,13 @@ buildvariants:
|
||||
- name: sharding_auth_gen
|
||||
- name: .stitch
|
||||
- name: .crypt
|
||||
distros:
|
||||
- suse12-sp5-large
|
||||
- name: .publish_crypt
|
||||
- name: test_packages
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-suse15-64
|
||||
@ -1542,7 +1523,6 @@ buildvariants:
|
||||
- name: .crypt
|
||||
- name: .publish_crypt
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: test_packages
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
@ -1597,7 +1577,6 @@ buildvariants:
|
||||
- name: .ssl
|
||||
- name: .stitch
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: test_packages
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
@ -1664,7 +1643,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-ubuntu1804-64
|
||||
@ -1747,7 +1725,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-ubuntu1804-arm64
|
||||
@ -1813,7 +1790,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: ubuntu1804-arm64
|
||||
@ -1853,7 +1829,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: ubuntu2204
|
||||
@ -1909,7 +1884,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2204-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: ubuntu2004
|
||||
@ -1965,7 +1939,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-ubuntu2004-64
|
||||
@ -2031,7 +2004,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2004-package
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-ubuntu2204-64
|
||||
@ -2097,11 +2069,7 @@ buildvariants:
|
||||
- name: test_packages
|
||||
distros:
|
||||
- ubuntu2204-large
|
||||
- name: test_packages_complete
|
||||
distros:
|
||||
- ubuntu2204-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-ubuntu2004-arm64
|
||||
@ -2163,7 +2131,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: ubuntu2004-arm64
|
||||
@ -2201,7 +2168,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu1804-arm64-build
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
|
||||
@ -2217,7 +2183,7 @@ buildvariants:
|
||||
push_path: linux
|
||||
push_bucket: downloads.10gen.com
|
||||
push_name: linux
|
||||
push_arch: arm64-enterprise-ubuntu2204
|
||||
push_arch: aarch64-enterprise-ubuntu2204
|
||||
compile_flags: --ssl MONGO_DISTMOD=ubuntu2204 -j$(grep -c ^processor /proc/cpuinfo) --variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars
|
||||
crypt_task_compile_flags: SHLINKFLAGS_EXTRA="-Wl,-Bsymbolic -Wl,--no-gnu-unique" CCFLAGS="-fno-gnu-unique"
|
||||
resmoke_jobs_max: 4 # Avoid starting too many mongod's on ARM test servers
|
||||
@ -2256,11 +2222,7 @@ buildvariants:
|
||||
- name: test_packages
|
||||
distros:
|
||||
- ubuntu2204-arm64-large
|
||||
- name: test_packages_complete
|
||||
distros:
|
||||
- ubuntu2204-arm64-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: ubuntu2204-arm64
|
||||
@ -2272,7 +2234,7 @@ buildvariants:
|
||||
push_path: linux
|
||||
push_bucket: downloads.mongodb.org
|
||||
push_name: linux
|
||||
push_arch: arm64-ubuntu2204
|
||||
push_arch: aarch64-ubuntu2204
|
||||
compile_flags: --ssl MONGO_DISTMOD=ubuntu2204 -j$(grep -c ^processor /proc/cpuinfo) --variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars
|
||||
resmoke_jobs_max: 8 # Avoid starting too many mongod's on ARM test servers
|
||||
has_packages: true
|
||||
@ -2294,7 +2256,6 @@ buildvariants:
|
||||
distros:
|
||||
- ubuntu2204-arm64-large
|
||||
- name: .publish
|
||||
- name: .publish_test
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: windows
|
||||
|
||||
882
etc/perf.yml
882
etc/perf.yml
@ -1,882 +0,0 @@
|
||||
command_type: system
|
||||
stepback: false
|
||||
|
||||
## Parameters for parameterized builds (see https://github.com/evergreen-ci/evergreen/wiki/Parameterized-Builds)
|
||||
parameters:
|
||||
- key: patch_compile_flags
|
||||
description: "Additional SCons flags to be applied during scons compile invocations in this patch"
|
||||
|
||||
variables:
|
||||
###
|
||||
# Leave this section uncommented to enable compile.
|
||||
_real_remote_file: &_remote_file
|
||||
${project_dir}/${version_id}/${revision}/${platform}/mongodb${compile-variant|}-${version_id}.tar.gz
|
||||
_real_compile: &_compile
|
||||
- variant: linux-wt-standalone
|
||||
name: compile
|
||||
_real_expansions: &_expansion_updates
|
||||
[]
|
||||
###
|
||||
|
||||
###
|
||||
# **Or**: Leave this section uncommented to bypass/skip compile.
|
||||
# This file ↓ came from a microbenchmarks waterfall run.
|
||||
# https://evergreen.mongodb.com/version/performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551
|
||||
# Artifacts eventually expire. If this fails, grab the compile artifacts url and update this.
|
||||
# _skip_remote_file: &_remote_file
|
||||
# /perf/performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551/c0a8cbe58cc46253a94130d2cb64cdd8089b3551/linux/mongodb-performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551.tar.gz
|
||||
# _skip_compile: &_compile
|
||||
# []
|
||||
# _skip_expansions: &_expansion_updates
|
||||
# - key: mdb_binary_for_server
|
||||
# value: https://mciuploads.s3.amazonaws.com/perf/performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551/c0a8cbe58cc46253a94130d2cb64cdd8089b3551/linux/mongodb-performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551.tar.gz
|
||||
# - key: mdb_binary_for_client
|
||||
# value: https://mciuploads.s3.amazonaws.com/perf/performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551/c0a8cbe58cc46253a94130d2cb64cdd8089b3551/linux/mongodb-performance_c0a8cbe58cc46253a94130d2cb64cdd8089b3551.tar.gz
|
||||
###
|
||||
|
||||
_src_dir: &src_dir src/mongo
|
||||
_modules: &modules
|
||||
- enterprise
|
||||
- mongo-tools
|
||||
- dsi
|
||||
- genny
|
||||
- workloads
|
||||
- linkbench
|
||||
- linkbench2
|
||||
- mongo-perf
|
||||
- YCSB
|
||||
- benchmarks
|
||||
- py-tpcc
|
||||
|
||||
modules:
|
||||
###
|
||||
# Same in every DSI project. Ensure that this block is synchronized with
|
||||
# evergreen-dsitest.yml, atlas/system_perf_atlas.yml, and src/dsi/onboarding.py
|
||||
# (search update-repos-here) in this repo, and etc/system_perf.yml and
|
||||
# etc/perf.yml in mongodb/mongo
|
||||
- name: dsi
|
||||
repo: git@github.com:10gen/dsi.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
- name: genny
|
||||
repo: git@github.com:10gen/genny.git
|
||||
prefix: ${workdir}/src
|
||||
branch: microbenchmarks-stable
|
||||
- name: workloads
|
||||
repo: git@github.com:10gen/workloads.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
- name: linkbench
|
||||
repo: git@github.com:10gen/linkbench.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
- name: linkbench2
|
||||
repo: git@github.com:10gen/linkbench2.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
- name: mongo-perf
|
||||
repo: git@github.com:mongodb/mongo-perf.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
- name: YCSB
|
||||
repo: git@github.com:mongodb-labs/YCSB.git
|
||||
prefix: ${workdir}/src
|
||||
branch: production
|
||||
- name: benchmarks
|
||||
repo: git@github.com:mongodb-labs/benchmarks.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
- name: py-tpcc
|
||||
repo: git@github.com:mongodb-labs/py-tpcc.git
|
||||
prefix: ${workdir}/src
|
||||
branch: production
|
||||
|
||||
###
|
||||
# - name: mongo
|
||||
# repo: git@github.com:mongodb/mongo.git
|
||||
# prefix: ${workdir}/src
|
||||
# branch: master
|
||||
- name: enterprise
|
||||
repo: git@github.com:10gen/mongo-enterprise-modules.git
|
||||
prefix: src/mongo/db/modules
|
||||
branch: master
|
||||
- name: mongo-tools
|
||||
repo: git@github.com:mongodb/mongo-tools.git
|
||||
prefix: mongo-tools/src/github.com/mongodb
|
||||
branch: master
|
||||
|
||||
|
||||
###
|
||||
# Same in every DSI project
|
||||
pre:
|
||||
- func: f_other_pre_ops
|
||||
- func: f_dsi_pre_run
|
||||
post:
|
||||
- func: f_dsi_post_run
|
||||
- func: f_other_post_ops
|
||||
timeout:
|
||||
- func: f_dsi_timeout
|
||||
- func: f_other_timeout
|
||||
###
|
||||
|
||||
functions:
|
||||
###
|
||||
# Same in every DSI project
|
||||
f_dsi_pre_run:
|
||||
- command: manifest.load
|
||||
- command: expansions.update
|
||||
params:
|
||||
updates: *_expansion_updates
|
||||
f_run_dsi_workload:
|
||||
- command: git.get_project
|
||||
params:
|
||||
directory: *src_dir
|
||||
revisions:
|
||||
dsi: ${dsi_rev}
|
||||
genny: ${genny_rev}
|
||||
linkbench: ${linkbench_rev}
|
||||
linkbench2: ${linkbench2_rev}
|
||||
workloads: ${workloads_rev}
|
||||
mongo-perf: ${mongo-perf_rev}
|
||||
YCSB: ${YCSB_rev}
|
||||
benchmarks: ${benchmarks_rev}
|
||||
py-tpcc: ${py-tpcc_rev}
|
||||
- command: expansions.write
|
||||
params:
|
||||
file: ./expansions.yml
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: ./src/dsi/run-dsi run_workload
|
||||
- command: shell.exec
|
||||
type: system
|
||||
params:
|
||||
script: ./src/dsi/run-dsi determine_failure -m SYSTEM
|
||||
- command: shell.exec
|
||||
type: setup
|
||||
params:
|
||||
script: ./src/dsi/run-dsi determine_failure -m SETUP
|
||||
- command: shell.exec
|
||||
type: test
|
||||
params:
|
||||
script: ./src/dsi/run-dsi determine_failure -m TEST
|
||||
f_dsi_post_run:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: ./src/dsi/run-dsi post_run
|
||||
- command: perf.send
|
||||
params:
|
||||
file: ./build/CedarReports/cedar_report.json
|
||||
aws_key: ${terraform_key}
|
||||
aws_secret: ${terraform_secret}
|
||||
bucket: genny-metrics
|
||||
region: us-east-1
|
||||
prefix: ${task_id}_${execution}
|
||||
- command: attach.results
|
||||
params:
|
||||
file_location: ./build/EvergreenResultsJson/results.json
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: ./build/Artifacts/DSIArtifacts.tgz
|
||||
remote_file: ${project_dir}/${build_variant}/${revision}/${task_id}/${version_id}/logs/dsi-artifacts-${task_name}-${build_id}-${execution}.tgz
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: application/x-gzip
|
||||
display_name: DSI Artifacts - Execution ${execution}
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: ./build/Documentation/index.html
|
||||
remote_file: ${project_dir}/${build_variant}/${revision}/${task_id}/${version_id}/logs/${task_name}-${build_id}-index.html
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: text/html
|
||||
display_name: Documentation
|
||||
f_dsi_timeout:
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: ./src/dsi/run-dsi on_timeout
|
||||
###
|
||||
|
||||
f_other_post_ops:
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
# removes files from the (local) scons cache when it's over a
|
||||
# threshold, to the $prune_ratio percentage. Ideally override
|
||||
# these default values in the distro config in evergreen.
|
||||
|
||||
if [ -d "${scons_cache_path}" ]; then
|
||||
/opt/mongodbtoolchain/v3/bin/python3 buildscripts/scons_cache_prune.py --cache-dir ${scons_cache_path} --cache-size ${scons_cache_size|200} --prune-ratio ${scons_prune_ratio|0.8}
|
||||
fi
|
||||
f_other_pre_ops:
|
||||
- &f_other_pre_ops
|
||||
command: shell.exec
|
||||
params:
|
||||
silent: true
|
||||
script: |
|
||||
for PS in mongo{,d,s,import,export,dump,restore,stat,files,top,bridge} resmoke.py python{,2} lldb _test; do
|
||||
pkill -9 "$PS"
|
||||
done
|
||||
f_other_timeout:
|
||||
# Can't be empty so just `echo`.
|
||||
- command: shell.exec
|
||||
params: {script: "echo"}
|
||||
|
||||
###
|
||||
# Compile
|
||||
compile mongodb:
|
||||
# We create a virtual environment with the Python dependencies for compiling the server
|
||||
# installed.
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
mkdir -p mongodb/bin
|
||||
|
||||
/opt/mongodbtoolchain/v3/bin/virtualenv --python /opt/mongodbtoolchain/v3/bin/python3 "${workdir}/compile_venv"
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
|
||||
python -m pip install -r etc/pip/compile-requirements.txt
|
||||
- command: expansions.write
|
||||
params:
|
||||
file: expansions.yml
|
||||
redacted: true
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
|
||||
# We get the raw version string (r1.2.3-45-gabcdef) from git
|
||||
export MONGO_VERSION=$(git describe --abbrev=7)
|
||||
|
||||
# If this is a patch build, we add the patch version id to the version string so we know
|
||||
# this build was a patch, and which evergreen task it came from
|
||||
if [ "${is_patch|false}" = "true" ]; then
|
||||
MONGO_VERSION="$MONGO_VERSION-patch-${version_id}"
|
||||
fi
|
||||
|
||||
# This script handles sanitizing the version string for use during SCons build
|
||||
# and when pushing artifacts up to S3.
|
||||
IS_PATCH=${is_patch|false} IS_COMMIT_QUEUE=${is_commit_queue|false} \
|
||||
buildscripts/generate_version_expansions.py --out version_expansions.yml
|
||||
- command: expansions.update
|
||||
params:
|
||||
file: src/version_expansions.yml
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
# This script handles whether the SCons cache should be used
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
SCONS_CACHE_MODE=${scons_cache_mode|} USE_SCONS_CACHE=${use_scons_cache|false} \
|
||||
IS_PATCH=${is_patch|false} IS_COMMIT_QUEUE=${is_commit_queue|false} \
|
||||
python buildscripts/generate_compile_expansions.py --out compile_expansions.yml
|
||||
- command: expansions.update
|
||||
params:
|
||||
file: src/compile_expansions.yml
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src/mongo-tools/src/github.com/mongodb/mongo-tools
|
||||
script: |
|
||||
set -o verbose
|
||||
set -o errexit
|
||||
|
||||
# make sure newlines in the scripts are handled correctly by windows
|
||||
if [ "Windows_NT" = "$OS" ]; then
|
||||
set -o igncr
|
||||
fi;
|
||||
|
||||
# set_goenv provides set_goenv(), print_ldflags() and print_tags() used below
|
||||
. ./set_goenv.sh
|
||||
GOROOT="" set_goenv || exit
|
||||
go version
|
||||
|
||||
build_tools="bsondump mongostat mongofiles mongoexport mongoimport mongorestore mongodump mongotop"
|
||||
if [ "${build_mongoreplay}" = "true" ]; then
|
||||
build_tools="$build_tools mongoreplay"
|
||||
fi
|
||||
for i in $build_tools; do
|
||||
go build -ldflags "$(print_ldflags)" ${args} -tags "$(print_tags ${tooltags})" -o "../../../../../mongodb/bin/$i${exe|}" $i/main/$i.go
|
||||
"../../../../../mongodb/bin/$i${exe|}" --version
|
||||
done
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
python ./buildscripts/idl/gen_all_feature_flag_list.py --import-dir src --import-dir src/mongo/db/modules/enterprise/src
|
||||
mkdir -p mongodb/feature_flags
|
||||
cp ./all_feature_flags.txt mongodb/feature_flags
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
python ./buildscripts/scons.py ${compile_flags|} ${scons_cache_args|} $extra_args install-core install-jstestshell SPLIT_DWARF=0 MONGO_VERSION=${version} DESTDIR=$(pwd)/mongodb ${patch_compile_flags|}
|
||||
mkdir -p mongodb/jstests/hooks
|
||||
if [ -d jstests/hooks ]
|
||||
then
|
||||
echo "Fetching JS test DB correctness checks from directory jstests"
|
||||
cp -a jstests/* mongodb/jstests
|
||||
|
||||
echo "Now adding our own special run_validate_collections.js wrapper"
|
||||
mv mongodb/jstests/hooks/run_validate_collections.js mongodb/jstests/hooks/run_validate_collections.actual.js
|
||||
|
||||
cat << EOF > mongodb/jstests/hooks/run_validate_collections.js
|
||||
print("NOTE: run_validate_collections.js will skip the oplog!");
|
||||
TestData = { skipValidationNamespaces: ['local.oplog.rs'] };
|
||||
load('jstests/hooks/run_validate_collections.actual.js');
|
||||
EOF
|
||||
fi
|
||||
tar czf mongodb${compile-variant|}.tar.gz mongodb
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/mongodb${compile-variant|}.tar.gz
|
||||
remote_file: ${project_dir}/${version_id}/${revision}/${platform}/mongodb${compile-variant|}-${version_id}.tar.gz
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
display_name: mongodb${compile-variant|}.tar.gz
|
||||
###
|
||||
|
||||
## Schedule Tasks ##
|
||||
f_schedule_tasks:
|
||||
- command: git.get_project
|
||||
params:
|
||||
directory: *src_dir
|
||||
revisions:
|
||||
dsi: ${dsi_rev}
|
||||
genny: ${genny_rev}
|
||||
linkbench: ${linkbench_rev}
|
||||
linkbench2: ${linkbench2_rev}
|
||||
workloads: ${workloads_rev}
|
||||
mongo-perf: ${mongo-perf_rev}
|
||||
YCSB: ${YCSB_rev}
|
||||
benchmarks: ${benchmarks_rev}
|
||||
py-tpcc: ${py-tpcc_rev}
|
||||
- command: expansions.write
|
||||
params:
|
||||
file: ./expansions.yml
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: ./src/dsi/run-dsi schedule_tasks --tasks=${tasks}
|
||||
- command: generate.tasks
|
||||
params:
|
||||
files:
|
||||
- build/TaskJSON/Tasks.json
|
||||
|
||||
|
||||
tasks:
|
||||
###
|
||||
# Same in every DSI project
|
||||
- name: schedule_global_auto_tasks
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_schedule_tasks
|
||||
vars:
|
||||
tasks: all_tasks
|
||||
- name: schedule_variant_auto_tasks
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_schedule_tasks
|
||||
vars:
|
||||
tasks: variant_tasks
|
||||
- name: schedule_patch_auto_tasks
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_schedule_tasks
|
||||
vars:
|
||||
tasks: patch_tasks
|
||||
- name: smoke_test
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: short
|
||||
- name: smoke_test_ssl
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: short
|
||||
mongodb_setup: replica-ssl
|
||||
infrastructure_provisioning: replica
|
||||
- name: smoke_test_standalone_auth
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: short
|
||||
mongodb_setup: standalone-auth
|
||||
infrastructure_provisioning: single
|
||||
- name: smoke_test_replset_auth
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: short
|
||||
mongodb_setup: replica-auth
|
||||
infrastructure_provisioning: replica
|
||||
- name: smoke_test_shard_lite_auth
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: short
|
||||
mongodb_setup: shard-lite-auth
|
||||
infrastructure_provisioning: shard-lite
|
||||
- name: dsi_integ_test_run_command_simple
|
||||
priority: 5
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: run_command_simple
|
||||
###
|
||||
|
||||
- name: compile
|
||||
commands:
|
||||
- command: manifest.load
|
||||
- command: git.get_project
|
||||
params:
|
||||
directory: src
|
||||
revisions:
|
||||
enterprise: ${enterprise_rev}
|
||||
mongo-tools: ${mongo-tools_rev}
|
||||
- func: "compile mongodb"
|
||||
|
||||
|
||||
- name: genny_execution_UserAcquisition
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: auto_genny_workload
|
||||
auto_workload_path: ./src/genny/dist/etc/genny/workloads/execution/UserAcquisition.yml
|
||||
- name: genny_scale_InsertRemove
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: auto_genny_workload
|
||||
auto_workload_path: ./src/genny/dist/etc/genny/workloads/scale/InsertRemove.yml
|
||||
- name: query_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: query,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: query_read_commands_large_dataset
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: query_large_dataset,
|
||||
include_filter_2: regression,
|
||||
exclude_filter: none,
|
||||
threads: "1 4",
|
||||
read_cmd: 'true',
|
||||
share_dataset: 'true'}
|
||||
- name: big_collection
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: query,
|
||||
include_filter_2: getmore,
|
||||
exclude_filter: none,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: views-query
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: query_identityview,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: views-aggregation
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: aggregation_identityview,
|
||||
include_filter_2: regression,
|
||||
exclude_filter: none,
|
||||
threads: "1",
|
||||
read_cmd: 'true'}
|
||||
- name: where_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: where,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: update_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: update,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: insert_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: insert,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: wildcard-index-read_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: wildcard_read,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: wildcard-index-write_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: wildcard_write,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: geo_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: geo,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: misc_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: command multi remove mixed,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: misc_custom_filter_default_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_standalone_custom_filter_default
|
||||
test_control_params: |
|
||||
{include_filter_1: command multi remove mixed,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: misc_custom_filter_slow_or_sample_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_standalone_custom_filter_slow_or_sample
|
||||
test_control_params: |
|
||||
{include_filter_1: command multi remove mixed,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: misc_custom_filter_complex_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_standalone_custom_filter_complex
|
||||
test_control_params: |
|
||||
{include_filter_1: command multi remove mixed,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: misc_custom_filter_whole_doc_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_standalone_custom_filter_whole_doc
|
||||
test_control_params: |
|
||||
{include_filter_1: command multi remove mixed,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: misc_slowms_everything_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_standalone_slowms_everything
|
||||
test_control_params: |
|
||||
{include_filter_1: command multi remove mixed,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: singleThreaded_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: single_threaded,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: none,
|
||||
threads: "1",
|
||||
read_cmd: 'true'}
|
||||
- name: aggregation_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: aggregation,
|
||||
include_filter_2: regression,
|
||||
exclude_filter: js,
|
||||
threads: "1",
|
||||
read_cmd: 'true'}
|
||||
- name: aggregation_read_commands_large_dataset
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: aggregation_large_dataset,
|
||||
include_filter_2: regression,
|
||||
exclude_filter: js,
|
||||
threads: "1 4",
|
||||
read_cmd: 'true',
|
||||
share_dataset: 'true'}
|
||||
- name: agg-query-comparison_read_commands
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: agg_query_comparison,
|
||||
include_filter_2: core regression,
|
||||
exclude_filter: single_threaded,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: pipeline-updates
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: pipeline-updates,
|
||||
include_filter_2: regression,
|
||||
exclude_filter: none,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
- name: javascript
|
||||
depends_on: *_compile
|
||||
commands:
|
||||
- func: f_run_dsi_workload
|
||||
vars:
|
||||
test_control: microbenchmarks
|
||||
test_control_params: |
|
||||
{include_filter_1: js,
|
||||
include_filter_2: aggregation,
|
||||
exclude_filter: none,
|
||||
threads: "1 2 4 8",
|
||||
read_cmd: 'true'}
|
||||
|
||||
|
||||
microbenchmark-buildvariants:
|
||||
_linux-wt-standalone: &linux-wt-standalone
|
||||
name: linux-wt-standalone
|
||||
display_name: Standalone Linux inMemory
|
||||
cron: "0 */4 * * *" # Every 4 hours starting at midnight
|
||||
modules: *modules
|
||||
expansions:
|
||||
# We are explicitly tracking the rhel62 variant compile options from evergreen.yml for
|
||||
# microbenchmarks, since they run on the centos6 boxes. If we can get proper artifacts directly
|
||||
# from that project, we should do that and remove the compile tasks.
|
||||
compile_flags: >-
|
||||
--ssl
|
||||
--separate-debug MONGO_DISTMOD=rhel62
|
||||
-j$(grep -c ^processor /proc/cpuinfo)
|
||||
--release
|
||||
--variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars
|
||||
mongod_exec_wrapper: &exec_wrapper "numactl --physcpubind=4,5,6,7 -i 1"
|
||||
perf_exec_wrapper: &perf_wrapper "numactl --physcpubind=1,2,3 -i 0"
|
||||
use_scons_cache: true
|
||||
platform: linux
|
||||
infrastructure_provisioning: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_standalone
|
||||
canaries: none
|
||||
storageEngine: inMemory
|
||||
project_dir: &project_dir perf
|
||||
run_on:
|
||||
- "centos6-perf"
|
||||
tasks:
|
||||
- name: compile
|
||||
distros:
|
||||
- rhel62-large
|
||||
- name: big_collection
|
||||
- name: genny_scale_InsertRemove
|
||||
- name: genny_execution_UserAcquisition
|
||||
- name: aggregation_read_commands
|
||||
- name: aggregation_read_commands_large_dataset
|
||||
- name: agg-query-comparison_read_commands
|
||||
- name: query_read_commands
|
||||
- name: query_read_commands_large_dataset
|
||||
- name: views-aggregation
|
||||
- name: views-query
|
||||
- name: where_read_commands
|
||||
- name: update_read_commands
|
||||
- name: insert_read_commands
|
||||
- name: wildcard-index-read_read_commands
|
||||
- name: wildcard-index-write_read_commands
|
||||
- name: geo_read_commands
|
||||
- name: misc_read_commands
|
||||
- name: misc_custom_filter_default_read_commands
|
||||
- name: misc_custom_filter_slow_or_sample_read_commands
|
||||
- name: misc_custom_filter_complex_read_commands
|
||||
- name: misc_custom_filter_whole_doc_read_commands
|
||||
- name: misc_slowms_everything_read_commands
|
||||
- name: singleThreaded_read_commands
|
||||
- name: pipeline-updates
|
||||
- name: javascript
|
||||
|
||||
_linux-wt-repl: &linux-wt-repl
|
||||
name: linux-wt-repl
|
||||
display_name: 1-Node ReplSet Linux inMemory
|
||||
cron: "0 */4 * * *" # Every 4 hours starting at midnight
|
||||
modules: *modules
|
||||
expansions:
|
||||
mongod_exec_wrapper: *exec_wrapper
|
||||
perf_exec_wrapper: *perf_wrapper
|
||||
platform: linux
|
||||
infrastructure_provisioning: microbenchmarks
|
||||
mongodb_setup: microbenchmarks_replica
|
||||
canaries: none
|
||||
storageEngine: inMemory
|
||||
project_dir: *project_dir
|
||||
run_on:
|
||||
- "centos6-perf"
|
||||
tasks:
|
||||
- name: genny_scale_InsertRemove
|
||||
- name: update_read_commands
|
||||
- name: insert_read_commands
|
||||
- name: misc_read_commands
|
||||
- name: singleThreaded_read_commands
|
||||
- name: wildcard-index-write_read_commands
|
||||
- name: pipeline-updates
|
||||
|
||||
buildvariants:
|
||||
- *linux-wt-standalone
|
||||
|
||||
- *linux-wt-repl
|
||||
|
||||
- <<: *linux-wt-standalone
|
||||
name: linux-wt-standalone-all-feature-flags
|
||||
display_name: Standalone Linux inMemory (all feature flags)
|
||||
cron: "0 0 * * *" # Every day starting at 00:00
|
||||
expansions:
|
||||
mongodb_setup: microbenchmarks_standalone-all-feature-flags
|
||||
|
||||
- <<: *linux-wt-standalone
|
||||
name: linux-wt-standalone-classic-query-engine
|
||||
display_name: Standalone Linux inMemory (Classic Query Engine)
|
||||
# Will make it less frequent when the current SBE perf improvement is finished (SERVER-69799).
|
||||
cron: "0 0 * * 0,2,3,4,5" # Run it every day except Saturday and Monday.
|
||||
expansions:
|
||||
mongodb_setup: microbenchmarks_standalone-classic-query-engine
|
||||
|
||||
- <<: *linux-wt-standalone
|
||||
name: linux-wt-standalone-sbe
|
||||
display_name: Standalone Linux inMemory (SBE)
|
||||
# Will make it less frequent when the current SBE perf improvement is finished (SERVER-69799).
|
||||
cron: "0 0 * * 0,2,3,4,5" # Run it every day except Saturday and Monday.
|
||||
expansions:
|
||||
mongodb_setup: microbenchmarks_standalone-sbe
|
||||
|
||||
- <<: *linux-wt-repl
|
||||
name: linux-wt-repl-all-feature-flags
|
||||
display_name: 1-Node ReplSet Linux inMemory (all feature flags)
|
||||
cron: "0 0 * * *" # Every day starting at 00:00
|
||||
expansions:
|
||||
mongodb_setup: microbenchmarks_replica-all-feature-flags
|
||||
@ -1,532 +0,0 @@
|
||||
templates:
|
||||
deb:
|
||||
org: |
|
||||
Origin: mongodb
|
||||
Label: mongodb
|
||||
Suite: {{ .CodeName }}
|
||||
Codename: {{ .CodeName }}/mongodb-org
|
||||
Architectures: {{ .Architectures }}
|
||||
Components: {{ .Component }}
|
||||
Description: MongoDB packages
|
||||
enterprise: |
|
||||
Origin: mongodb
|
||||
Label: mongodb
|
||||
Suite: {{ .CodeName }}
|
||||
Codename: {{ .CodeName }}/mongodb-enterprise
|
||||
Architectures: {{ .Architectures }}
|
||||
Components: {{ .Component }}
|
||||
Description: MongoDB packages
|
||||
index_page: |
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ .Title }}</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<table>
|
||||
<tr><td>
|
||||
<h1>{{ .Title }}</h1>
|
||||
</td></tr>
|
||||
|
||||
<tr><td>
|
||||
<hr>
|
||||
</td></tr>
|
||||
|
||||
<tr><td>
|
||||
<a href='..'>Parent Directory</a>
|
||||
</td></tr>
|
||||
|
||||
{{ range $fn := .Files }}
|
||||
<tr><td>
|
||||
<a href='{{ $fn }}'>{{ $fn }}</a>
|
||||
</td></tr>
|
||||
{{ end }}
|
||||
|
||||
<tr><td>
|
||||
<hr>
|
||||
</td></tr>
|
||||
|
||||
<tr><td>
|
||||
<address>{{ .RepoName }}</address>
|
||||
</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
repos:
|
||||
|
||||
####################
|
||||
#
|
||||
# Community Repos:
|
||||
#
|
||||
####################
|
||||
|
||||
- name: rhel70
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/redhat/7/mongodb-org
|
||||
- yum/redhat/7Server/mongodb-org
|
||||
|
||||
- name: rhel72
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/redhat/7/mongodb-org
|
||||
- yum/redhat/7Server/mongodb-org
|
||||
|
||||
- name: rhel80
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-org
|
||||
- yum/redhat/8Server/mongodb-org
|
||||
|
||||
- name: rhel82
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-org
|
||||
- yum/redhat/8Server/mongodb-org
|
||||
|
||||
- name: rhel83
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-org
|
||||
- yum/redhat/8Server/mongodb-org
|
||||
- name: rhel90
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/redhat/9/mongodb-org
|
||||
- yum/redhat/9Server/mongodb-org
|
||||
|
||||
|
||||
- name: amazon
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/amazon/2013.03/mongodb-org
|
||||
|
||||
- name: amazon2
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/amazon/2/mongodb-org
|
||||
|
||||
- name: amazon2022
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- yum/amazon/2022/mongodb-org
|
||||
|
||||
- name: suse11
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- zypper/suse/11/mongodb-org
|
||||
|
||||
- name: suse12
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- zypper/suse/12/mongodb-org
|
||||
|
||||
- name: suse15
|
||||
type: rpm
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
repos:
|
||||
- zypper/suse/15/mongodb-org
|
||||
|
||||
- name: debian81
|
||||
type: deb
|
||||
code_name: "jessie"
|
||||
bucket: test.repo.mongodb.org
|
||||
edition: org
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
- i386
|
||||
repos:
|
||||
- apt/debian/dists/jessie/mongodb-org
|
||||
|
||||
- name: debian92
|
||||
type: deb
|
||||
code_name: "stretch"
|
||||
bucket: test.repo.mongodb.org
|
||||
edition: org
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
repos:
|
||||
- apt/debian/dists/stretch/mongodb-org
|
||||
|
||||
- name: debian10
|
||||
type: deb
|
||||
code_name: "buster"
|
||||
bucket: test.repo.mongodb.org
|
||||
edition: org
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
repos:
|
||||
- apt/debian/dists/buster/mongodb-org
|
||||
|
||||
- name: debian11
|
||||
type: deb
|
||||
code_name: "bullseye"
|
||||
bucket: test.repo.mongodb.org
|
||||
edition: org
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
repos:
|
||||
- apt/debian/dists/bullseye/mongodb-org
|
||||
|
||||
- name: debian71
|
||||
type: deb
|
||||
code_name: "wheezy"
|
||||
bucket: test.repo.mongodb.org
|
||||
edition: org
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
- i386
|
||||
repos:
|
||||
- apt/debian/dists/wheezy/mongodb-org
|
||||
|
||||
- name: ubuntu1204
|
||||
type: deb
|
||||
code_name: "precise"
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- i386
|
||||
repos:
|
||||
- apt/ubuntu/dists/precise/mongodb-org
|
||||
|
||||
- name: ubuntu1404
|
||||
type: deb
|
||||
code_name: "trusty"
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- i386
|
||||
repos:
|
||||
- apt/ubuntu/dists/trusty/mongodb-org
|
||||
|
||||
- name: ubuntu1804
|
||||
type: deb
|
||||
code_name: "bionic"
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- i386
|
||||
- s390x
|
||||
- arm64
|
||||
repos:
|
||||
- apt/ubuntu/dists/bionic/mongodb-org
|
||||
|
||||
- name: ubuntu2004
|
||||
type: deb
|
||||
code_name: "focal"
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- s390x
|
||||
- arm64
|
||||
repos:
|
||||
- apt/ubuntu/dists/focal/mongodb-org
|
||||
|
||||
- name: ubuntu2204
|
||||
type: deb
|
||||
code_name: "jammy"
|
||||
edition: org
|
||||
bucket: test.repo.mongodb.org
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- arm64
|
||||
repos:
|
||||
- apt/ubuntu/dists/jammy/mongodb-org
|
||||
|
||||
####################
|
||||
#
|
||||
# Enterprise Repos:
|
||||
#
|
||||
####################
|
||||
|
||||
- name: rhel62
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/6/mongodb-enterprise
|
||||
- yum/redhat/6Server/mongodb-enterprise
|
||||
|
||||
- name: rhel67
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/6/mongodb-enterprise
|
||||
- yum/redhat/6Server/mongodb-enterprise
|
||||
|
||||
- name: rhel70
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/7/mongodb-enterprise
|
||||
- yum/redhat/7Server/mongodb-enterprise
|
||||
|
||||
- name: rhel71
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/7/mongodb-enterprise
|
||||
- yum/redhat/7Server/mongodb-enterprise
|
||||
|
||||
- name: rhel72
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/7/mongodb-enterprise
|
||||
- yum/redhat/7Server/mongodb-enterprise
|
||||
|
||||
- name: rhel80
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-enterprise
|
||||
- yum/redhat/8Server/mongodb-enterprise
|
||||
|
||||
- name: rhel81
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-enterprise
|
||||
- yum/redhat/8Server/mongodb-enterprise
|
||||
|
||||
- name: rhel82
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-enterprise
|
||||
- yum/redhat/8Server/mongodb-enterprise
|
||||
|
||||
- name: rhel83
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/8/mongodb-enterprise
|
||||
- yum/redhat/8Server/mongodb-enterprise
|
||||
|
||||
- name: rhel90
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/redhat/9/mongodb-enterprise
|
||||
- yum/redhat/9Server/mongodb-enterprise
|
||||
|
||||
- name: amazon
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/amazon/2013.03/mongodb-enterprise
|
||||
|
||||
- name: amazon2
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/amazon/2/mongodb-enterprise
|
||||
|
||||
- name: amazon2022
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- yum/amazon/2022/mongodb-enterprise
|
||||
|
||||
- name: suse11
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- zypper/suse/11/mongodb-enterprise
|
||||
|
||||
- name: suse12
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- zypper/suse/12/mongodb-enterprise
|
||||
|
||||
- name: suse15
|
||||
type: rpm
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
repos:
|
||||
- zypper/suse/15/mongodb-enterprise
|
||||
|
||||
- name: debian10
|
||||
type: deb
|
||||
edition: enterprise
|
||||
code_name: "buster"
|
||||
bucket: test.repo.mongodb.com
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
repos:
|
||||
- apt/debian/dists/buster/mongodb-enterprise
|
||||
|
||||
- name: debian11
|
||||
type: deb
|
||||
edition: enterprise
|
||||
code_name: "bullseye"
|
||||
bucket: test.repo.mongodb.com
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
repos:
|
||||
- apt/debian/dists/bullseye/mongodb-enterprise
|
||||
|
||||
- name: debian92
|
||||
type: deb
|
||||
edition: enterprise
|
||||
code_name: "stretch"
|
||||
bucket: test.repo.mongodb.com
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
repos:
|
||||
- apt/debian/dists/stretch/mongodb-enterprise
|
||||
|
||||
- name: debian81
|
||||
type: deb
|
||||
edition: enterprise
|
||||
code_name: "jessie"
|
||||
bucket: test.repo.mongodb.com
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
- ppc64el
|
||||
- s390x
|
||||
- i386
|
||||
repos:
|
||||
- apt/debian/dists/jessie/mongodb-enterprise
|
||||
|
||||
- name: debian71
|
||||
type: deb
|
||||
code_name: "wheezy"
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
component: main
|
||||
architectures:
|
||||
- amd64
|
||||
- ppc64el
|
||||
- s390x
|
||||
- i386
|
||||
repos:
|
||||
- apt/debian/dists/wheezy/mongodb-enterprise
|
||||
|
||||
- name: ubuntu1204
|
||||
type: deb
|
||||
code_name: "precise"
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- ppc64el
|
||||
- s390x
|
||||
- i386
|
||||
repos:
|
||||
- apt/ubuntu/dists/precise/mongodb-enterprise
|
||||
|
||||
- name: ubuntu1404
|
||||
type: deb
|
||||
code_name: "trusty"
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- ppc64el
|
||||
- s390x
|
||||
- i386
|
||||
repos:
|
||||
- apt/ubuntu/dists/trusty/mongodb-enterprise
|
||||
|
||||
- name: ubuntu1804
|
||||
type: deb
|
||||
code_name: "bionic"
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- ppc64el
|
||||
- i386
|
||||
- s390x
|
||||
- arm64
|
||||
repos:
|
||||
- apt/ubuntu/dists/bionic/mongodb-enterprise
|
||||
|
||||
- name: ubuntu2004
|
||||
type: deb
|
||||
code_name: "focal"
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- ppc64el
|
||||
- s390x
|
||||
- arm64
|
||||
repos:
|
||||
- apt/ubuntu/dists/focal/mongodb-enterprise
|
||||
|
||||
- name: ubuntu2204
|
||||
type: deb
|
||||
code_name: "jammy"
|
||||
edition: enterprise
|
||||
bucket: test.repo.mongodb.com
|
||||
component: multiverse
|
||||
architectures:
|
||||
- amd64
|
||||
- arm64
|
||||
repos:
|
||||
- apt/ubuntu/dists/jammy/mongodb-enterprise
|
||||
3268
etc/system_perf.yml
3268
etc/system_perf.yml
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@ enterprise_path="src/mongo/db/modules/enterprise"
|
||||
diff_file_name="with_base_upstream.diff"
|
||||
|
||||
# get the list of feature flags from the patched version
|
||||
$python buildscripts/idl/gen_all_feature_flag_list.py --import-dir src --import-dir "$enterprise_path"/src
|
||||
$python buildscripts/idl/gen_all_feature_flag_list.py
|
||||
mv all_feature_flags.txt patch_all_feature_flags.txt
|
||||
|
||||
# get the list of feature flags from the base commit
|
||||
@ -29,7 +29,7 @@ if [ -s "$diff_file_name" ]; then
|
||||
fi
|
||||
popd
|
||||
|
||||
$python buildscripts/idl/gen_all_feature_flag_list.py --import-dir src --import-dir "$enterprise_path"/src
|
||||
$python buildscripts/idl/gen_all_feature_flag_list.py
|
||||
mv all_feature_flags.txt base_all_feature_flags.txt
|
||||
|
||||
# print out the list of tests that previously had feature flag tag, that was
|
||||
|
||||
@ -6,4 +6,4 @@ cd src
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
activate_venv
|
||||
$python buildscripts/idl/gen_all_feature_flag_list.py --import-dir src --import-dir src/mongo/db/modules/enterprise/src
|
||||
$python buildscripts/idl/gen_all_feature_flag_list.py
|
||||
|
||||
@ -13,4 +13,5 @@ PATH=$PATH:$HOME:/ ./mongo-task-generator \
|
||||
--evg-auth-file ./.evergreen.yml \
|
||||
--evg-project-file ${evergreen_config_file_path} \
|
||||
--generate-sub-tasks-config etc/generate_subtasks_config.yml \
|
||||
--s3-test-stats-endpoint https://mongo-test-stats.s3.amazonaws.com \
|
||||
$@
|
||||
|
||||
@ -13,5 +13,6 @@ PATH=$PATH:$HOME:/ ./mongo-task-generator \
|
||||
--evg-auth-file ./.evergreen.yml \
|
||||
--evg-project-file ${evergreen_config_file_path} \
|
||||
--generate-sub-tasks-config etc/generate_subtasks_config.yml \
|
||||
--s3-test-stats-endpoint https://mongo-test-stats.s3.amazonaws.com \
|
||||
--burn-in \
|
||||
$@
|
||||
|
||||
@ -6,8 +6,6 @@ cd src/jstestfuzz
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
add_nodejs_to_path
|
||||
|
||||
if [ -f "../minimizer-outputs.json" ]; then
|
||||
eval npm run ${npm_command} -- -j "../minimizer-outputs.json"
|
||||
eval ./src/scripts/npm_run.sh ${npm_command} -- -j "../minimizer-outputs.json"
|
||||
fi
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)"
|
||||
. "$DIR/prelude.sh"
|
||||
|
||||
set -evo pipefail
|
||||
|
||||
cd src/jstestfuzz
|
||||
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
add_nodejs_to_path
|
||||
|
||||
in_patch_build_flag=""
|
||||
if [[ "${is_patch}" = "true" ]]; then
|
||||
case "${npm_command}" in
|
||||
@ -17,4 +14,4 @@ if [[ "${is_patch}" = "true" ]]; then
|
||||
esac
|
||||
fi
|
||||
|
||||
eval npm run "${npm_command}" -- "${jstestfuzz_vars}" "${in_patch_build_flag}" --branch "${branch_name}"
|
||||
./src/scripts/npm_run.sh ${npm_command} -- ${jstestfuzz_vars} ${in_patch_build_flag} --branch ${branch_name}
|
||||
|
||||
@ -6,11 +6,4 @@ cd src
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
add_nodejs_to_path
|
||||
|
||||
git clone git@github.com:10gen/jstestfuzz.git
|
||||
|
||||
pushd jstestfuzz
|
||||
npm install
|
||||
npm run prepare
|
||||
popd
|
||||
|
||||
@ -1,17 +1,22 @@
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)"
|
||||
. "$DIR/prelude.sh"
|
||||
|
||||
cd src
|
||||
cd src/jstestfuzz
|
||||
|
||||
set -o pipefail
|
||||
set -o verbose
|
||||
|
||||
add_nodejs_to_path
|
||||
|
||||
# Run first with help which will do the install
|
||||
# Then we can run it in parallel
|
||||
./src/scripts/npm_run.sh --help
|
||||
# Run parse-jsfiles on 50 files at a time with 32 processes in parallel.
|
||||
find "$PWD/jstests" "$PWD/src/mongo/db/modules/enterprise" -name "*.js" -print | xargs -P 32 -L 50 npm run --prefix jstestfuzz parse-jsfiles -- | tee lint_fuzzer_sanity.log
|
||||
# Skip javascript files in third_party directory
|
||||
find "$PWD/../jstests" "$PWD/../src/mongo/db/modules/enterprise" -path "$PWD/../jstests/third_party" -prune -o -name "*.js" -print | xargs -P 32 -L 50 ./src/scripts/npm_run.sh parse-jsfiles -- | tee lint_fuzzer_sanity.log
|
||||
exit_code=$?
|
||||
|
||||
# Exit out of the jstestfuzz directory
|
||||
cd ..
|
||||
|
||||
activate_venv
|
||||
$python ./buildscripts/simple_report.py --test-name lint_fuzzer_sanity_all --log-file lint_fuzzer_sanity.log --exit-code $exit_code
|
||||
$python ./buildscripts/simple_report.py --test-name lint_fuzzer_sanity_all --log-file jstestfuzz/lint_fuzzer_sanity.log --exit-code $exit_code
|
||||
exit $exit_code
|
||||
|
||||
@ -7,15 +7,17 @@ set -o pipefail
|
||||
set -o verbose
|
||||
|
||||
activate_venv
|
||||
add_nodejs_to_path
|
||||
|
||||
mkdir -p jstestfuzzinput jstestfuzzoutput
|
||||
|
||||
indir="$(pwd)/jstestfuzzinput"
|
||||
outdir="$(pwd)/jstestfuzzoutput"
|
||||
# We need to be the jstestfuzz repo for node to install/run
|
||||
cd jstestfuzz
|
||||
|
||||
indir="$(pwd)/../jstestfuzzinput"
|
||||
outdir="$(pwd)/../jstestfuzzoutput"
|
||||
|
||||
# Grep all the js files from modified_and_created_patch_files.txt and put them into $indir.
|
||||
(grep -v "\.tpl\.js$" modified_and_created_patch_files.txt | grep ".*jstests/.*\.js$" | xargs -I {} cp {} $indir || true)
|
||||
(grep -v "\.tpl\.js$" ../modified_and_created_patch_files.txt | grep ".*jstests/.*\.js$" | xargs -I {} cp {} $indir || true)
|
||||
|
||||
# Count the number of files in $indir.
|
||||
if [[ "$(ls -A $indir)" ]]; then
|
||||
@ -26,10 +28,13 @@ if [[ "$(ls -A $indir)" ]]; then
|
||||
num_files=50
|
||||
fi
|
||||
|
||||
npm run --prefix jstestfuzz jstestfuzz -- --jsTestsDir $indir --out $outdir --numSourceFiles $num_files --numGeneratedFiles 50
|
||||
./src/scripts/npm_run.sh jstestfuzz -- --jsTestsDir $indir --out $outdir --numSourceFiles $num_files --numGeneratedFiles 50
|
||||
|
||||
# Run parse-jsfiles on 50 files at a time with 32 processes in parallel.
|
||||
ls -1 -d $outdir/* | xargs -P 32 -L 50 npm run --prefix jstestfuzz parse-jsfiles -- | tee lint_fuzzer_sanity.log
|
||||
ls -1 -d $outdir/* | xargs -P 32 -L 50 ./src/scripts/npm_run.sh parse-jsfiles -- | tee lint_fuzzer_sanity.log
|
||||
exit_code=$?
|
||||
$python ./buildscripts/simple_report.py --test-name lint_fuzzer_sanity_patch --log-file lint_fuzzer_sanity.log --exit-code $exit_code
|
||||
|
||||
# Exit out of the jstestfuzz directory
|
||||
cd ..
|
||||
$python ./buildscripts/simple_report.py --test-name lint_fuzzer_sanity_patch --log-file jstestfuzz/lint_fuzzer_sanity.log --exit-code $exit_code
|
||||
fi
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null 2>&1 && pwd)"
|
||||
. "$DIR/prelude.sh"
|
||||
|
||||
cd src
|
||||
|
||||
. ./notary_env.sh
|
||||
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
CURATOR_RELEASE=${curator_release}
|
||||
curl -L -O http://boxes.10gen.com/build/curator/curator-dist-rhel70-$CURATOR_RELEASE.tar.gz
|
||||
tar -zxvf curator-dist-rhel70-$CURATOR_RELEASE.tar.gz
|
||||
NOTARY_KEY_NAME=${signing_key_name_test} NOTARY_TOKEN=${signing_auth_token_test} BARQUE_API_KEY=${barque_api_key_test} BARQUE_USERNAME=${barque_user_test} ./curator repo submit --service ${barque_url_test} --config ./etc/repo_config_test.yaml --distro ${packager_distro} --edition ${repo_edition} --version ${version} --arch ${packager_arch} --packages https://s3.amazonaws.com/mciuploads/${project}/${build_variant}/${revision}/artifacts/${build_id}-packages.tgz
|
||||
@ -32,21 +32,6 @@ unset expansions_default_yaml
|
||||
unset script
|
||||
unset evergreen_dir
|
||||
|
||||
function add_nodejs_to_path {
|
||||
# Add node and npm binaries to PATH
|
||||
if [ "Windows_NT" = "$OS" ]; then
|
||||
# An "npm" directory might not have been created in %APPDATA% by the Windows installer.
|
||||
# Work around the issue by specifying a different %APPDATA% path.
|
||||
# See: https://github.com/nodejs/node-v0.x-archive/issues/8141
|
||||
export APPDATA=${workdir}/npm-app-data
|
||||
export PATH="$PATH:/cygdrive/c/Program Files (x86)/nodejs" # Windows location
|
||||
# TODO: this is to work around BUILD-8652
|
||||
cd "$(pwd -P | sed 's,cygdrive/c/,cygdrive/z/,')"
|
||||
else
|
||||
export PATH="$PATH:/opt/node/bin"
|
||||
fi
|
||||
}
|
||||
|
||||
function posix_workdir {
|
||||
if [ "Windows_NT" = "$OS" ]; then
|
||||
echo $(cygpath -u "${workdir}")
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
function setup_mongo_task_generator {
|
||||
if [ ! -f mongo-task-generator ]; then
|
||||
curl -L https://github.com/mongodb/mongo-task-generator/releases/download/v0.6.6/mongo-task-generator --output mongo-task-generator
|
||||
curl -L https://github.com/mongodb/mongo-task-generator/releases/download/v0.6.7/mongo-task-generator --output mongo-task-generator
|
||||
chmod +x mongo-task-generator
|
||||
fi
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ from pydantic import ValidationError
|
||||
if __name__ == "__main__" and __package__ is None:
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from buildscripts.metrics.metrics_datatypes import ToolingMetrics
|
||||
from buildscripts.metrics.metrics_datatypes import ResmokeToolingMetrics, SConsToolingMetrics
|
||||
from buildscripts.metrics.tooling_metrics_utils import _get_internal_tooling_metrics_client
|
||||
from evergreen.api import RetryingEvergreenApi
|
||||
|
||||
@ -20,43 +20,56 @@ except Exception as exc:
|
||||
print("Could not connect to Atlas cluster")
|
||||
raise exc
|
||||
|
||||
try:
|
||||
# Get metrics for the last week
|
||||
one_week_ago_datetime = datetime.datetime.utcnow() - datetime.timedelta(days=7)
|
||||
last_week_metrics = client.metrics.tooling_metrics.find(
|
||||
{"utc_starttime": {"$gt": one_week_ago_datetime}})
|
||||
|
||||
malformed_metrics = []
|
||||
invalid_metrics = []
|
||||
total_docs = 0
|
||||
def get_metrics_data(source, MetricsClass, lookback=7):
|
||||
try:
|
||||
# Get SCons metrics for the lookback period
|
||||
lookback_datetime = datetime.datetime.utcnow() - datetime.timedelta(days=lookback)
|
||||
last_week_metrics = client.metrics.tooling_metrics.find({
|
||||
"source": source,
|
||||
"utc_starttime": {"$gt": lookback_datetime},
|
||||
})
|
||||
|
||||
# Find any malformed/invalid documents in the cluster
|
||||
for doc in last_week_metrics:
|
||||
total_docs += 1
|
||||
try:
|
||||
metrics = ToolingMetrics(**doc)
|
||||
if metrics.is_malformed():
|
||||
malformed_metrics.append(doc['_id'])
|
||||
except ValidationError:
|
||||
invalid_metrics.append(doc['_id'])
|
||||
malformed_metrics = []
|
||||
invalid_metrics = []
|
||||
total_docs = 0
|
||||
|
||||
metrics_detailed = ("METRICS DETAILED:\n"
|
||||
f"malformed_metrics_last_week: {malformed_metrics}\n"
|
||||
f"invalid_metrics_last_week: {invalid_metrics}\n"
|
||||
f"total_docs_last_week: {total_docs}")
|
||||
metrics_overview = (
|
||||
"METRICS OVERVIEW:\n"
|
||||
f"malformed_metrics_last_week: {len(malformed_metrics)} ({len(malformed_metrics)/total_docs*100:.2f}%)\n"
|
||||
f"invalid_metrics_last_week: {len(invalid_metrics)} ({len(invalid_metrics)/total_docs*100:.2f}%)\n"
|
||||
f"total_docs_last_week: {total_docs}")
|
||||
# Find any malformed/invalid documents in the cluster
|
||||
for doc in last_week_metrics:
|
||||
total_docs += 1
|
||||
try:
|
||||
metrics = MetricsClass(**doc)
|
||||
if metrics.is_malformed():
|
||||
malformed_metrics.append(doc['_id'])
|
||||
except ValidationError:
|
||||
invalid_metrics.append(doc['_id'])
|
||||
|
||||
print(metrics_overview)
|
||||
print(metrics_detailed)
|
||||
metrics_detailed = (f"METRICS DETAILED ({source}):\n"
|
||||
f"malformed_metrics_last_week: {malformed_metrics}\n"
|
||||
f"invalid_metrics_last_week: {invalid_metrics}\n"
|
||||
f"total_docs_last_week: {total_docs}\n")
|
||||
metrics_overview = (
|
||||
f"METRICS OVERVIEW ({source}):\n"
|
||||
f"malformed_metrics_last_week: {len(malformed_metrics)} ({len(malformed_metrics)/total_docs*100:.2f}%)\n"
|
||||
f"invalid_metrics_last_week: {len(invalid_metrics)} ({len(invalid_metrics)/total_docs*100:.2f}%)\n"
|
||||
f"total_docs_last_week: {total_docs}\n")
|
||||
|
||||
# Publish metrics to SDP Slack Channel
|
||||
evg_api = RetryingEvergreenApi.get_api(config_file="./.evergreen.yml")
|
||||
evg_api.send_slack_message(target="#server-sdp-bfs", msg=metrics_overview)
|
||||
print(metrics_overview)
|
||||
print(metrics_detailed)
|
||||
|
||||
except Exception as exc:
|
||||
print("Unexpected failure while getting metrics")
|
||||
raise exc
|
||||
return metrics_overview
|
||||
|
||||
except Exception as exc:
|
||||
print("Unexpected failure while getting metrics")
|
||||
raise exc
|
||||
|
||||
|
||||
scons_metrics_overview = get_metrics_data("scons", SConsToolingMetrics)
|
||||
resmoke_metrics_overview = get_metrics_data("resmoke", ResmokeToolingMetrics)
|
||||
|
||||
# Publish metrics to SDP Slack Channel
|
||||
evg_api = RetryingEvergreenApi.get_api(config_file="./.evergreen.yml")
|
||||
evg_api.send_slack_message(
|
||||
target="#server-sdp-bfs",
|
||||
msg=scons_metrics_overview + resmoke_metrics_overview,
|
||||
)
|
||||
|
||||
@ -9,7 +9,6 @@ set -o errexit
|
||||
activate_venv
|
||||
$python buildscripts/resmoke_tests_runtime_validate.py \
|
||||
--resmoke-report-file ./report.json \
|
||||
--evg-api-config ./.evergreen.yml \
|
||||
--project-id ${project_id} \
|
||||
--build-variant ${build_variant} \
|
||||
--task-name ${task_name}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
// Regression test for SERVER-71270.
|
||||
(function() {
|
||||
"use strict";
|
||||
load('jstests/aggregation/extras/utils.js'); // For assertArrayEq.
|
||||
const doc = {
|
||||
_id: 0,
|
||||
time: new Date('2019-01-18T13:24:15.443Z'),
|
||||
tag: {},
|
||||
};
|
||||
|
||||
db.ts.drop();
|
||||
db.coll.drop();
|
||||
|
||||
db.createCollection('ts', {timeseries: {timeField: 'time', metaField: 'tag'}});
|
||||
db.createCollection('coll');
|
||||
|
||||
db.ts.insertOne(doc);
|
||||
db.coll.insertOne(doc);
|
||||
const pipeline = [
|
||||
{$project: {'time': 0}},
|
||||
{$match: {'time': {$lte: new Date('2019-02-13T11:36:03.481Z')}}},
|
||||
];
|
||||
|
||||
const ts = db.ts.aggregate(pipeline).toArray();
|
||||
const vanilla = db.coll.aggregate(pipeline).toArray();
|
||||
assertArrayEq({
|
||||
actual: ts,
|
||||
expected: vanilla,
|
||||
});
|
||||
}());
|
||||
77
jstests/aggregation/bugs/window_inside_facet.js
Normal file
77
jstests/aggregation/bugs/window_inside_facet.js
Normal file
@ -0,0 +1,77 @@
|
||||
// Test that $setWindowFields inside $facet correctly propagates its state when it encounters paused
|
||||
// execution.
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
const coll = db.window_inside_facet;
|
||||
coll.drop();
|
||||
|
||||
assert.commandWorked(coll.insert([
|
||||
{_id: 'a', n: 0},
|
||||
{_id: 'b', n: 1},
|
||||
{_id: 'c', n: 2},
|
||||
{_id: 'd', n: 3},
|
||||
{_id: 'e', n: 4},
|
||||
{_id: 'f', n: 5}
|
||||
]));
|
||||
|
||||
// Test window with a sort within $facet alongside another pipeline that will cause it to pause
|
||||
// execution. The sort will cause the window to hit paused execution immediately, before an advance.
|
||||
let result =
|
||||
coll.aggregate({
|
||||
$facet: {
|
||||
facet1: [{
|
||||
$setWindowFields: {
|
||||
output: {prevId: {$shift: {by: -1, default: null, output: "$_id"}}},
|
||||
sortBy: {_id: 1}
|
||||
}
|
||||
}],
|
||||
facet2: [{$count: "count"}]
|
||||
}
|
||||
})
|
||||
.toArray()[0];
|
||||
let expected = {
|
||||
facet1: [
|
||||
{_id: 'a', n: 0, prevId: null},
|
||||
{_id: 'b', n: 1, prevId: 'a'},
|
||||
{_id: 'c', n: 2, prevId: 'b'},
|
||||
{_id: 'd', n: 3, prevId: 'c'},
|
||||
{_id: 'e', n: 4, prevId: 'd'},
|
||||
{_id: 'f', n: 5, prevId: 'e'}
|
||||
],
|
||||
facet2: [{count: 6}]
|
||||
};
|
||||
assert.docEq(expected, result, "$setWindowFields with sort failed.");
|
||||
|
||||
// Test window with no sort within $facet alongside another pipeline that will cause it to pause
|
||||
// execution. Having no sort will cause the window to hit paused execution after advancing.
|
||||
result = coll.aggregate({
|
||||
$facet: {
|
||||
facet1: [
|
||||
{
|
||||
$setWindowFields: {
|
||||
output: {
|
||||
min: {$min: "$n"},
|
||||
max: {$max: "$n"},
|
||||
}
|
||||
}
|
||||
},
|
||||
{$sort: {_id: 1}}
|
||||
],
|
||||
facet2: [{$count: "count"}]
|
||||
}
|
||||
})
|
||||
.toArray()[0];
|
||||
expected = {
|
||||
facet1: [
|
||||
{_id: 'a', n: 0, min: 0, max: 5},
|
||||
{_id: 'b', n: 1, min: 0, max: 5},
|
||||
{_id: 'c', n: 2, min: 0, max: 5},
|
||||
{_id: 'd', n: 3, min: 0, max: 5},
|
||||
{_id: 'e', n: 4, min: 0, max: 5},
|
||||
{_id: 'f', n: 5, min: 0, max: 5}
|
||||
],
|
||||
facet2: [{count: 6}]
|
||||
};
|
||||
assert.docEq(expected, result, "$setWindowFields without sort failed.");
|
||||
}());
|
||||
@ -5893,6 +5893,26 @@ var authCommandsLib = {
|
||||
{runOnDb: secondDbName, roles: {}}
|
||||
]
|
||||
},
|
||||
{
|
||||
testname: "setProfilingFilterGlobally",
|
||||
command: {setProfilingFilterGlobally: 1, filter: {nreturned: 0}},
|
||||
testcases: [
|
||||
{
|
||||
runOnDb: firstDbName,
|
||||
roles: roles_dbAdminAny,
|
||||
privileges: [{resource: {db: "", collection: ""}, actions: ["enableProfiler"]}],
|
||||
expectFail:
|
||||
true /* the command will fail because the query knob is not turned on */
|
||||
},
|
||||
{
|
||||
runOnDb: firstDbName,
|
||||
privileges: [
|
||||
{resource: {db: firstDbName, collection: ""}, actions: ["enableProfiler"]}
|
||||
],
|
||||
expectAuthzFailure: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
testname: "setParameter",
|
||||
command: {setParameter: 1, quiet: 1},
|
||||
|
||||
@ -34,7 +34,6 @@ load("jstests/libs/doc_validation_utils.js");
|
||||
|
||||
const dbName = 'bypass_document_validation';
|
||||
const collName = 'bypass_document_validation';
|
||||
const outputCollName = 'bypass_output_coll';
|
||||
const myDb = db.getSiblingDB(dbName);
|
||||
const coll = myDb[collName];
|
||||
|
||||
@ -65,6 +64,7 @@ function runBypassDocumentValidationTest(validator) {
|
||||
}
|
||||
|
||||
// Test the aggregation command with a $out stage.
|
||||
const outputCollName = 'bypass_output_coll';
|
||||
const outputColl = myDb[outputCollName];
|
||||
outputColl.drop();
|
||||
assert.commandWorked(myDb.createCollection(outputCollName, {validator: validator}));
|
||||
@ -199,8 +199,4 @@ runBypassDocumentValidationTest({a: {$exists: true}});
|
||||
|
||||
// Run the test again with an equivalent JSON Schema validator.
|
||||
runBypassDocumentValidationTest({$jsonSchema: {required: ['a']}});
|
||||
|
||||
// Set the validationAction to "warn" to avoid failing collection validation.
|
||||
assert.commandWorked(myDb.runCommand({collMod: collName, validationAction: "warn"}));
|
||||
assert.commandWorked(myDb.runCommand({collMod: outputCollName, validationAction: "warn"}));
|
||||
})();
|
||||
|
||||
@ -24,7 +24,6 @@ load("jstests/libs/index_catalog_helpers.js");
|
||||
load("jstests/concurrency/fsm_workload_helpers/server_types.js");
|
||||
// For isReplSet
|
||||
load("jstests/libs/fixture_helpers.js");
|
||||
load("jstests/libs/sbe_explain_helpers.js"); // For engineSpecificAssertion.
|
||||
// For areAllCollectionsClustered.
|
||||
load("jstests/libs/clustered_collections/clustered_collection_util.js");
|
||||
|
||||
@ -755,9 +754,7 @@ if (!isClustered) {
|
||||
assert.commandWorked(testDb.createCollection(coll.getName(), {collation: {locale: "en_US"}}));
|
||||
explainRes = coll.explain("executionStats").find({_id: "foo"}).finish();
|
||||
assert.commandWorked(explainRes);
|
||||
let classicAssert = null !== getPlanStage(getWinningPlan(explainRes.queryPlanner), "IDHACK");
|
||||
let sbeAssert = null !== getPlanStage(getWinningPlan(explainRes.queryPlanner), "IXSCAN");
|
||||
engineSpecificAssertion(classicAssert, sbeAssert, testDb, explainRes);
|
||||
assert.neq(null, getPlanStage(explainRes.executionStats.executionStages, "IDHACK"), explainRes);
|
||||
|
||||
// Find on _id should use idhack stage when explicitly given query collation matches
|
||||
// collection default.
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
* @tags: [
|
||||
* assumes_unsharded_collection,
|
||||
* requires_non_retryable_writes,
|
||||
* requires_fcv_60,
|
||||
* requires_fcv_62,
|
||||
* # This test could produce unexpected explain output if additional indexes are created.
|
||||
* assumes_no_implicit_index_creation,
|
||||
* # TODO SERVER-67506: Dotted path equality to null matches non-object array elements in CQF.
|
||||
|
||||
@ -15,48 +15,41 @@ const encryptedBinDataElement = BinData(6, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
const nonEncryptedBinDataElement = BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA");
|
||||
|
||||
// Only elements of type BinData with subtype '6' should match.
|
||||
assertSchemaMatch(coll, {properties: {bin: {encrypt: {}}}}, {bin: encryptedBinDataElement}, true);
|
||||
assertSchemaMatch(coll, {properties: {bin: {encrypt: {}}}}, {bin: {}}, false);
|
||||
assertSchemaMatch(
|
||||
coll, {properties: {bin: {encrypt: {}}}}, {bin: encryptedBinDataElement}, true, true);
|
||||
assertSchemaMatch(coll, {properties: {bin: {encrypt: {}}}}, {bin: {}}, false, true);
|
||||
assertSchemaMatch(
|
||||
coll, {properties: {bin: {encrypt: {}}}}, {bin: nonEncryptedBinDataElement}, false, true);
|
||||
coll, {properties: {bin: {encrypt: {}}}}, {bin: nonEncryptedBinDataElement}, false);
|
||||
// Nested in object.
|
||||
assertSchemaMatch(coll,
|
||||
{properties: {obj: {type: 'object', properties: {a: {encrypt: {}}}}}},
|
||||
{obj: {a: encryptedBinDataElement}},
|
||||
true,
|
||||
true);
|
||||
assertSchemaMatch(coll,
|
||||
{properties: {obj: {type: 'object', properties: {a: {encrypt: {}}}}}},
|
||||
{obj: {a: {}}},
|
||||
false,
|
||||
true);
|
||||
false);
|
||||
assertSchemaMatch(coll,
|
||||
{properties: {obj: {type: 'object', properties: {a: {encrypt: {}}}}}},
|
||||
{obj: {a: nonEncryptedBinDataElement}},
|
||||
false,
|
||||
true);
|
||||
false);
|
||||
|
||||
// Nested in array.
|
||||
assertSchemaMatch(coll,
|
||||
{properties: {arr: {type: 'array', items: {encrypt: {}}}}},
|
||||
{arr: [encryptedBinDataElement, encryptedBinDataElement]},
|
||||
true,
|
||||
true);
|
||||
assertSchemaMatch(
|
||||
coll, {properties: {arr: {type: 'array', items: {encrypt: {}}}}}, {arr: [{}, {}]}, false, true);
|
||||
coll, {properties: {arr: {type: 'array', items: {encrypt: {}}}}}, {arr: [{}, {}]}, false);
|
||||
assertSchemaMatch(coll,
|
||||
{properties: {arr: {type: 'array', items: {encrypt: {}}}}},
|
||||
{arr: [encryptedBinDataElement, nonEncryptedBinDataElement]},
|
||||
false,
|
||||
true);
|
||||
false);
|
||||
|
||||
// If array is not specified, should not traverse array of encrypted BinData's.
|
||||
assertSchemaMatch(coll,
|
||||
{properties: {bin: {encrypt: {}}}},
|
||||
{bin: [encryptedBinDataElement, encryptedBinDataElement]},
|
||||
false,
|
||||
true);
|
||||
false);
|
||||
|
||||
// Encrypt alongside type/bsontype should fail to parse.
|
||||
assert.commandFailedWithCode(
|
||||
|
||||
@ -25,19 +25,25 @@ function compareValues(v1, v2) {
|
||||
}
|
||||
|
||||
// Check that 'expectedQuery' and 'actualQuery' have the same plans, and produce the same result.
|
||||
function assertEquivPlanAndResult(expectedQuery, actualQuery) {
|
||||
function assertEquivPlanAndResult(expectedQuery, actualQuery, supportWithCollation) {
|
||||
const expectedExplain = coll.find(expectedQuery).explain("queryPlanner");
|
||||
const actualExplain = coll.find(actualQuery).explain("queryPlanner");
|
||||
|
||||
// The queries must be rewritten into the same form.
|
||||
assert.docEq(expectedExplain.parsedQuery, actualExplain.parsedQuery);
|
||||
assert.docEq(expectedExplain.queryPlanner.parsedQuery, actualExplain.queryPlanner.parsedQuery);
|
||||
|
||||
// Check if the test queries produce the same plans with collations
|
||||
const expectedExplainColln =
|
||||
// We are always running these queries to ensure a server crash is not triggered.
|
||||
// TODO SERVER-72450: Add appropriate assertions for the output.
|
||||
const expectedExplainCollation =
|
||||
coll.find(expectedQuery).sort({f1: 1}).collation({locale: 'en_US'}).explain("queryPlanner");
|
||||
const actualExplainColln =
|
||||
const actualExplainCollation =
|
||||
coll.find(actualQuery).sort({f1: 1}).collation({locale: 'en_US'}).explain("queryPlanner");
|
||||
assert.docEq(expectedExplainColln.parsedQuery, actualExplainColln.parsedQuery);
|
||||
|
||||
if (supportWithCollation) {
|
||||
// Check if the test queries produce the same plans with collations.
|
||||
assert.docEq(expectedExplainCollation.queryPlanner.parsedQuery,
|
||||
actualExplainCollation.queryPlanner.parsedQuery);
|
||||
}
|
||||
|
||||
// Make sure both queries have the same access plan.
|
||||
const expectedPlan = getWinningPlan(expectedExplain.queryPlanner);
|
||||
@ -51,12 +57,13 @@ function assertEquivPlanAndResult(expectedQuery, actualQuery) {
|
||||
const actualRes = coll.find(actualQuery).toArray();
|
||||
assert(arrayEq(expectedRes, actualRes, false, compareValues),
|
||||
`expected=${expectedRes}, actual=${actualRes}`);
|
||||
|
||||
// also with collation
|
||||
const expectedResColln =
|
||||
const expectedResCollation =
|
||||
coll.find(expectedQuery).sort({f1: 1}).collation({locale: 'en_US'}).toArray();
|
||||
const actualResColln =
|
||||
const actualResCollation =
|
||||
coll.find(actualQuery).sort({f1: 1}).collation({locale: 'en_US'}).toArray();
|
||||
assert(arrayEq(expectedResColln, actualResColln, false, compareValues),
|
||||
assert(arrayEq(expectedResCollation, actualResCollation, false, compareValues),
|
||||
`expected=${expectedRes}, actual=${actualRes}`);
|
||||
}
|
||||
|
||||
@ -94,32 +101,71 @@ assert.commandWorked(coll.insert(data));
|
||||
|
||||
// Pairs of queries where the first one is expressed via OR (which is supposed to be
|
||||
// rewritten as IN), and the second one is an equivalent query using IN.
|
||||
//
|
||||
// The third element of the array is optional, if present, implies that the rewrite is not
|
||||
// supported when there is a collation involved.
|
||||
//
|
||||
// TODO SERVER-72450: Remove or update this logic related to collation, and enforce stronger
|
||||
// assertions.
|
||||
const positiveTestQueries = [
|
||||
[{$or: [{f1: 5}, {f1: 3}, {f1: 7}]}, {f1: {$in: [7, 3, 5]}}],
|
||||
[{$or: [{f1: {$eq: 5}}, {f1: {$eq: 3}}, {f1: {$eq: 7}}]}, {f1: {$in: [7, 3, 5]}}],
|
||||
[{$or: [{f1: 42}, {f1: NaN}, {f1: 99}]}, {f1: {$in: [42, NaN, 99]}}],
|
||||
[{$or: [{f1: /^x/}, {f1: "ab"}]}, {f1: {$in: [/^x/, "ab"]}}],
|
||||
[{$or: [{f1: /^x/}, {f1: "^a"}]}, {f1: {$in: [/^x/, "^a"]}}],
|
||||
[{$or: [{f1: 42}, {f1: null}, {f1: 99}]}, {f1: {$in: [42, 99, null]}}],
|
||||
[{$or: [{f1: 1}, {f2: 9}, {f1: 99}]}, {$or: [{f2: 9}, {f1: {$in: [1, 99]}}]}],
|
||||
[{$or: [{f1: {$regex: /^x/}}, {f1: {$regex: /ab/}}]}, {f1: {$in: [/^x/, /ab/]}}],
|
||||
[
|
||||
{$and: [{$or: [{f1: 7}, {f1: 3}, {f1: 5}]}, {$or: [{f1: 1}, {f1: 2}, {f1: 3}]}]},
|
||||
{$and: [{f1: {$in: [7, 3, 5]}}, {f1: {$in: [1, 2, 3]}}]}
|
||||
],
|
||||
[
|
||||
{$or: [{$or: [{f1: 7}, {f1: 3}, {f1: 5}]}, {$or: [{f1: 1}, {f1: 2}, {f1: 3}]}]},
|
||||
{$or: [{f1: {$in: [7, 3, 5]}}, {f1: {$in: [1, 2, 3]}}]}
|
||||
],
|
||||
[
|
||||
{$or: [{$and: [{f1: 7}, {f2: 7}, {f1: 5}]}, {$or: [{f1: 1}, {f1: 2}, {f1: 3}]}]},
|
||||
{$or: [{$and: [{f1: 7}, {f2: 7}, {f1: 5}]}, {f1: {$in: [1, 2, 3]}}]},
|
||||
],
|
||||
[{$or: [{f2: [32, 52]}, {f2: [42, [13, 11]]}]}, {f2: {$in: [[32, 52], [42, [13, 11]]]}}],
|
||||
[{$or: [{f2: 52}, {f2: 13}]}, {f2: {$in: [52, 13]}}],
|
||||
[{$or: [{f2: [11]}, {f2: [23]}]}, {f2: {$in: [[11], [23]]}}],
|
||||
[{$or: [{f1: 42}, {f1: null}]}, {f1: {$in: [42, null]}}],
|
||||
[{$or: [{f1: "a"}, {f1: "b"}, {f1: /c/}]}, {f1: {$in: ["a", "b", /c/]}}],
|
||||
{actualQuery: {$or: [{f1: 5}, {f1: 3}, {f1: 7}]}, expectedQuery: {f1: {$in: [7, 3, 5]}}},
|
||||
{
|
||||
actualQuery: {$or: [{f1: {$eq: 5}}, {f1: {$eq: 3}}, {f1: {$eq: 7}}]},
|
||||
expectedQuery: {f1: {$in: [7, 3, 5]}}
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f1: 42}, {f1: NaN}, {f1: 99}]},
|
||||
expectedQuery: {f1: {$in: [42, NaN, 99]}}
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f1: /^x/}, {f1: "ab"}]},
|
||||
expectedQuery: {f1: {$in: [/^x/, "ab"]}},
|
||||
cannotRewriteWithCollation: true
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f1: /^x/}, {f1: "^a"}]},
|
||||
expectedQuery: {f1: {$in: [/^x/, "^a"]}},
|
||||
cannotRewriteWithCollation: true
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f1: 42}, {f1: null}, {f1: 99}]},
|
||||
expectedQuery: {f1: {$in: [42, 99, null]}}
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f1: 1}, {f2: 9}, {f1: 99}]},
|
||||
expectedQuery: {$or: [{f2: 9}, {f1: {$in: [1, 99]}}]}
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f1: {$regex: /^x/}}, {f1: {$regex: /ab/}}]},
|
||||
expectedQuery: {f1: {$in: [/^x/, /ab/]}}
|
||||
},
|
||||
{
|
||||
actualQuery:
|
||||
{$and: [{$or: [{f1: 7}, {f1: 3}, {f1: 5}]}, {$or: [{f1: 1}, {f1: 2}, {f1: 3}]}]},
|
||||
expectedQuery: {$and: [{f1: {$in: [7, 3, 5]}}, {f1: {$in: [1, 2, 3]}}]}
|
||||
},
|
||||
{
|
||||
actualQuery:
|
||||
{$or: [{$or: [{f1: 7}, {f1: 3}, {f1: 5}]}, {$or: [{f1: 1}, {f1: 2}, {f1: 3}]}]},
|
||||
expectedQuery: {$or: [{f1: {$in: [7, 3, 5]}}, {f1: {$in: [1, 2, 3]}}]}
|
||||
},
|
||||
{
|
||||
actualQuery:
|
||||
{$or: [{$and: [{f1: 7}, {f2: 7}, {f1: 5}]}, {$or: [{f1: 1}, {f1: 2}, {f1: 3}]}]},
|
||||
expectedQuery: {$or: [{$and: [{f1: 7}, {f2: 7}, {f1: 5}]}, {f1: {$in: [1, 2, 3]}}]},
|
||||
},
|
||||
{
|
||||
actualQuery: {$or: [{f2: [32, 52]}, {f2: [42, [13, 11]]}]},
|
||||
expectedQuery: {f2: {$in: [[32, 52], [42, [13, 11]]]}}
|
||||
},
|
||||
{actualQuery: {$or: [{f2: 52}, {f2: 13}]}, expectedQuery: {f2: {$in: [52, 13]}}},
|
||||
{actualQuery: {$or: [{f2: [11]}, {f2: [23]}]}, expectedQuery: {f2: {$in: [[11], [23]]}}},
|
||||
{actualQuery: {$or: [{f1: 42}, {f1: null}]}, expectedQuery: {f1: {$in: [42, null]}}},
|
||||
{
|
||||
actualQuery: {$or: [{f1: "a"}, {f1: "b"}, {f1: /c/}]},
|
||||
expectedQuery: {f1: {$in: ["a", "b", /c/]}},
|
||||
cannotRewriteWithCollation: true
|
||||
},
|
||||
];
|
||||
|
||||
// These $or queries should not be rewritten into $in because of different semantics.
|
||||
@ -133,22 +179,27 @@ for (const query of negativeTestQueries) {
|
||||
assertOrNotRewrittenToIn(query);
|
||||
}
|
||||
|
||||
function testOrToIn(queries) {
|
||||
function testOrToIn(queries, usesCollation) {
|
||||
for (const queryPair of queries) {
|
||||
assertEquivPlanAndResult(queryPair[0], queryPair[1]);
|
||||
if (usesCollation && queryPair.cannotRewriteWithCollation) {
|
||||
continue;
|
||||
}
|
||||
assertEquivPlanAndResult(
|
||||
queryPair.actualQuery, queryPair.expectedQuery, !queryPair.cannotRewriteWithCollation);
|
||||
}
|
||||
}
|
||||
|
||||
testOrToIn(positiveTestQueries); // test without indexes
|
||||
testOrToIn(positiveTestQueries, false /* usesCollation */); // test without indexes
|
||||
|
||||
assert.commandWorked(coll.createIndex({f1: 1}));
|
||||
|
||||
testOrToIn(positiveTestQueries); // single index
|
||||
testOrToIn(positiveTestQueries, false /* usesCollation */); // single index
|
||||
|
||||
assert.commandWorked(coll.createIndex({f2: 1}));
|
||||
assert.commandWorked(coll.createIndex({f1: 1, f2: 1}));
|
||||
|
||||
testOrToIn(positiveTestQueries); // three indexes, requires multiplanning
|
||||
testOrToIn(positiveTestQueries,
|
||||
false /* usesCollation */); // three indexes, requires multiplanning
|
||||
|
||||
// Test with a collection that has a collation, and that collation is the same as the query
|
||||
// collation
|
||||
@ -156,12 +207,12 @@ coll.drop();
|
||||
assert.commandWorked(db.createCollection("orToIn", {collation: {locale: 'en_US'}}));
|
||||
coll = db.orToIn;
|
||||
assert.commandWorked(coll.insert(data));
|
||||
testOrToIn(positiveTestQueries);
|
||||
testOrToIn(positiveTestQueries, true /* usesCollation */);
|
||||
// Test with a collection that has a collation, and that collation is different from the query
|
||||
// collation
|
||||
coll.drop();
|
||||
assert.commandWorked(db.createCollection("orToIn", {collation: {locale: 'de'}}));
|
||||
coll = db.orToIn;
|
||||
assert.commandWorked(coll.insert(data));
|
||||
testOrToIn(positiveTestQueries);
|
||||
testOrToIn(positiveTestQueries, true /* usesCollation */);
|
||||
}());
|
||||
|
||||
183
jstests/core/project_with_collation.js
Normal file
183
jstests/core/project_with_collation.js
Normal file
@ -0,0 +1,183 @@
|
||||
// Tests to verify the behavior of find command's project in the presence of collation.
|
||||
//
|
||||
// @tags: [
|
||||
// assumes_no_implicit_collection_creation_after_drop,
|
||||
// ]
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const collation = {
|
||||
locale: "en_US",
|
||||
strength: 2
|
||||
};
|
||||
const withCollationCollName = jsTestName() + "_collation";
|
||||
const noCollationCollName = jsTestName() + "_noCollation";
|
||||
|
||||
function setupCollection(withCollation) {
|
||||
const insertCollName = withCollation ? withCollationCollName : noCollationCollName;
|
||||
db[insertCollName].drop();
|
||||
if (withCollation) {
|
||||
assert.commandWorked(db.createCollection(insertCollName, {collation: withCollation}));
|
||||
}
|
||||
|
||||
const insertColl = db[insertCollName];
|
||||
assert.commandWorked(insertColl.insert(
|
||||
{_id: 0, str: "a", array: [{str: "b"}, {str: "A"}, {str: "B"}, {str: "a"}]}));
|
||||
assert.commandWorked(insertColl.insert({_id: 1, str: "a", elemMatch: [{str: "A"}, "ignored"]}));
|
||||
assert.commandWorked(insertColl.insert({_id: 2, str: "A", elemMatch: ["ignored", {str: "a"}]}));
|
||||
assert.commandWorked(insertColl.insert({_id: 3, str: "B"}));
|
||||
|
||||
return insertColl;
|
||||
}
|
||||
|
||||
function runQueryWithCollation(testColl, collationToUse) {
|
||||
let findCmd =
|
||||
testColl.find({str: 'A', elemMatch: {$elemMatch: {str: "a"}}}, {_id: 1, 'elemMatch.$': 1});
|
||||
if (collationToUse) {
|
||||
findCmd = findCmd.collation(collationToUse);
|
||||
}
|
||||
const elemMatchOutput = findCmd.toArray();
|
||||
assert.sameMembers(elemMatchOutput,
|
||||
[{_id: 1, elemMatch: [{str: "A"}]}, {_id: 2, elemMatch: [{str: "a"}]}]);
|
||||
|
||||
findCmd =
|
||||
testColl.find({str: 'A'}, {sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}});
|
||||
if (collationToUse) {
|
||||
findCmd = findCmd.collation(collationToUse);
|
||||
}
|
||||
const sortArrayOutput = findCmd.toArray();
|
||||
assert.sameMembers(sortArrayOutput, [
|
||||
{_id: 0, sortedArray: [{str: "A"}, {str: "a"}, {str: "b"}, {str: "B"}]},
|
||||
{_id: 1, sortedArray: null},
|
||||
{_id: 2, sortedArray: null}
|
||||
]);
|
||||
|
||||
const findAndUpdateOutput = testColl.findAndModify({
|
||||
query: {_id: 1, str: 'A', elemMatch: {$elemMatch: {str: "a"}}},
|
||||
fields: {_id: 1, 'elemMatch.$': 1, updated: 1},
|
||||
update: {$set: {updated: true}},
|
||||
collation: collationToUse
|
||||
});
|
||||
assert.docEq(findAndUpdateOutput, {_id: 1, elemMatch: [{str: "A"}]});
|
||||
|
||||
const findAndUpdateWithSortArrayOutput = testColl.findAndModify({
|
||||
query: {_id: 0, str: 'A'},
|
||||
fields: {_id: 1, str: 1, sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}},
|
||||
update: {$set: {updated: true}},
|
||||
collation: collationToUse,
|
||||
new: true
|
||||
});
|
||||
assert.docEq(findAndUpdateWithSortArrayOutput,
|
||||
{_id: 0, str: "a", sortedArray: [{str: "A"}, {str: "a"}, {str: "b"}, {str: "B"}]});
|
||||
|
||||
const findAndRemoveOutput = testColl.findAndModify({
|
||||
query: {_id: 1, str: 'A', elemMatch: {$elemMatch: {str: "a"}}},
|
||||
fields: {_id: 1, 'elemMatch.$': 1, updated: 1},
|
||||
remove: true,
|
||||
collation: collationToUse,
|
||||
});
|
||||
assert.docEq(findAndRemoveOutput, {_id: 1, elemMatch: [{str: "A"}], updated: true});
|
||||
|
||||
const findAndRemoveWithSortArrayOutput = testColl.findAndModify({
|
||||
query: {_id: 0, str: 'A'},
|
||||
fields: {_id: 1, str: 1, sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}},
|
||||
remove: true,
|
||||
collation: collationToUse
|
||||
});
|
||||
assert.docEq(findAndRemoveWithSortArrayOutput,
|
||||
{_id: 0, str: "a", sortedArray: [{str: "A"}, {str: "a"}, {str: "b"}, {str: "B"}]});
|
||||
}
|
||||
|
||||
// The output for the below two tests should not depend on the collection level collation.
|
||||
let collWithCollation = setupCollection({locale: "en_US"});
|
||||
runQueryWithCollation(collWithCollation, collation);
|
||||
|
||||
let noCollationColl = setupCollection(false);
|
||||
runQueryWithCollation(noCollationColl, collation);
|
||||
|
||||
// Tests to verify that the projection code inherits collection level collation in the absence of
|
||||
// query level collation.
|
||||
collWithCollation = setupCollection(collation);
|
||||
runQueryWithCollation(collWithCollation, null);
|
||||
|
||||
// The output of this should not depend on the collection level collation and simple collation
|
||||
// should be applied always.
|
||||
function queryWithSimpleCollation(testColl) {
|
||||
const elemMatchOutput =
|
||||
testColl.find({str: 'A', elemMatch: {$elemMatch: {str: "a"}}}, {_id: 1, 'elemMatch.$': 1})
|
||||
.collation({locale: "simple"})
|
||||
.toArray();
|
||||
assert.sameMembers(elemMatchOutput, [{_id: 2, elemMatch: [{str: "a"}]}]);
|
||||
|
||||
const sortArrayOutput =
|
||||
testColl.find({str: 'a'}, {sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}})
|
||||
.collation({locale: "simple"})
|
||||
.toArray();
|
||||
assert.sameMembers(sortArrayOutput, [
|
||||
{_id: 0, sortedArray: [{str: "A"}, {str: "B"}, {str: "a"}, {str: "b"}]},
|
||||
{_id: 1, sortedArray: null}
|
||||
]);
|
||||
|
||||
// Test findAndModify command with 'update'. Ensure that simple collation is always honored.
|
||||
const findAndUpdateOutput = testColl.findAndModify({
|
||||
query: {str: 'A', elemMatch: {$elemMatch: {str: "a"}}},
|
||||
fields: {_id: 1, 'elemMatch.$': 1, updated: 1},
|
||||
update: {$set: {updated: true}},
|
||||
collation: {locale: "simple"}
|
||||
});
|
||||
assert.docEq(findAndUpdateOutput, {_id: 2, elemMatch: [{str: "a"}]});
|
||||
|
||||
const findAndUpdateWithSortArrayOutput = testColl.findAndModify({
|
||||
query: {_id: 0, str: 'a'},
|
||||
fields: {_id: 1, str: 1, sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}},
|
||||
update: {$set: {updated: true}},
|
||||
collation: {locale: "simple"},
|
||||
new: true
|
||||
});
|
||||
assert.docEq(findAndUpdateWithSortArrayOutput,
|
||||
{_id: 0, str: "a", sortedArray: [{str: "A"}, {str: "B"}, {str: "a"}, {str: "b"}]});
|
||||
|
||||
// Test findAndModify command with remove:true. Ensure that simple collation is always honored.
|
||||
const findAndRemoveOutput = testColl.findAndModify({
|
||||
query: {_id: 2, str: 'A', elemMatch: {$elemMatch: {str: "a"}}},
|
||||
fields: {_id: 1, 'elemMatch.$': 1, updated: 1},
|
||||
remove: true,
|
||||
collation: {locale: "simple"},
|
||||
});
|
||||
assert.docEq(findAndRemoveOutput, {_id: 2, elemMatch: [{str: "a"}], updated: true});
|
||||
|
||||
const findAndRemoveWithSortArrayOutput = testColl.findAndModify({
|
||||
query: {_id: 0, str: 'a'},
|
||||
fields: {_id: 1, str: 1, sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}},
|
||||
remove: true,
|
||||
collation: {locale: "simple"}
|
||||
});
|
||||
assert.docEq(findAndRemoveWithSortArrayOutput,
|
||||
{_id: 0, str: "a", sortedArray: [{str: "A"}, {str: "B"}, {str: "a"}, {str: "b"}]});
|
||||
}
|
||||
|
||||
noCollationColl = setupCollection(false);
|
||||
queryWithSimpleCollation(noCollationColl);
|
||||
|
||||
collWithCollation = setupCollection(collation);
|
||||
queryWithSimpleCollation(collWithCollation);
|
||||
|
||||
// Test with views.
|
||||
(function viewWithCollation() {
|
||||
collWithCollation = setupCollection(collation);
|
||||
db[jsTestName() + "_view"].drop();
|
||||
assert.commandWorked(
|
||||
db.createView(jsTestName() + "_view", withCollationCollName, [], {collation: collation}));
|
||||
const viewColl = db[jsTestName() + "_view"];
|
||||
|
||||
const sortArrayOutput =
|
||||
viewColl.find({str: 'A'}, {sortedArray: {$sortArray: {input: "$array", sortBy: {str: 1}}}})
|
||||
.toArray();
|
||||
assert.sameMembers(sortArrayOutput, [
|
||||
{_id: 0, sortedArray: [{str: "A"}, {str: "a"}, {str: "b"}, {str: "B"}]},
|
||||
{_id: 1, sortedArray: null},
|
||||
{_id: 2, sortedArray: null}
|
||||
]);
|
||||
})();
|
||||
})();
|
||||
@ -37,7 +37,18 @@ if (!isMongos(db)) {
|
||||
});
|
||||
}
|
||||
|
||||
const res = coll.aggregate([{$group: {_id: '$meta', accmin: {$min: '$b'}, accmax: {$max: '$c'}}}])
|
||||
.toArray();
|
||||
assert.docEq(res, [{"_id": null, "accmin": 1, "accmax": 3}]);
|
||||
let res = coll.aggregate([{$group: {_id: '$meta', accmin: {$min: '$b'}, accmax: {$max: '$c'}}}])
|
||||
.toArray();
|
||||
assert.docEq([{"_id": null, "accmin": 1, "accmax": 3}], res);
|
||||
|
||||
// Test SERVER-73822 fix: complex $min and $max (i.e. not just straight field refs) work correctly.
|
||||
res = coll.aggregate([{
|
||||
$group: {
|
||||
_id: '$meta',
|
||||
accmin: {$min: {$add: ["$b", "$c"]}},
|
||||
accmax: {$max: {$add: ["$b", "$c"]}}
|
||||
}
|
||||
}])
|
||||
.toArray();
|
||||
assert.docEq([{"_id": null, "accmin": 2, "accmax": 6}], res);
|
||||
})();
|
||||
|
||||
55
jstests/core/transaction_too_large_for_cache.js
Normal file
55
jstests/core/transaction_too_large_for_cache.js
Normal file
@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Tests that an operation requiring more cache than available fails instead of retrying infinitely.
|
||||
*
|
||||
* @tags: [
|
||||
* does_not_support_config_fuzzer,
|
||||
* requires_fcv_62,
|
||||
* requires_persistence,
|
||||
* requires_non_retryable_writes,
|
||||
* requires_wiredtiger,
|
||||
* ]
|
||||
*/
|
||||
|
||||
(function() {
|
||||
load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
|
||||
load("jstests/libs/storage_engine_utils.js");
|
||||
|
||||
// TODO (SERVER-39362): remove once parallel suite respects tags properly.
|
||||
if (!storageEngineIsWiredTiger()) {
|
||||
jsTestLog("Skipping test because storage engine is not WiredTiger.");
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = {
|
||||
x: []
|
||||
};
|
||||
for (var j = 0; j < 334000; j++) {
|
||||
doc.x.push("" + Math.random() + Math.random());
|
||||
}
|
||||
|
||||
const coll = db[jsTestName()];
|
||||
coll.drop();
|
||||
|
||||
// Maximum amount of indexes is 64. _id is implicit, and sharded collections also have an index on
|
||||
// the shard key.
|
||||
assert.commandWorked(coll.createIndex({x: "text"}));
|
||||
for (let i = 0; i < 61; i++) {
|
||||
assert.commandWorked(coll.createIndex({x: 1, ["field" + i]: 1}));
|
||||
}
|
||||
|
||||
// Retry the operation until we eventually hit the TransactionTooLargeForCache. Retry on
|
||||
// WriteConflict or TemporarilyUnavailable errors, as those are expected to be returned if the
|
||||
// threshold for TransactionTooLargeForCache is not reached, possibly due to concurrent operations.
|
||||
assert.soon(() => {
|
||||
let result;
|
||||
try {
|
||||
result = coll.insert(doc);
|
||||
assert.commandFailedWithCode(result, ErrorCodes.TransactionTooLargeForCache);
|
||||
return true;
|
||||
} catch (e) {
|
||||
assert.commandFailedWithCode(result,
|
||||
[ErrorCodes.WriteConflict, ErrorCodes.TemporarilyUnavailable]);
|
||||
return false;
|
||||
}
|
||||
}, "Expected operation to eventually fail with TransactionTooLargeForCache error.");
|
||||
}());
|
||||
85
jstests/core/txns/transaction_too_large_for_cache.js
Normal file
85
jstests/core/txns/transaction_too_large_for_cache.js
Normal file
@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Tests a multi-document transaction requiring more cache than available fails with the expected
|
||||
* error code instead of a generic WriteConflictException.
|
||||
*
|
||||
* @tags: [
|
||||
* does_not_support_config_fuzzer,
|
||||
* requires_fcv_62,
|
||||
* requires_persistence,
|
||||
* requires_non_retryable_writes,
|
||||
* requires_wiredtiger,
|
||||
* uses_transactions,
|
||||
* ]
|
||||
*/
|
||||
|
||||
(function() {
|
||||
load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
|
||||
load('jstests/libs/transactions_util.js');
|
||||
|
||||
function getWtCacheSizeBytes() {
|
||||
let serverStatus;
|
||||
if (FixtureHelpers.isReplSet(db) || FixtureHelpers.isMongos(db)) {
|
||||
serverStatus = FixtureHelpers.getPrimaries(db)[0].getDB('admin').serverStatus();
|
||||
} else {
|
||||
serverStatus = db.serverStatus();
|
||||
}
|
||||
|
||||
assert.commandWorked(serverStatus);
|
||||
return serverStatus.wiredTiger.cache["maximum bytes configured"];
|
||||
}
|
||||
|
||||
const doc1 = {
|
||||
x: []
|
||||
};
|
||||
for (var j = 0; j < 100000; j++) {
|
||||
doc1.x.push("" + Math.random() + Math.random());
|
||||
}
|
||||
|
||||
const session = db.getMongo().startSession();
|
||||
const sessionDb = session.getDatabase(db.getName());
|
||||
const coll = sessionDb[jsTestName()];
|
||||
coll.drop();
|
||||
|
||||
// Scale the load in proportion to WT cache size, to reduce test run time.
|
||||
// A single collection can only have up to 64 indexes. Cap at _id + 1 text index + 62 indexes.
|
||||
const nIndexes = Math.min(Math.ceil(getWtCacheSizeBytes() * 2 / (1024 * 1024 * 1024)), 62);
|
||||
assert.commandWorked(coll.createIndex({x: "text"}));
|
||||
for (let i = 0; i < nIndexes; i++) {
|
||||
assert.commandWorked(coll.createIndex({x: 1, ["field" + i]: 1}));
|
||||
}
|
||||
|
||||
// Retry the transaction until we eventually hit the TransactionTooLargeForCache. Only retry on
|
||||
// WriteConflict error, which is the only expected error besides TransactionTooLargeForCache.
|
||||
assert.soon(() => {
|
||||
session.startTransaction();
|
||||
|
||||
// Keep inserting documents in the transaction until we eventually hit the cache limit.
|
||||
let insertCount = 0;
|
||||
let result;
|
||||
try {
|
||||
while (true) {
|
||||
try {
|
||||
++insertCount;
|
||||
result = coll.insert(doc1);
|
||||
assert.commandWorked(result);
|
||||
} catch (e) {
|
||||
session.abortTransaction();
|
||||
assert.commandFailedWithCode(result, ErrorCodes.TransactionTooLargeForCache);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
assert.commandFailedWithCode(result, ErrorCodes.WriteConflict);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The error should not have a transient transaction error label. At this point the error must
|
||||
// have been TransactionTooLargeForCache. We do this check here to avoid having to check
|
||||
// exception types in the outermost catch, in case this assertion fires.
|
||||
assert(!TransactionsUtil.isTransientTransactionError(result), result);
|
||||
|
||||
jsTestLog("Iterations until TransactionTooLargeForCache occured: " + insertCount);
|
||||
|
||||
return true;
|
||||
}, "Expected a transaction to eventually fail with TransactionTooLargeForCache error.");
|
||||
}());
|
||||
@ -134,4 +134,8 @@ if (checkCascadesOptimizerEnabled(db)) {
|
||||
{filter: {a: {$gte: MaxKey()}}, expected: [docs[31]]},
|
||||
{filter: {a: {$gt: MaxKey()}}, expected: []});
|
||||
}
|
||||
|
||||
for (const testData of tests) {
|
||||
runTest(testData.filter, testData.expected);
|
||||
}
|
||||
}());
|
||||
|
||||
@ -113,8 +113,7 @@ function runTest(badViewDefinition) {
|
||||
}
|
||||
|
||||
assert.commandWorked(
|
||||
viewsDB.runCommand(
|
||||
{collMod: "collection", validator: {x: {$type: "string"}}, validationAction: "warn"}),
|
||||
viewsDB.runCommand({collMod: "collection", validator: {x: {$type: "string"}}}),
|
||||
makeErrorMessage("collMod"));
|
||||
|
||||
const renameCommand = {
|
||||
|
||||
@ -151,10 +151,6 @@ assert.commandWorked(viewsDB.runCommand({
|
||||
assert.commandWorked(
|
||||
viewsDB.runCommand({aggregate: "largeView", pipeline: [{$sort: {x: -1}}], cursor: {}}),
|
||||
"Expected aggregate to succeed since 'allowDiskUse' is true by default");
|
||||
|
||||
// Set the validationAction to "warn" to avoid failing collection validation.
|
||||
assert.commandWorked(
|
||||
viewsDB.runCommand({collMod: validatedCollName, validationAction: "warn"}));
|
||||
})();
|
||||
|
||||
// Test explain modes on a view.
|
||||
|
||||
@ -626,6 +626,7 @@ let viewsCommandTests = {
|
||||
setDefaultRWConcern: {skip: isUnrelated},
|
||||
setFeatureCompatibilityVersion: {skip: isUnrelated},
|
||||
setFreeMonitoring: {skip: isUnrelated},
|
||||
setProfilingFilterGlobally: {skip: isUnrelated},
|
||||
setParameter: {skip: isUnrelated},
|
||||
setShardVersion: {skip: isUnrelated},
|
||||
setChangeStreamState: {skip: isUnrelated},
|
||||
|
||||
@ -55,6 +55,18 @@ assertArrayEq({
|
||||
],
|
||||
});
|
||||
|
||||
assertArrayEq({
|
||||
actual: c.find({a: {$ne: null}}, {_id: 0}).toArray(),
|
||||
expected: [
|
||||
{a: 1},
|
||||
{a: 2},
|
||||
{a: ""},
|
||||
{a: [1, 2]},
|
||||
{a: [2, 3]},
|
||||
{a: [[1, 2]]},
|
||||
],
|
||||
});
|
||||
|
||||
c.drop();
|
||||
|
||||
assert.commandWorked(c.insertMany([
|
||||
|
||||
@ -45,28 +45,6 @@ const isIgnorableError = function ignorableError(codeName) {
|
||||
*/
|
||||
const validateCollectionsBackgroundThread = function validateCollectionsBackground(
|
||||
host, isIgnorableErrorFunc) {
|
||||
// Some tests explicitly mess with schema validation. We will skip running background validation
|
||||
// on those collections.
|
||||
const skippedCollections = new Set([
|
||||
"collectionWithValidator",
|
||||
"jstests_schema_encrypt",
|
||||
"json_schema_additional_items",
|
||||
"schema_allowed_properties",
|
||||
"jstests_schema_bsontype",
|
||||
"jstests_schema_dependencies",
|
||||
"jstests_schema_encrypt",
|
||||
"json_schema_items",
|
||||
"jstests_json_schema",
|
||||
"jstests_json_schema_logical",
|
||||
"json_schema_min_max_items",
|
||||
"jstests_schema_min_max_properties",
|
||||
"schema_pattern_properties",
|
||||
"jstests_schema_required",
|
||||
"json_schema_unique_items",
|
||||
"json_schema_test_corpus",
|
||||
"jstests_json_schema_ignore_unsupported",
|
||||
]);
|
||||
|
||||
// Calls 'func' with the print() function overridden to be a no-op.
|
||||
const quietly = (func) => {
|
||||
const printOriginal = print;
|
||||
@ -128,9 +106,6 @@ const validateCollectionsBackgroundThread = function validateCollectionsBackgrou
|
||||
});
|
||||
|
||||
for (let collectionName of collectionNames) {
|
||||
if (dbName == "bypass_document_validation" || skippedCollections.has(collectionName)) {
|
||||
continue;
|
||||
}
|
||||
let res = conn.getDB(dbName).getCollection(collectionName).runCommand({
|
||||
"validate": collectionName,
|
||||
background: true,
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
* Asserts that 'doc' matches 'schema' if and only if 'valid' is true. Drops 'coll' in the process,
|
||||
* so do not pass a collection whose contents you wish to preserve.
|
||||
*/
|
||||
function assertSchemaMatch(coll, schema, doc, valid, removeValidator = false) {
|
||||
function assertSchemaMatch(coll, schema, doc, valid) {
|
||||
const errmsg = "Document " + tojson(doc) +
|
||||
(valid ? " should have matched the schema " : " unexpectedly matched the schema ") +
|
||||
tojson(schema);
|
||||
@ -60,12 +60,4 @@ function assertSchemaMatch(coll, schema, doc, valid, removeValidator = false) {
|
||||
ErrorCodes.DocumentValidationFailure,
|
||||
errmsg + " during update document validation in strict mode");
|
||||
}
|
||||
|
||||
if (removeValidator) {
|
||||
// Remove the validator to avoid failing collection validation.
|
||||
assert.commandWorked(coll.runCommand("collMod", {validator: {}}));
|
||||
} else {
|
||||
// Set the validationAction to "warn" to avoid failing collection validation.
|
||||
assert.commandWorked(coll.runCommand("collMod", {validationAction: "warn"}));
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ Mongo.prototype.runCommand = function(dbName, cmdObj, options) {
|
||||
}
|
||||
|
||||
cmdObj.pipeline = [
|
||||
{$facet: {originalPipeline: originalPipeline}},
|
||||
{$facet: {originalPipeline: originalPipeline, extraPipeline: [{$count: "count"}]}},
|
||||
{$unwind: '$originalPipeline'},
|
||||
{$replaceRoot: {newRoot: '$originalPipeline'}},
|
||||
];
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Verifies that the server ignores collection option "recordPreImages" on binary upgrade from the
|
||||
* last LTS version to the current, as well as removes the option from collection attributes on
|
||||
* FCV upgrade.
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
load('jstests/multiVersion/libs/multi_rs.js');
|
||||
|
||||
const lastLTSVersion = "last-lts";
|
||||
const latestVersion = "latest";
|
||||
|
||||
// Setup a two-node replica set with last LTS binaries, so it is possible to create a collection
|
||||
// with "recordPreImages" option.
|
||||
const rst = new ReplSetTest(
|
||||
{name: jsTestName(), nodes: [{binVersion: lastLTSVersion}, {binVersion: lastLTSVersion}]});
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
const testDB = rst.getPrimary().getDB("test");
|
||||
const primaryNode = rst.getPrimary();
|
||||
const secondaryNode = rst.getSecondary();
|
||||
|
||||
// Create the collection.
|
||||
const collectionName = "coll";
|
||||
assert.commandWorked(testDB.createCollection(collectionName, {recordPreImages: true}));
|
||||
let coll = testDB[collectionName];
|
||||
|
||||
// Insert a test document which will be updated to trigger recording of change stream pre-images.
|
||||
assert.commandWorked(coll.insert({_id: 1, a: 1}));
|
||||
assert.commandWorked(coll.updateOne({_id: 1}, {$inc: {a: 1}}));
|
||||
rst.awaitReplication();
|
||||
|
||||
// Upgrade the binary of the secondary node to the current version to setup a mixed binary cluster.
|
||||
rst.upgradeMembers([secondaryNode], {binVersion: latestVersion});
|
||||
|
||||
// Make sure the primary node did not change.
|
||||
rst.stepUp(primaryNode);
|
||||
|
||||
// Verify that recording of change stream pre-images succeeds.
|
||||
assert.commandWorked(coll.updateOne({_id: 1}, {$inc: {a: 1}}));
|
||||
rst.awaitReplication();
|
||||
|
||||
// Finally upgrade the binary of the primary node to the current version.
|
||||
rst.upgradePrimary(rst.getPrimary(), {binVersion: latestVersion});
|
||||
|
||||
// Update a document on the collection with inactive "recordPreImages" collection option.
|
||||
coll = rst.getPrimary().getDB("test")[collectionName];
|
||||
assert.commandWorked(coll.updateOne({_id: 1}, {$inc: {a: 1}}));
|
||||
rst.awaitReplication();
|
||||
|
||||
// Upgrade the FCV to the latest to trigger removal of "recordPreImages" collection option from
|
||||
// persistent catalog entries.
|
||||
assert.commandWorked(rst.getPrimary().adminCommand({setFeatureCompatibilityVersion: latestFCV}));
|
||||
|
||||
// To check the collection options, downgrade FCV to later replace the binary of the server with
|
||||
// the last LTS binary version.
|
||||
assert.commandWorked(rst.getPrimary().adminCommand({setFeatureCompatibilityVersion: lastLTSFCV}));
|
||||
rst.upgradeSet({binVersion: lastLTSVersion});
|
||||
|
||||
// Verify that collection option "recordPreImages" was removed.
|
||||
const result =
|
||||
assert.commandWorked(rst.getPrimary().getDB("test").runCommand({listCollections: 1}));
|
||||
assert.eq(result.cursor.firstBatch[0].name, collectionName);
|
||||
assert.docEq(
|
||||
{},
|
||||
result.cursor.firstBatch[0].options,
|
||||
`Collection option "recordPreImages" was not removed. Got response: ${tojson(result)}`);
|
||||
rst.stopSet();
|
||||
})();
|
||||
@ -32,30 +32,30 @@ for (let i = 0; i < 5; i++) {
|
||||
|
||||
// The collection has not been checkpointed yet, so there is nothing to validate.
|
||||
let res = assert.commandWorked(db.runCommand({validate: collName, background: true}));
|
||||
assert.eq(true, res.valid);
|
||||
assert.eq(false, res.hasOwnProperty("nrecords"));
|
||||
assert.eq(false, res.hasOwnProperty("nIndexes"));
|
||||
assert.eq(true, res.valid, res);
|
||||
assert.eq(false, res.hasOwnProperty("nrecords"), res);
|
||||
assert.eq(false, res.hasOwnProperty("nIndexes"), res);
|
||||
|
||||
forceCheckpoint();
|
||||
|
||||
res = assert.commandWorked(db.runCommand({validate: collName, background: true}));
|
||||
assert.eq(true, res.valid);
|
||||
assert.eq(true, res.hasOwnProperty("nrecords"));
|
||||
assert.eq(true, res.hasOwnProperty("nIndexes"));
|
||||
assert.eq(true, res.valid, res);
|
||||
assert.eq(true, res.hasOwnProperty("nrecords"), res);
|
||||
assert.eq(true, res.hasOwnProperty("nIndexes"), res);
|
||||
|
||||
assert.commandWorked(coll.createIndex({x: 1}));
|
||||
|
||||
// Shouldn't validate the newly created index here as it wasn't checkpointed yet.
|
||||
res = assert.commandWorked(db.runCommand({validate: collName, background: true}));
|
||||
assert.eq(true, res.valid);
|
||||
assert.eq(1, res.nIndexes);
|
||||
assert.eq(true, res.valid, res);
|
||||
assert.eq(1, res.nIndexes, res);
|
||||
|
||||
forceCheckpoint();
|
||||
|
||||
// Validating after the checkpoint should validate the newly created index.
|
||||
res = assert.commandWorked(db.runCommand({validate: collName, background: true}));
|
||||
assert.eq(true, res.valid);
|
||||
assert.eq(2, res.nIndexes);
|
||||
assert.eq(true, res.valid, res);
|
||||
assert.eq(2, res.nIndexes, res);
|
||||
|
||||
MongoRunner.stopMongod(conn);
|
||||
}());
|
||||
@ -24,15 +24,19 @@ let adminDb = m.getDB('admin');
|
||||
assert.eq(getParameter(adminDb, "diagnosticDataCollectionStatsNamespaces"), ["local.startup_log"]);
|
||||
|
||||
// Validate that collection stats are collected
|
||||
let doc = verifyGetDiagnosticData(adminDb);
|
||||
assert.eq(doc.collectionStats["local.startup_log"].ns, "local.startup_log");
|
||||
let doc;
|
||||
assert.soon(() => {
|
||||
doc = verifyGetDiagnosticData(adminDb);
|
||||
return doc.collectionStats["local.startup_log"].ns == "local.startup_log";
|
||||
});
|
||||
|
||||
// Validate that incorrect changes have no effect
|
||||
assert.commandFailed(setParameter(adminDb, {"diagnosticDataCollectionStatsNamespaces": ["local"]}));
|
||||
|
||||
assert.eq(getParameter(adminDb, "diagnosticDataCollectionStatsNamespaces"), ["local.startup_log"]);
|
||||
|
||||
// Validate that collection stats are collected for runtime collections
|
||||
// Validate that collection stats are collected for runtime collections and that we do not crash
|
||||
// for a non-existent collection
|
||||
assert.commandWorked(setParameter(
|
||||
adminDb,
|
||||
{"diagnosticDataCollectionStatsNamespaces": ["admin.system.version", "admin.does_not_exist"]}));
|
||||
@ -41,8 +45,7 @@ assert.soon(() => {
|
||||
jsTestLog("Collected: " + tojson(result));
|
||||
let collectionStats = result.data.collectionStats;
|
||||
return collectionStats.hasOwnProperty("admin.system.version") &&
|
||||
collectionStats["admin.system.version"].ns == "admin.system.version" &&
|
||||
collectionStats["admin.does_not_exist"].ns == "admin.does_not_exist" != undefined;
|
||||
collectionStats["admin.system.version"].ns == "admin.system.version";
|
||||
});
|
||||
|
||||
// Validate that when it is disabled, we stop collecting
|
||||
|
||||
281
jstests/noPassthrough/global_profiling_filter.js
Normal file
281
jstests/noPassthrough/global_profiling_filter.js
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Test the usage and behavior of the 'setProfilingFilterGlobally' command.
|
||||
*
|
||||
* @tags: [requires_sharding, requires_replication]
|
||||
*/
|
||||
(function() {
|
||||
|
||||
load("jstests/libs/log.js"); // For findMatchingLogLine.
|
||||
load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers.
|
||||
load("jstests/noPassthrough/libs/server_parameter_helpers.js"); // For setParameter.
|
||||
|
||||
// Updates the global profiling filter to 'newFilter' and validates that 'oldFilter' is returned in
|
||||
// the response and the change is logged correctly. If `newFilter' is null, unsets the filter.
|
||||
function updateProfilingFilterGlobally(db, {oldFilter, newFilter}) {
|
||||
const result = assert.commandWorked(
|
||||
db.runCommand({setProfilingFilterGlobally: 1, filter: newFilter ? newFilter : "unset"}));
|
||||
assert.eq(result.was, oldFilter ? oldFilter : "none");
|
||||
|
||||
const log = assert.commandWorked(db.adminCommand({getLog: "global"})).log;
|
||||
assert(!!findMatchingLogLine(log, {
|
||||
msg: "Profiler settings changed globally",
|
||||
from: {filter: oldFilter ? oldFilter : "none"},
|
||||
to: {filter: newFilter ? newFilter : "none"}
|
||||
}),
|
||||
"expected log line was not found");
|
||||
}
|
||||
|
||||
(function testQueryKnobMustBeEnabledToUseCommand() {
|
||||
// First make sure we can't run the command with the query knob turned off.
|
||||
const conn = MongoRunner.runMongod({});
|
||||
assert.commandFailedWithCode(
|
||||
conn.adminCommand({setProfilingFilterGlobally: 1, filter: "unset"}), 7283301);
|
||||
MongoRunner.stopMongod(conn);
|
||||
})();
|
||||
|
||||
(function testCommandOverridesDefaultFromConfigFile() {
|
||||
// Test that setting the global filter overrides the startup configuration.
|
||||
const conn = MongoRunner.runMongod({
|
||||
config: "jstests/libs/config_files/set_profiling_filter.json",
|
||||
setParameter: {internalQueryGlobalProfilingFilter: 1}
|
||||
});
|
||||
updateProfilingFilterGlobally(conn.getDB("test"), {
|
||||
oldFilter: {$expr: {$lt: [{$rand: {}}, {$const: 0.01}]}},
|
||||
newFilter: {$expr: {$lt: [{$rand: {}}, {$const: 0.5}]}}
|
||||
});
|
||||
updateProfilingFilterGlobally(
|
||||
conn.getDB("test"),
|
||||
{oldFilter: {$expr: {$lt: [{$rand: {}}, {$const: 0.5}]}}, newFilter: null});
|
||||
MongoRunner.stopMongod(conn);
|
||||
})();
|
||||
|
||||
// Run a set of correctness tests for the setProfilingFilterGlobally command on the given
|
||||
// connection.
|
||||
function runCorrectnessTests(conn) {
|
||||
// mongoS supports slow-query log lines but not profiling. So if we are talking to mongoS, we
|
||||
// will avoid enabling profiling, or making assertions about profiling.
|
||||
const db = conn.getDB("test");
|
||||
const isMongos = FixtureHelpers.isMongos(db);
|
||||
|
||||
const dbs = [conn.getDB("db1"), conn.getDB("db2"), conn.getDB("db3")];
|
||||
|
||||
function initDatabase(db) {
|
||||
db.c.drop();
|
||||
db.c.insert([{x: 25}, {x: 50}]);
|
||||
|
||||
// Fully enable profiling to start.
|
||||
db.setProfilingLevel(isMongos ? 0 : 1, {slowms: -1});
|
||||
}
|
||||
|
||||
// Initialize each database with some basic data and set the profiling level.
|
||||
for (const db of dbs) {
|
||||
initDatabase(db);
|
||||
}
|
||||
|
||||
// Test queries to run.
|
||||
const queries = {
|
||||
query1: {filter: {}, nreturned: 2},
|
||||
query2: {filter: {x: 25}, nreturned: 1},
|
||||
query3: {filter: {x: 1000}, nreturned: 0},
|
||||
};
|
||||
|
||||
// Profile filters matching specific test queries.
|
||||
const profileFilter1 = {filter: {nreturned: {$eq: 2}}, matchesQuery: "query1"};
|
||||
const profileFilter2 = {filter: {nreturned: {$eq: 1}}, matchesQuery: "query2"};
|
||||
|
||||
// Verify that profile filters are applied as expected for both logging and profiling, if
|
||||
// applicable. 'params' must specify three fields:
|
||||
// 'desc': short test description
|
||||
// 'globalFilter': expected global profiling filter setting
|
||||
// 'dbFilters': expected database-specific filter settings
|
||||
function verify(params) {
|
||||
function verifyDatabase(db) {
|
||||
// Create a unique comment for the query.
|
||||
function queryComment(queryName) {
|
||||
return `${params.desc}: ${db.getName()} -> ${queryName}`;
|
||||
}
|
||||
|
||||
// Check the log for the query's profile entry, and system.profile if this is being run
|
||||
// on mongod.
|
||||
function assertQueryProfiled(name, shouldProfile) {
|
||||
const log = assert.commandWorked(db.adminCommand({getLog: "global"})).log;
|
||||
assert.eq(
|
||||
!!findMatchingLogLine(log, {msg: "Slow query", comment: queryComment(name)}),
|
||||
shouldProfile,
|
||||
`expected query${shouldProfile ? "" : " not"} to be logged: ${
|
||||
queryComment(name)}`);
|
||||
|
||||
if (!isMongos) {
|
||||
assert.eq(!!db.system.profile.findOne({'command.comment': queryComment(name)}),
|
||||
shouldProfile,
|
||||
`expected query${shouldProfile ? "" : " not"} to be profiled: ${
|
||||
queryComment(name)}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [queryName, query] of Object.entries(queries)) {
|
||||
// First, run the query on the database.
|
||||
const comment = queryComment(queryName);
|
||||
const results = db.c.find(query.filter).comment(comment).itcount();
|
||||
assert.eq(results, query.nreturned, comment);
|
||||
|
||||
// Validate whether or not the query was profiled based on the database's
|
||||
// profile filter. We should profile *unless* there is a filter applicable to
|
||||
// this database that does not match the query.
|
||||
let shouldProfile = true;
|
||||
if (params.dbFilters.hasOwnProperty(db.getName())) {
|
||||
// This database has a database specific filter, does it match the query?
|
||||
shouldProfile = params.dbFilters[db.getName()].matchesQuery === queryName;
|
||||
} else if (params.globalFilter) {
|
||||
// No database filter, but there is a global filter, does it match the
|
||||
// query?
|
||||
shouldProfile = params.globalFilter.matchesQuery == queryName;
|
||||
}
|
||||
assertQueryProfiled(queryName, shouldProfile);
|
||||
}
|
||||
}
|
||||
|
||||
for (const db of dbs) {
|
||||
verifyDatabase(db);
|
||||
}
|
||||
|
||||
// Now create a new database and make sure the global settings are applied correctly.
|
||||
const newDB = conn.getDB("newDB");
|
||||
initDatabase(newDB);
|
||||
verifyDatabase(newDB);
|
||||
newDB.dropDatabase();
|
||||
}
|
||||
|
||||
// Error cases (invalid filters or parameters).
|
||||
assert.commandFailedWithCode(db.runCommand({setProfilingFilterGlobally: 1, filter: null}),
|
||||
ErrorCodes.BadValue);
|
||||
assert.commandFailedWithCode(
|
||||
db.runCommand({setProfilingFilterGlobally: 1, filter: {noSuchField: 1}}), 4910200);
|
||||
assert.commandFailedWithCode(
|
||||
db.runCommand({setProfilingFilterGlobally: 1, filter: {}, writeConcern: {w: "majority"}}),
|
||||
ErrorCodes.InvalidOptions);
|
||||
assert.commandFailedWithCode(
|
||||
db.runCommand(
|
||||
{setProfilingFilterGlobally: 1, filter: {}, readConcern: {level: "majority"}}),
|
||||
ErrorCodes.InvalidOptions);
|
||||
|
||||
// Make sure that behavior is as expected in the default/startup state.
|
||||
(function testDefaultSettingIsNone() {
|
||||
verify({desc: "default setting is none", globalFilter: null, dbFilters: {}});
|
||||
})();
|
||||
|
||||
(function testEmptyFilter() {
|
||||
updateProfilingFilterGlobally(db, {oldFilter: null, newFilter: {}});
|
||||
// Empty filter is always true - effectively none.
|
||||
verify({desc: "empty filter", globalFilter: null, dbFilters: {}});
|
||||
})();
|
||||
|
||||
(function testGlobalFilterSettingAffectsAllDatabases() {
|
||||
updateProfilingFilterGlobally(db, {oldFilter: {}, newFilter: profileFilter1.filter});
|
||||
verify({
|
||||
desc: "setting the global filter affects all databases",
|
||||
globalFilter: profileFilter1,
|
||||
dbFilters: {}
|
||||
});
|
||||
})();
|
||||
|
||||
(function testGlobalFilterUnsetClearsGlobalSetting() {
|
||||
updateProfilingFilterGlobally(db, {oldFilter: profileFilter1.filter, newFilter: null});
|
||||
verify({
|
||||
desc: "unsetting the global filter clears the global setting",
|
||||
globalFilter: null,
|
||||
dbFilters: {}
|
||||
});
|
||||
})();
|
||||
|
||||
(function testGlobalFilterUnsetWhenAlreadyUnsetIsNoop() {
|
||||
updateProfilingFilterGlobally(db, {oldFilter: null, newFilter: null});
|
||||
verify({
|
||||
desc: "unsetting the global filter when already unset is a noop",
|
||||
globalFilter: null,
|
||||
dbFilters: {}
|
||||
});
|
||||
})();
|
||||
|
||||
(function testGlobalFilterSettingOverridesDatabaseSpecificSettings() {
|
||||
let result = assert.commandWorked(db.getSiblingDB("db1").runCommand(
|
||||
{profile: isMongos ? 0 : 1, filter: profileFilter1.filter}));
|
||||
assert(!result.filter);
|
||||
result = assert.commandWorked(db.getSiblingDB("db2").runCommand(
|
||||
{profile: isMongos ? 0 : 1, filter: profileFilter2.filter}));
|
||||
assert(!result.filter);
|
||||
verify({
|
||||
desc: "setting the global filter overrides database specific settings (pre-validate)",
|
||||
globalFilter: null,
|
||||
dbFilters: {db1: profileFilter1, db2: profileFilter2}
|
||||
});
|
||||
|
||||
updateProfilingFilterGlobally(db, {oldFilter: null, newFilter: profileFilter2.filter});
|
||||
verify({
|
||||
desc: "setting the global filter overrides database specific settings",
|
||||
globalFilter: profileFilter2,
|
||||
dbFilters: {}
|
||||
});
|
||||
})();
|
||||
|
||||
(function testGlobalFilterSettingOverridesDatabaseSpecificSettingsEvenWhenNoop() {
|
||||
let result = assert.commandWorked(db.getSiblingDB("db1").runCommand(
|
||||
{profile: isMongos ? 0 : 1, filter: profileFilter1.filter}));
|
||||
assert.eq(result.filter, profileFilter2.filter);
|
||||
verify({
|
||||
desc:
|
||||
"setting the global filter overrides database specific settings even when a noop (pre-validate)",
|
||||
globalFilter: profileFilter2,
|
||||
dbFilters: {db1: profileFilter1}
|
||||
});
|
||||
|
||||
updateProfilingFilterGlobally(
|
||||
db, {oldFilter: profileFilter2.filter, newFilter: profileFilter2.filter});
|
||||
verify({
|
||||
desc: "setting the global filter overrides database specific settings even when a noop",
|
||||
globalFilter: profileFilter2,
|
||||
dbFilters: {}
|
||||
});
|
||||
})();
|
||||
|
||||
(function testGlobalFilterUnsetOverridesDatabaseSpecificSettings() {
|
||||
result = assert.commandWorked(db.getSiblingDB("db1").runCommand(
|
||||
{profile: isMongos ? 0 : 1, filter: profileFilter1.filter}));
|
||||
assert.eq(result.filter, profileFilter2.filter);
|
||||
result = assert.commandWorked(db.getSiblingDB("db3").runCommand(
|
||||
{profile: isMongos ? 0 : 1, filter: profileFilter2.filter}));
|
||||
assert.eq(result.filter, profileFilter2.filter);
|
||||
verify({
|
||||
desc: "unsetting the global filter overrides database specific settings (pre-validate)",
|
||||
globalFilter: profileFilter2,
|
||||
dbFilters: {db1: profileFilter1, db3: profileFilter2}
|
||||
});
|
||||
|
||||
updateProfilingFilterGlobally(db, {oldFilter: profileFilter2.filter, newFilter: null});
|
||||
verify({
|
||||
desc: "unsetting the global filter overrides database specific settings",
|
||||
globalFilter: null,
|
||||
dbFilters: {}
|
||||
});
|
||||
})();
|
||||
}
|
||||
|
||||
{
|
||||
// Run tests on mongod.
|
||||
const conn = MongoRunner.runMongod({setParameter: {internalQueryGlobalProfilingFilter: 1}});
|
||||
runCorrectnessTests(conn);
|
||||
MongoRunner.stopMongod(conn);
|
||||
}
|
||||
|
||||
{
|
||||
// Run tests on mongos.
|
||||
const st = ShardingTest({
|
||||
shards: 1,
|
||||
rs: {nodes: 1},
|
||||
config: 1,
|
||||
mongosOptions: {setParameter: {internalQueryGlobalProfilingFilter: 1}}
|
||||
});
|
||||
runCorrectnessTests(st);
|
||||
st.stop();
|
||||
}
|
||||
})();
|
||||
@ -29,7 +29,7 @@ const collName = "collectionWithMalformedValidator";
|
||||
assert.commandWorked(
|
||||
testDB.runCommand({collMod: collName, validator: {email: {$regex: invalidRegex}}}));
|
||||
|
||||
MongoRunner.stopMongod(conn, null, {skipValidation: true});
|
||||
MongoRunner.stopMongod(conn);
|
||||
})();
|
||||
|
||||
(function startUpWithMalformedValidator() {
|
||||
@ -48,6 +48,6 @@ const collName = "collectionWithMalformedValidator";
|
||||
assert.commandWorked(testDB.someOtherCollection.insert({a: 1}));
|
||||
assert.eq(testDB.someOtherCollection.find().itcount(), 1);
|
||||
|
||||
MongoRunner.stopMongod(conn, null, {skipValidation: true});
|
||||
MongoRunner.stopMongod(conn);
|
||||
})();
|
||||
})();
|
||||
|
||||
@ -253,8 +253,5 @@ bulkOp.find({$expr: {$eq: ["$x", 2]}}).update({$set: {x: 10}});
|
||||
bulkOp.find({$expr: {$lt: ["$x", 1]}}).remove();
|
||||
checkCounters(() => assert.commandWorked(bulkOp.execute()), {"$eq": 1, "$lt": 1});
|
||||
|
||||
// Set the validationAction to "warn" to avoid failing collection validation.
|
||||
assert.commandWorked(testColl.runCommand("collMod", {validationAction: "warn"}));
|
||||
|
||||
MongoRunner.stopMongod(mongod);
|
||||
})();
|
||||
|
||||
@ -18,6 +18,10 @@ function getPlanCacheSize() {
|
||||
return db.runCommand({serverStatus: 1}).metrics.query.planCacheTotalSizeEstimateBytes;
|
||||
}
|
||||
|
||||
function getGlobalPlanCacheNumEntries() {
|
||||
return db.runCommand({serverStatus: 1}).metrics.query.planCacheTotalQueryShapes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class that creates a collection, indexes on it, and makes a few queries to add entries to
|
||||
* the plan cache.
|
||||
@ -37,20 +41,21 @@ class TestCollection {
|
||||
|
||||
assert.gt(getPlanCacheSize(), 0);
|
||||
|
||||
this.nCacheEntries = this.getNumberOfPlanCacheEntries();
|
||||
this.nCacheEntries = this.getNumberOfCollectionPlanCacheEntries();
|
||||
assert.eq(2, this.nCacheEntries);
|
||||
}
|
||||
|
||||
getNumberOfPlanCacheEntries() {
|
||||
// The following three helper functions concern plan cache entries specific to a given
|
||||
// collection, and not the entire/global plan cache
|
||||
getNumberOfCollectionPlanCacheEntries() {
|
||||
return this.coll.getPlanCache().list().length;
|
||||
}
|
||||
|
||||
assertAllCollectionCacheEntriesRemoved() {
|
||||
assert.eq(0, this.getNumberOfPlanCacheEntries());
|
||||
assert.eq(0, this.getNumberOfCollectionPlanCacheEntries());
|
||||
}
|
||||
|
||||
assertCacheEntriesNotRemoved() {
|
||||
assert.eq(this.nCacheEntries, this.getNumberOfPlanCacheEntries());
|
||||
assertCollectionCacheEntriesNotRemoved() {
|
||||
assert.eq(this.nCacheEntries, this.getNumberOfCollectionPlanCacheEntries());
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,23 +64,28 @@ class TestCollection {
|
||||
const test = new TestCollection();
|
||||
|
||||
assert.gt(getPlanCacheSize(), initialPlanCacheSize);
|
||||
assert.eq(getGlobalPlanCacheNumEntries(), 2);
|
||||
|
||||
assert(test.coll.drop());
|
||||
|
||||
assert.eq(getPlanCacheSize(), initialPlanCacheSize);
|
||||
assert.eq(getGlobalPlanCacheNumEntries(), 0);
|
||||
}());
|
||||
|
||||
(function cacheEntriesNotRemovedIfAnotherCollectedDropped() {
|
||||
const test = new TestCollection("coll1");
|
||||
const cacheSizeForOneTestCollection = getPlanCacheSize();
|
||||
assert.eq(getGlobalPlanCacheNumEntries(), 2);
|
||||
|
||||
const anotherTest = new TestCollection("coll2");
|
||||
const cacheSizeForTwoTestCollections = getPlanCacheSize();
|
||||
assert.eq(getGlobalPlanCacheNumEntries(), 4);
|
||||
assert.gt(cacheSizeForTwoTestCollections, cacheSizeForOneTestCollection);
|
||||
assert(anotherTest.coll.drop());
|
||||
|
||||
assert.eq(cacheSizeForOneTestCollection, getPlanCacheSize());
|
||||
test.assertCacheEntriesNotRemoved();
|
||||
// Entries associated with anotherTest.coll are booted from the plan cache.
|
||||
assert.eq(getGlobalPlanCacheNumEntries(), 2);
|
||||
test.assertCollectionCacheEntriesNotRemoved();
|
||||
}());
|
||||
|
||||
(function cacheEntriesRemovedIfANewIndexCreated() {
|
||||
@ -107,14 +117,13 @@ class TestCollection {
|
||||
db.runCommand({collMod: collectionName, validator: {text: {$type: "string"}}}));
|
||||
|
||||
assert.eq(getPlanCacheSize(), initialPlanCacheSize);
|
||||
test.assertCacheEntriesNotRemoved();
|
||||
test.assertCollectionCacheEntriesNotRemoved();
|
||||
}());
|
||||
|
||||
(function cacheEntriesRemovedIfIndexChanged() {
|
||||
const collectionName = "coll";
|
||||
const test = new TestCollection(collectionName);
|
||||
const initialPlanCacheSize = getPlanCacheSize();
|
||||
|
||||
assert.commandWorked(db.runCommand({
|
||||
collMod: collectionName,
|
||||
index: {
|
||||
@ -122,7 +131,6 @@ class TestCollection {
|
||||
hidden: true,
|
||||
}
|
||||
}));
|
||||
|
||||
assert.lt(getPlanCacheSize(), initialPlanCacheSize);
|
||||
test.assertAllCollectionCacheEntriesRemoved();
|
||||
}());
|
||||
@ -143,16 +151,16 @@ class TestCollection {
|
||||
(function oneCacheEntryRemovedOnClearPlanCacheWithQueryCommand() {
|
||||
const collectionName = "coll";
|
||||
const test = new TestCollection(collectionName);
|
||||
const numberOfCacheEntries = test.getNumberOfPlanCacheEntries();
|
||||
const numberOfCacheEntries = getGlobalPlanCacheNumEntries();
|
||||
|
||||
test.coll.find({a: 1, b: 2, c: 3, d: 4}).itcount();
|
||||
assert.eq(numberOfCacheEntries + 1, test.getNumberOfPlanCacheEntries());
|
||||
assert.eq(numberOfCacheEntries + 1, getGlobalPlanCacheNumEntries());
|
||||
const planCacheSize = getPlanCacheSize();
|
||||
|
||||
assert.commandWorked(
|
||||
db.runCommand({planCacheClear: collectionName, query: {a: 1, b: 2, c: 3, d: 4}}));
|
||||
assert.lt(getPlanCacheSize(), planCacheSize);
|
||||
assert.eq(numberOfCacheEntries, test.getNumberOfPlanCacheEntries());
|
||||
assert.eq(numberOfCacheEntries, getGlobalPlanCacheNumEntries());
|
||||
}());
|
||||
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
@ -45,6 +45,9 @@ function getPlanCacheSize() {
|
||||
return db.serverStatus().metrics.query.planCacheTotalSizeEstimateBytes;
|
||||
}
|
||||
|
||||
function getPlanCacheNumEntries() {
|
||||
return db.serverStatus().metrics.query.planCacheTotalQueryShapes;
|
||||
}
|
||||
function assertQueryInPlanCache(coll, query) {
|
||||
const explainResult = assert.commandWorked(coll.explain().find(query).finish());
|
||||
const queryHash = getQueryHashFromExplain(explainResult, db);
|
||||
@ -65,7 +68,7 @@ assert.commandWorked(coll.createIndex({a: 1, b: 1}));
|
||||
|
||||
const initialPlanCacheSize = getPlanCacheSize();
|
||||
// Plan cache must be empty.
|
||||
assert.eq(0, coll.getPlanCache().list().length);
|
||||
assert.eq(0, getPlanCacheNumEntries());
|
||||
|
||||
const sbeQuery = {
|
||||
a: 1
|
||||
@ -79,7 +82,7 @@ const classicQuery = {
|
||||
assert.eq(1, coll.find(sbeQuery).itcount());
|
||||
assertQueryInPlanCache(coll, sbeQuery);
|
||||
// Plan Cache must contain exactly 1 entry.
|
||||
assert.eq(1, coll.getPlanCache().list().length);
|
||||
assert.eq(1, getPlanCacheNumEntries());
|
||||
|
||||
// Assert metric is incremented for new cache entry.
|
||||
const afterSbePlanCacheSize = getPlanCacheSize();
|
||||
@ -92,7 +95,7 @@ assert.commandWorked(
|
||||
assert.eq(1, coll.find(classicQuery).itcount());
|
||||
assertQueryInPlanCache(coll, classicQuery);
|
||||
// Plan Cache must contain exactly 2 entries.
|
||||
assert.eq(2, coll.getPlanCache().list().length);
|
||||
assert.eq(2, getPlanCacheNumEntries());
|
||||
|
||||
// Assert metric is incremented for new cache entry.
|
||||
const afterClassicPlanCacheSize = getPlanCacheSize();
|
||||
|
||||
@ -4,6 +4,10 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
// Disable the testing proctor. When the testing proctor is enabled, 'validate' will only warn about
|
||||
// non-compliant documents, even when the validation action is 'error'.
|
||||
TestData.testingDiagnosticsEnabled = false;
|
||||
|
||||
const conn = MongoRunner.runMongod();
|
||||
|
||||
const dbName = "test";
|
||||
|
||||
@ -25,44 +25,53 @@ function corruptIndex() {
|
||||
coll = conn.getDB("test").getCollection("corrupt");
|
||||
}
|
||||
|
||||
function checkValidate(errorPrefix, numMissingIndexEntries) {
|
||||
conn.getDB("test").adminCommand({setParameter: 1, maxValidateMemoryUsageMB: 1});
|
||||
function checkValidate(maxMemoryUsage, {minMissingKeys, maxMissingKeys}) {
|
||||
conn.getDB("test").adminCommand({setParameter: 1, maxValidateMemoryUsageMB: maxMemoryUsage});
|
||||
const res = coll.validate();
|
||||
assert.commandWorked(res);
|
||||
assert(!res.valid);
|
||||
assert.containsPrefix(errorPrefix, res.errors);
|
||||
assert.eq(res.missingIndexEntries.length, numMissingIndexEntries);
|
||||
assert(!res.valid, tojson(res));
|
||||
const notAllReportedPrefix =
|
||||
"Not all index entry inconsistencies are reported due to memory limitations.";
|
||||
assert.containsPrefix(notAllReportedPrefix, res.errors, tojson(res));
|
||||
assert.gte(res.missingIndexEntries.length, minMissingKeys, tojson(res));
|
||||
assert.lte(res.missingIndexEntries.length, maxMissingKeys, tojson(res));
|
||||
}
|
||||
|
||||
function checkValidateRepair(expectRepair) {
|
||||
function checkValidateRepair() {
|
||||
const res = coll.validate({repair: true});
|
||||
assert.commandWorked(res);
|
||||
assert(!res.valid, printjson(res));
|
||||
assert.eq(res.repaired, expectRepair, printjson(res));
|
||||
assert(!res.valid, tojson(res));
|
||||
assert(res.repaired, tojson(res));
|
||||
}
|
||||
|
||||
const noneReportedPrefix =
|
||||
"Unable to report index entry inconsistencies due to memory limitations.";
|
||||
const notAllReportedPrefix =
|
||||
"Not all index entry inconsistencies are reported due to memory limitations.";
|
||||
|
||||
// Insert a document with a key larger than maxValidateMemoryUsageMB so that validate does not
|
||||
// report any missing index entries.
|
||||
// Insert a document with a key larger than maxValidateMemoryUsageMB and test that we still report
|
||||
// at least one inconsistency.
|
||||
const indexKey = "a".repeat(kIndexKeyLength);
|
||||
assert.commandWorked(coll.insert({_id: indexKey}));
|
||||
corruptIndex();
|
||||
checkValidate(noneReportedPrefix, 0);
|
||||
checkValidate(1, {minMissingKeys: 1, maxMissingKeys: 1});
|
||||
|
||||
// Can't repair successfully if there aren't any index inconsistencies reported.
|
||||
checkValidateRepair(false);
|
||||
checkValidateRepair();
|
||||
|
||||
// Clear collection between tests.
|
||||
coll.drop();
|
||||
|
||||
// Test that if we have keys distributed across many buckets, and would exceed
|
||||
// maxValidateMemoryUsageMB, we report as many inconsistencies as we can.
|
||||
for (let i = 0; i < 10; ++i) {
|
||||
const indexKey = i.toString().repeat(kIndexKeyLength / 5);
|
||||
assert.commandWorked(coll.insert({_id: indexKey}));
|
||||
}
|
||||
|
||||
// Insert a document with a small key so that validate reports one missing index entry.
|
||||
assert.commandWorked(coll.insert({_id: 1}));
|
||||
corruptIndex();
|
||||
checkValidate(notAllReportedPrefix, 1);
|
||||
// If each key is maxMem/5, then we can keep 4 of them (the 5th would put us at the limit). However,
|
||||
// each key is counted twice, so realistically we only expect to track 2 of them. However, there's
|
||||
// a small chance we could get hash collisions that would lead to us reporting only 1.
|
||||
checkValidate(1, {minMissingKeys: 1, maxMissingKeys: 2});
|
||||
|
||||
// Repair, but incompletely if only some inconsistencies are reported.
|
||||
checkValidateRepair(true);
|
||||
checkValidateRepair();
|
||||
|
||||
MongoRunner.stopMongod(conn, null, {skipValidation: true});
|
||||
})();
|
||||
32
jstests/noPassthroughWithMongod/telemetry_configuration.js
Normal file
32
jstests/noPassthroughWithMongod/telemetry_configuration.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Tests that the telemetry store can be resized if it is configured, and cannot be resized if it is
|
||||
* disabled.
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
load("jstests/libs/feature_flag_util.js");
|
||||
|
||||
if (FeatureFlagUtil.isEnabled(db, "Telemetry")) {
|
||||
// The feature flag is enabled - make sure the telemetry store can be configured.
|
||||
const original = assert.commandWorked(
|
||||
db.adminCommand({getParameter: 1, internalQueryConfigureTelemetryCacheSize: 1}));
|
||||
assert(original.hasOwnProperty("internalQueryConfigureTelemetryCacheSize"), original);
|
||||
const originalValue = original.internalQueryConfigureTelemetryCacheSize;
|
||||
try {
|
||||
assert.doesNotThrow(
|
||||
() => db.adminCommand(
|
||||
{setParameter: 1, internalQueryConfigureTelemetryCacheSize: '2MB'}));
|
||||
// Other tests verify that resizing actually affects the data structure size.
|
||||
} finally {
|
||||
assert.doesNotThrow(
|
||||
() => db.adminCommand(
|
||||
{setParameter: 1, internalQueryConfigureTelemetryCacheSize: originalValue}));
|
||||
}
|
||||
} else {
|
||||
// The feature flag is disabled - make sure the telemetry store *cannot* be configured.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({setParameter: 1, internalQueryConfigureTelemetryCacheSize: '2MB'}),
|
||||
7373500);
|
||||
}
|
||||
}());
|
||||
33
jstests/noPassthroughWithMongod/validate_bson_types.js
Normal file
33
jstests/noPassthroughWithMongod/validate_bson_types.js
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Tests that the validate checkBSONConformance option works with various BSON types.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
const coll = db.validate_bson_types;
|
||||
|
||||
assert.commandWorked(coll.insert({a: 1.0})); // double
|
||||
assert.commandWorked(coll.insert({b: "abc"})); // string
|
||||
assert.commandWorked(coll.insert({c: {x: 1}})); // object
|
||||
assert.commandWorked(coll.insert({d: [1, 2, 3]})); // array
|
||||
assert.commandWorked(coll.insert({
|
||||
e: BinData(2, "KwAAAFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2c=")
|
||||
})); // binData
|
||||
assert.commandWorked(coll.insert({f: undefined})); // undefined
|
||||
assert.commandWorked(coll.insert({g: ObjectId("dbdbdbdbdbdbdbdbdbdbdbdb")})); // objectId
|
||||
assert.commandWorked(coll.insert({h: true})); // boolean
|
||||
assert.commandWorked(coll.insert({i: ISODate("2013-12-11T19:38:24.055Z")})); // UTC
|
||||
assert.commandWorked(coll.insert({j: null})); // null
|
||||
assert.commandWorked(coll.insert({k: RegExp("a")})); // regex
|
||||
assert.commandWorked(
|
||||
coll.insert({l: DBPointer("foo", ObjectId("bbbbbbbbbbbbbbbbbbbbbbbb"))})); // DBPointer
|
||||
assert.commandWorked(coll.insert({m: Code("noScope")})); // code
|
||||
assert.commandWorked(coll.insert({n: Code('function(){return 1;}', {})})); // code w/ scope
|
||||
assert.commandWorked(coll.insert({o: 3})); // int
|
||||
assert.commandWorked(coll.insert({p: Timestamp(1, 2)})); // timestamp
|
||||
assert.commandWorked(coll.insert({q: NumberLong(6)})); // 64-bit int
|
||||
assert.commandWorked(coll.insert({r: NumberDecimal("2.0")})); // decimal 128
|
||||
assert.commandWorked(coll.insert({s: MinKey()})); // MinKey
|
||||
assert.commandWorked(coll.insert({t: MaxKey()})); // MaxKey
|
||||
|
||||
assert.commandWorked(coll.validate({checkBSONConformance: true}));
|
||||
})();
|
||||
@ -22,6 +22,8 @@ const fullNs = dbName + "." + collName;
|
||||
// Pre-written reasons for skipping a test.
|
||||
const isAnInternalCommand = "internal command";
|
||||
const isDeprecated = "deprecated command";
|
||||
// TODO SERVER-69753 some commands we didn't have time for. Other commands are new in recent
|
||||
// releases and don't make sense to test here.
|
||||
const isNotImplementedYet = "not implemented yet";
|
||||
|
||||
let _lsid = UUID();
|
||||
@ -1097,6 +1099,7 @@ const allCommands = {
|
||||
setIndexCommitQuorum: {skip: isNotImplementedYet},
|
||||
setFeatureCompatibilityVersion: {skip: isNotImplementedYet},
|
||||
setFreeMonitoring: {skip: isNotImplementedYet},
|
||||
setProfilingFilterGlobally: {skip: isNotImplementedYet},
|
||||
setParameter: {skip: isNotImplementedYet},
|
||||
setShardVersion: {skip: isNotImplementedYet},
|
||||
setChangeStreamState: {skip: isNotImplementedYet},
|
||||
|
||||
@ -365,6 +365,7 @@ const allCommands = {
|
||||
setIndexCommitQuorum: {skip: isPrimaryOnly},
|
||||
setFeatureCompatibilityVersion: {skip: isPrimaryOnly},
|
||||
setFreeMonitoring: {skip: isPrimaryOnly},
|
||||
setProfilingFilterGlobally: {skip: isNotAUserDataRead},
|
||||
setParameter: {skip: isNotAUserDataRead},
|
||||
setShardVersion: {skip: isNotAUserDataRead},
|
||||
setChangeStreamState: {skip: isNotAUserDataRead},
|
||||
|
||||
@ -18,15 +18,12 @@ var coll = primary.getDB('test').getCollection(name);
|
||||
assert.commandWorked(coll.insert({_id: 0, x: 1}));
|
||||
assert.commandWorked(coll.runCommand("collMod", {"validator": {a: {$exists: true}}}));
|
||||
|
||||
secondary = replSet.restart(secondary, {startClean: true, skipValidation: true});
|
||||
secondary = replSet.restart(secondary, {startClean: true});
|
||||
replSet.awaitReplication();
|
||||
replSet.awaitSecondaryNodes();
|
||||
|
||||
assert.eq(1, secondary.getDB("test")[name].count());
|
||||
assert.docEq({_id: 0, x: 1}, secondary.getDB("test")[name].findOne());
|
||||
|
||||
// Set the validationAction to "warn" to avoid failing collection validation.
|
||||
assert.commandWorked(coll.runCommand("collMod", {validationAction: "warn"}));
|
||||
|
||||
replSet.stopSet();
|
||||
})();
|
||||
|
||||
194
jstests/replsets/libs/oplog_rollover_test.js
Normal file
194
jstests/replsets/libs/oplog_rollover_test.js
Normal file
@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Test that oplog (on both primary and secondary) rolls over when its size exceeds the configured
|
||||
* maximum, with parameters for setting the initial sync method and the storage engine.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
load("jstests/libs/fail_point_util.js");
|
||||
|
||||
function oplogRolloverTest(storageEngine, initialSyncMethod) {
|
||||
jsTestLog("Testing with storageEngine: " + storageEngine);
|
||||
if (initialSyncMethod) {
|
||||
jsTestLog(" and initial sync method: " + initialSyncMethod);
|
||||
}
|
||||
|
||||
// Pause the oplog cap maintainer thread for this test until oplog truncation is needed. The
|
||||
// truncation thread can hold a mutex for a short period of time which prevents new oplog stones
|
||||
// from being created during an insertion if the mutex cannot be obtained immediately. Instead,
|
||||
// the next insertion will attempt to create a new oplog stone, which this test does not do.
|
||||
let parameters = {
|
||||
logComponentVerbosity: tojson({storage: 2}),
|
||||
'failpoint.hangOplogCapMaintainerThread': tojson({mode: 'alwaysOn'})
|
||||
};
|
||||
if (initialSyncMethod) {
|
||||
parameters = Object.merge(parameters, {initialSyncMethod: initialSyncMethod});
|
||||
}
|
||||
const replSet = new ReplSetTest({
|
||||
// Set the syncdelay to 1s to speed up checkpointing.
|
||||
nodeOptions: {
|
||||
syncdelay: 1,
|
||||
setParameter: parameters,
|
||||
},
|
||||
nodes: [{}, {rsConfig: {priority: 0, votes: 0}}]
|
||||
});
|
||||
// Set max oplog size to 1MB.
|
||||
replSet.startSet({storageEngine: storageEngine, oplogSize: 1});
|
||||
replSet.initiate();
|
||||
|
||||
const primary = replSet.getPrimary();
|
||||
const primaryOplog = primary.getDB("local").oplog.rs;
|
||||
const secondary = replSet.getSecondary();
|
||||
const secondaryOplog = secondary.getDB("local").oplog.rs;
|
||||
|
||||
// Verify that the oplog cap maintainer thread is paused.
|
||||
assert.commandWorked(primary.adminCommand({
|
||||
waitForFailPoint: "hangOplogCapMaintainerThread",
|
||||
timesEntered: 1,
|
||||
maxTimeMS: kDefaultWaitForFailPointTimeout
|
||||
}));
|
||||
assert.commandWorked(secondary.adminCommand({
|
||||
waitForFailPoint: "hangOplogCapMaintainerThread",
|
||||
timesEntered: 1,
|
||||
maxTimeMS: kDefaultWaitForFailPointTimeout
|
||||
}));
|
||||
|
||||
const coll = primary.getDB("test").foo;
|
||||
// 400KB each so that oplog can keep at most two insert oplog entries.
|
||||
const longString = new Array(400 * 1024).join("a");
|
||||
|
||||
function numInsertOplogEntry(oplog) {
|
||||
print(`Oplog times for ${oplog.getMongo().host}: ${
|
||||
tojsononeline(oplog.find().projection({ts: 1, t: 1, op: 1, ns: 1}).toArray())}`);
|
||||
return oplog.find({op: "i", "ns": "test.foo"}).itcount();
|
||||
}
|
||||
|
||||
// Insert the first document.
|
||||
const firstInsertTimestamp =
|
||||
assert
|
||||
.commandWorked(coll.runCommand(
|
||||
"insert", {documents: [{_id: 0, longString: longString}], writeConcern: {w: 2}}))
|
||||
.operationTime;
|
||||
jsTestLog("First insert timestamp: " + tojson(firstInsertTimestamp));
|
||||
|
||||
// Test that oplog entry of the first insert exists on both primary and secondary.
|
||||
assert.eq(1, numInsertOplogEntry(primaryOplog));
|
||||
assert.eq(1, numInsertOplogEntry(secondaryOplog));
|
||||
|
||||
// Insert the second document.
|
||||
const secondInsertTimestamp =
|
||||
assert
|
||||
.commandWorked(coll.runCommand(
|
||||
"insert", {documents: [{_id: 1, longString: longString}], writeConcern: {w: 2}}))
|
||||
.operationTime;
|
||||
jsTestLog("Second insert timestamp: " + tojson(secondInsertTimestamp));
|
||||
|
||||
// Test that oplog entries of both inserts exist on both primary and secondary.
|
||||
assert.eq(2, numInsertOplogEntry(primaryOplog));
|
||||
assert.eq(2, numInsertOplogEntry(secondaryOplog));
|
||||
|
||||
// Have a more fine-grained test for enableMajorityReadConcern=true to also test oplog
|
||||
// truncation happens at the time we expect it to happen. When
|
||||
// enableMajorityReadConcern=false the lastStableRecoveryTimestamp is not available, so
|
||||
// switch to a coarser-grained mode to only test that oplog truncation will eventually
|
||||
// happen when oplog size exceeds the configured maximum.
|
||||
if (primary.getDB('admin').serverStatus().storageEngine.supportsCommittedReads) {
|
||||
const awaitCheckpointer = function(timestamp) {
|
||||
assert.soon(
|
||||
() => {
|
||||
const primaryTimestamp =
|
||||
assert.commandWorked(primary.adminCommand({replSetGetStatus: 1}))
|
||||
.lastStableRecoveryTimestamp;
|
||||
const secondaryTimestamp =
|
||||
assert.commandWorked(secondary.adminCommand({replSetGetStatus: 1}))
|
||||
.lastStableRecoveryTimestamp;
|
||||
jsTestLog("Awaiting last stable recovery timestamp " +
|
||||
`(primary: ${tojson(primaryTimestamp)}, secondary: ${
|
||||
tojson(secondaryTimestamp)}) ` +
|
||||
`target: ${tojson(timestamp)}`);
|
||||
return ((timestampCmp(primaryTimestamp, timestamp) >= 0) &&
|
||||
(timestampCmp(secondaryTimestamp, timestamp) >= 0));
|
||||
},
|
||||
"Timeout waiting for checkpointing to catch up",
|
||||
ReplSetTest.kDefaultTimeoutMS,
|
||||
2000);
|
||||
};
|
||||
|
||||
// Wait for checkpointing/stable timestamp to catch up with the second insert so oplog
|
||||
// entry of the first insert is allowed to be deleted by the oplog cap maintainer thread
|
||||
// when a new oplog stone is created. "inMemory" WT engine does not run checkpoint
|
||||
// thread and lastStableRecoveryTimestamp is the stable timestamp in this case.
|
||||
awaitCheckpointer(secondInsertTimestamp);
|
||||
|
||||
// Insert the third document which will trigger a new oplog stone to be created. The
|
||||
// oplog cap maintainer thread will then be unblocked on the creation of the new oplog
|
||||
// stone and will start truncating oplog entries. The oplog entry for the first
|
||||
// insert will be truncated after the oplog cap maintainer thread finishes.
|
||||
const thirdInsertTimestamp =
|
||||
assert
|
||||
.commandWorked(coll.runCommand(
|
||||
"insert",
|
||||
{documents: [{_id: 2, longString: longString}], writeConcern: {w: 2}}))
|
||||
.operationTime;
|
||||
jsTestLog("Third insert timestamp: " + tojson(thirdInsertTimestamp));
|
||||
|
||||
// There is a race between how we calculate the pinnedOplog and checkpointing. The timestamp
|
||||
// of the pinnedOplog could be less than the actual stable timestamp used in a checkpoint.
|
||||
// Wait for the checkpointer to run for another round to make sure the first insert oplog is
|
||||
// not pinned.
|
||||
awaitCheckpointer(thirdInsertTimestamp);
|
||||
|
||||
// Verify that there are three oplog entries while the oplog cap maintainer thread is
|
||||
// paused.
|
||||
assert.eq(3, numInsertOplogEntry(primaryOplog));
|
||||
assert.eq(3, numInsertOplogEntry(secondaryOplog));
|
||||
|
||||
// Let the oplog cap maintainer thread start truncating the oplog.
|
||||
assert.commandWorked(primary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
assert.commandWorked(secondary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
|
||||
// Test that oplog entry of the initial insert rolls over on both primary and secondary.
|
||||
// Use assert.soon to wait for oplog cap maintainer thread to run.
|
||||
assert.soon(() => {
|
||||
return numInsertOplogEntry(primaryOplog) === 2;
|
||||
}, "Timeout waiting for oplog to roll over on primary");
|
||||
assert.soon(() => {
|
||||
return numInsertOplogEntry(secondaryOplog) === 2;
|
||||
}, "Timeout waiting for oplog to roll over on secondary");
|
||||
|
||||
const res = primary.getDB("test").runCommand({serverStatus: 1});
|
||||
assert.commandWorked(res);
|
||||
assert.eq(res.oplogTruncation.truncateCount, 1, tojson(res.oplogTruncation));
|
||||
assert.gt(res.oplogTruncation.totalTimeTruncatingMicros, 0, tojson(res.oplogTruncation));
|
||||
} else {
|
||||
// Let the oplog cap maintainer thread start truncating the oplog.
|
||||
assert.commandWorked(primary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
assert.commandWorked(secondary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
|
||||
// Only test that oplog truncation will eventually happen.
|
||||
let numInserted = 2;
|
||||
assert.soon(function() {
|
||||
// Insert more documents.
|
||||
assert.commandWorked(
|
||||
coll.insert({_id: numInserted++, longString: longString}, {writeConcern: {w: 2}}));
|
||||
const numInsertOplogEntryPrimary = numInsertOplogEntry(primaryOplog);
|
||||
const numInsertOplogEntrySecondary = numInsertOplogEntry(secondaryOplog);
|
||||
// Oplog has been truncated if the number of insert oplog entries is less than
|
||||
// number of inserted.
|
||||
if (numInsertOplogEntryPrimary < numInserted &&
|
||||
numInsertOplogEntrySecondary < numInserted)
|
||||
return true;
|
||||
jsTestLog("Awaiting oplog truncation: number of oplog entries: " +
|
||||
`(primary: ${tojson(numInsertOplogEntryPrimary)}, ` +
|
||||
`secondary: ${tojson(numInsertOplogEntrySecondary)}) ` +
|
||||
`number inserted: ${numInserted}`);
|
||||
return false;
|
||||
}, "Timeout waiting for oplog to roll over", ReplSetTest.kDefaultTimeoutMS, 1000);
|
||||
}
|
||||
|
||||
replSet.stopSet();
|
||||
}
|
||||
@ -5,188 +5,9 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
load("jstests/libs/fail_point_util.js");
|
||||
load("jstests/replsets/libs/oplog_rollover_test.js");
|
||||
|
||||
function doTest(storageEngine) {
|
||||
jsTestLog("Testing with storageEngine: " + storageEngine);
|
||||
|
||||
// Pause the oplog cap maintainer thread for this test until oplog truncation is needed. The
|
||||
// truncation thread can hold a mutex for a short period of time which prevents new oplog stones
|
||||
// from being created during an insertion if the mutex cannot be obtained immediately. Instead,
|
||||
// the next insertion will attempt to create a new oplog stone, which this test does not do.
|
||||
const replSet = new ReplSetTest({
|
||||
// Set the syncdelay to 1s to speed up checkpointing.
|
||||
nodeOptions: {
|
||||
syncdelay: 1,
|
||||
setParameter: {
|
||||
logComponentVerbosity: tojson({storage: 2}),
|
||||
'failpoint.hangOplogCapMaintainerThread': tojson({mode: 'alwaysOn'})
|
||||
}
|
||||
},
|
||||
nodes: [{}, {rsConfig: {priority: 0, votes: 0}}]
|
||||
});
|
||||
// Set max oplog size to 1MB.
|
||||
replSet.startSet({storageEngine: storageEngine, oplogSize: 1});
|
||||
replSet.initiate();
|
||||
|
||||
const primary = replSet.getPrimary();
|
||||
const primaryOplog = primary.getDB("local").oplog.rs;
|
||||
const secondary = replSet.getSecondary();
|
||||
const secondaryOplog = secondary.getDB("local").oplog.rs;
|
||||
|
||||
// Verify that the oplog cap maintainer thread is paused.
|
||||
assert.commandWorked(primary.adminCommand({
|
||||
waitForFailPoint: "hangOplogCapMaintainerThread",
|
||||
timesEntered: 1,
|
||||
maxTimeMS: kDefaultWaitForFailPointTimeout
|
||||
}));
|
||||
assert.commandWorked(secondary.adminCommand({
|
||||
waitForFailPoint: "hangOplogCapMaintainerThread",
|
||||
timesEntered: 1,
|
||||
maxTimeMS: kDefaultWaitForFailPointTimeout
|
||||
}));
|
||||
|
||||
const coll = primary.getDB("test").foo;
|
||||
// 400KB each so that oplog can keep at most two insert oplog entries.
|
||||
const longString = new Array(400 * 1024).join("a");
|
||||
|
||||
function numInsertOplogEntry(oplog) {
|
||||
print(`Oplog times for ${oplog.getMongo().host}: ${
|
||||
tojsononeline(oplog.find().projection({ts: 1, t: 1, op: 1, ns: 1}).toArray())}`);
|
||||
return oplog.find({op: "i", "ns": "test.foo"}).itcount();
|
||||
}
|
||||
|
||||
// Insert the first document.
|
||||
const firstInsertTimestamp =
|
||||
assert
|
||||
.commandWorked(coll.runCommand(
|
||||
"insert", {documents: [{_id: 0, longString: longString}], writeConcern: {w: 2}}))
|
||||
.operationTime;
|
||||
jsTestLog("First insert timestamp: " + tojson(firstInsertTimestamp));
|
||||
|
||||
// Test that oplog entry of the first insert exists on both primary and secondary.
|
||||
assert.eq(1, numInsertOplogEntry(primaryOplog));
|
||||
assert.eq(1, numInsertOplogEntry(secondaryOplog));
|
||||
|
||||
// Insert the second document.
|
||||
const secondInsertTimestamp =
|
||||
assert
|
||||
.commandWorked(coll.runCommand(
|
||||
"insert", {documents: [{_id: 1, longString: longString}], writeConcern: {w: 2}}))
|
||||
.operationTime;
|
||||
jsTestLog("Second insert timestamp: " + tojson(secondInsertTimestamp));
|
||||
|
||||
// Test that oplog entries of both inserts exist on both primary and secondary.
|
||||
assert.eq(2, numInsertOplogEntry(primaryOplog));
|
||||
assert.eq(2, numInsertOplogEntry(secondaryOplog));
|
||||
|
||||
// Have a more fine-grained test for enableMajorityReadConcern=true to also test oplog
|
||||
// truncation happens at the time we expect it to happen. When
|
||||
// enableMajorityReadConcern=false the lastStableRecoveryTimestamp is not available, so
|
||||
// switch to a coarser-grained mode to only test that oplog truncation will eventually
|
||||
// happen when oplog size exceeds the configured maximum.
|
||||
if (primary.getDB('admin').serverStatus().storageEngine.supportsCommittedReads) {
|
||||
const awaitCheckpointer = function(timestamp) {
|
||||
assert.soon(
|
||||
() => {
|
||||
const primaryTimestamp =
|
||||
assert.commandWorked(primary.adminCommand({replSetGetStatus: 1}))
|
||||
.lastStableRecoveryTimestamp;
|
||||
const secondaryTimestamp =
|
||||
assert.commandWorked(secondary.adminCommand({replSetGetStatus: 1}))
|
||||
.lastStableRecoveryTimestamp;
|
||||
jsTestLog("Awaiting last stable recovery timestamp " +
|
||||
`(primary: ${tojson(primaryTimestamp)}, secondary: ${
|
||||
tojson(secondaryTimestamp)}) ` +
|
||||
`target: ${tojson(timestamp)}`);
|
||||
return ((timestampCmp(primaryTimestamp, timestamp) >= 0) &&
|
||||
(timestampCmp(secondaryTimestamp, timestamp) >= 0));
|
||||
},
|
||||
"Timeout waiting for checkpointing to catch up",
|
||||
ReplSetTest.kDefaultTimeoutMS,
|
||||
2000);
|
||||
};
|
||||
|
||||
// Wait for checkpointing/stable timestamp to catch up with the second insert so oplog
|
||||
// entry of the first insert is allowed to be deleted by the oplog cap maintainer thread
|
||||
// when a new oplog stone is created. "inMemory" WT engine does not run checkpoint
|
||||
// thread and lastStableRecoveryTimestamp is the stable timestamp in this case.
|
||||
awaitCheckpointer(secondInsertTimestamp);
|
||||
|
||||
// Insert the third document which will trigger a new oplog stone to be created. The
|
||||
// oplog cap maintainer thread will then be unblocked on the creation of the new oplog
|
||||
// stone and will start truncating oplog entries. The oplog entry for the first
|
||||
// insert will be truncated after the oplog cap maintainer thread finishes.
|
||||
const thirdInsertTimestamp =
|
||||
assert
|
||||
.commandWorked(coll.runCommand(
|
||||
"insert",
|
||||
{documents: [{_id: 2, longString: longString}], writeConcern: {w: 2}}))
|
||||
.operationTime;
|
||||
jsTestLog("Third insert timestamp: " + tojson(thirdInsertTimestamp));
|
||||
|
||||
// There is a race between how we calculate the pinnedOplog and checkpointing. The timestamp
|
||||
// of the pinnedOplog could be less than the actual stable timestamp used in a checkpoint.
|
||||
// Wait for the checkpointer to run for another round to make sure the first insert oplog is
|
||||
// not pinned.
|
||||
awaitCheckpointer(thirdInsertTimestamp);
|
||||
|
||||
// Verify that there are three oplog entries while the oplog cap maintainer thread is
|
||||
// paused.
|
||||
assert.eq(3, numInsertOplogEntry(primaryOplog));
|
||||
assert.eq(3, numInsertOplogEntry(secondaryOplog));
|
||||
|
||||
// Let the oplog cap maintainer thread start truncating the oplog.
|
||||
assert.commandWorked(primary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
assert.commandWorked(secondary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
|
||||
// Test that oplog entry of the initial insert rolls over on both primary and secondary.
|
||||
// Use assert.soon to wait for oplog cap maintainer thread to run.
|
||||
assert.soon(() => {
|
||||
return numInsertOplogEntry(primaryOplog) === 2;
|
||||
}, "Timeout waiting for oplog to roll over on primary");
|
||||
assert.soon(() => {
|
||||
return numInsertOplogEntry(secondaryOplog) === 2;
|
||||
}, "Timeout waiting for oplog to roll over on secondary");
|
||||
|
||||
const res = primary.getDB("test").runCommand({serverStatus: 1});
|
||||
assert.commandWorked(res);
|
||||
assert.eq(res.oplogTruncation.truncateCount, 1, tojson(res.oplogTruncation));
|
||||
assert.gt(res.oplogTruncation.totalTimeTruncatingMicros, 0, tojson(res.oplogTruncation));
|
||||
} else {
|
||||
// Let the oplog cap maintainer thread start truncating the oplog.
|
||||
assert.commandWorked(primary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
assert.commandWorked(secondary.adminCommand(
|
||||
{configureFailPoint: "hangOplogCapMaintainerThread", mode: "off"}));
|
||||
|
||||
// Only test that oplog truncation will eventually happen.
|
||||
let numInserted = 2;
|
||||
assert.soon(function() {
|
||||
// Insert more documents.
|
||||
assert.commandWorked(
|
||||
coll.insert({_id: numInserted++, longString: longString}, {writeConcern: {w: 2}}));
|
||||
const numInsertOplogEntryPrimary = numInsertOplogEntry(primaryOplog);
|
||||
const numInsertOplogEntrySecondary = numInsertOplogEntry(secondaryOplog);
|
||||
// Oplog has been truncated if the number of insert oplog entries is less than
|
||||
// number of inserted.
|
||||
if (numInsertOplogEntryPrimary < numInserted &&
|
||||
numInsertOplogEntrySecondary < numInserted)
|
||||
return true;
|
||||
jsTestLog("Awaiting oplog truncation: number of oplog entries: " +
|
||||
`(primary: ${tojson(numInsertOplogEntryPrimary)}, ` +
|
||||
`secondary: ${tojson(numInsertOplogEntrySecondary)}) ` +
|
||||
`number inserted: ${numInserted}`);
|
||||
return false;
|
||||
}, "Timeout waiting for oplog to roll over", ReplSetTest.kDefaultTimeoutMS, 1000);
|
||||
}
|
||||
|
||||
replSet.stopSet();
|
||||
}
|
||||
|
||||
doTest("wiredTiger");
|
||||
oplogRolloverTest("wiredTiger");
|
||||
|
||||
if (jsTest.options().storageEngine !== "inMemory") {
|
||||
jsTestLog(
|
||||
@ -194,5 +15,5 @@ if (jsTest.options().storageEngine !== "inMemory") {
|
||||
return;
|
||||
}
|
||||
|
||||
doTest("inMemory");
|
||||
oplogRolloverTest("inMemory");
|
||||
})();
|
||||
|
||||
@ -5,6 +5,8 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
load("jstests/replsets/rslib.js");
|
||||
|
||||
// Skip db hash check because secondary is left with a different config.
|
||||
TestData.skipCheckDBHashes = true;
|
||||
|
||||
@ -33,6 +35,10 @@ const lastOp = primaryOplog.find(expectedNoOp).sort({'$natural': -1}).limit(1).t
|
||||
assert(lastOp.length > 0);
|
||||
replTest.awaitReplication();
|
||||
|
||||
// Make sure that all nodes have installed the config before moving on.
|
||||
replTest.waitForConfigReplication(primary, nodes);
|
||||
assert.soonNoExcept(() => isConfigCommitted(primary));
|
||||
|
||||
jsTestLog("Invalid reconfig");
|
||||
config.version++;
|
||||
var badMember = {_id: numNodes, host: "localhost:12345", priority: "High"};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user