Compare commits
263 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b326ba837c | ||
|
|
726c889abf | ||
|
|
6e71d0d568 | ||
|
|
3a2816a72a | ||
|
|
aaa36d2ce7 | ||
|
|
ef07be6657 | ||
|
|
abe971536f | ||
|
|
cbf7d52a1d | ||
|
|
3e884ea31a | ||
|
|
bd9b764032 | ||
|
|
091f101deb | ||
|
|
44643e2666 | ||
|
|
94fc167ce1 | ||
|
|
ec81d59468 | ||
|
|
9cf9323912 | ||
|
|
93934f75b6 | ||
|
|
c91b1ea56a | ||
|
|
3d23661171 | ||
|
|
44f0661a7a | ||
|
|
43fe882029 | ||
|
|
9e55fe2085 | ||
|
|
dd1d0205b7 | ||
|
|
b7bb72efb1 | ||
|
|
ce2fa6d522 | ||
|
|
9802549c17 | ||
|
|
95ed4c1647 | ||
|
|
159b239d31 | ||
|
|
581abf6060 | ||
|
|
e6cf3f0a23 | ||
|
|
6d38a1f048 | ||
|
|
c5932d8130 | ||
|
|
5999d33d59 | ||
|
|
fa6424b932 | ||
|
|
499460c6e7 | ||
|
|
632bf23b96 | ||
|
|
453ed8aee3 | ||
|
|
452290d057 | ||
|
|
0f10a675ed | ||
|
|
5d6532f3d5 | ||
|
|
569041f7df | ||
|
|
8f4c9f4f69 | ||
|
|
d04d195125 | ||
|
|
f6fea15f85 | ||
|
|
cfd2f027d9 | ||
|
|
452c6c7ec2 | ||
|
|
c96e8cacf3 | ||
|
|
5ca8d6e282 | ||
|
|
9287affdb7 | ||
|
|
e9e372e1e2 | ||
|
|
e2835f1cbf | ||
|
|
15e8b49669 | ||
|
|
9744b58a00 | ||
|
|
70d53cfcb4 | ||
|
|
dd6350f7de | ||
|
|
97c4028c0f | ||
|
|
bcfb7a02bb | ||
|
|
b5e9e4ab04 | ||
|
|
a8cd82b2b8 | ||
|
|
da0d0762fc | ||
|
|
d6c68c80f4 | ||
|
|
43482c1e7a | ||
|
|
5d2d08fa53 | ||
|
|
7c47096da5 | ||
|
|
4a3c6e6288 | ||
|
|
7bb22cd3d5 | ||
|
|
c38252663a | ||
|
|
6a2aecae53 | ||
|
|
69456083e2 | ||
|
|
30b0de8351 | ||
|
|
f24d9df653 | ||
|
|
96cef1d7db | ||
|
|
fbab3da6d5 | ||
|
|
8a2368295c | ||
|
|
d96a296a65 | ||
|
|
fe64deb8f6 | ||
|
|
187028f283 | ||
|
|
a06749d784 | ||
|
|
3017515631 | ||
|
|
3400f82a11 | ||
|
|
59218cfc3c | ||
|
|
c7c5010151 | ||
|
|
040efca71b | ||
|
|
f0df9f46e6 | ||
|
|
ee444290d1 | ||
|
|
04a340e44a | ||
|
|
c6a9842123 | ||
|
|
f777c19331 | ||
|
|
556d071e9a | ||
|
|
3a1518befd | ||
|
|
5678318b99 | ||
|
|
efaa2ca55e | ||
|
|
2953b302d6 | ||
|
|
05a115fe4e | ||
|
|
d74c7ae40f | ||
|
|
927caf6e06 | ||
|
|
48a2945023 | ||
|
|
d1f62c6fbc | ||
|
|
cc4fd27db3 | ||
|
|
a443af8bd6 | ||
|
|
7779d79933 | ||
|
|
b1d4e49c38 | ||
|
|
28042f943a | ||
|
|
abbd286fba | ||
|
|
0158a5b221 | ||
|
|
59db3be441 | ||
|
|
cbc01eb39f | ||
|
|
9ab7297ce1 | ||
|
|
000cc60418 | ||
|
|
302072ddf3 | ||
|
|
0a2ffbf42f | ||
|
|
30f331f0e6 | ||
|
|
ecfe266a47 | ||
|
|
4a9123577a | ||
|
|
6c11f9de76 | ||
|
|
50dc132dbf | ||
|
|
ce0f41c5ac | ||
|
|
8652b39ffa | ||
|
|
d7ef8fa46e | ||
|
|
38196fe484 | ||
|
|
b2820de69b | ||
|
|
cf7061c06f | ||
|
|
a14910b0f2 | ||
|
|
2845b0d56d | ||
|
|
ee3f14ec31 | ||
|
|
fb79259aa5 | ||
|
|
8e63ae06e2 | ||
|
|
c49d0b2c99 | ||
|
|
d9e55e9897 | ||
|
|
90930e093a | ||
|
|
21a507148d | ||
|
|
0da900911b | ||
|
|
f279736f5a | ||
|
|
421c9d5abd | ||
|
|
6fc3b8d0c6 | ||
|
|
f8940e9fcc | ||
|
|
89b08ef601 | ||
|
|
229c741bee | ||
|
|
2f08ba3bcf | ||
|
|
706047320c | ||
|
|
6a85c05a7d | ||
|
|
9b551dcf79 | ||
|
|
cc111ed0c6 | ||
|
|
55714993ce | ||
|
|
96b613d7fb | ||
|
|
2b25f8b67c | ||
|
|
faf8917066 | ||
|
|
d3821c9162 | ||
|
|
f9e18c53d0 | ||
|
|
c210b9c6da | ||
|
|
54a7b59d3e | ||
|
|
212c55a986 | ||
|
|
8764f93fd1 | ||
|
|
9a4b9e1c43 | ||
|
|
8ecaad20df | ||
|
|
9a3b0ad675 | ||
|
|
c5d5cd03e0 | ||
|
|
84daeccc1e | ||
|
|
bd300f3fce | ||
|
|
a14d55980c | ||
|
|
0d3d4aacc2 | ||
|
|
eedd8c3f2f | ||
|
|
89fd93a3a6 | ||
|
|
e33506d4e2 | ||
|
|
180fa439fc | ||
|
|
1b88ff0fde | ||
|
|
1a45e445a3 | ||
|
|
edcc3c7393 | ||
|
|
7e0bff8abe | ||
|
|
ff42cf8a28 | ||
|
|
3905aa7b42 | ||
|
|
48bd5a400d | ||
|
|
fc56522f35 | ||
|
|
d3a520ddc2 | ||
|
|
e0f04ca05b | ||
|
|
1dfd2cffcf | ||
|
|
dab123e614 | ||
|
|
26e9dec5ab | ||
|
|
39879fb9b8 | ||
|
|
5a38be48f5 | ||
|
|
6331c474ba | ||
|
|
db3259d651 | ||
|
|
466dae32f1 | ||
|
|
f785174734 | ||
|
|
285f6dea07 | ||
|
|
27692afcd0 | ||
|
|
3383181860 | ||
|
|
a014a946bd | ||
|
|
cc9e9ae59d | ||
|
|
c7b0652274 | ||
|
|
f994214666 | ||
|
|
42ab40b9cc | ||
|
|
1067ba3603 | ||
|
|
30585ab4b4 | ||
|
|
d66acd141e | ||
|
|
40022a2fb1 | ||
|
|
126cecc636 | ||
|
|
5770fdcdf7 | ||
|
|
7468a98c83 | ||
|
|
ecbc309ccb | ||
|
|
337faaf74a | ||
|
|
b7e9e964c6 | ||
|
|
335c1ce2d1 | ||
|
|
2d853ea493 | ||
|
|
adbb4b5fd3 | ||
|
|
2cb657a2ae | ||
|
|
e7f86df75b | ||
|
|
70c96afa6c | ||
|
|
cdcdad5d5d | ||
|
|
52a93ee85f | ||
|
|
6670ca3ffa | ||
|
|
218539ae2c | ||
|
|
6745d91683 | ||
|
|
bb7e7fc215 | ||
|
|
fc108ae9e6 | ||
|
|
8e5a66b3bf | ||
|
|
ed2df219c7 | ||
|
|
45d232d316 | ||
|
|
eca57c613d | ||
|
|
d26f8791df | ||
|
|
fbda4f85b6 | ||
|
|
e882ffceeb | ||
|
|
e61483f604 | ||
|
|
2f4d9caae5 | ||
|
|
fa30e3f036 | ||
|
|
205797cbd4 | ||
|
|
e25bc60bbf | ||
|
|
0511dffda0 | ||
|
|
5c25a25571 | ||
|
|
5c7573d23a | ||
|
|
71f40809ee | ||
|
|
2c20582a12 | ||
|
|
f4fb80e4a9 | ||
|
|
d68ab71b6d | ||
|
|
9c1b2e0b9c | ||
|
|
f5bdb26566 | ||
|
|
3e32d7c455 | ||
|
|
b7e9ffa4eb | ||
|
|
ee352bdaaf | ||
|
|
d22ad6c935 | ||
|
|
f7e4eb5f50 | ||
|
|
886453786c | ||
|
|
6a8dc1be90 | ||
|
|
0dd27a3602 | ||
|
|
037404ae30 | ||
|
|
f5ef0ed466 | ||
|
|
5e4187605f | ||
|
|
fcd3517820 | ||
|
|
66e7122b55 | ||
|
|
6f7ab0c4fc | ||
|
|
f3c89e6192 | ||
|
|
621babeb2e | ||
|
|
3b469c0e56 | ||
|
|
b306a90872 | ||
|
|
c078dc37a9 | ||
|
|
d4396cda27 | ||
|
|
610c753e1d | ||
|
|
b22456cd6c | ||
|
|
29c6a904b1 | ||
|
|
b339d3de4e | ||
|
|
573080105a | ||
|
|
58da08a83c | ||
|
|
4e39f9a466 | ||
|
|
0d9b08b7e0 |
110
SConstruct
110
SConstruct
@ -1422,18 +1422,6 @@ if env.TargetOSIs('posix'):
|
||||
env.Append( LINKFLAGS=["-fstack-protector"] )
|
||||
env.Append( SHLINKFLAGS=["-fstack-protector"] )
|
||||
|
||||
if has_option( "ssl" ):
|
||||
env.SetConfigHeaderDefine("MONGO_CONFIG_SSL")
|
||||
env.Append( MONGO_CRYPTO=["openssl"] )
|
||||
if env.TargetOSIs('windows'):
|
||||
env.Append( LIBS=["libeay32"] )
|
||||
env.Append( LIBS=["ssleay32"] )
|
||||
else:
|
||||
env.Append( LIBS=["ssl"] )
|
||||
env.Append( LIBS=["crypto"] )
|
||||
else:
|
||||
env.Append( MONGO_CRYPTO=["tom"] )
|
||||
|
||||
wiredtiger = False
|
||||
if get_option('wiredtiger') == 'on':
|
||||
# Wiredtiger only supports 64-bit architecture, and will fail to compile on 32-bit
|
||||
@ -2212,6 +2200,70 @@ def doConfigure(myenv):
|
||||
})
|
||||
libdeps.setup_conftests(conf)
|
||||
|
||||
if has_option( "ssl" ):
|
||||
sslLibName = "ssl"
|
||||
cryptoLibName = "crypto"
|
||||
if conf.env.TargetOSIs('windows'):
|
||||
sslLibName = "ssleay32"
|
||||
cryptoLibName = "libeay32"
|
||||
|
||||
if not conf.CheckLibWithHeader(
|
||||
sslLibName,
|
||||
["openssl/ssl.h"],
|
||||
"C",
|
||||
"SSL_version(NULL);",
|
||||
autoadd=True):
|
||||
conf.env.ConfError("Couldn't find OpenSSL ssl.h header and library")
|
||||
|
||||
if not conf.CheckLibWithHeader(
|
||||
cryptoLibName,
|
||||
["openssl/crypto.h"],
|
||||
"C",
|
||||
"SSLeay_version(0);",
|
||||
autoadd=True):
|
||||
conf.env.ConfError("Couldn't find OpenSSL crypto.h header and library")
|
||||
|
||||
def CheckLinkSSL(context):
|
||||
test_body = """
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_free_strings();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
"""
|
||||
context.Message("Checking that linking to OpenSSL works...")
|
||||
ret = context.TryLink(textwrap.dedent(test_body), ".c")
|
||||
context.Result(ret)
|
||||
return ret
|
||||
|
||||
conf.AddTest("CheckLinkSSL", CheckLinkSSL)
|
||||
|
||||
if not conf.CheckLinkSSL():
|
||||
conf.env.ConfError("SSL is enabled, but is unavailable")
|
||||
|
||||
env.SetConfigHeaderDefine("MONGO_CONFIG_SSL")
|
||||
env.Append( MONGO_CRYPTO=["openssl"] )
|
||||
|
||||
if conf.CheckDeclaration(
|
||||
"FIPS_mode_set",
|
||||
includes="""
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
"""):
|
||||
conf.env.SetConfigHeaderDefine('MONGO_CONFIG_HAVE_FIPS_MODE_SET')
|
||||
|
||||
else:
|
||||
env.Append( MONGO_CRYPTO=["tom"] )
|
||||
|
||||
if use_system_version_of_library("pcre"):
|
||||
conf.FindSysLibDep("pcre", ["pcre"])
|
||||
conf.FindSysLibDep("pcrecpp", ["pcrecpp"])
|
||||
@ -2369,40 +2421,6 @@ def doConfigure(myenv):
|
||||
# ask each module to configure itself and the build environment.
|
||||
moduleconfig.configure_modules(mongo_modules, conf)
|
||||
|
||||
def CheckLinkSSL(context):
|
||||
test_body = """
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
ERR_load_crypto_strings();
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
ERR_free_strings();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
"""
|
||||
context.Message("Checking if OpenSSL is available...")
|
||||
ret = context.TryLink(textwrap.dedent(test_body), ".c")
|
||||
context.Result(ret)
|
||||
return ret
|
||||
conf.AddTest("CheckLinkSSL", CheckLinkSSL)
|
||||
|
||||
if has_option("ssl"):
|
||||
if not conf.CheckLinkSSL():
|
||||
conf.env.ConfError("SSL is enabled, but is unavailable")
|
||||
|
||||
if conf.CheckDeclaration(
|
||||
"FIPS_mode_set",
|
||||
includes="""
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
"""):
|
||||
conf.env.SetConfigHeaderDefine('MONGO_CONFIG_HAVE_FIPS_MODE_SET')
|
||||
|
||||
return conf.Finish()
|
||||
|
||||
env = doConfigure( env )
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
import json
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-b", "--baseline", default="base.json", dest="baseline",
|
||||
help="path to json file containing baseline data")
|
||||
parser.add_argument("-c", "--comparison", default="compare.json", dest="compare",
|
||||
help="path to json file containing comparison data")
|
||||
parser.add_argument("-t", "--threshold", default=75, dest="threshold",
|
||||
help="Comparison threshold in percent. Ex. -t 75 will fail if\n"
|
||||
"the comparison is less than 75% of the reference value")
|
||||
args = parser.parse_args()
|
||||
|
||||
compare = json.load(open(args.compare))
|
||||
baseline = json.load(open(args.baseline))
|
||||
baselinedict = dict((s['name'], s) for s in baseline['results'])
|
||||
|
||||
threshold = float(args.threshold)
|
||||
|
||||
# Note, we're putting things in an ops_per_sec fields, but it's really
|
||||
# a ratio. Would like to rename and have evergreen pick it up.
|
||||
|
||||
newresults = []
|
||||
reportresults = []
|
||||
fails = []
|
||||
for result in compare['results'] :
|
||||
nresult = {'name' : result['name']}
|
||||
nreport = {'test_file' : result['name'], 'exit_code' : 0, 'elapsed' : 5,
|
||||
'start': 1441227291.962453, 'end': 1441227293.428761}
|
||||
r = result['results']
|
||||
s = baselinedict[result['name']]['results']
|
||||
nresult['results'] = dict((thread,
|
||||
{'ops_per_sec' : 100*r[thread]['ops_per_sec']/s[thread]['ops_per_sec']})
|
||||
for thread in r if type(r[thread]) == type({}) and thread in s)
|
||||
newresults.append(nresult)
|
||||
failingThreads = [thread for thread in nresult['results']
|
||||
if nresult['results'][thread]['ops_per_sec'] < threshold]
|
||||
if len(failingThreads) > 0 :
|
||||
nreport['status'] = 'fail'
|
||||
for thread in failingThreads:
|
||||
print "Test %s failed comparison to baseline for thread level %s. Achieved %.2f %%" \
|
||||
" of the performance of baseline." % \
|
||||
(result['name'], thread, nresult['results'][thread]['ops_per_sec'])
|
||||
fails.append((result['name'], failingThreads))
|
||||
else:
|
||||
nreport['status'] = 'pass'
|
||||
reportresults.append(nreport)
|
||||
|
||||
out = open("perf.json", 'w')
|
||||
json.dump({'results' : newresults}, out, indent=4, separators=(',', ':'))
|
||||
|
||||
report = {}
|
||||
report['failures'] = len(fails)
|
||||
report['results'] = reportresults
|
||||
|
||||
reportFile = open('report.json', 'w')
|
||||
json.dump(report, reportFile, indent=4, separators=(',', ': '))
|
||||
|
||||
if len(fails) > 0:
|
||||
print "There were failing tests"
|
||||
print fails
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
@ -50,8 +50,8 @@ if not version_parts:
|
||||
exit(1)
|
||||
|
||||
if version_parts[0]:
|
||||
print "suffix: latest"
|
||||
print "src_suffix: latest"
|
||||
print "suffix: v3.2-latest"
|
||||
print "src_suffix: v3.2-latest"
|
||||
else:
|
||||
print "suffix: {0}".format(version_line)
|
||||
print "src_suffix: r{0}".format(version_line)
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
A prototype hang analyzer for MCI integration to help investigate test timeouts
|
||||
|
||||
1. Script supports taking dumps, and/or dumping a summary of useful information about a process
|
||||
2. Script will iterate through a list of interesting processes (mongo, mongod, and mongos),
|
||||
2. Script will iterate through a list of interesting processes (mongo, mongod, and mongos),
|
||||
and run the tools from step 1.
|
||||
|
||||
Supports Linux, MacOS X, and Windows.
|
||||
@ -25,6 +25,9 @@ import time
|
||||
from distutils import spawn
|
||||
from optparse import OptionParser
|
||||
|
||||
if sys.platform == "win32":
|
||||
import win32process
|
||||
|
||||
def call(a = []):
|
||||
sys.stdout.write(str(a) + "\n")
|
||||
sys.stdout.flush()
|
||||
@ -184,7 +187,23 @@ class LLDBDumper(object):
|
||||
|
||||
stream.write("INFO: Debugger %s, analyzing %d\n" % (dbg, pid))
|
||||
|
||||
call([dbg, "--version"])
|
||||
lldb_version = callo([dbg, "--version"])
|
||||
|
||||
stream.write(lldb_version)
|
||||
|
||||
# Do we have the XCode or LLVM version of lldb?
|
||||
# Old versions of lldb do not work well when taking commands via a file
|
||||
# XCode (7.2): lldb-340.4.119
|
||||
# LLVM - lldb version 3.7.0 ( revision )
|
||||
|
||||
if 'version' not in lldb_version:
|
||||
# We have XCode's lldb
|
||||
lldb_version = lldb_version[lldb_version.index("lldb-"):]
|
||||
lldb_version = lldb_version.replace('lldb-', '')
|
||||
lldb_major_version = int(lldb_version[:lldb_version.index('.')])
|
||||
if lldb_major_version < 340:
|
||||
stream.write("WARNING: Debugger lldb is too old, please upgrade to XCode 7.2\n")
|
||||
return
|
||||
|
||||
cmds = [
|
||||
"attach -p %d" % pid,
|
||||
@ -354,7 +373,13 @@ def signal_process(pid):
|
||||
|
||||
def timeout_protector():
|
||||
print "Script timeout has been hit, terminating"
|
||||
os.kill(os.getpid(), signal.SIGKILL)
|
||||
if sys.platform == "win32":
|
||||
# Have the process exit with code 9 when it terminates itself to closely match the exit code
|
||||
# of the process when it sends itself a SIGKILL.
|
||||
handle = win32process.GetCurrentProcess()
|
||||
win32process.TerminateProcess(handle, 9)
|
||||
else:
|
||||
os.kill(os.getpid(), signal.SIGKILL)
|
||||
|
||||
|
||||
# Basic procedure
|
||||
|
||||
@ -45,7 +45,7 @@ import urlparse
|
||||
DEFAULT_ARCHES=["x86_64"]
|
||||
|
||||
# Made up names for the flavors of distribution we package for.
|
||||
DISTROS=["suse", "debian","redhat","ubuntu"]
|
||||
DISTROS=["suse", "debian","redhat","ubuntu","amazon"]
|
||||
|
||||
|
||||
class Spec(object):
|
||||
@ -54,8 +54,10 @@ class Spec(object):
|
||||
self.gitspec = gitspec
|
||||
self.rel = rel
|
||||
|
||||
# Nightly version numbers can be in the form: 3.0.7-pre-, or 3.0.7-5-g3b67ac
|
||||
#
|
||||
def is_nightly(self):
|
||||
return bool(re.search("-$", self.version()))
|
||||
return bool(re.search("-$", self.version())) or bool(re.search("\d-\d+-g[0-9a-f]+$", self.version()))
|
||||
|
||||
def is_rc(self):
|
||||
return bool(re.search("-rc\d+$", self.version()))
|
||||
@ -109,7 +111,7 @@ class Spec(object):
|
||||
# our upstream version too).
|
||||
if re.search("^(debian|ubuntu)", distro.name()):
|
||||
return re.sub("-", "~", self.ver)
|
||||
elif re.search("(suse|redhat|fedora|centos)", distro.name()):
|
||||
elif re.search("(suse|redhat|fedora|centos|amazon)", distro.name()):
|
||||
return re.sub("-.*", "", self.ver)
|
||||
else:
|
||||
raise Exception("BUG: unsupported platform?")
|
||||
@ -133,7 +135,7 @@ class Distro(object):
|
||||
def archname(self, arch):
|
||||
if re.search("^(debian|ubuntu)", self.n):
|
||||
return "i386" if arch.endswith("86") else "amd64"
|
||||
elif re.search("^(suse|centos|redhat|fedora)", self.n):
|
||||
elif re.search("^(suse|centos|redhat|fedora|amazon)", self.n):
|
||||
return "i686" if arch.endswith("86") else "x86_64"
|
||||
else:
|
||||
raise Exception("BUG: unsupported platform?")
|
||||
@ -182,7 +184,7 @@ class Distro(object):
|
||||
|
||||
if re.search("^(debian|ubuntu)", self.n):
|
||||
return "repo/apt/%s/dists/%s/mongodb-enterprise/%s/%s/binary-%s/" % (self.n, self.repo_os_version(build_os), repo_directory, self.repo_component(), self.archname(arch))
|
||||
elif re.search("(redhat|fedora|centos)", self.n):
|
||||
elif re.search("(redhat|fedora|centos|amazon)", self.n):
|
||||
return "repo/yum/%s/%s/mongodb-enterprise/%s/%s/RPMS/" % (self.n, self.repo_os_version(build_os), repo_directory, self.archname(arch))
|
||||
elif re.search("(suse)", self.n):
|
||||
return "repo/zypper/%s/%s/mongodb-enterprise/%s/%s/RPMS/" % (self.n, self.repo_os_version(build_os), repo_directory, self.archname(arch))
|
||||
@ -202,11 +204,13 @@ class Distro(object):
|
||||
def repo_os_version(self, build_os):
|
||||
"""Return an OS version suitable for package repo directory
|
||||
naming - e.g. 5, 6 or 7 for redhat/centos, "precise," "wheezy," etc.
|
||||
for Ubuntu/Debian, 11 for suse"""
|
||||
for Ubuntu/Debian, 11 for suse, "2013.03" for amazon"""
|
||||
if self.n == 'suse':
|
||||
return re.sub(r'^suse(\d+)$', r'\1', build_os)
|
||||
if self.n == 'redhat':
|
||||
return re.sub(r'^rhel(\d).*$', r'\1', build_os)
|
||||
if self.n == 'amazon':
|
||||
return "2013.03"
|
||||
elif self.n == 'ubuntu':
|
||||
if build_os == 'ubuntu1204':
|
||||
return "precise"
|
||||
@ -225,7 +229,7 @@ class Distro(object):
|
||||
def make_pkg(self, build_os, arch, spec, srcdir):
|
||||
if re.search("^(debian|ubuntu)", self.n):
|
||||
return packager.make_deb(self, build_os, arch, spec, srcdir)
|
||||
elif re.search("^(suse|centos|redhat|fedora)", self.n):
|
||||
elif re.search("^(suse|centos|redhat|fedora|amazon)", self.n):
|
||||
return packager.make_rpm(self, build_os, arch, spec, srcdir)
|
||||
else:
|
||||
raise Exception("BUG: unsupported platform?")
|
||||
@ -239,6 +243,8 @@ class Distro(object):
|
||||
return [ "suse11", "suse12" ]
|
||||
if re.search("(redhat|fedora|centos)", self.n):
|
||||
return [ "rhel70", "rhel62", "rhel57" ]
|
||||
elif self.n == 'amazon':
|
||||
return [ "amazon" ]
|
||||
elif self.n == 'ubuntu':
|
||||
return [ "ubuntu1204", "ubuntu1404" ]
|
||||
elif self.n == 'debian':
|
||||
@ -250,7 +256,10 @@ class Distro(object):
|
||||
"""Return the release distribution to use in the rpm - "el5" for rhel 5.x,
|
||||
"el6" for rhel 6.x, return anything else unchanged"""
|
||||
|
||||
return re.sub(r'^rh(el\d).*$', r'\1', build_os)
|
||||
if self.n == 'amazon':
|
||||
return 'amzn1'
|
||||
else:
|
||||
return re.sub(r'^rh(el\d).*$', r'\1', build_os)
|
||||
|
||||
def main(argv):
|
||||
|
||||
@ -359,7 +368,7 @@ def make_package(distro, build_os, arch, spec, srcdir):
|
||||
def make_repo(repodir, distro, build_os, spec):
|
||||
if re.search("(debian|ubuntu)", repodir):
|
||||
make_deb_repo(repodir, distro, build_os, spec)
|
||||
elif re.search("(suse|centos|redhat|fedora)", repodir):
|
||||
elif re.search("(suse|centos|redhat|fedora|amazon)", repodir):
|
||||
packager.make_rpm_repo(repodir)
|
||||
else:
|
||||
raise Exception("BUG: unsupported platform?")
|
||||
|
||||
@ -54,8 +54,10 @@ class Spec(object):
|
||||
self.gitspec = gitspec
|
||||
self.rel = rel
|
||||
|
||||
# Nightly version numbers can be in the form: 3.0.7-pre-, or 3.0.7-5-g3b67ac
|
||||
#
|
||||
def is_nightly(self):
|
||||
return bool(re.search("-$", self.version()))
|
||||
return bool(re.search("-$", self.version())) or bool(re.search("\d-\d+-g[0-9a-f]+$", self.version()))
|
||||
|
||||
def is_rc(self):
|
||||
return bool(re.search("-rc\d+$", self.version()))
|
||||
@ -93,10 +95,10 @@ class Spec(object):
|
||||
else:
|
||||
corenum = 1
|
||||
# RC's
|
||||
if re.search("-rc\d+$", self.version()):
|
||||
if self.is_rc():
|
||||
return "0.%s.%s" % (corenum, re.sub('.*-','',self.version()))
|
||||
# Nightlies
|
||||
elif re.search("-$", self.version()):
|
||||
elif self.is_nightly():
|
||||
return "0.%s.%s" % (corenum, time.strftime("%Y%m%d"))
|
||||
else:
|
||||
return str(corenum)
|
||||
|
||||
@ -1,344 +0,0 @@
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
import itertools
|
||||
from dateutil import parser
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
# Example usage:
|
||||
# perf_regression_check.py -f history_file.json --rev 18808cd923789a34abd7f13d62e7a73fafd5ce5f
|
||||
# Loads the history json file, and looks for regressions at the revision 18808cd...
|
||||
# Will exit with status code 1 if any regression is found, 0 otherwise.
|
||||
|
||||
def compareOneResultNoise(this_one, reference, label, threadlevel="max", noiseLevel=0,
|
||||
noiseMultiple=1, minThreshold=0.05):
|
||||
'''
|
||||
Take two result series and compare them to see if they are acceptable.
|
||||
Return true if failed, and false if pass
|
||||
Uses historical noise data for the comparison.
|
||||
|
||||
'''
|
||||
failed = False;
|
||||
if not reference:
|
||||
return failed
|
||||
|
||||
ref = ""
|
||||
current = ""
|
||||
noise = 0
|
||||
|
||||
if threadlevel == "max":
|
||||
ref = reference["max"]
|
||||
current = this_one["max"]
|
||||
else:
|
||||
# Don't do a comparison if the thread data is missing
|
||||
if not threadlevel in reference["results"].keys():
|
||||
return failed
|
||||
ref = reference["results"][threadlevel]['ops_per_sec']
|
||||
current = this_one["results"][threadlevel]['ops_per_sec']
|
||||
|
||||
noise = noiseLevel * noiseMultiple
|
||||
delta = minThreshold * ref
|
||||
if (delta < noise):
|
||||
delta = noise
|
||||
# Do the check
|
||||
if ref - current >= delta:
|
||||
print ("\tregression found on %s: drop from %.2f ops/sec (commit %s) to %.2f ops/sec for comparison %s. Diff is"
|
||||
" %.2f ops/sec (%.2f%%), noise level is %.2f ops/sec and multiple is %.2f" %
|
||||
(threadlevel, ref, reference["revision"][:5], current, label, ref - current,
|
||||
100*(ref-current)/ref, noiseLevel, noiseMultiple))
|
||||
failed = True
|
||||
return failed
|
||||
|
||||
|
||||
def compareResults(this_one, reference, threshold, label, noiseLevels={}, noiseMultiple=1, threadThreshold=None, threadNoiseMultiple=None):
|
||||
'''
|
||||
Take two result series and compare them to see if they are acceptable.
|
||||
Return true if failed, and false if pass
|
||||
'''
|
||||
|
||||
failed = False;
|
||||
if not reference:
|
||||
return failed
|
||||
# Default threadThreshold to the same as the max threshold
|
||||
if not threadThreshold:
|
||||
threadThreshold = threshold
|
||||
if not threadNoiseMultiple :
|
||||
threadNoiseMultiple = noiseMultiple
|
||||
|
||||
# Check max throughput first
|
||||
noise = 0
|
||||
# For the max throughput, use the max noise across the thread levels as the noise parameter
|
||||
if len(noiseLevels.values()) > 0:
|
||||
noise = max(noiseLevels.values())
|
||||
if compareOneResultNoise(this_one, reference, label, "max", noiseLevel=noise,
|
||||
noiseMultiple=noiseMultiple, minThreshold=threshold):
|
||||
failed = True;
|
||||
# Check for regression on threading levels
|
||||
for (level, ops_per_sec) in (((r, this_one["results"][r]['ops_per_sec']) for r in
|
||||
this_one["results"] if type(this_one["results"][r]) == type({}))):
|
||||
noise = 0
|
||||
if level in noiseLevels:
|
||||
noise = noiseLevels[level]
|
||||
if compareOneResultNoise(this_one, reference, label, level, noiseLevel=noise,
|
||||
noiseMultiple=threadNoiseMultiple, minThreshold=threadThreshold):
|
||||
failed = True
|
||||
if not failed:
|
||||
print "\tno regression against %s and githash %s" %(label, reference["revision"][:5])
|
||||
return failed
|
||||
|
||||
|
||||
|
||||
def main(args):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-f", "--file", dest="file", help="path to json file containing"
|
||||
"history data")
|
||||
parser.add_argument("-t", "--tagFile", dest="tfile", help="path to json file containing"
|
||||
"tag data")
|
||||
parser.add_argument("--rev", dest="rev", help="revision to examine for regressions")
|
||||
parser.add_argument("--ndays", default=7, type=int, dest="ndays", help="Check against"
|
||||
"commit from n days ago.")
|
||||
parser.add_argument("--threshold", default=0.05, type=float, dest="threshold", help=
|
||||
"Don't flag an error if throughput is less than 'threshold'x100 percent off")
|
||||
parser.add_argument("--noiseLevel", default=1, type=float, dest="noise", help=
|
||||
"Don't flag an error if throughput is less than 'noise' times the computed noise level off")
|
||||
parser.add_argument("--threadThreshold", default=0.1, type=float, dest="threadThreshold", help=
|
||||
"Don't flag an error if thread level throughput is more than"
|
||||
"'threadThreshold'x100 percent off")
|
||||
parser.add_argument("--threadNoiseLevel", default=2, type=float, dest="threadNoise", help=
|
||||
"Don't flag an error if thread level throughput is less than 'noise' times the computed noise level off")
|
||||
parser.add_argument("--refTag", dest="reference", help=
|
||||
"Reference tag to compare against. Should be a valid tag name")
|
||||
parser.add_argument("--overrideFile", dest="overrideFile", help="File to read for comparison override information")
|
||||
parser.add_argument("--variant", dest="variant", help="Variant to lookup in the override file")
|
||||
|
||||
args = parser.parse_args()
|
||||
tagHistory = ""
|
||||
j = get_json(args.file)
|
||||
if args.tfile :
|
||||
t = get_json(args.tfile)
|
||||
tagHistory = History(t)
|
||||
history = History(j)
|
||||
testnames = history.testnames()
|
||||
failed = False
|
||||
failed = 0
|
||||
|
||||
results = []
|
||||
# Default empty override structure
|
||||
overrides = {'ndays' : {}, 'reference' : {}}
|
||||
if args.overrideFile :
|
||||
# Read the overrides file
|
||||
foverrides = get_json(args.overrideFile)
|
||||
# Is this variant in the overrides file?
|
||||
if args.variant in foverrides :
|
||||
overrides = foverrides[args.variant]
|
||||
|
||||
for test in testnames:
|
||||
# The first entry is valid. The rest is dummy data to match the existing format
|
||||
result = {'test_file' : test, 'exit_code' : 0, 'elapsed' : 5, 'start': 1441227291.962453, 'end': 1441227293.428761}
|
||||
this_one = history.seriesAtRevision(test, args.rev)
|
||||
testFailed = False
|
||||
print "checking %s.." % (test)
|
||||
if not this_one:
|
||||
print "\tno data at this revision, skipping"
|
||||
continue
|
||||
|
||||
#If the new build is 10% lower than the target (3.0 will be
|
||||
#used as the baseline for 3.2 for instance), consider it
|
||||
#regressed.
|
||||
previous = history.seriesItemsNBefore(test, args.rev, 1)
|
||||
if not previous:
|
||||
print "\tno previous data, skipping"
|
||||
continue
|
||||
if compareResults(this_one, previous[0], args.threshold, "Previous", history.noiseLevels(test),
|
||||
args.noise, args.threadThreshold, args.threadNoise):
|
||||
testFailed = True
|
||||
result['PreviousCompare'] = 'fail'
|
||||
else :
|
||||
result['PreviousCompare'] = 'pass'
|
||||
|
||||
daysprevious = history.seriesItemsNDaysBefore(test, args.rev,args.ndays)
|
||||
if test in overrides['ndays']:
|
||||
print "Override in ndays for test %s" % test
|
||||
daysprevious = overrides['ndays'][test]
|
||||
if compareResults(this_one, daysprevious, args.threshold, "NDays", history.noiseLevels(test),
|
||||
args.noise, args.threadThreshold, args.threadNoise):
|
||||
testFailed = True
|
||||
result['NDayCompare'] = 'fail'
|
||||
else :
|
||||
result['NDayCompare'] = 'pass'
|
||||
if tagHistory :
|
||||
reference = tagHistory.seriesAtTag(test, args.reference)
|
||||
if not reference :
|
||||
print "Didn't get any data for test %s with baseline %s" % (test, args.reference)
|
||||
if test in overrides['reference']:
|
||||
print "Override in references for test %s" % test
|
||||
reference = overrides['reference'][test]
|
||||
if compareResults(this_one, reference, args.threshold, "Baseline Comparison " + args.reference, history.noiseLevels(test),
|
||||
args.noise, args.threadThreshold, args.threadNoise):
|
||||
testFailed = True
|
||||
result['BaselineCompare'] = 'fail'
|
||||
else :
|
||||
result['BaselineCompare'] = 'pass'
|
||||
if testFailed :
|
||||
result['status'] = 'fail'
|
||||
failed += 1
|
||||
else :
|
||||
result['status'] = 'pass'
|
||||
results.append(result)
|
||||
|
||||
report = {}
|
||||
report['failures'] = failed
|
||||
report['results'] = results
|
||||
|
||||
reportFile = open('report.json', 'w')
|
||||
json.dump(report, reportFile, indent=4, separators=(',', ': '))
|
||||
if failed > 0 :
|
||||
sys.exit(1)
|
||||
else:
|
||||
sys.exit(0)
|
||||
|
||||
# We wouldn't need this function if we had numpy installed on the system
|
||||
def computeRange(result_list):
|
||||
'''
|
||||
Compute the max, min, and range (max - min) for the result list
|
||||
'''
|
||||
min = max = result_list[0]
|
||||
for result in result_list:
|
||||
if result < min:
|
||||
min = result
|
||||
if result > max:
|
||||
max = result
|
||||
return (max,min,max-min)
|
||||
|
||||
def get_json(filename):
|
||||
jf = open(filename, 'r')
|
||||
json_obj = json.load(jf)
|
||||
return json_obj
|
||||
|
||||
class History(object):
|
||||
def __init__(self, jsonobj):
|
||||
self._raw = sorted(jsonobj, key=lambda d: d["order"])
|
||||
self._noise = None
|
||||
|
||||
def testnames(self):
|
||||
return set(list(itertools.chain.from_iterable([[z["name"] for z in c["data"]["results"]]
|
||||
for c in self._raw])))
|
||||
|
||||
def seriesAtRevision(self, testname, revision):
|
||||
s = self.series(testname)
|
||||
for result in s:
|
||||
if result["revision"] == revision:
|
||||
return result
|
||||
return None
|
||||
|
||||
def seriesAtTag(self, testname, tagName):
|
||||
s = self.series(testname)
|
||||
for result in s:
|
||||
if result["tag"] == tagName:
|
||||
return result
|
||||
return None
|
||||
|
||||
def seriesItemsNBefore(self, testname, revision, n):
|
||||
"""
|
||||
Returns the 'n' items in the series under the given test name that
|
||||
appear prior to the specified revision.
|
||||
"""
|
||||
results = []
|
||||
found = False
|
||||
s = self.series(testname)
|
||||
for result in s:
|
||||
if result["revision"] == revision:
|
||||
found = True
|
||||
break
|
||||
results.append(result)
|
||||
|
||||
if found:
|
||||
return results[-1*n:]
|
||||
return []
|
||||
|
||||
def computeNoiseLevels(self):
|
||||
"""
|
||||
For each test, go through all results, and compute the average
|
||||
noise (max - min) for the series
|
||||
|
||||
"""
|
||||
self._noise = {}
|
||||
testnames = self.testnames()
|
||||
for test in testnames:
|
||||
self._noise[test] = {}
|
||||
s = self.series(test)
|
||||
threads = []
|
||||
for result in s:
|
||||
threads = result["threads"]
|
||||
break
|
||||
|
||||
# Determine levels from last commit? Probably a better way to do this.
|
||||
for thread in threads:
|
||||
s = self.series(test)
|
||||
self._noise[test][thread] = sum((computeRange(x["results"][thread]["ops_per_sec_values"])[2]
|
||||
for x in s))
|
||||
s = self.series(test)
|
||||
self._noise[test][thread] /= sum(1 for x in s)
|
||||
|
||||
|
||||
def noiseLevels(self, testname):
|
||||
"""
|
||||
Returns the average noise level of the given test. Noise levels
|
||||
are thread specific. Returns an array
|
||||
|
||||
"""
|
||||
# check if noise has been computed. Compute if it hasn't
|
||||
if not self._noise:
|
||||
print "Computing noise levels"
|
||||
self.computeNoiseLevels()
|
||||
# Look up noise value for test
|
||||
if not testname in self._noise:
|
||||
print "Test %s not in self._noise" % (testname)
|
||||
return self._noise[testname]
|
||||
|
||||
|
||||
def seriesItemsNDaysBefore(self, testname, revision, n):
|
||||
"""
|
||||
Returns the items in the series under the given test name that
|
||||
appear 'n' days prior to the specified revision.
|
||||
"""
|
||||
results = {}
|
||||
# Date for this revision
|
||||
s = self.seriesAtRevision(testname, revision)
|
||||
if s==[]:
|
||||
return []
|
||||
refdate = parser.parse(s["create_time"]) - timedelta(days=n)
|
||||
|
||||
s = self.series(testname)
|
||||
for result in s:
|
||||
if parser.parse(result["create_time"]) < refdate:
|
||||
results = result
|
||||
return results
|
||||
|
||||
|
||||
|
||||
def series(self, testname):
|
||||
for commit in self._raw:
|
||||
# get a copy of the samples for those whose name matches the given testname
|
||||
matching = filter( lambda x: x["name"]==testname, commit["data"]["results"])
|
||||
if matching:
|
||||
result = matching[0]
|
||||
result["revision"] = commit["revision"]
|
||||
result["tag"] = commit["tag"]
|
||||
result["create_time"] = commit["create_time"]
|
||||
result["order"] = commit["order"]
|
||||
result["max"] = max(f["ops_per_sec"] for f in result["results"].values()
|
||||
if type(f) == type({}))
|
||||
result["threads"] = [f for f in result["results"] if type(result["results"][f])
|
||||
== type({})]
|
||||
yield result
|
||||
|
||||
|
||||
class TestResult:
|
||||
def __init__(self, json):
|
||||
self._raw = json
|
||||
|
||||
#def max(self):
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
@ -23,6 +23,9 @@ if __name__ == "__main__" and __package__ is None:
|
||||
def _execute_suite(suite, logging_config):
|
||||
"""
|
||||
Executes each test group of 'suite', failing fast if requested.
|
||||
|
||||
Returns true if the execution of the suite was interrupted by the
|
||||
user, and false otherwise.
|
||||
"""
|
||||
|
||||
logger = resmokelib.logging.loggers.EXECUTOR
|
||||
@ -64,15 +67,15 @@ def _execute_suite(suite, logging_config):
|
||||
executor.run()
|
||||
if resmokelib.config.FAIL_FAST and group.return_code != 0:
|
||||
suite.return_code = group.return_code
|
||||
return
|
||||
except resmokelib.errors.StopExecution:
|
||||
return False
|
||||
except resmokelib.errors.UserInterrupt:
|
||||
suite.return_code = 130 # Simulate SIGINT as exit code.
|
||||
return
|
||||
return True
|
||||
except:
|
||||
logger.exception("Encountered an error when running %ss of suite %s.",
|
||||
group.test_kind, suite.get_name())
|
||||
suite.return_code = 2
|
||||
return
|
||||
return False
|
||||
|
||||
|
||||
def _log_summary(logger, suites, time_taken):
|
||||
@ -146,20 +149,21 @@ def main():
|
||||
resmoke_logger.info("Suites available to execute:\n%s", "\n".join(suite_names))
|
||||
sys.exit(0)
|
||||
|
||||
interrupted = False
|
||||
suites = resmokelib.parser.get_suites(values, args)
|
||||
try:
|
||||
for suite in suites:
|
||||
resmoke_logger.info(_dump_suite_config(suite, logging_config))
|
||||
|
||||
suite.record_start()
|
||||
_execute_suite(suite, logging_config)
|
||||
interrupted = _execute_suite(suite, logging_config)
|
||||
suite.record_end()
|
||||
|
||||
resmoke_logger.info("=" * 80)
|
||||
resmoke_logger.info("Summary of %s suite: %s",
|
||||
suite.get_name(), _summarize_suite(suite))
|
||||
|
||||
if resmokelib.config.FAIL_FAST and suite.return_code != 0:
|
||||
if interrupted or (resmokelib.config.FAIL_FAST and suite.return_code != 0):
|
||||
time_taken = time.time() - start_time
|
||||
_log_summary(resmoke_logger, suites, time_taken)
|
||||
sys.exit(suite.return_code)
|
||||
@ -171,6 +175,9 @@ def main():
|
||||
exit_code = max(suite.return_code for suite in suites)
|
||||
sys.exit(exit_code)
|
||||
finally:
|
||||
if not interrupted:
|
||||
resmokelib.logging.flush.stop_thread()
|
||||
|
||||
if resmokelib.config.REPORT_FILE is not None:
|
||||
_write_report_file(suites, resmokelib.config.REPORT_FILE)
|
||||
|
||||
|
||||
@ -4,21 +4,17 @@ selector:
|
||||
- jstests/aggregation/*.js
|
||||
- jstests/aggregation/bugs/*.js
|
||||
exclude_files:
|
||||
# mongod needs to start with the enableMajorityReadConcern flag. Tests that
|
||||
# start their own mongods will fail (ShardingTest, ReplSetTest, MongoRunner).
|
||||
- jstests/aggregation/bugs/server6118.js # Uses ShardingTest.
|
||||
- jstests/aggregation/bugs/server6179.js # Uses ShardingTest.
|
||||
- jstests/aggregation/bugs/server7781.js # Uses ShardingTest.
|
||||
- jstests/aggregation/bugs/server18198.js # Uses a mocked mongo client to test read preference.
|
||||
- jstests/aggregation/bugs/server19095.js # Uses ShardingTest.
|
||||
- jstests/aggregation/mongos_slaveok.js # Uses ShardingTest.
|
||||
- jstests/aggregation/testshard1.js # Uses ShardingTest.
|
||||
- jstests/aggregation/testSlave.js # Starts ReplSetTest.
|
||||
- jstests/aggregation/mongos_slaveok.js # Majority read on secondary requires afterOpTime.
|
||||
- jstests/aggregation/testSlave.js # Majority read on secondary requires afterOpTime.
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
config:
|
||||
shell_options:
|
||||
global_vars:
|
||||
TestData:
|
||||
enableMajorityReadConcern: ''
|
||||
eval: "var testingReplication = true; load('jstests/libs/override_methods/set_majority_read_and_write_concerns.js');"
|
||||
readMode: commands
|
||||
hooks:
|
||||
|
||||
@ -2,6 +2,9 @@ selector:
|
||||
js_test:
|
||||
roots:
|
||||
- jstests/concurrency/fsm_all_sharded*.js
|
||||
exclude_files:
|
||||
# These tests run in the concurrency_sharded_sccc suite.
|
||||
- jstests/concurrency/fsm_all_sharded*legacy_config_servers*.js
|
||||
|
||||
# Concurrency tests that run against a sharded cluster start one themselves.
|
||||
executor:
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
selector:
|
||||
js_test:
|
||||
roots:
|
||||
- jstests/concurrency/fsm_all_sharded*legacy_config_servers*.js
|
||||
|
||||
# Concurrency tests that run against a sharded cluster start one themselves.
|
||||
executor:
|
||||
js_test:
|
||||
config:
|
||||
shell_options:
|
||||
nodb: ''
|
||||
readMode: commands
|
||||
@ -9,6 +9,7 @@ executor:
|
||||
shell_options:
|
||||
readMode: commands
|
||||
hooks:
|
||||
- class: ValidateCollections
|
||||
- class: CleanEveryN
|
||||
n: 20
|
||||
fixture:
|
||||
|
||||
@ -6,6 +6,7 @@ selector:
|
||||
# These tests are not expected to pass with master-slave:
|
||||
- jstests/core/dbadmin.js
|
||||
- jstests/core/opcounters_write_cmd.js
|
||||
- jstests/core/capped_update.js
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -7,6 +7,7 @@ selector:
|
||||
- jstests/core/dbadmin.js
|
||||
- jstests/core/opcounters_write_cmd.js
|
||||
- jstests/core/read_after_optime.js
|
||||
- jstests/core/capped_update.js
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -18,3 +18,4 @@ executor:
|
||||
set_parameters:
|
||||
enableTestCommands: 1
|
||||
verbose: ''
|
||||
num_shards: 2
|
||||
|
||||
@ -11,6 +11,7 @@ selector:
|
||||
- jstests/core/batch_write_command*.js # these tests use various write concerns
|
||||
- jstests/core/bench_test*.js # benchRun() used for writes
|
||||
- jstests/core/bypass_doc_validation.js # applyOps() used for writes
|
||||
- jstests/core/capped_update.js # uses godinsert and can't run under replication.
|
||||
- jstests/core/crud_api.js # has specific w:0 tests
|
||||
- jstests/core/error2.js # db.eval() used
|
||||
- jstests/core/eval0.js # db.eval() used
|
||||
|
||||
@ -6,6 +6,9 @@ selector:
|
||||
- jstests/replsets/config_server_checks.js
|
||||
- jstests/replsets/last_op_visible.js
|
||||
- jstests/replsets/oplog_truncated_on_recovery.js
|
||||
- jstests/replsets/priority_takeover_cascading_priorities.js
|
||||
- jstests/replsets/priority_takeover_one_node_higher_priority.js
|
||||
- jstests/replsets/priority_takeover_two_nodes_equal_priority.js
|
||||
- jstests/replsets/read_committed*.js
|
||||
- jstests/replsets/read_majority_two_arbs.js
|
||||
# The combination of new bridges and PV0 can lead to an improper spanning tree in sync2.js.
|
||||
|
||||
@ -12,6 +12,7 @@ selector:
|
||||
- jstests/core/check_shard_index.js # checkShardingIndex.
|
||||
- jstests/core/collection_truncate.js # emptycapped.
|
||||
- jstests/core/compact_keeps_indexes.js # compact.
|
||||
- jstests/core/capped_update.js # uses godinsert and can't run under replication.
|
||||
- jstests/core/auth_copydb.js # copyDatabase.
|
||||
- jstests/core/copydb.js # copyDatabase.
|
||||
- jstests/core/dbadmin.js # "local" database.
|
||||
|
||||
@ -4,8 +4,6 @@ selector:
|
||||
- jstests/sharding/*.js
|
||||
- jstests/sharding/replset_config/*.js
|
||||
exclude_files:
|
||||
- jstests/sharding/csrs_upgrade.js # SERVER-20694
|
||||
- jstests/sharding/csrs_upgrade_during_migrate.js # flaky - SERVER-20580
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -10,8 +10,6 @@ selector:
|
||||
- jstests/sharding/replset_config/*.js
|
||||
- jstests/sharding/sync_cluster_config/*.js
|
||||
exclude_files:
|
||||
- jstests/sharding/csrs_upgrade.js # SERVER-20694
|
||||
- jstests/sharding/csrs_upgrade_during_migrate.js # flaky - SERVER-20580
|
||||
# Skip any tests that run with auth explicitly.
|
||||
- jstests/sharding/*[aA]uth*.js
|
||||
- jstests/sharding/replset_config/*[aA]uth*.js
|
||||
@ -23,6 +21,9 @@ selector:
|
||||
# Skip the testcases that do not have auth bypass when running ops in parallel.
|
||||
- jstests/sharding/cleanup_orphaned_cmd_during_movechunk.js # SERVER-21713
|
||||
- jstests/sharding/cleanup_orphaned_cmd_during_movechunk_hashed.js # SERVER-21713
|
||||
- jstests/sharding/migration_with_source_deletes.js # SERVER-21713
|
||||
- jstests/sharding/migration_sets_fromMigrate_flag.js # SERVER-21713
|
||||
- jstests/sharding/donor_shard_abort_and_start_new_migration.js # SERVER-21713
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -10,8 +10,6 @@ selector:
|
||||
- jstests/sharding/replset_config/*.js
|
||||
- jstests/sharding/sync_cluster_config/*.js
|
||||
exclude_files:
|
||||
- jstests/sharding/csrs_upgrade.js # SERVER-20694
|
||||
- jstests/sharding/csrs_upgrade_during_migrate.js # flaky - SERVER-20580
|
||||
# Skip any tests that run with auth explicitly.
|
||||
- jstests/sharding/*[aA]uth*.js
|
||||
- jstests/sharding/replset_config/*[aA]uth*.js
|
||||
@ -23,6 +21,9 @@ selector:
|
||||
# Skip the testcases that do not have auth bypass when running ops in parallel.
|
||||
- jstests/sharding/cleanup_orphaned_cmd_during_movechunk.js # SERVER-21713
|
||||
- jstests/sharding/cleanup_orphaned_cmd_during_movechunk_hashed.js # SERVER-21713
|
||||
- jstests/sharding/migration_with_source_deletes.js # SERVER-21713
|
||||
- jstests/sharding/migration_sets_fromMigrate_flag.js # SERVER-21713
|
||||
- jstests/sharding/donor_shard_abort_and_start_new_migration.js # SERVER-21713
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
selector:
|
||||
js_test:
|
||||
roots:
|
||||
- jstests/sharding/*.js
|
||||
- jstests/sharding/replset_config/*.js
|
||||
exclude_files:
|
||||
# Auth tests require authentication on the stepdown thread's connection
|
||||
- jstests/sharding/auth*.js
|
||||
- jstests/sharding/cleanup_orphaned_auth.js
|
||||
- jstests/sharding/localhostAuthBypass.js
|
||||
- jstests/sharding/mongos_rs_auth_shard_failure_tolerance.js
|
||||
- jstests/sharding/mrShardedOutputAuth.js
|
||||
# Count/write/aggregate/group commands against the config shard do not support retries yet
|
||||
- jstests/sharding/addshard1.js
|
||||
- jstests/sharding/addshard2.js
|
||||
- jstests/sharding/auto1.js
|
||||
- jstests/sharding/auto_rebalance.js
|
||||
- jstests/sharding/autosplit_heuristics.js
|
||||
- jstests/sharding/balance_tags1.js
|
||||
- jstests/sharding/balance_tags2.js
|
||||
- jstests/sharding/basic_drop_coll.js
|
||||
- jstests/sharding/bulk_shard_insert.js
|
||||
- jstests/sharding/count1.js
|
||||
- jstests/sharding/count2.js
|
||||
- jstests/sharding/cursor1.js
|
||||
- jstests/sharding/diffservers1.js
|
||||
- jstests/sharding/disable_autosplit.js
|
||||
- jstests/sharding/drop_sharded_db.js
|
||||
- jstests/sharding/fair_balancer_round.js
|
||||
- jstests/sharding/findandmodify1.js
|
||||
- jstests/sharding/findandmodify2.js
|
||||
- jstests/sharding/geo_shardedgeonear.js
|
||||
- jstests/sharding/hash_basic.js
|
||||
- jstests/sharding/hash_shard1.js
|
||||
- jstests/sharding/hash_shard_non_empty.js
|
||||
- jstests/sharding/hash_shard_num_chunks.js
|
||||
- jstests/sharding/hash_single_shard.js
|
||||
- jstests/sharding/key_many.js
|
||||
- jstests/sharding/key_string.js
|
||||
- jstests/sharding/large_chunk.js
|
||||
- jstests/sharding/limit_push.js
|
||||
- jstests/sharding/merge_chunks_basic.js
|
||||
- jstests/sharding/migrateBig_balancer.js
|
||||
- jstests/sharding/move_chunk_basic.js
|
||||
- jstests/sharding/movePrimary1.js
|
||||
- jstests/sharding/mrShardedOutput.js
|
||||
- jstests/sharding/names.js
|
||||
- jstests/sharding/prefix_shard_key.js
|
||||
- jstests/sharding/presplit.js
|
||||
- jstests/sharding/query_config.js
|
||||
- jstests/sharding/remove1.js
|
||||
- jstests/sharding/rename_across_mongos.js
|
||||
- jstests/sharding/shard1.js
|
||||
- jstests/sharding/shard2.js
|
||||
- jstests/sharding/shard3.js
|
||||
- jstests/sharding/shard_collection_basic.js
|
||||
- jstests/sharding/sharding_balance1.js
|
||||
- jstests/sharding/sharding_balance2.js
|
||||
- jstests/sharding/sharding_balance3.js
|
||||
- jstests/sharding/sharding_balance4.js
|
||||
- jstests/sharding/sharding_migrate_cursor1.js
|
||||
- jstests/sharding/tag_auto_split.js
|
||||
- jstests/sharding/tag_range.js
|
||||
- jstests/sharding/top_chunk_autosplit.js
|
||||
- jstests/sharding/count_config_servers.js
|
||||
# Calls the config server primary directly (not through mongos)
|
||||
- jstests/sharding/moveprimary_ignore_sharded.js
|
||||
- jstests/sharding/min_optime_recovery.js
|
||||
# Runs setShardVersion/getShardVersion against the config server and we don't support retries
|
||||
# for this command
|
||||
- jstests/sharding/major_version_check.js
|
||||
- jstests/sharding/replset_config/ssv_config_check.js
|
||||
# Already stop or blackholes the primary of the CSRS config shard
|
||||
- jstests/sharding/all_config_hosts_down.js
|
||||
- jstests/sharding/all_config_servers_blackholed_from_mongos.js
|
||||
- jstests/sharding/replset_config/batch_write_command_sharded.js
|
||||
- jstests/sharding/replset_config/config_rs_no_primary.js
|
||||
- jstests/sharding/startup_with_all_configs_down.js
|
||||
# Test runs really slow with the primary continuously stepping down
|
||||
- jstests/sharding/zbigMapReduce.js
|
||||
# Unknown balancer errors - need to be investigated
|
||||
- jstests/sharding/in_memory_sort_limit.js
|
||||
executor:
|
||||
js_test:
|
||||
config:
|
||||
shell_options:
|
||||
eval: "load('jstests/libs/override_methods/sharding_continuous_config_stepdown.js');"
|
||||
nodb: ''
|
||||
readMode: commands
|
||||
@ -8,8 +8,6 @@ selector:
|
||||
- jstests/sharding/*.js
|
||||
- jstests/sharding/replset_config/*.js
|
||||
exclude_files:
|
||||
- jstests/sharding/csrs_upgrade.js # SERVER-20694
|
||||
- jstests/sharding/csrs_upgrade_during_migrate.js # flaky - SERVER-20580
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -10,6 +10,7 @@ selector:
|
||||
- jstests/core/capped6.js # captrunc.
|
||||
- jstests/core/capped_convertToCapped1.js # cloneCollectionAsCapped.
|
||||
- jstests/core/capped_empty.js # emptycapped.
|
||||
- jstests/core/capped_update.js # uses godinsert and can't run under replication.
|
||||
- jstests/core/check_shard_index.js # checkShardingIndex.
|
||||
- jstests/core/collection_truncate.js # emptycapped.
|
||||
- jstests/core/compact_keeps_indexes.js # compact.
|
||||
@ -46,6 +47,8 @@ executor:
|
||||
config:
|
||||
shell_options:
|
||||
readMode: commands
|
||||
hooks:
|
||||
- class: ValidateCollections
|
||||
fixture:
|
||||
class: ShardedClusterFixture
|
||||
mongos_options:
|
||||
|
||||
@ -4,8 +4,6 @@ selector:
|
||||
- jstests/sharding/*.js
|
||||
- jstests/sharding/sync_cluster_config/*.js
|
||||
exclude_files:
|
||||
- jstests/sharding/csrs_upgrade.js # SERVER-20694
|
||||
- jstests/sharding/csrs_upgrade_during_migrate.js # SERVER-20580
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
|
||||
@ -2,44 +2,57 @@ selector:
|
||||
js_test:
|
||||
roots:
|
||||
- jstests/sharding/*.js
|
||||
- jstests/sharding/sync_cluster_config/*.js
|
||||
exclude_files:
|
||||
# The following tests fails because a certain command or functionality is not supported in v3.0
|
||||
# version of mongos:
|
||||
- jstests/sharding/csrs_upgrade.js
|
||||
- jstests/sharding/csrs_upgrade_during_migrate.js
|
||||
- jstests/sharding/find_getmore_cmd.js
|
||||
# explain format changed. In v3.0, read pref is inside explain sub document. In master,
|
||||
# it is on top level.
|
||||
|
||||
# The following tests fail because explain format changed. In v3.0, read pref is inside
|
||||
# explain sub document. In v3.2, it is on top level. See SERVER-21660.
|
||||
- jstests/sharding/auth_slaveok_routing.js
|
||||
- jstests/sharding/explain_read_pref.js
|
||||
- jstests/sharding/read_pref.js
|
||||
# This is testing latest mongos options
|
||||
|
||||
# This is testing latest mongos options.
|
||||
- jstests/sharding/sharding_options.js
|
||||
# This tests the new sharding section in server status.
|
||||
- jstests/sharding/server_status.js
|
||||
# v3.0 does not support explain on findAndModify.
|
||||
- jstests/sharding/explain_find_and_modify_sharded.js
|
||||
# Tests new functionality added to the printShardingStatus shell helper.
|
||||
- jstests/sharding/printShardingStatus.js
|
||||
# v3.2 responds with a new error code (just errmsg in v3.0).
|
||||
- jstests/sharding/drop_configdb.js
|
||||
# v3.2 has new fields, "DBClient (Global)" and "NetworkInterfaceASIO (Sharding)".
|
||||
- jstests/sharding/conn_pool_stats.js
|
||||
# v3.2 returns a named ErrorCode, whereas v3.0 returns a uassert location.
|
||||
- jstests/sharding/movePrimary1.js
|
||||
# v3.0 responds with $err, v3.2 returns err code for inprog and killop.
|
||||
- jstests/sharding/auth.js
|
||||
|
||||
# The following tests fails because of bugs in v3.0 that were already fixed in v3.2:
|
||||
- jstests/sharding/index1.js # SERVER-17915, fixed in v3.1.3
|
||||
- jstests/sharding/zero_shard_version.js # SERVER-20530, fixed in v3.1.9
|
||||
- jstests/sharding/unowned_doc_filtering.js # SERVER-19608, fixed in v3.1.8
|
||||
- jstests/sharding/addshard2.js # SERVER-17231, fixed in v3.1.1
|
||||
- jstests/sharding/all_config_servers_blackholed_from_mongos.js # SERVER-21293, fixed in v3.2.0-rc4
|
||||
- jstests/sharding/features1.js # SERVER-21193, fixed in v3.2.0-rc3
|
||||
- jstests/sharding/find_and_modify_after_multi_write.js # SERVER-20407, backported to v3.0.8
|
||||
- jstests/sharding/index1.js # SERVER-17915, fixed in v3.1.3
|
||||
- jstests/sharding/listDatabases.js # SERVER-21193, fixed in v3.2.0-rc3
|
||||
- jstests/sharding/all_config_servers_blackholed_from_mongos.js # SERVER-21293, 3.2.0-rc4
|
||||
- jstests/sharding/query_config.js # SERVER-11877, fixed in v3.1.8
|
||||
- jstests/sharding/read_does_not_create_namespaces.js # SERVER-17723, fixed in v3.1.2
|
||||
- jstests/sharding/startup_with_all_configs_down.js # SERVER-21215, fixed in v3.2.0-rc4
|
||||
- jstests/sharding/unowned_doc_filtering.js # SERVER-19608, fixed in v3.1.8
|
||||
- jstests/sharding/zero_shard_version.js # SERVER-20530, fixed in v3.1.9
|
||||
- jstests/sharding/drop_sharded_db.js # SERVER-17723, fixed in v3.1.2
|
||||
- jstests/sharding/implicit_db_creation.js # SERVER-17723, fixed in v3.1.2
|
||||
- jstests/sharding/secondary_query_routing.js # SERVER-18671, fixed in v3.3.0
|
||||
|
||||
# TODO: Investigate why these are failing:
|
||||
- jstests/sharding/find_and_modify_after_multi_write.js
|
||||
- jstests/sharding/explain_find_and_modify_sharded.js
|
||||
- jstests/sharding/printShardingStatus.js
|
||||
- jstests/sharding/rename.js
|
||||
- jstests/sharding/drop_configdb.js
|
||||
- jstests/sharding/query_config.js
|
||||
- jstests/sharding/explain_read_pref.js
|
||||
- jstests/sharding/conn_pool_stats.js
|
||||
- jstests/sharding/read_does_not_create_namespaces.js
|
||||
- jstests/sharding/movePrimary1.js
|
||||
- jstests/sharding/addshard2.js
|
||||
- jstests/sharding/all_config_hosts_down.js # possibly caused by SERVER-21215?
|
||||
- jstests/sharding/auth.js
|
||||
- jstests/sharding/auth_slaveok_routing.js
|
||||
- jstests/sharding/startup_with_all_configs_down.js
|
||||
# The following tests fail because of known bugs:
|
||||
- jstests/sharding/sync_cluster_config/sync6.js # SERVER-21660
|
||||
- jstests/sharding/move_primary_basic.js # SERVER-21851
|
||||
- jstests/sharding/enable_sharding_basic.js # SERVER-21853
|
||||
|
||||
executor:
|
||||
js_test:
|
||||
@ -47,6 +60,7 @@ executor:
|
||||
shell_options:
|
||||
global_vars:
|
||||
TestData:
|
||||
useLegacyReplicationProtocol: true
|
||||
useLegacyConfigServers: true
|
||||
mongosBinVersion: 'last-stable'
|
||||
nodb: ''
|
||||
|
||||
@ -11,15 +11,45 @@ import atexit
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
# The subprocess32 module resolves the thread-safety issues of the subprocess module in Python 2.x
|
||||
# when the _posixsubprocess C extension module is also available. Additionally, the _posixsubprocess
|
||||
# C extension module avoids triggering invalid free() calls on Python's internal data structure for
|
||||
# thread-local storage by skipping the PyOS_AfterFork() call when the 'preexec_fn' parameter isn't
|
||||
# specified to subprocess.Popen(). See SERVER-22219 for more details.
|
||||
#
|
||||
# The subprocess32 module is untested on Windows and thus isn't recommended for use, even when it's
|
||||
# installed. See https://github.com/google/python-subprocess32/blob/3.2.7/README.md#usage.
|
||||
if os.name == "posix" and sys.version_info[0] == 2:
|
||||
try:
|
||||
import subprocess32 as subprocess
|
||||
except ImportError:
|
||||
import warnings
|
||||
warnings.warn(("Falling back to using the subprocess module because subprocess32 isn't"
|
||||
" available. When using the subprocess module, a child process may trigger"
|
||||
" an invalid free(). See SERVER-22219 for more details."),
|
||||
RuntimeWarning)
|
||||
import subprocess
|
||||
else:
|
||||
import subprocess
|
||||
|
||||
from . import pipe
|
||||
from .. import utils
|
||||
|
||||
# Prevent race conditions when starting multiple subprocesses on the same thread.
|
||||
# See https://bugs.python.org/issue2320 for more details.
|
||||
# Attempt to avoid race conditions (e.g. hangs caused by a file descriptor being left open) when
|
||||
# starting subprocesses concurrently from multiple threads by guarding calls to subprocess.Popen()
|
||||
# with a lock. See https://bugs.python.org/issue2320 and https://bugs.python.org/issue12739 as
|
||||
# reports of such hangs.
|
||||
#
|
||||
# This lock probably isn't necessary when both the subprocess32 module and its _posixsubprocess C
|
||||
# extension module are available because either
|
||||
# (a) the pipe2() syscall is available on the platform we're using, so pipes are atomically
|
||||
# created with the FD_CLOEXEC flag set on them, or
|
||||
# (b) the pipe2() syscall isn't available, but the GIL isn't released during the
|
||||
# _posixsubprocess.fork_exec() call or the _posixsubprocess.cloexec_pipe() call.
|
||||
# See https://bugs.python.org/issue7213 for more details.
|
||||
_POPEN_LOCK = threading.Lock()
|
||||
|
||||
# Job objects are the only reliable way to ensure that processes are terminated on Windows.
|
||||
@ -94,12 +124,25 @@ class Process(object):
|
||||
if sys.platform == "win32" and _JOB_OBJECT is not None:
|
||||
creation_flags |= win32process.CREATE_BREAKAWAY_FROM_JOB
|
||||
|
||||
# Use unbuffered I/O pipes to avoid adding delay between when the subprocess writes output
|
||||
# and when the LoggerPipe thread reads it.
|
||||
buffer_size = 0
|
||||
|
||||
# Close file descriptors in the child process before executing the program. This prevents
|
||||
# file descriptors that were inherited due to multiple calls to fork() -- either within one
|
||||
# thread, or concurrently from multiple threads -- from causing another subprocess to wait
|
||||
# for the completion of the newly spawned child process. Closing other file descriptors
|
||||
# isn't supported on Windows when stdout and stderr are redirected.
|
||||
close_fds = (sys.platform != "win32")
|
||||
|
||||
with _POPEN_LOCK:
|
||||
self._process = subprocess.Popen(self.args,
|
||||
env=self.env,
|
||||
creationflags=creation_flags,
|
||||
bufsize=buffer_size,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
stderr=subprocess.PIPE,
|
||||
close_fds=close_fds,
|
||||
env=self.env,
|
||||
creationflags=creation_flags)
|
||||
self.pid = self._process.pid
|
||||
|
||||
self._stdout_pipe = pipe.LoggerPipe(self.logger, logging.INFO, self._process.stdout)
|
||||
|
||||
@ -18,6 +18,14 @@ class StopExecution(ResmokeError):
|
||||
pass
|
||||
|
||||
|
||||
class UserInterrupt(StopExecution):
|
||||
"""
|
||||
Exception that is raised when a user signals resmoke.py to
|
||||
unconditionally stop executing tests.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class TestFailure(ResmokeError):
|
||||
"""
|
||||
Exception that is raised by a hook in the after_test method if it
|
||||
|
||||
@ -17,12 +17,36 @@ from ..utils import queue
|
||||
|
||||
_LOGGER_QUEUE = queue.Queue()
|
||||
|
||||
_FLUSH_THREAD_LOCK = threading.Lock()
|
||||
_FLUSH_THREAD = None
|
||||
|
||||
|
||||
def start_thread():
|
||||
"""
|
||||
Starts the flush thread.
|
||||
"""
|
||||
_FlushThread().start()
|
||||
|
||||
global _FLUSH_THREAD
|
||||
with _FLUSH_THREAD_LOCK:
|
||||
if _FLUSH_THREAD is not None:
|
||||
raise ValueError("FlushThread has already been started")
|
||||
|
||||
_FLUSH_THREAD = _FlushThread()
|
||||
_FLUSH_THREAD.start()
|
||||
|
||||
|
||||
def stop_thread():
|
||||
"""
|
||||
Signals the flush thread to stop and waits until it does.
|
||||
"""
|
||||
|
||||
with _FLUSH_THREAD_LOCK:
|
||||
if _FLUSH_THREAD is None:
|
||||
raise ValueError("FlushThread hasn't been started")
|
||||
|
||||
# Add sentinel value to indicate when there are no more loggers to process.
|
||||
_LOGGER_QUEUE.put(None)
|
||||
_FLUSH_THREAD.join()
|
||||
|
||||
|
||||
def close_later(logger):
|
||||
@ -44,7 +68,7 @@ class _FlushThread(threading.Thread):
|
||||
"""
|
||||
|
||||
threading.Thread.__init__(self, name="FlushThread")
|
||||
# atexit handler is already set up to flush any loggers still in the queue when exiting.
|
||||
# Do not wait to flush the logs if interrupted by the user.
|
||||
self.daemon = True
|
||||
|
||||
def run(self):
|
||||
@ -54,7 +78,13 @@ class _FlushThread(threading.Thread):
|
||||
|
||||
while True:
|
||||
logger = _LOGGER_QUEUE.get()
|
||||
_FlushThread._shutdown_logger(logger)
|
||||
try:
|
||||
if logger is None:
|
||||
# Sentinel value received, so exit.
|
||||
break
|
||||
_FlushThread._shutdown_logger(logger)
|
||||
finally:
|
||||
_LOGGER_QUEUE.task_done()
|
||||
|
||||
@staticmethod
|
||||
def _shutdown_logger(logger):
|
||||
|
||||
@ -84,7 +84,7 @@ class TestGroupExecutor(object):
|
||||
|
||||
# If the user triggered a KeyboardInterrupt, then we should stop.
|
||||
if interrupted:
|
||||
raise errors.StopExecution("Received interrupt from user")
|
||||
raise errors.UserInterrupt("Received interrupt from user")
|
||||
|
||||
sb = [] # String builder.
|
||||
self._test_group.summarize_latest(sb)
|
||||
|
||||
@ -53,11 +53,13 @@ class MasterSlaveFixture(interface.ReplFixture):
|
||||
self.slave = None
|
||||
|
||||
def setup(self):
|
||||
self.master = self._new_mongod_master()
|
||||
if self.master is None:
|
||||
self.master = self._new_mongod_master()
|
||||
self.master.setup()
|
||||
self.port = self.master.port
|
||||
|
||||
self.slave = self._new_mongod_slave()
|
||||
if self.slave is None:
|
||||
self.slave = self._new_mongod_slave()
|
||||
self.slave.setup()
|
||||
|
||||
def await_ready(self):
|
||||
@ -71,9 +73,9 @@ class MasterSlaveFixture(interface.ReplFixture):
|
||||
# Keep retrying this until it times out waiting for replication.
|
||||
def insert_fn(remaining_secs):
|
||||
remaining_millis = int(round(remaining_secs * 1000))
|
||||
client.resmoke.await_ready.insert({"awaiting": "ready"},
|
||||
w=2,
|
||||
wtimeout=remaining_millis)
|
||||
write_concern = pymongo.WriteConcern(w=2, wtimeout=remaining_millis)
|
||||
coll = client.resmoke.get_collection("await_ready", write_concern=write_concern)
|
||||
coll.insert_one({"awaiting": "ready"})
|
||||
|
||||
try:
|
||||
self.retry_until_wtimeout(insert_fn)
|
||||
@ -150,9 +152,9 @@ class MasterSlaveFixture(interface.ReplFixture):
|
||||
# Keep retrying this until it times out waiting for replication.
|
||||
def insert_fn(remaining_secs):
|
||||
remaining_millis = int(round(remaining_secs * 1000))
|
||||
client[db_name].resmoke_await_repl.insert({"awaiting": "repl"},
|
||||
w=2,
|
||||
wtimeout=remaining_millis)
|
||||
write_concern = pymongo.WriteConcern(w=2, wtimeout=remaining_millis)
|
||||
coll = client[db_name].get_collection("await_repl", write_concern=write_concern)
|
||||
coll.insert_one({"awaiting": "repl"})
|
||||
|
||||
try:
|
||||
self.retry_until_wtimeout(insert_fn)
|
||||
|
||||
@ -81,6 +81,9 @@ class ReplicaSetFixture(interface.ReplFixture):
|
||||
member_info = {"_id": i, "host": node.get_connection_string()}
|
||||
if i > 0:
|
||||
member_info["priority"] = 0
|
||||
if i >= 7:
|
||||
# Only 7 nodes in a replica set can vote, so the other members must be non-voting.
|
||||
member_info["votes"] = 0
|
||||
members.append(member_info)
|
||||
initiate_cmd_obj = {"replSetInitiate": {"_id": self.replset_name, "members": members}}
|
||||
|
||||
@ -152,14 +155,15 @@ class ReplicaSetFixture(interface.ReplFixture):
|
||||
self.logger.info("Awaiting replication of insert (w=%d, wtimeout=%d min) to primary on port"
|
||||
" %d", self.num_nodes, interface.ReplFixture.AWAIT_REPL_TIMEOUT_MINS,
|
||||
self.port)
|
||||
client = utils.new_mongo_client(port=self.port)
|
||||
|
||||
# Keep retrying this until it times out waiting for replication.
|
||||
def insert_fn(remaining_secs):
|
||||
remaining_millis = int(round(remaining_secs * 1000))
|
||||
client = utils.new_mongo_client(port=self.port)
|
||||
client.resmoke.await_repl.insert({"awaiting": "repl"},
|
||||
w=self.num_nodes,
|
||||
wtimeout=remaining_millis)
|
||||
write_concern = pymongo.WriteConcern(w=self.num_nodes, wtimeout=remaining_millis)
|
||||
coll = client.resmoke.get_collection("await_repl", write_concern=write_concern)
|
||||
coll.insert_one({"awaiting": "repl"})
|
||||
|
||||
try:
|
||||
self.retry_until_wtimeout(insert_fn)
|
||||
except pymongo.errors.WTimeoutError:
|
||||
|
||||
@ -198,7 +198,7 @@ class CheckReplDBHash(CustomBehavior):
|
||||
self.test_case.return_code = 1
|
||||
test_report.addFailure(self.test_case, sys.exc_info())
|
||||
test_report.stopTest(self.test_case)
|
||||
raise errors.ServerFailure(err.message)
|
||||
raise errors.ServerFailure(err.args[0])
|
||||
except pymongo.errors.WTimeoutError:
|
||||
self.test_case.logger.exception("Awaiting replication timed out.")
|
||||
self.test_case.return_code = 2
|
||||
@ -213,7 +213,7 @@ class CheckReplDBHash(CustomBehavior):
|
||||
"""
|
||||
|
||||
if self.started:
|
||||
self.test_case.logger.exception("The dbhashes matched for all tests.")
|
||||
self.test_case.logger.info("The dbhashes matched for all tests.")
|
||||
self.test_case.return_code = 0
|
||||
test_report.addSuccess(self.test_case)
|
||||
# TestReport.stopTest() has already been called if there was a failure.
|
||||
@ -276,8 +276,11 @@ class CheckReplDBHash(CustomBehavior):
|
||||
list_db_output = secondary_conn.admin.command("listDatabases")
|
||||
secondary_dbs = [db["name"] for db in list_db_output["databases"]]
|
||||
|
||||
# There may be a difference in databases which is not considered an error, when
|
||||
# the database only contains system collections. This difference is only logged
|
||||
# when others are encountered, i.e., success = False.
|
||||
missing_on_primary, missing_on_secondary = CheckReplDBHash._check_difference(
|
||||
set(primary_dbs), set(secondary_dbs), "database", sb)
|
||||
set(primary_dbs), set(secondary_dbs), "database")
|
||||
|
||||
for missing_db in missing_on_secondary:
|
||||
db = primary_conn[missing_db]
|
||||
@ -304,6 +307,7 @@ class CheckReplDBHash(CustomBehavior):
|
||||
# It is only an error if there are any non-system collections in the database,
|
||||
# otherwise it's not well defined if it should exist or not.
|
||||
if non_system_colls:
|
||||
sb.append("Database %s present on secondary but not on primary." % (missing_db))
|
||||
CheckReplDBHash._dump_all_collections(db, non_system_colls, sb)
|
||||
success = False
|
||||
|
||||
@ -358,15 +362,15 @@ class CheckReplDBHash(CustomBehavior):
|
||||
secondary_coll_names = set(secondary_coll_hashes.keys())
|
||||
|
||||
missing_on_primary, missing_on_secondary = CheckReplDBHash._check_difference(
|
||||
primary_coll_names, secondary_coll_names, "collection", sb)
|
||||
primary_coll_names, secondary_coll_names, "collection", sb=sb)
|
||||
|
||||
if missing_on_primary or missing_on_secondary:
|
||||
|
||||
# 'sb' already describes which collections are missing where.
|
||||
for coll_name in missing_on_primary:
|
||||
CheckReplDBHash._dump_all_documents(primary_db, coll_name, sb)
|
||||
for coll_name in missing_on_secondary:
|
||||
CheckReplDBHash._dump_all_documents(secondary_db, coll_name, sb)
|
||||
for coll_name in missing_on_secondary:
|
||||
CheckReplDBHash._dump_all_documents(primary_db, coll_name, sb)
|
||||
return
|
||||
|
||||
for coll_name in primary_coll_names & secondary_coll_names:
|
||||
@ -410,7 +414,7 @@ class CheckReplDBHash(CustomBehavior):
|
||||
collection on the secondary, if any.
|
||||
"""
|
||||
|
||||
codec_options = bson.CodecOptions(document_class=bson.SON)
|
||||
codec_options = bson.CodecOptions(document_class=TypeSensitiveSON)
|
||||
|
||||
primary_coll = primary_db.get_collection(coll_name, codec_options=codec_options)
|
||||
secondary_coll = secondary_db.get_collection(coll_name, codec_options=codec_options)
|
||||
@ -497,7 +501,7 @@ class CheckReplDBHash(CustomBehavior):
|
||||
sb.append("All documents matched.")
|
||||
|
||||
@staticmethod
|
||||
def _check_difference(primary_set, secondary_set, item_type_name, sb):
|
||||
def _check_difference(primary_set, secondary_set, item_type_name, sb=None):
|
||||
"""
|
||||
Returns true if the contents of 'primary_set' and
|
||||
'secondary_set' are identical, and false otherwise. The sets
|
||||
@ -516,8 +520,9 @@ class CheckReplDBHash(CustomBehavior):
|
||||
for item in secondary_set - primary_set:
|
||||
missing_on_primary.add(item)
|
||||
|
||||
CheckReplDBHash._append_differences(
|
||||
missing_on_primary, missing_on_secondary, item_type_name, sb)
|
||||
if sb is not None:
|
||||
CheckReplDBHash._append_differences(
|
||||
missing_on_primary, missing_on_secondary, item_type_name, sb)
|
||||
|
||||
return (missing_on_primary, missing_on_secondary)
|
||||
|
||||
@ -570,8 +575,130 @@ class CheckReplDBHash(CustomBehavior):
|
||||
else:
|
||||
sb.append("No documents in %s.%s." % (database.name, coll_name))
|
||||
|
||||
class TypeSensitiveSON(bson.SON):
|
||||
"""
|
||||
Extends bson.SON to perform additional type-checking of document values
|
||||
to differentiate BSON types.
|
||||
"""
|
||||
|
||||
def items_with_types(self):
|
||||
"""
|
||||
Returns a list of triples. Each triple consists of a field name, a
|
||||
field value, and a field type for each field in the document.
|
||||
"""
|
||||
|
||||
return [(key, self[key], type(self[key])) for key in self]
|
||||
|
||||
def __eq__(self, other):
|
||||
"""
|
||||
Comparison to another TypeSensitiveSON is order-sensitive and
|
||||
type-sensitive while comparison to a regular dictionary ignores order
|
||||
and type mismatches.
|
||||
"""
|
||||
|
||||
if isinstance(other, TypeSensitiveSON):
|
||||
return (len(self) == len(other) and
|
||||
self.items_with_types() == other.items_with_types())
|
||||
|
||||
raise TypeError("TypeSensitiveSON objects cannot be compared to other types")
|
||||
|
||||
class ValidateCollections(CustomBehavior):
|
||||
"""
|
||||
Runs full validation (db.collection.validate(true)) on all collections
|
||||
in all databases on every standalone, or primary mongod. If validation
|
||||
fails (validate.valid), then the validate return object is logged.
|
||||
|
||||
Compatible with all subclasses.
|
||||
"""
|
||||
DEFAULT_FULL = True
|
||||
DEFAULT_SCANDATA = True
|
||||
|
||||
def __init__(self, logger, fixture, full=DEFAULT_FULL, scandata=DEFAULT_SCANDATA):
|
||||
CustomBehavior.__init__(self, logger, fixture)
|
||||
|
||||
if not isinstance(full, bool):
|
||||
raise TypeError("Fixture option full is not specified as type bool")
|
||||
|
||||
if not isinstance(scandata, bool):
|
||||
raise TypeError("Fixture option scandata is not specified as type bool")
|
||||
|
||||
self.test_case = testcases.TestCase(self.logger, "Hook", "#validate#")
|
||||
self.started = False
|
||||
self.full = full
|
||||
self.scandata = scandata
|
||||
|
||||
def after_test(self, test_report):
|
||||
"""
|
||||
After each test, run a full validation on all collections.
|
||||
"""
|
||||
|
||||
try:
|
||||
if not self.started:
|
||||
CustomBehavior.start_dynamic_test(self.test_case, test_report)
|
||||
self.started = True
|
||||
|
||||
sb = [] # String builder.
|
||||
|
||||
# The self.fixture.port can be used for client connection to a
|
||||
# standalone mongod, a replica-set primary, or mongos.
|
||||
# TODO: Run collection validation on all nodes in a replica-set.
|
||||
port = self.fixture.port
|
||||
conn = utils.new_mongo_client(port=port)
|
||||
|
||||
success = ValidateCollections._check_all_collections(
|
||||
conn, sb, self.full, self.scandata)
|
||||
|
||||
if not success:
|
||||
# Adding failures to a TestReport requires traceback information, so we raise
|
||||
# a 'self.test_case.failureException' that we will catch ourselves.
|
||||
self.test_case.logger.info("\n ".join(sb))
|
||||
raise self.test_case.failureException("Collection validation failed")
|
||||
except self.test_case.failureException as err:
|
||||
self.test_case.logger.exception("Collection validation failed")
|
||||
self.test_case.return_code = 1
|
||||
test_report.addFailure(self.test_case, sys.exc_info())
|
||||
test_report.stopTest(self.test_case)
|
||||
raise errors.ServerFailure(err.args[0])
|
||||
|
||||
def after_suite(self, test_report):
|
||||
"""
|
||||
If we get to this point, the #validate# test must have been
|
||||
successful, so add it to the test report.
|
||||
"""
|
||||
|
||||
if self.started:
|
||||
self.test_case.logger.info("Collection validation passed for all tests.")
|
||||
self.test_case.return_code = 0
|
||||
test_report.addSuccess(self.test_case)
|
||||
# TestReport.stopTest() has already been called if there was a failure.
|
||||
test_report.stopTest(self.test_case)
|
||||
|
||||
self.started = False
|
||||
|
||||
@staticmethod
|
||||
def _check_all_collections(conn, sb, full, scandata):
|
||||
"""
|
||||
Returns true if for all databases and collections validate_collection
|
||||
succeeds. Returns false otherwise.
|
||||
|
||||
Logs a message if any database's collection fails validate_collection.
|
||||
"""
|
||||
|
||||
success = True
|
||||
|
||||
for db_name in conn.database_names():
|
||||
for coll_name in conn[db_name].collection_names():
|
||||
try:
|
||||
conn[db_name].validate_collection(coll_name, full=full, scandata=scandata)
|
||||
except pymongo.errors.CollectionInvalid as err:
|
||||
sb.append("Database %s, collection %s failed to validate:\n%s"
|
||||
% (db_name, coll_name, err.args[0]))
|
||||
success = False
|
||||
return success
|
||||
|
||||
|
||||
_CUSTOM_BEHAVIORS = {
|
||||
"CleanEveryN": CleanEveryN,
|
||||
"CheckReplDBHash": CheckReplDBHash,
|
||||
"ValidateCollections": ValidateCollections,
|
||||
}
|
||||
|
||||
@ -86,14 +86,17 @@ class AlarmClock(threading.Thread):
|
||||
Repeatedly calls 'func' with a delay of 'interval' seconds between executions.
|
||||
|
||||
If the timer is snoozed before 'func' is called, then it waits to be reset.
|
||||
After is has been reset, the timer will again wait 'interval' seconds and
|
||||
After it has been reset, the timer will again wait 'interval' seconds and
|
||||
then try to call 'func'.
|
||||
|
||||
If the timer is dismissed, then no subsequent executions of 'func' are made.
|
||||
"""
|
||||
|
||||
with self.lock:
|
||||
while not self.dismissed:
|
||||
while True:
|
||||
with self.lock:
|
||||
if self.dismissed:
|
||||
return
|
||||
|
||||
# Wait for the specified amount of time.
|
||||
self.cond.wait(self.interval)
|
||||
|
||||
@ -112,8 +115,11 @@ class AlarmClock(threading.Thread):
|
||||
self.snoozed = False
|
||||
continue
|
||||
|
||||
# Execute the function.
|
||||
self.func(*self.args, **self.kwargs)
|
||||
# Execute the function after the lock has been released to prevent potential deadlocks
|
||||
# with the invoked function.
|
||||
self.func(*self.args, **self.kwargs)
|
||||
|
||||
# Reacquire the lock.
|
||||
with self.lock:
|
||||
# Ignore snoozes that took place while the function was being executed.
|
||||
self.snoozed = False
|
||||
|
||||
@ -5,6 +5,7 @@ import sys
|
||||
import os
|
||||
import tempfile
|
||||
import urllib2
|
||||
import urlparse
|
||||
import subprocess
|
||||
import tarfile
|
||||
import signal
|
||||
@ -12,8 +13,10 @@ import threading
|
||||
import traceback
|
||||
import shutil
|
||||
import errno
|
||||
from contextlib import closing
|
||||
# To ensure it exists on the system
|
||||
import gzip
|
||||
import zipfile
|
||||
|
||||
#
|
||||
# Useful script for installing multiple versions of MongoDB on a machine
|
||||
@ -73,6 +76,7 @@ class MultiVersionDownloader :
|
||||
return self._links
|
||||
|
||||
def download_links(self):
|
||||
# This href is for community builds; enterprise builds are not browseable.
|
||||
href = "http://dl.mongodb.org/dl/%s/%s" \
|
||||
% (self.platform.lower(), self.arch)
|
||||
|
||||
@ -90,7 +94,7 @@ class MultiVersionDownloader :
|
||||
|
||||
links = {}
|
||||
for line in html.split():
|
||||
match = re.compile("http:\/\/downloads\.mongodb\.org\/%s/mongodb-%s-%s-([^\"]*)\.tgz" \
|
||||
match = re.compile("http:.*/%s/mongodb-%s-%s-([^\"]*)\.(tgz|zip)" \
|
||||
% (self.platform.lower(), self.platform.lower(), self.arch)).search(line)
|
||||
|
||||
if match == None: continue
|
||||
@ -129,6 +133,7 @@ class MultiVersionDownloader :
|
||||
full_version = urls[-1][0]
|
||||
url = urls[-1][1]
|
||||
extract_dir = url.split("/")[-1][:-4]
|
||||
file_suffix = os.path.splitext(urlparse.urlparse(url).path)[1]
|
||||
|
||||
# only download if we don't already have the directory
|
||||
already_downloaded = os.path.isdir(os.path.join( self.install_dir, extract_dir))
|
||||
@ -137,26 +142,27 @@ class MultiVersionDownloader :
|
||||
% (version, full_version, extract_dir)
|
||||
else:
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
temp_file = tempfile.mktemp(suffix=".tgz")
|
||||
temp_file = tempfile.mktemp(suffix=file_suffix)
|
||||
|
||||
data = urllib2.urlopen(url)
|
||||
|
||||
print "Downloading data for version %s (%s)..." % (version, full_version)
|
||||
print "Download url is %s" % url
|
||||
|
||||
with open(temp_file, 'wb') as f:
|
||||
f.write(data.read())
|
||||
print "Uncompressing data for version %s (%s)..." % (version, full_version)
|
||||
|
||||
# Can't use cool with syntax b/c of python 2.6
|
||||
tf = tarfile.open(temp_file, 'r:gz')
|
||||
|
||||
try:
|
||||
tf.extractall(path=temp_dir)
|
||||
except:
|
||||
tf.close()
|
||||
raise
|
||||
|
||||
tf.close()
|
||||
if file_suffix == ".zip":
|
||||
# Support .zip downloads, used for Windows binaries.
|
||||
with zipfile.ZipFile(temp_file) as zf:
|
||||
zf.extractall(temp_dir)
|
||||
elif file_suffix == ".tgz":
|
||||
# Support .tgz downloads, used for Linux binaries.
|
||||
with closing(tarfile.open(temp_file, 'r:gz')) as tf:
|
||||
tf.extractall(path=temp_dir)
|
||||
else:
|
||||
raise Exception("Unsupported file extension %s" % file_suffix)
|
||||
|
||||
temp_install_dir = os.path.join(temp_dir, extract_dir)
|
||||
|
||||
@ -179,11 +185,24 @@ class MultiVersionDownloader :
|
||||
|
||||
for executable in os.listdir(os.path.join(installed_dir, "bin")):
|
||||
|
||||
link_name = "%s-%s" % (executable, version)
|
||||
executable_name, executable_extension = os.path.splitext(executable)
|
||||
link_name = "%s-%s%s" % (executable_name, version, executable_extension)
|
||||
|
||||
try:
|
||||
os.symlink(os.path.join(installed_dir, "bin", executable),\
|
||||
os.path.join(self.link_dir, link_name))
|
||||
executable = os.path.join(installed_dir, "bin", executable)
|
||||
executable_link = os.path.join(self.link_dir, link_name)
|
||||
if os.name == "nt":
|
||||
# os.symlink is not supported on Windows, use a direct method instead.
|
||||
def symlink_ms(source, link_name):
|
||||
import ctypes
|
||||
csl = ctypes.windll.kernel32.CreateSymbolicLinkW
|
||||
csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
|
||||
csl.restype = ctypes.c_ubyte
|
||||
flags = 1 if os.path.isdir(source) else 0
|
||||
if csl(link_name, source.replace('/', '\\'), flags) == 0:
|
||||
raise ctypes.WinError()
|
||||
os.symlink = symlink_ms
|
||||
os.symlink(executable, executable_link)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
pass
|
||||
@ -192,8 +211,9 @@ class MultiVersionDownloader :
|
||||
|
||||
CL_HELP_MESSAGE = \
|
||||
"""
|
||||
Downloads and installs particular mongodb versions (each binary is renamed to include its version)
|
||||
into an install directory and symlinks the binaries with versions to another directory.
|
||||
Downloads and installs particular mongodb versions (each binary is renamed to include its version)
|
||||
into an install directory and symlinks the binaries with versions to another directory. This script
|
||||
only supports community builds, not enterprise builds.
|
||||
|
||||
Usage: setup_multiversion_mongodb.py INSTALL_DIR LINK_DIR PLATFORM_AND_ARCH VERSION1 [VERSION2 VERSION3 ...]
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -10,14 +10,17 @@ post:
|
||||
file_location: src/report.json
|
||||
- command: shell.cleanup
|
||||
- command: shell.exec
|
||||
# destroy the cluster
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
# clean all file to be safe
|
||||
rm -rf *
|
||||
# call terraform destroy twice to avoid AWS timeout
|
||||
yes yes | ./terraform destroy
|
||||
yes yes | ./terraform destroy
|
||||
echo "Cluster DESTROYED."
|
||||
|
||||
functions:
|
||||
"prepare environment":
|
||||
@ -30,7 +33,7 @@ functions:
|
||||
# checkout and setup DSI environment
|
||||
params:
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -v
|
||||
git clone git@github.com:10gen/dsi.git
|
||||
cd dsi
|
||||
@ -44,7 +47,7 @@ functions:
|
||||
script: |
|
||||
cd ./clusters/${cluster}
|
||||
# stage aws credential for terraform
|
||||
../../bin/make_terraform_env.sh ${terraform_key} ${terraform_secret} https://s3.amazonaws.com/mciuploads/longevity/${build_variant}/${revision}/mongodb-${build_id}.tar.gz
|
||||
../../bin/make_terraform_env.sh ${terraform_key} ${terraform_secret} https://s3.amazonaws.com/mciuploads/longevity-v3.2/${build_variant}/${revision}/mongodb-${build_id}.tar.gz
|
||||
# generate aws private key file
|
||||
echo "${ec2_pem}" > ../../keys/aws.pem
|
||||
chmod 400 ../../keys/aws.pem
|
||||
@ -57,7 +60,7 @@ functions:
|
||||
silent: true
|
||||
script: |
|
||||
# to create a mongod EC2 cluster
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
./setup-cluster.sh
|
||||
@ -69,13 +72,13 @@ functions:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
# configure mongodb cluster with wiredTiger
|
||||
USE_CSRS=${use_csrs} ../../bin/config-${setup}.sh mongodb ${storageEngine} ${numShard}
|
||||
echo "${cluster} MongoDB Cluster STARTED."
|
||||
|
||||
|
||||
"run test":
|
||||
- command: shell.exec
|
||||
params:
|
||||
@ -110,7 +113,7 @@ functions:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
# destroy the EC2 cluster
|
||||
@ -129,7 +132,7 @@ functions:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}/reports
|
||||
# move additional file here
|
||||
@ -152,7 +155,7 @@ functions:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: reports.tgz
|
||||
remote_file: longevity/${build_variant}/${revision}/logs/${test}-${build_id}.${ext|tgz}
|
||||
remote_file: longevity-v3.2/${build_variant}/${revision}/logs/${test}-${build_id}.${ext|tgz}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
@ -171,14 +174,14 @@ functions:
|
||||
file: "src/tags.json"
|
||||
name: "perf"
|
||||
- command: shell.exec
|
||||
# post_run_check.py and longevity_override.json for DSI tests is part of dsi repo in dsi/bin
|
||||
# post_run_check.py and longevity_override.json for DSI tests are part of dsi repo
|
||||
type : test
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
python -u ../dsi/bin/post_run_check.py --rev ${revision} -f history.json -t tags.json --refTag 3.0.6-Baseline --overrideFile ../dsi/bin/longevity_override.json --project_id mongo-longevity --task_name ${task_name} --variant ${build_variant}
|
||||
python -u ../dsi/analysis/post_run_check.py --rev ${revision} -f history.json -t tags.json --refTag 3.0.6-Baseline --overrideFile ../dsi/analysis/v3.2/longevity_override.json --project_id mongo-longevity --task_name ${task_name} --variant ${build_variant}
|
||||
|
||||
|
||||
tasks:
|
||||
@ -212,7 +215,7 @@ tasks:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/mongodb.tar.gz
|
||||
remote_file: longevity/${build_variant}/${revision}/mongodb-${build_id}.tar.gz
|
||||
remote_file: longevity-v3.2/${build_variant}/${revision}/mongodb-${build_id}.tar.gz
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
@ -221,7 +224,7 @@ tasks:
|
||||
- name: shard_cluster_test
|
||||
depends_on:
|
||||
- name: compile
|
||||
exec_timeout_secs: 216000
|
||||
exec_timeout_secs: 216000
|
||||
commands:
|
||||
- func: "prepare environment"
|
||||
- func: "bring up 3 shard cluster"
|
||||
@ -235,8 +238,8 @@ tasks:
|
||||
- func: "upload log file"
|
||||
vars:
|
||||
test: "ycsb"
|
||||
- func: "analyze"
|
||||
- func: "destroy cluster"
|
||||
- func: "analyze"
|
||||
|
||||
- name: shard_cluster_CSRS_test
|
||||
depends_on:
|
||||
@ -255,8 +258,8 @@ tasks:
|
||||
- func: "upload log file"
|
||||
vars:
|
||||
test: "ycsb"
|
||||
- func: "analyze"
|
||||
- func: "destroy cluster"
|
||||
- func: "analyze"
|
||||
|
||||
- name: shard_cluster_MMAPv1_test
|
||||
depends_on:
|
||||
@ -275,13 +278,13 @@ tasks:
|
||||
- func: "upload log file"
|
||||
vars:
|
||||
test: "ycsb"
|
||||
- func: "analyze"
|
||||
- func: "destroy cluster"
|
||||
- func: "analyze"
|
||||
|
||||
buildvariants:
|
||||
- name: linux-wt-shard
|
||||
display_name: Linux WT Shard
|
||||
batchtime: 10080 # 1 week
|
||||
batchtime: 40320 # 4 weeks
|
||||
expansions:
|
||||
compile_flags: -j$(grep -c ^processor /proc/cpuinfo) CC=/opt/mongodbtoolchain/bin/gcc CXX=/opt/mongodbtoolchain/bin/g++ --release
|
||||
setup: shard
|
||||
@ -299,7 +302,7 @@ buildvariants:
|
||||
|
||||
- name: linux-wt-shard-csrs
|
||||
display_name: Linux WT Shard CSRS
|
||||
batchtime: 10080 # 1 week
|
||||
batchtime: 20160 # 2 weeks
|
||||
expansions:
|
||||
compile_flags: -j$(grep -c ^processor /proc/cpuinfo) CC=/opt/mongodbtoolchain/bin/gcc CXX=/opt/mongodbtoolchain/bin/g++ --release
|
||||
setup: shard
|
||||
@ -314,17 +317,17 @@ buildvariants:
|
||||
distros:
|
||||
- rhel55
|
||||
- name: shard_cluster_CSRS_test
|
||||
|
||||
|
||||
|
||||
- name: linux-mmapv1-shard
|
||||
display_name: Linux MMAPv1 Shard
|
||||
batchtime: 10080 # 1 week
|
||||
batchtime: 40320 # 4 week
|
||||
expansions:
|
||||
compile_flags: -j$(grep -c ^processor /proc/cpuinfo) CC=/opt/mongodbtoolchain/bin/gcc CXX=/opt/mongodbtoolchain/bin/g++ --release
|
||||
setup: shard
|
||||
cluster: longevity
|
||||
numShard: 3
|
||||
storageEngine: mmapv1
|
||||
storageEngine: mmapv1
|
||||
|
||||
run_on:
|
||||
- "linux-64-amzn-perf-longevity"
|
||||
|
||||
14832
etc/override.json
14832
etc/override.json
File diff suppressed because it is too large
Load Diff
178
etc/perf.yml
178
etc/perf.yml
@ -11,10 +11,6 @@ pre:
|
||||
exit 0
|
||||
|
||||
post:
|
||||
- command: "json.send"
|
||||
params:
|
||||
name: "perf"
|
||||
file: "src/perf.json"
|
||||
- command: attach.results
|
||||
params:
|
||||
file_location: src/report.json
|
||||
@ -27,6 +23,13 @@ post:
|
||||
- command: shell.cleanup
|
||||
|
||||
functions:
|
||||
"download analysis scripts":
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
set -v
|
||||
rm -rf ./dsi
|
||||
git clone git@github.com:10gen/dsi.git
|
||||
"start server":
|
||||
- command: shell.exec
|
||||
params:
|
||||
@ -37,33 +40,33 @@ functions:
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: perf/${version_id}/${revision}/mongod-${version_id}
|
||||
remote_file: perf-3.2/${version_id}/${revision}/mongod-${version_id}
|
||||
bucket: mciuploads
|
||||
local_file: src/mongod
|
||||
- command: s3.get
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: perf/${version_id}/${revision}/mongo-${version_id}
|
||||
remote_file: perf-3.2/${version_id}/${revision}/mongo-${version_id}
|
||||
bucket: mciuploads
|
||||
local_file: src/mongo
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -v
|
||||
chmod +x mongod
|
||||
chmod +x mongo
|
||||
git clone https://github.com/mongodb/mongo-perf perf
|
||||
cd perf
|
||||
git checkout r20150814
|
||||
git clone https://github.com/mongodb/mongo-perf perf-3.2
|
||||
cd perf-3.2
|
||||
git checkout r20160127
|
||||
- command: shell.exec
|
||||
params:
|
||||
background: true
|
||||
working_dir: src
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
mkdir -p ./dbdata
|
||||
${mongod_exec_wrapper} ./mongod --dbpath ./dbdata ${mongod_flags}
|
||||
@ -78,13 +81,6 @@ functions:
|
||||
./mongo --eval "if(db.isMaster().isreplicaset){rs.initiate(); assert.soon(function(){return db.isMaster().ismaster}, 'no primary')}"
|
||||
echo "MONGOD STARTED."
|
||||
"compare":
|
||||
- command: s3.get
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: perf/${version_id}/${revision}/compare.py-${version_id}
|
||||
bucket: mciuploads
|
||||
local_file: src/compare.py
|
||||
- command: json.get
|
||||
params:
|
||||
task: ${compare_task}
|
||||
@ -106,22 +102,12 @@ functions:
|
||||
set -o verbose
|
||||
virtualenv ./venv
|
||||
source ./venv/bin/activate
|
||||
python compare.py -b stand.json -c node.json -t ${threshold}
|
||||
python -u ../dsi/analysis/compare.py -b stand.json -c node.json
|
||||
- command: "json.send"
|
||||
params:
|
||||
name: "perf"
|
||||
file: "src/perf.json"
|
||||
"analyze":
|
||||
- command: s3.get
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: perf/${version_id}/${revision}/perf_regression_check.py-${version_id}
|
||||
bucket: mciuploads
|
||||
local_file: src/perf_regression_check.py
|
||||
- command: s3.get
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: perf/${version_id}/${revision}/override.json-${version_id}
|
||||
bucket: mciuploads
|
||||
local_file: src/override.json
|
||||
- command: shell.exec
|
||||
params:
|
||||
working_dir: src
|
||||
@ -150,7 +136,7 @@ functions:
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
source ./venv/bin/activate
|
||||
python perf_regression_check.py -f history.json --rev ${revision} -t tags.json --refTag 3.0.6-Baseline --overrideFile override.json --variant ${build_variant} --threshold 0.10 --threadThreshold 0.15
|
||||
python ../dsi/analysis/perf_regression_check.py -f history.json --rev ${revision} -t tags.json --refTag 3.0.6-Baseline --overrideFile ../dsi/analysis/v3.2/perf_override.json --variant ${build_variant} --threshold 0.10 --threadThreshold 0.15
|
||||
"run perf tests":
|
||||
- command: shell.exec
|
||||
params:
|
||||
@ -168,14 +154,14 @@ functions:
|
||||
set -e
|
||||
set -v
|
||||
source ./venv/bin/activate
|
||||
cd perf
|
||||
cd perf-3.2
|
||||
# give mongod a few seconds to start up so that we can connect.
|
||||
sleep 5
|
||||
${perf_exec_wrapper} python benchrun.py --shell ../mongo -t ${threads} --trialCount 5 -f testcases/*.js --includeFilter ${includeFilter1} --includeFilter ${includeFilter2} --excludeFilter ${excludeFilter} --out perf.json --exclude-testbed
|
||||
- command: "json.send"
|
||||
params:
|
||||
name: "perf"
|
||||
file: "src/perf/perf.json"
|
||||
file: "src/perf-3.2/perf.json"
|
||||
|
||||
tasks:
|
||||
- name: compile
|
||||
@ -199,7 +185,7 @@ tasks:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/mongod
|
||||
remote_file: perf/${version_id}/${revision}/mongod-${version_id}
|
||||
remote_file: perf-3.2/${version_id}/${revision}/mongod-${version_id}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/octet-stream}
|
||||
@ -209,42 +195,11 @@ tasks:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/mongo
|
||||
remote_file: perf/${version_id}/${revision}/mongo-${version_id}
|
||||
remote_file: perf-3.2/${version_id}/${revision}/mongo-${version_id}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/octet-stream}
|
||||
display_name: mongo
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/buildscripts/perf_regression_check.py
|
||||
remote_file: perf/${version_id}/${revision}/perf_regression_check.py-${version_id}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/octet-stream}
|
||||
display_name: perf_regression_check.py
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/buildscripts/compare.py
|
||||
remote_file: perf/${version_id}/${revision}/compare.py-${version_id}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/octet-stream}
|
||||
display_name: compare.py
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/etc/override.json
|
||||
remote_file: perf/${version_id}/${revision}/override.json-${version_id}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/octet-stream}
|
||||
display_name: override.json
|
||||
|
||||
- name: query
|
||||
depends_on:
|
||||
- variant: linux-wt-standalone
|
||||
@ -254,9 +209,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "query"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "single_threaded"
|
||||
threads : "1 2 4 8"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: where
|
||||
depends_on:
|
||||
@ -267,9 +223,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "where"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "single_threaded"
|
||||
threads : "1 2 4 8"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: update
|
||||
depends_on:
|
||||
@ -280,9 +237,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "update"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "single_threaded"
|
||||
threads : "1 2 4 8"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: insert
|
||||
depends_on:
|
||||
@ -293,9 +251,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "insert"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "single_threaded"
|
||||
threads : "1 2 4 8"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: geo
|
||||
depends_on:
|
||||
@ -306,9 +265,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "geo"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "single_threaded"
|
||||
threads : "1 2 4 8"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: misc
|
||||
depends_on:
|
||||
@ -319,9 +279,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "command multi remove mixed"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "single_threaded"
|
||||
threads : "1 2 4 8"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: singleThreaded
|
||||
depends_on:
|
||||
@ -332,9 +293,10 @@ tasks:
|
||||
- func: "run perf tests"
|
||||
vars:
|
||||
includeFilter1: "single_threaded"
|
||||
includeFilter2 : "core regression"
|
||||
includeFilter2 : "core regression"
|
||||
excludeFilter : "none"
|
||||
threads : "1"
|
||||
- func: "download analysis scripts"
|
||||
- func: "analyze"
|
||||
- name: singleThreaded-wt-repl-comp
|
||||
depends_on:
|
||||
@ -347,12 +309,13 @@ tasks:
|
||||
variant : linux-wt-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "singleThreaded"
|
||||
variant1 : "linux-wt-standalone"
|
||||
variant2 : "linux-wt-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: insert-wt-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -364,12 +327,13 @@ tasks:
|
||||
variant : linux-wt-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "insert"
|
||||
variant1 : "linux-wt-standalone"
|
||||
variant2 : "linux-wt-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: update-wt-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -381,12 +345,13 @@ tasks:
|
||||
variant : linux-wt-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "update"
|
||||
variant1 : "linux-wt-standalone"
|
||||
variant2 : "linux-wt-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: misc-wt-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -398,12 +363,13 @@ tasks:
|
||||
variant : linux-wt-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "misc"
|
||||
variant1 : "linux-wt-standalone"
|
||||
variant2 : "linux-wt-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: singleThreaded-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -415,12 +381,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "singleThreaded"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: query-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -432,12 +399,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "query"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: where-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -449,12 +417,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "where"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: geo-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -466,12 +435,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "geo"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: insert-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -483,12 +453,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "insert"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: update-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -500,12 +471,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "update"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: misc-wt-mmap-standalone-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -517,12 +489,13 @@ tasks:
|
||||
variant : linux-mmap-standalone
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "misc"
|
||||
variant2 : "linux-wt-standalone"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: singleThreaded-wt-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -534,12 +507,13 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "singleThreaded"
|
||||
variant2 : "linux-wt-repl"
|
||||
variant1 : "linux-mmap-repl"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: insert-wt-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -551,12 +525,13 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "insert"
|
||||
variant2 : "linux-wt-repl"
|
||||
variant1 : "linux-mmap-repl"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: update-wt-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -568,29 +543,31 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "update"
|
||||
variant2 : "linux-wt-repl"
|
||||
variant1 : "linux-mmap-repl"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: misc-wt-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
variant : linux-wt-standalone
|
||||
status : "*"
|
||||
- name : misc
|
||||
variant : linux-wt-repl
|
||||
status : "*"
|
||||
- name : misc
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "misc"
|
||||
variant2 : "linux-wt-repl"
|
||||
variant1 : "linux-mmap-repl"
|
||||
threshold : 85
|
||||
- func: "analyze"
|
||||
- name: singleThreaded-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -602,12 +579,13 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "singleThreaded"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
variant2 : "linux-mmap-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: insert-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -619,12 +597,13 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "insert"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
variant2 : "linux-mmap-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: update-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -636,12 +615,13 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "update"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
variant2 : "linux-mmap-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
- name: misc-mmap-repl-comp
|
||||
depends_on:
|
||||
- name : compile
|
||||
@ -653,12 +633,13 @@ tasks:
|
||||
variant : linux-mmap-repl
|
||||
status : "*"
|
||||
commands:
|
||||
- func: "download analysis scripts"
|
||||
- func: "compare"
|
||||
vars:
|
||||
compare_task : "misc"
|
||||
variant1 : "linux-mmap-standalone"
|
||||
variant2 : "linux-mmap-repl"
|
||||
threshold : 75
|
||||
- func: "analyze"
|
||||
|
||||
buildvariants:
|
||||
- name: linux-wt-standalone
|
||||
@ -708,7 +689,7 @@ buildvariants:
|
||||
display_name: 1-Node ReplSet Linux WT
|
||||
batchtime: 360 # 6 hours
|
||||
expansions:
|
||||
compile_flags: *compile_flags
|
||||
compile_flags: *compile_flags
|
||||
mongod_exec_wrapper: *exec_wrapper
|
||||
perf_exec_wrapper: *perf_wrapper
|
||||
mongod_flags: "--replSet=test --storageEngine=wiredTiger --logpath ./mongod.log --fork --syncdelay 0 --nojournal --setParameter ttlMonitorEnabled=false --wiredTigerCacheSizeGB 16 --oplogSize 100000 --setParameter diagnosticDataCollectionEnabled=false "
|
||||
@ -725,7 +706,7 @@ buildvariants:
|
||||
display_name: 1-Node ReplSet Linux MMAPv1
|
||||
batchtime: 360 # 6 hours
|
||||
expansions:
|
||||
compile_flags: *compile_flags
|
||||
compile_flags: *compile_flags
|
||||
mongod_exec_wrapper: *exec_wrapper
|
||||
perf_exec_wrapper: *perf_wrapper
|
||||
mongod_flags: "--replSet=test --storageEngine=mmapv1 --logpath ./mongod.log --fork --syncdelay 0 --nojournal --setParameter ttlMonitorEnabled=false --oplogSize 100000 --setParameter diagnosticDataCollectionEnabled=false"
|
||||
@ -788,4 +769,3 @@ buildvariants:
|
||||
- name: insert-wt-mmap-repl-comp
|
||||
- name: update-wt-mmap-repl-comp
|
||||
- name: misc-wt-mmap-repl-comp
|
||||
|
||||
|
||||
@ -5,10 +5,6 @@ pre:
|
||||
- command: shell.track
|
||||
|
||||
post:
|
||||
- command: "json.send"
|
||||
params:
|
||||
name: "perf"
|
||||
file: "src/perf.json"
|
||||
- command: attach.results
|
||||
params:
|
||||
file_location: src/report.json
|
||||
@ -18,7 +14,7 @@ post:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
if [ ! -f "test.success" ]; then yes yes | ./terraform destroy; fi
|
||||
@ -35,7 +31,7 @@ functions:
|
||||
# checkout dsi code
|
||||
params:
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -v
|
||||
git clone git@github.com:10gen/dsi.git
|
||||
cd dsi
|
||||
@ -50,7 +46,7 @@ functions:
|
||||
cd ./clusters/${cluster}
|
||||
# stage aws credential for terraform
|
||||
cp ../../terraform/* .
|
||||
../../bin/make_terraform_env.sh ${terraform_key} ${terraform_secret} https://s3.amazonaws.com/mciuploads/dsi/${version_id}/${revision}/mongod-${version_id}.tar.gz
|
||||
../../bin/make_terraform_env.sh ${terraform_key} ${terraform_secret} https://s3.amazonaws.com/mciuploads/dsi-v3.2/${version_id}/${revision}/mongod-${version_id}.tar.gz
|
||||
# generate aws private key file
|
||||
echo "${ec2_pem}" > ../../keys/aws.pem
|
||||
chmod 400 ../../keys/aws.pem
|
||||
@ -74,7 +70,7 @@ functions:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: "dsi/clusters/${cluster}/cluster_config.tgz"
|
||||
remote_file: dsi/${build_variant}/${revision}/cluster_configs/cluster_config-${build_id}.tgz
|
||||
remote_file: dsi-v3.2/${build_variant}/${revision}/cluster_configs/cluster_config-${build_id}.tgz
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
@ -85,7 +81,7 @@ functions:
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: dsi/${build_variant}/${revision}/cluster_configs/cluster_config-${build_id}.tgz
|
||||
remote_file: dsi-v3.2/${build_variant}/${revision}/cluster_configs/cluster_config-${build_id}.tgz
|
||||
bucket: mciuploads
|
||||
local_file: "dsi/clusters/${cluster}/cluster_config.tgz"
|
||||
- command: shell.exec
|
||||
@ -104,7 +100,7 @@ functions:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
# configure mongodb cluster with wiredTiger
|
||||
@ -153,7 +149,7 @@ functions:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}
|
||||
# destroy the EC2 cluster
|
||||
@ -169,7 +165,7 @@ functions:
|
||||
params:
|
||||
working_dir: dsi
|
||||
script: |
|
||||
set -e
|
||||
set -e
|
||||
set -o verbose
|
||||
cd ./clusters/${cluster}/reports
|
||||
# move additional file here
|
||||
@ -193,7 +189,7 @@ functions:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: reports.tgz
|
||||
remote_file: dsi/${build_variant}/${revision}/logs/${test}-${build_id}.${ext|tgz}
|
||||
remote_file: dsi-v3.2/${build_variant}/${revision}/${task_id}/${version_id}/logs/${test}-${build_id}.${ext|tgz}
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
@ -212,28 +208,30 @@ functions:
|
||||
file: "src/tags.json"
|
||||
name: "perf"
|
||||
- command: shell.exec
|
||||
# post_run_check.py and override.json for DSI tests is part of dsi repo in dsi/bin
|
||||
# post_run_check.py and override.json for DSI tests are part of dsi repo
|
||||
type : test
|
||||
params:
|
||||
working_dir: src
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
python -u ../dsi/bin/post_run_check.py --rev ${revision} -f history.json -t tags.json --refTag 3.0.6-Baseline --overrideFile ../dsi/bin/override.json --project_id sys-perf --task_name ${task_name} --variant ${build_variant}
|
||||
python -u ../dsi/analysis/post_run_check.py --rev ${revision} -f history.json -t tags.json --refTag 3.0.6-Baseline --overrideFile ../dsi/analysis/v3.2/system_perf_override.json --project_id sys-perf --task_name ${task_name} --variant ${build_variant}
|
||||
|
||||
"compare":
|
||||
- command: shell.exec
|
||||
params:
|
||||
script: |
|
||||
rm -rf ./*
|
||||
mkdir src
|
||||
- command: s3.get
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
remote_file: dsi/${revision}/compare.py
|
||||
bucket: mciuploads
|
||||
local_file: src/compare.py
|
||||
script: |
|
||||
set -o verbose
|
||||
rm -rf ./src
|
||||
mkdir src
|
||||
- command: shell.exec
|
||||
# checkout dsi code
|
||||
params:
|
||||
script: |
|
||||
set -e
|
||||
set -v
|
||||
rm -rf ./dsi
|
||||
git clone git@github.com:10gen/dsi.git
|
||||
- command: json.get
|
||||
params:
|
||||
task: ${compare_task}
|
||||
@ -253,9 +251,11 @@ functions:
|
||||
script: |
|
||||
set -o errexit
|
||||
set -o verbose
|
||||
python -u compare.py -b standalone.json -c oplog.json -t ${threshold}
|
||||
|
||||
|
||||
python -u ../dsi/analysis/compare.py -b standalone.json -c oplog.json
|
||||
- command: "json.send"
|
||||
params:
|
||||
name: "perf"
|
||||
file: "src/perf.json"
|
||||
|
||||
#######################################
|
||||
# Tasks #
|
||||
@ -288,21 +288,11 @@ tasks:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/mongodb.tar.gz
|
||||
remote_file: dsi/${version_id}/${revision}/mongod-${version_id}.tar.gz
|
||||
remote_file: dsi-v3.2/${version_id}/${revision}/mongod-${version_id}.tar.gz
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/x-gzip}
|
||||
display_name: mongodb.tar.gz
|
||||
- command: s3.put
|
||||
params:
|
||||
aws_key: ${aws_key}
|
||||
aws_secret: ${aws_secret}
|
||||
local_file: src/buildscripts/compare.py
|
||||
remote_file: dsi/${revision}/compare.py
|
||||
bucket: mciuploads
|
||||
permissions: public-read
|
||||
content_type: ${content_type|application/octet-stream}
|
||||
display_name: compare.py
|
||||
|
||||
|
||||
# The industry_benchmarks_WT task runs the "bring up cluster" task and is
|
||||
@ -386,7 +376,7 @@ tasks:
|
||||
- func: "upload log file"
|
||||
vars:
|
||||
test: "core_workloads_MMAPv1"
|
||||
- func: "destroy cluster"
|
||||
- func: "destroy cluster"
|
||||
- func: "analyze"
|
||||
|
||||
- name: industry_benchmarks_WT_oplog_comp
|
||||
@ -403,7 +393,7 @@ tasks:
|
||||
compare_task: "industry_benchmarks_WT"
|
||||
variant1: "linux-standalone"
|
||||
variant2: "linux-1-node-replSet"
|
||||
threshold: 70
|
||||
- func: "analyze"
|
||||
|
||||
- name: industry_benchmarks_MMAPv1_oplog_comp
|
||||
depends_on:
|
||||
@ -421,7 +411,7 @@ tasks:
|
||||
compare_task: "industry_benchmarks_MMAPv1"
|
||||
variant1: "linux-standalone"
|
||||
variant2: "linux-1-node-replSet"
|
||||
threshold: 70
|
||||
- func: "analyze"
|
||||
|
||||
- name: core_workloads_WT_oplog_comp
|
||||
depends_on:
|
||||
@ -439,7 +429,7 @@ tasks:
|
||||
compare_task: "core_workloads_WT"
|
||||
variant1: "linux-standalone"
|
||||
variant2: "linux-1-node-replSet"
|
||||
threshold: 70
|
||||
- func: "analyze"
|
||||
|
||||
- name: core_workloads_MMAPv1_oplog_comp
|
||||
depends_on:
|
||||
@ -457,8 +447,7 @@ tasks:
|
||||
compare_task: "core_workloads_MMAPv1"
|
||||
variant1: "linux-standalone"
|
||||
variant2: "linux-1-node-replSet"
|
||||
threshold: 70
|
||||
|
||||
- func: "analyze"
|
||||
|
||||
#######################################
|
||||
# Buildvariants #
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
// Test the db.auth() shell helper.
|
||||
//
|
||||
// This test requires users to persist across a restart.
|
||||
// @tags: [requires_persistence]
|
||||
|
||||
var conn = MongoRunner.runMongod({ smallfiles: ""});
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ function ClusterSpawnHelper(clusterType, startWithAuth) {
|
||||
else {
|
||||
replSetTest.awaitReplication();
|
||||
}
|
||||
this.conn = replSetTest.getMaster();
|
||||
this.conn = replSetTest.getPrimary();
|
||||
this.connString = replSetTest.getURL();
|
||||
}
|
||||
else {
|
||||
|
||||
@ -2031,6 +2031,7 @@ var authCommandsLib = {
|
||||
{
|
||||
runOnDb: firstDbName,
|
||||
roles: {
|
||||
backup: 1,
|
||||
dbAdmin: 1,
|
||||
dbAdminAnyDatabase: 1,
|
||||
dbOwner: 1,
|
||||
@ -2047,6 +2048,7 @@ var authCommandsLib = {
|
||||
{
|
||||
runOnDb: secondDbName,
|
||||
roles: {
|
||||
backup: 1,
|
||||
dbAdminAnyDatabase: 1,
|
||||
clusterMonitor: 1,
|
||||
clusterAdmin: 1,
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
// MapReduce executed by a read-only user when --auth enabled should only be able to use inline mode. Other modes require writing to an output collection which is not allowed. SERVER-3345
|
||||
//
|
||||
// This test requires users to persist across a restart.
|
||||
// @tags: [requires_persistence]
|
||||
|
||||
baseName = "jstests_mr_auth";
|
||||
dbName = "test";
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
/*
|
||||
/**
|
||||
* Regression test for SERVER-4892.
|
||||
*
|
||||
* Verify that a client can delete cursors that it creates, when mongod is running with "auth"
|
||||
* enabled.
|
||||
*
|
||||
* This test requires users to persist across a restart.
|
||||
* @tags: [requires_persistence]
|
||||
*/
|
||||
|
||||
var baseName = 'jstests_auth_server4892';
|
||||
|
||||
@ -95,7 +95,7 @@ m0.getDB("db1").createRole({
|
||||
rstest.add();
|
||||
rstest.reInitiate();
|
||||
|
||||
rstest.getMaster().getDB("db1").createRole({
|
||||
rstest.getPrimary().getDB("db1").createRole({
|
||||
role: "r3",
|
||||
roles: [ "r1", "r2" ],
|
||||
privileges: [
|
||||
@ -116,8 +116,8 @@ rstest.nodes.forEach(function (node) {
|
||||
});
|
||||
|
||||
// Verify that updating roles propagates.
|
||||
rstest.getMaster().getDB("db1").revokeRolesFromRole("r1", [ "read" ], { w: 2 });
|
||||
rstest.getMaster().getDB("db1").grantRolesToRole("r1", [ "dbAdmin" ], { w: 2 });
|
||||
rstest.getPrimary().getDB("db1").revokeRolesFromRole("r1", [ "read" ], { w: 2 });
|
||||
rstest.getPrimary().getDB("db1").grantRolesToRole("r1", [ "dbAdmin" ], { w: 2 });
|
||||
rstest.nodes.forEach(function (node) {
|
||||
var role = node.getDB("db1").getRole("r1");
|
||||
assert.eq(1, role.roles.length, node);
|
||||
@ -125,7 +125,7 @@ rstest.nodes.forEach(function (node) {
|
||||
});
|
||||
|
||||
// Verify that dropping roles propagates.
|
||||
rstest.getMaster().getDB("db1").dropRole("r2", { w: 2});
|
||||
rstest.getPrimary().getDB("db1").dropRole("r2", { w: 2});
|
||||
rstest.nodes.forEach(function (node) {
|
||||
assert.eq(null, node.getDB("db1").getRole("r2"));
|
||||
var role = node.getDB("db1").getRole("r3");
|
||||
@ -137,8 +137,8 @@ rstest.nodes.forEach(function (node) {
|
||||
});
|
||||
|
||||
// Verify that dropping the admin database propagates.
|
||||
assert.commandWorked(rstest.getMaster().getDB("admin").dropDatabase());
|
||||
assert.commandWorked(rstest.getMaster().getDB("admin").getLastErrorObj(2));
|
||||
assert.commandWorked(rstest.getPrimary().getDB("admin").dropDatabase());
|
||||
assert.commandWorked(rstest.getPrimary().getDB("admin").getLastErrorObj(2));
|
||||
rstest.nodes.forEach(function (node) {
|
||||
var roles = node.getDB("db1").getRoles();
|
||||
assert.eq(0, roles.length, node);
|
||||
@ -146,7 +146,7 @@ rstest.nodes.forEach(function (node) {
|
||||
|
||||
// Verify that applyOps commands propagate.
|
||||
// NOTE: This section of the test depends on the oplog and roles schemas.
|
||||
assert.commandWorked(rstest.getMaster().getDB("admin").runCommand({ applyOps: [
|
||||
assert.commandWorked(rstest.getPrimary().getDB("admin").runCommand({ applyOps: [
|
||||
{
|
||||
op: "c",
|
||||
ns: "admin.$cmd",
|
||||
@ -214,7 +214,7 @@ assert.commandWorked(rstest.getMaster().getDB("admin").runCommand({ applyOps: [
|
||||
}
|
||||
] }));
|
||||
|
||||
assert.commandWorked(rstest.getMaster().getDB("admin").getLastErrorObj(2));
|
||||
assert.commandWorked(rstest.getPrimary().getDB("admin").getLastErrorObj(2));
|
||||
rstest.nodes.forEach(function (node) {
|
||||
var role = node.getDB("db1").getRole("t1");
|
||||
assert.eq(1, role.roles.length, node);
|
||||
|
||||
@ -62,6 +62,12 @@ function runTest(conn) {
|
||||
db.createUser({user: 'user2', pwd: 'pwd', roles: [{role: '',
|
||||
db: 'test'}]});
|
||||
});
|
||||
assert.throws(function() {
|
||||
db.createUser({user: 'null\u0000char', pwd: 'pwd', roles: []});
|
||||
});
|
||||
assert.throws(function() {
|
||||
db.createUser({user: 'null\0char', pwd: 'pwd', roles: []});
|
||||
});
|
||||
// Regression test for SERVER-17125
|
||||
assert.throws(function() {
|
||||
db.getSiblingDB('$external').createUser({user: '', roles: []});
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
load('jstests/concurrency/fsm_libs/runner.js');
|
||||
|
||||
var dir = 'jstests/concurrency/fsm_workloads';
|
||||
|
||||
var blacklist = [
|
||||
].map(function(file) { return dir + '/' + file; });
|
||||
|
||||
// SERVER-16196 re-enable executing workloads with master-slave replication
|
||||
// runWorkloadsSerially(ls(dir).filter(function(file) {
|
||||
// return !Array.contains(blacklist, file);
|
||||
// }), { masterSlave: true });
|
||||
@ -1,82 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
load('jstests/concurrency/fsm_libs/runner.js');
|
||||
|
||||
var dir = 'jstests/concurrency/fsm_workloads';
|
||||
|
||||
var blacklist = [
|
||||
// Disabled due to known bugs
|
||||
'distinct.js', // SERVER-13116 distinct isn't sharding aware
|
||||
'distinct_noindex.js', // SERVER-13116 distinct isn't sharding aware
|
||||
'distinct_projection.js', // SERVER-13116 distinct isn't sharding aware
|
||||
'drop_database.js', // SERVER-17397 Drops of sharded namespaces may not fully succeed
|
||||
|
||||
// Disabled due to SERVER-3645, '.count() can be wrong on sharded collections'.
|
||||
// This bug is problematic for these workloads because they assert on count() values:
|
||||
'agg_match.js',
|
||||
'count.js',
|
||||
'count_limit_skip.js',
|
||||
'count_noindex.js',
|
||||
|
||||
// Disabled due to SERVER-20057, 'Concurrent, sharded mapReduces can fail when temporary
|
||||
// namespaces collide across mongos processes'
|
||||
'map_reduce_drop.js',
|
||||
'map_reduce_inline.js',
|
||||
'map_reduce_merge.js',
|
||||
'map_reduce_merge_nonatomic.js',
|
||||
'map_reduce_reduce.js',
|
||||
'map_reduce_reduce_nonatomic.js',
|
||||
'map_reduce_replace.js',
|
||||
'map_reduce_replace_nonexistent.js',
|
||||
|
||||
// Disabled due to MongoDB restrictions and/or workload restrictions
|
||||
|
||||
// These workloads sometimes trigger 'Could not lock auth data update lock'
|
||||
// errors because the AuthorizationManager currently waits for only five
|
||||
// seconds to acquire the lock for authorization documents
|
||||
'auth_create_role.js',
|
||||
'auth_create_user.js',
|
||||
'auth_drop_role.js',
|
||||
'auth_drop_user.js',
|
||||
|
||||
'agg_group_external.js', // uses >100MB of data, which can overwhelm test hosts
|
||||
'agg_sort_external.js', // uses >100MB of data, which can overwhelm test hosts
|
||||
'compact.js', // compact can only be run against a standalone mongod
|
||||
'compact_simultaneous_padding_bytes.js', // compact can only be run against a mongod
|
||||
'convert_to_capped_collection.js', // convertToCapped can't be run on mongos processes
|
||||
'convert_to_capped_collection_index.js', // convertToCapped can't be run on mongos processes
|
||||
'findAndModify_remove_queue.js', // remove cannot be {} for findAndModify
|
||||
'findAndModify_update_collscan.js', // findAndModify requires a shard key
|
||||
'findAndModify_update_queue.js', // findAndModify requires a shard key
|
||||
'group.js', // the group command cannot be issued against a sharded cluster
|
||||
'group_cond.js', // the group command cannot be issued against a sharded cluster
|
||||
'indexed_insert_eval.js', // eval doesn't work with sharded collections
|
||||
'indexed_insert_eval_nolock.js', // eval doesn't work with sharded collections
|
||||
'plan_cache_drop_database.js', // cannot ensureIndex after dropDatabase without sharding first
|
||||
'remove_single_document.js', // our .remove(query, {justOne: true}) calls lack shard keys
|
||||
'remove_single_document_eval.js', // eval doesn't work with sharded collections
|
||||
'remove_single_document_eval_nolock.js', // eval doesn't work with sharded collections
|
||||
|
||||
// The rename_* workloads are disabled since renameCollection doesn't work with sharded
|
||||
// collections
|
||||
'rename_capped_collection_chain.js',
|
||||
'rename_capped_collection_dbname_chain.js',
|
||||
'rename_capped_collection_dbname_droptarget.js',
|
||||
'rename_capped_collection_droptarget.js',
|
||||
'rename_collection_chain.js',
|
||||
'rename_collection_dbname_chain.js',
|
||||
'rename_collection_dbname_droptarget.js',
|
||||
'rename_collection_droptarget.js',
|
||||
|
||||
'update_simple_eval.js', // eval doesn't work with sharded collections
|
||||
'update_simple_eval_nolock.js', // eval doesn't work with sharded collections
|
||||
'update_upsert_multi.js', // our update queries lack shard keys
|
||||
'update_upsert_multi_noindex.js', // our update queries lack shard keys
|
||||
'yield_and_hashed.js', // stagedebug can only be run against a standalone mongod
|
||||
'yield_and_sorted.js', // stagedebug can only be run against a standalone mongod
|
||||
].map(function(file) { return dir + '/' + file; });
|
||||
|
||||
// SERVER-16196 re-enable executing workloads against sharded clusters
|
||||
// runWorkloadsSerially(ls(dir).filter(function(file) {
|
||||
// return !Array.contains(blacklist, file);
|
||||
// }), { sharded: true, useLegacyConfigServers: false });
|
||||
@ -102,6 +102,7 @@ var Cluster = function(options) {
|
||||
var st;
|
||||
|
||||
var initialized = false;
|
||||
var clusterStartTime;
|
||||
|
||||
var _conns = {
|
||||
mongos: [],
|
||||
@ -119,6 +120,7 @@ var Cluster = function(options) {
|
||||
|
||||
this.setup = function setup() {
|
||||
var verbosityLevel = 0;
|
||||
const REPL_SET_INITIATE_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
|
||||
if (initialized) {
|
||||
throw new Error('cluster has already been initialized');
|
||||
@ -144,6 +146,11 @@ var Cluster = function(options) {
|
||||
oplogSize: 1024,
|
||||
verbose: verbosityLevel
|
||||
};
|
||||
shardConfig.rsOptions = {
|
||||
// Specify a longer timeout for replSetInitiate, to ensure that
|
||||
// slow hardware has sufficient time for file pre-allocation.
|
||||
initiateTimeout: REPL_SET_INITIATE_TIMEOUT_MS,
|
||||
}
|
||||
}
|
||||
|
||||
st = new ShardingTest(shardConfig);
|
||||
@ -196,8 +203,10 @@ var Cluster = function(options) {
|
||||
var rst = new ReplSetTest(replSetConfig);
|
||||
rst.startSet();
|
||||
|
||||
// Send the replSetInitiate command and wait for initiation
|
||||
rst.initiate();
|
||||
// Send the replSetInitiate command and wait for initialization, with an increased
|
||||
// timeout. This should provide sufficient time for slow hardware, where files may need
|
||||
// to be pre-allocated.
|
||||
rst.initiate(null, null, REPL_SET_INITIATE_TIMEOUT_MS);
|
||||
rst.awaitSecondaryNodes();
|
||||
|
||||
conn = rst.getPrimary();
|
||||
@ -238,6 +247,7 @@ var Cluster = function(options) {
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
clusterStartTime = new Date();
|
||||
|
||||
options.setupFunctions.mongod.forEach(this.executeOnMongodNodes);
|
||||
if (options.sharded) {
|
||||
@ -254,10 +264,8 @@ var Cluster = function(options) {
|
||||
};
|
||||
|
||||
this.executeOnMongodNodes = function executeOnMongodNodes(fn) {
|
||||
if (!initialized) {
|
||||
throw new Error('cluster must be initialized before functions can be executed ' +
|
||||
'against it');
|
||||
}
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
|
||||
if (!fn || typeof(fn) !== 'function' || fn.length !== 1) {
|
||||
throw new Error('mongod function must be a function that takes a db as an argument');
|
||||
}
|
||||
@ -267,10 +275,8 @@ var Cluster = function(options) {
|
||||
};
|
||||
|
||||
this.executeOnMongosNodes = function executeOnMongosNodes(fn) {
|
||||
if (!initialized) {
|
||||
throw new Error('cluster must be initialized before functions can be executed ' +
|
||||
'against it');
|
||||
}
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
|
||||
if (!fn || typeof(fn) !== 'function' || fn.length !== 1) {
|
||||
throw new Error('mongos function must be a function that takes a db as an argument');
|
||||
}
|
||||
@ -280,21 +286,17 @@ var Cluster = function(options) {
|
||||
};
|
||||
|
||||
this.teardown = function teardown() {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
options.teardownFunctions.mongod.forEach(this.executeOnMongodNodes);
|
||||
};
|
||||
|
||||
this.getDB = function getDB(dbName) {
|
||||
if (!initialized) {
|
||||
throw new Error('cluster has not been initialized yet');
|
||||
}
|
||||
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
return conn.getDB(dbName);
|
||||
};
|
||||
|
||||
this.getHost = function getHost() {
|
||||
if (!initialized) {
|
||||
throw new Error('cluster has not been initialized yet');
|
||||
}
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
|
||||
// Alternate mongos connections for sharded clusters
|
||||
if (this.isSharded()) {
|
||||
@ -317,6 +319,7 @@ var Cluster = function(options) {
|
||||
};
|
||||
|
||||
this.shardCollection = function shardCollection() {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
assert(this.isSharded(), 'cluster is not sharded');
|
||||
st.shardColl.apply(st, arguments);
|
||||
};
|
||||
@ -350,6 +353,8 @@ var Cluster = function(options) {
|
||||
// }
|
||||
// }
|
||||
this.getSerializedCluster = function getSerializedCluster() {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
|
||||
// TODO: Add support for non-sharded clusters.
|
||||
if (!this.isSharded()) {
|
||||
return '';
|
||||
@ -397,11 +402,13 @@ var Cluster = function(options) {
|
||||
}
|
||||
|
||||
this.startBalancer = function startBalancer() {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
assert(this.isSharded(), 'cluster is not sharded');
|
||||
st.startBalancer();
|
||||
};
|
||||
|
||||
this.stopBalancer = function stopBalancer() {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
assert(this.isSharded(), 'cluster is not sharded');
|
||||
st.stopBalancer();
|
||||
};
|
||||
@ -411,6 +418,7 @@ var Cluster = function(options) {
|
||||
};
|
||||
|
||||
this.awaitReplication = function awaitReplication(message) {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
if (this.isReplication()) {
|
||||
var wc = {
|
||||
writeConcern: {
|
||||
@ -463,8 +471,8 @@ var Cluster = function(options) {
|
||||
|
||||
hashes.slaves.forEach(function(slaveHashes) {
|
||||
assert.commandWorked(slaveHashes);
|
||||
assert.eq(masterHashes.numCollections,
|
||||
slaveHashes.numCollections,
|
||||
assert.eq(Object.keys(masterHashes.collections).length,
|
||||
Object.keys(slaveHashes.collections).length,
|
||||
message + ' dbHash number of collections in db ' +
|
||||
dbInfo.name + ' ' + tojson(hashes));
|
||||
|
||||
@ -492,6 +500,35 @@ var Cluster = function(options) {
|
||||
}, this);
|
||||
}, this);
|
||||
};
|
||||
|
||||
this.recordConfigServerData = function recordConfigServerData(configServer) {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
assert(this.isSharded(), 'cluster is not sharded');
|
||||
|
||||
var data = {};
|
||||
var configDB = configServer.getDB('config');
|
||||
|
||||
// We record the contents of the 'lockpings' and 'locks' collections to make it easier to
|
||||
// debug issues with distributed locks in the sharded cluster.
|
||||
data.lockpings = configDB.lockpings.find({ ping: { $gte: clusterStartTime } }).toArray();
|
||||
|
||||
// We suppress some fields from the result set to reduce the amount of data recorded.
|
||||
data.locks = configDB.locks.find({ when: { $gte: clusterStartTime } },
|
||||
{ process: 0, ts: 0 }).toArray();
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
this.recordAllConfigServerData = function recordAllConfigServerData() {
|
||||
assert(initialized, 'cluster must be initialized first');
|
||||
assert(this.isSharded(), 'cluster is not sharded');
|
||||
|
||||
var data = {};
|
||||
st._configServers.forEach(config =>
|
||||
(data[config.host] = this.recordConfigServerData(config)));
|
||||
|
||||
return data;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -45,12 +45,10 @@ var fsm = (function() {
|
||||
currentState = nextState;
|
||||
}
|
||||
|
||||
// Null out the workload connection cache and perform garbage collection to clean up,
|
||||
// i.e., close, the open connections.
|
||||
if (args.passConnectionCache) {
|
||||
connCache.mongos.forEach(conn => conn = null);
|
||||
connCache.config.forEach(conn => conn = null);
|
||||
|
||||
var shardNames = Object.keys(connCache.shards);
|
||||
shardNames.forEach(name => connCache.shards[name].forEach(conn => conn = null));
|
||||
connCache = null;
|
||||
gc();
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,36 +258,39 @@ var runner = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function WorkloadFailure(err, stack, kind) {
|
||||
function WorkloadFailure(err, stack, tid, header) {
|
||||
this.err = err;
|
||||
this.stack = stack;
|
||||
this.kind = kind;
|
||||
this.tid = tid;
|
||||
this.header = header;
|
||||
|
||||
this.format = function format() {
|
||||
return this.kind + '\n' + this.err + '\n\n' + this.stack;
|
||||
return this.header + '\n' + this.err + '\n\n' + this.stack;
|
||||
};
|
||||
}
|
||||
|
||||
function throwError(workerErrs) {
|
||||
// Returns an array containing all unique values from the stackTraces array,
|
||||
// their corresponding number of occurrences in the stackTraces array, and
|
||||
// the associated thread ids (tids).
|
||||
function freqCount(stackTraces, tids) {
|
||||
var uniqueStackTraces = [];
|
||||
var associatedTids = [];
|
||||
|
||||
// Returns an array containing all unique values from the specified array
|
||||
// and their corresponding number of occurrences in the original array.
|
||||
function freqCount(arr) {
|
||||
var unique = [];
|
||||
var freqs = [];
|
||||
|
||||
arr.forEach(function(item) {
|
||||
var i = unique.indexOf(item);
|
||||
stackTraces.forEach(function(item, stackTraceIndex) {
|
||||
var i = uniqueStackTraces.indexOf(item);
|
||||
if (i < 0) {
|
||||
unique.push(item);
|
||||
freqs.push(1);
|
||||
uniqueStackTraces.push(item);
|
||||
associatedTids.push(new Set([tids[stackTraceIndex]]));
|
||||
} else {
|
||||
freqs[i]++;
|
||||
associatedTids[i].add(tids[stackTraceIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
return unique.map(function(value, i) {
|
||||
return { value: value, freq: freqs[i] };
|
||||
return uniqueStackTraces.map(function(value, i) {
|
||||
return { value: value,
|
||||
freq: associatedTids[i].size,
|
||||
tids: Array.from(associatedTids[i]) };
|
||||
});
|
||||
}
|
||||
|
||||
@ -302,8 +305,10 @@ var runner = (function() {
|
||||
return num + ' ' + str + suffix;
|
||||
}
|
||||
|
||||
function prepareMsg(stackTraces) {
|
||||
var uniqueTraces = freqCount(stackTraces);
|
||||
function prepareMsg(workerErrs) {
|
||||
var stackTraces = workerErrs.map(e => e.format());
|
||||
var stackTids = workerErrs.map(e => e.tid);
|
||||
var uniqueTraces = freqCount(stackTraces, stackTids);
|
||||
var numUniqueTraces = uniqueTraces.length;
|
||||
|
||||
// Special case message when threads all have the same trace
|
||||
@ -312,21 +317,19 @@ var runner = (function() {
|
||||
indent(uniqueTraces[0].value, 8);
|
||||
}
|
||||
|
||||
var summary = pluralize('thread', stackTraces.length) + ' threw ' +
|
||||
numUniqueTraces + ' different exceptions:\n\n';
|
||||
var summary = pluralize('exception', stackTraces.length) + ' were thrown, ' +
|
||||
numUniqueTraces + ' of which were unique:\n\n';
|
||||
|
||||
return summary + uniqueTraces.map(function(obj) {
|
||||
var line = pluralize('thread', obj.freq) + ' threw\n';
|
||||
var line = pluralize('thread', obj.freq) +
|
||||
' with tids ' + JSON.stringify(obj.tids) +
|
||||
' threw\n';
|
||||
return indent(line + obj.value, 8);
|
||||
}).join('\n\n');
|
||||
}
|
||||
|
||||
if (workerErrs.length > 0) {
|
||||
var stackTraces = workerErrs.map(function(e) {
|
||||
return e.format();
|
||||
});
|
||||
|
||||
var err = new Error(prepareMsg(stackTraces) + '\n');
|
||||
var err = new Error(prepareMsg(workerErrs) + '\n');
|
||||
|
||||
// Avoid having any stack traces omitted from the logs
|
||||
var maxLogLine = 10 * 1024; // 10KB
|
||||
@ -396,13 +399,15 @@ var runner = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
function loadWorkloadContext(workloads, context, executionOptions) {
|
||||
function loadWorkloadContext(workloads, context, executionOptions, applyMultipliers) {
|
||||
workloads.forEach(function(workload) {
|
||||
load(workload); // for $config
|
||||
assert.neq('undefined', typeof $config, '$config was not defined by ' + workload);
|
||||
context[workload] = { config: parseConfig($config) };
|
||||
context[workload].config.iterations *= executionOptions.iterationMultiplier;
|
||||
context[workload].config.threadCount *= executionOptions.threadMultiplier;
|
||||
if (applyMultipliers) {
|
||||
context[workload].config.iterations *= executionOptions.iterationMultiplier;
|
||||
context[workload].config.threadCount *= executionOptions.threadMultiplier;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -422,7 +427,7 @@ var runner = (function() {
|
||||
jsTest.log('End of schedule');
|
||||
}
|
||||
|
||||
function cleanupWorkload(workload, context, cluster, errors, kind, dbHashBlacklist) {
|
||||
function cleanupWorkload(workload, context, cluster, errors, header, dbHashBlacklist) {
|
||||
// Returns true if the workload's teardown succeeds and false if the workload's
|
||||
// teardown fails.
|
||||
|
||||
@ -434,23 +439,48 @@ var runner = (function() {
|
||||
// before the workload's teardown method is called.
|
||||
cluster.checkDbHashes(dbHashBlacklist, 'before workload teardown');
|
||||
} catch (e) {
|
||||
errors.push(new WorkloadFailure(e.toString(), e.stack,
|
||||
kind + ' checking consistency on secondaries'));
|
||||
errors.push(new WorkloadFailure(e.toString(), e.stack, 'main',
|
||||
header + ' checking consistency on secondaries'));
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
teardownWorkload(workload, context, cluster);
|
||||
} catch (e) {
|
||||
errors.push(new WorkloadFailure(e.toString(), e.stack, kind + ' Teardown'));
|
||||
errors.push(new WorkloadFailure(e.toString(), e.stack, 'main', header + ' Teardown'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function runWorkloadGroup(threadMgr, workloads, context, cluster, clusterOptions,
|
||||
executionMode, executionOptions, errors, maxAllowedThreads,
|
||||
dbHashBlacklist) {
|
||||
function recordConfigServerData(cluster, workloads, configServerData, errors) {
|
||||
const CONFIG_DATA_LENGTH = 3;
|
||||
|
||||
if (cluster.isSharded()) {
|
||||
var newData;
|
||||
try {
|
||||
newData = cluster.recordAllConfigServerData();
|
||||
} catch (e) {
|
||||
var failureType = 'Config Server Data Collection';
|
||||
errors.push(new WorkloadFailure(e.toString(), e.stack, 'main', failureType));
|
||||
return;
|
||||
}
|
||||
|
||||
newData.previousWorkloads = workloads;
|
||||
newData.time = (new Date()).toISOString();
|
||||
configServerData.push(newData);
|
||||
|
||||
// Limit the amount of data recorded to avoid logging too much info when a test
|
||||
// fails.
|
||||
while (configServerData.length > CONFIG_DATA_LENGTH) {
|
||||
configServerData.shift();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function runWorkloadGroup(threadMgr, workloads, context, cluster, clusterOptions, executionMode,
|
||||
executionOptions, errors, maxAllowedThreads, dbHashBlacklist,
|
||||
configServerData) {
|
||||
var cleanup = [];
|
||||
var teardownFailed = false;
|
||||
var startTime = Date.now(); // Initialize in case setupWorkload fails below.
|
||||
@ -488,7 +518,8 @@ var runner = (function() {
|
||||
// Threads must be joined before destruction, so do this
|
||||
// even in the presence of exceptions.
|
||||
errors.push(...threadMgr.joinAll().map(e =>
|
||||
new WorkloadFailure(e.err, e.stack, 'Foreground')));
|
||||
new WorkloadFailure(e.err, e.stack, e.tid,
|
||||
'Foreground ' + e.workloads.join(' '))));
|
||||
}
|
||||
} finally {
|
||||
// Call each foreground workload's teardown function. After all teardowns have completed
|
||||
@ -501,6 +532,8 @@ var runner = (function() {
|
||||
totalTime = Date.now() - startTime;
|
||||
jsTest.log('Workload(s) completed in ' + totalTime + ' ms: ' +
|
||||
workloads.join(' '));
|
||||
|
||||
recordConfigServerData(cluster, workloads, configServerData, errors);
|
||||
}
|
||||
|
||||
// Only drop the collections/databases if all the workloads ran successfully.
|
||||
@ -556,13 +589,14 @@ var runner = (function() {
|
||||
globalAssertLevel = assertLevel;
|
||||
|
||||
var context = {};
|
||||
loadWorkloadContext(workloads, context, executionOptions);
|
||||
loadWorkloadContext(workloads, context, executionOptions, true /* applyMultipliers */);
|
||||
var threadMgr = new ThreadManager(clusterOptions, executionMode);
|
||||
|
||||
var bgContext = {};
|
||||
var bgWorkloads = executionOptions.backgroundWorkloads;
|
||||
loadWorkloadContext(bgWorkloads, bgContext, executionOptions);
|
||||
var bgThreadMgr = new ThreadManager(clusterOptions, { composed: false });
|
||||
loadWorkloadContext(bgWorkloads, bgContext, executionOptions,
|
||||
false /* applyMultipliers */);
|
||||
var bgThreadMgr = new ThreadManager(clusterOptions);
|
||||
|
||||
var cluster = new Cluster(clusterOptions);
|
||||
if (cluster.isSharded()) {
|
||||
@ -591,6 +625,7 @@ var runner = (function() {
|
||||
Random.setRandomSeed(clusterOptions.seed);
|
||||
var bgCleanup = [];
|
||||
var errors = [];
|
||||
var configServerData = [];
|
||||
|
||||
try {
|
||||
prepareCollections(bgWorkloads, bgContext, cluster, clusterOptions, executionOptions);
|
||||
@ -637,15 +672,16 @@ var runner = (function() {
|
||||
});
|
||||
|
||||
// Run the next group of workloads in the schedule.
|
||||
runWorkloadGroup(threadMgr, workloads, groupContext, cluster,
|
||||
clusterOptions, executionMode, executionOptions,
|
||||
errors, maxAllowedThreads, dbHashBlacklist);
|
||||
runWorkloadGroup(threadMgr, workloads, groupContext, cluster, clusterOptions,
|
||||
executionMode, executionOptions, errors, maxAllowedThreads,
|
||||
dbHashBlacklist, configServerData);
|
||||
});
|
||||
} finally {
|
||||
// Set a flag so background threads know to terminate.
|
||||
bgThreadMgr.markAllForTermination();
|
||||
errors.push(...bgThreadMgr.joinAll().map(e =>
|
||||
new WorkloadFailure(e.err, e.stack, 'Background')));
|
||||
new WorkloadFailure(e.err, e.stack, e.tid,
|
||||
'Background ' + e.workloads.join(' '))));
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
@ -660,7 +696,13 @@ var runner = (function() {
|
||||
// the foreground and background workloads. IterationEnd errors are ignored because
|
||||
// they are thrown when the background workloads are instructed by the thread
|
||||
// manager to terminate.
|
||||
throwError(errors.filter(e => (e.err.startsWith('IterationEnd:') === false)));
|
||||
var workloadErrors = errors.filter(e => !e.err.startsWith('IterationEnd:'));
|
||||
|
||||
if (cluster.isSharded() && workloadErrors.length) {
|
||||
jsTest.log('Config Server Data:\n' + tojsononeline(configServerData));
|
||||
}
|
||||
|
||||
throwError(workloadErrors);
|
||||
} finally {
|
||||
cluster.teardown();
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ load('jstests/concurrency/fsm_libs/worker_thread.js'); // for workerThread
|
||||
* Helper for spawning and joining worker threads.
|
||||
*/
|
||||
|
||||
var ThreadManager = function(clusterOptions, executionMode) {
|
||||
var ThreadManager = function(clusterOptions, executionMode = { composed: false }) {
|
||||
if (!(this instanceof ThreadManager)) {
|
||||
return new ThreadManager(clusterOptions, executionMode);
|
||||
}
|
||||
|
||||
@ -105,7 +105,13 @@ var workerThread = (function() {
|
||||
return { ok: 1 };
|
||||
} catch(e) {
|
||||
args.errorLatch.countDown();
|
||||
return { ok: 0, err: e.toString(), stack: e.stack };
|
||||
return {
|
||||
ok: 0,
|
||||
err: e.toString(),
|
||||
stack: e.stack,
|
||||
tid: args.tid,
|
||||
workloads: workloads,
|
||||
};
|
||||
}
|
||||
} finally {
|
||||
// Avoid retention of connection object
|
||||
|
||||
@ -35,6 +35,14 @@ function makeCapped($config, $super) {
|
||||
// failures due to collection truncation
|
||||
globalAssertLevel = AssertLevel.ALWAYS;
|
||||
$super.states.find.apply(this, arguments);
|
||||
} catch (e) {
|
||||
if (e.message.indexOf('CappedPositionLost') >= 0) {
|
||||
// Ignore errors when a cursor's position in the capped collection is deleted.
|
||||
// Reads from the beginning of a capped collection are not guaranteed to succeed
|
||||
// when there are concurrent inserts that cause a truncation.
|
||||
return;
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
globalAssertLevel = oldAssertLevel;
|
||||
}
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
*
|
||||
* This workload was designed to reproduce SERVER-15892.
|
||||
*/
|
||||
load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1
|
||||
|
||||
var $config = (function() {
|
||||
|
||||
var states = {
|
||||
@ -22,20 +24,41 @@ var $config = (function() {
|
||||
update: function update(db, collName) {
|
||||
var updateDoc = { $inc: {} };
|
||||
updateDoc.$inc[this.fieldName] = 1;
|
||||
db[collName].findAndModify({
|
||||
|
||||
var res = db.runCommand({
|
||||
findAndModify: collName,
|
||||
query: { _id: 'findAndModify_inc' },
|
||||
update: updateDoc
|
||||
});
|
||||
++this.count;
|
||||
assertAlways.commandWorked(res);
|
||||
|
||||
// If the document was invalidated during a yield, then we wouldn't have modified it.
|
||||
// The "findAndModify" command returns a null value in this case. See SERVER-22002 for
|
||||
// more details.
|
||||
if (isMongod(db) && !isMMAPv1(db)) {
|
||||
// For storage engines other than MMAPv1, if the document is modified by another
|
||||
// thread during a yield, then the operation is retried internally. We never expect
|
||||
// to see a null value returned by the "findAndModify" command when it is known that
|
||||
// a matching document exists in the collection.
|
||||
assertWhenOwnColl(res.value !== null, 'query spec should have matched a document');
|
||||
}
|
||||
|
||||
if (res.value !== null) {
|
||||
++this.count;
|
||||
}
|
||||
},
|
||||
|
||||
find: function find(db, collName) {
|
||||
var docs = db[collName].find().toArray();
|
||||
assertWhenOwnColl.eq(1, docs.length);
|
||||
assertWhenOwnColl((function() {
|
||||
assertWhenOwnColl(() => {
|
||||
var doc = docs[0];
|
||||
assertWhenOwnColl.eq(this.count, doc[this.fieldName]);
|
||||
}).bind(this));
|
||||
if (doc.hasOwnProperty(this.fieldName)) {
|
||||
assertWhenOwnColl.eq(this.count, doc[this.fieldName]);
|
||||
} else {
|
||||
assertWhenOwnColl.eq(this.count, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
*
|
||||
* This workload was designed to reproduce SERVER-18304.
|
||||
*/
|
||||
load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1
|
||||
|
||||
var $config = (function() {
|
||||
|
||||
var data = {
|
||||
@ -65,10 +67,15 @@ var $config = (function() {
|
||||
assertAlways.commandWorked(res);
|
||||
|
||||
var doc = res.value;
|
||||
assertWhenOwnColl.neq(
|
||||
doc, null, 'findAndModify should have found and removed a matching document');
|
||||
if (isMongod(db) && !isMMAPv1(db)) {
|
||||
// MMAPv1 does not automatically retry if there was a conflict, so it is expected
|
||||
// that it may return null in the case of a conflict. All other storage engines
|
||||
// should automatically retry the operation, and thus should never return null.
|
||||
assertWhenOwnColl.neq(
|
||||
doc, null, 'findAndModify should have found and removed a matching document');
|
||||
}
|
||||
if (doc !== null) {
|
||||
this.saveDocId.call(this, db, collName, doc._id);
|
||||
this.saveDocId(db, collName, doc._id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,10 +113,16 @@ var $config = (function() {
|
||||
var ownedDB = db.getSiblingDB(db.getName() + this.uniqueDBName);
|
||||
|
||||
if (this.opName === 'removed') {
|
||||
// Each findAndModify should remove exactly one document, and this.numDocs ==
|
||||
// this.iterations * this.threadCount, so there should not be any documents remaining.
|
||||
assertWhenOwnColl.eq(
|
||||
db[collName].find().itcount(), 0, 'Expected all documents to have been removed');
|
||||
if (isMongod(db) && !isMMAPv1(db)) {
|
||||
// On storage engines other than MMAPv1, each findAndModify should remove exactly
|
||||
// one document. This is not true on MMAPv1 since it will not automatically retry a
|
||||
// findAndModify when there is a conflict, indicating there were no matches instead.
|
||||
// Since this.numDocs == this.iterations * this.threadCount, there should not be any
|
||||
// documents remaining.
|
||||
assertWhenOwnColl.eq(db[collName].find().itcount(),
|
||||
0,
|
||||
'Expected all documents to have been removed');
|
||||
}
|
||||
}
|
||||
|
||||
assertWhenOwnColl(function() {
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
*/
|
||||
load('jstests/concurrency/fsm_libs/extend_workload.js'); // for extendWorkload
|
||||
load('jstests/concurrency/fsm_workloads/findAndModify_remove_queue.js'); // for $config
|
||||
load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1
|
||||
|
||||
var $config = extendWorkload($config, function($config, $super) {
|
||||
|
||||
@ -43,10 +44,15 @@ var $config = extendWorkload($config, function($config, $super) {
|
||||
assertAlways.commandWorked(res);
|
||||
|
||||
var doc = res.value;
|
||||
assertWhenOwnColl.neq(
|
||||
doc, null, 'findAndModify should have found and updated a matching document');
|
||||
if (isMongod(db) && !isMMAPv1(db)) {
|
||||
// MMAPv1 does not automatically retry if there was a conflict, so it is expected
|
||||
// that it may return null in the case of a conflict. All other storage engines
|
||||
// should automatically retry the operation, and thus should never return null.
|
||||
assertWhenOwnColl.neq(
|
||||
doc, null, 'findAndModify should have found and updated a matching document');
|
||||
}
|
||||
if (doc !== null) {
|
||||
this.saveDocId.call(this, db, collName, doc._id);
|
||||
this.saveDocId(db, collName, doc._id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -49,9 +49,15 @@ var $config = (function() {
|
||||
assertWhenOwnColl.neq(null, doc);
|
||||
assertWhenOwnColl(doc.hasOwnProperty('arr'),
|
||||
'doc should have contained a field named "arr": ' + tojson(doc));
|
||||
assertWhenOwnColl.contains(value, doc.arr,
|
||||
"doc.arr doesn't contain value (" + value +
|
||||
') after $push: ' + tojson(doc.arr));
|
||||
|
||||
// If the document was invalidated during a yield, then we may not have updated
|
||||
// anything. The $push operator always modifies the matched document, so if we
|
||||
// matched something, then we must have updated it.
|
||||
if (res.nMatched > 0) {
|
||||
assertWhenOwnColl.contains(value, doc.arr,
|
||||
"doc.arr doesn't contain value (" + value +
|
||||
') after $push: ' + tojson(doc.arr));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -65,9 +71,16 @@ var $config = (function() {
|
||||
var doc = db[collName].findOne({ _id: docIndex });
|
||||
assertWhenOwnColl(function() {
|
||||
assertWhenOwnColl.neq(null, doc);
|
||||
assertWhenOwnColl.eq(-1, doc.arr.indexOf(value),
|
||||
'doc.arr contains removed value (' + value +
|
||||
') after $pull: ' + tojson(doc.arr));
|
||||
|
||||
// If the document was invalidated during a yield, then we may not have updated
|
||||
// anything. If the update matched a document, then the $pull operator would have
|
||||
// removed all occurrences of 'value' from the array (meaning that there should be
|
||||
// none left).
|
||||
if (res.nMatched > 0) {
|
||||
assertWhenOwnColl.eq(-1, doc.arr.indexOf(value),
|
||||
'doc.arr contains removed value (' + value +
|
||||
') after $pull: ' + tojson(doc.arr));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -49,16 +49,23 @@ var $config = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
++this.count;
|
||||
// The $inc operator always modifies the matched document, so if we matched something,
|
||||
// then we must have updated it.
|
||||
this.count += (res.nMatched >= 1);
|
||||
},
|
||||
|
||||
find: function find(db, collName) {
|
||||
var docs = db[collName].find().toArray();
|
||||
assertWhenOwnColl.eq(1, docs.length);
|
||||
assertWhenOwnColl((function() {
|
||||
assertWhenOwnColl(() => {
|
||||
// If the document hasn't been updated at all, then the field won't exist.
|
||||
var doc = docs[0];
|
||||
assertWhenOwnColl.eq(this.count, doc[this.fieldName]);
|
||||
}).bind(this));
|
||||
if (doc.hasOwnProperty(this.fieldName)) {
|
||||
assertWhenOwnColl.eq(this.count, doc[this.fieldName]);
|
||||
} else {
|
||||
assertWhenOwnColl.eq(this.count, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -54,7 +54,10 @@ var $config = (function() {
|
||||
}
|
||||
|
||||
docs.forEach(function(doc) {
|
||||
assertWhenOwnColl.lte(doc[this.fieldName], this.count);
|
||||
// If the document hasn't been updated at all, then the field won't exist.
|
||||
if (doc.hasOwnProperty(this.fieldName)) {
|
||||
assertWhenOwnColl.lte(doc[this.fieldName], this.count);
|
||||
}
|
||||
assertWhenOwnColl.lt(doc._id, this.docCount);
|
||||
}, this);
|
||||
}
|
||||
|
||||
@ -6,14 +6,29 @@
|
||||
* Does updates that replace an entire document.
|
||||
* The collection has indexes on some but not all fields.
|
||||
*/
|
||||
load('jstests/concurrency/fsm_workload_helpers/server_types.js'); // for isMongod and isMMAPv1
|
||||
|
||||
var $config = (function() {
|
||||
|
||||
// explicitly pass db to avoid accidentally using the global `db`
|
||||
function assertResult(db, res) {
|
||||
assertAlways.eq(0, res.nUpserted, tojson(res));
|
||||
assertWhenOwnColl.eq(1, res.nMatched, tojson(res));
|
||||
|
||||
if (isMongod(db) && !isMMAPv1(db)) {
|
||||
// For non-MMAPv1 storage engines we can make a stong assertion that exactly one
|
||||
// document was matched.
|
||||
assertWhenOwnColl.eq(res.nMatched, 1, tojson(res));
|
||||
} else {
|
||||
// It's possible to match zero documents with MMAPv1 because the update can skip a
|
||||
// document that was invalidated during a yield.
|
||||
assertWhenOwnColl.contains(res.nMatched, [0, 1], tojson(res));
|
||||
}
|
||||
|
||||
if (db.getMongo().writeMode() === 'commands') {
|
||||
// It's possible that we replaced the document with its current contents, making the
|
||||
// update a no-op.
|
||||
assertWhenOwnColl.contains(res.nModified, [0, 1], tojson(res));
|
||||
assertAlways.lte(res.nModified, res.nMatched, tojson(res));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -61,6 +61,58 @@
|
||||
'applyOps should fail on non-"n" operation type with empty "ns" field value'
|
||||
);
|
||||
|
||||
// Missing 'o' field value in an operation of type 'i' on 'system.indexes' collection.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes'}]}),
|
||||
ErrorCodes.NoSuchKey,
|
||||
'applyOps should fail on system.indexes insert operation without "o" field');
|
||||
|
||||
// Non-object 'o' field value in an operation of type 'i' on 'system.indexes' collection.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes', o: 'bar'}]}),
|
||||
ErrorCodes.TypeMismatch,
|
||||
'applyOps should fail on system.indexes insert operation with non-object "o" field');
|
||||
|
||||
// Missing 'ns' field in index spec.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes', o: {
|
||||
key: {a: 1},
|
||||
name: 'a_1',
|
||||
}}]}),
|
||||
ErrorCodes.NoSuchKey,
|
||||
'applyOps should fail on system.indexes insert operation with missing index namespace');
|
||||
|
||||
// Non-string 'ns' field in index spec.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes', o: {
|
||||
ns: 12345,
|
||||
key: {a: 1},
|
||||
name: 'a_1',
|
||||
}}]}),
|
||||
ErrorCodes.TypeMismatch,
|
||||
'applyOps should fail on system.indexes insert operation with non-string index namespace');
|
||||
|
||||
// Invalid 'ns' field in index spec.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes', o: {
|
||||
ns: 'invalid_namespace',
|
||||
key: {a: 1},
|
||||
name: 'a_1',
|
||||
}}]}),
|
||||
ErrorCodes.InvalidNamespace,
|
||||
'applyOps should fail on system.indexes insert operation with invalid index namespace');
|
||||
|
||||
// Inconsistent database name in index spec namespace.
|
||||
assert.commandFailedWithCode(
|
||||
db.adminCommand({applyOps: [{op: 'i', ns: db.getName() + '.system.indexes', o: {
|
||||
ns: 'baddbprefix' + t.getFullName(),
|
||||
key: {a: 1},
|
||||
name: 'a_1',
|
||||
}}]}),
|
||||
ErrorCodes.InvalidNamespace,
|
||||
'applyOps should fail on system.indexes insert operation with index namespace containing ' +
|
||||
'inconsistent database name');
|
||||
|
||||
// Valid 'ns' field value in unknown operation type 'x'.
|
||||
assert.commandFailed(
|
||||
db.adminCommand({applyOps: [{op: 'x', ns: t.getFullName()}]}),
|
||||
@ -150,4 +202,37 @@
|
||||
|
||||
assert.eq(true, res.results[0], "Valid update failed");
|
||||
assert.eq(true, res.results[1], "Valid update failed");
|
||||
|
||||
// Foreground index build.
|
||||
res = assert.commandWorked(db.adminCommand({
|
||||
applyOps: [{"op": "i", "ns": db.getName() + ".system.indexes", "o": {
|
||||
ns: t.getFullName(),
|
||||
key: {a: 1},
|
||||
name: "a_1",
|
||||
}
|
||||
}]}));
|
||||
assert.eq(1, res.applied, "Incorrect number of operations applied");
|
||||
assert.eq(true, res.results[0], "Foreground index creation failed");
|
||||
res = t.getIndexes();
|
||||
assert.eq(
|
||||
1,
|
||||
res.filter(function(element, index, array) {return element.name == 'a_1';}).length,
|
||||
'Foreground index not found in listIndexes result: ' + tojson(res));
|
||||
|
||||
// Background indexes are created in the foreground when processed by applyOps.
|
||||
res = assert.commandWorked(db.adminCommand({
|
||||
applyOps: [{"op": "i", "ns": db.getName() + ".system.indexes", "o": {
|
||||
ns: t.getFullName(),
|
||||
key: {b: 1},
|
||||
name: "b_1",
|
||||
background: true,
|
||||
}
|
||||
}]}));
|
||||
assert.eq(1, res.applied, "Incorrect number of operations applied");
|
||||
assert.eq(true, res.results[0], "Background index creation failed");
|
||||
res = t.getIndexes();
|
||||
assert.eq(
|
||||
1,
|
||||
res.filter(function(element, index, array) {return element.name == 'b_1';}).length,
|
||||
'Background index not found in listIndexes result: ' + tojson(res));
|
||||
})();
|
||||
|
||||
@ -11,7 +11,6 @@ benchArgs = { ops : [ { ns : t.getFullName() ,
|
||||
update : { $inc : { x : 1 } } } ] ,
|
||||
parallel : 2 ,
|
||||
seconds : 1 ,
|
||||
totals : true ,
|
||||
host : db.getMongo().host }
|
||||
|
||||
if (jsTest.options().auth) {
|
||||
|
||||
@ -9,7 +9,6 @@ benchArgs = { ops : [ { ns : t.getFullName() ,
|
||||
update : { $inc : { x : 1 } } } ] ,
|
||||
parallel : 2 ,
|
||||
seconds : 5 ,
|
||||
totals : true ,
|
||||
host : db.getMongo().host }
|
||||
|
||||
if (jsTest.options().auth) {
|
||||
|
||||
@ -1,109 +1,104 @@
|
||||
// Test NamespaceDetails::cappedTruncateAfter via 'captrunc' command
|
||||
// Test NamespaceDetails::cappedTruncateAfter via "captrunc" command
|
||||
(function() {
|
||||
var coll = db.capped6;
|
||||
|
||||
Random.setRandomSeed();
|
||||
Random.setRandomSeed();
|
||||
var maxDocuments = Random.randInt(400) + 100;
|
||||
|
||||
db.capped6.drop();
|
||||
db._dbCommand( { create: "capped6", capped: true, size: 1000, $nExtents: 11, autoIndexId: false } );
|
||||
tzz = db.capped6;
|
||||
|
||||
function debug( x ) {
|
||||
// print( x );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that documents in the collection are in order according to the value
|
||||
* of a, which corresponds to the insert order. This is a check that the oldest
|
||||
* document(s) is/are deleted when space is needed for the newest document. The
|
||||
* check is performed in both forward and reverse directions.
|
||||
*/
|
||||
function checkOrder( i ) {
|
||||
res = tzz.find().sort( { $natural: -1 } );
|
||||
assert( res.hasNext(), "A" );
|
||||
var j = i;
|
||||
while( res.hasNext() ) {
|
||||
try {
|
||||
assert.eq( val[ j-- ].a, res.next().a, "B" );
|
||||
} catch( e ) {
|
||||
debug( "capped6 err " + j );
|
||||
throw e;
|
||||
/**
|
||||
* Check that documents in the collection are in order according to the value
|
||||
* of a, which corresponds to the insert order. This is a check that the oldest
|
||||
* document(s) is/are deleted when space is needed for the newest document. The
|
||||
* check is performed in both forward and reverse directions.
|
||||
*/
|
||||
function checkOrder(i, valueArray) {
|
||||
res = coll.find().sort( { $natural: -1 } );
|
||||
assert( res.hasNext(), "A" );
|
||||
var j = i;
|
||||
while(res.hasNext()) {
|
||||
assert.eq( valueArray[j--].a, res.next().a, "B" );
|
||||
}
|
||||
}
|
||||
res = tzz.find().sort( { $natural: 1 } );
|
||||
assert( res.hasNext(), "C" );
|
||||
while( res.hasNext() )
|
||||
assert.eq( val[ ++j ].a, res.next().a, "D" );
|
||||
assert.eq( j, i, "E" );
|
||||
}
|
||||
|
||||
var val = new Array( 500 );
|
||||
var c = "";
|
||||
for( i = 0; i < 500; ++i, c += "-" ) {
|
||||
// The a values are strings of increasing length.
|
||||
val[ i ] = { a: c };
|
||||
}
|
||||
|
||||
var oldMax = Random.randInt( 500 );
|
||||
var max = 0;
|
||||
|
||||
/**
|
||||
* Insert new documents until there are 'oldMax' documents in the collection,
|
||||
* then remove a random number of documents (often all but one) via one or more
|
||||
* 'captrunc' requests.
|
||||
*/
|
||||
function doTest() {
|
||||
for( var i = max; i < oldMax; ++i ) {
|
||||
tzz.insert( val[ i ] );
|
||||
}
|
||||
max = oldMax;
|
||||
count = tzz.count();
|
||||
|
||||
var min = 1;
|
||||
if ( Random.rand() > 0.3 ) {
|
||||
min = Random.randInt( count ) + 1;
|
||||
}
|
||||
|
||||
// Iteratively remove a random number of documents until we have no more
|
||||
// than 'min' documents.
|
||||
while( count > min ) {
|
||||
// 'n' is the number of documents to remove - we must account for the
|
||||
// possibility that 'inc' will be true, and avoid removing all documents
|
||||
// from the collection in that case, as removing all documents is not
|
||||
// allowed by 'captrunc'
|
||||
var n = Random.randInt( count - min - 1 ); // 0 <= x <= count - min - 1
|
||||
var inc = Random.rand() > 0.5;
|
||||
debug( count + " " + n + " " + inc );
|
||||
assert.commandWorked( db.runCommand( { captrunc:"capped6", n:n, inc:inc } ) );
|
||||
if ( inc ) {
|
||||
n += 1;
|
||||
res = coll.find().sort( { $natural: 1 } );
|
||||
assert( res.hasNext(), "C" );
|
||||
while( res.hasNext() ) {
|
||||
assert.eq( valueArray[++j].a, res.next().a, "D" );
|
||||
}
|
||||
count -= n;
|
||||
max -= n;
|
||||
// Validate the remaining documents.
|
||||
checkOrder( max - 1 );
|
||||
assert.eq( j, i, "E" );
|
||||
}
|
||||
}
|
||||
|
||||
// Repeatedly add up to 'oldMax' documents and then truncate the newest
|
||||
// documents. Newer documents take up more space than older documents.
|
||||
for( var i = 0; i < 10; ++i ) {
|
||||
doTest();
|
||||
}
|
||||
/*
|
||||
* Prepare the values to insert and create the capped collection.
|
||||
*/
|
||||
function prepareCollection(shouldReverse) {
|
||||
coll.drop();
|
||||
db._dbCommand({create: "capped6", capped: true, size: 1000, $nExtents: 11,
|
||||
autoIndexId: false});
|
||||
var valueArray = new Array(maxDocuments);
|
||||
var c = "";
|
||||
for( i = 0; i < maxDocuments; ++i, c += "-" ) {
|
||||
// The a values are strings of increasing length.
|
||||
valueArray[i] = {a: c};
|
||||
}
|
||||
if (shouldReverse) {
|
||||
valueArray.reverse();
|
||||
}
|
||||
return valueArray;
|
||||
}
|
||||
|
||||
// reverse order of values
|
||||
var val = new Array( 500 );
|
||||
/**
|
||||
* 1. When this function is called the first time, insert new documents until 'maxDocuments'
|
||||
* number of documents have been inserted. Note that the collection may not have
|
||||
* 'maxDocuments' number of documents since it is a capped collection.
|
||||
* 2. Remove all but one documents via one or more "captrunc" requests.
|
||||
* 3. For each subsequent call to this function, keep track of the removed documents using
|
||||
* 'valueArrayIndexes' and re-insert the removed documents each time this function is
|
||||
* called.
|
||||
*/
|
||||
function runCapTrunc(valueArray, valueArrayCurIndex, n, inc) {
|
||||
|
||||
var c = "";
|
||||
for( i = 499; i >= 0; --i, c += "-" ) {
|
||||
val[ i ] = { a: c };
|
||||
}
|
||||
db.capped6.drop();
|
||||
db._dbCommand( { create: "capped6", capped: true, size: 1000, $nExtents: 11, autoIndexId: false } );
|
||||
tzz = db.capped6;
|
||||
// If n <= 0, no documents are removed by captrunc.
|
||||
assert.gt(n, 0);
|
||||
assert.gte(valueArray.length, maxDocuments);
|
||||
for (var i = valueArrayCurIndex; i < maxDocuments; ++i) {
|
||||
assert.writeOK(coll.insert(valueArray[i]));
|
||||
}
|
||||
count = coll.count();
|
||||
|
||||
// Same test as above, but now the newer documents take less space than the
|
||||
// older documents instead of more.
|
||||
for( var i = 0; i < 10; ++i ) {
|
||||
doTest();
|
||||
}
|
||||
// The index corresponding to the last document in the collection.
|
||||
valueArrayCurIndex = maxDocuments - 1;
|
||||
|
||||
tzz.drop();
|
||||
// Number of times to call "captrunc" so that (count - 1) documents are removed
|
||||
// and at least 1 document is left in the array.
|
||||
var iterations = Math.floor((count - 1) / (n + inc));
|
||||
|
||||
for (i = 0; i < iterations; ++i) {
|
||||
assert.commandWorked(db.runCommand({captrunc:"capped6", n:n, inc:inc}));
|
||||
count -= (n + inc);
|
||||
valueArrayCurIndex -= (n + inc);
|
||||
checkOrder(valueArrayCurIndex, valueArray);
|
||||
};
|
||||
// We return the index of the next document that should be inserted into the capped
|
||||
// collection, which would be the document after valueArrayCurIndex.
|
||||
return valueArrayCurIndex + 1;
|
||||
}
|
||||
|
||||
function doTest(shouldReverse) {
|
||||
var valueArray = prepareCollection(shouldReverse);
|
||||
var valueArrayIndex = 0;
|
||||
valueArrayIndex = runCapTrunc(valueArray, valueArrayIndex, 1, false);
|
||||
valueArrayIndex = runCapTrunc(valueArray, valueArrayIndex, 1, true);
|
||||
valueArrayIndex = runCapTrunc(valueArray, valueArrayIndex, 16, true);
|
||||
valueArrayIndex = runCapTrunc(valueArray, valueArrayIndex, 16, false);
|
||||
valueArrayIndex = runCapTrunc(valueArray, valueArrayIndex, maxDocuments - 2, true);
|
||||
valueArrayIndex = runCapTrunc(valueArray, valueArrayIndex, maxDocuments - 2, false);
|
||||
}
|
||||
|
||||
// Repeatedly add up to 'maxDocuments' documents and then truncate the newest
|
||||
// documents. Newer documents take up more space than older documents.
|
||||
doTest(false);
|
||||
|
||||
// Same test as above, but now the newer documents take less space than the
|
||||
// older documents instead of more.
|
||||
doTest(true)
|
||||
})();
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
/**
|
||||
* SERVER-20529: Ensure capped document sizes do not change
|
||||
* Tests various update scenarios on capped collections:
|
||||
* -- SERVER-20529: Ensure capped document sizes do not change
|
||||
* -- SERVER-11983: Don't create _id field on capped updates
|
||||
*/
|
||||
(function() {
|
||||
'use strict';
|
||||
var t = db.cannot_change_capped_size;
|
||||
t.drop();
|
||||
assert.commandWorked(db.createCollection(t.getName(), {capped: true, size: 1024}));
|
||||
assert.commandWorked(db.createCollection(t.getName(), {capped: true,
|
||||
size: 1024,
|
||||
autoIndexId:false}));
|
||||
assert.eq(0, t.getIndexes().length, "the capped collection has indexes");
|
||||
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
assert.writeOK(t.insert({_id: j, s: "Hello, World!"}));
|
||||
@ -15,4 +20,11 @@
|
||||
assert.writeError(t.update({_id: 3}, {$set: {s: "Hello!"}}));
|
||||
assert.writeError(t.update({_id: 10}, {}));
|
||||
assert.writeError(t.update({_id: 10}, {s: "Hello, World!!!"}));
|
||||
|
||||
assert.commandWorked(t.getDB().runCommand({godinsert:t.getName(), obj:{a:2}}));
|
||||
var doc = t.findOne({a:2});
|
||||
assert.eq(undefined, doc["_id"], "now has _id after godinsert");
|
||||
assert.writeOK(t.update({a:2}, {$inc:{a:1}}))
|
||||
doc = t.findOne({a:3});
|
||||
assert.eq(undefined, doc["_id"], "now has _id after update");
|
||||
})();
|
||||
|
||||
23
jstests/core/collmod_bad_spec.js
Normal file
23
jstests/core/collmod_bad_spec.js
Normal file
@ -0,0 +1,23 @@
|
||||
// This is a regression test for SERVER-21545.
|
||||
//
|
||||
// Tests that a collMod with a bad specification does not cause any changes, and does not crash the
|
||||
// server.
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var collName = "collModBadSpec";
|
||||
var coll = db.getCollection(collName);
|
||||
|
||||
coll.drop();
|
||||
assert.commandWorked(db.createCollection(collName));
|
||||
|
||||
// Get the original collection options for the collection.
|
||||
var originalResult = db.getCollectionInfos({name: collName});
|
||||
|
||||
// Issue an invalid command.
|
||||
assert.commandFailed(coll.runCommand("collMod", {validationLevel: "off", unknownField: "x"}));
|
||||
|
||||
// Make sure the options are unchanged.
|
||||
var newResult = db.getCollectionInfos({name: collName});
|
||||
assert.eq(originalResult, newResult);
|
||||
})();
|
||||
25
jstests/core/ensure_sorted.js
Normal file
25
jstests/core/ensure_sorted.js
Normal file
@ -0,0 +1,25 @@
|
||||
// SERVER-17011 Tests whether queries which specify sort and batch size can generate results out of
|
||||
// order due to the ntoreturn hack. The EnsureSortedStage should solve this problem.
|
||||
(function() {
|
||||
'use strict';
|
||||
var coll = db.ensure_sorted;
|
||||
|
||||
coll.drop();
|
||||
assert.commandWorked(coll.createIndex({a: 1, b: 1}));
|
||||
assert.writeOK(coll.insert({a: 1, b: 4}));
|
||||
assert.writeOK(coll.insert({a: 2, b: 3}));
|
||||
assert.writeOK(coll.insert({a: 3, b: 2}));
|
||||
assert.writeOK(coll.insert({a: 4, b: 1}));
|
||||
|
||||
var cursor = coll.find({a: {$lt: 5}}).sort({b: -1}).batchSize(2);
|
||||
cursor.next(); // {a: 1, b: 4}.
|
||||
cursor.next(); // {a: 2, b: 3}.
|
||||
|
||||
assert.writeOK(coll.update({b: 2}, {$set: {b: 5}}));
|
||||
var result = cursor.next();
|
||||
|
||||
// We might either drop the document where "b" is 2 from the result set, or we might include the
|
||||
// old version of this document (before the update is applied). Either is acceptable, but
|
||||
// out-of-order results are unacceptable.
|
||||
assert(result.b === 2 || result.b === 1, "cursor returned: " + printjson(result));
|
||||
})();
|
||||
@ -90,4 +90,7 @@
|
||||
assert.eq(cmdRes.cursor.id, NumberLong(0));
|
||||
assert.eq(cmdRes.cursor.ns, coll.getFullName());
|
||||
assert.eq(cmdRes.cursor.firstBatch.length, 10);
|
||||
|
||||
// Error on invalid collection name.
|
||||
assert.commandFailedWithCode(db.runCommand({find: ""}), ErrorCodes.InvalidNamespace);
|
||||
})();
|
||||
|
||||
@ -1,10 +1,13 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
load("jstests/libs/analyze_plan.js");
|
||||
|
||||
var colName = "jstests_index_stats";
|
||||
var col = db[colName];
|
||||
col.drop();
|
||||
|
||||
var getUsageCount = function (indexName) {
|
||||
var getUsageCount = function(indexName) {
|
||||
var cursor = col.aggregate([{$indexStats: {}}]);
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
@ -17,7 +20,7 @@
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var getIndexKey = function (indexName) {
|
||||
var getIndexKey = function(indexName) {
|
||||
var cursor = col.aggregate([{$indexStats: {}}]);
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
@ -30,81 +33,178 @@
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var getIndexNamesForWinningPlan = function(explain) {
|
||||
var indexNameList = [];
|
||||
var winningStages = getPlanStages(explain.queryPlanner.winningPlan, "IXSCAN");
|
||||
for (var i = 0; i < winningStages.length; ++i) {
|
||||
indexNameList.push(winningStages[i].indexName);
|
||||
}
|
||||
|
||||
return indexNameList;
|
||||
}
|
||||
|
||||
assert.writeOK(col.insert({a: 1, b: 1, c: 1}));
|
||||
assert.writeOK(col.insert({a: 2, b: 2, c: 2}));
|
||||
assert.writeOK(col.insert({a: 3, b: 3, c: 3}));
|
||||
|
||||
//
|
||||
// Confirm no index stats object exists prior to index creation.
|
||||
//
|
||||
col.findOne({a: 1});
|
||||
assert.eq(undefined, getUsageCount("a_1"));
|
||||
|
||||
//
|
||||
// Create indexes.
|
||||
//
|
||||
assert.commandWorked(col.createIndex({a: 1}, {name: "a_1"}));
|
||||
assert.commandWorked(col.createIndex({b: 1, c: 1}, {name: "b_1_c_1"}));
|
||||
var countA = 0;
|
||||
var countB = 0;
|
||||
var countA = 0; // Tracks expected index access for "a_1".
|
||||
var countB = 0; // Tracks expected index access for "b_1_c_1".
|
||||
|
||||
//
|
||||
// Confirm a stats object exists post index creation (with 0 count).
|
||||
//
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
assert.eq({a: 1}, getIndexKey("a_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on find().
|
||||
//
|
||||
col.findOne({a: 1});
|
||||
countA++;
|
||||
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
|
||||
// Confirm index stats tick on findAndModify().
|
||||
var res = db.runCommand({findAndModify: colName,
|
||||
query: {a: 1},
|
||||
update: {$set: {d: 1}},
|
||||
//
|
||||
// Confirm index stats tick on findAndModify() update.
|
||||
//
|
||||
var res = db.runCommand({findAndModify: colName,
|
||||
query: {a: 1},
|
||||
update: {$set: {d: 1}},
|
||||
'new': true});
|
||||
assert.commandWorked(res);
|
||||
countA++;
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on findAndModify() delete.
|
||||
//
|
||||
res = db.runCommand({findAndModify: colName,
|
||||
query: {a: 2},
|
||||
remove: true});
|
||||
assert.commandWorked(res);
|
||||
countA++;
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
assert.writeOK(col.insert(res.value));
|
||||
|
||||
//
|
||||
// Confirm $and operation ticks indexes for winning plan, but not rejected plans.
|
||||
//
|
||||
|
||||
// Run explain to determine which indexes would be used for this query. Note that index
|
||||
// access counters are not incremented for explain execution.
|
||||
var explain = col.find({a: 2, b: 2}).explain("queryPlanner");
|
||||
var indexNameList = getIndexNamesForWinningPlan(explain);
|
||||
assert.gte(indexNameList.length, 1);
|
||||
|
||||
for (var i = 0; i < indexNameList.length; ++i) {
|
||||
// Increment the expected $indexStats count for each index used.
|
||||
var name = indexNameList[i];
|
||||
if (name === "a_1") {
|
||||
countA++;
|
||||
}
|
||||
else {
|
||||
assert(name === "b_1_c_1");
|
||||
countB++;
|
||||
}
|
||||
}
|
||||
|
||||
// Run the query again without explain to increment index access counters.
|
||||
col.findOne({a: 2, b: 2});
|
||||
// Check all indexes for proper count.
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
assert.eq(0, getUsageCount("_id_"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on distinct().
|
||||
//
|
||||
res = db.runCommand({distinct: colName, key: "b", query: {b: 1}});
|
||||
assert.commandWorked(res);
|
||||
countB++;
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on group().
|
||||
//
|
||||
res = db.runCommand({group: {ns: colName,
|
||||
key: {b: 1, c: 1},
|
||||
cond: {b: {$gt: 0}},
|
||||
$reduce: function(curr, result) {},
|
||||
$reduce: function(curr, result) {},
|
||||
initial: {}}});
|
||||
assert.commandWorked(res);
|
||||
countB++;
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on aggregate w/ match.
|
||||
//
|
||||
res = db.runCommand({aggregate: colName,
|
||||
pipeline: [{$match: {b: 1}}]});
|
||||
assert.commandWorked(res);
|
||||
countB++;
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on mapReduce with query.
|
||||
//
|
||||
res = db.runCommand({mapReduce: colName,
|
||||
map: function() {emit(this.b, this.c);},
|
||||
reduce: function(key, val) {return val;},
|
||||
query: {b: 2},
|
||||
out: {inline: true}});
|
||||
assert.commandWorked(res);
|
||||
countB++;
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on update().
|
||||
//
|
||||
assert.writeOK(col.update({a: 2}, {$set: {d: 2}}));
|
||||
countA++;
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats tick on remove().
|
||||
//
|
||||
assert.writeOK(col.remove({a: 2}));
|
||||
countA++;
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
|
||||
//
|
||||
// Confirm multiple index $or operation ticks all involved indexes.
|
||||
//
|
||||
col.findOne({$or: [{a: 1}, {b: 1, c: 1}]});
|
||||
countA++;
|
||||
countB++;
|
||||
assert.eq(countA, getUsageCount("a_1"));
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats object does not exist post index drop.
|
||||
//
|
||||
assert.commandWorked(col.dropIndex("b_1_c_1"));
|
||||
countB = 0;
|
||||
assert.eq(undefined, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm index stats object exists with count 0 once index is recreated.
|
||||
//
|
||||
assert.commandWorked(col.createIndex({b: 1, c: 1}, {name: "b_1_c_1"}));
|
||||
assert.eq(countB, getUsageCount("b_1_c_1"));
|
||||
|
||||
//
|
||||
// Confirm that retrieval fails if $indexStats is not in the first pipeline position.
|
||||
//
|
||||
assert.throws(function() { col.aggregate([{$match: {}}, {$indexStats: {}}]) });
|
||||
})();
|
||||
|
||||
155
jstests/core/kill_cursors.js
Normal file
155
jstests/core/kill_cursors.js
Normal file
@ -0,0 +1,155 @@
|
||||
// Test the killCursors command.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var cmdRes;
|
||||
var cursor;
|
||||
var cursorId;
|
||||
|
||||
var coll = db.jstest_killcursors;
|
||||
coll.drop();
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assert.writeOK(coll.insert({_id: i}));
|
||||
}
|
||||
|
||||
// killCursors command should fail if the collection name is not a string.
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: {foo: "bad collection param"},
|
||||
cursors: [NumberLong(123), NumberLong(456)]
|
||||
});
|
||||
assert.commandFailedWithCode(cmdRes, ErrorCodes.FailedToParse);
|
||||
|
||||
// killCursors command should fail if the cursors parameter is not an array.
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: coll.getName(),
|
||||
cursors: {a: NumberLong(123), b: NumberLong(456)}
|
||||
});
|
||||
assert.commandFailedWithCode(cmdRes, ErrorCodes.FailedToParse);
|
||||
|
||||
// killCursors command should fail if the cursors parameter is an empty array.
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: coll.getName(),
|
||||
cursors: []
|
||||
});
|
||||
assert.commandFailedWithCode(cmdRes, ErrorCodes.BadValue);
|
||||
|
||||
// killCursors command should report cursors as not found if the collection does not exist.
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: "non-existent-collection",
|
||||
cursors: [NumberLong(123), NumberLong(456)]
|
||||
});
|
||||
assert.commandWorked(cmdRes);
|
||||
assert.eq(cmdRes.cursorsKilled, []);
|
||||
assert.eq(cmdRes.cursorsNotFound, [NumberLong(123), NumberLong(456)]);
|
||||
assert.eq(cmdRes.cursorsAlive, []);
|
||||
assert.eq(cmdRes.cursorsUnknown, []);
|
||||
|
||||
// killCursors command should report non-existent cursors as "not found".
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: coll.getName(),
|
||||
cursors: [NumberLong(123), NumberLong(456)]
|
||||
});
|
||||
assert.commandWorked(cmdRes);
|
||||
assert.eq(cmdRes.cursorsKilled, []);
|
||||
assert.eq(cmdRes.cursorsNotFound, [NumberLong(123), NumberLong(456)]);
|
||||
assert.eq(cmdRes.cursorsAlive, []);
|
||||
assert.eq(cmdRes.cursorsUnknown, []);
|
||||
|
||||
// Test a case where one cursors exists and is killed but the other does not exist.
|
||||
cmdRes = db.runCommand({find: coll.getName(), batchSize: 2});
|
||||
assert.commandWorked(cmdRes);
|
||||
cursorId = cmdRes.cursor.id;
|
||||
assert.neq(cursorId, NumberLong(0));
|
||||
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: coll.getName(),
|
||||
cursors: [NumberLong(123), cursorId]
|
||||
});
|
||||
assert.commandWorked(cmdRes);
|
||||
assert.eq(cmdRes.cursorsKilled, [cursorId]);
|
||||
assert.eq(cmdRes.cursorsNotFound, [NumberLong(123)]);
|
||||
assert.eq(cmdRes.cursorsAlive, []);
|
||||
assert.eq(cmdRes.cursorsUnknown, []);
|
||||
|
||||
// Test killing a noTimeout cursor.
|
||||
cmdRes = db.runCommand({find: coll.getName(), batchSize: 2, noCursorTimeout: true});
|
||||
assert.commandWorked(cmdRes);
|
||||
cursorId = cmdRes.cursor.id;
|
||||
assert.neq(cursorId, NumberLong(0));
|
||||
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: coll.getName(),
|
||||
cursors: [NumberLong(123), cursorId]
|
||||
});
|
||||
assert.commandWorked(cmdRes);
|
||||
assert.eq(cmdRes.cursorsKilled, [cursorId]);
|
||||
assert.eq(cmdRes.cursorsNotFound, [NumberLong(123)]);
|
||||
assert.eq(cmdRes.cursorsAlive, []);
|
||||
assert.eq(cmdRes.cursorsUnknown, []);
|
||||
|
||||
// Test killing a pinned cursor. Since cursors are generally pinned for short periods of time
|
||||
// while result batches are generated, this requires some special machinery to keep a cursor
|
||||
// permanently pinned.
|
||||
var failpointName = "keepCursorPinnedDuringGetMore";
|
||||
var cleanup;
|
||||
try {
|
||||
// Enable a failpoint to ensure that the cursor remains pinned.
|
||||
assert.commandWorked(db.adminCommand({
|
||||
configureFailPoint: failpointName,
|
||||
mode: "alwaysOn"
|
||||
}));
|
||||
|
||||
cmdRes = db.runCommand({find: coll.getName(), batchSize: 2});
|
||||
assert.commandWorked(cmdRes);
|
||||
cursorId = cmdRes.cursor.id;
|
||||
assert.neq(cursorId, NumberLong(0));
|
||||
|
||||
cmdRes = db.runCommand({isMaster: 1});
|
||||
assert.commandWorked(cmdRes);
|
||||
var isMongos = (cmdRes.msg === "isdbgrid");
|
||||
|
||||
// Pin the cursor during a getMore.
|
||||
var code = 'db.runCommand({getMore: ' + cursorId.toString() +
|
||||
', collection: "' + coll.getName() + '"});'
|
||||
cleanup = startParallelShell(code);
|
||||
|
||||
// Sleep to make it more likely that the cursor will be pinned.
|
||||
sleep(2000);
|
||||
|
||||
// Attempt to kill the cursor. In order to avoid flakiness, we do not assume that the cursor
|
||||
// is already pinned (although generally it will be).
|
||||
//
|
||||
// Currently, pinned cursors that are targeted by a killCursors operation are kept alive on
|
||||
// mongod but are killed on mongos (see SERVER-21710).
|
||||
cmdRes = db.runCommand({
|
||||
killCursors: coll.getName(),
|
||||
cursors: [NumberLong(123), cursorId]
|
||||
});
|
||||
assert.commandWorked(cmdRes);
|
||||
assert.eq(cmdRes.cursorsNotFound, [NumberLong(123)]);
|
||||
assert.eq(cmdRes.cursorsUnknown, []);
|
||||
|
||||
if (isMongos) {
|
||||
assert.eq(cmdRes.cursorsKilled, [cursorId]);
|
||||
assert.eq(cmdRes.cursorsAlive, []);
|
||||
}
|
||||
else {
|
||||
// If the cursor has already been pinned it will be left alive; otherwise it will be
|
||||
// killed.
|
||||
if (cmdRes.cursorsAlive.length === 1) {
|
||||
assert.eq(cmdRes.cursorsKilled, []);
|
||||
assert.eq(cmdRes.cursorsAlive, [cursorId]);
|
||||
}
|
||||
else {
|
||||
assert.eq(cmdRes.cursorsKilled, [cursorId]);
|
||||
assert.eq(cmdRes.cursorsAlive, []);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
assert.commandWorked(db.adminCommand({configureFailPoint: failpointName, mode: "off"}));
|
||||
if (cleanup) {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
})();
|
||||
@ -23,10 +23,10 @@ log = db.adminCommand( { getLog : "global" } ).log
|
||||
|
||||
found = false
|
||||
for ( i=log.length - 1; i>= 0; i-- ) {
|
||||
if ( log[i].indexOf( "warning: log line attempted (16k)" ) >= 0 ) {
|
||||
if ( log[i].indexOf( "warning: log line attempted (16kB)" ) >= 0 ) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert( found )
|
||||
assert(found, tojson(log));
|
||||
|
||||
@ -157,6 +157,20 @@ try {
|
||||
assert.eq(lastOp.op, "getmore");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
|
||||
// getMore entry created by iterating the cursor should have the same format, regardless of
|
||||
// readMode.
|
||||
coll.find().batchSize(3).itcount();
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.op, "getmore");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
assert("getMore" in lastOp.query);
|
||||
assert.eq(lastOp.query.getMore, lastOp.cursorid);
|
||||
assert.eq(lastOp.query.collection, coll.getName());
|
||||
assert.eq(lastOp.query.batchSize, 3)
|
||||
assert.eq(lastOp.cursorExhausted, true)
|
||||
assert.eq(lastOp.nreturned, 2);
|
||||
assert("responseLength" in lastOp);
|
||||
|
||||
// Ensure that special $-prefixed OP_QUERY options like $hint and $returnKey get added to the
|
||||
// profiler entry correctly.
|
||||
coll.find().hint({_id: 1}).itcount();
|
||||
@ -191,6 +205,100 @@ try {
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.query.snapshot, true);
|
||||
|
||||
// Tests for profiling findAndModify.
|
||||
coll.drop();
|
||||
for (var i = 0; i < 3; i++) {
|
||||
assert.writeOK(coll.insert({_id: i, a: i}));
|
||||
}
|
||||
|
||||
// Update as findAndModify.
|
||||
assert.eq({_id: 2, a: 2}, coll.findAndModify({query: {a: 2}, update: {$inc: {b: 1}}}));
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.op, "command");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
assert.eq(lastOp.command.query, {a: 2});
|
||||
assert.eq(lastOp.command.update, {$inc: {b: 1}});
|
||||
assert.eq(lastOp.updateobj, {$inc: {b: 1}});
|
||||
assert.eq(lastOp.keysExamined, 0);
|
||||
assert.eq(lastOp.docsExamined, 3);
|
||||
assert.eq(lastOp.nMatched, 1);
|
||||
assert.eq(lastOp.nModified, 1);
|
||||
|
||||
// Delete as findAndModify.
|
||||
assert.eq({_id: 2, a: 2, b: 1}, coll.findAndModify({query: {a: 2}, remove: true}));
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.op, "command");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
assert.eq(lastOp.command.query, {a: 2});
|
||||
assert.eq(lastOp.command.remove, true);
|
||||
assert(!("updateobj" in lastOp));
|
||||
assert.eq(lastOp.ndeleted, 1);
|
||||
|
||||
// Update with {upsert: true} as findAndModify.
|
||||
assert.eq({_id: 2, a: 2, b: 1}, coll.findAndModify({
|
||||
query: {_id: 2, a: 2},
|
||||
update: {$inc: {b: 1}},
|
||||
upsert: true,
|
||||
new: true
|
||||
}));
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.op, "command");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
assert.eq(lastOp.command.query, {_id: 2, a: 2});
|
||||
assert.eq(lastOp.command.update, {$inc: {b: 1}});
|
||||
assert.eq(lastOp.command.upsert, true);
|
||||
assert.eq(lastOp.command.new, true);
|
||||
assert.eq(lastOp.updateobj, {$inc: {b: 1}});
|
||||
assert.eq(lastOp.keysExamined, 0);
|
||||
assert.eq(lastOp.docsExamined, 0);
|
||||
assert.eq(lastOp.nMatched, 1);
|
||||
assert.eq(lastOp.nModified, 1);
|
||||
assert.eq(lastOp.upsert, true);
|
||||
|
||||
// Idhack update as findAndModify.
|
||||
assert.eq({_id: 2, a: 2, b: 1}, coll.findAndModify({
|
||||
query: {_id: 2},
|
||||
update: {$inc: {b: 1}}
|
||||
}));
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.keysExamined, 1);
|
||||
assert.eq(lastOp.docsExamined, 1);
|
||||
assert.eq(lastOp.nMatched, 1);
|
||||
assert.eq(lastOp.nModified, 1);
|
||||
|
||||
// Update as findAndModify with projection.
|
||||
assert.eq({a: 2}, coll.findAndModify({
|
||||
query: {a: 2},
|
||||
update: {$inc: {b: 1}},
|
||||
fields: {_id: 0, a: 1}
|
||||
}));
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.op, "command");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
assert.eq(lastOp.command.query, {a: 2});
|
||||
assert.eq(lastOp.command.update, {$inc: {b: 1}});
|
||||
assert.eq(lastOp.command.fields, {_id: 0, a: 1});
|
||||
assert.eq(lastOp.updateobj, {$inc: {b: 1}});
|
||||
assert.eq(lastOp.keysExamined, 0);
|
||||
assert.eq(lastOp.docsExamined, 3);
|
||||
assert.eq(lastOp.nMatched, 1);
|
||||
assert.eq(lastOp.nModified, 1);
|
||||
|
||||
// Delete as findAndModify with projection.
|
||||
assert.eq({a: 2}, coll.findAndModify({
|
||||
query: {a: 2},
|
||||
remove: true,
|
||||
fields: {_id: 0, a: 1}
|
||||
}));
|
||||
lastOp = getLastOp();
|
||||
assert.eq(lastOp.op, "command");
|
||||
assert.eq(lastOp.ns, coll.getFullName());
|
||||
assert.eq(lastOp.command.query, {a: 2});
|
||||
assert.eq(lastOp.command.remove, true);
|
||||
assert.eq(lastOp.command.fields, {_id: 0, a: 1});
|
||||
assert(!("updateobj" in lastOp));
|
||||
assert.eq(lastOp.ndeleted, 1);
|
||||
|
||||
db.setProfilingLevel(0);
|
||||
db.system.profile.drop();
|
||||
}
|
||||
|
||||
21
jstests/core/single_batch.js
Normal file
21
jstests/core/single_batch.js
Normal file
@ -0,0 +1,21 @@
|
||||
// Test the "single batch" semantics of negative limit.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var coll = db.jstests_single_batch;
|
||||
coll.drop();
|
||||
|
||||
// Approximately 1 MB.
|
||||
var padding = new Array(1024 * 1024).join("x");
|
||||
|
||||
// Insert ~10 MB of data.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
assert.writeOK(coll.insert({_id: i, padding: padding}));
|
||||
}
|
||||
|
||||
// The limit is 8, but we should end up with fewer documents since 8 docs won't fit in a single
|
||||
// 4 MB batch.
|
||||
var numResults = coll.find().limit(-8).itcount();
|
||||
assert.lt(numResults, 8);
|
||||
assert.gt(numResults, 0);
|
||||
})();
|
||||
@ -62,9 +62,10 @@ assert.eq(buildinfo, latestStartUpLog.buildinfo, "buildinfo doesn't match that f
|
||||
// Test version and version Array
|
||||
var version = latestStartUpLog.buildinfo.version.split('-')[0];
|
||||
var versionArray = latestStartUpLog.buildinfo.versionArray;
|
||||
var versionArrayCleaned = [];
|
||||
// Only create a string with 2 dots (2.5.5, not 2.5.5.0)
|
||||
for (var i = 0; i < (versionArray.length - 1); i++) if (versionArray[i] >= 0) { versionArrayCleaned.push(versionArray[i]); }
|
||||
var versionArrayCleaned = versionArray.slice(0, 3);
|
||||
if (versionArray[3] == -100) {
|
||||
versionArrayCleaned[2] -= 1;
|
||||
}
|
||||
|
||||
assert.eq(serverStatus.version, latestStartUpLog.buildinfo.version, "Mongo version doesn't match that from ServerStatus");
|
||||
assert.eq(version, versionArrayCleaned.join('.'), "version doesn't match that from the versionArray");
|
||||
|
||||
@ -7,7 +7,6 @@ var st = new ShardingTest({
|
||||
rs0: {
|
||||
nodes: { n0: {}, n1: { rsConfig: { priority: 0 } } },
|
||||
oplogSize: 10,
|
||||
verbose: 1,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -5,7 +5,7 @@ var replTest = new ReplSetTest({name: name, oplogSize: 1, nodes: 3,
|
||||
settings: {chainingAllowed: false}});
|
||||
var nodes = replTest.startSet();
|
||||
replTest.initiate();
|
||||
var master = replTest.getMaster();
|
||||
var master = replTest.getPrimary();
|
||||
var mdb = master.getDB("test");
|
||||
|
||||
// synchronize replication
|
||||
@ -49,7 +49,7 @@ assert.eq(gle.wtimeout, null);
|
||||
|
||||
// take a node down and GLE for more nodes than are up
|
||||
replTest.stop(2);
|
||||
master = replTest.getMaster();
|
||||
master = replTest.getPrimary();
|
||||
mdb = master.getDB("test");
|
||||
// do w:2 write so secondary is caught up before calling {gle w:3}.
|
||||
assert.writeOK(mdb.foo.insert({_id: "3"}, {writeConcern: {w: 2, wtimeout:30000}}));
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
//
|
||||
// Tests the use of the wOpTime option in getLastError
|
||||
//
|
||||
// This test requires fsync to lock the secondary, so cannot be run on storage engines which do not
|
||||
// support the command.
|
||||
// @tags: [requires_fsync]
|
||||
|
||||
var rst = new ReplSetTest({ nodes : 2 });
|
||||
rst.startSet();
|
||||
@ -18,7 +21,7 @@ assert.eq( null, gleObj.err );
|
||||
var opTimeBeforeFailure = gleObj.lastOp;
|
||||
|
||||
// Lock the secondary
|
||||
secondary.getDB("admin").fsyncLock();
|
||||
assert.commandWorked(secondary.getDB("admin").fsyncLock());
|
||||
|
||||
// Insert a doc and replicate it to the primary only
|
||||
coll.insert({ some : "doc" });
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
// Tests of sharded GLE enforcing write concern against operations in a cluster
|
||||
// Basic sharded GLE operation is tested elsewhere.
|
||||
//
|
||||
// This test asserts that a journaled write to a mongod running with --nojournal should be rejected,
|
||||
// so cannot be run on the ephemeralForTest storage engine, as it accepts all journaled writes.
|
||||
// @tags: [SERVER-21420]
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Options for a cluster with two replica set shards, the first with two nodes the second with one
|
||||
// This lets us try a number of GLE scenarios
|
||||
@ -11,8 +16,7 @@ var options = { rs : true,
|
||||
rs0 : { nodes : 3 },
|
||||
rs1 : { nodes : 3 } };
|
||||
|
||||
var st = new ShardingTest({ shards : 2, mongos : 1, other : options, verbose: 4});
|
||||
st.stopBalancer();
|
||||
var st = new ShardingTest({ shards: 2, other : options });
|
||||
|
||||
var mongos = st.s0;
|
||||
var admin = mongos.getDB( "admin" );
|
||||
@ -125,8 +129,6 @@ assert(gle.ok);
|
||||
assert(gle.err);
|
||||
assert.eq(coll.count({ _id : 1 }), 1);
|
||||
|
||||
jsTest.log( "DONE!" );
|
||||
|
||||
st.stop();
|
||||
|
||||
})();
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
// TODO: remove test after we deprecate SyncClusterConnection
|
||||
//
|
||||
// This test involves restarting a standalone shard, so cannot be run on ephemeral storage engines.
|
||||
// A restarted standalone will lose all data when using an ephemeral storage engine.
|
||||
// @tags: [requires_persistence]
|
||||
|
||||
var test = new SyncCCTest( "sync1" );
|
||||
|
||||
|
||||
@ -17,10 +17,17 @@
|
||||
}
|
||||
var c1, c2, c3;
|
||||
|
||||
c1 = MongoRunner.runMongod({configsvr: "", port: 27019, replSet: "csrs"});
|
||||
// The config servers must support readConcern: majority to be run as a replica set, so
|
||||
// explicitly set storage engine to wiredTiger.
|
||||
c1 = MongoRunner.runMongod({
|
||||
configsvr: "",
|
||||
port: 27019,
|
||||
replSet: "csrs",
|
||||
storageEngine: "wiredTiger"
|
||||
});
|
||||
assert.commandWorked(c1.adminCommand("replSetInitiate"));
|
||||
c2 = MongoRunner.runMongod({configsvr: ""});
|
||||
c3 = MongoRunner.runMongod({configsvr: ""});
|
||||
c2 = MongoRunner.runMongod({configsvr: "", storageEngine: "wiredTiger"});
|
||||
c3 = MongoRunner.runMongod({configsvr: "", storageEngine: "wiredTiger"});
|
||||
|
||||
var configstrs = [
|
||||
getHostPart(c1.host) + "," + c2.host + "," + c3.host,
|
||||
|
||||
@ -3,31 +3,53 @@
|
||||
// scan or whether the plan is covered (index only).
|
||||
|
||||
/**
|
||||
* Given the root stage of explain's JSON representation of a query plan ('root'), returns the
|
||||
* subdocument with its stage as 'stage'. Returns null if the plan does not have such a stage.
|
||||
* Given the root stage of explain's JSON representation of a query plan ('root'), returns all
|
||||
* subdocuments whose stage is 'stage'. Returns an empty array if the plan does not have the
|
||||
* requested stage.
|
||||
*/
|
||||
function getPlanStage(root, stage) {
|
||||
function getPlanStages(root, stage) {
|
||||
var results = [];
|
||||
|
||||
if (root.stage === stage) {
|
||||
return root;
|
||||
} else if ("inputStage" in root) {
|
||||
return getPlanStage(root.inputStage, stage);
|
||||
} else if ("inputStages" in root) {
|
||||
results.push(root);
|
||||
}
|
||||
|
||||
if ("inputStage" in root) {
|
||||
results = results.concat(getPlanStages(root.inputStage, stage));
|
||||
}
|
||||
|
||||
if ("inputStages" in root) {
|
||||
for (var i = 0; i < root.inputStages.length; i++) {
|
||||
var stage = getPlanStage(root.inputStages[i], stage);
|
||||
if (stage !== null) {
|
||||
return stage;
|
||||
}
|
||||
}
|
||||
} else if ("shards" in root) {
|
||||
for (var i = 0; i < root.shards.length; i++) {
|
||||
var stage = getPlanStage(root.shards[i].winningPlan, stage);
|
||||
if (stage !== null) {
|
||||
return stage;
|
||||
}
|
||||
results = results.concat(getPlanStages(root.inputStages[i], stage));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
if ("shards" in root) {
|
||||
for (var i = 0; i < root.shards.length; i++) {
|
||||
results = results.concat(getPlanStages(root.shards[i].winningPlan, stage));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the root stage of explain's JSON representation of a query plan ('root'), returns the
|
||||
* subdocument with its stage as 'stage'. Returns null if the plan does not have such a stage.
|
||||
* Asserts that no more than one stage is a match.
|
||||
*/
|
||||
function getPlanStage(root, stage) {
|
||||
var planStageList = getPlanStages(root, stage);
|
||||
|
||||
if (planStageList.length === 0) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
assert(planStageList.length === 1,
|
||||
"getPlanStage expects to find 0 or 1 matching stages. planStageList: "
|
||||
+ tojson(planStageList));
|
||||
return planStageList[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -201,7 +201,7 @@ function waitForMigrateStep( shardConnection, stepNumber ) {
|
||||
admin = shardConnection.getDB( 'admin' );
|
||||
|
||||
assert( stepNumber >= 1);
|
||||
assert( stepNumber <= 5 );
|
||||
assert( stepNumber <= 5);
|
||||
|
||||
var msg = (
|
||||
'Migrate thread on ' + shardConnection.shardName
|
||||
@ -215,7 +215,11 @@ function waitForMigrateStep( shardConnection, stepNumber ) {
|
||||
for ( var i = 0; i < in_progress.length; ++i ) {
|
||||
var op = in_progress[i];
|
||||
if ( op.desc && op.desc === 'migrateThread' ) {
|
||||
return op.msg.startsWith( searchString );
|
||||
if (op.hasOwnProperty('msg')) {
|
||||
return op.msg.startsWith( searchString );
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ var ElectionTimingTest = function(opts) {
|
||||
|
||||
// The config is set to two electable nodes since we use waitForMemberState
|
||||
// to wait for the electable secondary to become primary.
|
||||
this.nodes = [
|
||||
this.nodes = opts.nodes || [
|
||||
{},
|
||||
{},
|
||||
{rsConfig: {arbiterOnly: true}}
|
||||
@ -32,6 +32,9 @@ var ElectionTimingTest = function(opts) {
|
||||
// A function that triggers election, default is to kill the mongod process.
|
||||
this.electionTrigger = opts.electionTrigger || this.stopPrimary;
|
||||
|
||||
// A function that waits for new primary to be elected.
|
||||
this.waitForNewPrimary = opts.waitForNewPrimary || this.waitForNewPrimary;
|
||||
|
||||
// A function that cleans up after the election trigger.
|
||||
this.testReset = opts.testReset || this.stopPrimaryReset;
|
||||
|
||||
@ -77,21 +80,27 @@ ElectionTimingTest.prototype._runTimingTest = function() {
|
||||
|
||||
// Create and populate a collection.
|
||||
var primary = this.rst.getPrimary();
|
||||
var coll = primary.getCollection(collectionName);
|
||||
var secondary = this.rst.getSecondary();
|
||||
|
||||
this.electionTimeoutLimitMillis =
|
||||
ElectionTimingTest.calculateElectionTimeoutLimitMillis(primary);
|
||||
jsTestLog('Election timeout limit: ' + this.electionTimeoutLimitMillis + ' ms');
|
||||
|
||||
var coll = primary.getCollection(collectionName);
|
||||
for (var i = 0; i < 100; i++) {
|
||||
assert.writeOK(coll.insert({_id: i,
|
||||
x: i * 3,
|
||||
arbitraryStr: "this is a string"}));
|
||||
}
|
||||
|
||||
// Make sure the secondaries are up then await replication.
|
||||
this.rst.awaitSecondaryNodes();
|
||||
this.rst.awaitReplication();
|
||||
|
||||
// Run the election tests on this ReplSetTest instance.
|
||||
var secondary;
|
||||
for (var cycle = 0; cycle < this.testCycles; cycle++) {
|
||||
// Wait for replication.
|
||||
this.rst.awaitSecondaryNodes();
|
||||
this.rst.awaitReplication();
|
||||
primary = this.rst.getPrimary();
|
||||
secondary = this.rst.getSecondary();
|
||||
|
||||
jsTestLog("Starting test: " + this.name + " run: " + run + " cycle: " + cycle);
|
||||
var oldElectionId = primary.getDB("admin").isMaster().electionId;
|
||||
|
||||
@ -107,21 +116,13 @@ ElectionTimingTest.prototype._runTimingTest = function() {
|
||||
|
||||
// Wait for the electable secondary to become primary.
|
||||
try {
|
||||
assert.commandWorked(
|
||||
secondary.adminCommand({
|
||||
replSetTest: 1,
|
||||
waitForMemberState: this.rst.PRIMARY,
|
||||
timeoutMillis: 60 * 1000
|
||||
}),
|
||||
"node " + secondary.host + " failed to become primary"
|
||||
);
|
||||
this.waitForNewPrimary(this.rst, secondary);
|
||||
} catch (e) {
|
||||
// If we didn"t find a primary, save the error, break so this
|
||||
// ReplSetTest is stopped. We can"t continue from a flaky state.
|
||||
this.testErrors.push({testRun: run,
|
||||
cycle: cycle,
|
||||
status: "waitForMemberState(PRIMARY) failed: " +
|
||||
secondary.host,
|
||||
status: "new primary not elected",
|
||||
error: e});
|
||||
break;
|
||||
}
|
||||
@ -159,13 +160,6 @@ ElectionTimingTest.prototype._runTimingTest = function() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Wait for replication. When there are only two nodes in the set,
|
||||
// the previous primary should be given a chance to catch up or
|
||||
// else there will be rollbacks after the next election cycle.
|
||||
this.rst.awaitSecondaryNodes();
|
||||
this.rst.awaitReplication();
|
||||
primary = newPrimary;
|
||||
secondary = this.rst.getSecondary();
|
||||
}
|
||||
this.testResults.push(cycleData);
|
||||
this.rst.stopSet();
|
||||
@ -190,3 +184,44 @@ ElectionTimingTest.prototype.stepDownPrimaryReset = function() {
|
||||
sleep(this.stepDownGuardTime * 1000);
|
||||
};
|
||||
|
||||
ElectionTimingTest.prototype.waitForNewPrimary = function(rst, secondary) {
|
||||
assert.commandWorked(
|
||||
secondary.adminCommand({
|
||||
replSetTest: 1,
|
||||
waitForMemberState: ReplSetTest.State.PRIMARY,
|
||||
timeoutMillis: 60 * 1000
|
||||
}),
|
||||
"node " + secondary.host + " failed to become primary"
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculates upper limit for actual failover time in milliseconds.
|
||||
*/
|
||||
ElectionTimingTest.calculateElectionTimeoutLimitMillis = function(primary) {
|
||||
var configResult = assert.commandWorked(primary.adminCommand({replSetGetConfig: 1}));
|
||||
var config = configResult.config;
|
||||
// Protocol version is 0 if missing from config.
|
||||
var protocolVersion = config.hasOwnProperty("protocolVersion") ? config.protocolVersion : 0;
|
||||
var electionTimeoutMillis = 0;
|
||||
var electionTimeoutOffsetLimitFraction = 0;
|
||||
if (protocolVersion === 0) {
|
||||
electionTimeoutMillis = 30000; // from TopologyCoordinatorImpl::VoteLease::leaseTime
|
||||
electionTimeoutOffsetLimitFraction = 0;
|
||||
} else {
|
||||
electionTimeoutMillis = config.settings.electionTimeoutMillis;
|
||||
var getParameterResult = assert.commandWorked(primary.adminCommand({
|
||||
getParameter: 1,
|
||||
replElectionTimeoutOffsetLimitFraction: 1,
|
||||
}));
|
||||
electionTimeoutOffsetLimitFraction =
|
||||
getParameterResult.replElectionTimeoutOffsetLimitFraction;
|
||||
}
|
||||
var assertSoonIntervalMillis = 200; // from assert.js
|
||||
var applierDrainWaitMillis = 1000; // from SyncTail::tryPopAndWaitForMore()
|
||||
var electionTimeoutLimitMillis =
|
||||
(1 + electionTimeoutOffsetLimitFraction) * electionTimeoutMillis +
|
||||
applierDrainWaitMillis +
|
||||
assertSoonIntervalMillis;
|
||||
return electionTimeoutLimitMillis;
|
||||
};
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
|
||||
// These commands directly support a writeConcern argument.
|
||||
var commandsToForceWriteConcern = [
|
||||
"authSchemaUpgrade",
|
||||
"createRole",
|
||||
"createUser",
|
||||
"delete",
|
||||
@ -59,7 +60,6 @@
|
||||
// These are reading commands that support majority readConcern.
|
||||
var commandsToForceReadConcern = [
|
||||
"count",
|
||||
"dbStats",
|
||||
"distinct",
|
||||
"find",
|
||||
"geoNear",
|
||||
|
||||
@ -0,0 +1,179 @@
|
||||
/**
|
||||
* Loading this file extends the prototype for ReplSetTest to spawn a thread, which continuously
|
||||
* step down the primary.
|
||||
*/
|
||||
|
||||
// Contains the declaration for ScopedThread and CountDownLatch
|
||||
load('jstests/libs/parallelTester.js');
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
// Preserve the original ReplSetTest and ShardingTest constructors, because we are overriding them
|
||||
var originalReplSetTest = ReplSetTest;
|
||||
var originalShardingTest = ShardingTest;
|
||||
|
||||
/**
|
||||
* Overrides the ReplSetTest constructor to start the continuous config server stepdown thread.
|
||||
*/
|
||||
ReplSetTest = function ReplSetTestWithContinuousPrimaryStepdown() {
|
||||
// Construct the original object
|
||||
originalReplSetTest.apply(this, arguments);
|
||||
|
||||
/**
|
||||
* This function is intended to be called in a separate thread and it continuously steps down
|
||||
* the current primary for a number of attempts.
|
||||
*
|
||||
* @param {string} seedNode The connection string of a node from which to discover the primary
|
||||
* of the replica set.
|
||||
* @param {CountDownLatch} stopCounter Object, which can be used to stop the thread.
|
||||
*
|
||||
* @return Object with the following fields:
|
||||
* ok {integer}: 0 if it failed, 1 if it succeeded.
|
||||
* error {string}: Only present if ok == 0. Contains the cause for the error.
|
||||
* stack {string}: Only present if ok == 0. Contains the stack at the time of the error.
|
||||
*/
|
||||
function _continuousPrimaryStepdownFn(seedNode, stopCounter) {
|
||||
'use strict';
|
||||
|
||||
var stepdownDelaySeconds = 10;
|
||||
|
||||
print('*** Continuous stepdown thread running with seed node ' + seedNode);
|
||||
|
||||
try {
|
||||
var replSet = new ReplSetTest(seedNode);
|
||||
var primary = replSet.getPrimary();
|
||||
|
||||
while (stopCounter.getCount() > 0) {
|
||||
print('*** Stepping down ' + primary);
|
||||
|
||||
assert.throws(function() {
|
||||
var result = primary.adminCommand({
|
||||
replSetStepDown: stepdownDelaySeconds,
|
||||
secondaryCatchUpPeriodSecs: stepdownDelaySeconds });
|
||||
print('replSetStepDown command did not throw and returned: ' + tojson(result));
|
||||
|
||||
// The call to replSetStepDown should never succeed
|
||||
assert.commandWorked(result);
|
||||
});
|
||||
|
||||
// Wait for primary to get elected and allow the test to make some progress before
|
||||
// attempting another stepdown.
|
||||
if (stopCounter.getCount() > 0)
|
||||
primary = replSet.getPrimary();
|
||||
|
||||
if (stopCounter.getCount() > 0)
|
||||
sleep(8000);
|
||||
}
|
||||
|
||||
print('*** Continuous stepdown thread completed successfully');
|
||||
return { ok: 1 };
|
||||
}
|
||||
catch (e) {
|
||||
print('*** Continuous stepdown thread caught exception: ' + tojson(e));
|
||||
return { ok: 0, error: e.toString(), stack: e.stack };
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve the original stopSet method, because we are overriding it to stop the continuous
|
||||
// stepdown thread.
|
||||
var _originalStartSetFn = this.startSet;
|
||||
var _originalStopSetFn = this.stopSet;
|
||||
|
||||
// These two manage the scoped failover thread
|
||||
var _scopedPrimaryStepdownThread;
|
||||
var _scopedPrimaryStepdownThreadStopCounter;
|
||||
|
||||
/**
|
||||
* Overrides the startSet call so we can increase the logging verbosity
|
||||
*/
|
||||
this.startSet = function(options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
options.verbose = 2;
|
||||
return _originalStartSetFn.call(this, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the stopSet call so it terminates the failover thread.
|
||||
*/
|
||||
this.stopSet = function() {
|
||||
this.stopContinuousFailover();
|
||||
_originalStopSetFn.apply(this, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawns a thread to invoke continuousPrimaryStepdownFn. See its comments for more information.
|
||||
*/
|
||||
this.startContinuousFailover = function() {
|
||||
if (_scopedPrimaryStepdownThread) {
|
||||
throw new Error('Continuous failover thread is already active');
|
||||
}
|
||||
|
||||
_scopedPrimaryStepdownThreadStopCounter = new CountDownLatch(1);
|
||||
_scopedPrimaryStepdownThread = new ScopedThread(_continuousPrimaryStepdownFn,
|
||||
this.nodes[0].host,
|
||||
_scopedPrimaryStepdownThreadStopCounter);
|
||||
_scopedPrimaryStepdownThread.start();
|
||||
};
|
||||
|
||||
/**
|
||||
* Blocking method, which tells the thread running continuousPrimaryStepdownFn to stop and waits
|
||||
* for it to terminate.
|
||||
*/
|
||||
this.stopContinuousFailover = function() {
|
||||
if (!_scopedPrimaryStepdownThread) {
|
||||
return;
|
||||
}
|
||||
|
||||
_scopedPrimaryStepdownThreadStopCounter.countDown();
|
||||
_scopedPrimaryStepdownThreadStopCounter = null;
|
||||
|
||||
_scopedPrimaryStepdownThread.join();
|
||||
|
||||
var retVal = _scopedPrimaryStepdownThread.returnData();
|
||||
_scopedPrimaryStepdownThread = null;
|
||||
|
||||
return assert.commandWorked(retVal);
|
||||
};
|
||||
};
|
||||
|
||||
Object.extend(ReplSetTest, originalReplSetTest);
|
||||
|
||||
/**
|
||||
* Overrides the ShardingTest constructor to start the continuous config server stepdown thread.
|
||||
*/
|
||||
ShardingTest = function ShardingTestWithContinuousConfigPrimaryStepdown() {
|
||||
if (!arguments[0].other) {
|
||||
arguments[0].other = {};
|
||||
}
|
||||
arguments[0].verbose = 2;
|
||||
|
||||
if (!arguments[0].other.shardOptions) {
|
||||
arguments[0].other.shardOptions = {};
|
||||
}
|
||||
arguments[0].other.shardOptions.verbose = 2;
|
||||
|
||||
// Construct the original object
|
||||
originalShardingTest.apply(this, arguments);
|
||||
|
||||
if (!this.configRS) {
|
||||
throw new Error('Continuous config server step down only available with CSRS');
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is disabled because it runs aggregation, which doesn't handle config server
|
||||
* stepdown correctly.
|
||||
*/
|
||||
this.printShardingStatus = function() {
|
||||
|
||||
}
|
||||
|
||||
// Start the continuous config server stepdown thread
|
||||
this.configRS.startContinuousFailover();
|
||||
};
|
||||
|
||||
Object.extend(ShardingTest, originalShardingTest);
|
||||
|
||||
})();
|
||||
@ -1,22 +1,18 @@
|
||||
/**
|
||||
* The ParallelTester class is used to test more than one test concurrently
|
||||
*/
|
||||
|
||||
|
||||
if ( typeof _threadInject != "undefined" ){
|
||||
//print( "fork() available!" );
|
||||
|
||||
if (typeof _threadInject != "undefined") {
|
||||
Thread = function(){
|
||||
this.init.apply( this, arguments );
|
||||
}
|
||||
_threadInject( Thread.prototype );
|
||||
|
||||
|
||||
ScopedThread = function() {
|
||||
this.init.apply( this, arguments );
|
||||
}
|
||||
ScopedThread.prototype = new Thread( function() {} );
|
||||
_scopedThreadInject( ScopedThread.prototype );
|
||||
|
||||
|
||||
fork = function() {
|
||||
var t = new Thread( function() {} );
|
||||
Thread.apply( t, arguments );
|
||||
@ -29,7 +25,7 @@ if ( typeof _threadInject != "undefined" ){
|
||||
if (host == undefined) host = db.getMongo().host;
|
||||
this.events = new Array( me, collectionName, host );
|
||||
}
|
||||
|
||||
|
||||
EventGenerator.prototype._add = function( action ) {
|
||||
this.events.push( [ Random.genExp( this.mean ), action ] );
|
||||
}
|
||||
@ -160,6 +156,9 @@ if ( typeof _threadInject != "undefined" ){
|
||||
// This overwrites MinKey/MaxKey's singleton which breaks
|
||||
// any other test that uses MinKey/MaxKey
|
||||
"type6.js",
|
||||
|
||||
// Assumes that other tests are not creating cursors.
|
||||
"kill_cursors.js",
|
||||
] );
|
||||
|
||||
var parallelFilesDir = "jstests/core";
|
||||
|
||||
92
jstests/multiVersion/migration_session_id.js
Normal file
92
jstests/multiVersion/migration_session_id.js
Normal file
@ -0,0 +1,92 @@
|
||||
//
|
||||
// Testing migration session ID functionality in migrations between two mongods
|
||||
// where either the donor or recipient shard does not have migration session ID
|
||||
// implemented. Migrations should still be successful.
|
||||
//
|
||||
|
||||
load("./jstests/multiVersion/libs/verify_versions.js");
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var options = {shards: [{binVersion : "3.0"},
|
||||
{binVersion : "3.0"},
|
||||
{binVersion : "3.2"},
|
||||
{binVersion : "3.2"}],
|
||||
mongos: 1,
|
||||
other: {mongosOptions: {binVersion : "3.0"},
|
||||
sync: true}
|
||||
};
|
||||
|
||||
var st = new ShardingTest(options);
|
||||
|
||||
assert.binVersion(st.shard0, "3.0");
|
||||
assert.binVersion(st.shard1, "3.0");
|
||||
assert.binVersion(st.shard2, "3.2");
|
||||
assert.binVersion(st.shard3, "3.2");
|
||||
assert.binVersion(st.s0, "3.0");
|
||||
|
||||
var mongos = st.s0,
|
||||
admin = mongos.getDB('admin'),
|
||||
shards = mongos.getCollection('config.shards').find().toArray(),
|
||||
|
||||
fooDB = "fooTest",
|
||||
fooNS = fooDB + ".foo",
|
||||
fooColl = mongos.getCollection(fooNS),
|
||||
fooDonor = st.shard0,
|
||||
fooRecipient = st.shard2,
|
||||
fooDonorColl = fooDonor.getCollection(fooNS),
|
||||
fooRecipientColl = fooRecipient.getCollection(fooNS),
|
||||
|
||||
barDB = "barTest",
|
||||
barNS = barDB + ".foo",
|
||||
barColl = mongos.getCollection(barNS),
|
||||
barDonor = st.shard3,
|
||||
barRecipient = st.shard1,
|
||||
barDonorColl = barDonor.getCollection(barNS),
|
||||
barRecipientColl = barRecipient.getCollection(barNS);
|
||||
|
||||
assert.commandWorked(admin.runCommand({enableSharding: fooDB}));
|
||||
assert.commandWorked(admin.runCommand({enableSharding: barDB}));
|
||||
st.ensurePrimaryShard(fooDB, shards[0]._id);
|
||||
st.ensurePrimaryShard(barDB, shards[3]._id);
|
||||
|
||||
assert.commandWorked(admin.runCommand({shardCollection: fooNS, key: {a: 1}}));
|
||||
assert.commandWorked(admin.runCommand({split: fooNS, middle: {a: 10}}));
|
||||
assert.commandWorked(admin.runCommand({shardCollection: barNS, key: {a: 1}}));
|
||||
assert.commandWorked(admin.runCommand({split: barNS, middle: {a: 10}}));
|
||||
|
||||
fooColl.insert({a: 0});
|
||||
assert.eq(null, fooColl.getDB().getLastError());
|
||||
fooColl.insert({a: 10});
|
||||
assert.eq(null, fooColl.getDB().getLastError());
|
||||
assert.eq(0, fooRecipientColl.count());
|
||||
assert.eq(2, fooDonorColl.count());
|
||||
assert.eq(2, fooColl.count());
|
||||
|
||||
barColl.insert({a: 0});
|
||||
assert.eq(null, barColl.getDB().getLastError());
|
||||
barColl.insert({a: 10});
|
||||
assert.eq(null, barColl.getDB().getLastError());
|
||||
assert.eq(0, barRecipientColl.count());
|
||||
assert.eq(2, barDonorColl.count());
|
||||
assert.eq(2, barColl.count());
|
||||
|
||||
/**
|
||||
* Perform two migrations:
|
||||
* shard0 (v3.0) -> shard2 (v3.2)
|
||||
* shard3 (v3.2) -> shard1 (v3.0)
|
||||
*/
|
||||
|
||||
assert.commandWorked(admin.runCommand({moveChunk: fooNS, find: {a: 10}, to: shards[2]._id}));
|
||||
assert.commandWorked(admin.runCommand({moveChunk: barNS, find: {a: 10}, to: shards[1]._id}));
|
||||
assert.eq(1, fooRecipientColl.count(), "Foo collection migration failed.");
|
||||
assert.eq(1, fooDonorColl.count(), "Foo donor lost its document");
|
||||
assert.eq(2, fooColl.count(), "Incorrect number of documents in foo collection");
|
||||
assert.eq(1, barRecipientColl.count(), "Bar collection migration failed.");
|
||||
assert.eq(1, barDonorColl.count(), "Bar donor lost its document");
|
||||
assert.eq(2, barColl.count(), "Incorrect number of documents in bar collection");
|
||||
|
||||
st.stop();
|
||||
|
||||
})();
|
||||
39
jstests/multiVersion/readmode_compatibility.js
Normal file
39
jstests/multiVersion/readmode_compatibility.js
Normal file
@ -0,0 +1,39 @@
|
||||
// Ensure that the latest version of the shell (with no particular readMode set) can issue find
|
||||
// operations against a 3.0 server. The shell should read the wire version and fall back to "legacy"
|
||||
// readMode.
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var conn30 = MongoRunner.runMongod({binVersion: '3.0'});
|
||||
assert.neq(conn30, null, 'unable to start 3.0 mongod');
|
||||
|
||||
// Force writeMode to "commands" so that we can check the results of write operations.
|
||||
conn30.forceWriteMode('commands');
|
||||
|
||||
// Forcing the readMode to "compatibility" and then asking for the readMode should cause the
|
||||
// shell to resolve the readMode to "legacy".
|
||||
conn30.forceReadMode('compatibility');
|
||||
assert.eq('legacy', conn30.readMode());
|
||||
|
||||
var testDB = conn30.getDB('test');
|
||||
var coll = testDB.readmode_compatibility;
|
||||
coll.drop();
|
||||
|
||||
for (var i = 0; i < 5; i++) {
|
||||
assert.writeOK(coll.insert({_id: i}));
|
||||
}
|
||||
|
||||
// Use a batchSize of 2 to ensure that we exercise both find and getMore.
|
||||
conn30.forceReadMode('compatibility');
|
||||
assert.eq(5, coll.find().batchSize(2).itcount());
|
||||
assert.eq('legacy', conn30._readMode);
|
||||
|
||||
MongoRunner.stopMongod(conn30);
|
||||
|
||||
// With the latest version of mongod, forcing the readMode to "compatibility" and then asking
|
||||
// for the readMode should cause the shell to resolve the readMode to "commands".
|
||||
var connLatest = MongoRunner.runMongod({});
|
||||
assert.neq(connLatest, null, 'unable to start 3.2 mongod');
|
||||
connLatest.forceReadMode('compatibility');
|
||||
assert.eq('commands', connLatest.readMode());
|
||||
})();
|
||||
@ -255,7 +255,7 @@
|
||||
}
|
||||
|
||||
// Wait up to 60 seconds until restarted node is in state secondary
|
||||
rst.waitForState(rst.getSecondaries(), rst.SECONDARY, 60 * 1000);
|
||||
rst.waitForState(rst.getSecondaries(), ReplSetTest.State.SECONDARY, 60 * 1000);
|
||||
|
||||
// Add new hidden node to replSetTest
|
||||
var hiddenCfg = {
|
||||
@ -289,7 +289,9 @@
|
||||
' failed to reconfigure replSet ' + tojson(rsConfig));
|
||||
|
||||
// Wait up to 60 seconds until the new hidden node is in state RECOVERING.
|
||||
rst.waitForState(rst.nodes[numNodes], [rst.RECOVERING, rst.SECONDARY], 60 * 1000);
|
||||
rst.waitForState(rst.nodes[numNodes],
|
||||
[ReplSetTest.State.RECOVERING, ReplSetTest.State.SECONDARY],
|
||||
60 * 1000);
|
||||
|
||||
// Stop CRUD client and FSM client.
|
||||
assert(checkProgram(crudPid), testName + ' CRUD client was not running at end of test');
|
||||
@ -298,7 +300,7 @@
|
||||
stopMongoProgramByPid(fsmPid);
|
||||
|
||||
// Wait up to 60 seconds until the new hidden node is in state SECONDARY.
|
||||
rst.waitForState(rst.nodes[numNodes], rst.SECONDARY, 60 * 1000);
|
||||
rst.waitForState(rst.nodes[numNodes], ReplSetTest.State.SECONDARY, 60 * 1000);
|
||||
|
||||
// Stop set.
|
||||
rst.stopSet();
|
||||
|
||||
@ -109,9 +109,27 @@ var randDataType = function() {
|
||||
function deg2rad(arg) { return arg * Math.PI / 180.0; }
|
||||
function rad2deg(arg) { return arg * 180.0 / Math.PI; }
|
||||
|
||||
function computexscandist(y, maxDistDegrees) {
|
||||
return maxDistDegrees / Math.min(Math.cos(deg2rad(Math.min(89.0, y + maxDistDegrees))),
|
||||
Math.cos(deg2rad(Math.max(-89.0, y - maxDistDegrees))));
|
||||
function computexscandist(latDegrees, maxDistDegrees) {
|
||||
// See s2cap.cc
|
||||
//
|
||||
// Compute the range of longitudes covered by the cap. We use the law
|
||||
// of sines for spherical triangles. Consider the triangle ABC where
|
||||
// A is the north pole, B is the center of the cap, and C is the point
|
||||
// of tangency between the cap boundary and a line of longitude. Then
|
||||
// C is a right angle, and letting a,b,c denote the sides opposite A,B,C,
|
||||
// we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c).
|
||||
// Here "a" is the cap angle, and "c" is the colatitude (90 degrees
|
||||
// minus the latitude). This formula also works for negative latitudes.
|
||||
//
|
||||
// Angle A is the difference of longitudes of B and C.
|
||||
var sin_c = Math.cos(deg2rad(latDegrees));
|
||||
var sin_a = Math.sin(deg2rad(maxDistDegrees));
|
||||
if (sin_a > sin_c) {
|
||||
// Double floating number error, return invalid distance
|
||||
return 180;
|
||||
}
|
||||
var angleA = Math.asin(sin_a / sin_c);
|
||||
return rad2deg(angleA);
|
||||
}
|
||||
|
||||
function errorMarginForPoint(env) {
|
||||
@ -124,12 +142,13 @@ function errorMarginForPoint(env) {
|
||||
|
||||
function pointIsOK(startPoint, radius, env) {
|
||||
var error = errorMarginForPoint(env);
|
||||
yscandist = rad2deg(radius) + error;
|
||||
xscandist = computexscandist(startPoint[1], yscandist);
|
||||
return (startPoint[0] + xscandist < 180)
|
||||
&& (startPoint[0] - xscandist > -180)
|
||||
&& (startPoint[1] + yscandist < 90)
|
||||
&& (startPoint[1] - yscandist > -90);
|
||||
var distDegrees = rad2deg(radius) + error;
|
||||
// Cap should not include the South/North Pole.
|
||||
if ((startPoint[1] + distDegrees > 90) || (startPoint[1] - distDegrees < -90)) {
|
||||
return false;
|
||||
}
|
||||
var xscandist = computexscandist(startPoint[1], distDegrees);
|
||||
return (startPoint[0] + xscandist < 180) && (startPoint[0] - xscandist > -180);
|
||||
}
|
||||
|
||||
var randQuery = function( env ) {
|
||||
|
||||
@ -78,7 +78,9 @@ replTest.reInitiate(awaitTimeout * 2);
|
||||
secondary.setSlaveOk();
|
||||
// Wait for the secondary to get ReplSetInitiate command.
|
||||
replTest.waitForState(secondary,
|
||||
[replTest.STARTUP_2, replTest.RECOVERING, replTest.SECONDARY],
|
||||
[ReplSetTest.State.STARTUP_2,
|
||||
ReplSetTest.State.RECOVERING,
|
||||
ReplSetTest.State.SECONDARY],
|
||||
60 * 1000);
|
||||
|
||||
// This fail point will cause the first intial sync to fail, and leave an op in the buffer to
|
||||
|
||||
@ -9,7 +9,7 @@ var host = getHostName();
|
||||
var nodes = replTest.startSet();
|
||||
replTest.initiate();
|
||||
|
||||
var master = replTest.getMaster();
|
||||
var master = replTest.getPrimary();
|
||||
var mdb = master.getDB("foo");
|
||||
|
||||
print("1: initial insert");
|
||||
|
||||
@ -30,7 +30,7 @@ replTest.initiate({_id : name, members : [
|
||||
{_id : 2, host : host+":"+replTest.ports[2], arbiterOnly : true}
|
||||
]});
|
||||
var slaves = replTest.liveNodes.slaves;
|
||||
var master = replTest.getMaster();
|
||||
var master = replTest.getPrimary();
|
||||
var masterId = replTest.getNodeId(master);
|
||||
var slave = slaves[0];
|
||||
var slaveId = replTest.getNodeId(slave);
|
||||
@ -62,7 +62,7 @@ print("6: start up slave");
|
||||
replTest.restart(slaveId);
|
||||
|
||||
print("7: writes on former slave")
|
||||
master = replTest.getMaster();
|
||||
master = replTest.getPrimary();
|
||||
mdb1 = master.getDB("foo");
|
||||
mdb1.foo.save({a:1002});
|
||||
|
||||
|
||||
47
jstests/noPassthrough/write_local.js
Normal file
47
jstests/noPassthrough/write_local.js
Normal file
@ -0,0 +1,47 @@
|
||||
// SERVER-22011: Deadlock in ticket distribution
|
||||
(function() {
|
||||
'use strict'
|
||||
|
||||
// Limit concurrent WiredTiger transactions to maximize locking issues, harmless for other SEs.
|
||||
var options = { verbose: 1 };
|
||||
|
||||
// Create a new single node replicaSet
|
||||
var replTest = new ReplSetTest({ name: "write_local",
|
||||
nodes: 1,
|
||||
oplogSize: 1,
|
||||
nodeOptions: options });
|
||||
replTest.startSet();
|
||||
replTest.initiate();
|
||||
var mongod = replTest.getPrimary();
|
||||
mongod.adminCommand({ setParameter: 1, wiredTigerConcurrentWriteTransactions: 1 });
|
||||
|
||||
var local = mongod.getDB('local');
|
||||
|
||||
// Start inserting documents in test.capped and local.capped capped collections.
|
||||
var shells = ['test', 'local'].map(function(dbname){
|
||||
var mydb = local.getSiblingDB(dbname);
|
||||
mydb.capped.drop();
|
||||
mydb.createCollection('capped', { capped: true, size: 20*1000 });
|
||||
return startParallelShell(
|
||||
'var mydb=db.getSiblingDB("' + dbname + '"); ' +
|
||||
'(function() { ' +
|
||||
' for(var i=0; i < 10*1000; i++) { ' +
|
||||
' mydb.capped.insert({ x: i }); ' +
|
||||
' } ' +
|
||||
'})();', mongod.port);
|
||||
});
|
||||
|
||||
// The following causes inconsistent locking order in the ticket system, depending on
|
||||
// timeouts to avoid deadlock.
|
||||
var oldObjects = 0;
|
||||
for (var i = 0; i < 1000; i++) {
|
||||
print(local.stats().objects);
|
||||
sleep(1);
|
||||
};
|
||||
|
||||
// Wait for parallel shells to terminate and stop our replset.
|
||||
shells.forEach((function(f) {
|
||||
f();
|
||||
}));
|
||||
replTest.stopSet();
|
||||
}())
|
||||
@ -40,7 +40,7 @@ else {
|
||||
config.members[0].priority = 1;
|
||||
replTest.initiate(config);
|
||||
|
||||
var masterDB = replTest.getMaster().getDB("test");
|
||||
var masterDB = replTest.getPrimary().getDB("test");
|
||||
var secondary1 = replTest.liveNodes.slaves[0];
|
||||
|
||||
jsTestLog("add some data to collection foo");
|
||||
|
||||
@ -6,6 +6,19 @@
|
||||
coll.drop();
|
||||
assert.commandWorked(coll.getDB().createCollection(coll.getName()));
|
||||
|
||||
function makeDocument(docSize) {
|
||||
var doc = { "fieldName":"" };
|
||||
var longString = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
|
||||
while(Object.bsonsize(doc) < docSize) {
|
||||
if (Object.bsonsize(doc) < docSize - longString.length) {
|
||||
doc.fieldName += longString;
|
||||
} else {
|
||||
doc.fieldName += "x";
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
function executeBenchRun(benchOps) {
|
||||
var benchArgs = {ops: benchOps, parallel: 2, seconds: 1, host: db.getMongo().host};
|
||||
if (jsTest.options().auth) {
|
||||
@ -16,17 +29,14 @@
|
||||
return benchRun(benchArgs);
|
||||
}
|
||||
|
||||
function testInsert(writeCmd) {
|
||||
function testInsert(docs, writeCmd, wc) {
|
||||
coll.drop();
|
||||
|
||||
var docs = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
docs.push({x: 1});
|
||||
}
|
||||
var res = executeBenchRun([{ns: coll.getFullName(),
|
||||
op: "insert",
|
||||
doc: docs,
|
||||
writeCmd: writeCmd}]);
|
||||
writeCmd: writeCmd,
|
||||
writeConcern : wc}]);
|
||||
|
||||
assert.gt(coll.count(), 0);
|
||||
assert.eq(coll.findOne({}, {_id:0}), docs[0]);
|
||||
@ -60,8 +70,23 @@
|
||||
assert.gt(res.findOne, 0, tojson(res));
|
||||
}
|
||||
|
||||
testInsert(false);
|
||||
testInsert(true);
|
||||
function testWriteConcern(writeCmd) {
|
||||
var bigDoc = makeDocument(260 * 1024);
|
||||
var docs = [];
|
||||
for (var i = 0; i < 100; i++) {
|
||||
docs.push({x: 1});
|
||||
}
|
||||
|
||||
testInsert([bigDoc], writeCmd, {});
|
||||
testInsert(docs, writeCmd, {});
|
||||
testInsert(docs, writeCmd, {"writeConcern" : {"w" : "majority"}});
|
||||
testInsert(docs, writeCmd, {"writeConcern" : {"w" : 1, "j": false}});
|
||||
testInsert(docs, writeCmd, {"writeConcern" : {"j" : true}});
|
||||
}
|
||||
|
||||
testWriteConcern(false);
|
||||
testWriteConcern(true);
|
||||
|
||||
testFind(false);
|
||||
testFind(true);
|
||||
testFindOne(false);
|
||||
|
||||
@ -17,6 +17,13 @@
|
||||
autoIndexId: true }));
|
||||
var t = db.capped_truncate;
|
||||
|
||||
// It is an error to remove a non-positive number of documents.
|
||||
assert.commandFailed(db.runCommand({ captrunc: "capped_truncate", n: -1 }),
|
||||
"captrunc didn't return an error when attempting to remove a negative " +
|
||||
"number of documents");
|
||||
assert.commandFailed(db.runCommand({ captrunc: "capped_truncate", n: 0 }),
|
||||
"captrunc didn't return an error when attempting to remove 0 documents");
|
||||
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
assert.writeOK(t.insert({x:j}));
|
||||
}
|
||||
@ -39,10 +46,13 @@
|
||||
"captrunc didn't return an error for a nonexistent collection");
|
||||
|
||||
// It is an error to run the captrunc command on a non-capped collection.
|
||||
assert.commandWorked(db.runCommand({ create: "noncapped", capped: false }));
|
||||
var collName = "noncapped";
|
||||
db[collName].drop();
|
||||
|
||||
assert.commandWorked(db.runCommand({ create: collName, capped: false }));
|
||||
for (var j = 1; j <= 10; j++) {
|
||||
assert.writeOK(db.noncapped.insert({x:j}));
|
||||
assert.writeOK(db[collName].insert({x:j}));
|
||||
}
|
||||
assert.commandFailed(db.runCommand({ captrunc: "noncapped", n: 5 }),
|
||||
assert.commandFailed(db.runCommand({ captrunc: collName, n: 5 }),
|
||||
"captrunc didn't return an error for a non-capped collection");
|
||||
})();
|
||||
|
||||
@ -27,7 +27,7 @@ replTest.initiate({"_id" : "bgIndex",
|
||||
{"_id" : 1, "host" : nodes[1]},
|
||||
{"_id" : 2, "host" : nodes[2], "arbiterOnly" : true}]});
|
||||
|
||||
var master = replTest.getMaster();
|
||||
var master = replTest.getPrimary();
|
||||
var second = replTest.getSecondary();
|
||||
|
||||
var masterId = replTest.getNodeId(master);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user