Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
015a334997 | ||
|
|
b6e0525d6d | ||
|
|
c72363a277 | ||
|
|
1d5760aab2 | ||
|
|
5f3a51a85a | ||
|
|
564b1554dc | ||
|
|
c8980c27cc | ||
|
|
619c6e89ea | ||
|
|
2af7b4b1a2 | ||
|
|
3c29923bb5 | ||
|
|
59018abb95 | ||
|
|
14b1ea6d58 | ||
|
|
9d71aeee1e | ||
|
|
6d0624e75a | ||
|
|
cf6bde6ffd | ||
|
|
597ebc2163 | ||
|
|
ed2177d7d5 | ||
|
|
ae3b498a0a | ||
|
|
0d202a5f19 | ||
|
|
d5004b929a | ||
|
|
832151ec17 | ||
|
|
d4297ee553 | ||
|
|
e8ed64ed04 | ||
|
|
6424b1f1f1 | ||
|
|
3217b54dde | ||
|
|
763a3efc81 | ||
|
|
cc34fa291c | ||
|
|
05e07fbe36 | ||
|
|
53fbcb16c2 | ||
|
|
3c4ff87b9f | ||
|
|
8f1e14b436 | ||
|
|
f0aa22b7dd | ||
|
|
e2023e4001 | ||
|
|
8a86f72b69 | ||
|
|
7cda3b8762 | ||
|
|
adbe39ee1e | ||
|
|
45a16968d4 | ||
|
|
08344712d5 | ||
|
|
bf56db18c0 | ||
|
|
5c952f408e | ||
|
|
63d2ed6429 | ||
|
|
123941cc37 | ||
|
|
eac2f0e5cd | ||
|
|
ae9ebae348 | ||
|
|
ccc7ca852e | ||
|
|
dd101c2823 | ||
|
|
1446eda8a1 | ||
|
|
f01a90978a | ||
|
|
907fc9e6cd | ||
|
|
c4cdb28205 | ||
|
|
b74a7fa06a | ||
|
|
f83bf6c4be | ||
|
|
ca0b75fbe2 | ||
|
|
afbc967e8c | ||
|
|
fd61bf1c37 | ||
|
|
6863b64003 | ||
|
|
cd67916e3d | ||
|
|
63c5901e2b | ||
|
|
24143f27c9 | ||
|
|
b06609850b | ||
|
|
a5a26be2e8 | ||
|
|
2f7eb41c22 | ||
|
|
22450cd55f | ||
|
|
0eac00554b | ||
|
|
4e215cded5 | ||
|
|
3b86f77155 | ||
|
|
7ce0332a70 | ||
|
|
8e62da90c4 | ||
|
|
fd671b6b1e | ||
|
|
7182f9d06e | ||
|
|
a58b2debd6 | ||
|
|
eb45396105 | ||
|
|
a29518c420 | ||
|
|
c72e1a93ce | ||
|
|
cf72acd7f8 | ||
|
|
daaff3b144 | ||
|
|
0c4e155bf4 | ||
|
|
c91d35a92b | ||
|
|
20504d1d14 | ||
|
|
559d9f496e | ||
|
|
da44564ea5 | ||
|
|
96cdc44b67 | ||
|
|
e810b3d11d | ||
|
|
004a674a84 | ||
|
|
69c423f89e | ||
|
|
bc94a2eaad | ||
|
|
e727f8d673 | ||
|
|
c972dc2930 | ||
|
|
e678887c8d | ||
|
|
15b767ef70 | ||
|
|
4e18d134ba | ||
|
|
4f43d4cfdd | ||
|
|
b809265967 | ||
|
|
c001aaccbf | ||
|
|
b5d984a43a | ||
|
|
57054f63b1 | ||
|
|
a73c751ddd | ||
|
|
5e5a1d5d90 | ||
|
|
cf68cf1fdc |
@ -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.
|
||||
|
||||
|
||||
213
SConstruct
213
SConstruct
@ -3,6 +3,7 @@
|
||||
import atexit
|
||||
import copy
|
||||
import errno
|
||||
import functools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@ -1016,17 +1017,10 @@ env_vars.Add(
|
||||
converter=variable_shlex_converter,
|
||||
)
|
||||
|
||||
default_destdir = '$BUILD_ROOT/install'
|
||||
if get_option('ninja') != 'disabled':
|
||||
# Workaround for SERVER-53952 where issues wih different
|
||||
# ninja files building to the same install dir. Different
|
||||
# ninja files need to build to different install dirs.
|
||||
default_destdir = '$BUILD_DIR/install'
|
||||
|
||||
env_vars.Add(
|
||||
'DESTDIR',
|
||||
help='Where builds will install files',
|
||||
default=default_destdir,
|
||||
default='$BUILD_ROOT/install',
|
||||
)
|
||||
|
||||
env_vars.Add(
|
||||
@ -1220,7 +1214,7 @@ env_vars.Add(
|
||||
env_vars.Add(
|
||||
'NINJA_BUILDDIR',
|
||||
help="Location for shared Ninja state",
|
||||
default="$BUILD_DIR/ninja",
|
||||
default="$BUILD_ROOT/ninja",
|
||||
)
|
||||
|
||||
env_vars.Add(
|
||||
@ -2486,6 +2480,15 @@ for suboption in get_option('experimental-runtime-hardening'):
|
||||
elif suboption.startswith('+'):
|
||||
selected_experimental_runtime_hardenings.add(suboption[1:])
|
||||
|
||||
# Disable floating-point contractions such as forming of fused multiply-add operations.
|
||||
if env.ToolchainIs('clang', 'gcc'):
|
||||
env.Append(CCFLAGS=["-ffp-contract=off"])
|
||||
else:
|
||||
# msvc defaults to /fp:precise. Visual Studio 2022 does not emit floating-point contractions
|
||||
# with /fp:precise, but previous versions can. Disable contractions altogether by using
|
||||
# /fp:strict.
|
||||
env.Append(CCFLAGS=["/fp:strict"])
|
||||
|
||||
if env.TargetOSIs('linux'):
|
||||
env.Append(LIBS=["m"])
|
||||
if not env.TargetOSIs('android'):
|
||||
@ -3251,7 +3254,7 @@ def doConfigure(myenv):
|
||||
|
||||
conf.Finish()
|
||||
|
||||
def AddFlagIfSupported(env, tool, extension, flag, link, **mutation):
|
||||
def CheckFlag(env, flag, tool, extension, link, **mutation):
|
||||
def CheckFlagTest(context, tool, extension, flag):
|
||||
if link:
|
||||
if tool == 'C':
|
||||
@ -3327,34 +3330,33 @@ def doConfigure(myenv):
|
||||
)
|
||||
available = conf.CheckFlag()
|
||||
conf.Finish()
|
||||
return available
|
||||
|
||||
def AddFlagIfSupported(env, flag, tool, extension, link, **mutation):
|
||||
|
||||
available = CheckFlag(env, flag, tool, extension, link, **mutation)
|
||||
|
||||
if available:
|
||||
env.Append(**mutation)
|
||||
return available
|
||||
|
||||
def AddToCFLAGSIfSupported(env, flag):
|
||||
return AddFlagIfSupported(env, 'C', '.c', flag, False, CFLAGS=[flag])
|
||||
conf_check_vars = {
|
||||
'CFLAGS': {'tool': 'C', 'extension': '.c', 'link': False},
|
||||
'CCFLAGS': {'tool': 'C', 'extension': '.c', 'link': False},
|
||||
'CXXFLAGS': {'tool': 'C++', 'extension': '.cpp', 'link': False},
|
||||
'LINKFLAGS': {'tool': 'C', 'extension': '.c', 'link': True},
|
||||
'SHLINKFLAGS': {'tool': 'C', 'extension': '.c', 'link': True},
|
||||
}
|
||||
|
||||
env.AddMethod(AddToCFLAGSIfSupported)
|
||||
def var_func(env, flag, var, func):
|
||||
kwargs = dict({var: [flag]}, **conf_check_vars[var])
|
||||
return func(env, flag, **kwargs)
|
||||
|
||||
def AddToCCFLAGSIfSupported(env, flag):
|
||||
return AddFlagIfSupported(env, 'C', '.c', flag, False, CCFLAGS=[flag])
|
||||
|
||||
env.AddMethod(AddToCCFLAGSIfSupported)
|
||||
|
||||
def AddToCXXFLAGSIfSupported(env, flag):
|
||||
return AddFlagIfSupported(env, 'C++', '.cpp', flag, False, CXXFLAGS=[flag])
|
||||
|
||||
env.AddMethod(AddToCXXFLAGSIfSupported)
|
||||
|
||||
def AddToLINKFLAGSIfSupported(env, flag):
|
||||
return AddFlagIfSupported(env, 'C', '.c', flag, True, LINKFLAGS=[flag])
|
||||
|
||||
env.AddMethod(AddToLINKFLAGSIfSupported)
|
||||
|
||||
def AddToSHLINKFLAGSIfSupported(env, flag):
|
||||
return AddFlagIfSupported(env, 'C', '.c', flag, True, SHLINKFLAGS=[flag])
|
||||
|
||||
env.AddMethod(AddToSHLINKFLAGSIfSupported)
|
||||
for var in conf_check_vars:
|
||||
myenv.AddMethod(
|
||||
functools.partial(var_func, var=var, func=AddFlagIfSupported), f"AddTo{var}IfSupported")
|
||||
myenv.AddMethod(
|
||||
functools.partial(var_func, var=var, func=CheckFlag), f"Check{var}Supported")
|
||||
|
||||
if myenv.ToolchainIs('gcc', 'clang'):
|
||||
# This tells clang/gcc to use the gold linker if it is available - we prefer the gold linker
|
||||
@ -3378,20 +3380,20 @@ def doConfigure(myenv):
|
||||
#
|
||||
# We should revisit all of these issues the next time we upgrade our clang minimum.
|
||||
if get_option('separate-debug') == 'off' and get_option('link-model') != 'dynamic':
|
||||
if not AddToLINKFLAGSIfSupported(myenv, '-fuse-ld=lld'):
|
||||
AddToLINKFLAGSIfSupported(myenv, '-fuse-ld=gold')
|
||||
if not myenv.AddToLINKFLAGSIfSupported('-fuse-ld=lld'):
|
||||
myenv.AddToLINKFLAGSIfSupported('-fuse-ld=gold')
|
||||
else:
|
||||
AddToLINKFLAGSIfSupported(myenv, '-fuse-ld=gold')
|
||||
myenv.AddToLINKFLAGSIfSupported('-fuse-ld=gold')
|
||||
elif link_model.startswith("dynamic") and linker_ld == 'bfd':
|
||||
# BFD is not supported due to issues with it causing warnings from some of
|
||||
# the third party libraries that mongodb is linked with:
|
||||
# https://jira.mongodb.org/browse/SERVER-49465
|
||||
myenv.FatalError(f"Linker {linker_ld} is not supported with dynamic link model builds.")
|
||||
else:
|
||||
if not AddToLINKFLAGSIfSupported(myenv, f'-fuse-ld={linker_ld}'):
|
||||
if not myenv.AddToLINKFLAGSIfSupported(f'-fuse-ld={linker_ld}'):
|
||||
myenv.FatalError(f"Linker {linker_ld} could not be configured.")
|
||||
|
||||
if has_option('gcov') and AddToCCFLAGSIfSupported(myenv, '-fprofile-update=single'):
|
||||
if has_option('gcov') and myenv.AddToCCFLAGSIfSupported('-fprofile-update=single'):
|
||||
myenv.AppendUnique(LINKFLAGS=['-fprofile-update=single'])
|
||||
|
||||
detectCompiler = Configure(
|
||||
@ -3424,11 +3426,11 @@ def doConfigure(myenv):
|
||||
|
||||
if myenv.ToolchainIs('clang', 'gcc'):
|
||||
# This warning was added in g++-4.8.
|
||||
AddToCCFLAGSIfSupported(myenv, '-Wno-unused-local-typedefs')
|
||||
myenv.AddToCCFLAGSIfSupported('-Wno-unused-local-typedefs')
|
||||
|
||||
# Clang likes to warn about unused functions, which seems a tad aggressive and breaks
|
||||
# -Werror, which we want to be able to use.
|
||||
AddToCCFLAGSIfSupported(myenv, '-Wno-unused-function')
|
||||
myenv.AddToCCFLAGSIfSupported('-Wno-unused-function')
|
||||
|
||||
# TODO: Note that the following two flags are added to CCFLAGS even though they are
|
||||
# really C++ specific. We need to do this because SCons passes CXXFLAGS *before*
|
||||
@ -3438,79 +3440,79 @@ def doConfigure(myenv):
|
||||
#
|
||||
# Clang likes to warn about unused private fields, but some of our third_party
|
||||
# libraries have such things.
|
||||
AddToCCFLAGSIfSupported(myenv, '-Wno-unused-private-field')
|
||||
myenv.AddToCCFLAGSIfSupported('-Wno-unused-private-field')
|
||||
|
||||
# Prevents warning about using deprecated features (such as auto_ptr in c++11)
|
||||
# Using -Wno-error=deprecated-declarations does not seem to work on some compilers,
|
||||
# including at least g++-4.6.
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-deprecated-declarations")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-deprecated-declarations")
|
||||
|
||||
# As of clang-3.4, this warning appears in v8, and gets escalated to an error.
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-tautological-constant-out-of-range-compare")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-tautological-constant-out-of-range-compare")
|
||||
|
||||
# As of clang in Android NDK 17, these warnings appears in boost and/or ICU, and get escalated to errors
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-tautological-constant-compare")
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-tautological-unsigned-zero-compare")
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-tautological-unsigned-enum-zero-compare")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-tautological-constant-compare")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-tautological-unsigned-zero-compare")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-tautological-unsigned-enum-zero-compare")
|
||||
|
||||
# New in clang-3.4, trips up things mostly in third_party, but in a few places in the
|
||||
# primary mongo sources as well.
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-unused-const-variable")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-unused-const-variable")
|
||||
|
||||
# Prevents warning about unused but set variables found in boost version 1.49
|
||||
# in boost/date_time/format_date_parser.hpp which does not work for compilers
|
||||
# GCC >= 4.6. Error explained in https://svn.boost.org/trac/boost/ticket/6136 .
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-unused-but-set-variable")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-unused-but-set-variable")
|
||||
|
||||
# This has been suppressed in gcc 4.8, due to false positives, but not in clang. So
|
||||
# we explicitly disable it here.
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-missing-braces")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-missing-braces")
|
||||
|
||||
# Suppress warnings about not consistently using override everywhere in a class. It seems
|
||||
# very pedantic, and we have a fair number of instances.
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-inconsistent-missing-override")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-inconsistent-missing-override")
|
||||
|
||||
# Don't issue warnings about potentially evaluated expressions
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-potentially-evaluated-expression")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-potentially-evaluated-expression")
|
||||
|
||||
# Warn about moves of prvalues, which can inhibit copy elision.
|
||||
AddToCXXFLAGSIfSupported(myenv, "-Wpessimizing-move")
|
||||
myenv.AddToCXXFLAGSIfSupported("-Wpessimizing-move")
|
||||
|
||||
# Disable warning about variables that may not be initialized
|
||||
# Failures are triggered in the case of boost::optional in GCC 4.8.x
|
||||
# TODO: re-evaluate when we move to GCC 5.3
|
||||
# see: http://stackoverflow.com/questions/21755206/how-to-get-around-gcc-void-b-4-may-be-used-uninitialized-in-this-funct
|
||||
AddToCXXFLAGSIfSupported(myenv, "-Wno-maybe-uninitialized")
|
||||
myenv.AddToCXXFLAGSIfSupported("-Wno-maybe-uninitialized")
|
||||
|
||||
# Disable warning about templates that can't be implicitly instantiated. It is an attempt to
|
||||
# make a link error into an easier-to-debug compiler failure, but it triggers false
|
||||
# positives if explicit instantiation is used in a TU that can see the full definition. This
|
||||
# is a problem at least for the S2 headers.
|
||||
AddToCXXFLAGSIfSupported(myenv, "-Wno-undefined-var-template")
|
||||
myenv.AddToCXXFLAGSIfSupported("-Wno-undefined-var-template")
|
||||
|
||||
# This warning was added in clang-4.0, but it warns about code that is required on some
|
||||
# platforms. Since the warning just states that 'explicit instantiation of [a template] that
|
||||
# occurs after an explicit specialization has no effect', it is harmless on platforms where
|
||||
# it isn't required
|
||||
AddToCXXFLAGSIfSupported(myenv, "-Wno-instantiation-after-specialization")
|
||||
myenv.AddToCXXFLAGSIfSupported("-Wno-instantiation-after-specialization")
|
||||
|
||||
# This warning was added in clang-5 and flags many of our lambdas. Since it isn't actively
|
||||
# harmful to capture unused variables we are suppressing for now with a plan to fix later.
|
||||
AddToCCFLAGSIfSupported(myenv, "-Wno-unused-lambda-capture")
|
||||
myenv.AddToCCFLAGSIfSupported("-Wno-unused-lambda-capture")
|
||||
|
||||
# Enable sized deallocation support.
|
||||
AddToCXXFLAGSIfSupported(myenv, '-fsized-deallocation')
|
||||
myenv.AddToCXXFLAGSIfSupported('-fsized-deallocation')
|
||||
|
||||
# This warning was added in Apple clang version 11 and flags many explicitly defaulted move
|
||||
# constructors and assignment operators for being implicitly deleted, which is not useful.
|
||||
AddToCXXFLAGSIfSupported(myenv, "-Wno-defaulted-function-deleted")
|
||||
myenv.AddToCXXFLAGSIfSupported("-Wno-defaulted-function-deleted")
|
||||
|
||||
# SERVER-44856: Our windows builds complain about unused
|
||||
# exception parameters, but GCC and clang don't seem to do
|
||||
# that for us automatically. In the interest of making it more
|
||||
# likely to catch these errors early, add the (currently clang
|
||||
# only) flag that turns it on.
|
||||
AddToCXXFLAGSIfSupported(myenv, "-Wunused-exception-parameter")
|
||||
myenv.AddToCXXFLAGSIfSupported("-Wunused-exception-parameter")
|
||||
|
||||
# TODO(SERVER-60151): Avoid the dilemma identified in
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100493. Unfortunately,
|
||||
@ -3522,7 +3524,7 @@ def doConfigure(myenv):
|
||||
# TODO(SERVER-60175): In fact we will want to explicitly opt
|
||||
# in to -Wdeprecated, since clang doesn't include it in -Wall.
|
||||
if get_option('cxx-std') == "20":
|
||||
AddToCXXFLAGSIfSupported(myenv, '-Wno-deprecated')
|
||||
myenv.AddToCXXFLAGSIfSupported('-Wno-deprecated')
|
||||
|
||||
# Check if we can set "-Wnon-virtual-dtor" when "-Werror" is set. The only time we can't set it is on
|
||||
# clang 3.4, where a class with virtual function(s) and a non-virtual destructor throws a warning when
|
||||
@ -3568,18 +3570,18 @@ def doConfigure(myenv):
|
||||
# by -Wall), in order to enforce that -mXXX-version-min=YYY
|
||||
# will enforce that you don't use APIs from ZZZ.
|
||||
if env.TargetOSIs('darwin'):
|
||||
AddToCCFLAGSIfSupported(env, '-Wunguarded-availability')
|
||||
env.AddToCCFLAGSIfSupported('-Wunguarded-availability')
|
||||
|
||||
if get_option('runtime-hardening') == "on":
|
||||
# Enable 'strong' stack protection preferentially, but fall back to 'all' if it is not
|
||||
# available. Note that we need to add these to the LINKFLAGS as well, since otherwise we
|
||||
# might not link libssp when we need to (see SERVER-12456).
|
||||
if myenv.ToolchainIs('gcc', 'clang'):
|
||||
if AddToCCFLAGSIfSupported(myenv, '-fstack-protector-strong'):
|
||||
if myenv.AddToCCFLAGSIfSupported('-fstack-protector-strong'):
|
||||
myenv.Append(LINKFLAGS=[
|
||||
'-fstack-protector-strong',
|
||||
], )
|
||||
elif AddToCCFLAGSIfSupported(myenv, '-fstack-protector-all'):
|
||||
elif myenv.AddToCCFLAGSIfSupported('-fstack-protector-all'):
|
||||
myenv.Append(LINKFLAGS=[
|
||||
'-fstack-protector-all',
|
||||
], )
|
||||
@ -3590,10 +3592,10 @@ def doConfigure(myenv):
|
||||
], )
|
||||
|
||||
if 'stackclash' in selected_experimental_runtime_hardenings:
|
||||
AddToCCFLAGSIfSupported(myenv, "-fstack-clash-protection")
|
||||
myenv.AddToCCFLAGSIfSupported("-fstack-clash-protection")
|
||||
|
||||
if 'controlflow' in selected_experimental_runtime_hardenings:
|
||||
AddToCCFLAGSIfSupported(myenv, "-fcf-protection=full")
|
||||
myenv.AddToCCFLAGSIfSupported("-fcf-protection=full")
|
||||
|
||||
if myenv.ToolchainIs('clang'):
|
||||
# TODO: There are several interesting things to try here, but they each have
|
||||
@ -3636,7 +3638,7 @@ def doConfigure(myenv):
|
||||
if has_option('libc++'):
|
||||
if not myenv.ToolchainIs('clang'):
|
||||
myenv.FatalError('libc++ is currently only supported for clang')
|
||||
if AddToCXXFLAGSIfSupported(myenv, '-stdlib=libc++'):
|
||||
if myenv.AddToCXXFLAGSIfSupported('-stdlib=libc++'):
|
||||
myenv.Append(LINKFLAGS=['-stdlib=libc++'])
|
||||
else:
|
||||
myenv.ConfError('libc++ requested, but compiler does not support -stdlib=libc++')
|
||||
@ -3673,13 +3675,13 @@ def doConfigure(myenv):
|
||||
myenv.AppendUnique(CCFLAGS=['/std:c++20'])
|
||||
else:
|
||||
if get_option('cxx-std') == "17":
|
||||
if not AddToCXXFLAGSIfSupported(myenv, '-std=c++17'):
|
||||
if not myenv.AddToCXXFLAGSIfSupported('-std=c++17'):
|
||||
myenv.ConfError('Compiler does not honor -std=c++17')
|
||||
elif get_option('cxx-std') == "20":
|
||||
if not AddToCXXFLAGSIfSupported(myenv, '-std=c++20'):
|
||||
if not myenv.AddToCXXFLAGSIfSupported('-std=c++20'):
|
||||
myenv.ConfError('Compiler does not honor -std=c++20')
|
||||
|
||||
if not AddToCFLAGSIfSupported(myenv, '-std=c11'):
|
||||
if not myenv.AddToCFLAGSIfSupported('-std=c11'):
|
||||
myenv.ConfError("C++17 mode selected for C++ files, but can't enable C11 for C files")
|
||||
|
||||
if using_system_version_of_cxx_libraries():
|
||||
@ -4002,7 +4004,7 @@ def doConfigure(myenv):
|
||||
|
||||
sanitizer_option = '-fsanitize=' + ','.join(sanitizer_list)
|
||||
|
||||
if AddToCCFLAGSIfSupported(myenv, sanitizer_option):
|
||||
if myenv.AddToCCFLAGSIfSupported(sanitizer_option):
|
||||
myenv.Append(LINKFLAGS=[sanitizer_option])
|
||||
myenv.Append(CCFLAGS=['-fno-omit-frame-pointer'])
|
||||
else:
|
||||
@ -4013,7 +4015,7 @@ def doConfigure(myenv):
|
||||
if has_option('sanitize-coverage') and using_fsan:
|
||||
sanitize_coverage_list = get_option('sanitize-coverage')
|
||||
sanitize_coverage_option = '-fsanitize-coverage=' + sanitize_coverage_list
|
||||
if AddToCCFLAGSIfSupported(myenv, sanitize_coverage_option):
|
||||
if myenv.AddToCCFLAGSIfSupported(sanitize_coverage_option):
|
||||
myenv.Append(LINKFLAGS=[sanitize_coverage_option])
|
||||
else:
|
||||
myenv.ConfError('Failed to enable -fsanitize-coverage with flag: {0}',
|
||||
@ -4036,7 +4038,7 @@ def doConfigure(myenv):
|
||||
supportedDenyfiles = []
|
||||
denyfilesTestEnv = myenv.Clone()
|
||||
for denyfile in denyfiles:
|
||||
if AddToCCFLAGSIfSupported(denyfilesTestEnv, f"-fsanitize-blacklist={denyfile}"):
|
||||
if denyfilesTestEnv.AddToCCFLAGSIfSupported(f"-fsanitize-blacklist={denyfile}"):
|
||||
supportedDenyfiles.append(denyfile)
|
||||
denyfilesTestEnv = None
|
||||
supportedDenyfiles = sorted(supportedDenyfiles)
|
||||
@ -4219,8 +4221,8 @@ def doConfigure(myenv):
|
||||
# have renamed the flag.
|
||||
# However, this flag cannot be included when using the fuzzer sanitizer
|
||||
# if we want to suppress errors to uncover new ones.
|
||||
if not using_fsan and not AddToCCFLAGSIfSupported(myenv, "-fno-sanitize-recover"):
|
||||
AddToCCFLAGSIfSupported(myenv, "-fno-sanitize-recover=undefined")
|
||||
if not using_fsan and not myenv.AddToCCFLAGSIfSupported("-fno-sanitize-recover"):
|
||||
myenv.AddToCCFLAGSIfSupported("-fno-sanitize-recover=undefined")
|
||||
myenv.AppendUnique(CPPDEFINES=['UNDEFINED_BEHAVIOR_SANITIZER'])
|
||||
|
||||
# If anything is changed, added, or removed in ubsan_options, be
|
||||
@ -4242,7 +4244,7 @@ def doConfigure(myenv):
|
||||
# same as the correct link graph for a regular dynamic
|
||||
# build.
|
||||
if link_model == "dynamic":
|
||||
if AddToCCFLAGSIfSupported(myenv, "-fno-sanitize=vptr"):
|
||||
if myenv.AddToCCFLAGSIfSupported("-fno-sanitize=vptr"):
|
||||
myenv.AppendUnique(LINKFLAGS=["-fno-sanitize=vptr"])
|
||||
|
||||
if myenv.ToolchainIs('msvc') and optBuild != "off":
|
||||
@ -4259,21 +4261,28 @@ def doConfigure(myenv):
|
||||
# Usually, --gdb-index is too expensive in big static binaries, but for dynamic
|
||||
# builds it works well.
|
||||
if link_model.startswith("dynamic"):
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,--gdb-index')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,--gdb-index')
|
||||
|
||||
if link_model != 'dynamic':
|
||||
# This will create an extra section where debug types can be referred from,
|
||||
# reducing other section sizes. This helps most with big static links as there
|
||||
# will be lots of duplicate debug type info.
|
||||
myenv.AddToCCFLAGSIfSupported('-fdebug-types-section')
|
||||
myenv.AddToLINKFLAGSIfSupported('-fdebug-types-section')
|
||||
|
||||
# Our build is already parallel.
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,--no-threads')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,--no-threads')
|
||||
|
||||
# Explicitly enable GNU build id's if the linker supports it.
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,--build-id')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,--build-id')
|
||||
|
||||
# Explicitly use the new gnu hash section if the linker offers
|
||||
# it, except on android since older runtimes seem to not
|
||||
# support it. For that platform, use 'both'.
|
||||
if env.TargetOSIs('android'):
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,--hash-style=both')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,--hash-style=both')
|
||||
else:
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,--hash-style=gnu')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,--hash-style=gnu')
|
||||
|
||||
# Try to have the linker tell us about ODR violations. Don't
|
||||
# use it when using clang with libstdc++, as libstdc++ was
|
||||
@ -4288,17 +4297,17 @@ def doConfigure(myenv):
|
||||
env.FatalError(
|
||||
'The --detect-odr-violations flag is expected to only be reliable with --opt=off'
|
||||
)
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,--detect-odr-violations')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,--detect-odr-violations')
|
||||
|
||||
# Disallow an executable stack. Also, issue a warning if any files are found that would
|
||||
# cause the stack to become executable if the noexecstack flag was not in play, so that we
|
||||
# can find them and fix them. We do this here after we check for ld.gold because the
|
||||
# --warn-execstack is currently only offered with gold.
|
||||
AddToLINKFLAGSIfSupported(myenv, "-Wl,-z,noexecstack")
|
||||
AddToLINKFLAGSIfSupported(myenv, "-Wl,--warn-execstack")
|
||||
myenv.AddToLINKFLAGSIfSupported("-Wl,-z,noexecstack")
|
||||
myenv.AddToLINKFLAGSIfSupported("-Wl,--warn-execstack")
|
||||
|
||||
# If possible with the current linker, mark relocations as read-only.
|
||||
AddToLINKFLAGSIfSupported(myenv, "-Wl,-z,relro")
|
||||
myenv.AddToLINKFLAGSIfSupported("-Wl,-z,relro")
|
||||
|
||||
# As far as we know these flags only apply on posix-y systems,
|
||||
# and not on Darwin.
|
||||
@ -4331,11 +4340,9 @@ def doConfigure(myenv):
|
||||
compress_type = "zlib-gabi"
|
||||
compress_flag = "compress-debug-sections"
|
||||
|
||||
AddToCCFLAGSIfSupported(
|
||||
myenv,
|
||||
myenv.AddToCCFLAGSIfSupported(
|
||||
f"-Wa,--{compress_flag}={compress_type}"
|
||||
if "as" in debug_compress else f"-Wa,--no{compress_flag}",
|
||||
)
|
||||
if "as" in debug_compress else f"-Wa,--no{compress_flag}", )
|
||||
|
||||
# We shouldn't enable debug compression in the linker
|
||||
# (meaning our final binaries contain compressed debug
|
||||
@ -4371,22 +4378,16 @@ def doConfigure(myenv):
|
||||
conf.Finish()
|
||||
|
||||
if have_shf_compressed and 'ld' in debug_compress:
|
||||
AddToLINKFLAGSIfSupported(
|
||||
myenv,
|
||||
f"-Wl,--{compress_flag}={compress_type}",
|
||||
)
|
||||
myenv.AddToLINKFLAGSIfSupported(f"-Wl,--{compress_flag}={compress_type}", )
|
||||
else:
|
||||
AddToLINKFLAGSIfSupported(
|
||||
myenv,
|
||||
f"-Wl,--{compress_flag}=none",
|
||||
)
|
||||
myenv.AddToLINKFLAGSIfSupported(f"-Wl,--{compress_flag}=none", )
|
||||
|
||||
if "fnsi" in selected_experimental_optimizations:
|
||||
AddToCCFLAGSIfSupported(myenv, "-fno-semantic-interposition")
|
||||
myenv.AddToCCFLAGSIfSupported("-fno-semantic-interposition")
|
||||
|
||||
# Avoid deduping symbols on OS X debug builds, as it takes a long time.
|
||||
if optBuild == "off" and myenv.ToolchainIs('clang') and env.TargetOSIs('darwin'):
|
||||
AddToLINKFLAGSIfSupported(myenv, "-Wl,-no_deduplicate")
|
||||
myenv.AddToLINKFLAGSIfSupported("-Wl,-no_deduplicate")
|
||||
|
||||
# Apply any link time optimization settings as selected by the 'lto' option.
|
||||
if has_option('lto'):
|
||||
@ -4403,13 +4404,13 @@ def doConfigure(myenv):
|
||||
elif myenv.ToolchainIs('gcc', 'clang'):
|
||||
# For GCC and clang, the flag is -flto, and we need to pass it both on the compile
|
||||
# and link lines.
|
||||
if not AddToCCFLAGSIfSupported(myenv, '-flto') or \
|
||||
not AddToLINKFLAGSIfSupported(myenv, '-flto'):
|
||||
if not myenv.AddToCCFLAGSIfSupported('-flto') or \
|
||||
not myenv.AddToLINKFLAGSIfSupported('-flto'):
|
||||
myenv.ConfError("Link time optimization requested, "
|
||||
"but selected compiler does not honor -flto")
|
||||
|
||||
if myenv.TargetOSIs('darwin'):
|
||||
AddToLINKFLAGSIfSupported(myenv, '-Wl,-object_path_lto,${TARGET}.lto')
|
||||
myenv.AddToLINKFLAGSIfSupported('-Wl,-object_path_lto,${TARGET}.lto')
|
||||
|
||||
else:
|
||||
myenv.ConfError("Don't know how to enable --lto on current toolchain")
|
||||
@ -4525,7 +4526,7 @@ def doConfigure(myenv):
|
||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=43052
|
||||
if myenv.ToolchainIs('gcc') and (env['TARGET_ARCH'] in ['i386', 'x86_64']):
|
||||
if not 'builtin-memcmp' in selected_experimental_optimizations:
|
||||
AddToCCFLAGSIfSupported(myenv, '-fno-builtin-memcmp')
|
||||
myenv.AddToCCFLAGSIfSupported('-fno-builtin-memcmp')
|
||||
|
||||
# pthread_setname_np was added in GLIBC 2.12, and Solaris 11.3
|
||||
if posix_system:
|
||||
@ -5179,7 +5180,7 @@ def doConfigure(myenv):
|
||||
myenv = conf.Finish()
|
||||
|
||||
if env['TARGET_ARCH'] == "aarch64":
|
||||
AddToCCFLAGSIfSupported(myenv, "-moutline-atomics")
|
||||
myenv.AddToCCFLAGSIfSupported("-moutline-atomics")
|
||||
|
||||
conf = Configure(myenv)
|
||||
usdt_enabled = get_option('enable-usdt-probes')
|
||||
@ -5494,10 +5495,12 @@ if get_option('separate-debug') == "on" or env.TargetOSIs("windows"):
|
||||
)
|
||||
separate_debug(env)
|
||||
|
||||
# TODO: SERVER-68475
|
||||
# temp fix for BF-25986, should be removed when better solution is found
|
||||
if env['SPLIT_DWARF'] == "auto":
|
||||
env['SPLIT_DWARF'] = env.ToolchainIs('gcc') and not link_model == "dynamic"
|
||||
# For static builds, splitting out the dwarf info reduces memory requirments, link time
|
||||
# and binary size significantly. It's affect is less prominent in dynamic builds. The downside
|
||||
# is .dwo files use absolute paths in the debug info, so it's not relocatable.
|
||||
env['SPLIT_DWARF'] = not link_model == "dynamic" and env.ToolchainIs(
|
||||
'gcc', 'clang') and env.CheckCCFLAGSSupported('-gsplit-dwarf')
|
||||
|
||||
if env['SPLIT_DWARF']:
|
||||
env.Tool('split_dwarf')
|
||||
|
||||
@ -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.1-latest"
|
||||
expansions["src_suffix"] = "v6.1-latest"
|
||||
expansions["is_release"] = "false"
|
||||
else:
|
||||
expansions["suffix"] = version_line
|
||||
|
||||
@ -256,9 +256,6 @@ selector:
|
||||
- jstests/core/index_check6.js
|
||||
- jstests/core/index_check7.js
|
||||
- jstests/core/index_decimal.js
|
||||
- jstests/core/index_elemmatch1.js
|
||||
- jstests/core/index_elemmatch2.js
|
||||
- jstests/core/index_elemmatch2.js
|
||||
- jstests/core/index_filter_commands.js
|
||||
- jstests/core/index_filter_on_hidden_index.js
|
||||
- jstests/core/index_multiple_compatibility.js
|
||||
@ -506,8 +503,6 @@ selector:
|
||||
- jstests/core/in7.js
|
||||
- jstests/core/index13.js
|
||||
- jstests/core/index_check2.js
|
||||
- jstests/core/index_elemmatch1.js
|
||||
- jstests/core/index_elemmatch2.js
|
||||
- jstests/core/indexl.js
|
||||
- jstests/core/json_schema/misc_validation.js
|
||||
- jstests/core/ne_array.js
|
||||
@ -756,7 +751,6 @@ selector:
|
||||
- jstests/core/geonear_key.js
|
||||
- jstests/core/getmore_invalidated_documents.js
|
||||
- jstests/core/hidden_index.js
|
||||
- jstests/core/index_elemmatch2.js
|
||||
- jstests/core/index_partial_2dsphere.js
|
||||
- jstests/core/json_schema/misc_validation.js
|
||||
- jstests/core/list_collections_filter.js
|
||||
|
||||
@ -276,16 +276,15 @@ class PeriodicKillSecondariesTestCase(interface.DynamicTestCase):
|
||||
secondary.await_ready()
|
||||
|
||||
client = secondary.mongo_client()
|
||||
minvalid_doc = client.local["replset.minvalid"].find_one()
|
||||
oplog_truncate_after_doc = client.local["replset.oplogTruncateAfterPoint"].find_one()
|
||||
recovery_timestamp_res = client.admin.command("replSetTest",
|
||||
getLastStableRecoveryTimestamp=True)
|
||||
latest_oplog_doc = client.local["oplog.rs"].find_one(sort=[("$natural",
|
||||
pymongo.DESCENDING)])
|
||||
|
||||
self.logger.info("Checking invariants: minValid: {}, oplogTruncateAfterPoint: {},"
|
||||
self.logger.info("Checking replication invariants. oplogTruncateAfterPoint: {},"
|
||||
" stable recovery timestamp: {}, latest oplog doc: {}".format(
|
||||
minvalid_doc, oplog_truncate_after_doc, recovery_timestamp_res,
|
||||
oplog_truncate_after_doc, recovery_timestamp_res,
|
||||
latest_oplog_doc))
|
||||
|
||||
null_ts = bson.Timestamp(0, 0)
|
||||
@ -299,13 +298,6 @@ class PeriodicKillSecondariesTestCase(interface.DynamicTestCase):
|
||||
raise errors.ServerFailure(
|
||||
"Latest oplog entry had no 'ts' field: {}".format(latest_oplog_doc))
|
||||
|
||||
# The "oplogTruncateAfterPoint" document may not exist at startup. If so, we default
|
||||
# it to null.
|
||||
oplog_truncate_after_ts = null_ts
|
||||
if oplog_truncate_after_doc is not None:
|
||||
oplog_truncate_after_ts = oplog_truncate_after_doc.get(
|
||||
"oplogTruncateAfterPoint", null_ts)
|
||||
|
||||
# The "lastStableRecoveryTimestamp" field is present if the storage engine supports
|
||||
# "recover to a timestamp". If it's a null timestamp on a durable storage engine, that
|
||||
# means we do not yet have a stable checkpoint timestamp and must be restarting at the
|
||||
@ -328,94 +320,6 @@ class PeriodicKillSecondariesTestCase(interface.DynamicTestCase):
|
||||
recovery_timestamp, latest_oplog_entry_ts,
|
||||
recovery_timestamp_res, latest_oplog_doc))
|
||||
|
||||
if minvalid_doc is not None:
|
||||
applied_through_ts = minvalid_doc.get("begin", {}).get("ts", null_ts)
|
||||
minvalid_ts = minvalid_doc.get("ts", null_ts)
|
||||
|
||||
# The "appliedThrough" value should always equal the "last stable recovery
|
||||
# timestamp", AKA the stable checkpoint for durable engines, on server restart.
|
||||
#
|
||||
# The written "appliedThrough" time is updated with the latest timestamp at the end
|
||||
# of each batch application, and batch boundaries are the only valid stable
|
||||
# timestamps on secondaries. Therefore, a non-null appliedThrough timestamp must
|
||||
# equal the checkpoint timestamp, because any stable timestamp that the checkpoint
|
||||
# could use includes an equal persisted appliedThrough timestamp.
|
||||
if (recovery_timestamp != null_ts and applied_through_ts != null_ts
|
||||
and (not recovery_timestamp == applied_through_ts)):
|
||||
raise errors.ServerFailure(
|
||||
"The condition last stable recovery timestamp ({}) == appliedThrough ({})"
|
||||
" doesn't hold: minValid document={},"
|
||||
" getLastStableRecoveryTimestamp result={}, last oplog entry={}".format(
|
||||
recovery_timestamp, applied_through_ts, minvalid_doc,
|
||||
recovery_timestamp_res, latest_oplog_doc))
|
||||
|
||||
if applied_through_ts == null_ts:
|
||||
# We clear "appliedThrough" to represent having applied through the top of the
|
||||
# oplog in PRIMARY state or immediately after "rollback via refetch".
|
||||
# If we are using a storage engine that supports "recover to a timestamp,"
|
||||
# then we will have a "last stable recovery timestamp" and we should use that
|
||||
# as our "appliedThrough" (similarly to why we assert their equality above).
|
||||
# If both are null, then we are in PRIMARY state on a storage engine that does
|
||||
# not support "recover to a timestamp" or in RECOVERING immediately after
|
||||
# "rollback via refetch". Since we do not update "minValid" in PRIMARY state,
|
||||
# we leave "appliedThrough" as null so that the invariants below hold, rather
|
||||
# than substituting the latest oplog entry for the "appliedThrough" value.
|
||||
applied_through_ts = recovery_timestamp
|
||||
|
||||
if minvalid_ts == null_ts:
|
||||
# The server treats the "ts" field in the minValid document as missing when its
|
||||
# value is the null timestamp.
|
||||
minvalid_ts = applied_through_ts
|
||||
|
||||
if latest_oplog_entry_ts == null_ts:
|
||||
# If the oplog is empty, we treat the "minValid" as the latest oplog entry.
|
||||
latest_oplog_entry_ts = minvalid_ts
|
||||
|
||||
if oplog_truncate_after_ts == null_ts:
|
||||
# The server treats the "oplogTruncateAfterPoint" field as missing when its
|
||||
# value is the null timestamp. When it is null, the oplog is complete and
|
||||
# should not be truncated, so it is effectively the top of the oplog.
|
||||
oplog_truncate_after_ts = latest_oplog_entry_ts
|
||||
|
||||
# Check the ordering invariants before the secondary has reconciled the end of
|
||||
# its oplog.
|
||||
# The "oplogTruncateAfterPoint" is set to the first timestamp of each batch of
|
||||
# oplog entries before they are written to the oplog. Thus, it can be ahead
|
||||
# of the top of the oplog before any oplog entries are written, and behind it
|
||||
# after some are written. Thus, we cannot compare it to the top of the oplog.
|
||||
|
||||
# appliedThrough <= minValid
|
||||
# appliedThrough represents the end of the previous batch, so it is always the
|
||||
# earliest.
|
||||
if applied_through_ts > minvalid_ts:
|
||||
raise errors.ServerFailure(
|
||||
"The condition appliedThrough <= minValid ({} <= {}) doesn't hold: minValid"
|
||||
" document={}, latest oplog entry={}".format(
|
||||
applied_through_ts, minvalid_ts, minvalid_doc, latest_oplog_doc))
|
||||
|
||||
# minValid <= oplogTruncateAfterPoint
|
||||
# This is true because this hook is never run after a rollback. Thus, we only
|
||||
# move "minValid" to the end of each batch after the batch is written to the oplog.
|
||||
# We reset the "oplogTruncateAfterPoint" to null before we move "minValid" from
|
||||
# the end of the previous batch to the end of the current batch. Thus "minValid"
|
||||
# must be less than or equal to the "oplogTruncateAfterPoint".
|
||||
if minvalid_ts > oplog_truncate_after_ts:
|
||||
raise errors.ServerFailure(
|
||||
"The condition minValid <= oplogTruncateAfterPoint ({} <= {}) doesn't"
|
||||
" hold: minValid document={}, oplogTruncateAfterPoint document={},"
|
||||
" latest oplog entry={}".format(minvalid_ts, oplog_truncate_after_ts,
|
||||
minvalid_doc, oplog_truncate_after_doc,
|
||||
latest_oplog_doc))
|
||||
|
||||
# minvalid <= latest oplog entry
|
||||
# "minValid" is set to the end of a batch after the batch is written to the oplog.
|
||||
# Thus it is always less than or equal to the top of the oplog.
|
||||
if minvalid_ts > latest_oplog_entry_ts:
|
||||
raise errors.ServerFailure(
|
||||
"The condition minValid <= top of oplog ({} <= {}) doesn't"
|
||||
" hold: minValid document={}, latest oplog entry={}".format(
|
||||
minvalid_ts, latest_oplog_entry_ts, minvalid_doc, latest_oplog_doc))
|
||||
|
||||
try:
|
||||
secondary.teardown()
|
||||
except errors.ServerFailure:
|
||||
|
||||
@ -282,6 +282,8 @@ last-continuous:
|
||||
test_file: jstests/sharding/move_chunk_interrupt_postimage.js
|
||||
- ticket: SERVER-64741
|
||||
test_file: jstests/sharding/append_oplog_note_mongos.js
|
||||
- ticket: SERVER-68115
|
||||
test_file: jstests/core/elemmatch_index.js
|
||||
|
||||
# Tests that should only be excluded from particular suites should be listed under that suite.
|
||||
suites:
|
||||
@ -697,6 +699,8 @@ last-lts:
|
||||
test_file: jstests/sharding/move_chunk_interrupt_postimage.js
|
||||
- ticket: SERVER-64741
|
||||
test_file: jstests/sharding/append_oplog_note_mongos.js
|
||||
- ticket: SERVER-68115
|
||||
test_file: jstests/core/elemmatch_index.js
|
||||
|
||||
# Tests that should only be excluded from particular suites should be listed under that suite.
|
||||
suites:
|
||||
|
||||
@ -57,6 +57,7 @@ include:
|
||||
- filename: etc/evergreen_yml_components/variants/task_generation.yml
|
||||
- filename: etc/evergreen_yml_components/variants/sanitizer.yml
|
||||
- filename: etc/evergreen_yml_components/variants/in_memory.yml
|
||||
- filename: etc/evergreen_yml_components/variants/ninja.yml
|
||||
|
||||
variables:
|
||||
- &libfuzzertests
|
||||
@ -274,21 +275,6 @@ buildvariants:
|
||||
- rhel80-large
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- <<: *linux-64-debug-required-template
|
||||
name: linux-64-debug-wtdevelop
|
||||
display_name: "~ Linux DEBUG WiredTiger develop"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- wtdevelop
|
||||
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
|
||||
|
||||
- name: tla-plus
|
||||
display_name: TLA+
|
||||
run_on:
|
||||
@ -936,21 +922,6 @@ buildvariants:
|
||||
# distros:
|
||||
# - windows-vsCurrent-xlarge
|
||||
|
||||
- name: enterprise-windows-ninja
|
||||
display_name: "Ninja Build: Enterprise Windows"
|
||||
cron: "0 4 * * *" # From the ${project_nightly_cron} parameter.
|
||||
modules:
|
||||
- enterprise
|
||||
expansions:
|
||||
compile_flags: --ssl MONGO_DISTMOD=windows CPPPATH="c:/sasl/include" LIBPATH="c:/sasl/lib" -j$(bc <<< "$(grep -c '^processor' /proc/cpuinfo) / 1.5") --win-version-min=win10
|
||||
tasks:
|
||||
- name: compile_ninja_next_TG
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
- name: compile_ninja_TG
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
|
||||
- name: enterprise-windows-cxx20-debug-experimental
|
||||
display_name: "~ Enterprise Windows C++20 DEBUG"
|
||||
cron: "0 4 * * *" # From the ${project_nightly_cron} parameter.
|
||||
@ -1070,20 +1041,6 @@ buildvariants:
|
||||
- name: unittest_shell_hang_analyzer_gen
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: macos-enterprise-ninja
|
||||
display_name: "Ninja Build: macOS Enterprise"
|
||||
cron: "0 4 * * *" # From the ${project_nightly_cron} parameter.
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- macos-1100
|
||||
expansions:
|
||||
compile_env: DEVELOPER_DIR=/Applications/Xcode13.app
|
||||
compile_flags: --ssl -j$(sysctl -n hw.logicalcpu) --libc++ --variables-files=etc/scons/xcode_macosx.vars
|
||||
tasks:
|
||||
- name: compile_ninja_next_TG
|
||||
- name: compile_ninja_TG
|
||||
|
||||
- name: enterprise-macos-rosetta-2
|
||||
display_name: "Enterprise macOS Via Rosetta 2"
|
||||
cron: "0 4 * * *" # From the ${project_nightly_cron} parameter.
|
||||
@ -1382,26 +1339,6 @@ buildvariants:
|
||||
<<: *enterprise-rhel-80-64-bit-dynamic-required-expansions
|
||||
compile_flags: --ssl MONGO_DISTMOD=rhel80 -j$(grep -c ^processor /proc/cpuinfo) --variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars --link-model=dynamic --use-glibcxx-debug --dbg=on --allocator=system
|
||||
|
||||
- name: enterprise-rhel-80-64-bit-dynamic-required-ninja
|
||||
display_name: "Ninja Build: Enterprise RHEL 8.0"
|
||||
cron: "0 4 * * *" # From the ${project_nightly_cron} parameter.
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- rhel80-small
|
||||
stepback: false
|
||||
expansions:
|
||||
compile_flags: --ssl --ocsp-stapling=off MONGO_DISTMOD=rhel80 -j$(grep -c ^processor /proc/cpuinfo) --variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars --link-model=dynamic
|
||||
repo_edition: enterprise
|
||||
has_packages: false
|
||||
tasks:
|
||||
- name: compile_ninja_next_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_ninja_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
|
||||
- &enterprise-rhel-80-64-bit-dynamic-all-feature-flags-required-template
|
||||
name: enterprise-rhel-80-64-bit-dynamic-all-feature-flags-required
|
||||
display_name: "! Shared Library Enterprise RHEL 8.0 (all feature flags)"
|
||||
@ -3105,17 +3042,6 @@ buildvariants:
|
||||
- windows-vsCurrent-large
|
||||
- name: .benchmarks !benchmarks_orphaned
|
||||
|
||||
- <<: *enterprise-windows-nopush-template
|
||||
name: 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
|
||||
|
||||
### QO & QE Patch-Specific Build Variants ###
|
||||
- <<: *enterprise-windows-nopush-template
|
||||
name: windows-compile-query-patch-only
|
||||
|
||||
@ -6,14 +6,386 @@ 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
|
||||
#- 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
|
||||
|
||||
|
||||
parameters:
|
||||
- key: evergreen_config_file_path
|
||||
value: "etc/evergreen_nightly.yml"
|
||||
description: "path to this file"
|
||||
|
||||
buildvariants:
|
||||
- &linux-64-debug-required-template
|
||||
name: linux-64-debug-required
|
||||
display_name: "! Shared Library Linux 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
|
||||
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_scope: shared
|
||||
scons_cache_mode: all
|
||||
test_flags: --excludeWithAnyTags=requires_http_client
|
||||
target_resmoke_time: 15
|
||||
max_sub_suites: 5
|
||||
num_scons_link_jobs_available: 0.99
|
||||
large_distro_name: rhel80-medium
|
||||
tasks:
|
||||
- name: compile_test_and_package_parallel_core_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_test_and_package_parallel_unittest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_test_and_package_parallel_dbtest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- 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: .integration !.audit
|
||||
- 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
|
||||
- name: .stitch
|
||||
- name: server_discovery_and_monitoring_json_test_TG
|
||||
distros:
|
||||
- rhel80-large
|
||||
- name: server_selection_json_test_TG
|
||||
distros:
|
||||
- rhel80-large
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- &enterprise-windows-required-template
|
||||
name: 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:
|
||||
burn_in_tests_build_variant: enterprise-windows-required
|
||||
exe: ".exe"
|
||||
additional_package_targets: archive-mongocryptd archive-mongocryptd-debug msi archive-mh archive-mh-debug
|
||||
content_type: application/zip
|
||||
compile_flags: --ssl MONGO_DISTMOD=windows CPPPATH="c:/sasl/include" LIBPATH="c:/sasl/lib" -j$(bc <<< "$(grep -c '^processor' /proc/cpuinfo) / 1.8") --win-version-min=win10
|
||||
num_scons_link_jobs_available: 0.2
|
||||
python: '/cygdrive/c/python/python37/python.exe'
|
||||
ext: zip
|
||||
scons_cache_scope: shared
|
||||
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: compile_test_and_package_serial_TG
|
||||
distros:
|
||||
- windows-vsCurrent-xlarge
|
||||
- name: compile_build_tools_next_TG
|
||||
distros:
|
||||
- windows-vsCurrent-xlarge
|
||||
- name: burn_in_tests_gen
|
||||
- name: audit
|
||||
- name: auth_audit_gen
|
||||
- name: buildscripts_test
|
||||
- name: causally_consistent_jscore_txns_passthrough
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
- name: .crypt
|
||||
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
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
- name: replica_sets_ese_gen
|
||||
- name: sasl
|
||||
- name: server_discovery_and_monitoring_json_test_TG
|
||||
- name: server_selection_json_test_TG
|
||||
- name: .sharding .txns
|
||||
- name: sharding_auth_gen
|
||||
- name: sharding_auth_audit_gen
|
||||
- name: sharding_ese_gen
|
||||
- name: sharding_opportunistic_secondary_targeting_gen
|
||||
- name: unittest_shell_hang_analyzer_gen
|
||||
|
||||
- &enterprise-rhel-80-64-bit-dynamic-required-template
|
||||
name: 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-required-expansions
|
||||
additional_package_targets: archive-mongocryptd archive-mongocryptd-debug archive-mh archive-mh-debug
|
||||
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"
|
||||
multiversion_platform: rhel80
|
||||
multiversion_edition: enterprise
|
||||
has_packages: false
|
||||
scons_cache_scope: shared
|
||||
scons_cache_mode: all
|
||||
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
|
||||
num_scons_link_jobs_available: 0.99
|
||||
tasks:
|
||||
- name: compile_test_and_package_parallel_core_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
# - name: compile_test_and_package_parallel_unittest_stream_with_recording_TG
|
||||
# distros:
|
||||
# - rhel80-xlarge
|
||||
- name: compile_test_and_package_parallel_unittest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_test_and_package_parallel_dbtest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: .lint
|
||||
- name: lint_fuzzer_sanity_patch
|
||||
- name: test_api_version_compatibility
|
||||
- name: burn_in_tests_gen
|
||||
- name: check_feature_flag_tags
|
||||
- name: check_for_todos
|
||||
- name: .aggfuzzer !.feature_flag_guarded
|
||||
- name: .aggregation !.feature_flag_guarded
|
||||
- name: audit
|
||||
- name: .auth
|
||||
# - name: burn_in_tags_gen
|
||||
- name: buildscripts_test
|
||||
- name: resmoke_end2end_tests
|
||||
- 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: config_fuzzer_concurrency
|
||||
- name: config_fuzzer_simulate_crash_concurrency_replication
|
||||
distros:
|
||||
- rhel80-large
|
||||
- name: config_fuzzer_concurrency_replication
|
||||
distros:
|
||||
- rhel80-large
|
||||
- name: config_fuzzer_jsCore
|
||||
- name: config_fuzzer_replica_sets_jscore_passthrough
|
||||
distros:
|
||||
- rhel80-large
|
||||
- name: disk_wiredtiger
|
||||
- name: .encrypt
|
||||
- name: idl_tests
|
||||
- name: initial_sync_fuzzer_gen
|
||||
- name: .integration
|
||||
distros:
|
||||
- rhel80-medium
|
||||
- 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: .multiversion_sanity_check
|
||||
- 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: sharding_opportunistic_secondary_targeting_gen
|
||||
- name: .stitch
|
||||
- name: .crypt
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: crypt_build_debug_and_test
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: .updatefuzzer
|
||||
- name: secondary_reads_passthrough_gen
|
||||
- name: server_discovery_and_monitoring_json_test_TG
|
||||
- name: .serverless
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: server_selection_json_test_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- name: enterprise-rhel80-dynamic-clang-tidy-required
|
||||
display_name: "! Enterprise Clang Tidy"
|
||||
cron: "0 */4 * * *" # From the ${project_required_suggested_cron} parameter
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- rhel80-xxlarge
|
||||
expansions:
|
||||
lang_environment: LANG=C
|
||||
compile_flags: --link-model=dynamic -j$(grep -c ^processor /proc/cpuinfo) --variables-files=etc/scons/mongodbtoolchain_${clang_tidy_toolchain}_clang.vars
|
||||
scons_cache_scope: shared
|
||||
scons_cache_mode: all
|
||||
show_scons_timings: false
|
||||
clang_tidy_toolchain: v3
|
||||
clang_tidy_file: .clang-tidy-extra-checks
|
||||
tasks:
|
||||
- name: clang_tidy_TG
|
||||
|
||||
- &rhel80-debug-aubsan-lite-required-template
|
||||
name: 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
|
||||
stepback: true
|
||||
expansions:
|
||||
additional_package_targets: archive-mongocryptd archive-mongocryptd-debug
|
||||
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"
|
||||
compile_flags: --variables-files=etc/scons/mongodbtoolchain_v3_clang.vars --dbg=on --opt=on --allocator=system --sanitize=undefined,address --ssl --ocsp-stapling=off -j$(grep -c ^processor /proc/cpuinfo) --link-model=dynamic
|
||||
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
|
||||
scons_cache_scope: shared
|
||||
scons_cache_mode: all
|
||||
max_sub_suites: 3
|
||||
num_scons_link_jobs_available: 0.99
|
||||
separate_debug: off
|
||||
large_distro_name: rhel80-build
|
||||
tasks:
|
||||
- name: compile_test_and_package_parallel_core_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_test_and_package_parallel_unittest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_test_and_package_parallel_dbtest_stream_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: jsCore
|
||||
- name: jsCore_txns
|
||||
- name: unittest_shell_hang_analyzer_gen
|
||||
- name: generate_buildid_to_debug_symbols_mapping
|
||||
|
||||
- <<: *enterprise-rhel-80-64-bit-dynamic-required-template
|
||||
name: commit-queue
|
||||
display_name: "~ Commit Queue"
|
||||
cron: "0 4 * * 0" # From the ${project_weekly_cron} parameter
|
||||
stepback: false
|
||||
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
|
||||
|
||||
@ -35,6 +35,8 @@ overrides:
|
||||
enterprise-rhel-80-64-bit-dynamic-required:
|
||||
- task: replica_sets_large_txns_format
|
||||
exec_timeout: 120 # 2 hours.
|
||||
- task: config_fuzzer_replica_sets_jscore_passthrough
|
||||
exec_timeout: 150 # 2.5 hours.
|
||||
|
||||
enterprise-rhel80-debug-tsan:
|
||||
- task: run_unittests
|
||||
|
||||
@ -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.1
|
||||
|
||||
- name: wtdevelop
|
||||
repo: git@github.com:wiredtiger/wiredtiger.git
|
||||
@ -304,7 +304,6 @@ variables:
|
||||
- enterprise-windows-debug-unoptimized
|
||||
- enterprise-windows-inmem
|
||||
- enterprise-windows-required
|
||||
- enterprise-windows-wtdevelop
|
||||
- ubuntu1804-debug-asan
|
||||
- ubuntu1804-debug-ubsan
|
||||
- ubuntu1804-debug-aubsan-lite-required
|
||||
@ -578,7 +577,7 @@ functions:
|
||||
"get buildnumber": &get_buildnumber
|
||||
command: keyval.inc
|
||||
params:
|
||||
key: "${build_variant}_master"
|
||||
key: "${build_variant}_v6.1"
|
||||
destination: "builder_num"
|
||||
|
||||
"run diskstats": &run_diskstats
|
||||
@ -1039,6 +1038,8 @@ functions:
|
||||
- *f_expansions_write
|
||||
- *kill_processes
|
||||
- *cleanup_environment
|
||||
- *get_version_expansions
|
||||
- *apply_version_expansions
|
||||
- *fetch_venv
|
||||
- *adjust_venv
|
||||
- *f_expansions_write
|
||||
@ -1179,6 +1180,15 @@ functions:
|
||||
args:
|
||||
- "src/evergreen/scons_compile.sh"
|
||||
|
||||
"ninja compile":
|
||||
- *f_expansions_write
|
||||
- command: subprocess.exec
|
||||
type: test
|
||||
params:
|
||||
binary: bash
|
||||
args:
|
||||
- "./src/evergreen/ninja_compile.sh"
|
||||
|
||||
"generate version expansions": &generate_version_expansions
|
||||
command: subprocess.exec
|
||||
params:
|
||||
@ -2303,11 +2313,93 @@ tasks:
|
||||
task_compile_flags: >-
|
||||
--ninja
|
||||
- *f_expansions_write
|
||||
- command: subprocess.exec
|
||||
params:
|
||||
binary: bash
|
||||
args:
|
||||
- "./src/evergreen/ninja_compile.sh"
|
||||
- func: "ninja compile"
|
||||
vars:
|
||||
ninja_file: "build.ninja"
|
||||
|
||||
- name: compile_ninja_default_profile
|
||||
tags: []
|
||||
depends_on:
|
||||
- name: version_expansions_gen
|
||||
variant: generate-tasks-for-version
|
||||
commands:
|
||||
- func: "scons compile"
|
||||
vars:
|
||||
generating_for_ninja: true
|
||||
separate_debug: off
|
||||
task_install_action:
|
||||
default
|
||||
task_compile_flags: >-
|
||||
--build-profile=default
|
||||
--variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars
|
||||
--ninja
|
||||
- *f_expansions_write
|
||||
- func: "ninja compile"
|
||||
vars:
|
||||
ninja_file: "build.ninja"
|
||||
|
||||
- name: compile_ninja_opt_profile
|
||||
tags: []
|
||||
depends_on:
|
||||
- name: version_expansions_gen
|
||||
variant: generate-tasks-for-version
|
||||
commands:
|
||||
- func: "scons compile"
|
||||
vars:
|
||||
generating_for_ninja: true
|
||||
separate_debug: off
|
||||
task_install_action:
|
||||
default
|
||||
task_compile_flags: >-
|
||||
--build-profile=opt
|
||||
CCACHE=
|
||||
ICECC=
|
||||
- *f_expansions_write
|
||||
- func: "ninja compile"
|
||||
vars:
|
||||
ninja_file: "opt.ninja"
|
||||
|
||||
- name: compile_ninja_fast_profile
|
||||
tags: []
|
||||
depends_on:
|
||||
- name: version_expansions_gen
|
||||
variant: generate-tasks-for-version
|
||||
commands:
|
||||
- func: "scons compile"
|
||||
vars:
|
||||
generating_for_ninja: true
|
||||
separate_debug: off
|
||||
task_install_action:
|
||||
default
|
||||
task_compile_flags: >-
|
||||
--build-profile=fast
|
||||
CCACHE=
|
||||
ICECC=
|
||||
- *f_expansions_write
|
||||
- func: "ninja compile"
|
||||
vars:
|
||||
ninja_file: "fast.ninja"
|
||||
|
||||
- name: compile_ninja_san_profile
|
||||
tags: []
|
||||
depends_on:
|
||||
- name: version_expansions_gen
|
||||
variant: generate-tasks-for-version
|
||||
commands:
|
||||
- func: "scons compile"
|
||||
vars:
|
||||
generating_for_ninja: true
|
||||
separate_debug: off
|
||||
task_install_action:
|
||||
default
|
||||
task_compile_flags: >-
|
||||
--build-profile=san
|
||||
CCACHE=
|
||||
ICECC=
|
||||
- *f_expansions_write
|
||||
- func: "ninja compile"
|
||||
vars:
|
||||
ninja_file: "san.ninja"
|
||||
|
||||
- name: compile_ninja_next
|
||||
tags: []
|
||||
@ -2325,11 +2417,9 @@ tasks:
|
||||
--build-tools=next
|
||||
--ninja
|
||||
- *f_expansions_write
|
||||
- command: subprocess.exec
|
||||
params:
|
||||
binary: bash
|
||||
args:
|
||||
- "./src/evergreen/ninja_compile.sh"
|
||||
- func: "ninja compile"
|
||||
vars:
|
||||
ninja_file: "build.ninja"
|
||||
|
||||
- name: compile_build_tools_next
|
||||
tags: []
|
||||
@ -2895,7 +2985,7 @@ tasks:
|
||||
task_compile_flags: >-
|
||||
--allocator=system
|
||||
--enable-free-mon=off
|
||||
--enterprise-features=fle
|
||||
--enterprise-features=fle,search
|
||||
--js-engine=none
|
||||
--link-model=dynamic-sdk
|
||||
--enable-http-client=off
|
||||
@ -2933,7 +3023,7 @@ tasks:
|
||||
--opt=off
|
||||
--allocator=system
|
||||
--enable-free-mon=off
|
||||
--enterprise-features=fle
|
||||
--enterprise-features=fle,search
|
||||
--js-engine=none
|
||||
--link-model=dynamic-sdk
|
||||
DESTDIR='$BUILD_ROOT/crypt-lib-$MONGO_VERSION'
|
||||
@ -2973,7 +3063,7 @@ tasks:
|
||||
task_compile_flags: >-
|
||||
--allocator=system
|
||||
--enable-free-mon=off
|
||||
--enterprise-features=fle
|
||||
--enterprise-features=fle,search
|
||||
--js-engine=none
|
||||
--link-model=static
|
||||
DESTDIR='$BUILD_ROOT/crypt-lib-$MONGO_VERSION'
|
||||
@ -6586,6 +6676,7 @@ tasks:
|
||||
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"
|
||||
@ -7487,6 +7578,26 @@ task_groups:
|
||||
content_type: text/plain
|
||||
display_name: build.ninja
|
||||
|
||||
- <<: *compile_task_group_template
|
||||
name: compile_ninja_default_profile_TG
|
||||
tasks:
|
||||
- compile_ninja_default_profile
|
||||
|
||||
- <<: *compile_task_group_template
|
||||
name: compile_ninja_opt_profile_TG
|
||||
tasks:
|
||||
- compile_ninja_opt_profile
|
||||
|
||||
- <<: *compile_task_group_template
|
||||
name: compile_ninja_san_profile_TG
|
||||
tasks:
|
||||
- compile_ninja_san_profile
|
||||
|
||||
- <<: *compile_task_group_template
|
||||
name: compile_ninja_fast_profile_TG
|
||||
tasks:
|
||||
- compile_ninja_fast_profile
|
||||
|
||||
- <<: *compile_task_group_template
|
||||
name: server_discovery_and_monitoring_json_test_TG
|
||||
tasks:
|
||||
|
||||
74
etc/evergreen_yml_components/variants/ninja.yml
Normal file
74
etc/evergreen_yml_components/variants/ninja.yml
Normal file
@ -0,0 +1,74 @@
|
||||
buildvariants:
|
||||
|
||||
- name: enterprise-windows-ninja
|
||||
display_name: "Ninja Build: Enterprise Windows"
|
||||
cron: "0 4 * * 0" # Run once a week to ensure no failures introduced to ninja builds
|
||||
modules:
|
||||
- enterprise
|
||||
expansions:
|
||||
compile_flags: --ssl MONGO_DISTMOD=windows CPPPATH="c:/sasl/include c:/snmp/include" LIBPATH="c:/sasl/lib c:/snmp/lib" -j$(bc <<< "$(grep -c '^processor' /proc/cpuinfo) / 1.5") --win-version-min=win10
|
||||
tasks:
|
||||
- name: compile_ninja_next_TG
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
- name: compile_ninja_TG
|
||||
distros:
|
||||
- windows-vsCurrent-large
|
||||
|
||||
- name: macos-enterprise-ninja
|
||||
display_name: "Ninja Build: macOS Enterprise"
|
||||
cron: "0 4 * * 0" # Run once a week to ensure no failures introduced to ninja builds
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- macos-1100
|
||||
expansions:
|
||||
compile_env: DEVELOPER_DIR=/Applications/Xcode13.app
|
||||
compile_flags: --ssl -j$(sysctl -n hw.logicalcpu) --libc++ --variables-files=etc/scons/xcode_macosx.vars
|
||||
tasks:
|
||||
- name: compile_ninja_next_TG
|
||||
- name: compile_ninja_TG
|
||||
|
||||
- name: ubuntu1804-ninja-build-profiles
|
||||
display_name: "Ninja Build Profiles: Ubuntu 18.04"
|
||||
cron: "0 4 * * 0" # Run once a week to ensure no failures introduced to build profiles
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- ubuntu1804-small
|
||||
stepback: false
|
||||
expansions:
|
||||
compile_flags: --ssl --ocsp-stapling=off MONGO_DISTMOD=ubuntu1804 -j$(grep -c ^processor /proc/cpuinfo)
|
||||
repo_edition: enterprise
|
||||
has_packages: false
|
||||
tasks:
|
||||
- name: compile_ninja_default_profile_TG
|
||||
distros:
|
||||
- ubuntu1804-xlarge
|
||||
- name: compile_ninja_opt_profile_TG
|
||||
distros:
|
||||
- ubuntu1804-xlarge
|
||||
- name: compile_ninja_san_profile_TG
|
||||
distros:
|
||||
- ubuntu1804-xlarge
|
||||
- name: compile_ninja_fast_profile_TG
|
||||
distros:
|
||||
- ubuntu1804-xlarge
|
||||
|
||||
- name: enterprise-rhel-80-64-bit-dynamic-required-ninja
|
||||
display_name: "Ninja Build: Enterprise RHEL 8.0"
|
||||
cron: "0 4 * * 0" # Run once a week to ensure no failures introduced to ninja builds
|
||||
modules:
|
||||
- enterprise
|
||||
run_on:
|
||||
- rhel80-small
|
||||
expansions:
|
||||
compile_flags: --ssl MONGO_DISTMOD=rhel80 -j$(grep -c ^processor /proc/cpuinfo) --variables-files=etc/scons/mongodbtoolchain_v3_gcc.vars --link-model=dynamic
|
||||
has_packages: false
|
||||
tasks:
|
||||
- name: compile_ninja_next_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
- name: compile_ninja_TG
|
||||
distros:
|
||||
- rhel80-xlarge
|
||||
@ -104,7 +104,7 @@ modules:
|
||||
- name: enterprise
|
||||
repo: git@github.com:10gen/mongo-enterprise-modules.git
|
||||
prefix: src/mongo/db/modules
|
||||
branch: master
|
||||
branch: v6.1
|
||||
- name: mongo-tools
|
||||
repo: git@github.com:mongodb/mongo-tools.git
|
||||
prefix: mongo-tools/src/github.com/mongodb
|
||||
@ -336,10 +336,7 @@ functions:
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
# TODO: SERVER-68475
|
||||
# remove this when a better solution is found
|
||||
extra_args="SPLIT_DWARF_DWP_FILES=1"
|
||||
python ./buildscripts/scons.py ${compile_flags|} ${scons_cache_args|} $extra_args install-core install-jstestshell MONGO_VERSION=${version} DESTDIR=$(pwd)/mongodb ${patch_compile_flags|}
|
||||
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
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
# This file is now empty and the settings have been made the default.
|
||||
# This file exists only to prevent breakage when used with existing command line invocations.
|
||||
|
||||
# Configures the build for building with a unified ninja
|
||||
# Each configuration will share a ninja log
|
||||
# This allows the output binaries of each configuration to share a common directory
|
||||
|
||||
NINJA_BUILDDIR="$BUILD_ROOT/ninja"
|
||||
DESTDIR="$BUILD_ROOT/install"
|
||||
# NINJA_BUILDDIR="$BUILD_ROOT/ninja"
|
||||
# DESTDIR="$BUILD_ROOT/install"
|
||||
|
||||
@ -101,8 +101,7 @@ modules:
|
||||
- name: tsbs
|
||||
repo: git@github.com:gregorynoma/tsbs.git
|
||||
prefix: ${workdir}/src
|
||||
branch: master
|
||||
ref: 027f4b2d83267ae69d2740855654b4a7cac72498
|
||||
branch: production
|
||||
- name: mongo-perf
|
||||
repo: git@github.com:mongodb/mongo-perf.git
|
||||
prefix: ${workdir}/src
|
||||
@ -127,7 +126,7 @@ modules:
|
||||
- name: enterprise
|
||||
repo: git@github.com:10gen/mongo-enterprise-modules.git
|
||||
prefix: src/mongo/db/modules
|
||||
branch: master
|
||||
branch: v6.1
|
||||
- name: mongo-tools
|
||||
repo: git@github.com:mongodb/mongo-tools.git
|
||||
prefix: mongo-tools/src/github.com/mongodb
|
||||
@ -366,10 +365,7 @@ functions:
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
source "${workdir}/compile_venv/bin/activate"
|
||||
# TODO: SERVER-68475
|
||||
# remove this when a better solution is found
|
||||
extra_args="SPLIT_DWARF_DWP_FILES=1"
|
||||
python ./buildscripts/scons.py ${compile_flags|} ${scons_cache_args|} $extra_args install-core install-jstestshell MONGO_VERSION=${version} DESTDIR=$(pwd)/mongodb ${patch_compile_flags|}
|
||||
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
|
||||
@ -506,43 +502,6 @@ tasks:
|
||||
mongo-tools: ${mongo-tools_rev}
|
||||
- func: "compile mongodb"
|
||||
|
||||
- name: renew_ssl_cert
|
||||
commands:
|
||||
- command: git.get_project
|
||||
params:
|
||||
directory: *src_dir
|
||||
revisions:
|
||||
dsi: ${dsi_rev}
|
||||
# Run the script to generate ssl cert files
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: AWS_ACCESS_KEY_ID=${terraform_key} AWS_SECRET_ACCESS_KEY=${terraform_secret} ./src/dsi/run-dsi generate_ssl_cert
|
||||
# Upload files for further DSI usage
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: member.pem
|
||||
# path to the remote file is intended to be static
|
||||
remote_file: dsi/ssl/member.pem
|
||||
bucket: mciuploads
|
||||
# the visibility has to be public for consumption by DSI
|
||||
permissions: public-read
|
||||
content_type: text/plain
|
||||
display_name: member.pem
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: root.crt
|
||||
# path to the remote file is intended to be static
|
||||
remote_file: dsi/ssl/root.crt
|
||||
bucket: mciuploads
|
||||
# the visibility has to be public for consumption by DSI
|
||||
permissions: public-read
|
||||
content_type: text/plain
|
||||
display_name: root.crt
|
||||
|
||||
- name: linkbench
|
||||
priority: 5
|
||||
commands:
|
||||
@ -957,7 +916,7 @@ tasks:
|
||||
test_control: "initialsync-logkeeper"
|
||||
mongodb_setup: "initialsync-logkeeper-short"
|
||||
# Logkeeper dataset with FCV set to 6.0
|
||||
mongodb_dataset: "https://s3-us-west-2.amazonaws.com/dsi-donot-remove/InitialSyncLogKeeper/logkeeper-slice-data-mongodb-6.0.tgz"
|
||||
mongodb_dataset: "https://dsi-donot-remove.s3-us-west-2.amazonaws.com/InitialSyncLogKeeper/logkeeper-slice-data-mongodb-6.1.tgz"
|
||||
|
||||
- name: initialsync-logkeeper-short-fcbis
|
||||
priority: 5
|
||||
@ -967,7 +926,7 @@ tasks:
|
||||
test_control: "initialsync-logkeeper"
|
||||
mongodb_setup: "initialsync-logkeeper-short-fcbis"
|
||||
# Logkeeper dataset with FCV set to 6.0
|
||||
mongodb_dataset: "https://s3-us-west-2.amazonaws.com/dsi-donot-remove/InitialSyncLogKeeper/logkeeper-slice-data-mongodb-6.0.tgz"
|
||||
mongodb_dataset: "https://dsi-donot-remove.s3-us-west-2.amazonaws.com/InitialSyncLogKeeper/logkeeper-slice-data-mongodb-6.1.tgz"
|
||||
|
||||
- name: initialsync-logkeeper
|
||||
priority: 5
|
||||
@ -999,7 +958,7 @@ tasks:
|
||||
test_control: "initialsync-logkeeper-short-s3-update"
|
||||
mongodb_setup: "initialsync-logkeeper-short-s3-update"
|
||||
# Update this to Logkeeper dataset with FCV set to latest after each LTS release.
|
||||
mongodb_dataset: "https://s3-us-west-2.amazonaws.com/dsi-donot-remove/InitialSyncLogKeeper/logkeeper-slice-data-mongodb-6.0.tgz"
|
||||
mongodb_dataset: "https://dsi-donot-remove.s3-us-west-2.amazonaws.com/InitialSyncLogKeeper/logkeeper-slice-data-mongodb-6.1.tgz"
|
||||
|
||||
- name: initialsync-logkeeper-snapshot-update
|
||||
priority: 5
|
||||
@ -1171,41 +1130,6 @@ buildvariants:
|
||||
- name: tpch_10_normalized
|
||||
- name: tpch_10_denormalized
|
||||
|
||||
- name: linux-standalone-all-feature-flags
|
||||
display_name: Linux Standalone (all feature flags)
|
||||
cron: "0 0 * * *" # Everyday at 00:00
|
||||
modules: *modules
|
||||
expansions:
|
||||
mongodb_setup: standalone-all-feature-flags
|
||||
infrastructure_provisioning: single
|
||||
platform: linux
|
||||
project_dir: *project_dir
|
||||
authentication: enabled
|
||||
storageEngine: wiredTiger
|
||||
run_on:
|
||||
- "rhel70-perf-single"
|
||||
depends_on: *_compile_amazon2
|
||||
tasks:
|
||||
- name: schedule_patch_auto_tasks
|
||||
- name: schedule_variant_auto_tasks
|
||||
- name: industry_benchmarks
|
||||
- name: ycsb_60GB
|
||||
- name: ycsb_60GB.long
|
||||
- name: crud_workloads
|
||||
- name: bestbuy_agg
|
||||
- name: bestbuy_agg_merge_different_db
|
||||
- name: bestbuy_agg_merge_same_db
|
||||
- name: bestbuy_agg_merge_wordcount
|
||||
- name: bestbuy_query
|
||||
- name: cursor_manager
|
||||
- name: map_reduce_workloads
|
||||
- name: tpcc
|
||||
- name: tpch_1_normalized
|
||||
- name: tpch_1_denormalized
|
||||
- name: tpch_10_normalized
|
||||
- name: tpch_10_denormalized
|
||||
- name: column_store_index_charts_events_1G
|
||||
|
||||
- name: linux-standalone-classic-query-engine
|
||||
display_name: Linux Standalone (Classic Query Engine)
|
||||
cron: "0 0 * * 4" # 00:00 on Thursday
|
||||
@ -1658,26 +1582,6 @@ buildvariants:
|
||||
- name: industry_benchmarks
|
||||
- name: linkbench
|
||||
|
||||
- name: linux-shard-lite-all-feature-flags
|
||||
display_name: Linux Shard Lite (all feature flags)
|
||||
cron: "0 0 * * 4" # 00:00 on Thursday
|
||||
modules: *modules
|
||||
expansions:
|
||||
mongodb_setup: shard-lite-all-feature-flags
|
||||
infrastructure_provisioning: shard-lite
|
||||
platform: linux
|
||||
project_dir: *project_dir
|
||||
authentication: enabled
|
||||
storageEngine: wiredTiger
|
||||
run_on:
|
||||
- "rhel70-perf-shard-lite"
|
||||
depends_on: *_compile_amazon2
|
||||
tasks:
|
||||
- name: schedule_patch_auto_tasks
|
||||
- name: schedule_variant_auto_tasks
|
||||
- name: change_streams_preimage_throughput
|
||||
- name: change_streams_preimage_latency
|
||||
|
||||
- name: linux-shard-single
|
||||
display_name: Linux Shard Single
|
||||
cron: "0 0 * * 0,4" # 00:00 on Sunday,Thursday
|
||||
@ -1869,67 +1773,6 @@ buildvariants:
|
||||
- name: sb_large_scale
|
||||
- name: sb_timeseries
|
||||
|
||||
# Note that the "disabled-feature-flags" part of the name is kept to avoid breaking
|
||||
# history even though the display name is "all feature flags"
|
||||
- name: linux-3-node-replSet-disabled-feature-flags
|
||||
display_name: Linux 3-Node ReplSet (all feature flags)
|
||||
cron: "0 0 * * 4" # 00:00 on Thursday
|
||||
modules: *modules
|
||||
expansions:
|
||||
mongodb_setup: replica-all-feature-flags
|
||||
infrastructure_provisioning: replica
|
||||
platform: linux
|
||||
project_dir: *project_dir
|
||||
authentication: enabled
|
||||
storageEngine: wiredTiger
|
||||
run_on:
|
||||
- "rhel70-perf-replset"
|
||||
depends_on: *_compile_amazon2
|
||||
tasks:
|
||||
- name: schedule_patch_auto_tasks
|
||||
- name: schedule_variant_auto_tasks
|
||||
- name: industry_benchmarks
|
||||
- name: ycsb_60GB
|
||||
- name: ycsb_60GB.long
|
||||
- name: industry_benchmarks_secondary_reads
|
||||
- name: crud_workloads
|
||||
- name: crud_workloads_majority
|
||||
- name: mixed_workloads
|
||||
- name: misc_workloads
|
||||
- name: map_reduce_workloads
|
||||
- name: refine_shard_key_transaction_stress
|
||||
- name: smoke_test
|
||||
- name: retryable_writes_workloads
|
||||
- name: industry_benchmarks_wmajority
|
||||
- name: secondary_performance # Uses a special 2 node mongodb setup
|
||||
- name: non_sharded_workloads
|
||||
- name: bestbuy_agg
|
||||
- name: bestbuy_agg_merge_different_db
|
||||
- name: bestbuy_agg_merge_same_db
|
||||
- name: bestbuy_agg_merge_wordcount
|
||||
- name: bestbuy_query
|
||||
- name: change_streams_throughput
|
||||
- name: change_streams_latency
|
||||
- name: change_streams_listen_throughput
|
||||
- name: change_streams_preimage_throughput
|
||||
- name: change_streams_preimage_latency
|
||||
- name: snapshot_reads
|
||||
- name: secondary_reads
|
||||
- name: tpcc
|
||||
- name: tpch_1_normalized
|
||||
- name: tpch_1_denormalized
|
||||
# TODO: Enable in SERVER-66572.
|
||||
# - name: tpch_10_normalized
|
||||
# - name: tpch_10_denormalized
|
||||
- name: linkbench
|
||||
- name: linkbench2
|
||||
# Time-series collections are available since v5.0.
|
||||
# - name: tsbs_load
|
||||
# - name: tsbs_query
|
||||
# - name: tsbs_query_manual_bucketing
|
||||
- name: sb_large_scale
|
||||
- name: sb_timeseries
|
||||
|
||||
- name: linux-3-node-replSet-noflowcontrol
|
||||
display_name: Linux 3-Node ReplSet (Flow Control off)
|
||||
cron: "0 0 * * 4" # 00:00 on Thursday
|
||||
@ -2041,7 +1884,7 @@ buildvariants:
|
||||
mongodb_setup: initialsync-logkeeper
|
||||
infrastructure_provisioning: initialsync-logkeeper
|
||||
# EBS logkeeper snapshot with FCV set to 6.0
|
||||
snapshotId: snap-0306dce35f030ebec
|
||||
snapshotId: snap-0716ed59d18225693
|
||||
platform: linux
|
||||
authentication: disabled
|
||||
storageEngine: wiredTiger
|
||||
@ -2065,7 +1908,7 @@ buildvariants:
|
||||
# mongodb_setup: initialsync-logkeeper-snapshot-update
|
||||
# infrastructure_provisioning: initialsync-logkeeper-snapshot-update
|
||||
# # Update this to latest snapshot after each LTS release.
|
||||
# snapshotId: snap-0306dce35f030ebec
|
||||
# snapshotId: snap-0716ed59d18225693
|
||||
# platform: linux
|
||||
# authentication: disabled
|
||||
# storageEngine: wiredTiger
|
||||
@ -2160,12 +2003,3 @@ buildvariants:
|
||||
- name: tpcc
|
||||
- name: linkbench
|
||||
- name: linkbench2
|
||||
|
||||
- name: renew-ssl-cert
|
||||
display_name: Renew SSL Cert
|
||||
cron: "0 0 * * 4" # 00:00 on Thursday
|
||||
modules: *modules
|
||||
run_on: # Certbot with route53 plugin is installed on Ubuntu 20.04
|
||||
- "ubuntu2004-small"
|
||||
tasks:
|
||||
- name: renew_ssl_cert
|
||||
|
||||
@ -5,6 +5,7 @@ cd src
|
||||
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
|
||||
activate_venv
|
||||
python -m pip install ninja
|
||||
if [ "Windows_NT" = "$OS" ]; then
|
||||
@ -13,8 +14,8 @@ if [ "Windows_NT" = "$OS" ]; then
|
||||
for i in "${compile_env[@]}"; do
|
||||
echo "set $i" >> msvc.bat
|
||||
done
|
||||
echo "ninja install-core" >> msvc.bat
|
||||
echo "ninja -f ${ninja_file} install-core" >> msvc.bat
|
||||
cmd /C msvc.bat
|
||||
else
|
||||
eval ${compile_env} ninja install-core
|
||||
eval ${compile_env} ninja -f ${ninja_file} install-core
|
||||
fi
|
||||
|
||||
@ -58,9 +58,7 @@ else
|
||||
extra_args="$extra_args --release"
|
||||
fi
|
||||
|
||||
# TODO: SERVER-68475
|
||||
# remove this when a better solution is found
|
||||
extra_args="$extra_args SPLIT_DWARF_DWP_FILES=1"
|
||||
extra_args="$extra_args SPLIT_DWARF=0"
|
||||
|
||||
if [ "${generating_for_ninja}" = "true" ] && [ "Windows_NT" = "$OS" ]; then
|
||||
vcvars="$(vswhere -latest -property installationPath | tr '\\' '/' | dos2unix.exe)/VC/Auxiliary/Build/"
|
||||
|
||||
@ -1084,5 +1084,29 @@ verifyOnWholeCluster(
|
||||
{change_stream_match_pushdown_and_rewrite_and_rewrite: {dropDatabase: [dbName, dbName]}},
|
||||
1 /* expectedOplogRetDocsForEachShard */);
|
||||
|
||||
// Create two sharded collections in the main test database, then start a new change stream to get a
|
||||
// fresh resume token.
|
||||
const collWithDot =
|
||||
createShardedCollection(st, "_id" /* shardKey */, dbName, "foo.bar", 2 /*splitAt */);
|
||||
const collWithUnderscore =
|
||||
createShardedCollection(st, "_id" /* shardKey */, dbName, "foo_bar", 2 /*splitAt */);
|
||||
const thirdResumeAfterToken =
|
||||
db.getSiblingDB("admin").watch([], {allChangesForCluster: true}).getResumeToken();
|
||||
|
||||
// Insert one document per collection, per shard. The test cases below verify the behavior of regex
|
||||
// matches with escaped characters on collections with special names (e.g. containing dots). This
|
||||
// exercises the fix for SERVER-67715.
|
||||
assert.commandWorked(collWithDot.insert({_id: 1}));
|
||||
assert.commandWorked(collWithDot.insert({_id: 3}));
|
||||
assert.commandWorked(collWithUnderscore.insert({_id: 1}));
|
||||
assert.commandWorked(collWithUnderscore.insert({_id: 3}));
|
||||
|
||||
// Ensure that a regex match properly respects escaped characters (here, testing that the escaped
|
||||
// "." character is treated as a literal dot).
|
||||
verifyOnWholeCluster(thirdResumeAfterToken,
|
||||
{$match: {"ns.coll": {$nin: [/^foo\./]}}},
|
||||
{"foo_bar": {insert: [1, 3]}},
|
||||
1 /* expectedOplogRetDocsForEachShard */);
|
||||
|
||||
st.stop();
|
||||
})();
|
||||
|
||||
@ -816,5 +816,48 @@ verifyOnWholeCluster(
|
||||
},
|
||||
[9, 3] /* expectedOplogRetDocsForEachShard */);
|
||||
|
||||
// Create a new change stream and resume token for replaying the stream after this point.
|
||||
const thirdResumeAfterToken =
|
||||
db.getSiblingDB("admin").watch([], {allChangesForCluster: true}).getResumeToken();
|
||||
|
||||
// The test cases below verify the behavior of regex matches with escaped characters on collections
|
||||
// with special names (e.g. containing dots). This exercises the fix for SERVER-67715.
|
||||
const collWithDot =
|
||||
createShardedCollection(st, "_id" /* shardKey */, dbName, "foo.bar", 2 /*splitAt */);
|
||||
assert.commandWorked(collWithDot.createIndex({x: 1}));
|
||||
assert.commandWorked(collWithDot.insert({_id: 1}));
|
||||
assert.commandWorked(collWithDot.insert({_id: 3}));
|
||||
assert.commandWorked(
|
||||
collWithDot.runCommand({collMod: "foo.bar", index: {keyPattern: {x: 1}, hidden: true}}));
|
||||
assert.commandWorked(collWithDot.runCommand({dropIndexes: "foo.bar", index: {x: 1}}));
|
||||
|
||||
const collWithUnderscore =
|
||||
createShardedCollection(st, "_id" /* shardKey */, dbName, "foo_bar", 2 /*splitAt */);
|
||||
assert.commandWorked(collWithUnderscore.createIndex({x: 1}));
|
||||
assert.commandWorked(collWithUnderscore.insert({_id: 1}));
|
||||
assert.commandWorked(collWithUnderscore.insert({_id: 3}));
|
||||
assert.commandWorked(
|
||||
collWithUnderscore.runCommand({collMod: "foo_bar", index: {keyPattern: {x: 1}, hidden: true}}));
|
||||
assert.commandWorked(collWithUnderscore.runCommand({dropIndexes: "foo_bar", index: {x: 1}}));
|
||||
|
||||
// Ensure that a regex match properly respects escaped characters (here, testing that the escaped
|
||||
// "." character is treated as a literal dot). Note that we expect 5 extra oplog entries on shard0:
|
||||
// - 1 from the "create" event (which only appears on shard0)
|
||||
// - 4 no-op entries from sharding the two collections that are not affected by the filter pushdown
|
||||
// (2 "shardCollection" + 2 "migrateChunkToNewShard")
|
||||
verifyOnWholeCluster(thirdResumeAfterToken,
|
||||
{$match: {"ns.coll": {$nin: [/^foo\./]}}},
|
||||
{
|
||||
"foo_bar": {
|
||||
create: ["foo_bar"],
|
||||
shardCollection: ["foo_bar"],
|
||||
createIndexes: ["foo_bar", "foo_bar"],
|
||||
insert: [1, 3],
|
||||
modify: ["foo_bar", "foo_bar"],
|
||||
dropIndexes: ["foo_bar", "foo_bar"]
|
||||
}
|
||||
},
|
||||
[9, 4] /* expectedOplogRetDocsForEachShard */);
|
||||
|
||||
st.stop();
|
||||
})();
|
||||
|
||||
@ -39,13 +39,6 @@ assert.commandFailedWithCode(
|
||||
db.runCommand({"collMod": collName, "index": {"keyPattern": {a: 1}, "expireAfterSeconds": -1}}),
|
||||
ErrorCodes.InvalidOptions);
|
||||
|
||||
// Tries to modify with an 'expireAfterSeconds' value too large.
|
||||
assert.commandFailedWithCode(db.runCommand({
|
||||
"collMod": collName,
|
||||
"index": {"keyPattern": {a: 1}, "expireAfterSeconds": 10000000000000}
|
||||
}),
|
||||
ErrorCodes.InvalidOptions);
|
||||
|
||||
// Successfully converts to a TTL index.
|
||||
assert.commandWorked(db.runCommand(
|
||||
{"collMod": collName, "index": {"keyPattern": {a: 1}, "expireAfterSeconds": 100}}));
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
* Test that queries containing $elemMatch correctly use an index if each child expression is
|
||||
* compatible with the index.
|
||||
* @tags: [
|
||||
* assumes_balancer_off,
|
||||
* assumes_read_concern_local,
|
||||
* ]
|
||||
*/
|
||||
@ -64,4 +65,51 @@ assertIndexResults(coll, {a: {$elemMatch: {$not: {$in: [/^a/]}}}}, false, 3);
|
||||
coll.dropIndexes();
|
||||
assert.commandWorked(coll.createIndex({a: 1}));
|
||||
assertIndexResults(coll, {a: {$elemMatch: {$not: {$in: [/^a/]}}}}, false, 3);
|
||||
|
||||
(function() {
|
||||
assert(coll.drop());
|
||||
assert.commandWorked(coll.insert({a: [{b: {c: "x"}}]}));
|
||||
assert.commandWorked(coll.createIndex({"a.b.c": 1}));
|
||||
|
||||
// Tests $elemMatch with path components that are empty strings. The system should not attempt to
|
||||
// use the index for these queries.
|
||||
assertIndexResults(coll, {"": {$elemMatch: {"a.b.c": "x"}}}, false, 0);
|
||||
assertIndexResults(coll, {"": {$all: [{$elemMatch: {"a.b.c": "x"}}]}}, false, 0);
|
||||
assertIndexResults(coll, {a: {$elemMatch: {"": {$elemMatch: {"b.c": "x"}}}}}, false, 0);
|
||||
|
||||
// Tests $elemMatch with supporting index and no path components that are empty strings.
|
||||
assertIndexResults(coll, {a: {$elemMatch: {"b.c": "x"}}}, true, 1);
|
||||
assertIndexResults(coll, {a: {$all: [{$elemMatch: {"b.c": "x"}}]}}, true, 1);
|
||||
})();
|
||||
|
||||
(function() {
|
||||
const coll = db.index_elemmatch1;
|
||||
coll.drop();
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
const bulk = coll.initializeUnorderedBulkOp();
|
||||
for (let a = 0; a < 10; a++) {
|
||||
for (let b = 0; b < 10; b++) {
|
||||
bulk.insert({a: a, b: b % 10, arr: [{x: x++ % 10, y: y++ % 10}]});
|
||||
}
|
||||
}
|
||||
assert.commandWorked(bulk.execute());
|
||||
|
||||
assert.commandWorked(coll.createIndex({a: 1, b: 1}));
|
||||
assert.commandWorked(coll.createIndex({"arr.x": 1, a: 1}));
|
||||
|
||||
const query = {
|
||||
a: 5,
|
||||
b: {$in: [1, 3, 5]},
|
||||
arr: {$elemMatch: {x: 5, y: 5}}
|
||||
};
|
||||
|
||||
const count = coll.find(query).itcount();
|
||||
assert.eq(count, 1);
|
||||
|
||||
const explain = coll.find(query).hint({"arr.x": 1, a: 1}).explain("executionStats");
|
||||
assert.commandWorked(explain);
|
||||
assert.eq(count, explain.executionStats.totalKeysExamined, explain);
|
||||
})();
|
||||
})();
|
||||
@ -5,6 +5,7 @@
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
db.filler_collection.drop();
|
||||
assert.commandWorked(db.createCollection("filler_collection"));
|
||||
|
||||
var missingColl = db.explain_null_collection;
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Tests find with $elemMatch when supporting indexes are in place.
|
||||
* @tags: [
|
||||
* assumes_balancer_off,
|
||||
* assumes_read_concern_local,
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
const coll = db.index_elemmatch1;
|
||||
coll.drop();
|
||||
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
const bulk = coll.initializeUnorderedBulkOp();
|
||||
for (let a = 0; a < 10; a++) {
|
||||
for (let b = 0; b < 10; b++) {
|
||||
bulk.insert({a: a, b: b % 10, arr: [{x: x++ % 10, y: y++ % 10}]});
|
||||
}
|
||||
}
|
||||
assert.commandWorked(bulk.execute());
|
||||
|
||||
assert.commandWorked(coll.createIndex({a: 1, b: 1}));
|
||||
assert.commandWorked(coll.createIndex({"arr.x": 1, a: 1}));
|
||||
|
||||
const query = {
|
||||
a: 5,
|
||||
b: {$in: [1, 3, 5]},
|
||||
arr: {$elemMatch: {x: 5, y: 5}}
|
||||
};
|
||||
|
||||
const count = coll.find(query).itcount();
|
||||
assert.eq(count, 1);
|
||||
|
||||
const explain = coll.find(query).hint({"arr.x": 1, a: 1}).explain("executionStats");
|
||||
assert.commandWorked(explain);
|
||||
assert.eq(count, explain.executionStats.totalKeysExamined, explain);
|
||||
})();
|
||||
18
jstests/core/mr_single_reduce.js
Normal file
18
jstests/core/mr_single_reduce.js
Normal file
@ -0,0 +1,18 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
const coll = db.bar;
|
||||
|
||||
assert.commandWorked(coll.insert({x: 1}));
|
||||
|
||||
const map = function() {
|
||||
emit(0, "mapped value");
|
||||
};
|
||||
|
||||
const reduce = function(key, values) {
|
||||
return "reduced value";
|
||||
};
|
||||
|
||||
const res = assert.commandWorked(
|
||||
db.runCommand({mapReduce: 'bar', map: map, reduce: reduce, out: {inline: 1}}));
|
||||
assert.eq(res.results[0], {_id: 0, value: "reduced value"});
|
||||
}());
|
||||
@ -1,7 +1,10 @@
|
||||
/**
|
||||
* Ensures that the options passed in for TTL indexes are validated during index creation.
|
||||
*
|
||||
* @tags: [requires_ttl_index]
|
||||
* @tags: [
|
||||
* requires_fcv_61,
|
||||
* requires_ttl_index,
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
@ -16,11 +19,10 @@ assert.commandFailedWithCode(
|
||||
assert.commandFailedWithCode(coll.createIndexes([{x: 1}], {expireAfterSeconds: 9999999999999999}),
|
||||
ErrorCodes.CannotCreateIndex);
|
||||
|
||||
// Ensure that we cannot provide a time that is larger than the current epoch time.
|
||||
// Ensure that we can provide a time that is larger than the current epoch time.
|
||||
let secondsSinceEpoch = Date.now() / 1000;
|
||||
assert.commandFailedWithCode(
|
||||
coll.createIndexes([{x: 1}], {expireAfterSeconds: secondsSinceEpoch + 1000}),
|
||||
ErrorCodes.CannotCreateIndex);
|
||||
assert.commandWorked(
|
||||
coll.createIndexes([{x_before_epoch: 1}], {expireAfterSeconds: secondsSinceEpoch + 1000}));
|
||||
|
||||
// 'expireAfterSeconds' cannot be less than 0.
|
||||
assert.commandFailedWithCode(coll.createIndexes([{x: 1}], {expireAfterSeconds: -1}),
|
||||
|
||||
@ -8,6 +8,43 @@
|
||||
|
||||
load('jstests/disk/libs/wt_file_helper.js');
|
||||
|
||||
function checkTableLogSettings(conn, enabled) {
|
||||
conn.getDBNames().forEach(function(d) {
|
||||
let collNames =
|
||||
conn.getDB(d)
|
||||
.runCommand({listCollections: 1, nameOnly: true, filter: {type: "collection"}})
|
||||
.cursor.firstBatch;
|
||||
|
||||
collNames.forEach(function(c) {
|
||||
let stats = conn.getDB(d).runCommand({collStats: c.name});
|
||||
|
||||
let logStr = "log=(enabled=" + (enabled ? "true" : "false") + ")";
|
||||
if (d == "local") {
|
||||
if (c.name == "replset.minvalid" && !enabled) {
|
||||
// This collection is never logged in a replica set.
|
||||
logStr = "log=(enabled=false)";
|
||||
} else {
|
||||
// All other collections and indexes in the 'local' database have table
|
||||
// logging enabled always.
|
||||
logStr = "log=(enabled=true)";
|
||||
}
|
||||
}
|
||||
|
||||
assert.eq(true, stats.wiredTiger.creationString.includes(logStr));
|
||||
Object.keys(stats.indexDetails).forEach(function(i) {
|
||||
assert.eq(true, stats.indexDetails[i].creationString.includes(logStr));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function checkTableChecksFileRemoved(dbpath) {
|
||||
let files = listFiles(dbpath);
|
||||
for (file of files) {
|
||||
assert.eq(false, file.name.includes("_wt_table_checks"));
|
||||
}
|
||||
}
|
||||
|
||||
// Create a bunch of collections under various database names.
|
||||
let conn = MongoRunner.runMongod({});
|
||||
const dbpath = conn.dbpath;
|
||||
@ -16,112 +53,75 @@ for (let i = 0; i < 10; i++) {
|
||||
assert.commandWorked(conn.getDB(i.toString()).createCollection(i.toString()));
|
||||
}
|
||||
|
||||
checkTableLogSettings(conn, /*enabled=*/true);
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
/**
|
||||
* Test 1. The regular case, where no table logging setting modifications are needed.
|
||||
* Test 1. Change into a single node replica set, which requires all of the table logging settings
|
||||
* to be updated. Write the '_wt_table_checks' file and check that it gets removed.
|
||||
*/
|
||||
jsTest.log("Test 1.");
|
||||
|
||||
conn = startMongodOnExistingPath(dbpath, {});
|
||||
checkLog.containsJson(conn, 4366408, {loggingEnabled: true});
|
||||
writeFile(dbpath + "/_wt_table_checks", "");
|
||||
conn = startMongodOnExistingPath(
|
||||
dbpath, {replSet: "mySet", setParameter: {logComponentVerbosity: tojson({verbosity: 1})}});
|
||||
checkTableChecksFileRemoved(dbpath);
|
||||
|
||||
// Changing table logging settings.
|
||||
checkLog.containsJson(conn, 22432);
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
/**
|
||||
* Test 2. Repair checks all of the table logging settings.
|
||||
* Test 2. Restart in standalone mode with wiredTigerSkipTableLoggingChecksOnStartup. No table log
|
||||
* settings are updated. Write the '_wt_table_checks' file and check that it gets removed.
|
||||
*/
|
||||
jsTest.log("Test 2.");
|
||||
|
||||
assertRepairSucceeds(dbpath, conn.port, {});
|
||||
|
||||
// Cannot use checkLog here as the server is no longer running.
|
||||
let logContents = rawMongoProgramOutput();
|
||||
assert(logContents.indexOf(
|
||||
"Modifying the table logging settings for all existing WiredTiger tables") > 0);
|
||||
|
||||
/**
|
||||
* Test 3. Explicitly create the '_wt_table_checks' file to force all of the table logging setting
|
||||
* modifications to be made.
|
||||
*/
|
||||
jsTest.log("Test 3.");
|
||||
|
||||
let files = listFiles(dbpath);
|
||||
for (f in files) {
|
||||
assert(!files[f].name.includes("_wt_table_checks"));
|
||||
}
|
||||
|
||||
writeFile(dbpath + "/_wt_table_checks", "");
|
||||
|
||||
// Cannot skip table logging checks on startup when there are previously incomplete table checks.
|
||||
assert.throws(() => startMongodOnExistingPath(
|
||||
dbpath, {setParameter: "wiredTigerSkipTableLoggingChecksOnStartup=true"}));
|
||||
|
||||
conn = startMongodOnExistingPath(dbpath, {});
|
||||
checkLog.containsJson(
|
||||
conn, 4366405, {loggingEnabled: true, repair: false, hasPreviouslyIncompleteTableChecks: true});
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
/**
|
||||
* Test 4. Change into a single replica set, which requires all of the table logging settings to be
|
||||
* updated. But simulate an interruption/crash while starting up during the table logging check
|
||||
* phase.
|
||||
*
|
||||
* The next start up will detect an unclean shutdown causing all of the table logging settings to be
|
||||
* updated.
|
||||
*/
|
||||
jsTest.log("Test 4.");
|
||||
|
||||
assert.throws(() => startMongodOnExistingPath(dbpath, {
|
||||
replSet: "mySet",
|
||||
setParameter: "failpoint.crashAfterUpdatingFirstTableLoggingSettings=" +
|
||||
tojson({"mode": "alwaysOn"})
|
||||
}));
|
||||
|
||||
// Cannot use checkLog here as the server is no longer running.
|
||||
logContents = rawMongoProgramOutput();
|
||||
assert(logContents.indexOf(
|
||||
"Crashing due to 'crashAfterUpdatingFirstTableLoggingSettings' fail point") > 0);
|
||||
|
||||
// The '_wt_table_checks' still exists, so all table logging settings should be modified.
|
||||
conn = startMongodOnExistingPath(dbpath, {});
|
||||
checkLog.containsJson(
|
||||
conn, 4366405, {loggingEnabled: true, repair: false, hasPreviouslyIncompleteTableChecks: true});
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
/**
|
||||
* Test 5. Change into a single node replica set, which requires all of the table logging settings
|
||||
* to be updated as the node was successfully started up as a standalone the last time.
|
||||
*/
|
||||
jsTest.log("Test 5.");
|
||||
|
||||
conn = startMongodOnExistingPath(dbpath, {replSet: "mySet"});
|
||||
checkLog.containsJson(conn, 4366406, {loggingEnabled: false});
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
/**
|
||||
* Test 6. Restart as a standalone and skip table logging checks on startup. Verify that restarting
|
||||
* as a replica set again does not require any table logging modifications.
|
||||
*/
|
||||
jsTest.log("Test 6.");
|
||||
|
||||
conn = startMongodOnExistingPath(dbpath, {
|
||||
setParameter: {
|
||||
wiredTigerSkipTableLoggingChecksOnStartup: true,
|
||||
logComponentVerbosity: tojson({verbosity: 1})
|
||||
}
|
||||
});
|
||||
checkTableChecksFileRemoved(dbpath);
|
||||
|
||||
// Skipping table logging checks for all existing tables.
|
||||
checkLog.containsJson(conn, 5548301, {wiredTigerSkipTableLoggingChecksOnStartup: true});
|
||||
|
||||
// Log level 1 prints each individual table it skips table logging checks for.
|
||||
// Skipping table logging checks.
|
||||
checkLog.containsJson(conn, 5548302);
|
||||
|
||||
// Changing table logging settings.
|
||||
assert(checkLog.checkContainsWithCountJson(conn, 22432, undefined, 0));
|
||||
checkTableLogSettings(conn, /*enabled=*/false);
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
conn = startMongodOnExistingPath(dbpath, {replSet: "mySet"});
|
||||
/**
|
||||
* Test 3. Change into a single node replica set again. Table log settings are checked but none are
|
||||
* changed. Write the '_wt_table_checks' file and check that it gets removed.
|
||||
*/
|
||||
jsTestLog("Test 3.");
|
||||
writeFile(dbpath + "/_wt_table_checks", "");
|
||||
conn = startMongodOnExistingPath(
|
||||
dbpath, {replSet: "mySet", setParameter: {logComponentVerbosity: tojson({verbosity: 1})}});
|
||||
checkTableChecksFileRemoved(dbpath);
|
||||
|
||||
// No table logging settings modifications are required.
|
||||
checkLog.containsJson(conn, 4366408);
|
||||
// Changing table logging settings.
|
||||
assert(checkLog.checkContainsWithCountJson(conn, 22432, undefined, 0));
|
||||
MongoRunner.stopMongod(conn);
|
||||
|
||||
/**
|
||||
* Test 4. Back to standalone. Check that the table log settings are enabled. Write the
|
||||
* '_wt_table_checks' file and check that it gets removed.
|
||||
*/
|
||||
jsTest.log("Test 4.");
|
||||
writeFile(dbpath + "/_wt_table_checks", "");
|
||||
conn = startMongodOnExistingPath(dbpath,
|
||||
{setParameter: {logComponentVerbosity: tojson({verbosity: 1})}});
|
||||
checkTableChecksFileRemoved(dbpath);
|
||||
|
||||
// Changing table logging settings.
|
||||
checkLog.containsJson(conn, 22432);
|
||||
|
||||
// Skipping table logging checks.
|
||||
assert(checkLog.checkContainsWithCountJson(conn, 5548302, undefined, 0));
|
||||
checkTableLogSettings(conn, /*enabled=*/true);
|
||||
MongoRunner.stopMongod(conn);
|
||||
}());
|
||||
|
||||
@ -138,7 +138,10 @@ function assertNumMatchingOplogEventsForShard(stats, shardName, expectedTotalRet
|
||||
assert(stats.shards.hasOwnProperty(shardName), stats);
|
||||
assert.eq(Object.keys(stats.shards[shardName].stages[0])[0], "$cursor", stats);
|
||||
const executionStats = stats.shards[shardName].stages[0].$cursor.executionStats;
|
||||
assert.eq(executionStats.nReturned, expectedTotalReturned, executionStats);
|
||||
assert.eq(executionStats.nReturned,
|
||||
expectedTotalReturned,
|
||||
() => `Expected ${expectedTotalReturned} events on shard ${shardName} but got ` +
|
||||
`${executionStats.nReturned}. Execution stats:\n${tojson(executionStats)}`);
|
||||
}
|
||||
|
||||
// Returns a newly created sharded collection sharded by caller provided shard key.
|
||||
@ -180,7 +183,10 @@ function verifyChangeStreamOnWholeCluster(
|
||||
eventIdentifierList.forEach(eventIdentifier => {
|
||||
assert.soon(() => cursor.hasNext(), {op: op, eventIdentifier: eventIdentifier});
|
||||
const event = cursor.next();
|
||||
assert.eq(event.operationType, op, event);
|
||||
assert.eq(event.operationType,
|
||||
op,
|
||||
() => `Expected "${op}" but got "${event.operationType}". Full event: ` +
|
||||
`${tojson(event)}`);
|
||||
|
||||
if (op == "dropDatabase") {
|
||||
assert.eq(event.ns.db, eventIdentifier, event);
|
||||
|
||||
@ -98,7 +98,7 @@ let c = pscWatch(sdb, "coll", shardId);
|
||||
for (let i = 1; i <= 4; i++) {
|
||||
sdb.coll.insertOne({location: 2, i});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
}
|
||||
assert(!c.hasNext());
|
||||
@ -108,12 +108,12 @@ c = pscWatch(sdb, 1, shardId);
|
||||
|
||||
sdb.coll.insertOne({location: 3});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
|
||||
sdb.coll2.insertOne({location: 4});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
|
||||
assert(!c.hasNext());
|
||||
@ -129,7 +129,7 @@ assert(!c.hasNext());
|
||||
|
||||
sdb.toBeCreated.insertOne({location: 8});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
|
||||
assert(!c.hasNext());
|
||||
@ -164,7 +164,7 @@ c = pscWatch(sdb, "coll", shardId);
|
||||
|
||||
sdb.coll.insertOne({location: 5, _id: -2});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
|
||||
sdb.coll.insertOne({location: 6, _id: 2});
|
||||
@ -176,12 +176,12 @@ c = pscWatch(sdb.getSiblingDB("admin"), 1, shardId, {}, {allChangesForCluster: t
|
||||
|
||||
sdb.coll.insertOne({location: 7, _id: -3});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
|
||||
sdb.coll2.insertOne({location: 8, _id: -4});
|
||||
assert(!c.isExhausted());
|
||||
assert(c.hasNext());
|
||||
assert.soon(() => c.hasNext());
|
||||
c.next();
|
||||
|
||||
sdb.coll.insertOne({location: 9, _id: 3});
|
||||
|
||||
47
jstests/noPassthrough/floating_point_exp_contraction_off.js
Normal file
47
jstests/noPassthrough/floating_point_exp_contraction_off.js
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Validates that the server doesn't use fused multiply-add instructions (-ffp-contract=off).
|
||||
*
|
||||
* @tags: [
|
||||
* multiversion_incompatible,
|
||||
* ]
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const conn = MongoRunner.runMongod();
|
||||
|
||||
const coll = conn.getDB('test').getCollection('c');
|
||||
|
||||
assert.commandWorked(coll.createIndex({loc: "2dsphere"}));
|
||||
assert.commandWorked(coll.insertOne({
|
||||
"loc": {
|
||||
"type": "Polygon",
|
||||
"coordinates": [[
|
||||
[-85.0329458713531, 41.3677690255613],
|
||||
[-85.0296092033386, 41.3677690255613],
|
||||
[-85.0296092033386, 41.360594065847],
|
||||
[-85.0329458713531, 41.360594065847],
|
||||
[-85.0329458713531, 41.3677690255613]
|
||||
]]
|
||||
}
|
||||
}));
|
||||
|
||||
// Assert that the query returns the document. If the query does not return any result, then
|
||||
// this is likely because of different rounding due to fused multiply-add instructions on platforms
|
||||
// that have native support, like arm64.
|
||||
assert.eq(
|
||||
1,
|
||||
coll.find({
|
||||
"loc": {
|
||||
"$near": {
|
||||
"$geometry":
|
||||
{"type": "Point", "coordinates": [-85.031218528747559, 41.364586470348961]},
|
||||
"$maxDistance": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
.itcount());
|
||||
|
||||
MongoRunner.stopMongod(conn);
|
||||
}());
|
||||
29
jstests/noPassthrough/mr_single_reduce_optimization.js
Normal file
29
jstests/noPassthrough/mr_single_reduce_optimization.js
Normal file
@ -0,0 +1,29 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
// See SERVER-68766. Verify that the reduce function is not run on a single value if the relevant
|
||||
// flag is enabled.
|
||||
|
||||
const conn = MongoRunner.runMongod({setParameter: {mrEnableSingleReduceOptimization: true}});
|
||||
const testDB = conn.getDB('foo');
|
||||
const coll = testDB.bar;
|
||||
|
||||
assert.commandWorked(coll.insert({x: 1}));
|
||||
|
||||
const map = function() {
|
||||
emit(0, "mapped value");
|
||||
};
|
||||
|
||||
const reduce = function(key, values) {
|
||||
return "reduced value";
|
||||
};
|
||||
|
||||
let res = assert.commandWorked(
|
||||
testDB.runCommand({mapReduce: 'bar', map: map, reduce: reduce, out: {inline: 1}}));
|
||||
assert.eq(res.results[0], {_id: 0, value: "mapped value"});
|
||||
assert.commandWorked(coll.insert({x: 2}));
|
||||
res = assert.commandWorked(
|
||||
testDB.runCommand({mapReduce: 'bar', map: map, reduce: reduce, out: {inline: 1}}));
|
||||
assert.eq(res.results[0], {_id: 0, value: "reduced value"});
|
||||
|
||||
MongoRunner.stopMongod(conn);
|
||||
}());
|
||||
146
jstests/noPassthrough/retryable_writes_operation_metrics.js
Normal file
146
jstests/noPassthrough/retryable_writes_operation_metrics.js
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Tests resource consumption metrics for retryable writes.
|
||||
* Retryable writes persist transaction documents in "config.transaction" and all reads and writes
|
||||
* to "config.transaction" should not trigger any operation metrics.
|
||||
*
|
||||
* @tags: [
|
||||
* requires_replication,
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
load("jstests/libs/retryable_writes_util.js");
|
||||
|
||||
function setupReplicaSet() {
|
||||
var rst = new ReplSetTest({
|
||||
nodes: 2,
|
||||
nodeOptions: {setParameter: {"aggregateOperationResourceConsumptionMetrics": true}}
|
||||
});
|
||||
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
return rst;
|
||||
}
|
||||
|
||||
function clearMetrics(conn) {
|
||||
conn.getDB('admin').aggregate([{$operationMetrics: {clearMetrics: true}}]);
|
||||
}
|
||||
|
||||
function getMetrics(conn) {
|
||||
const cursor = conn.getDB('admin').aggregate([{$operationMetrics: {}}]);
|
||||
|
||||
let allMetrics = {};
|
||||
while (cursor.hasNext()) {
|
||||
let doc = cursor.next();
|
||||
allMetrics[doc.db] = doc;
|
||||
}
|
||||
return allMetrics;
|
||||
}
|
||||
|
||||
function assertMetricsZeroRead(metrics, dbName) {
|
||||
assert(metrics[dbName]);
|
||||
assert.eq(metrics[dbName].primaryMetrics.docBytesRead, 0);
|
||||
assert.eq(metrics[dbName].primaryMetrics.docUnitsRead, 0);
|
||||
assert.eq(metrics[dbName].primaryMetrics.idxEntryBytesRead, 0);
|
||||
assert.eq(metrics[dbName].primaryMetrics.idxEntryUnitsRead, 0);
|
||||
}
|
||||
|
||||
function assertMetricsWritten(metrics, dbName, expectedWritten) {
|
||||
assert(metrics[dbName]);
|
||||
assert.eq(metrics[dbName].docBytesWritten, expectedWritten.docBytesWritten);
|
||||
assert.eq(metrics[dbName].docUnitsWritten, expectedWritten.docUnitsWritten);
|
||||
assert.eq(metrics[dbName].idxEntryBytesWritten, expectedWritten.idxEntryBytesWritten);
|
||||
assert.eq(metrics[dbName].idxEntryUnitsWritten, expectedWritten.idxEntryUnitsWritten);
|
||||
assert.eq(metrics[dbName].totalUnitsWritten, expectedWritten.totalUnitsWritten);
|
||||
}
|
||||
|
||||
function runRetryableTransaction(sessionDb, txnNumber, cmdObj) {
|
||||
let cmd = Object.assign(
|
||||
{}, cmdObj, {txnNumber: NumberLong(txnNumber), startTransaction: true, autocommit: false});
|
||||
|
||||
assert.commandWorked(sessionDb.runCommand(cmd));
|
||||
assert.commandWorked(sessionDb.adminCommand({
|
||||
commitTransaction: 1,
|
||||
txnNumber: NumberLong(txnNumber),
|
||||
autocommit: false,
|
||||
writeConcern: {w: "majority"}
|
||||
}));
|
||||
}
|
||||
|
||||
function runRetryableWriteCmd(sessionDb, txnNumber, cmdObj) {
|
||||
let cmd = Object.assign({}, cmdObj, {txnNumber: NumberLong(txnNumber)});
|
||||
jsTest.log("run retryable write with command :" + tojson(cmd));
|
||||
assert.commandWorked(sessionDb.runCommand(cmd));
|
||||
}
|
||||
|
||||
const rst = setupReplicaSet();
|
||||
const primary = rst.getPrimary();
|
||||
const kDbName = "testDb";
|
||||
const kCollName = "testColl";
|
||||
const testDb = primary.getDB(kDbName);
|
||||
|
||||
assert.commandWorked(testDb.createCollection(kCollName));
|
||||
|
||||
const session = testDb.getMongo().startSession();
|
||||
let sessionDB = session.getDatabase(kDbName);
|
||||
let txnNumber = 0;
|
||||
|
||||
let makeInsertCmdObj = (docs) => {
|
||||
return {insert: kCollName, documents: docs, ordered: false};
|
||||
};
|
||||
|
||||
let makeDocs = (fromId, toId) => {
|
||||
let docs = [];
|
||||
for (let i = fromId; i <= toId; i++) {
|
||||
docs.push({_id: i, number: i});
|
||||
}
|
||||
return docs;
|
||||
};
|
||||
|
||||
let expectedWritten;
|
||||
|
||||
jsTest.log("Tests non-retryable insert comamnd which has no transaction number.");
|
||||
{
|
||||
clearMetrics(primary);
|
||||
let cmdObj = makeInsertCmdObj(makeDocs(1, 3));
|
||||
assert.commandWorked(testDb.runCommand(cmdObj));
|
||||
let metrics = getMetrics(primary);
|
||||
|
||||
assertMetricsZeroRead(metrics, kDbName);
|
||||
|
||||
// Init the expected doc, index and total data written. The following retryable writes
|
||||
// should have the same output as the size of inserted user data is same.
|
||||
expectedWritten = {
|
||||
docBytesWritten: metrics[kDbName].docBytesWritten,
|
||||
docUnitsWritten: metrics[kDbName].docUnitsWritten,
|
||||
idxEntryBytesWritten: metrics[kDbName].idxEntryBytesWritten,
|
||||
idxEntryUnitsWritten: metrics[kDbName].idxEntryUnitsWritten,
|
||||
totalUnitsWritten: metrics[kDbName].totalUnitsWritten
|
||||
};
|
||||
}
|
||||
|
||||
jsTest.log("Tests retryable commitTransaction command which inserts documents.");
|
||||
{
|
||||
clearMetrics(primary);
|
||||
let cmdObj = makeInsertCmdObj(makeDocs(4, 6));
|
||||
runRetryableTransaction(sessionDB, txnNumber, cmdObj);
|
||||
let metrics = getMetrics(primary);
|
||||
assertMetricsZeroRead(metrics, kDbName);
|
||||
assertMetricsWritten(metrics, kDbName, expectedWritten);
|
||||
}
|
||||
|
||||
txnNumber++;
|
||||
jsTest.log("Tests retryable insert command.");
|
||||
{
|
||||
clearMetrics(primary);
|
||||
let cmdObj = makeInsertCmdObj(makeDocs(7, 9));
|
||||
runRetryableWriteCmd(sessionDB, txnNumber, cmdObj);
|
||||
let metrics = getMetrics(primary);
|
||||
assertMetricsZeroRead(metrics, kDbName);
|
||||
assertMetricsWritten(metrics, kDbName, expectedWritten);
|
||||
}
|
||||
|
||||
session.endSession();
|
||||
rst.stopSet();
|
||||
}());
|
||||
120
jstests/noPassthrough/ttl_expire_nan.js
Normal file
120
jstests/noPassthrough/ttl_expire_nan.js
Normal file
@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Tests TTL indexes with NaN for 'expireAfterSeconds'.
|
||||
*
|
||||
* Existing TTL indexes from older versions of the server may contain a NaN for the duration.
|
||||
* Newer server versions (5.0+) normalize the TTL duration to 0.
|
||||
*
|
||||
* @tags: [
|
||||
* requires_replication,
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
load("jstests/libs/fail_point_util.js");
|
||||
load('jstests/noPassthrough/libs/index_build.js');
|
||||
|
||||
const rst = new ReplSetTest({
|
||||
nodes: [{}, {rsConfig: {votes: 0, priority: 0}}],
|
||||
nodeOptions: {setParameter: {ttlMonitorSleepSecs: 5}},
|
||||
// Sync from primary only so that we have a well-defined node to check listIndexes behavior.
|
||||
settings: {chainingAllowed: false},
|
||||
});
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
|
||||
let primary = rst.getPrimary();
|
||||
const db = primary.getDB('test');
|
||||
const coll = db.t;
|
||||
|
||||
// The test cases here revolve around having a TTL index in the catalog with a NaN
|
||||
// 'expireAfterSeconds'. The current createIndexes behavior will overwrite NaN with int32::max
|
||||
// unless we use a fail point.
|
||||
const fp = configureFailPoint(primary, 'skipTTLIndexNaNExpireAfterSecondsValidation');
|
||||
try {
|
||||
assert.commandWorked(coll.createIndex({t: 1}, {expireAfterSeconds: NaN}));
|
||||
} finally {
|
||||
fp.off();
|
||||
}
|
||||
|
||||
assert.commandWorked(coll.insert({_id: 0, t: ISODate()}));
|
||||
|
||||
// Wait for "TTL indexes require the expire field to be numeric, skipping TTL job" log message.
|
||||
checkLog.containsJson(primary, 22542, {ns: coll.getFullName()});
|
||||
|
||||
// TTL index should be replicated to the secondary with a NaN 'expireAfterSeconds'.
|
||||
const secondary = rst.getSecondary();
|
||||
checkLog.containsJson(secondary, 20384, {
|
||||
namespace: coll.getFullName(),
|
||||
properties: (spec) => {
|
||||
jsTestLog('TTL index on secondary: ' + tojson(spec));
|
||||
return isNaN(spec.expireAfterSeconds);
|
||||
}
|
||||
});
|
||||
|
||||
assert.eq(
|
||||
coll.countDocuments({}), 1, 'ttl index with NaN duration should not remove any documents.');
|
||||
|
||||
// Confirm that TTL index is replicated with a non-zero 'expireAfterSeconds' during initial sync.
|
||||
const newNode = rst.add({rsConfig: {votes: 0, priority: 0}});
|
||||
rst.reInitiate();
|
||||
rst.waitForState(newNode, ReplSetTest.State.SECONDARY);
|
||||
rst.awaitReplication();
|
||||
let newNodeTestDB = newNode.getDB(db.getName());
|
||||
let newNodeColl = newNodeTestDB.getCollection(coll.getName());
|
||||
const newNodeIndexes = IndexBuildTest.assertIndexes(newNodeColl, 2, ['_id_', 't_1']);
|
||||
const newNodeSpec = newNodeIndexes.t_1;
|
||||
jsTestLog('TTL index on initial sync node: ' + tojson(newNodeSpec));
|
||||
assert(newNodeSpec.hasOwnProperty('expireAfterSeconds'),
|
||||
'Index was not replicated as a TTL index during initial sync.');
|
||||
assert.gt(newNodeSpec.expireAfterSeconds,
|
||||
0,
|
||||
'NaN expireAferSeconds was replicated as zero during initial sync.');
|
||||
|
||||
// Check that listIndexes on the primary logged a "Fixing expire field from TTL index spec" message
|
||||
// during the NaN 'expireAfterSeconds' conversion.
|
||||
checkLog.containsJson(primary, 6835900, {namespace: coll.getFullName()});
|
||||
|
||||
// Confirm that a node with an existing TTL index with NaN 'expireAfterSeconds' will convert the
|
||||
// duration on the TTL index from NaN to a large positive value when it becomes the primary node.
|
||||
// When stepping down the primary, we use 'force' because there's no other electable node.
|
||||
// Subsequently, we wait for the stepped down node to become primary again.
|
||||
// To confirm that the TTL index has been fixed, we check the oplog for a collMod operation on the
|
||||
// TTL index that changes the `expireAfterSeconds` field from NaN to a large positive value.
|
||||
assert.commandWorked(primary.adminCommand({replSetStepDown: 5, force: true}));
|
||||
primary = rst.waitForPrimary();
|
||||
const collModOplogEntries =
|
||||
rst.findOplog(primary,
|
||||
{
|
||||
op: 'c',
|
||||
ns: coll.getDB().getCollection('$cmd').getFullName(),
|
||||
'o.collMod': coll.getName(),
|
||||
'o.index.name': 't_1',
|
||||
'o.index.expireAfterSeconds': newNodeSpec.expireAfterSeconds
|
||||
},
|
||||
/*limit=*/1)
|
||||
.toArray();
|
||||
assert.eq(collModOplogEntries.length,
|
||||
1,
|
||||
'TTL index with NaN expireAfterSeconds was not fixed using collMod during step-up: ' +
|
||||
tojson(rst.findOplog(primary, {op: {$ne: 'n'}}, /*limit=*/10).toArray()));
|
||||
|
||||
// Confirm that createIndexes will overwrite a NaN 'expireAfterSeconds' in a TTL index before saving
|
||||
// it to the catalog and replicating it downstream.
|
||||
const coll2 = db.w;
|
||||
assert.commandWorked(coll2.createIndex({t: 1}, {expireAfterSeconds: NaN}));
|
||||
assert.commandWorked(coll2.insert({_id: 0, t: ISODate()}));
|
||||
|
||||
// TTL index should be replicated to the secondary with a non-NaN 'expireAfterSeconds'.
|
||||
checkLog.containsJson(secondary, 20384, {
|
||||
namespace: coll2.getFullName(),
|
||||
properties: (spec) => {
|
||||
jsTestLog('TTL index on secondary (with overwritten NaN expireAfterSeconds): ' +
|
||||
tojson(spec));
|
||||
return spec.hasOwnProperty('expireAfterSeconds') && !isNaN(spec.expireAfterSeconds) &&
|
||||
spec.expireAfterSeconds === newNodeSpec.expireAfterSeconds;
|
||||
}
|
||||
});
|
||||
|
||||
rst.stopSet();
|
||||
})();
|
||||
56
jstests/noPassthrough/ttl_expire_nan_warning_on_startup.js
Normal file
56
jstests/noPassthrough/ttl_expire_nan_warning_on_startup.js
Normal file
@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Tests that a server containing a TTL index with NaN for 'expireAfterSeconds'
|
||||
* will log a warning on startup.
|
||||
*
|
||||
* @tags: [
|
||||
* requires_persistence,
|
||||
* requires_replication,
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
load("jstests/libs/fail_point_util.js");
|
||||
load('jstests/noPassthrough/libs/index_build.js');
|
||||
|
||||
const rst = new ReplSetTest({nodes: [{}, {rsConfig: {votes: 0, priority: 0}}]});
|
||||
rst.startSet();
|
||||
rst.initiate();
|
||||
|
||||
let primary = rst.getPrimary();
|
||||
const db = primary.getDB('test');
|
||||
const coll = db.t;
|
||||
|
||||
// The test cases here revolve around having a TTL index in the catalog with a NaN
|
||||
// 'expireAfterSeconds'. The current createIndexes behavior will overwrite NaN with int32::max
|
||||
// unless we use a fail point.
|
||||
const fp = configureFailPoint(primary, 'skipTTLIndexNaNExpireAfterSecondsValidation');
|
||||
try {
|
||||
assert.commandWorked(coll.createIndex({t: 1}, {expireAfterSeconds: NaN}));
|
||||
} finally {
|
||||
fp.off();
|
||||
}
|
||||
|
||||
assert.commandWorked(coll.insert({_id: 0, t: ISODate()}));
|
||||
|
||||
// Force checkpoint in storage engine to ensure index is part of the catalog in
|
||||
// in finished state at startup.
|
||||
rst.awaitReplication();
|
||||
let secondary = rst.getSecondary();
|
||||
assert.commandWorked(secondary.adminCommand({fsync: 1}));
|
||||
|
||||
// Restart the secondary and check for the startup warning in the logs.
|
||||
secondary = rst.restart(secondary);
|
||||
rst.waitForState(secondary, ReplSetTest.State.SECONDARY);
|
||||
|
||||
// Wait for "Found an existing TTL index with NaN 'expireAfterSeconds' in the catalog" log message.
|
||||
checkLog.containsJson(secondary, 6852200, {
|
||||
ns: coll.getFullName(),
|
||||
spec: (spec) => {
|
||||
jsTestLog('TTL index on secondary at startup: ' + tojson(spec));
|
||||
return isNaN(spec.expireAfterSeconds);
|
||||
}
|
||||
});
|
||||
|
||||
rst.stopSet();
|
||||
})();
|
||||
80
jstests/noPassthrough/txn_index_catalog_changes.js
Normal file
80
jstests/noPassthrough/txn_index_catalog_changes.js
Normal file
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Verifies that a multi-document transaction aborts with WriteConflictError if an index build has
|
||||
* committed since the transaction's read snapshot.
|
||||
*
|
||||
* @tags: [
|
||||
* requires_replication,
|
||||
* ]
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
const replTest = new ReplSetTest({nodes: 2});
|
||||
replTest.startSet();
|
||||
replTest.initiate();
|
||||
|
||||
const primary = replTest.getPrimary();
|
||||
const db = primary.getDB('test');
|
||||
|
||||
// Transaction inserting an index key.
|
||||
{
|
||||
assert.commandWorked(db['c'].insertOne({_id: 0, num: 0}));
|
||||
|
||||
const s0 = db.getMongo().startSession();
|
||||
s0.startTransaction();
|
||||
assert.commandWorked(s0.getDatabase('test')['c'].deleteOne({_id: 0}));
|
||||
s0.commitTransaction();
|
||||
|
||||
const clusterTime = s0.getClusterTime().clusterTime;
|
||||
|
||||
assert.commandWorked(db['c'].createIndex({num: 1}));
|
||||
|
||||
// Start a transaction whose snapshot predates the completion of the index build, and which
|
||||
// reserves an oplog entry after the index build commits.
|
||||
try {
|
||||
const s1 = db.getMongo().startSession();
|
||||
s1.startTransaction({readConcern: {level: "snapshot", atClusterTime: clusterTime}});
|
||||
s1.getDatabase('test').c.insertOne({_id: 1, num: 1});
|
||||
|
||||
// Transaction should have failed.
|
||||
assert(0);
|
||||
} catch (e) {
|
||||
assert(e.hasOwnProperty("errorLabels"), tojson(e));
|
||||
assert.contains("TransientTransactionError", e.errorLabels, tojson(e));
|
||||
assert.eq(e["code"], ErrorCodes.WriteConflict, tojson(e));
|
||||
}
|
||||
}
|
||||
|
||||
db.c.drop();
|
||||
|
||||
// Transaction deleting an index key.
|
||||
{
|
||||
assert.commandWorked(db.createCollection('c'));
|
||||
|
||||
const s0 = db.getMongo().startSession();
|
||||
s0.startTransaction();
|
||||
assert.commandWorked(s0.getDatabase('test')['c'].insertOne({_id: 0, num: 0}));
|
||||
s0.commitTransaction();
|
||||
|
||||
const clusterTime = s0.getClusterTime().clusterTime;
|
||||
|
||||
assert.commandWorked(db['c'].createIndex({num: 1}));
|
||||
|
||||
// Start a transaction whose snapshot predates the completion of the index build, and which
|
||||
// reserves an oplog entry after the index build commits.
|
||||
try {
|
||||
const s1 = db.getMongo().startSession();
|
||||
s1.startTransaction({readConcern: {level: "snapshot", atClusterTime: clusterTime}});
|
||||
s1.getDatabase('test').c.deleteOne({_id: 0});
|
||||
|
||||
// Transaction should have failed.
|
||||
assert(0);
|
||||
} catch (e) {
|
||||
assert(e.hasOwnProperty("errorLabels"), tojson(e));
|
||||
assert.contains("TransientTransactionError", e.errorLabels, tojson(e));
|
||||
assert.eq(e["code"], ErrorCodes.WriteConflict, tojson(e));
|
||||
}
|
||||
}
|
||||
|
||||
replTest.stopSet();
|
||||
})();
|
||||
@ -247,6 +247,194 @@ assertResultsMatchWithAndWithoutPushdown(
|
||||
assertResultsMatchWithAndWithoutPushdown(
|
||||
coll, pipeline, [{_id: "a", ss: 30}, {_id: "b", ss: 60}, {_id: "c", ss: 10}], 2));
|
||||
|
||||
// The second $group stage refers to a top-field below a $switch
|
||||
assertResultsMatchWithAndWithoutPushdown(coll,
|
||||
[
|
||||
{$group: {_id: {$divide: ["$price", 5]}}},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
lowp: {
|
||||
$sum: {
|
||||
$switch: {
|
||||
branches: [{
|
||||
case: {$lte: ["$_id", 3]},
|
||||
then: 1
|
||||
}],
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
highp: {
|
||||
$sum: {
|
||||
$switch: {
|
||||
branches: [{
|
||||
case: {$gt: ["$_id", 3]},
|
||||
then: 1
|
||||
}],
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[{"_id": null, "lowp": 2, "highp": 1}],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to a top-field below a $cond
|
||||
assertResultsMatchWithAndWithoutPushdown(
|
||||
coll,
|
||||
[
|
||||
{$group: {_id: {$divide: ["$price", 5]}}},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
lowp: {$sum: {$cond: [{$lte: ["$_id", 3]}, 1, 0]}},
|
||||
highp: {$sum: {$cond: [{$gt: ["$_id", 3]}, 1, 0]}}
|
||||
}
|
||||
}
|
||||
],
|
||||
[{"_id": null, "lowp": 2, "highp": 1}],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to a top-field below a nested $cond / $ifNull
|
||||
assertResultsMatchWithAndWithoutPushdown(coll,
|
||||
[
|
||||
{$group: {_id: {$divide: ["$price", 5]}}},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
lowp: {
|
||||
$sum: {
|
||||
$cond: [
|
||||
{
|
||||
$lte:
|
||||
[{$ifNull: ["$_id", 0]}, 3]
|
||||
},
|
||||
1,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
highp: {
|
||||
$sum: {
|
||||
$cond: [
|
||||
{$gt: [{$ifNull: ["$_id", 0]}, 3]},
|
||||
1,
|
||||
0
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[{"_id": null, "lowp": 2, "highp": 1}],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to top-fields below a $filter
|
||||
assertResultsMatchWithAndWithoutPushdown(
|
||||
coll,
|
||||
[
|
||||
{$group: {_id: "$item", prices: {$push: "$price"}}},
|
||||
{
|
||||
$group: {
|
||||
_id: "$_id",
|
||||
o: {$push: {$filter: {input: "$prices", as: "p", cond: {$gte: ["$$p", 5]}}}}
|
||||
}
|
||||
}
|
||||
],
|
||||
[{"_id": "a", "o": [[10, 5]]}, {"_id": "b", "o": [[20, 10]]}, {"_id": "c", "o": [[5]]}],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to top-fields below a $let
|
||||
assertResultsMatchWithAndWithoutPushdown(
|
||||
coll,
|
||||
[
|
||||
{$group: {
|
||||
_id: "$item", maxp: {$max: "$price"}, minp: {$min: "$price"}, count: {$count: {}}
|
||||
}},
|
||||
{$group: {_id: "$_id", o: {$sum: {
|
||||
$let: {
|
||||
vars: {
|
||||
minPlusMax: {$add: ["$maxp", "$minp"]},
|
||||
count: "$count"
|
||||
},
|
||||
in: {$multiply: ["$$minPlusMax", "$$count"]}
|
||||
}
|
||||
}}}}
|
||||
],
|
||||
[{ "_id" : "a", "o" : 30 }, { "_id" : "c", "o" : 10 }, { "_id" : "b", "o" : 60 }],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to top-fields below a $and
|
||||
assertResultsMatchWithAndWithoutPushdown(coll,
|
||||
[
|
||||
{
|
||||
$group: {
|
||||
_id: "$item",
|
||||
maxp: {$max: "$price"},
|
||||
minp: {$min: "$price"}
|
||||
}
|
||||
},
|
||||
{
|
||||
$group:
|
||||
{
|
||||
_id: "$_id",
|
||||
o:
|
||||
{
|
||||
$sum:
|
||||
{
|
||||
$and: [
|
||||
{$gt: ["$maxp", 15]},
|
||||
{$lt: ["$minp", 10]}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{"_id": "a", "o": 0},
|
||||
{"_id": "c", "o": 0},
|
||||
{"_id": "b", "o": 0}
|
||||
],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to top-fields below a $or
|
||||
assertResultsMatchWithAndWithoutPushdown(coll,
|
||||
[
|
||||
{
|
||||
$group: {
|
||||
_id: "$item",
|
||||
maxp: {$max: "$price"},
|
||||
minp: {$min: "$price"}
|
||||
}
|
||||
},
|
||||
{
|
||||
$group:
|
||||
{
|
||||
_id: "$_id",
|
||||
o:
|
||||
{
|
||||
$sum:
|
||||
{
|
||||
$or: [
|
||||
{$gt: ["$maxp", 15]},
|
||||
{$lt: ["$minp", 10]}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{"_id": "a", "o": 0},
|
||||
{"_id": "c", "o": 0},
|
||||
{"_id": "b", "o": 0}
|
||||
],
|
||||
2);
|
||||
|
||||
// The second $group stage refers to both a top-level field and a sub-field twice which does not
|
||||
// exist.
|
||||
assertResultsMatchWithAndWithoutPushdown(
|
||||
|
||||
@ -53,8 +53,13 @@ admin_s1.runCommand({replSetFreeze: 999999});
|
||||
print("6. Bring up #3");
|
||||
var hostname = getHostName();
|
||||
|
||||
var secondary2 =
|
||||
MongoRunner.runMongod(Object.merge({replSet: basename, oplogSize: 2}, x509_options2));
|
||||
var secondary2 = MongoRunner.runMongod(Object.merge({
|
||||
replSet: basename,
|
||||
oplogSize: 2,
|
||||
// Preserve the initial sync state to validate an assertion.
|
||||
setParameter: {"failpoint.skipClearInitialSyncState": tojson({mode: 'alwaysOn'})}
|
||||
},
|
||||
x509_options2));
|
||||
|
||||
var local_s2 = secondary2.getDB("local");
|
||||
var admin_s2 = secondary2.getDB("admin");
|
||||
@ -108,5 +113,11 @@ assert.commandWorked(bulk.execute());
|
||||
print("11. Everyone happy eventually");
|
||||
replTest.awaitReplication();
|
||||
|
||||
// SERVER-69001: Assert that the last oplog for initial sync was persisted in the minvalid document.
|
||||
let syncingNodeMinvalid = secondary2.getDB("local").replset.minvalid.findOne()["ts"];
|
||||
let lastInitialSyncOp =
|
||||
secondary2.adminCommand("replSetGetStatus")["initialSyncStatus"]["initialSyncOplogEnd"];
|
||||
assert.eq(lastInitialSyncOp, syncingNodeMinvalid);
|
||||
|
||||
MongoRunner.stopMongod(secondary2);
|
||||
replTest.stopSet();
|
||||
|
||||
@ -57,6 +57,11 @@ node = rst.restart(node, {
|
||||
noReplSet: true,
|
||||
setParameter: {recoverFromOplogAsStandalone: true, logComponentVerbosity: logLevel}
|
||||
});
|
||||
|
||||
// Verify that the 'config.system.indexBuilds' collection is empty after recovering from the oplog
|
||||
// in standalone mode.
|
||||
assert.eq(0, node.getCollection("config.system.indexBuilds").count());
|
||||
|
||||
reconnect(node);
|
||||
|
||||
rst.stopSet();
|
||||
|
||||
@ -21,8 +21,7 @@ assert.throwsWithCode(() => {
|
||||
st.rs0.getPrimary().getDB(dbName).runCommand({
|
||||
checkShardingIndex: ns,
|
||||
keyPattern: {x: 1},
|
||||
shardVersion:
|
||||
{e: ObjectId(), t: Timestamp(1, 1), v: Timestamp(99, 10101), i: Timestamp(0, 0)},
|
||||
shardVersion: {e: ObjectId(), t: Timestamp(1, 1), v: Timestamp(99, 10101)},
|
||||
});
|
||||
}, ErrorCodes.StaleConfig);
|
||||
|
||||
|
||||
@ -74,6 +74,7 @@ st.rs1.restart(0, {
|
||||
startClean: false,
|
||||
setParameter: "disableResumableRangeDeleter=true"
|
||||
});
|
||||
st.rs1.waitForPrimary();
|
||||
|
||||
jsTest.log("Shard0 should be able to donate a chunk and shard1 should be able to receive it.");
|
||||
// disableResumableRangeDeleter should not prevent a shard from donating a chunk, and should not
|
||||
@ -88,6 +89,7 @@ st.rs0.restart(0, {
|
||||
startClean: false,
|
||||
setParameter: "disableResumableRangeDeleter=false"
|
||||
});
|
||||
st.rs0.waitForPrimary();
|
||||
|
||||
jsTest.log("Shard0 should now be able to re-receive the chunk it failed to receive earlier.");
|
||||
assert.commandWorked(st.s.adminCommand({moveChunk: ns, find: {_id: 0}, to: st.shard0.shardName}));
|
||||
@ -110,6 +112,7 @@ st.rs1.restart(0, {
|
||||
});
|
||||
|
||||
st.rs0.getPrimary().adminCommand({setParameter: 1, receiveChunkWaitForRangeDeleterTimeoutMS: 500});
|
||||
st.rs1.waitForPrimary();
|
||||
|
||||
let bulkOp = st.s.getCollection(ns).initializeUnorderedBulkOp();
|
||||
|
||||
|
||||
@ -67,12 +67,6 @@ function runTest(st, stepDownShard0PrimaryFunc, testOpts = {
|
||||
const prepareTxnRes = assert.commandWorked(testDB.adminCommand(prepareCmdObj));
|
||||
commitCmdObj.commitTimestamp = prepareTxnRes.prepareTimestamp;
|
||||
|
||||
// It is possible that a secondary that steps up could use a stale majority-committed snapshot,
|
||||
// so we want to wait until the prepareTransaction is visible in the majority-committed snapshot
|
||||
// view for all nodes in the replica set. We do this because commitTransaction for a prepared
|
||||
// transaction cannot be run before its prepare oplog entry has been majority committed
|
||||
st.rs0.awaitLastOpCommitted();
|
||||
|
||||
stepDownShard0PrimaryFunc();
|
||||
|
||||
testDB = st.rs0.getPrimary().getDB(kDbName);
|
||||
@ -174,9 +168,9 @@ function runTest(st, stepDownShard0PrimaryFunc, testOpts = {
|
||||
st.rs0.stopSet(null /* signal */, true /*forRestart */);
|
||||
st.rs0.startSet({restart: true});
|
||||
st.rs0.getPrimary();
|
||||
// Wait for replication since it is illegal to run commitTransaction before the prepare
|
||||
// oplog entry has been majority committed.
|
||||
st.rs0.awaitReplication();
|
||||
// Wait for replication to recover the lastCommittedOpTime since it is illegal to run
|
||||
// commitTransaction before the prepare oplog entry has been majority committed.
|
||||
st.rs0.awaitLastOpCommitted();
|
||||
};
|
||||
|
||||
// Test findAnModify without pre/post image.
|
||||
|
||||
@ -8,8 +8,7 @@ var ShardVersioningUtil = (function() {
|
||||
const kIgnoredShardVersion = {
|
||||
e: ObjectId("00000000ffffffffffffffff"),
|
||||
t: Timestamp(Math.pow(2, 32) - 1, Math.pow(2, 32) - 1),
|
||||
v: Timestamp(0, 0),
|
||||
i: Timestamp(0, 0)
|
||||
v: Timestamp(0, 0)
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@ -21,7 +21,8 @@ const sourceCollection = reshardingTest.createShardedCollection({
|
||||
});
|
||||
|
||||
const mongos = sourceCollection.getMongo();
|
||||
const topology = DiscoverTopology.findConnectedNodes(mongos);
|
||||
const ns = sourceCollection.getFullName();
|
||||
let topology = DiscoverTopology.findConnectedNodes(mongos);
|
||||
|
||||
const recipientShardNames = reshardingTest.recipientShardNames;
|
||||
const recipient = new Mongo(topology.shards[recipientShardNames[0]].primary);
|
||||
@ -38,6 +39,13 @@ const shardsvrAbortReshardCollectionFailpoint = configureFailPoint(recipient, "f
|
||||
failCommands: ["_shardsvrAbortReshardCollection"],
|
||||
});
|
||||
|
||||
// We pause the _configsvrReshardCollection command upon joining an existing ReshardingCoordinator
|
||||
// instance on all of the config server replica set because we don't know which node will be elected
|
||||
// primary from calling stepUpNewPrimaryOnShard().
|
||||
const configsvrConnections = topology.configsvr.nodes.map(host => new Mongo(host));
|
||||
const reshardCollectionJoinedFailPointsList = configsvrConnections.map(
|
||||
conn => configureFailPoint(conn, "reshardCollectionJoinedExistingOperation"));
|
||||
|
||||
let awaitAbort;
|
||||
reshardingTest.withReshardingInBackground(
|
||||
{
|
||||
@ -48,7 +56,6 @@ reshardingTest.withReshardingInBackground(
|
||||
// Wait until participants are aware of the resharding operation.
|
||||
reshardingTest.awaitCloneTimestampChosen();
|
||||
|
||||
const ns = sourceCollection.getFullName();
|
||||
awaitAbort = startParallelShell(funWithArgs(function(ns) {
|
||||
db.adminCommand({abortReshardCollection: ns});
|
||||
}, ns), mongos.port);
|
||||
@ -70,13 +77,12 @@ reshardingTest.withReshardingInBackground(
|
||||
// Mongos automatically retries the abortReshardCollection command on retryable errors.
|
||||
// We interrupt the abortReshardCollection command running on mongos to verify that the
|
||||
// ReshardingCoordinator recovers the decision on its own.
|
||||
const ops =
|
||||
mongos.getDB("admin")
|
||||
.aggregate([
|
||||
{$currentOp: {localOps: true}},
|
||||
{$match: {"command.abortReshardCollection": sourceCollection.getFullName()}}
|
||||
])
|
||||
.toArray();
|
||||
const ops = mongos.getDB("admin")
|
||||
.aggregate([
|
||||
{$currentOp: {localOps: true}},
|
||||
{$match: {"command.abortReshardCollection": ns}}
|
||||
])
|
||||
.toArray();
|
||||
|
||||
assert.neq([], ops, "failed to find abortReshardCollection command running on mongos");
|
||||
assert.eq(
|
||||
@ -88,6 +94,28 @@ reshardingTest.withReshardingInBackground(
|
||||
assert.commandWorked(mongos.getDB("admin").killOp(ops[0].opid));
|
||||
|
||||
reshardingTest.stepUpNewPrimaryOnShard(reshardingTest.configShardName);
|
||||
|
||||
// After a stepdown, the _configsvrReshardCollection command will be retried by the
|
||||
// primary shard. We use the reshardCollectionJoinedExistingOperation failpoint to
|
||||
// ensure the primary shard upon retrying finds the ongoing resharding operation on the
|
||||
// new config server primary. It would otherwise be possible for the
|
||||
// reshardingPauseCoordinatorBeforeCompletion failpoint to be released by the
|
||||
// ReshardingTest fixture after this function returns, for the ongoing resharding
|
||||
// operation to complete, and for the retried _configsvrReshardCollection command to
|
||||
// spawn an entirely new resharding operation which won't get aborted by the test
|
||||
// client.
|
||||
topology = DiscoverTopology.findConnectedNodes(mongos);
|
||||
const configsvrPrimary = new Mongo(topology.configsvr.primary);
|
||||
const idx = reshardCollectionJoinedFailPointsList.findIndex(fp => fp.conn.host ===
|
||||
configsvrPrimary.host);
|
||||
reshardCollectionJoinedFailPointsList[idx].wait();
|
||||
|
||||
// Wait for secondaries to recover and catchup with primary before turning off the
|
||||
// failpoints as a replication roll back can disconnect the test client.
|
||||
const configRS = reshardingTest.getReplSetForShard(reshardingTest.configShardName);
|
||||
configRS.awaitSecondaryNodes();
|
||||
configRS.awaitReplication();
|
||||
reshardCollectionJoinedFailPointsList.forEach(fp => fp.off());
|
||||
shardsvrAbortReshardCollectionFailpoint.off();
|
||||
},
|
||||
});
|
||||
|
||||
@ -94,8 +94,6 @@ reshardingTest.withReshardingInBackground(
|
||||
assert(curOpSection.hasOwnProperty('recipientState'), tojson(curOpSection));
|
||||
assert(curOpSection.hasOwnProperty('documentsCopied'), tojson(curOpSection));
|
||||
assert(curOpSection.hasOwnProperty('oplogEntriesApplied'), tojson(curOpSection));
|
||||
assert(curOpSection.hasOwnProperty('remainingOperationTimeEstimatedSecs'),
|
||||
tojson(curOpSection));
|
||||
});
|
||||
|
||||
const curOpSection =
|
||||
|
||||
@ -83,7 +83,7 @@ BUILD_PROFILES = {
|
||||
sanitize="undefined,address",
|
||||
link_model="dynamic",
|
||||
dbg="on",
|
||||
opt="off",
|
||||
opt="debug",
|
||||
ICECC="icecc",
|
||||
CCACHE="ccache",
|
||||
NINJA_PREFIX="san",
|
||||
|
||||
@ -488,8 +488,6 @@ error_codes:
|
||||
- {code: 376, name: ChangeStreamNotEnabled}
|
||||
- {code: 377, name: FLEMaxTagLimitExceeded }
|
||||
- {code: 378, name: NonConformantBSON, categories: [ValidationError]}
|
||||
- {code: 379, name: InvalidSignature}
|
||||
|
||||
|
||||
# Error codes 4000-8999 are reserved.
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ Future<AsyncDBClient::Handle> AsyncDBClient::connect(
|
||||
ServiceContext* const context,
|
||||
transport::ReactorHandle reactor,
|
||||
Milliseconds timeout,
|
||||
ConnectionMetrics* connectionMetrics,
|
||||
std::shared_ptr<ConnectionMetrics> connectionMetrics,
|
||||
std::shared_ptr<const transport::SSLConnectionContext> transientSSLContext) {
|
||||
auto tl = context->getTransportLayer();
|
||||
return tl
|
||||
|
||||
@ -61,7 +61,7 @@ public:
|
||||
ServiceContext* context,
|
||||
transport::ReactorHandle reactor,
|
||||
Milliseconds timeout,
|
||||
ConnectionMetrics* connectionMetrics, // must remain valid until the future is ready
|
||||
std::shared_ptr<ConnectionMetrics> connectionMetrics,
|
||||
std::shared_ptr<const transport::SSLConnectionContext> transientSSLContext = nullptr);
|
||||
|
||||
Future<executor::RemoteCommandResponse> runCommandRequest(
|
||||
|
||||
@ -63,27 +63,6 @@ cryptoEnv.Library(
|
||||
],
|
||||
)
|
||||
|
||||
cryptoEnv.Library(
|
||||
target='rsa_public_key',
|
||||
source=[
|
||||
'rsa_public_key.cpp',
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/base',
|
||||
],
|
||||
)
|
||||
|
||||
cryptoEnv.Library(
|
||||
target='asymmetric_crypto',
|
||||
source=[
|
||||
'asymmetric_crypto_{}.cpp'.format(ssl_provider),
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/util/net/openssl_init' if ssl_provider == 'openssl' else '',
|
||||
'rsa_public_key',
|
||||
],
|
||||
)
|
||||
|
||||
env.Library(
|
||||
target="aead_encryption",
|
||||
source=[
|
||||
@ -147,11 +126,9 @@ env.CppUnitTest(
|
||||
target='crypto_test',
|
||||
source=[
|
||||
'aead_encryption_test.cpp',
|
||||
'asymmetric_crypto_openssl_test.cpp',
|
||||
'encryption_fields_util_test.cpp',
|
||||
'fle_crypto_test.cpp',
|
||||
'mechanism_scram_test.cpp',
|
||||
'rsa_public_key_test.cpp',
|
||||
'sha1_block_test.cpp',
|
||||
'sha256_block_test.cpp',
|
||||
'sha512_block_test.cpp',
|
||||
@ -162,37 +139,8 @@ env.CppUnitTest(
|
||||
'$BUILD_DIR/mongo/base/secure_allocator',
|
||||
'$BUILD_DIR/mongo/util/net/openssl_init' if ssl_provider == 'openssl' else '',
|
||||
'aead_encryption',
|
||||
'asymmetric_crypto',
|
||||
'encrypted_field_config',
|
||||
'fle_crypto',
|
||||
'rsa_public_key',
|
||||
'sha_block_${MONGO_CRYPTO}',
|
||||
],
|
||||
)
|
||||
|
||||
env.Library(
|
||||
target='jwt',
|
||||
source=[
|
||||
'jwt_types.idl',
|
||||
'jwk_manager.cpp',
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/base',
|
||||
],
|
||||
LIBDEPS_PRIVATE=[
|
||||
'$BUILD_DIR/mongo/idl/idl_parser',
|
||||
'rsa_public_key',
|
||||
],
|
||||
)
|
||||
|
||||
env.CppUnitTest(
|
||||
target='jwt_test',
|
||||
source=[
|
||||
'jwt_test.cpp',
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/base',
|
||||
'jwt',
|
||||
'rsa_public_key',
|
||||
],
|
||||
)
|
||||
|
||||
@ -1,48 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/data_range.h"
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/crypto/rsa_public_key.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
|
||||
enum class HashingAlgorithm { SHA256 };
|
||||
|
||||
class RSAKeySignatureVerifier {
|
||||
public:
|
||||
virtual ~RSAKeySignatureVerifier() = default;
|
||||
virtual Status verifySignature(ConstDataRange msg, ConstDataRange signature) = 0;
|
||||
static StatusWith<std::unique_ptr<RSAKeySignatureVerifier>> create(const RsaPublicKey& pubKey,
|
||||
HashingAlgorithm hashAlg);
|
||||
};
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/asymmetric_crypto.h"
|
||||
|
||||
#include "mongo/base/error_codes.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
|
||||
class RsaKeySignatureVerifierApple : public RSAKeySignatureVerifier {
|
||||
public:
|
||||
RsaKeySignatureVerifierApple(){};
|
||||
|
||||
Status verifySignature(ConstDataRange msg, ConstDataRange signature) final {
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// TODO: SERVER-68518, remove or implement this class
|
||||
StatusWith<std::unique_ptr<RSAKeySignatureVerifier>> RSAKeySignatureVerifier::create(
|
||||
const RsaPublicKey& pubKey, HashingAlgorithm hashAlg) {
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
} // namespace mongo::crypto
|
||||
@ -1,54 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/asymmetric_crypto.h"
|
||||
|
||||
#include "mongo/base/error_codes.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
|
||||
class RSAKeySignatureVerifierNone : public RSAKeySignatureVerifier {
|
||||
|
||||
public:
|
||||
RSAKeySignatureVerifierNone(){};
|
||||
|
||||
Status verifySignature(ConstDataRange msg, ConstDataRange signature) final {
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// TODO: SERVER-68518, remove or implement this class
|
||||
StatusWith<std::unique_ptr<RSAKeySignatureVerifier>> RSAKeySignatureVerifier::create(
|
||||
const RsaPublicKey& pubKey, HashingAlgorithm hashAlg) {
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,142 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/asymmetric_crypto.h"
|
||||
|
||||
#include <memory>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/ossl_typ.h>
|
||||
#include <openssl/rsa.h>
|
||||
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/crypto/rsa_public_key.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/net/ssl_manager.h"
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000fL)
|
||||
namespace {
|
||||
// Copies of OpenSSL 1.1.0 and later define new EVP digest routines. We must
|
||||
// polyfill used definitions to interact with older OpenSSL versions.
|
||||
EVP_MD_CTX* EVP_MD_CTX_new() {
|
||||
return EVP_MD_CTX_create();
|
||||
}
|
||||
|
||||
void EVP_MD_CTX_free(EVP_MD_CTX* ctx) {
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
|
||||
using UniqueRSA = std::unique_ptr<RSA, OpenSSLDeleter<decltype(RSA_free), RSA_free>>;
|
||||
using UniqueEVPPKey =
|
||||
std::unique_ptr<EVP_PKEY, OpenSSLDeleter<decltype(EVP_PKEY_free), EVP_PKEY_free>>;
|
||||
using UniqueBIGNUM = std::unique_ptr<BIGNUM, OpenSSLDeleter<decltype(BN_free), BN_free>>;
|
||||
|
||||
class RSAKeySignatureVerifierOpenSSL : public RSAKeySignatureVerifier {
|
||||
public:
|
||||
RSAKeySignatureVerifierOpenSSL(const RsaPublicKey& pubKey, HashingAlgorithm hashAlg)
|
||||
: _verificationCtx(EVP_MD_CTX_new()) {
|
||||
#if OPENSSL_VERSION_NUMBER > 0x10100000L || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER > 0x2070000fL)
|
||||
const auto* pubKeyNData = pubKey.getN().data<unsigned char>();
|
||||
UniqueBIGNUM n(BN_bin2bn(pubKeyNData, pubKey.getN().length(), nullptr));
|
||||
uassertOpenSSL("Failed creating modulus", n.get() != nullptr);
|
||||
|
||||
const auto* pubKeyEData = pubKey.getE().data<unsigned char>();
|
||||
UniqueBIGNUM e(BN_bin2bn(pubKeyEData, pubKey.getE().length(), nullptr));
|
||||
uassertOpenSSL("Failed creating exponent", e.get() != nullptr);
|
||||
|
||||
UniqueRSA rsa(RSA_new());
|
||||
uassertOpenSSL("Failed creating RSAKey", rsa.get() != nullptr);
|
||||
uassertOpenSSL("RSA key setup failed",
|
||||
RSA_set0_key(rsa.get(), n.get(), e.get(), nullptr) == 1);
|
||||
n.release(); // Now owned by rsa
|
||||
e.release(); // Now owned by rsa
|
||||
|
||||
UniqueEVPPKey evpKey(EVP_PKEY_new());
|
||||
uassertOpenSSL("Failed creating EVP_PKey", evpKey.get() != nullptr);
|
||||
uassertOpenSSL("EVP_PKEY assignment failed",
|
||||
EVP_PKEY_assign_RSA(evpKey.get(), rsa.get()) == 1);
|
||||
rsa.release(); // Now owned by evpKey
|
||||
|
||||
uassert(6755199, "Unknown hashing algorithm", hashAlg == HashingAlgorithm::SHA256);
|
||||
uassertOpenSSL("DigestVerifyInit failed",
|
||||
EVP_DigestVerifyInit(
|
||||
_verificationCtx.get(), nullptr, EVP_sha256(), nullptr, evpKey.get()) ==
|
||||
1);
|
||||
#endif
|
||||
}
|
||||
|
||||
Status verifySignature(ConstDataRange msg, ConstDataRange signature) final {
|
||||
#if OPENSSL_VERSION_NUMBER > 0x10100000L || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER > 0x2070000fL)
|
||||
|
||||
uassertOpenSSL("DigestVerifyUpdate failed",
|
||||
EVP_DigestVerifyUpdate(
|
||||
_verificationCtx.get(), msg.data<std::uint8_t>(), msg.length()) == 1);
|
||||
|
||||
int verifyRes = EVP_DigestVerifyFinal(
|
||||
_verificationCtx.get(), signature.data<std::uint8_t>(), signature.length());
|
||||
if (verifyRes == 0) {
|
||||
return {ErrorCodes::InvalidSignature, "OpenSSL: Signature is invalid"};
|
||||
} else if (verifyRes != 1) {
|
||||
return {ErrorCodes::UnknownError,
|
||||
SSLManagerInterface::getSSLErrorMessage(ERR_get_error())};
|
||||
}
|
||||
return Status::OK();
|
||||
#endif
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<EVP_MD_CTX, OpenSSLDeleter<decltype(EVP_MD_CTX_free), ::EVP_MD_CTX_free>>
|
||||
_verificationCtx;
|
||||
|
||||
static void uassertOpenSSL(StringData context, bool success) {
|
||||
uassert(ErrorCodes::OperationFailed,
|
||||
str::stream() << context << ": "
|
||||
<< SSLManagerInterface::getSSLErrorMessage(ERR_get_error()),
|
||||
success);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
StatusWith<std::unique_ptr<RSAKeySignatureVerifier>> RSAKeySignatureVerifier::create(
|
||||
const RsaPublicKey& pubKey, HashingAlgorithm hashAlg) try {
|
||||
return std::make_unique<RSAKeySignatureVerifierOpenSSL>(pubKey, hashAlg);
|
||||
} catch (const DBException& e) {
|
||||
return e.toStatus();
|
||||
}
|
||||
} // namespace mongo::crypto
|
||||
@ -1,170 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/asymmetric_crypto.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mongo/base/data_range.h"
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/bson/bsontypes.h"
|
||||
#include "mongo/config.h"
|
||||
#include "mongo/crypto/rsa_public_key.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/base64.h"
|
||||
#include "mongo/util/hex.h"
|
||||
|
||||
#if MONGO_CONFIG_SSL_PROVIDER == MONGO_CONFIG_SSL_PROVIDER_OPENSSL
|
||||
|
||||
namespace mongo::crypto {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L || \
|
||||
(defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
|
||||
|
||||
class AsymmetricCryptoTestVectors : public unittest::Test {
|
||||
public:
|
||||
class RSAKeySignatureVerificationVector {
|
||||
public:
|
||||
RSAKeySignatureVerificationVector(StringData keyID,
|
||||
StringData e,
|
||||
StringData n,
|
||||
StringData msg,
|
||||
StringData signature,
|
||||
bool shouldPass) {
|
||||
this->keyID = keyID.toString();
|
||||
|
||||
std::string strE = hexblob::decode(e);
|
||||
std::string base64E = base64url::encode(StringData(strE.data(), strE.length()));
|
||||
this->e = base64E;
|
||||
|
||||
std::string strN = hexblob::decode(n);
|
||||
std::string base64N = base64url::encode(StringData(strN.data(), strN.length()));
|
||||
this->n = base64N;
|
||||
|
||||
this->msg = hexblob::decode(msg);
|
||||
this->signature = hexblob::decode(signature);
|
||||
this->shouldPass = shouldPass;
|
||||
}
|
||||
|
||||
std::string keyID;
|
||||
std::string e;
|
||||
std::string n;
|
||||
std::string msg;
|
||||
std::string signature;
|
||||
bool shouldPass;
|
||||
};
|
||||
|
||||
void evaluate(RSAKeySignatureVerificationVector test) {
|
||||
|
||||
RsaPublicKey rsaKey(test.keyID, test.e, test.n);
|
||||
|
||||
HashingAlgorithm hashAlg = HashingAlgorithm::SHA256;
|
||||
auto asymmetricKey = uassertStatusOK(RSAKeySignatureVerifier::create(rsaKey, hashAlg));
|
||||
Status result = asymmetricKey->verifySignature(
|
||||
ConstDataRange(test.msg.data(), test.msg.length()),
|
||||
ConstDataRange(test.signature.data(), test.signature.length()));
|
||||
if (test.shouldPass == false) {
|
||||
ASSERT_NOT_OK(result);
|
||||
} else if (test.shouldPass == true) {
|
||||
ASSERT_OK(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* RSA test vectors are otained from FIPS 186-4 RSA:
|
||||
* https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures#rsa2vs
|
||||
*/
|
||||
|
||||
TEST_F(AsymmetricCryptoTestVectors, RSASignatureVerificationTest1) {
|
||||
evaluate(RSAKeySignatureVerificationVector(
|
||||
"0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd,
|
||||
"49d2a1"_sd,
|
||||
"c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"_sd,
|
||||
"95123c8d1b236540b86976a11cea31f8bd4e6c54c235147d20ce722b03a6ad756fbd918c27df8ea9ce3104444c0bbe877305bc02e35535a02a58dcda306e632ad30b3dc3ce0ba97fdf46ec192965dd9cd7f4a71b02b8cba3d442646eeec4af590824ca98d74fbca934d0b6867aa1991f3040b707e806de6e66b5934f05509bea"_sd,
|
||||
"51265d96f11ab338762891cb29bf3f1d2b3305107063f5f3245af376dfcc7027d39365de70a31db05e9e10eb6148cb7f6425f0c93c4fb0e2291adbd22c77656afc196858a11e1c670d9eeb592613e69eb4f3aa501730743ac4464486c7ae68fd509e896f63884e9424f69c1c5397959f1e52a368667a598a1fc90125273d9341295d2f8e1cc4969bf228c860e07a3546be2eeda1cde48ee94d062801fe666e4a7ae8cb9cd79262c017b081af874ff00453ca43e34efdb43fffb0bb42a4e2d32a5e5cc9e8546a221fe930250e5f5333e0efe58ffebf19369a3b8ae5a67f6a048bc9ef915bda25160729b508667ada84a0c27e7e26cf2abca413e5e4693f4a9405"_sd,
|
||||
true));
|
||||
}
|
||||
|
||||
TEST_F(AsymmetricCryptoTestVectors, RSASignatureVerificationTest2) {
|
||||
evaluate(RSAKeySignatureVerificationVector(
|
||||
"0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd,
|
||||
"49d2a1"_sd,
|
||||
"c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"_sd,
|
||||
"f89fd2f6c45a8b5066a651410b8e534bfec0d9a36f3e2b887457afd44dd651d1ec79274db5a455f182572fceea5e9e39c3c7c5d9e599e4fe31c37c34d253b419c3e8fb6b916aef6563f87d4c37224a456e5952698ba3d01b38945d998a795bd285d69478e3131f55117284e27b441f16095dca7ce9c5b68890b09a2bfbb010a5"_sd,
|
||||
"ba48538708512d45c0edcac57a9b4fb637e9721f72003c60f13f5c9a36c968cef9be8f54665418141c3d9ecc02a5bf952cfc055fb51e18705e9d8850f4e1f5a344af550de84ffd0805e27e557f6aa50d2645314c64c1c71aa6bb44faf8f29ca6578e2441d4510e36052f46551df341b2dcf43f761f08b946ca0b7081dadbb88e955e820fd7f657c4dd9f4554d167dd7c9a487ed41ced2b40068098deedc951060faf7e15b1f0f80ae67ff2ee28a238d80bf72dd71c8d95c79bc156114ece8ec837573a4b66898d45b45a5eacd0b0e41447d8fa08a367f437645e50c9920b88a16bc0880147acfb9a79de9e351b3fa00b3f4e9f182f45553dffca55e393c5eab6"_sd,
|
||||
false));
|
||||
}
|
||||
|
||||
TEST_F(AsymmetricCryptoTestVectors, RSASignatureVerificationTest3) {
|
||||
evaluate(RSAKeySignatureVerificationVector(
|
||||
"0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd,
|
||||
"49d2a1"_sd,
|
||||
"c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"_sd,
|
||||
"915c5e4c16acfa0f49de43d6491f0060a944034475ba518572c08366a8d36c7f1e6afc11e5e4649757bf7b9da10a61d57f1d626847871d8a2948e551b54167c79de88d3ebd40a3e35809b996a53348f98a9918c7a7ec606896ed30c271e00c51953dd97aa6a8fe1cd423c3695c83fcf45120ec0a9cd1644642182b60e599a246"_sd,
|
||||
"3d57ea5961db8fc144301ca4278f799911229d865ea3e992c7fbc4d03c6551729e26034e95dd71da312340e4051c9dd9b12f7700a821fe3b7c37785d5106350b667ac255a57c13da5842d90bcadea9e6b1f720c607d6893a2caa3c5f3c4074e914451a45380a767c291a67cac3f1cab1fbd05adc37036856a8404e7cea3654019466de449ad6e92b27254f3d25949b1b860065406455a13db7c5fe25d1af7a84cddf7792c64e16260c950d60bd86d005924148ad097c126b84947ab6e89d48f61e711d62522b6e48f16186d1339e6ab3f58c359eb24cb68043737591cd7d9390a468c0022b3b253be52f1a7fc408f84e9ffb4c34fa9e01605851d6583aa13032"_sd,
|
||||
false));
|
||||
}
|
||||
|
||||
TEST_F(AsymmetricCryptoTestVectors, RSASignatureVerificationTest4) {
|
||||
evaluate(RSAKeySignatureVerificationVector(
|
||||
"0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd,
|
||||
"7485b2"_sd,
|
||||
"c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"_sd,
|
||||
"3d2f0693517cffb2b724c1f30502c5359c051c1bcd88dc1dd54b89e6981009d275a813b2bf016b74d0f6ed0d91e62d0884785c9afd8fd1fb7e99246cd4005cdda71a39cb649197a996d8ad2d23fdfb6bb015f24ec3d7f88af64fb83b4b525eb06607d133eec834cf7d6c9ab817b4c0dda370459d9cfba05ad0c1adc86a909fe1"_sd,
|
||||
"511abd82218cab344979b2887b02600d2427f1eb12ac01d97684c2a443a9272834c3f79cded07a39dbee3770dde827a74dc994b17bfd8a26d07b239d26d58c42f79d560264c31b7e1c3dddef6d7556f228c394414f4cec561c3da2686a8eebec7702f32850809a93deeb84b2a02fcdba224d2fd9efb8e056e796f49b57d56e9f3e90d0b49b08bdee93a2e12e676fb4d4fa838c5bd88eda008f1b592a72465587be0ae17d9b156b904f44a7e04d3b58d24ad67b71b0f4c699fa51639546b62b9f83597ff03d465f1bb396ae15e92d0e92e85647d5df113e2c7518d0e3ad2e7aa7dac720c98347aa151e4f37fea081dbed350cc9c93f606b38f21a3e5de6d140d2"_sd,
|
||||
false));
|
||||
}
|
||||
|
||||
TEST_F(AsymmetricCryptoTestVectors, RSASignatureVerificationTest5) {
|
||||
evaluate(RSAKeySignatureVerificationVector(
|
||||
"0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd,
|
||||
"49d2a1"_sd,
|
||||
"c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"_sd,
|
||||
"dffe42bfda886e1a73fe8a8dfcf71c9fb44deb054588a9bb9199d554aecce08f2ff88f2aa6f8a0fb675fb03c8e685c27432ca7c33c189bfd849d34fa7b2979ac1f57eca389632426bae0b98398ad60a3342557e14e96041c1bf4d90b46cf7ad1348322d28caf43c4f7e86c0924ae703c109ec50a84ea2a43df078c3015a52b28"_sd,
|
||||
"8f4dd479239f2d08dc05d7d40539288b67c4d77210ecb16be76f0b1925e8b088570831e361a1ca57893135f8af64b8e2996b8d635899da4e04c68acb9b1b3813697d57da90c57f18509e0ab6705c704feb448cca5c07d258ecd884ab93f508cefdb25f2bc3061c4006099e2e33b27972c3edb0a0a33114d381c82ab506d041ff680af595ef3400a8bb6774030d2e38dd304272092bd32a553017f7bda4b998b27aa8aca12def327b1f11063a5342b0d55738183417d321c5682fc4ab64e79174216feebb989521e1e3d827647068003be34fe1d093964d28f4877c49b4065672448597a89b91919cfb55ca13836e7e6f3b3fd04f417cf1c16d9872538bf4e87a"_sd,
|
||||
false));
|
||||
}
|
||||
|
||||
TEST_F(AsymmetricCryptoTestVectors, RSASignatureVerificationTest6) {
|
||||
evaluate(RSAKeySignatureVerificationVector(
|
||||
"0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd,
|
||||
"49d2a1"_sd,
|
||||
"c47abacc2a84d56f3614d92fd62ed36ddde459664b9301dcd1d61781cfcc026bcb2399bee7e75681a80b7bf500e2d08ceae1c42ec0b707927f2b2fe92ae852087d25f1d260cc74905ee5f9b254ed05494a9fe06732c3680992dd6f0dc634568d11542a705f83ae96d2a49763d5fbb24398edf3702bc94bc168190166492b8671de874bb9cecb058c6c8344aa8c93754d6effcd44a41ed7de0a9dcd9144437f212b18881d042d331a4618a9e630ef9bb66305e4fdf8f0391b3b2313fe549f0189ff968b92f33c266a4bc2cffc897d1937eeb9e406f5d0eaa7a14782e76af3fce98f54ed237b4a04a4159a5f6250a296a902880204e61d891c4da29f2d65f34cbb"_sd,
|
||||
"cfe99788f55ec6944942bd0a187d51b80fd8bd4051bd4f07c73e614eb75a8b9f997b176b2642b5f1b1877061ba9ce142c1d2a311583f072b7cbe08ed253681191c209d7b0d438fcdddc284d93d59d6dd80e48333a921dd31c9b6834f88768f8701e01102d3e8bdf074fbe0b8c93d9951f41545ef6eeb3be35530babc079f1fb3"_sd,
|
||||
"9fd6f6107e838107f906c26cb2910704599f175b6a84db485fbc30776eb7fd53bfe20c38c537b154a3e519b662bd9fdc8e3045e21f6e5ae97d0ff6a9d8632825544525d84f99f80e3ed4e69dc5e219d59ccfbb37c23c84fe3b3e6fb22f402f94e5225c6387fdf8bcdb3508f8832908fe05771521e92234348004e8fe19a8f24bebcab9f074327c88d066bc12081748d696be6135c6aea32220ea786ebd7800e6936365ff25831c28cb6c8a59237ff84f5cf89036cff188ee0f9a6195f2b1aca2e4442af8369f1b49322fa2f891b83a14a97b60c6aeafd6c2928047affda9c8d869ff5294bb5943ad14a6d64e784d126c469d51e292b9ce33e1d8371ba5f467b3"_sd,
|
||||
false));
|
||||
}
|
||||
#endif
|
||||
} // namespace mongo::crypto
|
||||
|
||||
#endif
|
||||
@ -1,53 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/asymmetric_crypto.h"
|
||||
|
||||
#include "mongo/base/error_codes.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
|
||||
class RSAKeySignatureVerifierWindows : public RSAKeySignatureVerifier {
|
||||
public:
|
||||
RSAKeySignatureVerifierWindows(){};
|
||||
|
||||
Status verifySignature(ConstDataRange msg, ConstDataRange signature) final {
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// TODO: SERVER-68518, remove or implement this class
|
||||
StatusWith<std::unique_ptr<RSAKeySignatureVerifier>> RSAKeySignatureVerifier::create(
|
||||
const RsaPublicKey& pubKey, HashingAlgorithm hashAlg) {
|
||||
return {ErrorCodes::OperationFailed, "Signature Verification Not Available"};
|
||||
}
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,92 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/jwk_manager.h"
|
||||
|
||||
#include "mongo/bson/json.h"
|
||||
#include "mongo/crypto/jwt_types_gen.h"
|
||||
#include "mongo/logv2/log.h"
|
||||
#include "mongo/util/base64.h"
|
||||
|
||||
#define MONGO_LOGV2_DEFAULT_COMPONENT ::mongo::logv2::LogComponent::kAccessControl
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
constexpr auto kMinKeySizeBytes = 512 >> 3;
|
||||
|
||||
// Strip insignificant leading zeroes to determine the key's true size.
|
||||
StringData reduceInt(StringData value) {
|
||||
std::size_t ofs = 0;
|
||||
while ((ofs < value.size()) && (value[ofs] == 0)) {
|
||||
++ofs;
|
||||
}
|
||||
return value.substr(ofs);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
JWKManager::JWKManager(BSONObj data) {
|
||||
auto keys = JWKSet::parse(IDLParserContext{"JWKSet"}, data);
|
||||
for (const auto& key : keys.getKeys()) {
|
||||
uassert(ErrorCodes::BadValue,
|
||||
str::stream() << "Only RSA key types are accepted at this time",
|
||||
key.getType() == "RSA"_sd);
|
||||
uassert(ErrorCodes::BadValue, "Key ID must be non-empty", !key.getKeyId().empty());
|
||||
|
||||
// Sanity check so that we don't load a dangerously small key.
|
||||
auto N = reduceInt(key.getN());
|
||||
uassert(ErrorCodes::BadValue,
|
||||
str::stream() << "Key scale is smaller (" << (N.size() << 3)
|
||||
<< " bits) than minimum required: " << (kMinKeySizeBytes << 3),
|
||||
N.size() >= kMinKeySizeBytes);
|
||||
|
||||
// Sanity check so that we don't load an insensible encrypt component.
|
||||
auto E = reduceInt(key.getE());
|
||||
uassert(ErrorCodes::BadValue,
|
||||
str::stream() << "Public key component invalid: " << base64url::encode(key.getE()),
|
||||
(E.size() > 1) || ((E.size() == 1) && (E[0] >= 3)));
|
||||
|
||||
auto keyId = key.getKeyId().toString();
|
||||
uassert(ErrorCodes::DuplicateKey,
|
||||
str::stream() << "Key IDs must be unique, duplicate '" << keyId << "'",
|
||||
_keys.find(keyId) == _keys.end());
|
||||
|
||||
LOGV2_DEBUG(6766000, 5, "Loaded JWK Key", "kid"_attr = key.getKeyId());
|
||||
_keys.insert({keyId, {key.getKeyId(), {E.rawData(), E.size()}, {N.rawData(), N.size()}}});
|
||||
}
|
||||
}
|
||||
|
||||
const RsaPublicKey& JWKManager::getKey(StringData keyId) const {
|
||||
auto it = _keys.find(keyId.toString());
|
||||
uassert(
|
||||
ErrorCodes::NoSuchKey, str::stream() << "Unknown key '" << keyId << "'", it != _keys.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/crypto/rsa_public_key.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
|
||||
class JWKManager {
|
||||
public:
|
||||
JWKManager() = default;
|
||||
explicit JWKManager(BSONObj data);
|
||||
|
||||
const RsaPublicKey& getKey(StringData keyId) const;
|
||||
|
||||
private:
|
||||
std::map<std::string, RsaPublicKey> _keys;
|
||||
};
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/platform/basic.h"
|
||||
|
||||
#include "mongo/crypto/jwk_manager.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
|
||||
// Test key source: RFC 7515 "JSON Web Key" Appendix B.
|
||||
constexpr auto k512BitKeyE = "AQAB"_sd;
|
||||
constexpr auto k512BitKeyN =
|
||||
"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK_PdiVGKPrqzmDIsL"
|
||||
"I7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7i"
|
||||
"I7w0LK9orSMhBEwwZDCxTWq4aYWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy"
|
||||
"1wgzjYQDwHMTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMvVfJb6"
|
||||
"jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ"_sd;
|
||||
|
||||
BSONObj getTestJWKSet(StringData e, StringData n) {
|
||||
BSONObjBuilder set;
|
||||
BSONArrayBuilder keys(set.subarrayStart("keys"_sd));
|
||||
|
||||
{
|
||||
BSONObjBuilder key(keys.subobjStart());
|
||||
key.append("kty", "RSA");
|
||||
key.append("kid", "numberOneKey");
|
||||
key.append("e", e);
|
||||
key.append("n", n);
|
||||
key.doneFast();
|
||||
}
|
||||
|
||||
keys.doneFast();
|
||||
return set.obj();
|
||||
}
|
||||
|
||||
TEST(JWKManager, parseJWKSetBasic) {
|
||||
// Parse the test JWKSet and pull out numberOneKey, then compare it to the inputs.
|
||||
auto basic = getTestJWKSet(k512BitKeyE, k512BitKeyN);
|
||||
JWKManager manager(basic);
|
||||
auto key = manager.getKey("numberOneKey");
|
||||
|
||||
ASSERT_BSONOBJ_EQ(key.toBSON(), basic["keys"_sd].Obj()[0].Obj());
|
||||
}
|
||||
|
||||
TEST(JWKManager, parseJWKSetEmptyComponents) {
|
||||
auto emptyE = getTestJWKSet("", k512BitKeyN);
|
||||
ASSERT_THROWS_WHAT(JWKManager(emptyE), DBException, "Public key component invalid: ");
|
||||
|
||||
auto emptyN = getTestJWKSet(k512BitKeyE, "");
|
||||
ASSERT_THROWS_WHAT(JWKManager(emptyN),
|
||||
DBException,
|
||||
"Key scale is smaller (0 bits) than minimum required: 512");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo::crypto
|
||||
@ -1,64 +0,0 @@
|
||||
# Copyright (C) 2022-present MongoDB, Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the Server Side Public License, version 1,
|
||||
# as published by MongoDB, Inc.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# Server Side Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the Server Side Public License
|
||||
# along with this program. If not, see
|
||||
# <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
#
|
||||
# As a special exception, the copyright holders give permission to link the
|
||||
# code of portions of this program with the OpenSSL library under certain
|
||||
# conditions as described in each individual source file and distribute
|
||||
# linked combinations including the program with the OpenSSL library. You
|
||||
# must comply with the Server Side Public License in all respects for
|
||||
# all of the code used other than as permitted herein. If you modify file(s)
|
||||
# with this exception, you may extend this exception to your version of the
|
||||
# file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
# delete this exception statement from your version. If you delete this
|
||||
# exception statement from all source files in the program, then also delete
|
||||
# it in the license file.
|
||||
|
||||
global:
|
||||
cpp_namespace: "mongo::crypto"
|
||||
|
||||
imports:
|
||||
- "mongo/idl/basic_types.idl"
|
||||
|
||||
structs:
|
||||
JWK:
|
||||
# RFC 7515 Section 4
|
||||
description: JSON Web Key
|
||||
fields:
|
||||
kty:
|
||||
description: Key type
|
||||
type: string
|
||||
cpp_name: type
|
||||
kid:
|
||||
description: Unique Key ID
|
||||
type: string
|
||||
cpp_name: keyId
|
||||
|
||||
# RSA specific fields
|
||||
n:
|
||||
description: Modulus of the RSA Key
|
||||
type: base64urlstring
|
||||
e:
|
||||
description: Public key component of the RSA Key
|
||||
type: base64urlstring
|
||||
|
||||
JWKSet:
|
||||
# RFC 7517 Section 5
|
||||
description: A set of JSON Web Keys
|
||||
fields:
|
||||
keys:
|
||||
description: The JWK objects
|
||||
type: array<JWK>
|
||||
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/rsa_public_key.h"
|
||||
#include "mongo/crypto/jwt_types_gen.h"
|
||||
#include "mongo/util/base64.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
namespace {
|
||||
std::vector<std::uint8_t> vectorFromCDR(ConstDataRange cdr) {
|
||||
return {cdr.data(), cdr.data() + cdr.length()};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
RsaPublicKey::RsaPublicKey(StringData keyId, ConstDataRange e, ConstDataRange n)
|
||||
: _keyId(keyId.toString()), _e(vectorFromCDR(e)), _n(vectorFromCDR(n)) {}
|
||||
|
||||
void RsaPublicKey::appendToBSON(BSONObjBuilder* builder) const {
|
||||
builder->append(JWK::kTypeFieldName, "RSA"_sd);
|
||||
builder->append(JWK::kKeyIdFieldName, _keyId);
|
||||
builder->append(JWK::kEFieldName, base64url::encode(_e.data(), _e.size()));
|
||||
builder->append(JWK::kNFieldName, base64url::encode(_n.data(), _n.size()));
|
||||
}
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,83 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "mongo/base/data_range.h"
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
|
||||
/**
|
||||
* Provides an interface for managing parameters to an RSA signing operation.
|
||||
* Note that this key contains public material only, and is not suitable for decryption.
|
||||
*/
|
||||
class RsaPublicKey {
|
||||
public:
|
||||
/**
|
||||
* Creates an `RsaPublicKey` instance identified by the opaque string name {keyId}.
|
||||
* The RSA operation parameters of {E} and {N} must be passed as Base64URL encoded values (RFC
|
||||
* 4648 §5).
|
||||
*/
|
||||
RsaPublicKey(StringData keyId, ConstDataRange e, ConstDataRange n);
|
||||
std::size_t getKeySizeBytes() const {
|
||||
return _n.size();
|
||||
}
|
||||
|
||||
ConstDataRange getE() const {
|
||||
return ConstDataRange(_e);
|
||||
}
|
||||
|
||||
ConstDataRange getN() const {
|
||||
return ConstDataRange(_n);
|
||||
}
|
||||
|
||||
StringData getKeyId() const {
|
||||
return _keyId;
|
||||
}
|
||||
|
||||
void appendToBSON(BSONObjBuilder* builder) const;
|
||||
BSONObj toBSON() const {
|
||||
BSONObjBuilder builder;
|
||||
appendToBSON(&builder);
|
||||
return builder.obj();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _keyId;
|
||||
std::vector<std::uint8_t> _e;
|
||||
std::vector<std::uint8_t> _n;
|
||||
};
|
||||
|
||||
} // namespace mongo::crypto
|
||||
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2022-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/crypto/rsa_public_key.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
#include "mongo/util/base64.h"
|
||||
|
||||
namespace mongo::crypto {
|
||||
|
||||
TEST(RSAPublickKeyTest, rsaKeyDecode) {
|
||||
const auto keyID = "0UhWwyvtfIdxPvR9zCWYJB5_AM0LE2qc6RGOcI0cQjw"_sd;
|
||||
const auto e = "AQAB"_sd;
|
||||
const auto n =
|
||||
"ionlnDDd4AG2rRFgjEowRUiZ8x7LTfM-cwwBTuV4TAWgZKb3RycprPwdPODtbKSxnyoM6-Bi-"
|
||||
"qM0FInx13vsC3h3xxzMIreH-vRQPWWocsJ6CZrgfbXyUclcLzgJX_E2V_6hpG0CeUBfNYsgfgwm4Y_"
|
||||
"wjUu3HKsKPdIPqjf6zdrgv8W3OySt-QSFVBy_OQXraZ2wA7gJPyPmNhBr8L9M3AYRS_"
|
||||
"E1XRpsldMSrIe8bfxGyP2B9txiUQXIycWLC-e172SPjAjdUyaK3YLqGRtki6EgQ3qlzRPjoQheE-"
|
||||
"r3l62UaaAgHOo6FercdjdsIzT2-vhqZMQk59WhGuvygymiLw"_sd;
|
||||
|
||||
auto eStr = base64url::decode(e);
|
||||
auto nStr = base64url::decode(n);
|
||||
RsaPublicKey key(keyID, {eStr.c_str(), eStr.size()}, {nStr.c_str(), nStr.size()});
|
||||
|
||||
std::string strE;
|
||||
strE.assign(key.getE().data(), key.getE().length());
|
||||
ASSERT_EQ(base64url::encode(strE), e);
|
||||
|
||||
std::string strN;
|
||||
strN.assign(key.getN().data(), key.getN().length());
|
||||
ASSERT_EQ(base64url::encode(strN), n);
|
||||
}
|
||||
} // namespace mongo::crypto
|
||||
@ -1301,9 +1301,11 @@ env.Library(
|
||||
'$BUILD_DIR/mongo/db/commands/fsync_locked',
|
||||
'$BUILD_DIR/mongo/db/ops/write_ops',
|
||||
'$BUILD_DIR/mongo/db/record_id_helpers',
|
||||
'$BUILD_DIR/mongo/db/repl/replica_set_aware_service',
|
||||
'$BUILD_DIR/mongo/db/repl/tenant_migration_access_blocker',
|
||||
'$BUILD_DIR/mongo/db/s/sharding_runtime_d',
|
||||
'$BUILD_DIR/mongo/idl/server_parameter',
|
||||
'catalog/catalog_helpers',
|
||||
'catalog/database_holder',
|
||||
'commands/server_status_core',
|
||||
'service_context',
|
||||
@ -2517,6 +2519,7 @@ if wiredtiger:
|
||||
'transaction/transaction_history_iterator_test.cpp',
|
||||
'transaction/transaction_participant_retryable_writes_test.cpp',
|
||||
'transaction/transaction_participant_test.cpp',
|
||||
'ttl_collection_cache_test.cpp',
|
||||
'ttl_test.cpp',
|
||||
'update_index_data_test.cpp',
|
||||
'vector_clock_mongod_test.cpp',
|
||||
@ -2617,6 +2620,7 @@ if wiredtiger:
|
||||
'snapshot_window_options',
|
||||
'startup_warnings_mongod',
|
||||
'time_proof_service',
|
||||
'ttl_collection_cache',
|
||||
'ttl_d',
|
||||
'update_index_data',
|
||||
'vector_clock',
|
||||
|
||||
@ -187,7 +187,6 @@ env.Library(
|
||||
],
|
||||
LIBDEPS_PRIVATE=[
|
||||
'$BUILD_DIR/mongo/idl/server_parameter',
|
||||
'oidc_authentication_config',
|
||||
],
|
||||
)
|
||||
|
||||
@ -437,8 +436,6 @@ env.Library(
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/base/secure_allocator',
|
||||
'$BUILD_DIR/mongo/crypto/asymmetric_crypto',
|
||||
'$BUILD_DIR/mongo/crypto/rsa_public_key',
|
||||
'$BUILD_DIR/mongo/crypto/sha_block_${MONGO_CRYPTO}',
|
||||
'$BUILD_DIR/mongo/db/commands/test_commands_enabled',
|
||||
'$BUILD_DIR/mongo/util/icu',
|
||||
@ -538,20 +535,6 @@ env.Library(
|
||||
],
|
||||
)
|
||||
|
||||
env.Library(
|
||||
target="oidc_authentication_config",
|
||||
source=[
|
||||
"oidc_authentication.idl",
|
||||
],
|
||||
LIBDEPS=[
|
||||
'$BUILD_DIR/mongo/base',
|
||||
],
|
||||
LIBDEPS_PRIVATE=[
|
||||
'$BUILD_DIR/mongo/idl/feature_flag',
|
||||
'$BUILD_DIR/mongo/idl/idl_parser',
|
||||
],
|
||||
)
|
||||
|
||||
env.CppUnitTest(
|
||||
target='db_auth_test',
|
||||
source=[
|
||||
|
||||
@ -149,8 +149,7 @@ void reopenAllDatabasesAndReloadCollectionCatalog(OperationContext* opCtx,
|
||||
|
||||
// Opening CollectionCatalog: The collection catalog is now in sync with the storage engine
|
||||
// catalog. Clear the pre-closing state.
|
||||
CollectionCatalog::write(opCtx,
|
||||
[&](CollectionCatalog& catalog) { catalog.onOpenCatalog(opCtx); });
|
||||
CollectionCatalog::write(opCtx, [](CollectionCatalog& catalog) { catalog.onOpenCatalog(); });
|
||||
opCtx->getServiceContext()->incrementCatalogGeneration();
|
||||
LOGV2(20278, "openCatalog: finished reloading collection catalog");
|
||||
}
|
||||
@ -210,14 +209,13 @@ PreviousCatalogState closeCatalog(OperationContext* opCtx) {
|
||||
// Need to mark the CollectionCatalog as open if we our closeAll fails, dismissed if successful.
|
||||
ScopeGuard reopenOnFailure([opCtx] {
|
||||
CollectionCatalog::write(opCtx,
|
||||
[&](CollectionCatalog& catalog) { catalog.onOpenCatalog(opCtx); });
|
||||
[](CollectionCatalog& catalog) { catalog.onOpenCatalog(); });
|
||||
});
|
||||
// Closing CollectionCatalog: only lookupNSSByUUID will fall back to using pre-closing state to
|
||||
// allow authorization for currently unknown UUIDs. This is needed because authorization needs
|
||||
// to work before acquiring locks, and might otherwise spuriously regard a UUID as unknown
|
||||
// while reloading the catalog.
|
||||
CollectionCatalog::write(opCtx,
|
||||
[&](CollectionCatalog& catalog) { catalog.onCloseCatalog(opCtx); });
|
||||
CollectionCatalog::write(opCtx, [](CollectionCatalog& catalog) { catalog.onCloseCatalog(); });
|
||||
|
||||
LOGV2_DEBUG(20270, 1, "closeCatalog: closing collection catalog");
|
||||
|
||||
|
||||
@ -246,7 +246,8 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati
|
||||
"TTL indexes are not supported for capped collections."};
|
||||
}
|
||||
if (auto status = index_key_validate::validateExpireAfterSeconds(
|
||||
*cmdIndex.getExpireAfterSeconds());
|
||||
*cmdIndex.getExpireAfterSeconds(),
|
||||
index_key_validate::ValidateExpireAfterSecondsMode::kSecondaryTTLIndex);
|
||||
!status.isOK()) {
|
||||
return {ErrorCodes::InvalidOptions, status.reason()};
|
||||
}
|
||||
@ -530,7 +531,9 @@ StatusWith<std::pair<ParsedCollModRequest, BSONObj>> parseCollModRequest(Operati
|
||||
},
|
||||
[&oplogEntryBuilder](std::int64_t value) {
|
||||
oplogEntryBuilder.append(CollMod::kExpireAfterSecondsFieldName, value);
|
||||
return index_key_validate::validateExpireAfterSeconds(value);
|
||||
return index_key_validate::validateExpireAfterSeconds(
|
||||
value,
|
||||
index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex);
|
||||
},
|
||||
},
|
||||
*expireAfterSeconds);
|
||||
@ -593,7 +596,8 @@ void _setClusteredExpireAfterSeconds(
|
||||
if (!oldExpireAfterSeconds) {
|
||||
auto ttlCache = &TTLCollectionCache::get(opCtx->getServiceContext());
|
||||
opCtx->recoveryUnit()->onCommit([ttlCache, uuid = coll->uuid()](auto _) {
|
||||
ttlCache->registerTTLInfo(uuid, TTLCollectionCache::ClusteredId());
|
||||
ttlCache->registerTTLInfo(
|
||||
uuid, TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -69,8 +69,10 @@ void _processCollModIndexRequestExpireAfterSeconds(OperationContext* opCtx,
|
||||
// Do not refer to 'idx' within this commit handler as it may be be invalidated by
|
||||
// IndexCatalog::refreshEntry().
|
||||
opCtx->recoveryUnit()->onCommit(
|
||||
[ttlCache, uuid = coll->uuid(), indexName = idx->indexName()](auto _) {
|
||||
ttlCache->registerTTLInfo(uuid, indexName);
|
||||
[ttlCache, uuid = coll->uuid(), indexName = idx->indexName(), indexExpireAfterSeconds](
|
||||
auto _) {
|
||||
ttlCache->registerTTLInfo(
|
||||
uuid, TTLCollectionCache::Info{indexName, /*isExpireAfterSecondsNaN=*/false});
|
||||
});
|
||||
|
||||
// Change the value of "expireAfterSeconds" on disk.
|
||||
@ -79,6 +81,29 @@ void _processCollModIndexRequestExpireAfterSeconds(OperationContext* opCtx,
|
||||
return;
|
||||
}
|
||||
|
||||
// If the current `expireAfterSeconds` is NaN, it can never be equal to
|
||||
// 'indexExpireAfterSeconds'.
|
||||
if (oldExpireSecsElement.isNaN()) {
|
||||
// Setting *oldExpireSecs is mostly for informational purposes.
|
||||
// We could also use index_key_validate::kExpireAfterSecondsForInactiveTTLIndex but
|
||||
// 0 is more consistent with the previous safeNumberLong() behavior and avoids potential
|
||||
// showing the same value for the new and old values in the collMod response.
|
||||
*oldExpireSecs = 0;
|
||||
|
||||
// Change the value of "expireAfterSeconds" on disk.
|
||||
autoColl->getWritableCollection(opCtx)->updateTTLSetting(
|
||||
opCtx, idx->indexName(), indexExpireAfterSeconds);
|
||||
|
||||
// Keep the TTL information maintained by the TTLCollectionCache in sync so that we don't
|
||||
// try to fix up the TTL index during the next step-up.
|
||||
auto ttlCache = &TTLCollectionCache::get(opCtx->getServiceContext());
|
||||
const auto& coll = autoColl->getCollection();
|
||||
opCtx->recoveryUnit()->onCommit(
|
||||
[ttlCache, uuid = coll->uuid(), indexName = idx->indexName(), indexExpireAfterSeconds](
|
||||
auto _) { ttlCache->unsetTTLIndexExpireAfterSecondsNaN(uuid, indexName); });
|
||||
return;
|
||||
}
|
||||
|
||||
// This collection is already TTL. Compare the requested value against the existing setting
|
||||
// before updating the catalog.
|
||||
*oldExpireSecs = oldExpireSecsElement.safeNumberLong();
|
||||
|
||||
@ -733,16 +733,17 @@ void CollectionCatalog::onCloseDatabase(OperationContext* opCtx, DatabaseName db
|
||||
_viewsForDatabase.erase(dbName);
|
||||
}
|
||||
|
||||
void CollectionCatalog::onCloseCatalog(OperationContext* opCtx) {
|
||||
invariant(opCtx->lockState()->isW());
|
||||
invariant(!_shadowCatalog);
|
||||
void CollectionCatalog::onCloseCatalog() {
|
||||
if (_shadowCatalog) {
|
||||
return;
|
||||
}
|
||||
|
||||
_shadowCatalog.emplace();
|
||||
for (auto& entry : _catalog)
|
||||
_shadowCatalog->insert({entry.first, entry.second->ns()});
|
||||
}
|
||||
|
||||
void CollectionCatalog::onOpenCatalog(OperationContext* opCtx) {
|
||||
invariant(opCtx->lockState()->isW());
|
||||
void CollectionCatalog::onOpenCatalog() {
|
||||
invariant(_shadowCatalog);
|
||||
_shadowCatalog.reset();
|
||||
++_epoch;
|
||||
|
||||
@ -522,14 +522,14 @@ public:
|
||||
*
|
||||
* Must be called with the global lock acquired in exclusive mode.
|
||||
*/
|
||||
void onCloseCatalog(OperationContext* opCtx);
|
||||
void onCloseCatalog();
|
||||
|
||||
/**
|
||||
* Puts the catalog back in open state, removing the pre-close state. See onCloseCatalog.
|
||||
*
|
||||
* Must be called with the global lock acquired in exclusive mode.
|
||||
*/
|
||||
void onOpenCatalog(OperationContext* opCtx);
|
||||
void onOpenCatalog();
|
||||
|
||||
/**
|
||||
* The epoch is incremented whenever the catalog is closed and re-opened.
|
||||
|
||||
@ -523,7 +523,7 @@ TEST_F(CollectionCatalogTest, RenameCollection) {
|
||||
TEST_F(CollectionCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsOldNSSIfDropped) {
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onCloseCatalog(opCtx.get());
|
||||
catalog.onCloseCatalog();
|
||||
}
|
||||
|
||||
catalog.deregisterCollection(opCtx.get(), colUUID, /*isDropPending=*/false);
|
||||
@ -532,7 +532,7 @@ TEST_F(CollectionCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsOldNSSIfDrop
|
||||
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onOpenCatalog(opCtx.get());
|
||||
catalog.onOpenCatalog();
|
||||
}
|
||||
|
||||
ASSERT_EQUALS(catalog.lookupNSSByUUID(opCtx.get(), colUUID), boost::none);
|
||||
@ -547,7 +547,7 @@ TEST_F(CollectionCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsNewlyCreated
|
||||
// Ensure that looking up non-existing UUIDs doesn't affect later registration of those UUIDs.
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onCloseCatalog(opCtx.get());
|
||||
catalog.onCloseCatalog();
|
||||
}
|
||||
|
||||
ASSERT(catalog.lookupCollectionByUUID(opCtx.get(), newUUID) == nullptr);
|
||||
@ -559,7 +559,7 @@ TEST_F(CollectionCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsNewlyCreated
|
||||
// Ensure that collection still exists after opening the catalog again.
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onOpenCatalog(opCtx.get());
|
||||
catalog.onOpenCatalog();
|
||||
}
|
||||
|
||||
ASSERT_EQUALS(catalog.lookupCollectionByUUID(opCtx.get(), newUUID), newCol);
|
||||
@ -573,7 +573,7 @@ TEST_F(CollectionCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsFreshestNSS)
|
||||
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onCloseCatalog(opCtx.get());
|
||||
catalog.onCloseCatalog();
|
||||
}
|
||||
|
||||
catalog.deregisterCollection(opCtx.get(), colUUID, /*isDropPending=*/false);
|
||||
@ -586,7 +586,7 @@ TEST_F(CollectionCatalogTest, LookupNSSByUUIDForClosedCatalogReturnsFreshestNSS)
|
||||
// Ensure that collection still exists after opening the catalog again.
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onOpenCatalog(opCtx.get());
|
||||
catalog.onOpenCatalog();
|
||||
}
|
||||
|
||||
ASSERT_EQUALS(catalog.lookupCollectionByUUID(opCtx.get(), colUUID), newCol);
|
||||
@ -599,8 +599,8 @@ TEST_F(CollectionCatalogTest, CollectionCatalogEpoch) {
|
||||
|
||||
{
|
||||
Lock::GlobalLock globalLk(opCtx.get(), MODE_X);
|
||||
catalog.onCloseCatalog(opCtx.get());
|
||||
catalog.onOpenCatalog(opCtx.get());
|
||||
catalog.onCloseCatalog();
|
||||
catalog.onOpenCatalog();
|
||||
}
|
||||
|
||||
auto incrementedEpoch = catalog.getEpoch();
|
||||
|
||||
@ -409,11 +409,11 @@ void CollectionImpl::init(OperationContext* opCtx) {
|
||||
if (opCtx->lockState()->inAWriteUnitOfWork()) {
|
||||
opCtx->recoveryUnit()->onCommit([svcCtx, uuid](auto ts) {
|
||||
TTLCollectionCache::get(svcCtx).registerTTLInfo(
|
||||
uuid, TTLCollectionCache::ClusteredId{});
|
||||
uuid, TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
|
||||
});
|
||||
} else {
|
||||
TTLCollectionCache::get(svcCtx).registerTTLInfo(uuid,
|
||||
TTLCollectionCache::ClusteredId{});
|
||||
TTLCollectionCache::get(svcCtx).registerTTLInfo(
|
||||
uuid, TTLCollectionCache::Info{TTLCollectionCache::ClusteredId{}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +118,9 @@ Status validateClusteredIndexSpec(OperationContext* opCtx,
|
||||
|
||||
if (expireAfterSeconds) {
|
||||
// Not included in the indexSpec itself.
|
||||
auto status = index_key_validate::validateExpireAfterSeconds(*expireAfterSeconds);
|
||||
auto status = index_key_validate::validateExpireAfterSeconds(
|
||||
*expireAfterSeconds,
|
||||
index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex);
|
||||
if (!status.isOK()) {
|
||||
return status;
|
||||
}
|
||||
@ -378,8 +380,9 @@ Status _createTimeseries(OperationContext* opCtx,
|
||||
// Cluster time-series buckets collections by _id.
|
||||
auto expireAfterSeconds = options.expireAfterSeconds;
|
||||
if (expireAfterSeconds) {
|
||||
uassertStatusOK(
|
||||
index_key_validate::validateExpireAfterSeconds(*expireAfterSeconds));
|
||||
uassertStatusOK(index_key_validate::validateExpireAfterSeconds(
|
||||
*expireAfterSeconds,
|
||||
index_key_validate::ValidateExpireAfterSecondsMode::kClusteredTTLIndex));
|
||||
bucketsOptions.expireAfterSeconds = expireAfterSeconds;
|
||||
}
|
||||
|
||||
|
||||
@ -278,7 +278,10 @@ void IndexBuildBlock::success(OperationContext* opCtx, Collection* collection) {
|
||||
// Note that TTL deletion is supported on capped clustered collections via bounded
|
||||
// collection scan, which does not use an index.
|
||||
if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName) && !coll->isCapped()) {
|
||||
TTLCollectionCache::get(svcCtx).registerTTLInfo(coll->uuid(), indexName);
|
||||
TTLCollectionCache::get(svcCtx).registerTTLInfo(
|
||||
coll->uuid(),
|
||||
TTLCollectionCache::Info{
|
||||
indexName, spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -205,13 +205,30 @@ Status IndexCatalogImpl::init(OperationContext* opCtx, Collection* collection) {
|
||||
|
||||
auto descriptor = std::make_unique<IndexDescriptor>(_getAccessMethodName(keyPattern), spec);
|
||||
|
||||
// TTL indexes with NaN 'expireAfterSeconds' cause problems in multiversion settings.
|
||||
if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName)) {
|
||||
if (spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()) {
|
||||
LOGV2_OPTIONS(6852200,
|
||||
{logv2::LogTag::kStartupWarnings},
|
||||
"Found an existing TTL index with NaN 'expireAfterSeconds' in the "
|
||||
"catalog.",
|
||||
"ns"_attr = collection->ns(),
|
||||
"uuid"_attr = collection->uuid(),
|
||||
"index"_attr = indexName,
|
||||
"spec"_attr = spec);
|
||||
}
|
||||
}
|
||||
|
||||
// TTL indexes are not compatible with capped collections.
|
||||
// Note that TTL deletion is supported on capped clustered collections via bounded
|
||||
// collection scan, which does not use an index.
|
||||
if (spec.hasField(IndexDescriptor::kExpireAfterSecondsFieldName) &&
|
||||
!collection->isCapped()) {
|
||||
TTLCollectionCache::get(opCtx->getServiceContext())
|
||||
.registerTTLInfo(collection->uuid(), indexName);
|
||||
.registerTTLInfo(
|
||||
collection->uuid(),
|
||||
TTLCollectionCache::Info{
|
||||
indexName, spec[IndexDescriptor::kExpireAfterSecondsFieldName].isNaN()});
|
||||
}
|
||||
|
||||
bool ready = collection->isIndexReady(indexName);
|
||||
|
||||
@ -71,6 +71,10 @@ namespace {
|
||||
// specification.
|
||||
MONGO_FAIL_POINT_DEFINE(skipIndexCreateFieldNameValidation);
|
||||
|
||||
// When the skipTTLIndexNaNExpireAfterSecondsValidation failpoint is enabled, validation for
|
||||
// TTL index 'expireAfterSeconds' will be disabled.
|
||||
MONGO_FAIL_POINT_DEFINE(skipTTLIndexNaNExpireAfterSecondsValidation);
|
||||
|
||||
static const std::set<StringData> allowedIdIndexFieldNames = {
|
||||
IndexDescriptor::kCollationFieldName,
|
||||
IndexDescriptor::kIndexNameFieldName,
|
||||
@ -269,8 +273,8 @@ BSONObj removeUnknownFields(const NamespaceString& ns, const BSONObj& indexSpec)
|
||||
BSONObj repairIndexSpec(const NamespaceString& ns,
|
||||
const BSONObj& indexSpec,
|
||||
const std::set<StringData>& allowedFieldNames) {
|
||||
auto fixBoolIndexSpecFn = [&indexSpec, &ns](const BSONElement& indexSpecElem,
|
||||
BSONObjBuilder* builder) {
|
||||
auto fixIndexSpecFn = [&indexSpec, &ns](const BSONElement& indexSpecElem,
|
||||
BSONObjBuilder* builder) {
|
||||
StringData fieldName = indexSpecElem.fieldNameStringData();
|
||||
if ((IndexDescriptor::kBackgroundFieldName == fieldName ||
|
||||
IndexDescriptor::kUniqueFieldName == fieldName ||
|
||||
@ -285,17 +289,28 @@ BSONObj repairIndexSpec(const NamespaceString& ns,
|
||||
"fieldName"_attr = redact(fieldName),
|
||||
"indexSpec"_attr = redact(indexSpec));
|
||||
builder->appendBool(fieldName, true);
|
||||
} else if (IndexDescriptor::kExpireAfterSecondsFieldName == fieldName &&
|
||||
!(indexSpecElem.isNumber() && !indexSpecElem.isNaN())) {
|
||||
LOGV2_WARNING(6835900,
|
||||
"Fixing expire field from TTL index spec",
|
||||
"namespace"_attr = redact(ns.toString()),
|
||||
"fieldName"_attr = redact(fieldName),
|
||||
"indexSpec"_attr = redact(indexSpec));
|
||||
builder->appendNumber(fieldName,
|
||||
durationCount<Seconds>(kExpireAfterSecondsForInactiveTTLIndex));
|
||||
} else {
|
||||
builder->append(indexSpecElem);
|
||||
}
|
||||
};
|
||||
return buildRepairedIndexSpec(ns, indexSpec, allowedFieldNames, fixBoolIndexSpecFn);
|
||||
|
||||
return buildRepairedIndexSpec(ns, indexSpec, allowedFieldNames, fixIndexSpecFn);
|
||||
}
|
||||
|
||||
StatusWith<BSONObj> validateIndexSpec(OperationContext* opCtx, const BSONObj& indexSpec) {
|
||||
bool hasKeyPatternField = false;
|
||||
bool hasIndexNameField = false;
|
||||
bool hasNamespaceField = false;
|
||||
bool isTTLIndexWithNaNExpireAfterSeconds = false;
|
||||
bool hasVersionField = false;
|
||||
bool hasCollationField = false;
|
||||
bool hasWeightsField = false;
|
||||
@ -554,6 +569,8 @@ StatusWith<BSONObj> validateIndexSpec(OperationContext* opCtx, const BSONObj& in
|
||||
str::stream() << "The field '" << indexSpecElemFieldName
|
||||
<< "' must be a number, but got "
|
||||
<< typeName(indexSpecElem.type())};
|
||||
} else if (IndexDescriptor::kExpireAfterSecondsFieldName == indexSpecElemFieldName) {
|
||||
isTTLIndexWithNaNExpireAfterSeconds = indexSpecElem.isNaN();
|
||||
} else {
|
||||
// We can assume field name is valid at this point. Validation of fieldname is handled
|
||||
// prior to this in validateIndexSpecFieldNames().
|
||||
@ -625,6 +642,19 @@ StatusWith<BSONObj> validateIndexSpec(OperationContext* opCtx, const BSONObj& in
|
||||
modifiedSpec = modifiedSpec.removeField(IndexDescriptor::kNamespaceFieldName);
|
||||
}
|
||||
|
||||
if (isTTLIndexWithNaNExpireAfterSeconds &&
|
||||
!skipTTLIndexNaNExpireAfterSecondsValidation.shouldFail()) {
|
||||
// We create a new index specification with the 'expireAfterSeconds' field set as
|
||||
// kExpireAfterSecondsForInactiveTTLIndex if the current value is NaN. A similar
|
||||
// treatment is done in repairIndexSpec(). This rewrites the 'expireAfterSeconds'
|
||||
// value to be compliant with the 'safeInt' IDL type for the listIndexes response.
|
||||
BSONObjBuilder builder;
|
||||
builder.appendNumber(IndexDescriptor::kExpireAfterSecondsFieldName,
|
||||
durationCount<Seconds>(kExpireAfterSecondsForInactiveTTLIndex));
|
||||
auto obj = builder.obj();
|
||||
modifiedSpec = modifiedSpec.addField(obj.firstElement());
|
||||
}
|
||||
|
||||
if (!hasVersionField) {
|
||||
// We create a new index specification with the 'v' field set as 'defaultIndexVersion' if
|
||||
// the field was omitted.
|
||||
@ -785,7 +815,8 @@ StatusWith<BSONObj> validateIndexSpecCollation(OperationContext* opCtx,
|
||||
return indexSpec;
|
||||
}
|
||||
|
||||
Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds) {
|
||||
Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds,
|
||||
ValidateExpireAfterSecondsMode mode) {
|
||||
if (expireAfterSeconds < 0) {
|
||||
return {ErrorCodes::InvalidOptions,
|
||||
str::stream() << "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName
|
||||
@ -796,16 +827,31 @@ Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds) {
|
||||
<< "TTL index '" << IndexDescriptor::kExpireAfterSecondsFieldName
|
||||
<< "' option must be within an acceptable range, try a lower number";
|
||||
|
||||
// There are two cases where we can encounter an issue here.
|
||||
// The first case is when we try to cast to millseconds from seconds, which could cause an
|
||||
// overflow. The second case is where 'expireAfterSeconds' is larger than the current epoch
|
||||
// time.
|
||||
if (expireAfterSeconds > std::numeric_limits<std::int64_t>::max() / 1000) {
|
||||
return {ErrorCodes::InvalidOptions, tooLargeErr};
|
||||
}
|
||||
auto expireAfterMillis = duration_cast<Milliseconds>(Seconds(expireAfterSeconds));
|
||||
if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) {
|
||||
return {ErrorCodes::InvalidOptions, tooLargeErr};
|
||||
if (mode == ValidateExpireAfterSecondsMode::kSecondaryTTLIndex) {
|
||||
// Relax epoch restriction on TTL indexes. This allows us to export and import existing
|
||||
// TTL indexes with large values or NaN for the 'expireAfterSeconds' field.
|
||||
// Additionally, the 'expireAfterSeconds' for TTL indexes is defined as safeInt (int32_t)
|
||||
// in the IDL for listIndexes and collMod. See list_indexes.idl and coll_mod.idl.
|
||||
if (expireAfterSeconds > std::numeric_limits<std::int32_t>::max()) {
|
||||
return {ErrorCodes::InvalidOptions, tooLargeErr};
|
||||
}
|
||||
} else {
|
||||
// Clustered collections with TTL.
|
||||
// Note that 'expireAfterSeconds' is defined as safeInt64 in the IDL for the create and
|
||||
// collMod commands. See create.idl and coll_mod.idl.
|
||||
// There are two cases where we can encounter an issue here.
|
||||
// The first case is when we try to cast to millseconds from seconds, which could cause an
|
||||
// overflow. The second case is where 'expireAfterSeconds' is larger than the current epoch
|
||||
// time. This isn't necessarily problematic for the general case, but for the specific case
|
||||
// of time series collections, we cluster the collection by an OID value, where the
|
||||
// timestamp portion is only a 32-bit unsigned integer offset of seconds since the epoch.
|
||||
if (expireAfterSeconds > std::numeric_limits<std::int64_t>::max() / 1000) {
|
||||
return {ErrorCodes::InvalidOptions, tooLargeErr};
|
||||
}
|
||||
auto expireAfterMillis = duration_cast<Milliseconds>(Seconds(expireAfterSeconds));
|
||||
if (expireAfterMillis > Date_t::now().toDurationSinceEpoch()) {
|
||||
return {ErrorCodes::InvalidOptions, tooLargeErr};
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
@ -829,7 +875,9 @@ Status validateIndexSpecTTL(const BSONObj& indexSpec) {
|
||||
<< "'. Index spec: " << indexSpec};
|
||||
}
|
||||
|
||||
if (auto status = validateExpireAfterSeconds(expireAfterSecondsElt.safeNumberLong());
|
||||
if (auto status =
|
||||
validateExpireAfterSeconds(expireAfterSecondsElt.safeNumberLong(),
|
||||
ValidateExpireAfterSecondsMode::kSecondaryTTLIndex);
|
||||
!status.isOK()) {
|
||||
return {ErrorCodes::CannotCreateIndex,
|
||||
str::stream() << status.reason() << ". Index spec: " << indexSpec};
|
||||
|
||||
@ -43,6 +43,12 @@ class StatusWith;
|
||||
|
||||
namespace index_key_validate {
|
||||
|
||||
// TTL indexes with 'expireAfterSeconds' are repaired with this duration, which is chosen to be
|
||||
// the largest possible value for the 'safeInt' type that can be returned in the listIndexes
|
||||
// response.
|
||||
constexpr auto kExpireAfterSecondsForInactiveTTLIndex =
|
||||
Seconds(std::numeric_limits<int32_t>::max());
|
||||
|
||||
static std::set<StringData> allowedFieldNames = {
|
||||
IndexDescriptor::k2dIndexBitsFieldName,
|
||||
IndexDescriptor::k2dIndexMaxFieldName,
|
||||
@ -122,9 +128,14 @@ StatusWith<BSONObj> validateIndexSpecCollation(OperationContext* opCtx,
|
||||
const CollatorInterface* defaultCollator);
|
||||
|
||||
/**
|
||||
* Validates the the 'expireAfterSeconds' value for a TTL index..
|
||||
* Validates the the 'expireAfterSeconds' value for a TTL index or clustered collection.
|
||||
*/
|
||||
Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds);
|
||||
enum class ValidateExpireAfterSecondsMode {
|
||||
kSecondaryTTLIndex,
|
||||
kClusteredTTLIndex,
|
||||
};
|
||||
Status validateExpireAfterSeconds(std::int64_t expireAfterSeconds,
|
||||
ValidateExpireAfterSecondsMode mode);
|
||||
|
||||
/**
|
||||
* Returns true if 'indexSpec' refers to a TTL index.
|
||||
|
||||
@ -400,6 +400,15 @@ TEST(IndexKeyValidateTest, RemoveUnkownFieldsFromIndexSpecs) {
|
||||
fromjson("{key: {a: 1}, name: 'index', safe: true, force: true}"))));
|
||||
}
|
||||
|
||||
TEST(IndexKeyValidateTest, UpdateTTLIndexNaNExpireAfterSeconds) {
|
||||
ASSERT(BSON("key" << BSON("a" << 1) << "name"
|
||||
<< "index"
|
||||
<< "expireAfterSeconds" << std::numeric_limits<int32_t>::max()
|
||||
<< IndexDescriptor::kIndexVersionFieldName << IndexVersion::kV2)
|
||||
.binaryEqual(unittest::assertGet(index_key_validate::validateIndexSpec(
|
||||
nullptr, fromjson("{key: {a: 1}, name: 'index', expireAfterSeconds: NaN}")))));
|
||||
}
|
||||
|
||||
TEST(IndexKeyValidateTest, RepairIndexSpecs) {
|
||||
ASSERT(fromjson("{key: {a: 1}, name: 'index'}")
|
||||
.binaryEqual(index_key_validate::repairIndexSpec(
|
||||
@ -426,6 +435,20 @@ TEST(IndexKeyValidateTest, RepairIndexSpecs) {
|
||||
NamespaceString("coll"),
|
||||
fromjson("{key: {a: 1}, name: 'index', sparse: 'true', background: '1', safe: "
|
||||
"true, force: true}"))));
|
||||
|
||||
ASSERT(BSON("key" << BSON("a" << 1) << "name"
|
||||
<< "index"
|
||||
<< "expireAfterSeconds" << std::numeric_limits<int32_t>::max())
|
||||
.binaryEqual(index_key_validate::repairIndexSpec(
|
||||
NamespaceString("coll"),
|
||||
fromjson("{key: {a: 1}, name: 'index', expireAfterSeconds: NaN}"))));
|
||||
|
||||
ASSERT(BSON("key" << BSON("a" << 1) << "name"
|
||||
<< "index"
|
||||
<< "expireAfterSeconds" << std::numeric_limits<int32_t>::max())
|
||||
.binaryEqual(index_key_validate::repairIndexSpec(
|
||||
NamespaceString("coll"),
|
||||
fromjson("{key: {a: 1}, name: 'index', expireAfterSeconds: '123'}"))));
|
||||
}
|
||||
|
||||
TEST(IndexKeyValidateTest, GeoIndexSpecs) {
|
||||
|
||||
@ -1116,6 +1116,17 @@ Status SortedDataIndexAccessMethod::_indexKeysOrWriteToSideTable(
|
||||
*keysInsertedOut += inserted;
|
||||
}
|
||||
} else {
|
||||
// Ensure that our snapshot is compatible with the index's minimum visibile snapshot.
|
||||
const auto minVisibleTimestamp = _indexCatalogEntry->getMinimumVisibleSnapshot();
|
||||
const auto readTimestamp =
|
||||
opCtx->recoveryUnit()->getPointInTimeReadTimestamp(opCtx).value_or(
|
||||
opCtx->recoveryUnit()->getCatalogConflictingTimestamp());
|
||||
if (minVisibleTimestamp && !readTimestamp.isNull() &&
|
||||
readTimestamp < *minVisibleTimestamp) {
|
||||
throwWriteConflictException(
|
||||
"Unable to read from a snapshot due to pending catalog changes.");
|
||||
}
|
||||
|
||||
int64_t numInserted = 0;
|
||||
status = insertKeysAndUpdateMultikeyPaths(
|
||||
opCtx,
|
||||
@ -1178,6 +1189,15 @@ void SortedDataIndexAccessMethod::_unindexKeysOrWriteToSideTable(
|
||||
options.dupsAllowed = options.dupsAllowed || !_indexCatalogEntry->isReady(opCtx) ||
|
||||
(checkRecordId == CheckRecordId::On);
|
||||
|
||||
// Ensure that our snapshot is compatible with the index's minimum visibile snapshot.
|
||||
const auto minVisibleTimestamp = _indexCatalogEntry->getMinimumVisibleSnapshot();
|
||||
const auto readTimestamp = opCtx->recoveryUnit()->getPointInTimeReadTimestamp(opCtx).value_or(
|
||||
opCtx->recoveryUnit()->getCatalogConflictingTimestamp());
|
||||
if (minVisibleTimestamp && !readTimestamp.isNull() && readTimestamp < *minVisibleTimestamp) {
|
||||
throwWriteConflictException(
|
||||
"Unable to read from a snapshot due to pending catalog changes.");
|
||||
}
|
||||
|
||||
int64_t removed = 0;
|
||||
Status status = removeKeys(opCtx, keys, options, &removed);
|
||||
|
||||
|
||||
@ -192,8 +192,9 @@ void removeIndexBuildEntryAfterCommitOrAbort(OperationContext* opCtx,
|
||||
}
|
||||
|
||||
if (replCoord->getSettings().shouldRecoverFromOplogAsStandalone()) {
|
||||
// TODO SERVER-60753: Remove this mixed-mode write.
|
||||
opCtx->recoveryUnit()->allowUntimestampedWrite();
|
||||
// Writes to the 'config.system.indexBuilds' collection are replicated and the index entry
|
||||
// will be removed when the delete oplog entry is replayed at a later time.
|
||||
return;
|
||||
}
|
||||
|
||||
auto status = indexbuildentryhelpers::removeIndexBuildEntry(
|
||||
@ -2702,7 +2703,11 @@ IndexBuildsCoordinator::CommitResult IndexBuildsCoordinator::_insertKeysFromSide
|
||||
// TODO SERVER-67437 Once ReplIndexBuildState holds DatabaseName, use dbName directly for
|
||||
// lock
|
||||
DatabaseName dbName(boost::none, replState->dbName);
|
||||
AutoGetDb autoDb(opCtx, dbName, MODE_IX);
|
||||
// Skip the check for sharding's critical section check as it can only be acquired during a
|
||||
// `movePrimary` or drop database operations. The only operation that would affect the index
|
||||
// build is when the collection's data needs to get modified, but the only modification possible
|
||||
// is to delete the entire collection, which will cause the index to be dropped.
|
||||
Lock::DBLock dbLock(opCtx, dbName, MODE_IX);
|
||||
|
||||
// Unlock RSTL to avoid deadlocks with prepare conflicts and state transitions caused by waiting
|
||||
// for a a strong collection lock. See SERVER-42621.
|
||||
|
||||
@ -701,8 +701,10 @@ void OpObserverImpl::onInserts(OperationContext* opCtx,
|
||||
// DOES need to be -- that will cause correctness issues). Additionally, if the user tried
|
||||
// to insert measurements with dates outside the standard range, chances are they will do so
|
||||
// again, and we will have only set the flag a little early.
|
||||
invariant(opCtx->lockState()->isCollectionLockedForMode(nss, MODE_IX));
|
||||
auto bucketsColl =
|
||||
CollectionCatalog::get(opCtx)->lookupCollectionByNamespaceForRead(opCtx, nss);
|
||||
tassert(6905201, "Could not find collection for write", bucketsColl);
|
||||
auto timeSeriesOptions = bucketsColl->getTimeseriesOptions();
|
||||
if (timeSeriesOptions.has_value()) {
|
||||
if (auto currentSetting = bucketsColl->getRequiresTimeseriesExtendedRangeSupport();
|
||||
|
||||
@ -123,6 +123,7 @@ env.Library(
|
||||
'accumulator_rank.cpp',
|
||||
'accumulator_std_dev.cpp',
|
||||
'accumulator_sum.cpp',
|
||||
'map_reduce_options.idl',
|
||||
'window_function/window_bounds.cpp',
|
||||
'window_function/window_function_covariance.cpp',
|
||||
'window_function/window_function_count.cpp',
|
||||
@ -142,6 +143,7 @@ env.Library(
|
||||
LIBDEPS_PRIVATE=[
|
||||
'$BUILD_DIR/mongo/db/exec/sort_executor',
|
||||
'$BUILD_DIR/mongo/db/index/key_generator',
|
||||
'$BUILD_DIR/mongo/idl/idl_parser',
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/db/pipeline/accumulator_js_reduce.h"
|
||||
#include "mongo/db/pipeline/make_js_function.h"
|
||||
#include "mongo/db/pipeline/map_reduce_options_gen.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
@ -119,6 +120,10 @@ void AccumulatorInternalJsReduce::processInternal(const Value& input, bool mergi
|
||||
Value AccumulatorInternalJsReduce::getValue(bool toBeMerged) {
|
||||
if (_values.size() < 1) {
|
||||
return Value{};
|
||||
} else if (mrSingleReduceOptimizationEnabled && _values.size() == 1) {
|
||||
// This optimization existed in the old Pre-4.4 MapReduce implementation. If the flag is
|
||||
// set, then we should replicate the optimization. See SERVER-68766 for more details.
|
||||
return _values[0];
|
||||
}
|
||||
|
||||
const auto keySize = _key.getApproximateSize();
|
||||
|
||||
@ -38,6 +38,7 @@
|
||||
#include "mongo/db/pipeline/process_interface/standalone_process_interface.h"
|
||||
#include "mongo/db/service_context_d_test_fixture.h"
|
||||
#include "mongo/dbtests/dbtests.h"
|
||||
#include "mongo/idl/server_parameter_test_util.h"
|
||||
#include "mongo/scripting/engine.h"
|
||||
|
||||
namespace mongo {
|
||||
@ -197,6 +198,38 @@ TEST_F(MapReduceFixture, InternalJsReduceFailsWhenEvalContainsInvalidJavascript)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
MapReduceFixture,
|
||||
InternalJsReduceFailsDependentOnDocumentCountWhenEvalIsInvalidJavascriptWithSingleReduceOpt) {
|
||||
RAIIServerParameterControllerForTest flag("mrEnableSingleReduceOptimization", true);
|
||||
std::string eval("INVALID_JAVASCRIPT");
|
||||
// Multiple source documents should evaluate the passed in function and return an error with
|
||||
// invalid javascript.
|
||||
{
|
||||
auto accum = AccumulatorInternalJsReduce::create(getExpCtx(), "INVALID_JAVASCRIPT");
|
||||
auto input = Value(DOC("k" << Value(1) << "v" << Value(2)));
|
||||
accum->process(input, false);
|
||||
accum->process(input, false);
|
||||
|
||||
ASSERT_THROWS_CODE(accum->getValue(false), DBException, ErrorCodes::JSInterpreterFailure);
|
||||
}
|
||||
|
||||
// Single source document. With the reduce optimization, we simply return this document rather
|
||||
// than executing the JS engine at all, so no error is thrown.
|
||||
{
|
||||
auto accum = AccumulatorInternalJsReduce::create(getExpCtx(), "INVALID_JAVASCRIPT");
|
||||
|
||||
auto input = Value(DOC("k" << Value(1) << "v" << Value(2)));
|
||||
auto expectedResult = Value(2);
|
||||
|
||||
accum->process(input, false);
|
||||
Value result = accum->getValue(false);
|
||||
|
||||
ASSERT_VALUE_EQ(expectedResult, result);
|
||||
ASSERT_EQUALS(expectedResult.getType(), result.getType());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(MapReduceFixture, InternalJsReduceFailsIfArgumentNotDocument) {
|
||||
auto argument = Value(2);
|
||||
assertProcessFailsWithCode<AccumulatorInternalJsReduce>(
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
|
||||
#include "mongo/db/pipeline/change_stream_rewrite_helpers.h"
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
#include "mongo/db/matcher/expression_always_boolean.h"
|
||||
#include "mongo/db/matcher/expression_expr.h"
|
||||
#include "mongo/db/pipeline/document_source_change_stream.h"
|
||||
@ -888,9 +890,12 @@ std::unique_ptr<MatchExpression> matchRewriteGenericNamespace(
|
||||
}();
|
||||
|
||||
// Convert the MatchExpression $regex into a $regexMatch on the corresponding field.
|
||||
// Backslashes must be escaped to ensure they retain their special behavior.
|
||||
const auto regex =
|
||||
boost::replace_all_copy(std::string(nsElem.regex()), R"(\)", R"(\\)");
|
||||
const std::string exprRegexMatch = str::stream()
|
||||
<< "{$regexMatch: {input: " << exprDbOrCollName << ", regex: '"
|
||||
<< nsElem.regex() << "', options: '" << nsElem.regexFlags() << "'}}";
|
||||
<< "{$regexMatch: {input: " << exprDbOrCollName << ", regex: '" << regex
|
||||
<< "', options: '" << nsElem.regexFlags() << "'}}";
|
||||
|
||||
// Finally, wrap the regex in a $let which defines the '$$oplogField' variable.
|
||||
const std::string exprRewrittenPredicate = str::stream()
|
||||
|
||||
@ -656,6 +656,7 @@ DocumentSourceGraphLookUp::DocumentSourceGraphLookUp(
|
||||
_variablesParseState(expCtx->variablesParseState.copyWith(_variables.useIdGenerator())) {
|
||||
const auto& resolvedNamespace = pExpCtx->getResolvedNamespace(_from);
|
||||
_fromExpCtx = pExpCtx->copyForSubPipeline(resolvedNamespace.ns, resolvedNamespace.uuid);
|
||||
_fromExpCtx->inLookup = true;
|
||||
|
||||
// We append an additional BSONObj to '_fromPipeline' as a placeholder for the $match stage
|
||||
// we'll eventually construct from the input document.
|
||||
|
||||
@ -487,6 +487,10 @@ intrusive_ptr<DocumentSourceSort> DocumentSourceSort::parseBoundedSort(
|
||||
BSONElement key = args["sortKey"];
|
||||
uassert(6369904, "$_internalBoundedSort sortKey must be an object", key.type() == Object);
|
||||
|
||||
// Empty sort pattern is not allowed for the bounded sort.
|
||||
uassert(6900501,
|
||||
"$_internalBoundedSort stage must have at least one sort key",
|
||||
!key.embeddedObject().isEmpty());
|
||||
SortPattern pat{key.embeddedObject(), expCtx};
|
||||
|
||||
{
|
||||
|
||||
@ -436,7 +436,7 @@ public:
|
||||
// Tracks the depth of nested aggregation sub-pipelines. Used to enforce depth limits.
|
||||
long long subPipelineDepth = 0;
|
||||
|
||||
// True if this 'ExpressionContext' object is for the inner side of a $lookup.
|
||||
// True if this 'ExpressionContext' object is for the inner side of a $lookup or $graphLookup.
|
||||
bool inLookup = false;
|
||||
|
||||
// If set, this will disallow use of features introduced in versions above the provided version.
|
||||
|
||||
@ -24,15 +24,19 @@
|
||||
# delete this exception statement from your version. If you delete this
|
||||
# exception statement from all source files in the program, then also delete
|
||||
# it in the license file.
|
||||
#
|
||||
|
||||
global:
|
||||
cpp_namespace: "mongo"
|
||||
|
||||
imports:
|
||||
- "mongo/idl/basic_types.idl"
|
||||
|
||||
feature_flags:
|
||||
featureFlagOIDCSpike:
|
||||
description: "Feature flag to guard OIDC during spike implementation"
|
||||
cpp_varname: gFeatureFlagOIDCSpike
|
||||
server_parameters:
|
||||
mrEnableSingleReduceOptimization:
|
||||
description: >
|
||||
In version 4.2 and before, MongoDB MapReduce will not call the reduce function
|
||||
for a key that has only a single value. In version 4.4 and later, the reduce
|
||||
function is still called in order to validate the JavaScript reduce function
|
||||
even when there is only one value. This setting will re-enable the old optimization.
|
||||
set_at: startup
|
||||
cpp_vartype: bool
|
||||
cpp_varname: mrSingleReduceOptimizationEnabled
|
||||
default: false
|
||||
@ -50,11 +50,8 @@ public:
|
||||
PerFunctionMemoryTracker() = delete;
|
||||
|
||||
void update(long long diff) {
|
||||
tassert(5578603,
|
||||
str::stream() << "Underflow on memory tracking, attempting to add " << diff
|
||||
<< " but only " << _currentMemoryBytes << " available",
|
||||
diff >= 0 || _currentMemoryBytes >= std::abs(diff));
|
||||
set(_currentMemoryBytes + diff);
|
||||
// TODO SERVER-61281: Check for memory underflow.
|
||||
set(std::max(_currentMemoryBytes + diff, 0ll));
|
||||
}
|
||||
|
||||
void set(long long total) {
|
||||
@ -145,11 +142,8 @@ public:
|
||||
* Updates total memory usage.
|
||||
*/
|
||||
void update(long long diff) {
|
||||
tassert(5578602,
|
||||
str::stream() << "Underflow on memory tracking, attempting to add " << diff
|
||||
<< " but only " << _memoryUsageBytes << " available",
|
||||
diff >= 0 || (int)_memoryUsageBytes >= -1 * diff);
|
||||
set(_memoryUsageBytes + diff);
|
||||
// TODO SERVER-61281: Check for memory underflow.
|
||||
set(std::max(_memoryUsageBytes + diff, 0ll));
|
||||
}
|
||||
|
||||
auto currentMemoryBytes() const {
|
||||
|
||||
@ -99,9 +99,8 @@ TEST_F(MemoryUsageTrackerTest, UpdateUsageUpdatesGlobal) {
|
||||
ASSERT_EQ(_tracker.maxMemoryBytes(), 150LL);
|
||||
}
|
||||
|
||||
DEATH_TEST_F(MemoryUsageTrackerTest,
|
||||
UpdateFunctionUsageToNegativeIsDisallowed,
|
||||
"Underflow on memory tracking") {
|
||||
// TODO SERVER-61281: Switch to 'DEATH_TEST_F' checking the underflow case.
|
||||
TEST_F(MemoryUsageTrackerTest, UpdateFunctionUsageToNegativeIsDisallowed) {
|
||||
_funcTracker.set(50LL);
|
||||
ASSERT_EQ(_funcTracker.currentMemoryBytes(), 50LL);
|
||||
ASSERT_EQ(_funcTracker.maxMemoryBytes(), 50LL);
|
||||
@ -109,16 +108,19 @@ DEATH_TEST_F(MemoryUsageTrackerTest,
|
||||
ASSERT_EQ(_tracker.maxMemoryBytes(), 50LL);
|
||||
|
||||
_funcTracker.update(-100);
|
||||
ASSERT_EQ(_tracker.currentMemoryBytes(), 0LL);
|
||||
ASSERT_EQ(_tracker.maxMemoryBytes(), 50LL);
|
||||
}
|
||||
|
||||
DEATH_TEST_F(MemoryUsageTrackerTest,
|
||||
UpdateMemUsageToNegativeIsDisallowed,
|
||||
"Underflow on memory tracking") {
|
||||
// TODO SERVER-61281: Switch to 'DEATH_TEST_F' checking the underflow case.
|
||||
TEST_F(MemoryUsageTrackerTest, UpdateMemUsageToNegativeIsDisallowed) {
|
||||
_tracker.set(50LL);
|
||||
ASSERT_EQ(_tracker.currentMemoryBytes(), 50LL);
|
||||
ASSERT_EQ(_tracker.maxMemoryBytes(), 50LL);
|
||||
|
||||
_tracker.update(-100);
|
||||
ASSERT_EQ(_tracker.currentMemoryBytes(), 0LL);
|
||||
ASSERT_EQ(_tracker.maxMemoryBytes(), 50LL);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -31,6 +31,8 @@
|
||||
#include "mongo/db/pipeline/document_source_mock.h"
|
||||
#include "mongo/db/pipeline/sharded_agg_helpers.h"
|
||||
#include "mongo/db/s/config/initial_split_policy.h"
|
||||
#include "mongo/idl/server_parameter_test_util.h"
|
||||
#include "mongo/logv2/log.h"
|
||||
#include "mongo/s/query/sharded_agg_test_fixture.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
|
||||
@ -45,7 +47,6 @@ const ShardId primaryShardId = ShardId("0");
|
||||
|
||||
TEST_F(ReshardingSplitPolicyTest, ShardKeyWithNonDottedFieldAndIdIsNotProjectedSucceeds) {
|
||||
auto shardKeyPattern = ShardKeyPattern(BSON("a" << 1));
|
||||
|
||||
auto pipeline =
|
||||
Pipeline::parse(ReshardingSplitPolicy::createRawPipeline(
|
||||
shardKeyPattern, 2 /* samplingRatio */, 1 /* numSplitPoints */),
|
||||
@ -53,7 +54,6 @@ TEST_F(ReshardingSplitPolicyTest, ShardKeyWithNonDottedFieldAndIdIsNotProjectedS
|
||||
auto mockSource =
|
||||
DocumentSourceMock::createForTest({"{_id: 10, a: 15}", "{_id: 3, a: 5}"}, expCtx());
|
||||
pipeline->addInitialSource(mockSource.get());
|
||||
|
||||
// We sample all of the documents since numSplitPoints(1) * samplingRatio (2) = 2 and the
|
||||
// document source has 2 chunks. So we can assert on the returned values.
|
||||
auto next = pipeline->getNext();
|
||||
@ -90,7 +90,6 @@ TEST_F(ReshardingSplitPolicyTest, ShardKeyWithIdFieldIsProjectedSucceeds) {
|
||||
TEST_F(ReshardingSplitPolicyTest, CompoundShardKeyWithNonDottedHashedFieldSucceeds) {
|
||||
auto shardKeyPattern = ShardKeyPattern(BSON("a" << 1 << "b"
|
||||
<< "hashed"));
|
||||
|
||||
auto pipeline =
|
||||
Pipeline::parse(ReshardingSplitPolicy::createRawPipeline(
|
||||
shardKeyPattern, 2 /* samplingRatio */, 1 /* numSplitPoints */),
|
||||
@ -98,7 +97,6 @@ TEST_F(ReshardingSplitPolicyTest, CompoundShardKeyWithNonDottedHashedFieldSuccee
|
||||
auto mockSource = DocumentSourceMock::createForTest(
|
||||
{"{x: 1, b: 16, a: 15}", "{x: 2, b: 123, a: 5}"}, expCtx());
|
||||
pipeline->addInitialSource(mockSource.get());
|
||||
|
||||
// We sample all of the documents since numSplitPoints(1) * samplingRatio (2) = 2 and the
|
||||
// document source has 2 chunks. So we can assert on the returned values.
|
||||
auto next = pipeline->getNext();
|
||||
@ -126,9 +124,9 @@ TEST_F(ReshardingSplitPolicyTest, CompoundShardKeyWithDottedFieldSucceeds) {
|
||||
// We sample all of the documents since numSplitPoints(1) * samplingRatio (2) = 2 and the
|
||||
// document source has 2 chunks. So we can assert on the returned values.
|
||||
auto next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("a" << BSON("b" << 10) << "c" << 5));
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("a.b" << 10 << "c" << 5));
|
||||
next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("a" << BSON("b" << 20) << "c" << 1));
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("a.b" << 20 << "c" << 1));
|
||||
ASSERT(!pipeline->getNext());
|
||||
}
|
||||
|
||||
@ -148,10 +146,10 @@ TEST_F(ReshardingSplitPolicyTest, CompoundShardKeyWithDottedHashedFieldSucceeds)
|
||||
// document source has 2 chunks. So we can assert on the returned values.
|
||||
auto next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(),
|
||||
BSON("a" << BSON("b" << 10 << "c" << -6548868637522515075LL) << "c" << 5));
|
||||
BSON("a.b" << 10 << "c" << 5 << "a.c" << -6548868637522515075LL));
|
||||
next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(),
|
||||
BSON("a" << BSON("b" << 20 << "c" << 2598032665634823220LL) << "c" << 1));
|
||||
BSON("a.b" << 20 << "c" << 1 << "a.c" << 2598032665634823220LL));
|
||||
ASSERT(!pipeline->getNext());
|
||||
}
|
||||
|
||||
@ -198,5 +196,78 @@ TEST_F(ReshardingSplitPolicyTest, SamplingSuceeds) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ReshardingSplitPolicyTest, ShardKeyWithDottedPathAndIdIsNotProjectedSucceeds) {
|
||||
auto shardKeyPattern = ShardKeyPattern(BSON("b" << 1));
|
||||
auto pipeline =
|
||||
Pipeline::parse(ReshardingSplitPolicy::createRawPipeline(
|
||||
shardKeyPattern, 2 /* samplingRatio */, 1 /* numSplitPoints */),
|
||||
expCtx());
|
||||
auto mockSource = DocumentSourceMock::createForTest(
|
||||
{"{_id: {a: 15}, b: 10}", "{_id: {a: 5}, b:1}"}, expCtx());
|
||||
pipeline->addInitialSource(mockSource.get());
|
||||
auto next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("b" << 1));
|
||||
next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("b" << 10));
|
||||
ASSERT(!pipeline->getNext());
|
||||
}
|
||||
|
||||
TEST_F(ReshardingSplitPolicyTest, CompoundShardKeyWithDottedPathAndIdIsProjectedSucceeds) {
|
||||
auto shardKeyPattern = ShardKeyPattern(BSON("_id.a" << 1 << "c" << 1));
|
||||
auto pipeline =
|
||||
Pipeline::parse(ReshardingSplitPolicy::createRawPipeline(
|
||||
shardKeyPattern, 2 /* samplingRatio */, 1 /* numSplitPoints */),
|
||||
expCtx());
|
||||
auto mockSource = DocumentSourceMock::createForTest(
|
||||
{"{_id: {a: 15}, c: 10}", "{_id: {a: 5}, c: 1}"}, expCtx());
|
||||
pipeline->addInitialSource(mockSource.get());
|
||||
auto next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("_id.a" << 5 << "c" << 1));
|
||||
next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(), BSON("_id.a" << 15 << "c" << 10));
|
||||
ASSERT(!pipeline->getNext());
|
||||
}
|
||||
|
||||
TEST_F(ReshardingSplitPolicyTest, CompoundShardKeyWithDottedHashedPathSucceeds) {
|
||||
auto shardKeyPattern = ShardKeyPattern(BSON("_id.a" << 1 << "b" << 1 << "_id.b"
|
||||
<< "hashed"));
|
||||
auto pipeline =
|
||||
Pipeline::parse(ReshardingSplitPolicy::createRawPipeline(
|
||||
shardKeyPattern, 2 /* samplingRatio */, 1 /* numSplitPoints */),
|
||||
expCtx());
|
||||
auto mockSource = DocumentSourceMock::createForTest(
|
||||
{"{x: 10, _id: {a: 20, b: 16}, b: 1}", "{x: 3, _id: {a: 10, b: 123}, b: 5}"}, expCtx());
|
||||
pipeline->addInitialSource(mockSource.get());
|
||||
|
||||
auto next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(),
|
||||
BSON("_id.a" << 10 << "b" << 5 << "_id.b" << -6548868637522515075LL));
|
||||
next = pipeline->getNext();
|
||||
ASSERT_BSONOBJ_EQ(next.value().toBson(),
|
||||
BSON("_id.a" << 20 << "b" << 1 << "_id.b" << 2598032665634823220LL));
|
||||
ASSERT(!pipeline->getNext());
|
||||
}
|
||||
|
||||
TEST_F(ReshardingSplitPolicyTest, ReshardingSucceedsWithLimitedMemoryForSortOperation) {
|
||||
RAIIServerParameterControllerForTest sortMaxMemory{
|
||||
"internalQueryMaxBlockingSortMemoryUsageBytes", 100};
|
||||
auto shardKeyPattern = ShardKeyPattern(BSON("a" << 1));
|
||||
const NamespaceString ns("reshard", "foo");
|
||||
auto pipelineDocSource =
|
||||
ReshardingSplitPolicy::makePipelineDocumentSource_forTest(operationContext(),
|
||||
kTestAggregateNss,
|
||||
shardKeyPattern,
|
||||
3 /*numInitialChunks*/,
|
||||
2 /*samplesPerChunk*/);
|
||||
auto mockSource = DocumentSourceMock::createForTest(
|
||||
{"{_id: 20, a: 4}", "{_id: 30, a: 3}", "{_id: 40, a: 2}", "{_id: 50, a: 1}"}, expCtx());
|
||||
pipelineDocSource->getPipeline_forTest()->addInitialSource(mockSource.get());
|
||||
auto next = pipelineDocSource->getNext();
|
||||
ASSERT_BSONOBJ_EQ(BSON("a" << 2), next.value());
|
||||
next = pipelineDocSource->getNext();
|
||||
ASSERT_BSONOBJ_EQ(BSON("a" << 4), next.value());
|
||||
ASSERT(!pipelineDocSource->getNext());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace mongo
|
||||
|
||||
@ -257,15 +257,11 @@ void QueryPlannerIXSelect::getFields(const MatchExpression* node,
|
||||
// Leaf nodes with a path and some array operators.
|
||||
if (Indexability::nodeCanUseIndexOnOwnField(node)) {
|
||||
out->insert(prefix + node->path().toString());
|
||||
} else if (Indexability::arrayUsesIndexOnChildren(node)) {
|
||||
} else if (Indexability::arrayUsesIndexOnChildren(node) && !node->path().empty()) {
|
||||
// If the array uses an index on its children, it's something like
|
||||
// {foo : {$elemMatch: {bar: 1}}}, in which case the predicate is really over foo.bar.
|
||||
//
|
||||
// When we have {foo: {$all: [{$elemMatch: {a: 1}}], the path of the embedded elemMatch
|
||||
// is empty. We don't want to append a dot in that case as the field would be foo..a.
|
||||
if (!node->path().empty()) {
|
||||
prefix += node->path().toString() + ".";
|
||||
}
|
||||
// Note we skip empty path components since they are not allowed in index key patterns.
|
||||
prefix += node->path().toString() + ".";
|
||||
|
||||
for (size_t i = 0; i < node->numChildren(); ++i) {
|
||||
getFields(node->getChild(i), prefix, out);
|
||||
@ -783,7 +779,8 @@ void QueryPlannerIXSelect::_rateIndices(MatchExpression* node,
|
||||
childRt->path = rt->path;
|
||||
node->getChild(0)->setTag(childRt);
|
||||
}
|
||||
} else if (Indexability::arrayUsesIndexOnChildren(node)) {
|
||||
} else if (Indexability::arrayUsesIndexOnChildren(node) && !node->path().empty()) {
|
||||
// Note we skip empty path components since they are not allowed in index key patterns.
|
||||
const auto newPath = prefix + node->path().toString();
|
||||
ElemMatchContext newContext;
|
||||
// Note this StringData is unowned and references the string declared on the stack here.
|
||||
@ -794,12 +791,7 @@ void QueryPlannerIXSelect::_rateIndices(MatchExpression* node,
|
||||
|
||||
// If the array uses an index on its children, it's something like
|
||||
// {foo: {$elemMatch: {bar: 1}}}, in which case the predicate is really over foo.bar.
|
||||
//
|
||||
// When we have {foo: {$all: [{$elemMatch: {a: 1}}], the path of the embedded elemMatch
|
||||
// is empty. We don't want to append a dot in that case as the field would be foo..a.
|
||||
if (!node->path().empty()) {
|
||||
prefix += node->path().toString() + ".";
|
||||
}
|
||||
prefix += node->path().toString() + ".";
|
||||
for (size_t i = 0; i < node->numChildren(); ++i) {
|
||||
_rateIndices(node->getChild(i), prefix, indices, collator, newContext);
|
||||
}
|
||||
|
||||
@ -2188,44 +2188,104 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
|
||||
|
||||
namespace {
|
||||
template <typename F>
|
||||
struct FieldPathVisitor : public SelectiveConstExpressionVisitorBase {
|
||||
struct FieldPathAndCondPreVisitor : public SelectiveConstExpressionVisitorBase {
|
||||
// To avoid overloaded-virtual warnings.
|
||||
using SelectiveConstExpressionVisitorBase::visit;
|
||||
|
||||
FieldPathVisitor(const F& fn) : _fn(fn) {}
|
||||
FieldPathAndCondPreVisitor(const F& fn, int32_t& nestedCondLevel)
|
||||
: _fn(fn), _nestedCondLevel(nestedCondLevel) {}
|
||||
|
||||
void visit(const ExpressionFieldPath* expr) final {
|
||||
_fn(expr);
|
||||
_fn(expr, _nestedCondLevel);
|
||||
}
|
||||
|
||||
void visit(const ExpressionCond* expr) final {
|
||||
++_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionSwitch* expr) final {
|
||||
++_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionIfNull* expr) final {
|
||||
++_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionAnd* expr) final {
|
||||
++_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionOr* expr) final {
|
||||
++_nestedCondLevel;
|
||||
}
|
||||
|
||||
F _fn;
|
||||
// Tracks the number of conditional expressions like $cond or $ifNull that are above us in the
|
||||
// tree.
|
||||
int32_t& _nestedCondLevel;
|
||||
};
|
||||
|
||||
struct CondPostVisitor : public SelectiveConstExpressionVisitorBase {
|
||||
// To avoid overloaded-virtual warnings.
|
||||
using SelectiveConstExpressionVisitorBase::visit;
|
||||
|
||||
CondPostVisitor(int32_t& nestedCondLevel) : _nestedCondLevel(nestedCondLevel) {}
|
||||
|
||||
void visit(const ExpressionCond* expr) final {
|
||||
--_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionSwitch* expr) final {
|
||||
--_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionIfNull* expr) final {
|
||||
--_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionAnd* expr) final {
|
||||
--_nestedCondLevel;
|
||||
}
|
||||
|
||||
void visit(const ExpressionOr* expr) final {
|
||||
--_nestedCondLevel;
|
||||
}
|
||||
|
||||
int32_t& _nestedCondLevel;
|
||||
};
|
||||
|
||||
/**
|
||||
* Walks through the 'expr' expression tree and whenever finds an 'ExpressionFieldPath', calls
|
||||
* the 'fn' function. Type requirement for 'fn' is it must have a const 'ExpressionFieldPath'
|
||||
* pointer parameter.
|
||||
* pointer parameter and 'nestedCondLevel' parameter.
|
||||
*/
|
||||
template <typename F>
|
||||
void walkAndActOnFieldPaths(Expression* expr, const F& fn) {
|
||||
FieldPathVisitor<F> visitor(fn);
|
||||
ExpressionWalker walker(&visitor, nullptr /*inVisitor*/, nullptr /*postVisitor*/);
|
||||
int32_t nestedCondLevel = 0;
|
||||
FieldPathAndCondPreVisitor<F> preVisitor(fn, nestedCondLevel);
|
||||
CondPostVisitor postVisitor(nestedCondLevel);
|
||||
ExpressionWalker walker(&preVisitor, nullptr /*inVisitor*/, &postVisitor);
|
||||
expression_walker::walk(expr, &walker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether all field paths in 'idExpr' and all accumulator expressions are top-level ones.
|
||||
*/
|
||||
bool checkAllFieldPathsAreTopLevel(const boost::intrusive_ptr<Expression>& idExpr,
|
||||
const std::vector<AccumulationStatement>& accStmts) {
|
||||
auto areAllTopLevelFields = true;
|
||||
bool areAllFieldPathsOptimizable(const boost::intrusive_ptr<Expression>& idExpr,
|
||||
const std::vector<AccumulationStatement>& accStmts) {
|
||||
auto areFieldPathsOptimizable = true;
|
||||
|
||||
auto checkFieldPath = [&](const ExpressionFieldPath* fieldExpr) {
|
||||
auto checkFieldPath = [&](const ExpressionFieldPath* fieldExpr, int32_t nestedCondLevel) {
|
||||
// We optimize neither a field path for the top-level document itself (getPathLength() == 1)
|
||||
// nor a field path that refers to a variable. We can optimize only top-level fields
|
||||
// (getPathLength() == 2).
|
||||
if (fieldExpr->getFieldPath().getPathLength() != 2 || fieldExpr->isVariableReference()) {
|
||||
areAllTopLevelFields = false;
|
||||
//
|
||||
// The 'nestedCondLevel' being > 0 means that a field path is refered to below conditional
|
||||
// expressions at the parent $group node, when we cannot optimize field path access and
|
||||
// therefore, cannot avoid materialization.
|
||||
if (nestedCondLevel > 0 || fieldExpr->getFieldPath().getPathLength() != 2 ||
|
||||
fieldExpr->isVariableReference()) {
|
||||
areFieldPathsOptimizable = false;
|
||||
return;
|
||||
}
|
||||
};
|
||||
@ -2238,7 +2298,7 @@ bool checkAllFieldPathsAreTopLevel(const boost::intrusive_ptr<Expression>& idExp
|
||||
walkAndActOnFieldPaths(accStmt.expr.argument.get(), checkFieldPath);
|
||||
}
|
||||
|
||||
return areAllTopLevelFields;
|
||||
return areFieldPathsOptimizable;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2264,7 +2324,7 @@ EvalStage optimizeFieldPaths(StageBuilderState& state,
|
||||
auto searchInChildOutputs = !optionalRootSlot.has_value();
|
||||
auto retEvalStage = std::move(childEvalStage);
|
||||
|
||||
walkAndActOnFieldPaths(expr.get(), [&](const ExpressionFieldPath* fieldExpr) {
|
||||
walkAndActOnFieldPaths(expr.get(), [&](const ExpressionFieldPath* fieldExpr, int32_t) {
|
||||
// We optimize neither a field path for the top-level document itself nor a field path that
|
||||
// refers to a variable instead of calling getField().
|
||||
if (fieldExpr->getFieldPath().getPathLength() == 1 || fieldExpr->isVariableReference()) {
|
||||
@ -2558,10 +2618,8 @@ std::pair<std::unique_ptr<sbe::PlanStage>, PlanStageSlots> SlotBasedStageBuilder
|
||||
const auto& accStmts = groupNode->accumulators;
|
||||
auto childStageType = childNode->getType();
|
||||
|
||||
auto areAllTopLevelFields = checkAllFieldPathsAreTopLevel(idExpr, accStmts);
|
||||
|
||||
auto childReqs = reqs.copy();
|
||||
if (childStageType == StageType::STAGE_GROUP && areAllTopLevelFields) {
|
||||
if (childStageType == StageType::STAGE_GROUP && areAllFieldPathsOptimizable(idExpr, accStmts)) {
|
||||
// Does not ask the GROUP child for the result slot to avoid unnecessary materialization if
|
||||
// all fields are top-level fields. See the end of this function. For example, GROUP - GROUP
|
||||
// - COLLSCAN case.
|
||||
|
||||
@ -64,9 +64,11 @@ bool isQuerySbeCompatible(const CollectionPtr* collection,
|
||||
|
||||
const bool doesNotHaveElemMatchProject = !cq->getProj() || !cq->getProj()->containsElemMatch();
|
||||
|
||||
const bool isNotInnerSideOfLookup = !(expCtx && expCtx->inLookup);
|
||||
|
||||
return allExpressionsSupported && isNotCount && doesNotContainMetadataRequirements &&
|
||||
isQueryNotAgainstTimeseriesCollection && isQueryNotAgainstClusteredCollection &&
|
||||
doesNotSortOnMetaOrPathWithNumericComponents && isNotOplog && doesNotRequireMatchDetails &&
|
||||
doesNotHaveElemMatchProject && isNotChangeCollection;
|
||||
doesNotHaveElemMatchProject && isNotChangeCollection && isNotInnerSideOfLookup;
|
||||
}
|
||||
} // namespace mongo::sbe
|
||||
|
||||
@ -1397,53 +1397,63 @@ void InitialSyncer::_lastOplogEntryFetcherCallbackForStopTimestamp(
|
||||
std::shared_ptr<OnCompletionGuard> onCompletionGuard) {
|
||||
OpTimeAndWallTime resultOpTimeAndWallTime = {OpTime(), Date_t()};
|
||||
{
|
||||
stdx::lock_guard<Latch> lock(_mutex);
|
||||
auto status = _checkForShutdownAndConvertStatus_inlock(
|
||||
result.getStatus(), "error fetching last oplog entry for stop timestamp");
|
||||
if (_shouldRetryError(lock, status)) {
|
||||
auto scheduleStatus =
|
||||
(*_attemptExec)
|
||||
->scheduleWork([this,
|
||||
onCompletionGuard](executor::TaskExecutor::CallbackArgs args) {
|
||||
// It is not valid to schedule the retry from within this callback,
|
||||
// hence we schedule a lambda to schedule the retry.
|
||||
stdx::lock_guard<Latch> lock(_mutex);
|
||||
// Since the stopTimestamp is retrieved after we have done all the work of
|
||||
// retrieving collection data, we handle retries within this class by
|
||||
// retrying for 'initialSyncTransientErrorRetryPeriodSeconds' (default 24
|
||||
// hours). This is the same retry strategy used when retrieving collection
|
||||
// data, and avoids retrieving all the data and then throwing it away due to
|
||||
// a transient network outage.
|
||||
auto status = _scheduleLastOplogEntryFetcher_inlock(
|
||||
[=](const StatusWith<mongo::Fetcher::QueryResponse>& status,
|
||||
mongo::Fetcher::NextAction*,
|
||||
mongo::BSONObjBuilder*) {
|
||||
_lastOplogEntryFetcherCallbackForStopTimestamp(status,
|
||||
onCompletionGuard);
|
||||
},
|
||||
kInitialSyncerHandlesRetries);
|
||||
if (!status.isOK()) {
|
||||
onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock, status);
|
||||
}
|
||||
});
|
||||
if (scheduleStatus.isOK())
|
||||
{
|
||||
stdx::lock_guard<Latch> lock(_mutex);
|
||||
auto status = _checkForShutdownAndConvertStatus_inlock(
|
||||
result.getStatus(), "error fetching last oplog entry for stop timestamp");
|
||||
if (_shouldRetryError(lock, status)) {
|
||||
auto scheduleStatus =
|
||||
(*_attemptExec)
|
||||
->scheduleWork(
|
||||
[this, onCompletionGuard](executor::TaskExecutor::CallbackArgs args) {
|
||||
// It is not valid to schedule the retry from within this callback,
|
||||
// hence we schedule a lambda to schedule the retry.
|
||||
stdx::lock_guard<Latch> lock(_mutex);
|
||||
// Since the stopTimestamp is retrieved after we have done all the
|
||||
// work of retrieving collection data, we handle retries within this
|
||||
// class by retrying for
|
||||
// 'initialSyncTransientErrorRetryPeriodSeconds' (default 24 hours).
|
||||
// This is the same retry strategy used when retrieving collection
|
||||
// data, and avoids retrieving all the data and then throwing it
|
||||
// away due to a transient network outage.
|
||||
auto status = _scheduleLastOplogEntryFetcher_inlock(
|
||||
[=](const StatusWith<mongo::Fetcher::QueryResponse>& status,
|
||||
mongo::Fetcher::NextAction*,
|
||||
mongo::BSONObjBuilder*) {
|
||||
_lastOplogEntryFetcherCallbackForStopTimestamp(
|
||||
status, onCompletionGuard);
|
||||
},
|
||||
kInitialSyncerHandlesRetries);
|
||||
if (!status.isOK()) {
|
||||
onCompletionGuard->setResultAndCancelRemainingWork_inlock(
|
||||
lock, status);
|
||||
}
|
||||
});
|
||||
if (scheduleStatus.isOK())
|
||||
return;
|
||||
// If scheduling failed, we're shutting down and cannot retry.
|
||||
// So just continue with the original failed status.
|
||||
}
|
||||
if (!status.isOK()) {
|
||||
onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock, status);
|
||||
return;
|
||||
// If scheduling failed, we're shutting down and cannot retry.
|
||||
// So just continue with the original failed status.
|
||||
}
|
||||
if (!status.isOK()) {
|
||||
onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock, status);
|
||||
return;
|
||||
}
|
||||
|
||||
auto&& optimeStatus = parseOpTimeAndWallTime(result);
|
||||
if (!optimeStatus.isOK()) {
|
||||
onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock,
|
||||
optimeStatus.getStatus());
|
||||
return;
|
||||
}
|
||||
resultOpTimeAndWallTime = optimeStatus.getValue();
|
||||
}
|
||||
|
||||
auto&& optimeStatus = parseOpTimeAndWallTime(result);
|
||||
if (!optimeStatus.isOK()) {
|
||||
onCompletionGuard->setResultAndCancelRemainingWork_inlock(lock,
|
||||
optimeStatus.getStatus());
|
||||
return;
|
||||
}
|
||||
resultOpTimeAndWallTime = optimeStatus.getValue();
|
||||
// Release the _mutex to write to disk.
|
||||
auto opCtx = makeOpCtx();
|
||||
_replicationProcess->getConsistencyMarkers()->setMinValid(
|
||||
opCtx.get(), resultOpTimeAndWallTime.opTime, true);
|
||||
|
||||
stdx::lock_guard<Latch> lock(_mutex);
|
||||
_initialSyncState->stopTimestamp = resultOpTimeAndWallTime.opTime.getTimestamp();
|
||||
|
||||
// If the beginFetchingTimestamp is different from the stopTimestamp, it indicates that
|
||||
|
||||
@ -110,12 +110,6 @@ ScopedCollectionFilter CollectionShardingRuntime::getOwnershipFilter(
|
||||
// No operations should be calling getOwnershipFilter without a shard version
|
||||
invariant(optReceivedShardVersion,
|
||||
"getOwnershipFilter called by operation that doesn't specify shard version");
|
||||
uassert(6279300,
|
||||
"Request was received without an attached index version. This could indicate that "
|
||||
"this request was sent by a router of an older version",
|
||||
!feature_flags::gGlobalIndexesShardingCatalog.isEnabled(
|
||||
serverGlobalParams.featureCompatibility) ||
|
||||
optReceivedShardVersion->indexVersion());
|
||||
}
|
||||
|
||||
auto metadata =
|
||||
@ -148,12 +142,6 @@ ScopedCollectionDescription CollectionShardingRuntime::getCollectionDescription(
|
||||
|
||||
auto optMetadata = _getCurrentMetadataIfKnown(boost::none);
|
||||
const auto receivedShardVersion{oss.getShardVersion(_nss)};
|
||||
uassert(6279301,
|
||||
"Request was received without an attached index version. This could indicate that this "
|
||||
"request was sent by a router of an older version",
|
||||
!feature_flags::gGlobalIndexesShardingCatalog.isEnabled(
|
||||
serverGlobalParams.featureCompatibility) ||
|
||||
!receivedShardVersion || receivedShardVersion->indexVersion());
|
||||
uassert(
|
||||
StaleConfigInfo(_nss,
|
||||
receivedShardVersion ? (ChunkVersion)*receivedShardVersion
|
||||
@ -368,13 +356,6 @@ CollectionShardingRuntime::_getMetadataWithVersionCheckAt(
|
||||
// Assume that the received shard version was IGNORED if the current operation wasn't versioned
|
||||
const auto& receivedShardVersion =
|
||||
optReceivedShardVersion ? (ChunkVersion)*optReceivedShardVersion : ChunkVersion::IGNORED();
|
||||
uassert(6279302,
|
||||
"Request was received without an attached index version. This could indicate that this "
|
||||
"request was sent by a router of an older version",
|
||||
!feature_flags::gGlobalIndexesShardingCatalog.isEnabled(
|
||||
serverGlobalParams.featureCompatibility) ||
|
||||
receivedShardVersion == ChunkVersion::IGNORED() ||
|
||||
optReceivedShardVersion->indexVersion());
|
||||
|
||||
auto csrLock = CSRLock::lockShared(opCtx, this);
|
||||
|
||||
|
||||
@ -33,12 +33,14 @@
|
||||
#include "mongo/db/bson/dotted_path_support.h"
|
||||
#include "mongo/db/catalog/collection_catalog.h"
|
||||
#include "mongo/db/curop.h"
|
||||
#include "mongo/db/pipeline/document_source.h"
|
||||
#include "mongo/db/pipeline/lite_parsed_pipeline.h"
|
||||
#include "mongo/db/pipeline/process_interface/shardsvr_process_interface.h"
|
||||
#include "mongo/db/pipeline/sharded_agg_helpers.h"
|
||||
#include "mongo/db/s/balancer/balancer_policy.h"
|
||||
#include "mongo/db/s/sharding_state.h"
|
||||
#include "mongo/db/vector_clock.h"
|
||||
#include "mongo/logv2/log.h"
|
||||
#include "mongo/s/balancer_configuration.h"
|
||||
#include "mongo/s/catalog/type_shard.h"
|
||||
#include "mongo/s/grid.h"
|
||||
@ -52,6 +54,7 @@ namespace {
|
||||
|
||||
using ChunkDistributionMap = stdx::unordered_map<ShardId, size_t>;
|
||||
using ZoneShardMap = StringMap<std::vector<ShardId>>;
|
||||
using boost::intrusive_ptr;
|
||||
|
||||
std::vector<ShardId> getAllShardIdsSorted(OperationContext* opCtx) {
|
||||
// Many tests assume that chunks will be placed on shards
|
||||
@ -636,34 +639,29 @@ std::vector<BSONObj> ReshardingSplitPolicy::createRawPipeline(const ShardKeyPatt
|
||||
|
||||
std::vector<BSONObj> res;
|
||||
const auto& shardKeyFields = shardKey.getKeyPatternFields();
|
||||
|
||||
BSONObjBuilder projectValBuilder;
|
||||
BSONObjBuilder sortValBuilder;
|
||||
|
||||
using Doc = Document;
|
||||
using Arr = std::vector<Value>;
|
||||
using V = Value;
|
||||
Arr arrayToObjectBuilder;
|
||||
for (auto&& fieldRef : shardKeyFields) {
|
||||
// If the shard key includes a hashed field and current fieldRef is the hashed field.
|
||||
if (shardKey.isHashedPattern() &&
|
||||
fieldRef->dottedField().compare(shardKey.getHashedField().fieldNameStringData()) == 0) {
|
||||
projectValBuilder.append(fieldRef->dottedField(),
|
||||
BSON("$toHashedIndexKey"
|
||||
<< "$" + fieldRef->dottedField()));
|
||||
arrayToObjectBuilder.emplace_back(
|
||||
Doc{{"k", V{fieldRef->dottedField()}},
|
||||
{"v", Doc{{"$toHashedIndexKey", V{"$" + fieldRef->dottedField()}}}}});
|
||||
} else {
|
||||
projectValBuilder.append(
|
||||
str::stream() << fieldRef->dottedField(),
|
||||
BSON("$ifNull" << BSON_ARRAY("$" + fieldRef->dottedField() << BSONNULL)));
|
||||
arrayToObjectBuilder.emplace_back(Doc{
|
||||
{"k", V{fieldRef->dottedField()}},
|
||||
{"v", Doc{{"$ifNull", V{Arr{V{"$" + fieldRef->dottedField()}, V{BSONNULL}}}}}}});
|
||||
}
|
||||
|
||||
sortValBuilder.append(fieldRef->dottedField().toString(), 1);
|
||||
}
|
||||
|
||||
// Do not project _id if it's not part of the shard key.
|
||||
if (!shardKey.hasId()) {
|
||||
projectValBuilder.append("_id", 0);
|
||||
}
|
||||
|
||||
res.push_back(BSON("$sample" << BSON("size" << numSplitPoints * samplesPerChunk)));
|
||||
res.push_back(BSON("$project" << projectValBuilder.obj()));
|
||||
res.push_back(BSON("$sort" << sortValBuilder.obj()));
|
||||
res.push_back(
|
||||
Doc{{"$replaceWith", Doc{{"$arrayToObject", Arr{V{arrayToObjectBuilder}}}}}}.toBson());
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -786,26 +784,34 @@ void ReshardingSplitPolicy::_appendSplitPointsFromSample(BSONObjSet* splitPoints
|
||||
|
||||
while (nextKey && nRemaining > 0) {
|
||||
// if key is hashed, nextKey values are already hashed
|
||||
auto result = splitPoints->insert(
|
||||
dotted_path_support::extractElementsBasedOnTemplate(*nextKey, shardKey.toBSON())
|
||||
.getOwned());
|
||||
|
||||
auto result = splitPoints->insert(nextKey->getOwned());
|
||||
if (result.second) {
|
||||
nRemaining--;
|
||||
}
|
||||
|
||||
nextKey = _samples->getNext();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ReshardingSplitPolicy::SampleDocumentSource>
|
||||
ReshardingSplitPolicy::makePipelineDocumentSource_forTest(OperationContext* opCtx,
|
||||
const NamespaceString& ns,
|
||||
const ShardKeyPattern& shardKey,
|
||||
int numInitialChunks,
|
||||
int samplesPerChunk) {
|
||||
MakePipelineOptions opts;
|
||||
opts.attachCursorSource = false;
|
||||
return _makePipelineDocumentSource(
|
||||
opCtx, ns, shardKey, numInitialChunks, samplesPerChunk, std::move(opts));
|
||||
}
|
||||
|
||||
std::unique_ptr<ReshardingSplitPolicy::SampleDocumentSource>
|
||||
ReshardingSplitPolicy::_makePipelineDocumentSource(OperationContext* opCtx,
|
||||
const NamespaceString& ns,
|
||||
const ShardKeyPattern& shardKey,
|
||||
int numInitialChunks,
|
||||
int samplesPerChunk) {
|
||||
int samplesPerChunk,
|
||||
MakePipelineOptions opts) {
|
||||
auto rawPipeline = createRawPipeline(shardKey, numInitialChunks - 1, samplesPerChunk);
|
||||
|
||||
StringMap<ExpressionContext::ResolvedNamespace> resolvedNamespaces;
|
||||
resolvedNamespaces[ns.coll()] = {ns, std::vector<BSONObj>{}};
|
||||
|
||||
@ -819,7 +825,7 @@ ReshardingSplitPolicy::_makePipelineDocumentSource(OperationContext* opCtx,
|
||||
boost::none, /* explain */
|
||||
false, /* fromMongos */
|
||||
false, /* needsMerge */
|
||||
false, /* allowDiskUse */
|
||||
true, /* allowDiskUse */
|
||||
true, /* bypassDocumentValidation */
|
||||
false, /* isMapReduceCommand */
|
||||
ns,
|
||||
@ -829,8 +835,10 @@ ReshardingSplitPolicy::_makePipelineDocumentSource(OperationContext* opCtx,
|
||||
std::move(resolvedNamespaces),
|
||||
boost::none); /* collUUID */
|
||||
|
||||
return std::make_unique<PipelineDocumentSource>(Pipeline::makePipeline(rawPipeline, expCtx, {}),
|
||||
samplesPerChunk - 1);
|
||||
expCtx->tempDir = storageGlobalParams.dbpath + "/tmp";
|
||||
|
||||
return std::make_unique<PipelineDocumentSource>(
|
||||
Pipeline::makePipeline(rawPipeline, expCtx, opts), samplesPerChunk - 1);
|
||||
}
|
||||
|
||||
ReshardingSplitPolicy::PipelineDocumentSource::PipelineDocumentSource(
|
||||
|
||||
@ -40,7 +40,6 @@
|
||||
#include "mongo/s/catalog/type_tags.h"
|
||||
#include "mongo/s/shard_key_pattern.h"
|
||||
#include "mongo/util/string_map.h"
|
||||
|
||||
namespace mongo {
|
||||
|
||||
struct SplitPolicyParams {
|
||||
@ -291,6 +290,7 @@ public:
|
||||
public:
|
||||
virtual ~SampleDocumentSource(){};
|
||||
virtual boost::optional<BSONObj> getNext() = 0;
|
||||
virtual Pipeline* getPipeline_forTest() = 0;
|
||||
};
|
||||
|
||||
// Provides documents from a real Pipeline
|
||||
@ -299,6 +299,9 @@ public:
|
||||
PipelineDocumentSource() = delete;
|
||||
PipelineDocumentSource(SampleDocumentPipeline pipeline, int skip);
|
||||
boost::optional<BSONObj> getNext() override;
|
||||
Pipeline* getPipeline_forTest() override {
|
||||
return _pipeline.get();
|
||||
}
|
||||
|
||||
private:
|
||||
SampleDocumentPipeline _pipeline;
|
||||
@ -334,13 +337,21 @@ public:
|
||||
|
||||
static constexpr int kDefaultSamplesPerChunk = 10;
|
||||
|
||||
static std::unique_ptr<SampleDocumentSource> makePipelineDocumentSource_forTest(
|
||||
OperationContext* opCtx,
|
||||
const NamespaceString& ns,
|
||||
const ShardKeyPattern& shardKey,
|
||||
int numInitialChunks,
|
||||
int samplesPerChunk);
|
||||
|
||||
private:
|
||||
static std::unique_ptr<SampleDocumentSource> _makePipelineDocumentSource(
|
||||
OperationContext* opCtx,
|
||||
const NamespaceString& ns,
|
||||
const ShardKeyPattern& shardKey,
|
||||
int numInitialChunks,
|
||||
int samplesPerChunk);
|
||||
int samplesPerChunk,
|
||||
MakePipelineOptions opts = {});
|
||||
|
||||
/**
|
||||
* Returns a set of split points to ensure that chunk boundaries will align with the zone
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "mongo/db/s/config/config_server_test_fixture.h"
|
||||
#include "mongo/db/s/config/initial_split_policy.h"
|
||||
#include "mongo/db/vector_clock.h"
|
||||
#include "mongo/logv2/log.h"
|
||||
#include "mongo/s/catalog/type_shard.h"
|
||||
#include "mongo/s/catalog/type_tags.h"
|
||||
#include "mongo/unittest/unittest.h"
|
||||
@ -1725,6 +1726,10 @@ public:
|
||||
return next;
|
||||
}
|
||||
|
||||
Pipeline* getPipeline_forTest() override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<BSONObj> _toReturn;
|
||||
};
|
||||
@ -1763,9 +1768,9 @@ TEST_F(ReshardingInitSplitTest, NoZones) {
|
||||
shardRegistry()->reload(operationContext());
|
||||
|
||||
std::list<BSONObj> mockSamples;
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 10));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 20));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 30));
|
||||
mockSamples.push_back(BSON("y" << 10));
|
||||
mockSamples.push_back(BSON("y" << 20));
|
||||
mockSamples.push_back(BSON("y" << 30));
|
||||
|
||||
auto mockSampleSource = std::make_unique<MockPipelineSource>(std::move(mockSamples));
|
||||
|
||||
@ -1800,9 +1805,9 @@ TEST_F(ReshardingInitSplitTest, HashedShardKey) {
|
||||
shardRegistry()->reload(operationContext());
|
||||
|
||||
std::list<BSONObj> mockSamples;
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 7766103514953448109LL));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << -9117533237618642180LL));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << -1196399207910989725LL));
|
||||
mockSamples.push_back(BSON("y" << 7766103514953448109LL));
|
||||
mockSamples.push_back(BSON("y" << -9117533237618642180LL));
|
||||
mockSamples.push_back(BSON("y" << -1196399207910989725LL));
|
||||
|
||||
auto mockSampleSource = std::make_unique<MockPipelineSource>(std::move(mockSamples));
|
||||
|
||||
@ -1865,9 +1870,9 @@ TEST_F(ReshardingInitSplitTest, ZonesCoversEntireDomainButInsufficient) {
|
||||
shardRegistry()->reload(operationContext());
|
||||
|
||||
std::list<BSONObj> mockSamples;
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 10));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 20));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 30));
|
||||
mockSamples.push_back(BSON("y" << 10));
|
||||
mockSamples.push_back(BSON("y" << 20));
|
||||
mockSamples.push_back(BSON("y" << 30));
|
||||
|
||||
auto mockSampleSource = std::make_unique<MockPipelineSource>(std::move(mockSamples));
|
||||
|
||||
@ -1905,9 +1910,9 @@ TEST_F(ReshardingInitSplitTest, SamplesCoincidingWithZones) {
|
||||
shardRegistry()->reload(operationContext());
|
||||
|
||||
std::list<BSONObj> mockSamples;
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 10));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 20));
|
||||
mockSamples.push_back(BSON("x" << 10 << "y" << 30));
|
||||
mockSamples.push_back(BSON("y" << 10));
|
||||
mockSamples.push_back(BSON("y" << 20));
|
||||
mockSamples.push_back(BSON("y" << 30));
|
||||
|
||||
auto mockSampleSource = std::make_unique<MockPipelineSource>(std::move(mockSamples));
|
||||
|
||||
|
||||
@ -229,8 +229,9 @@ GlobalIndexCumulativeMetrics* GlobalIndexMetrics::getGlobalIndexCumulativeMetric
|
||||
}
|
||||
|
||||
|
||||
Milliseconds GlobalIndexMetrics::getRecipientHighEstimateRemainingTimeMillis() const {
|
||||
return Milliseconds{0};
|
||||
boost::optional<Milliseconds> GlobalIndexMetrics::getRecipientHighEstimateRemainingTimeMillis()
|
||||
const {
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
@ -212,7 +212,6 @@ public:
|
||||
bool unique,
|
||||
ServiceContext* serviceContext);
|
||||
|
||||
Milliseconds getRecipientHighEstimateRemainingTimeMillis() const;
|
||||
template <typename T>
|
||||
static auto initializeFrom(const T& document, ServiceContext* serviceContext) {
|
||||
static_assert(isStateDocument<T>);
|
||||
@ -240,6 +239,9 @@ public:
|
||||
_stateHolder.onStateTransition(before, after);
|
||||
}
|
||||
|
||||
protected:
|
||||
boost::optional<Milliseconds> getRecipientHighEstimateRemainingTimeMillis() const override;
|
||||
|
||||
private:
|
||||
GlobalIndexCumulativeMetrics* getGlobalIndexCumulativeMetrics();
|
||||
std::string createOperationDescription() const noexcept override;
|
||||
|
||||
@ -167,12 +167,20 @@ CoordinatorCommitMonitor::queryRemainingOperationTimeForRecipients() const {
|
||||
uassertStatusOKWithContext(status, errorContext);
|
||||
|
||||
const auto remainingTime = extractOperationRemainingTime(shardResponse.data);
|
||||
// A recipient shard does not report the remaining operation time when there is no data
|
||||
// to copy and no oplog entry to apply.
|
||||
if (remainingTime && remainingTime.value() < minRemainingTime) {
|
||||
|
||||
// If any recipient omits the "remainingMillis" field of the response then
|
||||
// we cannot conclude that it is safe to begin the critical section.
|
||||
// It is possible that the recipient just had a failover and
|
||||
// was not able to restore its metrics before it replied to the
|
||||
// _shardsvrReshardingOperationTime command.
|
||||
if (!remainingTime) {
|
||||
maxRemainingTime = Milliseconds::max();
|
||||
continue;
|
||||
}
|
||||
if (remainingTime.value() < minRemainingTime) {
|
||||
minRemainingTime = remainingTime.value();
|
||||
}
|
||||
if (remainingTime && remainingTime.value() > maxRemainingTime) {
|
||||
if (remainingTime.value() > maxRemainingTime) {
|
||||
maxRemainingTime = remainingTime.value();
|
||||
}
|
||||
}
|
||||
@ -206,11 +214,19 @@ ExecutorFuture<void> CoordinatorCommitMonitor::_makeFuture() const {
|
||||
"Encountered an error while querying recipients, will retry shortly",
|
||||
"error"_attr = status);
|
||||
|
||||
return RemainingOperationTimes{Milliseconds(0), Milliseconds::max()};
|
||||
// On error we definitely cannot begin the critical section. Therefore,
|
||||
// return Milliseconds::max for remainingTimes.max (remainingTimes.max is used
|
||||
// for determining whether the critical section should begin).
|
||||
return RemainingOperationTimes{Milliseconds(-1), Milliseconds::max()};
|
||||
})
|
||||
.then([this, anchor = shared_from_this()](RemainingOperationTimes remainingTimes) {
|
||||
_metrics->setCoordinatorHighEstimateRemainingTimeMillis(remainingTimes.max);
|
||||
_metrics->setCoordinatorLowEstimateRemainingTimeMillis(remainingTimes.min);
|
||||
// If remainingTimes.max (or remainingTimes.min) is Milliseconds::max, then use -1 so
|
||||
// that the scale of the y-axis is still useful when looking at FTDC metrics.
|
||||
auto clampIfMax = [](Milliseconds t) {
|
||||
return t != Milliseconds::max() ? t : Milliseconds(-1);
|
||||
};
|
||||
_metrics->setCoordinatorHighEstimateRemainingTimeMillis(clampIfMax(remainingTimes.max));
|
||||
_metrics->setCoordinatorLowEstimateRemainingTimeMillis(clampIfMax(remainingTimes.min));
|
||||
|
||||
// Check if all recipient shards are within the commit threshold.
|
||||
if (remainingTimes.max <= _threshold)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user