Compare commits
371 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c28079649e | ||
|
|
e5a7faaecd | ||
|
|
ec60d7de77 | ||
|
|
7e34cb36a6 | ||
|
|
cae3ea8b1d | ||
|
|
446b5ded23 | ||
|
|
bb999bb503 | ||
|
|
b7b6c38446 | ||
|
|
3c5c12f7d5 | ||
|
|
bc7400d46a | ||
|
|
a340a57af7 | ||
|
|
1d3d3ba938 | ||
|
|
744074d2cf | ||
|
|
365e89e5ec | ||
|
|
6d021c86d3 | ||
|
|
7577f95c80 | ||
|
|
3d8d35771f | ||
|
|
239d7d6a19 | ||
|
|
5eaf375b0c | ||
|
|
0786517099 | ||
|
|
01477c20c8 | ||
|
|
86b6b474a8 | ||
|
|
4d37e1acba | ||
|
|
8ea06cd353 | ||
|
|
72cc3481b9 | ||
|
|
daf7aec921 | ||
|
|
875033920e | ||
|
|
d54dc5e869 | ||
|
|
24f90398dc | ||
|
|
9efe4cce27 | ||
|
|
de0cfcd8de | ||
|
|
8e42151ecc | ||
|
|
6701b6ea85 | ||
|
|
6aa80782bc | ||
|
|
c88805a6f6 | ||
|
|
7542d7baa9 | ||
|
|
90fa91c352 | ||
|
|
1ba3671b1c | ||
|
|
a56780496b | ||
|
|
accb562a78 | ||
|
|
61d3a31ca4 | ||
|
|
75fe13d0b1 | ||
|
|
dc6d43de35 | ||
|
|
8e584c2fe8 | ||
|
|
e67589e105 | ||
|
|
c6a40c4423 | ||
|
|
ea06d5d59a | ||
|
|
87c31eb85c | ||
|
|
a31879b251 | ||
|
|
6b24994f08 | ||
|
|
560db9021b | ||
|
|
afd5eb4e2c | ||
|
|
b0e8e2bfa9 | ||
|
|
7d3a8463b4 | ||
|
|
97d9c1b815 | ||
|
|
6de83ddbe9 | ||
|
|
f198c21e4b | ||
|
|
2ff57e149c | ||
|
|
b72aa7471c | ||
|
|
e25d6a4165 | ||
|
|
b39f7ff6b9 | ||
|
|
16fdca3498 | ||
|
|
17c3a2c3a7 | ||
|
|
bf25d51add | ||
|
|
ae56ae532c | ||
|
|
9dc4c3fb5b | ||
|
|
80f143dc95 | ||
|
|
73370d7bc4 | ||
|
|
12de29a401 | ||
|
|
dd36d37c09 | ||
|
|
75fa25bc1d | ||
|
|
7012a03237 | ||
|
|
6c19d602f1 | ||
|
|
4ecae7484e | ||
|
|
4bb6e76b20 | ||
|
|
f45435a022 | ||
|
|
034b064a04 | ||
|
|
881d6229e1 | ||
|
|
4ada6dfbc2 | ||
|
|
f9828af52c | ||
|
|
e3120a3fbd | ||
|
|
e1504ed07f | ||
|
|
ef11f578a4 | ||
|
|
9ffb237472 | ||
|
|
e1c0cbc258 | ||
|
|
eba41d3136 | ||
|
|
ec370983a5 | ||
|
|
095dc59270 | ||
|
|
6b08691668 | ||
|
|
c3d0639c9a | ||
|
|
1d84085365 | ||
|
|
dfbf91e16a | ||
|
|
dbd87d09fd | ||
|
|
ee64e77c32 | ||
|
|
03c34bd5fa | ||
|
|
db9d17af5c | ||
|
|
0397af7906 | ||
|
|
2cf91c83a1 | ||
|
|
1aaac5eb0d | ||
|
|
dd21525b02 | ||
|
|
77db8a50cf | ||
|
|
c94a29477a | ||
|
|
4fc6bb1e17 | ||
|
|
40049a3869 | ||
|
|
8f38fc36d1 | ||
|
|
1bb4de4630 | ||
|
|
935e22aaed | ||
|
|
52db24582f | ||
|
|
dc2f8cd3df | ||
|
|
98c748b295 | ||
|
|
1c22afa5d0 | ||
|
|
947fa6924c | ||
|
|
97ab12742a | ||
|
|
09db1bc1be | ||
|
|
f53de1128d | ||
|
|
d861e27e78 | ||
|
|
6fb6a11f58 | ||
|
|
36ae878645 | ||
|
|
e51ea6f089 | ||
|
|
e50b2e2428 | ||
|
|
6477fd1d75 | ||
|
|
672793ce4f | ||
|
|
49144209b1 | ||
|
|
bea6d4be20 | ||
|
|
6245495cab | ||
|
|
264b914c4b | ||
|
|
1911459b0d | ||
|
|
5f49613090 | ||
|
|
e1b56eb28a | ||
|
|
9bb928d020 | ||
|
|
d99695ccc2 | ||
|
|
df84cb3159 | ||
|
|
87374c2c5c | ||
|
|
735e498aa6 | ||
|
|
b39c6a7e18 | ||
|
|
05b07daf5b | ||
|
|
41be3168d4 | ||
|
|
ba8d0f572c | ||
|
|
e69445666b | ||
|
|
83fa0f7bc1 | ||
|
|
7460b30455 | ||
|
|
aee93cd91a | ||
|
|
b32511d875 | ||
|
|
30f515ac16 | ||
|
|
660f41ad65 | ||
|
|
d87357cf26 | ||
|
|
a55507d21e | ||
|
|
23a1c8019a | ||
|
|
829e3beaa0 | ||
|
|
dbf67c31dc | ||
|
|
654b5284e4 | ||
|
|
6a6b4613fc | ||
|
|
64f3b376ca | ||
|
|
bb76d2d736 | ||
|
|
5893841d96 | ||
|
|
329f3c47fe | ||
|
|
9ede2213f3 | ||
|
|
240177f3cb | ||
|
|
22ffe27906 | ||
|
|
cc61e1f8fc | ||
|
|
67c9ac45c3 | ||
|
|
d216bc37c3 | ||
|
|
8983591f83 | ||
|
|
edc6a48921 | ||
|
|
446f597cf4 | ||
|
|
abb6284056 | ||
|
|
d13fb74970 | ||
|
|
b626e959a3 | ||
|
|
5899a6daa5 | ||
|
|
816af2185c | ||
|
|
32cc883b29 | ||
|
|
c546330522 | ||
|
|
05da017dee | ||
|
|
05bb8aa793 | ||
|
|
3fc4863624 | ||
|
|
a83ab12c51 | ||
|
|
ac5eb821bb | ||
|
|
7948814df6 | ||
|
|
3b1fecd968 | ||
|
|
29cd169c4d | ||
|
|
f96f1c70ec | ||
|
|
24e1f0b92e | ||
|
|
8849484b70 | ||
|
|
8c259b9984 | ||
|
|
2bedc3cf68 | ||
|
|
ad9f37fddb | ||
|
|
dd046106ba | ||
|
|
d77cc8d850 | ||
|
|
643c3a25c7 | ||
|
|
d39bc96a0c | ||
|
|
43fa534919 | ||
|
|
e74f5546d5 | ||
|
|
4ef4f1904c | ||
|
|
0bd2737e38 | ||
|
|
5fc7c288e0 | ||
|
|
5c71438495 | ||
|
|
9ee926acf4 | ||
|
|
2d4f7f4c88 | ||
|
|
3885acaf7a | ||
|
|
6b3ab0f6e1 | ||
|
|
6b2231a15c | ||
|
|
9a09c9f1af | ||
|
|
527d4d28df | ||
|
|
5e29dc25bb | ||
|
|
4770f74e6e | ||
|
|
9bbdf6d86b | ||
|
|
9bc4189471 | ||
|
|
330e2a7407 | ||
|
|
6f9da3de69 | ||
|
|
ab1e828dd7 | ||
|
|
076f88cf6f | ||
|
|
5ca3f27109 | ||
|
|
937077b4c1 | ||
|
|
c202889e9c | ||
|
|
078c791acd | ||
|
|
41a6f84004 | ||
|
|
c3449caf1a | ||
|
|
c91aa2519e | ||
|
|
29f74a507c | ||
|
|
68e305862a | ||
|
|
0b38e1009b | ||
|
|
86e26ee445 | ||
|
|
3aaea5262d | ||
|
|
6e95d546e2 | ||
|
|
2d5c1aeeb4 | ||
|
|
b8447afd0e | ||
|
|
53bc8fe361 | ||
|
|
5464d74491 | ||
|
|
400d7730ef | ||
|
|
774b90baee | ||
|
|
45190b81b5 | ||
|
|
8cd62bc27c | ||
|
|
a719914879 | ||
|
|
843bc2c58e | ||
|
|
d88037845b | ||
|
|
3eaa472e9a | ||
|
|
feb1ef76fc | ||
|
|
514b122d30 | ||
|
|
a841b2e681 | ||
|
|
6759c3ffa1 | ||
|
|
9e517db3e1 | ||
|
|
9179db2dc8 | ||
|
|
f8a4019253 | ||
|
|
7d32bec61f | ||
|
|
4d5e2c7b4f | ||
|
|
b9e4db5cdc | ||
|
|
85488010f7 | ||
|
|
f0d35552f7 | ||
|
|
468b35887e | ||
|
|
d99fcfeb75 | ||
|
|
bd438596e1 | ||
|
|
2a4486134b | ||
|
|
48e6b7b16e | ||
|
|
c0044c79fb | ||
|
|
a91919f726 | ||
|
|
e9ec52c686 | ||
|
|
bc22bd4487 | ||
|
|
d69d52ceef | ||
|
|
c0588190d4 | ||
|
|
415486ce5c | ||
|
|
f48ecaa2c6 | ||
|
|
163a0ede78 | ||
|
|
3a57f18f76 | ||
|
|
ada33ce4be | ||
|
|
7e3de32bcc | ||
|
|
e82af25e9c | ||
|
|
a695bea89c | ||
|
|
771f7f7dd6 | ||
|
|
b9e8ac58d2 | ||
|
|
84a3d7aa3d | ||
|
|
8723be4f95 | ||
|
|
58a501e059 | ||
|
|
d4ba20e727 | ||
|
|
0f14f054c2 | ||
|
|
154f5545b6 | ||
|
|
e6e308d2c0 | ||
|
|
340048ba58 | ||
|
|
e5cd12cde3 | ||
|
|
d1854d7b70 | ||
|
|
cc5021feda | ||
|
|
bc9d59f5db | ||
|
|
d42cf0df2b | ||
|
|
6cea95fbf5 | ||
|
|
50349cb760 | ||
|
|
d2feeb87c8 | ||
|
|
3e5d0e345b | ||
|
|
0f6d8d7e4c | ||
|
|
e76f527687 | ||
|
|
04ba3d198a | ||
|
|
4b1ac7b2b6 | ||
|
|
4514def09a | ||
|
|
bcf4ca4407 | ||
|
|
aede6b131d | ||
|
|
f275d3202a | ||
|
|
5862ea06eb | ||
|
|
c74b40f65a | ||
|
|
897466810d | ||
|
|
975be960bb | ||
|
|
ebdb2b61f3 | ||
|
|
d11ede1282 | ||
|
|
3e599665f8 | ||
|
|
65e38831fe | ||
|
|
267bb38b54 | ||
|
|
0c382de588 | ||
|
|
de05cdff53 | ||
|
|
db49a5ce19 | ||
|
|
d04d7d118b | ||
|
|
e65e110553 | ||
|
|
f74345924b | ||
|
|
ccc60a0948 | ||
|
|
774ba87717 | ||
|
|
f6c73c58a5 | ||
|
|
b81ad0bae0 | ||
|
|
9fa387e6f0 | ||
|
|
9c5824c948 | ||
|
|
3a5cf0e213 | ||
|
|
061c8ca173 | ||
|
|
241dab901c | ||
|
|
9bf70fe751 | ||
|
|
4f42dce0db | ||
|
|
858d2ad0cd | ||
|
|
3bf3ab3bb1 | ||
|
|
6f3f2379b9 | ||
|
|
de0632c805 | ||
|
|
b2910bcc5e | ||
|
|
9e642f566f | ||
|
|
9e8ae84247 | ||
|
|
3c02b91d0e | ||
|
|
dda9a77986 | ||
|
|
a7a36ecbf0 | ||
|
|
417b9110ce | ||
|
|
0f605b5ab3 | ||
|
|
35a196a13e | ||
|
|
56b7cf65d1 | ||
|
|
41ff2157a3 | ||
|
|
a7796c9c09 | ||
|
|
8814b71b26 | ||
|
|
17caecbd77 | ||
|
|
7f66400562 | ||
|
|
f394d2c29a | ||
|
|
05916b4e71 | ||
|
|
6728c042dd | ||
|
|
afa5bc3d0e | ||
|
|
4eff1007c9 | ||
|
|
671479a616 | ||
|
|
59aa78ea64 | ||
|
|
414d14dbd8 | ||
|
|
3461007a7a | ||
|
|
4063312d60 | ||
|
|
729ad802c8 | ||
|
|
0b2cd79560 | ||
|
|
fec06cfe91 | ||
|
|
47360db9e8 | ||
|
|
6aee211334 | ||
|
|
33342ebb76 | ||
|
|
f5f0053a1f | ||
|
|
44da5dff2a | ||
|
|
2c34a826b6 | ||
|
|
a05db65257 | ||
|
|
b6bf498b3c | ||
|
|
99b2d74eb9 | ||
|
|
7ce5d643a0 | ||
|
|
1eb2d4556d | ||
|
|
fad753ee00 | ||
|
|
2250ff90c5 | ||
|
|
692f4ec8df | ||
|
|
ade41dcba8 | ||
|
|
1c064ab915 | ||
|
|
4449aae6e4 | ||
|
|
f2bf86ff3f | ||
|
|
695c67dff0 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -43,6 +43,8 @@ shell/mongo-server.cpp
|
||||
*/*/Debug/
|
||||
*/Release/
|
||||
*/*/Release/
|
||||
*/ipch/
|
||||
*/*/ipch/
|
||||
db/.gdb*
|
||||
db/makefile.local
|
||||
db/_ReSharper.db
|
||||
|
||||
76
SConstruct
76
SConstruct
@ -25,6 +25,17 @@ import buildscripts.bb
|
||||
import stat
|
||||
from buildscripts import utils
|
||||
|
||||
|
||||
def _rpartition(string, sep):
|
||||
"""A replacement for str.rpartition which is missing in Python < 2.5
|
||||
"""
|
||||
idx = string.rfind(sep)
|
||||
if idx == -1:
|
||||
return '', '', string
|
||||
return string[:idx], sep, string[idx + 1:]
|
||||
|
||||
|
||||
|
||||
buildscripts.bb.checkOk()
|
||||
|
||||
def findSettingsSetup():
|
||||
@ -32,6 +43,16 @@ def findSettingsSetup():
|
||||
sys.path.append( ".." )
|
||||
sys.path.append( "../../" )
|
||||
|
||||
def getThirdPartyShortNames():
|
||||
lst = []
|
||||
for x in os.listdir( "third_party" ):
|
||||
if not x.endswith( ".py" ) or x.find( "#" ) >= 0:
|
||||
continue
|
||||
|
||||
lst.append( _rpartition( x, "." )[0] )
|
||||
return lst
|
||||
|
||||
|
||||
# --- options ----
|
||||
|
||||
options = {}
|
||||
@ -135,6 +156,8 @@ add_option( "staticlibpath", "comma separated list of dirs to search for staticl
|
||||
add_option( "boost-compiler", "compiler used for boost (gcc41)" , 1 , True , "boostCompiler" )
|
||||
add_option( "boost-version", "boost version for linking(1_38)" , 1 , True , "boostVersion" )
|
||||
|
||||
add_option( "no-glibc-check" , "don't check for new versions of glibc" , 0 , False )
|
||||
|
||||
# experimental features
|
||||
add_option( "mm", "use main memory instead of memory mapped files" , 0 , True )
|
||||
add_option( "asio" , "Use Asynchronous IO (NOT READY YET)" , 0 , True )
|
||||
@ -170,6 +193,11 @@ add_option( "heapcheck", "link to heap-checking malloc-lib and look for memory l
|
||||
|
||||
add_option("smokedbprefix", "prefix to dbpath et al. for smoke tests", 1 , False )
|
||||
|
||||
for shortName in getThirdPartyShortNames():
|
||||
add_option( "use-system-" + shortName , "use system version of library " + shortName , 0 , True )
|
||||
|
||||
add_option( "use-system-all" , "use all system libraries " + shortName , 0 , True )
|
||||
|
||||
# --- environment setup ---
|
||||
|
||||
def removeIfInList( lst , thing ):
|
||||
@ -317,6 +345,7 @@ class InstallSetup:
|
||||
self.clientTestsDir = "client/examples/"
|
||||
|
||||
installSetup = InstallSetup()
|
||||
env["installSetup"] = installSetup
|
||||
if distBuild:
|
||||
installSetup.bannerDir = "distsrc"
|
||||
|
||||
@ -327,7 +356,7 @@ if has_option( "full" ):
|
||||
|
||||
# ------ SOURCE FILE SETUP -----------
|
||||
|
||||
commonFiles = Split( "pch.cpp buildinfo.cpp db/indexkey.cpp db/jsobj.cpp bson/oid.cpp db/json.cpp db/lasterror.cpp db/nonce.cpp db/queryutil.cpp db/querypattern.cpp db/projection.cpp shell/mongo.cpp db/security_common.cpp db/security_commands.cpp" )
|
||||
commonFiles = Split( "pch.cpp buildinfo.cpp db/indexkey.cpp db/jsobj.cpp bson/oid.cpp db/json.cpp db/lasterror.cpp db/nonce.cpp db/queryutil.cpp db/querypattern.cpp db/projection.cpp shell/mongo.cpp" )
|
||||
commonFiles += [ "util/background.cpp" , "util/util.cpp" , "util/file_allocator.cpp" ,
|
||||
"util/assert_util.cpp" , "util/log.cpp" , "util/ramlog.cpp" , "util/md5main.cpp" , "util/base64.cpp", "util/concurrency/vars.cpp", "util/concurrency/task.cpp", "util/debug_util.cpp",
|
||||
"util/concurrency/thread_pool.cpp", "util/password.cpp", "util/version.cpp", "util/signal_handlers.cpp",
|
||||
@ -343,8 +372,9 @@ coreDbFiles = [ "db/commands.cpp" ]
|
||||
coreServerFiles = [ "util/net/message_server_port.cpp" ,
|
||||
"client/parallel.cpp" , "db/common.cpp",
|
||||
"util/net/miniwebserver.cpp" , "db/dbwebserver.cpp" ,
|
||||
"db/matcher.cpp" , "db/dbcommands_generic.cpp" , "db/dbmessage.cpp" ]
|
||||
|
||||
"db/matcher.cpp" , "db/dbcommands_generic.cpp" , "db/dbmessage.cpp",
|
||||
"db/security_common.cpp", "db/security_commands.cpp",
|
||||
]
|
||||
mmapFiles = [ "util/mmap.cpp" ]
|
||||
|
||||
if has_option( "mm" ):
|
||||
@ -514,6 +544,8 @@ elif "sunos5" == os.sys.platform:
|
||||
solaris = True
|
||||
env.Append( CPPDEFINES=[ "__sunos__" ] )
|
||||
env.Append( LIBS=["socket","resolv"] )
|
||||
# Use GNU tar instead of Solaris tar
|
||||
env['TAR']="/opt/local/bin/tar"
|
||||
|
||||
elif os.sys.platform.startswith( "freebsd" ):
|
||||
nix = True
|
||||
@ -683,8 +715,11 @@ if nix:
|
||||
env.Append( LIBS=[] )
|
||||
|
||||
#make scons colorgcc friendly
|
||||
env['ENV']['HOME'] = os.environ['HOME']
|
||||
env['ENV']['TERM'] = os.environ['TERM']
|
||||
for key in ('HOME', 'TERM'):
|
||||
try:
|
||||
env['ENV'][key] = os.environ[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if linux and has_option( "sharedclient" ):
|
||||
env.Append( LINKFLAGS=" -Wl,--as-needed -Wl,-zdefs " )
|
||||
@ -757,21 +792,20 @@ if not windows:
|
||||
keyfile = "jstests/libs/key%s" % keysuffix
|
||||
os.chmod( keyfile , stat.S_IWUSR|stat.S_IRUSR )
|
||||
|
||||
for x in os.listdir( "third_party" ):
|
||||
if not x.endswith( ".py" ) or x.find( "#" ) >= 0:
|
||||
continue
|
||||
|
||||
shortName = x.rpartition( "." )[0]
|
||||
path = "third_party/%s" % x
|
||||
|
||||
|
||||
moduleFiles = {}
|
||||
for shortName in getThirdPartyShortNames():
|
||||
path = "third_party/%s.py" % shortName
|
||||
myModule = imp.load_module( "third_party_%s" % shortName , open( path , "r" ) , path , ( ".py" , "r" , imp.PY_SOURCE ) )
|
||||
fileLists = { "commonFiles" : commonFiles , "serverOnlyFiles" : serverOnlyFiles , "scriptingFiles" : scriptingFiles }
|
||||
|
||||
options_topass["windows"] = windows
|
||||
options_topass["nix"] = nix
|
||||
|
||||
myModule.configure( env , fileLists , options_topass )
|
||||
if has_option( "use-system-" + shortName ) or has_option( "use-system-all" ):
|
||||
print( "using system version of: " + shortName )
|
||||
myModule.configureSystem( env , fileLists , options_topass )
|
||||
else:
|
||||
myModule.configure( env , fileLists , options_topass )
|
||||
|
||||
coreServerFiles += scriptingFiles
|
||||
|
||||
@ -911,6 +945,7 @@ def doConfigure( myenv , shell=False ):
|
||||
m.configure( conf , myenv )
|
||||
|
||||
if solaris:
|
||||
conf.CheckLib( "rt" )
|
||||
conf.CheckLib( "nsl" )
|
||||
|
||||
if usev8:
|
||||
@ -1131,7 +1166,7 @@ if darwin or clientEnv["_HAVEPCAP"]:
|
||||
sniffEnv.Append( LIBS=[ "wpcap" ] )
|
||||
|
||||
sniffEnv.Prepend( LIBPATH=["."] )
|
||||
sniffEnv.Append( LIBS=[ "mongotestfiles" ] )
|
||||
sniffEnv.Prepend( LIBS=[ "mongotestfiles" ] )
|
||||
|
||||
sniffEnv.Program( "mongosniff" , "tools/sniffer.cpp" )
|
||||
|
||||
@ -1166,6 +1201,7 @@ elif not onlyServer:
|
||||
shellEnv = doConfigure( shellEnv , shell=True )
|
||||
|
||||
shellEnv.Prepend( LIBS=[ "mongoshellfiles"] )
|
||||
|
||||
mongo = shellEnv.Program( "mongo" , coreShellFiles )
|
||||
|
||||
|
||||
@ -1200,7 +1236,7 @@ def addSmoketest( name, deps ):
|
||||
else:
|
||||
target = name[5].lower() + name[6:]
|
||||
|
||||
addTest(name, deps, [ "python buildscripts/smoke.py " + " ".join(smokeFlags) + ' ' + target ])
|
||||
addTest(name, deps, [ utils.smoke_python_name() + " buildscripts/smoke.py " + " ".join(smokeFlags) + ' ' + target ])
|
||||
|
||||
addSmoketest( "smoke", [ add_exe( "test" ) ] )
|
||||
addSmoketest( "smokePerf", [ "perftest" ] )
|
||||
@ -1431,7 +1467,7 @@ def installBinary( e , name ):
|
||||
if (solaris or linux) and (not has_option("nostrip")):
|
||||
e.AddPostAction( inst, e.Action( 'strip ' + fullInstallName ) )
|
||||
|
||||
if linux and len( COMMAND_LINE_TARGETS ) == 1 and str( COMMAND_LINE_TARGETS[0] ) == "s3dist":
|
||||
if not has_option( "no-glibc-check" ) and linux and len( COMMAND_LINE_TARGETS ) == 1 and str( COMMAND_LINE_TARGETS[0] ) == "s3dist":
|
||||
e.AddPostAction( inst , checkGlibc )
|
||||
|
||||
if nix:
|
||||
@ -1462,7 +1498,7 @@ if installSetup.headers:
|
||||
if installSetup.clientSrc:
|
||||
for x in allClientFiles:
|
||||
x = str(x)
|
||||
env.Install( installDir + "/mongo/" + x.rpartition( "/" )[0] , x )
|
||||
env.Install( installDir + "/mongo/" + _rpartition( x, "/" )[0] , x )
|
||||
|
||||
#lib
|
||||
if installSetup.libraries:
|
||||
@ -1541,7 +1577,7 @@ def s3push( localName , remoteName=None , remotePrefix=None , fixName=True , pla
|
||||
remoteName = localName
|
||||
|
||||
if fixName:
|
||||
(root,dot,suffix) = localName.rpartition( "." )
|
||||
(root,dot,suffix) = _rpartition( localName, "." )
|
||||
name = remoteName + "-" + getSystemInstallName()
|
||||
name += remotePrefix
|
||||
if dot == "." :
|
||||
@ -1598,7 +1634,7 @@ def build_and_test_client(env, target, source):
|
||||
|
||||
call(scons_command + ["libmongoclient.a", "clientTests"], cwd=installDir)
|
||||
|
||||
return bool(call(["python", "buildscripts/smoke.py",
|
||||
return bool(call([utils.smoke_python_name(), "buildscripts/smoke.py",
|
||||
"--test-path", installDir, "client"]))
|
||||
env.Alias("clientBuild", [mongod, installDir], [build_and_test_client])
|
||||
env.AlwaysBuild("clientBuild")
|
||||
|
||||
@ -308,6 +308,8 @@ namespace mongo {
|
||||
bool operator==(const BSONElement& r) const {
|
||||
return woCompare( r , true ) == 0;
|
||||
}
|
||||
/** Returns true if elements are unequal. */
|
||||
bool operator!=(const BSONElement& r) const { return !operator==(r); }
|
||||
|
||||
/** Well ordered comparison.
|
||||
@return <0: l<r. 0:l==r. >0:l>r
|
||||
|
||||
@ -254,6 +254,11 @@ namespace mongo {
|
||||
|
||||
BSONElement getFieldUsingIndexNames(const char *fieldName, const BSONObj &indexKey) const;
|
||||
|
||||
/** arrays are bson objects with numeric and increasing field names
|
||||
@return true if field names are numeric and increasing
|
||||
*/
|
||||
bool couldBeArray() const;
|
||||
|
||||
/** @return the raw data of the object */
|
||||
const char *objdata() const {
|
||||
return _objdata;
|
||||
@ -360,6 +365,7 @@ namespace mongo {
|
||||
string md5() const;
|
||||
|
||||
bool operator==( const BSONObj& other ) const { return equal( other ); }
|
||||
bool operator!=(const BSONObj& other) const { return !operator==( other); }
|
||||
|
||||
enum MatchType {
|
||||
Equality = 0,
|
||||
|
||||
@ -469,17 +469,14 @@ namespace mongo {
|
||||
Use BinDataGeneral if you don't care about the type.
|
||||
@param data the byte array
|
||||
*/
|
||||
BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const char *data ) {
|
||||
BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const void *data ) {
|
||||
_b.appendNum( (char) BinData );
|
||||
_b.appendStr( fieldName );
|
||||
_b.appendNum( len );
|
||||
_b.appendNum( (char) type );
|
||||
_b.appendBuf( (void *) data, len );
|
||||
_b.appendBuf( data, len );
|
||||
return *this;
|
||||
}
|
||||
BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const unsigned char *data ) {
|
||||
return appendBinData(fieldName, len, type, (const char *) data);
|
||||
}
|
||||
|
||||
/**
|
||||
Subtype 2 is deprecated.
|
||||
@ -487,13 +484,13 @@ namespace mongo {
|
||||
@param data a byte array
|
||||
@param len the length of data
|
||||
*/
|
||||
BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldName , const char * data , int len ) {
|
||||
BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldName , const void * data , int len ) {
|
||||
_b.appendNum( (char) BinData );
|
||||
_b.appendStr( fieldName );
|
||||
_b.appendNum( len + 4 );
|
||||
_b.appendNum( (char)0x2 );
|
||||
_b.appendNum( len );
|
||||
_b.appendBuf( (void *) data, len );
|
||||
_b.appendBuf( data, len );
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -755,6 +752,8 @@ namespace mongo {
|
||||
|
||||
int len() const { return _b.len(); }
|
||||
|
||||
int arrSize() const { return _i; }
|
||||
|
||||
private:
|
||||
void fill( const StringData& name ) {
|
||||
char *r;
|
||||
|
||||
@ -61,7 +61,7 @@ namespace mongo {
|
||||
|
||||
// accessors
|
||||
const char* data() const { return _data; }
|
||||
const unsigned size() const { return _size; }
|
||||
unsigned size() const { return _size; }
|
||||
|
||||
private:
|
||||
const char* const _data; // is always null terminated
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cfloat>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@ -36,7 +37,7 @@ namespace mongo {
|
||||
const int BSONObjMaxUserSize = 16 * 1024 * 1024;
|
||||
|
||||
/*
|
||||
Sometimeswe we need objects slightly larger - an object in the replication local.oplog
|
||||
Sometimes we need objects slightly larger - an object in the replication local.oplog
|
||||
is slightly larger than a user object for example.
|
||||
*/
|
||||
const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 );
|
||||
@ -65,6 +66,8 @@ namespace mongo {
|
||||
if( p == buf ) {
|
||||
if( sz <= SZ ) return buf;
|
||||
void *d = malloc(sz);
|
||||
if ( d == 0 )
|
||||
msgasserted( 15912 , "out of memory StackAllocator::Realloc" );
|
||||
memcpy(d, p, SZ);
|
||||
return d;
|
||||
}
|
||||
@ -113,6 +116,8 @@ namespace mongo {
|
||||
if ( maxSize && size > maxSize ) {
|
||||
al.Free(data);
|
||||
data = (char*)al.Malloc(maxSize);
|
||||
if ( data == 0 )
|
||||
msgasserted( 15913 , "out of memory BufBuilder::reset" );
|
||||
size = maxSize;
|
||||
}
|
||||
}
|
||||
@ -226,42 +231,51 @@ namespace mongo {
|
||||
void decouple(); // not allowed. not implemented.
|
||||
};
|
||||
|
||||
namespace {
|
||||
#if defined(_WIN32)
|
||||
#pragma warning( push )
|
||||
// warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS.
|
||||
#pragma warning( disable : 4996 )
|
||||
int (*mongo_snprintf)(char *str, size_t size, const char *format, ...) = &sprintf_s;
|
||||
#else
|
||||
int (*mongo_snprintf)(char *str, size_t size, const char *format, ...) = &snprintf;
|
||||
#endif
|
||||
}
|
||||
|
||||
/** stringstream deals with locale so this is a lot faster than std::stringstream for UTF8 */
|
||||
class StringBuilder {
|
||||
public:
|
||||
static const size_t MONGO_DBL_SIZE = 3 + DBL_MANT_DIG - DBL_MIN_EXP;
|
||||
static const size_t MONGO_S32_SIZE = 12;
|
||||
static const size_t MONGO_U32_SIZE = 11;
|
||||
static const size_t MONGO_S64_SIZE = 23;
|
||||
static const size_t MONGO_U64_SIZE = 22;
|
||||
static const size_t MONGO_S16_SIZE = 7;
|
||||
|
||||
StringBuilder( int initsize=256 )
|
||||
: _buf( initsize ) {
|
||||
}
|
||||
|
||||
StringBuilder& operator<<( double x ) {
|
||||
return SBNUM( x , 25 , "%g" );
|
||||
return SBNUM( x , MONGO_DBL_SIZE , "%g" );
|
||||
}
|
||||
StringBuilder& operator<<( int x ) {
|
||||
return SBNUM( x , 11 , "%d" );
|
||||
return SBNUM( x , MONGO_S32_SIZE , "%d" );
|
||||
}
|
||||
StringBuilder& operator<<( unsigned x ) {
|
||||
return SBNUM( x , 11 , "%u" );
|
||||
return SBNUM( x , MONGO_U32_SIZE , "%u" );
|
||||
}
|
||||
StringBuilder& operator<<( long x ) {
|
||||
return SBNUM( x , 22 , "%ld" );
|
||||
return SBNUM( x , MONGO_S64_SIZE , "%ld" );
|
||||
}
|
||||
StringBuilder& operator<<( unsigned long x ) {
|
||||
return SBNUM( x , 22 , "%lu" );
|
||||
return SBNUM( x , MONGO_U64_SIZE , "%lu" );
|
||||
}
|
||||
StringBuilder& operator<<( long long x ) {
|
||||
return SBNUM( x , 22 , "%lld" );
|
||||
return SBNUM( x , MONGO_S64_SIZE , "%lld" );
|
||||
}
|
||||
StringBuilder& operator<<( unsigned long long x ) {
|
||||
return SBNUM( x , 22 , "%llu" );
|
||||
return SBNUM( x , MONGO_U64_SIZE , "%llu" );
|
||||
}
|
||||
StringBuilder& operator<<( short x ) {
|
||||
return SBNUM( x , 8 , "%hd" );
|
||||
return SBNUM( x , MONGO_S16_SIZE , "%hd" );
|
||||
}
|
||||
StringBuilder& operator<<( char c ) {
|
||||
_buf.grow( 1 )[0] = c;
|
||||
@ -269,10 +283,12 @@ namespace mongo {
|
||||
}
|
||||
|
||||
void appendDoubleNice( double x ) {
|
||||
int prev = _buf.l;
|
||||
char * start = _buf.grow( 32 );
|
||||
int z = sprintf( start , "%.16g" , x );
|
||||
const int prev = _buf.l;
|
||||
const int maxSize = 32;
|
||||
char * start = _buf.grow( maxSize );
|
||||
int z = mongo_snprintf( start , maxSize , "%.16g" , x );
|
||||
assert( z >= 0 );
|
||||
assert( z < maxSize );
|
||||
_buf.l = prev + z;
|
||||
if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strchr(start, 'N') == 0 ) {
|
||||
write( ".0" , 2 );
|
||||
@ -304,15 +320,12 @@ namespace mongo {
|
||||
template <typename T>
|
||||
StringBuilder& SBNUM(T val,int maxSize,const char *macro) {
|
||||
int prev = _buf.l;
|
||||
int z = sprintf( _buf.grow(maxSize) , macro , (val) );
|
||||
int z = mongo_snprintf( _buf.grow(maxSize) , maxSize , macro , (val) );
|
||||
assert( z >= 0 );
|
||||
assert( z < maxSize );
|
||||
_buf.l = prev + z;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
|
||||
import sys
|
||||
import os
|
||||
import os, os.path
|
||||
import utils
|
||||
import time
|
||||
from optparse import OptionParser
|
||||
|
||||
cwd = os.getcwd();
|
||||
if cwd.find("buildscripts" ) > 0 :
|
||||
cwd = cwd.partition( "buildscripts" )[0]
|
||||
# set cwd to the root mongo dir, one level up from this
|
||||
# file's location (if we're not already running from there)
|
||||
cwd = os.getcwd()
|
||||
if os.path.basename(cwd) == 'buildscripts':
|
||||
cwd = os.path.dirname(cwd)
|
||||
|
||||
print( "cwd [" + cwd + "]" )
|
||||
|
||||
@ -38,7 +40,7 @@ def killprocs( signal="" ):
|
||||
if not shouldKill( x ):
|
||||
continue
|
||||
|
||||
pid = x.partition( " " )[0]
|
||||
pid = x.split( " " )[0]
|
||||
print( "killing: " + x )
|
||||
utils.execsys( "/bin/kill " + signal + " " + pid )
|
||||
killed = killed + 1
|
||||
|
||||
@ -3,6 +3,8 @@ import re
|
||||
import socket
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
|
||||
# various utilities that are handy
|
||||
|
||||
def getAllSourceFiles( arr=None , prefix="." ):
|
||||
@ -134,3 +136,34 @@ def didMongodStart( port=27017 , timeout=20 ):
|
||||
timeout = timeout - 1
|
||||
return False
|
||||
|
||||
def smoke_python_name():
|
||||
# if this script is being run by py2.5 or greater,
|
||||
# then we assume that "python" points to a 2.5 or
|
||||
# greater python VM. otherwise, explicitly use 2.5
|
||||
# which we assume to be installed.
|
||||
min_version_tuple = (2, 5)
|
||||
try:
|
||||
if sys.version_info >= min_version_tuple:
|
||||
return sys.executable
|
||||
except AttributeError:
|
||||
# In case the version of Python is somehow missing sys.version_info or sys.executable.
|
||||
pass
|
||||
|
||||
import subprocess
|
||||
version = re.compile(r'[Pp]ython ([\d\.]+)', re.MULTILINE)
|
||||
binaries = ['python2.5', 'python2.6', 'python2.7', 'python25', 'python26', 'python27', 'python']
|
||||
for binary in binaries:
|
||||
try:
|
||||
# py-2.4 compatible replacement for shell backticks
|
||||
out, err = subprocess.Popen([binary, '-V'], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
|
||||
for stream in (out, err):
|
||||
match = version.search(stream)
|
||||
if match:
|
||||
versiontuple = tuple(map(int, match.group(1).split('.')))
|
||||
if versiontuple >= min_version_tuple:
|
||||
return binary
|
||||
except:
|
||||
pass
|
||||
# if that all fails, fall back to "python"
|
||||
return "python"
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ namespace mongo {
|
||||
|
||||
void PoolForHost::done( DBConnectionPool * pool, DBClientBase * c ) {
|
||||
if ( _pool.size() >= _maxPerHost ) {
|
||||
pool->onDestory( c );
|
||||
pool->onDestroy( c );
|
||||
delete c;
|
||||
}
|
||||
else {
|
||||
@ -55,7 +55,7 @@ namespace mongo {
|
||||
_pool.pop();
|
||||
|
||||
if ( ! sc.ok( now ) ) {
|
||||
pool->onDestory( sc.conn );
|
||||
pool->onDestroy( sc.conn );
|
||||
delete sc.conn;
|
||||
continue;
|
||||
}
|
||||
@ -145,9 +145,15 @@ namespace mongo {
|
||||
PoolForHost& p = _pools[PoolKey(host,socketTimeout)];
|
||||
p.createdOne( conn );
|
||||
}
|
||||
|
||||
onCreate( conn );
|
||||
onHandedOut( conn );
|
||||
|
||||
try {
|
||||
onCreate( conn );
|
||||
onHandedOut( conn );
|
||||
}
|
||||
catch ( std::exception& e ) {
|
||||
delete conn;
|
||||
throw;
|
||||
}
|
||||
|
||||
return conn;
|
||||
}
|
||||
@ -155,7 +161,13 @@ namespace mongo {
|
||||
DBClientBase* DBConnectionPool::get(const ConnectionString& url, double socketTimeout) {
|
||||
DBClientBase * c = _get( url.toString() , socketTimeout );
|
||||
if ( c ) {
|
||||
onHandedOut( c );
|
||||
try {
|
||||
onHandedOut( c );
|
||||
}
|
||||
catch ( std::exception& e ) {
|
||||
delete c;
|
||||
throw;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -169,7 +181,13 @@ namespace mongo {
|
||||
DBClientBase* DBConnectionPool::get(const string& host, double socketTimeout) {
|
||||
DBClientBase * c = _get( host , socketTimeout );
|
||||
if ( c ) {
|
||||
onHandedOut( c );
|
||||
try {
|
||||
onHandedOut( c );
|
||||
}
|
||||
catch ( std::exception& e ) {
|
||||
delete c;
|
||||
throw;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@ -185,7 +203,7 @@ namespace mongo {
|
||||
|
||||
void DBConnectionPool::release(const string& host, DBClientBase *c) {
|
||||
if ( c->isFailed() ) {
|
||||
onDestory( c );
|
||||
onDestroy( c );
|
||||
delete c;
|
||||
return;
|
||||
}
|
||||
@ -228,12 +246,12 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void DBConnectionPool::onDestory( DBClientBase * conn ) {
|
||||
void DBConnectionPool::onDestroy( DBClientBase * conn ) {
|
||||
if ( _hooks->size() == 0 )
|
||||
return;
|
||||
|
||||
for ( list<DBConnectionHook*>::iterator i = _hooks->begin(); i != _hooks->end(); i++ ) {
|
||||
(*i)->onDestory( conn );
|
||||
(*i)->onDestroy( conn );
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,7 +375,7 @@ namespace mongo {
|
||||
|
||||
for ( size_t i=0; i<toDelete.size(); i++ ) {
|
||||
try {
|
||||
onDestory( toDelete[i] );
|
||||
onDestroy( toDelete[i] );
|
||||
delete toDelete[i];
|
||||
}
|
||||
catch ( ... ) {
|
||||
@ -387,7 +405,7 @@ namespace mongo {
|
||||
if ( _conn ) {
|
||||
if ( ! _conn->isFailed() ) {
|
||||
/* see done() comments above for why we log this line */
|
||||
log() << "~ScopedDbConnection: _conn != null" << endl;
|
||||
log() << "scoped connection to " << _conn->getServerAddress() << " not being returned to the pool" << endl;
|
||||
}
|
||||
kill();
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ namespace mongo {
|
||||
virtual ~DBConnectionHook() {}
|
||||
virtual void onCreate( DBClientBase * conn ) {}
|
||||
virtual void onHandedOut( DBClientBase * conn ) {}
|
||||
virtual void onDestory( DBClientBase * conn ) {}
|
||||
virtual void onDestroy( DBClientBase * conn ) {}
|
||||
};
|
||||
|
||||
/** Database connection pool.
|
||||
@ -119,7 +119,7 @@ namespace mongo {
|
||||
|
||||
void onCreate( DBClientBase * conn );
|
||||
void onHandedOut( DBClientBase * conn );
|
||||
void onDestory( DBClientBase * conn );
|
||||
void onDestroy( DBClientBase * conn );
|
||||
|
||||
void flush();
|
||||
|
||||
|
||||
@ -247,6 +247,11 @@ namespace mongo {
|
||||
return o["ok"].trueValue();
|
||||
}
|
||||
|
||||
bool DBClientWithCommands::isNotMasterErrorString( const BSONElement& e ) {
|
||||
return e.type() == String && str::contains( e.valuestr() , "not master" );
|
||||
}
|
||||
|
||||
|
||||
enum QueryOptions DBClientWithCommands::availableOptions() {
|
||||
if ( !_haveCachedAvailableOptions ) {
|
||||
BSONObj ret;
|
||||
@ -599,6 +604,20 @@ namespace mongo {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
inline bool DBClientConnection::runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options) {
|
||||
if ( DBClientWithCommands::runCommand( dbname , cmd , info , options ) )
|
||||
return true;
|
||||
|
||||
if ( clientSet && isNotMasterErrorString( info["errmsg"] ) ) {
|
||||
clientSet->isntMaster();
|
||||
// At this point, we've probably deleted *this* object, do *not* use afterward
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void DBClientConnection::_checkConnection() {
|
||||
if ( !_failed )
|
||||
return;
|
||||
@ -933,6 +952,7 @@ namespace mongo {
|
||||
an exception. we should make it return void and just throw an exception anytime
|
||||
it fails
|
||||
*/
|
||||
checkConnection();
|
||||
try {
|
||||
if ( !port().call(toSend, response) ) {
|
||||
_failed = true;
|
||||
@ -982,8 +1002,7 @@ namespace mongo {
|
||||
if ( clientSet && nReturned ) {
|
||||
assert(data);
|
||||
BSONObj o(data);
|
||||
BSONElement e = getErrField(o);
|
||||
if ( e.type() == String && str::contains( e.valuestr() , "not master" ) ) {
|
||||
if ( isNotMasterErrorString( getErrField(o) ) ) {
|
||||
clientSet->isntMaster();
|
||||
}
|
||||
}
|
||||
|
||||
@ -721,8 +721,12 @@ namespace mongo {
|
||||
}
|
||||
|
||||
protected:
|
||||
/** if the result of a command is ok*/
|
||||
bool isOk(const BSONObj&);
|
||||
|
||||
/** if the element contains a not master error */
|
||||
bool isNotMasterErrorString( const BSONElement& e );
|
||||
|
||||
BSONObj _countCmd(const string &ns, const BSONObj& query, int options, int limit, int skip );
|
||||
|
||||
enum QueryOptions availableOptions();
|
||||
@ -892,6 +896,8 @@ namespace mongo {
|
||||
unsigned long long query( boost::function<void(const BSONObj&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
|
||||
unsigned long long query( boost::function<void(DBClientCursorBatchIterator&)> f, const string& ns, Query query, const BSONObj *fieldsToReturn = 0, int queryOptions = 0);
|
||||
|
||||
virtual bool runCommand(const string &dbname, const BSONObj& cmd, BSONObj &info, int options=0);
|
||||
|
||||
/**
|
||||
@return true if this connection is currently in a failed state. When autoreconnect is on,
|
||||
a connection will transition back to an ok state after reconnecting.
|
||||
|
||||
@ -72,6 +72,15 @@ namespace mongo {
|
||||
|
||||
} replicaSetMonitorWatcher;
|
||||
|
||||
string seedString( const vector<HostAndPort>& servers ){
|
||||
string seedStr;
|
||||
for ( unsigned i = 0; i < servers.size(); i++ ){
|
||||
seedStr += servers[i].toString();
|
||||
if( i < servers.size() - 1 ) seedStr += ",";
|
||||
}
|
||||
|
||||
return seedStr;
|
||||
}
|
||||
|
||||
ReplicaSetMonitor::ReplicaSetMonitor( const string& name , const vector<HostAndPort>& servers )
|
||||
: _lock( "ReplicaSetMonitor instance" ) , _checkConnectionLock( "ReplicaSetMonitor check connection lock" ), _name( name ) , _master(-1), _nextSlave(0) {
|
||||
@ -82,28 +91,36 @@ namespace mongo {
|
||||
warning() << "replica set name empty, first node: " << servers[0] << endl;
|
||||
}
|
||||
|
||||
log() << "starting new replica set monitor for replica set " << _name << " with seed of " << seedString( servers ) << endl;
|
||||
|
||||
string errmsg;
|
||||
for ( unsigned i = 0; i < servers.size(); i++ ) {
|
||||
|
||||
for ( unsigned i=0; i<servers.size(); i++ ) {
|
||||
|
||||
bool haveAlready = false;
|
||||
for ( unsigned n = 0; n < _nodes.size() && ! haveAlready; n++ )
|
||||
haveAlready = ( _nodes[n].addr == servers[i] );
|
||||
if( haveAlready ) continue;
|
||||
// Don't check servers we have already
|
||||
if( _find_inlock( servers[i] ) >= 0 ) continue;
|
||||
|
||||
auto_ptr<DBClientConnection> conn( new DBClientConnection( true , 0, 5.0 ) );
|
||||
if (!conn->connect( servers[i] , errmsg ) ) {
|
||||
log(1) << "error connecting to seed " << servers[i] << ": " << errmsg << endl;
|
||||
try{
|
||||
if( ! conn->connect( servers[i] , errmsg ) ){
|
||||
throw DBException( errmsg, 15928 );
|
||||
}
|
||||
log() << "successfully connected to seed " << servers[i] << " for replica set " << this->_name << endl;
|
||||
}
|
||||
catch( DBException& e ){
|
||||
log() << "error connecting to seed " << servers[i] << causedBy( e ) << endl;
|
||||
// skip seeds that don't work
|
||||
continue;
|
||||
}
|
||||
|
||||
_nodes.push_back( Node( servers[i] , conn.release() ) );
|
||||
|
||||
int myLoc = _nodes.size() - 1;
|
||||
string maybePrimary;
|
||||
_checkConnection( _nodes[myLoc].conn.get() , maybePrimary, false, myLoc );
|
||||
_checkConnection( conn.get(), maybePrimary, false, -1 );
|
||||
}
|
||||
|
||||
// Check everything to get the first data
|
||||
_check( true );
|
||||
|
||||
log() << "replica set monitor for replica set " << _name << " started, address is " << getServerAddress() << endl;
|
||||
|
||||
}
|
||||
|
||||
ReplicaSetMonitor::~ReplicaSetMonitor() {
|
||||
@ -164,18 +181,21 @@ namespace mongo {
|
||||
}
|
||||
|
||||
string ReplicaSetMonitor::getServerAddress() const {
|
||||
scoped_lock lk( _lock );
|
||||
return _getServerAddress_inlock();
|
||||
}
|
||||
|
||||
string ReplicaSetMonitor::_getServerAddress_inlock() const {
|
||||
StringBuilder ss;
|
||||
if ( _name.size() )
|
||||
ss << _name << "/";
|
||||
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
for ( unsigned i=0; i<_nodes.size(); i++ ) {
|
||||
if ( i > 0 )
|
||||
ss << ",";
|
||||
ss << _nodes[i].addr.toString();
|
||||
}
|
||||
for ( unsigned i=0; i<_nodes.size(); i++ ) {
|
||||
if ( i > 0 )
|
||||
ss << ",";
|
||||
ss << _nodes[i].addr.toString();
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
@ -191,6 +211,7 @@ namespace mongo {
|
||||
|
||||
void ReplicaSetMonitor::notifyFailure( const HostAndPort& server ) {
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
if ( _master >= 0 && _master < (int)_nodes.size() ) {
|
||||
if ( server == _nodes[_master].addr ) {
|
||||
_nodes[_master].ok = false;
|
||||
@ -204,6 +225,7 @@ namespace mongo {
|
||||
HostAndPort ReplicaSetMonitor::getMaster() {
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
assert(_master < static_cast<int>(_nodes.size()));
|
||||
if ( _master >= 0 && _nodes[_master].ok )
|
||||
return _nodes[_master].addr;
|
||||
}
|
||||
@ -212,6 +234,7 @@ namespace mongo {
|
||||
|
||||
scoped_lock lk( _lock );
|
||||
uassert( 10009 , str::stream() << "ReplicaSetMonitor no master found for set: " << _name , _master >= 0 );
|
||||
assert(_master < static_cast<int>(_nodes.size()));
|
||||
return _nodes[_master].addr;
|
||||
}
|
||||
|
||||
@ -247,43 +270,32 @@ namespace mongo {
|
||||
}
|
||||
|
||||
HostAndPort ReplicaSetMonitor::getSlave() {
|
||||
LOG(2) << "dbclient_rs getSlave " << getServerAddress() << endl;
|
||||
|
||||
LOG(2) << "selecting new slave from replica set " << getServerAddress() << endl;
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
// Logic is to retry three times for any secondary node, if we can't find any secondary, we'll take
|
||||
// any "ok" node
|
||||
// TODO: Could this query hidden nodes?
|
||||
const int MAX = 3;
|
||||
for ( int xxx=0; xxx<MAX; xxx++ ) {
|
||||
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
unsigned i = 0;
|
||||
for ( ; i<_nodes.size(); i++ ) {
|
||||
_nextSlave = ( _nextSlave + 1 ) % _nodes.size();
|
||||
if ( _nextSlave == _master ){
|
||||
LOG(2) << "not selecting " << _nodes[_nextSlave] << " as it is the current master" << endl;
|
||||
continue;
|
||||
}
|
||||
if ( _nodes[ _nextSlave ].okForSecondaryQueries() || ( _nodes[ _nextSlave ].ok && ( xxx + 1 ) >= MAX ) )
|
||||
return _nodes[ _nextSlave ].addr;
|
||||
|
||||
LOG(2) << "not selecting " << _nodes[_nextSlave] << " as it is not ok to use" << endl;
|
||||
}
|
||||
|
||||
for ( unsigned ii = 0; ii < _nodes.size(); ii++ ) {
|
||||
_nextSlave = ( _nextSlave + 1 ) % _nodes.size();
|
||||
if ( _nextSlave != _master ) {
|
||||
if ( _nodes[ _nextSlave ].okForSecondaryQueries() )
|
||||
return _nodes[ _nextSlave ].addr;
|
||||
LOG(2) << "dbclient_rs getSlave not selecting " << _nodes[_nextSlave] << ", not currently okForSecondaryQueries" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
check(false);
|
||||
if( _master >= 0 ) {
|
||||
assert( static_cast<unsigned>(_master) < _nodes.size() );
|
||||
LOG(2) << "dbclient_rs getSlave no member in secondary state found, returning primary " << _nodes[ _master ] << endl;
|
||||
return _nodes[_master].addr;
|
||||
}
|
||||
|
||||
LOG(2) << "no suitable slave nodes found, returning default node " << _nodes[ 0 ] << endl;
|
||||
|
||||
LOG(2) << "dbclient_rs getSlave no suitable member found, returning first node " << _nodes[ 0 ] << endl;
|
||||
assert( _nodes.size() > 0 );
|
||||
return _nodes[0].addr;
|
||||
}
|
||||
|
||||
/**
|
||||
* notify the monitor that server has faild
|
||||
* notify the monitor that server has failed
|
||||
*/
|
||||
void ReplicaSetMonitor::notifySlaveFailure( const HostAndPort& server ) {
|
||||
int x = _find( server );
|
||||
@ -293,12 +305,29 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void ReplicaSetMonitor::_checkStatus(DBClientConnection *conn) {
|
||||
void ReplicaSetMonitor::_checkStatus( const string& hostAddr ) {
|
||||
BSONObj status;
|
||||
|
||||
if (!conn->runCommand("admin", BSON("replSetGetStatus" << 1), status) ||
|
||||
!status.hasField("members") ||
|
||||
status["members"].type() != Array) {
|
||||
/* replSetGetStatus requires admin auth so use a connection from the pool,
|
||||
* which are authenticated with the keyFile credentials.
|
||||
*/
|
||||
ScopedDbConnection authenticatedConn( hostAddr );
|
||||
|
||||
if ( !authenticatedConn->runCommand( "admin", BSON( "replSetGetStatus" << 1 ), status )) {
|
||||
LOG(1) << "dbclient_rs replSetGetStatus failed" << endl;
|
||||
authenticatedConn.done(); // connection worked properly, but we got an error from server
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we return when finished
|
||||
authenticatedConn.done();
|
||||
|
||||
if( !status.hasField("members") ) {
|
||||
log() << "dbclient_rs error expected members field in replSetGetStatus result" << endl;
|
||||
return;
|
||||
}
|
||||
if( status["members"].type() != Array) {
|
||||
log() << "dbclient_rs error expected members field in replSetGetStatus result to be an array" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -324,51 +353,172 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void ReplicaSetMonitor::_checkHosts( const BSONObj& hostList, bool& changed ) {
|
||||
NodeDiff ReplicaSetMonitor::_getHostDiff_inlock( const BSONObj& hostList ){
|
||||
|
||||
NodeDiff diff;
|
||||
set<int> nodesFound;
|
||||
|
||||
int index = 0;
|
||||
BSONObjIterator hi( hostList );
|
||||
while( hi.more() ){
|
||||
|
||||
string toCheck = hi.next().String();
|
||||
int nodeIndex = _find_inlock( toCheck );
|
||||
|
||||
// Node-to-add
|
||||
if( nodeIndex < 0 ) diff.first.insert( toCheck );
|
||||
else nodesFound.insert( nodeIndex );
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < _nodes.size(); i++ ){
|
||||
if( nodesFound.find( static_cast<int>(i) ) == nodesFound.end() ) diff.second.insert( static_cast<int>(i) );
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
bool ReplicaSetMonitor::_shouldChangeHosts( const BSONObj& hostList, bool inlock ){
|
||||
|
||||
int origHosts = 0;
|
||||
if( ! inlock ){
|
||||
scoped_lock lk( _lock );
|
||||
origHosts = _nodes.size();
|
||||
}
|
||||
else origHosts = _nodes.size();
|
||||
int numHosts = 0;
|
||||
bool changed = false;
|
||||
|
||||
BSONObjIterator hi(hostList);
|
||||
while ( hi.more() ) {
|
||||
string toCheck = hi.next().String();
|
||||
|
||||
if ( _find( toCheck ) >= 0 )
|
||||
continue;
|
||||
numHosts++;
|
||||
int index = 0;
|
||||
if( ! inlock ) index = _find( toCheck );
|
||||
else index = _find_inlock( toCheck );
|
||||
|
||||
if ( index >= 0 ) continue;
|
||||
|
||||
HostAndPort h( toCheck );
|
||||
DBClientConnection * newConn = new DBClientConnection( true, 0, 5.0 );
|
||||
string temp;
|
||||
newConn->connect( h , temp );
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
if ( _find_inlock( toCheck ) >= 0 ) {
|
||||
// we need this check inside the lock so there isn't thread contention on adding to vector
|
||||
continue;
|
||||
}
|
||||
_nodes.push_back( Node( h , newConn ) );
|
||||
}
|
||||
log() << "updated set (" << _name << ") to: " << getServerAddress() << endl;
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return (changed || origHosts != numHosts) && numHosts > 0;
|
||||
|
||||
}
|
||||
|
||||
void ReplicaSetMonitor::_checkHosts( const BSONObj& hostList, bool& changed ) {
|
||||
|
||||
// Fast path, still requires intermittent locking
|
||||
if( ! _shouldChangeHosts( hostList, false ) ){
|
||||
changed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Slow path, double-checked though
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
// Our host list may have changed while waiting for another thread in the meantime,
|
||||
// so double-check here
|
||||
// TODO: Do we really need this much protection, this should be pretty rare and not
|
||||
// triggered from lots of threads, duping old behavior for safety
|
||||
if( ! _shouldChangeHosts( hostList, true ) ){
|
||||
changed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// LogLevel can be pretty low, since replica set reconfiguration should be pretty rare and
|
||||
// we want to record our changes
|
||||
log() << "changing hosts to " << hostList << " from " << _getServerAddress_inlock() << endl;
|
||||
|
||||
NodeDiff diff = _getHostDiff_inlock( hostList );
|
||||
set<string> added = diff.first;
|
||||
set<int> removed = diff.second;
|
||||
|
||||
assert( added.size() > 0 || removed.size() > 0 );
|
||||
changed = true;
|
||||
|
||||
// Delete from the end so we don't invalidate as we delete, delete indices are ascending
|
||||
for( set<int>::reverse_iterator i = removed.rbegin(), end = removed.rend(); i != end; ++i ){
|
||||
|
||||
log() << "erasing host " << _nodes[ *i ] << " from replica set " << this->_name << endl;
|
||||
_nodes.erase( _nodes.begin() + *i );
|
||||
}
|
||||
|
||||
// Add new nodes
|
||||
for( set<string>::iterator i = added.begin(), end = added.end(); i != end; ++i ){
|
||||
|
||||
log() << "trying to add new host " << *i << " to replica set " << this->_name << endl;
|
||||
|
||||
// Connect to new node
|
||||
HostAndPort h( *i );
|
||||
DBClientConnection * newConn = new DBClientConnection( true, 0, 5.0 );
|
||||
|
||||
string errmsg;
|
||||
try{
|
||||
if( ! newConn->connect( h , errmsg ) ){
|
||||
throw DBException( errmsg, 15927 );
|
||||
}
|
||||
log() << "successfully connected to new host " << *i << " in replica set " << this->_name << endl;
|
||||
}
|
||||
catch( DBException& e ){
|
||||
warning() << "cannot connect to new host " << *i << " to replica set " << this->_name << causedBy( e ) << endl;
|
||||
}
|
||||
|
||||
_nodes.push_back( Node( h , newConn ) );
|
||||
}
|
||||
|
||||
// Invalidate the cached _master index since the _nodes structure has
|
||||
// already been modified.
|
||||
_master = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool ReplicaSetMonitor::_checkConnection( DBClientConnection * c , string& maybePrimary , bool verbose , int nodesOffset ) {
|
||||
bool ReplicaSetMonitor::_checkConnection( DBClientConnection* conn,
|
||||
string& maybePrimary, bool verbose, int nodesOffset ) {
|
||||
|
||||
assert( conn );
|
||||
|
||||
scoped_lock lk( _checkConnectionLock );
|
||||
bool isMaster = false;
|
||||
bool changed = false;
|
||||
bool errorOccured = false;
|
||||
|
||||
if ( nodesOffset >= 0 ){
|
||||
scoped_lock lk( _lock );
|
||||
if ( !_checkConnMatch_inlock( conn, nodesOffset )) {
|
||||
/* Another thread modified _nodes -> invariant broken.
|
||||
* This also implies that another thread just passed
|
||||
* through here and refreshed _nodes. So no need to do
|
||||
* duplicate work.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Timer t;
|
||||
BSONObj o;
|
||||
c->isMaster(isMaster, &o);
|
||||
|
||||
conn->isMaster( isMaster, &o );
|
||||
|
||||
if ( o["setName"].type() != String || o["setName"].String() != _name ) {
|
||||
warning() << "node: " << c->getServerAddress() << " isn't a part of set: " << _name
|
||||
warning() << "node: " << conn->getServerAddress()
|
||||
<< " isn't a part of set: " << _name
|
||||
<< " ismaster: " << o << endl;
|
||||
if ( nodesOffset >= 0 )
|
||||
|
||||
if ( nodesOffset >= 0 ) {
|
||||
scoped_lock lk( _lock );
|
||||
_nodes[nodesOffset].ok = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( nodesOffset >= 0 ) {
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
_nodes[nodesOffset].pingTimeMillis = t.millis();
|
||||
_nodes[nodesOffset].hidden = o["hidden"].trueValue();
|
||||
_nodes[nodesOffset].secondary = o["secondary"].trueValue();
|
||||
@ -377,26 +527,42 @@ namespace mongo {
|
||||
_nodes[nodesOffset].lastIsMaster = o.copy();
|
||||
}
|
||||
|
||||
log( ! verbose ) << "ReplicaSetMonitor::_checkConnection: " << c->toString() << ' ' << o << endl;
|
||||
log( ! verbose ) << "ReplicaSetMonitor::_checkConnection: " << conn->toString()
|
||||
<< ' ' << o << endl;
|
||||
|
||||
// add other nodes
|
||||
BSONArrayBuilder b;
|
||||
if ( o["hosts"].type() == Array ) {
|
||||
if ( o["primary"].type() == String )
|
||||
maybePrimary = o["primary"].String();
|
||||
|
||||
_checkHosts(o["hosts"].Obj(), changed);
|
||||
BSONObjIterator it( o["hosts"].Obj() );
|
||||
while( it.more() ) b.append( it.next() );
|
||||
}
|
||||
if (o.hasField("passives") && o["passives"].type() == Array) {
|
||||
_checkHosts(o["passives"].Obj(), changed);
|
||||
}
|
||||
|
||||
_checkStatus(c);
|
||||
|
||||
if (o.hasField("passives") && o["passives"].type() == Array) {
|
||||
BSONObjIterator it( o["passives"].Obj() );
|
||||
while( it.more() ) b.append( it.next() );
|
||||
}
|
||||
|
||||
_checkHosts( b.arr(), changed);
|
||||
_checkStatus( conn->getServerAddress() );
|
||||
|
||||
}
|
||||
catch ( std::exception& e ) {
|
||||
log( ! verbose ) << "ReplicaSetMonitor::_checkConnection: caught exception " << c->toString() << ' ' << e.what() << endl;
|
||||
_nodes[nodesOffset].ok = false;
|
||||
log( ! verbose ) << "ReplicaSetMonitor::_checkConnection: caught exception "
|
||||
<< conn->toString() << ' ' << e.what() << endl;
|
||||
|
||||
errorOccured = true;
|
||||
}
|
||||
|
||||
if ( errorOccured && nodesOffset >= 0 ) {
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
if (_checkConnMatch_inlock(conn, nodesOffset)) {
|
||||
// Make sure _checkHosts didn't modify the _nodes structure
|
||||
_nodes[nodesOffset].ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( changed && _hook )
|
||||
@ -406,63 +572,119 @@ namespace mongo {
|
||||
}
|
||||
|
||||
void ReplicaSetMonitor::_check( bool checkAllSecondaries ) {
|
||||
|
||||
bool triedQuickCheck = false;
|
||||
|
||||
LOG(1) << "_check : " << getServerAddress() << endl;
|
||||
|
||||
int newMaster = -1;
|
||||
shared_ptr<DBClientConnection> nodeConn;
|
||||
|
||||
for ( int retry = 0; retry < 2; retry++ ) {
|
||||
for ( unsigned i=0; i<_nodes.size(); i++ ) {
|
||||
shared_ptr<DBClientConnection> c;
|
||||
bool triedQuickCheck = false;
|
||||
|
||||
if ( !checkAllSecondaries ) {
|
||||
scoped_lock lk( _lock );
|
||||
assert(_master < static_cast<int>(_nodes.size()));
|
||||
if ( _master >= 0 ) {
|
||||
/* Nothing else to do since another thread already
|
||||
* found the _master
|
||||
*/
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for ( unsigned i = 0; /* should not check while outside of lock! */ ; i++ ) {
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
c = _nodes[i].conn;
|
||||
if ( i >= _nodes.size() ) break;
|
||||
nodeConn = _nodes[i].conn;
|
||||
}
|
||||
|
||||
string maybePrimary;
|
||||
if ( _checkConnection( c.get() , maybePrimary , retry , i ) ) {
|
||||
_master = i;
|
||||
newMaster = i;
|
||||
if ( ! checkAllSecondaries )
|
||||
return;
|
||||
}
|
||||
if ( _checkConnection( nodeConn.get(), maybePrimary, retry, i ) ) {
|
||||
scoped_lock lk( _lock );
|
||||
if ( _checkConnMatch_inlock( nodeConn.get(), i )) {
|
||||
_master = i;
|
||||
newMaster = i;
|
||||
|
||||
if ( ! triedQuickCheck && maybePrimary.size() ) {
|
||||
int x = _find( maybePrimary );
|
||||
if ( x >= 0 ) {
|
||||
triedQuickCheck = true;
|
||||
string dummy;
|
||||
shared_ptr<DBClientConnection> testConn;
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
testConn = _nodes[x].conn;
|
||||
}
|
||||
if ( _checkConnection( testConn.get() , dummy , false , x ) ) {
|
||||
_master = x;
|
||||
newMaster = x;
|
||||
if ( ! checkAllSecondaries )
|
||||
return;
|
||||
}
|
||||
if ( !checkAllSecondaries )
|
||||
return;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Somebody modified _nodes and most likely set the new
|
||||
* _master, so try again.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( ! triedQuickCheck && ! maybePrimary.empty() ) {
|
||||
int probablePrimaryIdx = -1;
|
||||
shared_ptr<DBClientConnection> probablePrimaryConn;
|
||||
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
probablePrimaryIdx = _find_inlock( maybePrimary );
|
||||
|
||||
if (probablePrimaryIdx >= 0) {
|
||||
probablePrimaryConn = _nodes[probablePrimaryIdx].conn;
|
||||
}
|
||||
}
|
||||
|
||||
if ( probablePrimaryIdx >= 0 ) {
|
||||
triedQuickCheck = true;
|
||||
|
||||
string dummy;
|
||||
if ( _checkConnection( probablePrimaryConn.get(), dummy,
|
||||
false, probablePrimaryIdx ) ) {
|
||||
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
if ( _checkConnMatch_inlock( probablePrimaryConn.get(),
|
||||
probablePrimaryIdx )) {
|
||||
|
||||
_master = probablePrimaryIdx;
|
||||
newMaster = probablePrimaryIdx;
|
||||
|
||||
if ( ! checkAllSecondaries )
|
||||
return;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Somebody modified _nodes and most likely set the
|
||||
* new _master, so try again.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( newMaster >= 0 )
|
||||
return;
|
||||
|
||||
sleepsecs(1);
|
||||
sleepsecs( 1 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ReplicaSetMonitor::check( bool checkAllSecondaries ) {
|
||||
// first see if the current master is fine
|
||||
if ( _master >= 0 ) {
|
||||
shared_ptr<DBClientConnection> masterConn;
|
||||
|
||||
{
|
||||
scoped_lock lk( _lock );
|
||||
|
||||
// first see if the current master is fine
|
||||
if ( _master >= 0 ) {
|
||||
assert(_master < static_cast<int>(_nodes.size()));
|
||||
masterConn = _nodes[_master].conn;
|
||||
}
|
||||
}
|
||||
|
||||
if ( masterConn.get() != NULL ) {
|
||||
string temp;
|
||||
if ( _checkConnection( _nodes[_master].conn.get() , temp , false , _master ) ) {
|
||||
|
||||
if ( _checkConnection( masterConn.get(), temp, false, _master )) {
|
||||
if ( ! checkAllSecondaries ) {
|
||||
// current master is fine, so we're done
|
||||
return;
|
||||
@ -480,21 +702,17 @@ namespace mongo {
|
||||
}
|
||||
|
||||
int ReplicaSetMonitor::_find_inlock( const string& server ) const {
|
||||
for ( unsigned i=0; i<_nodes.size(); i++ )
|
||||
if ( _nodes[i].addr == server )
|
||||
const size_t size = _nodes.size();
|
||||
|
||||
for ( unsigned i = 0; i < size; i++ ) {
|
||||
if ( _nodes[i].addr == server ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int ReplicaSetMonitor::_find( const HostAndPort& server ) const {
|
||||
scoped_lock lk( _lock );
|
||||
for ( unsigned i=0; i<_nodes.size(); i++ )
|
||||
if ( _nodes[i].addr == server )
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ReplicaSetMonitor::appendInfo( BSONObjBuilder& b ) const {
|
||||
scoped_lock lk( _lock );
|
||||
BSONArrayBuilder hosts( b.subarrayStart( "hosts" ) );
|
||||
@ -514,6 +732,13 @@ namespace mongo {
|
||||
b.append( "nextSlave" , _nextSlave );
|
||||
}
|
||||
|
||||
bool ReplicaSetMonitor::_checkConnMatch_inlock( DBClientConnection* conn,
|
||||
size_t nodeOffset ) const {
|
||||
|
||||
return ( nodeOffset < _nodes.size() &&
|
||||
conn->getServerAddress() == _nodes[nodeOffset].conn->getServerAddress() );
|
||||
}
|
||||
|
||||
|
||||
mongo::mutex ReplicaSetMonitor::_setsLock( "ReplicaSetMonitor" );
|
||||
map<string,ReplicaSetMonitorPtr> ReplicaSetMonitor::_sets;
|
||||
@ -537,6 +762,7 @@ namespace mongo {
|
||||
// a master is selected. let's just make sure connection didn't die
|
||||
if ( ! _master->isFailed() )
|
||||
return _master.get();
|
||||
|
||||
_monitor->notifyFailure( _masterHost );
|
||||
}
|
||||
|
||||
@ -579,7 +805,6 @@ namespace mongo {
|
||||
warning() << "cached auth failed for set: " << _monitor->getName() << " db: " << a.dbname << " user: " << a.username << endl;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
DBClientConnection& DBClientReplicaSet::masterConn() {
|
||||
@ -686,6 +911,8 @@ namespace mongo {
|
||||
}
|
||||
|
||||
auto_ptr<DBClientCursor> DBClientReplicaSet::checkSlaveQueryResult( auto_ptr<DBClientCursor> result ){
|
||||
if ( result.get() == NULL ) return result;
|
||||
|
||||
BSONObj error;
|
||||
bool isError = result->peekError( &error );
|
||||
if( ! isError ) return result;
|
||||
@ -820,10 +1047,14 @@ namespace mongo {
|
||||
|
||||
|
||||
bool DBClientReplicaSet::call( Message &toSend, Message &response, bool assertOk , string * actualServer ) {
|
||||
const char * ns = 0;
|
||||
|
||||
if ( toSend.operation() == dbQuery ) {
|
||||
// TODO: might be possible to do this faster by changing api
|
||||
DbMessage dm( toSend );
|
||||
QueryMessage qm( dm );
|
||||
ns = qm.ns;
|
||||
|
||||
if ( qm.queryOptions & QueryOption_SlaveOk ) {
|
||||
for ( int i=0; i<3; i++ ) {
|
||||
try {
|
||||
@ -844,7 +1075,26 @@ namespace mongo {
|
||||
DBClientConnection* m = checkMaster();
|
||||
if ( actualServer )
|
||||
*actualServer = m->getServerAddress();
|
||||
return m->call( toSend , response , assertOk );
|
||||
|
||||
if ( ! m->call( toSend , response , assertOk ) )
|
||||
return false;
|
||||
|
||||
if ( ns ) {
|
||||
QueryResult * res = (QueryResult*)response.singleData();
|
||||
if ( res->nReturned == 1 ) {
|
||||
BSONObj x(res->data() );
|
||||
if ( str::contains( ns , "$cmd" ) ) {
|
||||
if ( isNotMasterErrorString( x["errmsg"] ) )
|
||||
isntMaster();
|
||||
}
|
||||
else {
|
||||
if ( isNotMasterErrorString( getErrField( x ) ) )
|
||||
isntMaster();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ namespace mongo {
|
||||
|
||||
class ReplicaSetMonitor;
|
||||
typedef shared_ptr<ReplicaSetMonitor> ReplicaSetMonitorPtr;
|
||||
typedef pair<set<string>,set<int> > NodeDiff;
|
||||
|
||||
/**
|
||||
* manages state about a replica set for client
|
||||
@ -92,7 +93,7 @@ namespace mongo {
|
||||
string getName() const { return _name; }
|
||||
|
||||
string getServerAddress() const;
|
||||
|
||||
|
||||
bool contains( const string& server ) const;
|
||||
|
||||
void appendInfo( BSONObjBuilder& b ) const;
|
||||
@ -106,13 +107,20 @@ namespace mongo {
|
||||
*/
|
||||
ReplicaSetMonitor( const string& name , const vector<HostAndPort>& servers );
|
||||
|
||||
/**
|
||||
* Checks all connections from the host list and sets the current
|
||||
* master.
|
||||
*
|
||||
* @param checkAllSecondaries if set to false, stop immediately when
|
||||
* the master is found or when _master is not -1.
|
||||
*/
|
||||
void _check( bool checkAllSecondaries );
|
||||
|
||||
/**
|
||||
* Use replSetGetStatus command to make sure hosts in host list are up
|
||||
* and readable. Sets Node::ok appropriately.
|
||||
*/
|
||||
void _checkStatus(DBClientConnection *conn);
|
||||
void _checkStatus( const string& hostAddr );
|
||||
|
||||
/**
|
||||
* Add array of hosts to host list. Doesn't do anything if hosts are
|
||||
@ -124,25 +132,56 @@ namespace mongo {
|
||||
|
||||
/**
|
||||
* Updates host list.
|
||||
* @param c the connection to check
|
||||
* Invariant: if nodesOffset is >= 0, _nodes[nodesOffset].conn should be
|
||||
* equal to conn.
|
||||
*
|
||||
* @param conn the connection to check
|
||||
* @param maybePrimary OUT
|
||||
* @param verbose
|
||||
* @param nodesOffset - offset into _nodes array, -1 for not in it
|
||||
* @return if the connection is good
|
||||
*
|
||||
* @return true if the connection is good or false if invariant
|
||||
* is broken
|
||||
*/
|
||||
bool _checkConnection( DBClientConnection * c , string& maybePrimary , bool verbose , int nodesOffset );
|
||||
bool _checkConnection( DBClientConnection* conn, string& maybePrimary,
|
||||
bool verbose, int nodesOffset );
|
||||
|
||||
string _getServerAddress_inlock() const;
|
||||
|
||||
NodeDiff _getHostDiff_inlock( const BSONObj& hostList );
|
||||
bool _shouldChangeHosts( const BSONObj& hostList, bool inlock );
|
||||
|
||||
/**
|
||||
* @return the index to _nodes corresponding to the server address.
|
||||
*/
|
||||
int _find( const string& server ) const ;
|
||||
int _find_inlock( const string& server ) const ;
|
||||
int _find( const HostAndPort& server ) const ;
|
||||
|
||||
mutable mongo::mutex _lock; // protects _nodes
|
||||
/**
|
||||
* Checks whether the given connection matches the connection stored in _nodes.
|
||||
* Mainly used for sanity checking to confirm that nodeOffset still
|
||||
* refers to the right connection after releasing and reacquiring
|
||||
* a mutex.
|
||||
*/
|
||||
bool _checkConnMatch_inlock( DBClientConnection* conn, size_t nodeOffset ) const;
|
||||
|
||||
// protects _nodes and indices pointing to it (_master & _nextSlave)
|
||||
mutable mongo::mutex _lock;
|
||||
|
||||
/**
|
||||
* "Synchronizes" the _checkConnection method. Should ideally be one mutex per
|
||||
* connection object being used. The purpose of this lock is to make sure that
|
||||
* the reply from the connection the lock holder got is the actual response
|
||||
* to what it sent.
|
||||
*
|
||||
* Deadlock WARNING: never acquire this while holding _lock
|
||||
*/
|
||||
mutable mongo::mutex _checkConnectionLock;
|
||||
|
||||
string _name;
|
||||
struct Node {
|
||||
Node( const HostAndPort& a , DBClientConnection* c )
|
||||
: addr( a ) , conn(c) , ok(true) ,
|
||||
: addr( a ) , conn(c) , ok( c != NULL ),
|
||||
ismaster(false), secondary( false ) , hidden( false ) , pingTimeMillis(0) {
|
||||
}
|
||||
|
||||
|
||||
@ -138,9 +138,10 @@ namespace mongo {
|
||||
assert( !haveLimit );
|
||||
auto_ptr<Message> response(new Message());
|
||||
assert( _client );
|
||||
_client->recv(*response);
|
||||
b.m = response;
|
||||
dataReceived();
|
||||
if ( _client->recv(*response) ) {
|
||||
b.m = response;
|
||||
dataReceived();
|
||||
}
|
||||
}
|
||||
|
||||
void DBClientCursor::dataReceived( bool& retry, string& host ) {
|
||||
@ -290,12 +291,23 @@ namespace mongo {
|
||||
m.setData( dbKillCursors , b.buf() , b.len() );
|
||||
|
||||
if ( _client ) {
|
||||
_client->sayPiggyBack( m );
|
||||
|
||||
// Kill the cursor the same way the connection itself would. Usually, non-lazily
|
||||
if( DBClientConnection::getLazyKillCursor() )
|
||||
_client->sayPiggyBack( m );
|
||||
else
|
||||
_client->say( m );
|
||||
|
||||
}
|
||||
else {
|
||||
assert( _scopedHost.size() );
|
||||
ScopedDbConnection conn( _scopedHost );
|
||||
conn->sayPiggyBack( m );
|
||||
|
||||
if( DBClientConnection::getLazyKillCursor() )
|
||||
conn->sayPiggyBack( m );
|
||||
else
|
||||
conn->say( m );
|
||||
|
||||
conn.done();
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
namespace mongo {
|
||||
|
||||
LabeledLevel DistributedLock::logLvl( 1 );
|
||||
DistributedLock::LastPings DistributedLock::lastPings;
|
||||
|
||||
ThreadLocalValue<string> distLockIds("");
|
||||
|
||||
@ -84,7 +85,7 @@ namespace mongo {
|
||||
Date_t pingTime;
|
||||
|
||||
try {
|
||||
ScopedDbConnection conn( addr );
|
||||
ScopedDbConnection conn( addr, 30.0 );
|
||||
|
||||
pingTime = jsTime();
|
||||
|
||||
@ -110,6 +111,9 @@ namespace mongo {
|
||||
// replace it for a quite a while)
|
||||
// if the lock is taken, the take-over mechanism should handle the situation
|
||||
auto_ptr<DBClientCursor> c = conn->query( DistributedLock::locksNS , BSONObj() );
|
||||
// TODO: Would be good to make clear whether query throws or returns empty on errors
|
||||
uassert( 16060, str::stream() << "cannot query locks collection on config server " << conn.getHost(), c.get() );
|
||||
|
||||
set<string> pids;
|
||||
while ( c->more() ) {
|
||||
BSONObj lock = c->next();
|
||||
@ -224,7 +228,7 @@ namespace mongo {
|
||||
string s = pingThreadId( conn, processId );
|
||||
|
||||
// Ignore if we already have a pinging thread for this process.
|
||||
if ( _seen.count( s ) > 0 ) return "";
|
||||
if ( _seen.count( s ) > 0 ) return s;
|
||||
|
||||
// Check our clock skew
|
||||
try {
|
||||
@ -300,9 +304,22 @@ namespace mongo {
|
||||
_lockTimeout( lockTimeout == 0 ? LOCK_TIMEOUT : lockTimeout ), _maxClockSkew( _lockTimeout / LOCK_SKEW_FACTOR ), _maxNetSkew( _maxClockSkew ), _lockPing( _maxClockSkew ),
|
||||
_mutex( "DistributedLock" )
|
||||
{
|
||||
log( logLvl - 1 ) << "created new distributed lock for " << name << " on " << conn
|
||||
<< " ( lock timeout : " << _lockTimeout
|
||||
<< ", ping interval : " << _lockPing << ", process : " << asProcess << " )" << endl;
|
||||
log( logLvl ) << "created new distributed lock for " << name << " on " << conn
|
||||
<< " ( lock timeout : " << _lockTimeout
|
||||
<< ", ping interval : " << _lockPing << ", process : " << asProcess << " )"
|
||||
<< endl;
|
||||
|
||||
|
||||
}
|
||||
|
||||
DistributedLock::PingData DistributedLock::LastPings::getLastPing( const ConnectionString& conn, const string& lockName ){
|
||||
scoped_lock lock( _mutex );
|
||||
return _lastPings[ std::pair< string, string >( conn.toString(), lockName ) ];
|
||||
}
|
||||
|
||||
void DistributedLock::LastPings::setLastPing( const ConnectionString& conn, const string& lockName, const PingData& pd ){
|
||||
scoped_lock lock( _mutex );
|
||||
_lastPings[ std::pair< string, string >( conn.toString(), lockName ) ] = pd;
|
||||
}
|
||||
|
||||
Date_t DistributedLock::getRemoteTime() {
|
||||
@ -458,6 +475,11 @@ namespace mongo {
|
||||
// This should always be true, if not, we are using the lock incorrectly.
|
||||
assert( _name != "" );
|
||||
|
||||
log( logLvl ) << "trying to acquire new distributed lock for " << _name << " on " << _conn
|
||||
<< " ( lock timeout : " << _lockTimeout
|
||||
<< ", ping interval : " << _lockPing << ", process : " << _processId << " )"
|
||||
<< endl;
|
||||
|
||||
// write to dummy if 'other' is null
|
||||
BSONObj dummyOther;
|
||||
if ( other == NULL )
|
||||
@ -512,6 +534,7 @@ namespace mongo {
|
||||
|
||||
unsigned long long elapsed = 0;
|
||||
unsigned long long takeover = _lockTimeout;
|
||||
PingData _lastPingCheck = getLastPing();
|
||||
|
||||
log( logLvl ) << "checking last ping for lock '" << lockName << "'" << " against process " << _lastPingCheck.get<0>() << " and ping " << _lastPingCheck.get<1>() << endl;
|
||||
|
||||
@ -527,8 +550,7 @@ namespace mongo {
|
||||
|
||||
if( recPingChange || recTSChange ) {
|
||||
// If the ping has changed since we last checked, mark the current date and time
|
||||
scoped_lock lk( _mutex );
|
||||
_lastPingCheck = boost::tuple<string, Date_t, Date_t, OID>( lastPing["_id"].String().c_str(), lastPing["ping"].Date(), remote, o["ts"].OID() );
|
||||
setLastPing( PingData( lastPing["_id"].String().c_str(), lastPing["ping"].Date(), remote, o["ts"].OID() ) );
|
||||
}
|
||||
else {
|
||||
|
||||
@ -540,7 +562,6 @@ namespace mongo {
|
||||
else
|
||||
elapsed = remote - _lastPingCheck.get<2>();
|
||||
}
|
||||
|
||||
}
|
||||
catch( LockException& e ) {
|
||||
|
||||
|
||||
@ -71,6 +71,22 @@ namespace mongo {
|
||||
|
||||
static LabeledLevel logLvl;
|
||||
|
||||
typedef boost::tuple<string, Date_t, Date_t, OID> PingData;
|
||||
|
||||
class LastPings {
|
||||
public:
|
||||
LastPings() : _mutex( "DistributedLock::LastPings" ) {}
|
||||
~LastPings(){}
|
||||
|
||||
PingData getLastPing( const ConnectionString& conn, const string& lockName );
|
||||
void setLastPing( const ConnectionString& conn, const string& lockName, const PingData& pd );
|
||||
|
||||
mongo::mutex _mutex;
|
||||
map< std::pair<string, string>, PingData > _lastPings;
|
||||
};
|
||||
|
||||
static LastPings lastPings;
|
||||
|
||||
/**
|
||||
* The constructor does not connect to the configdb yet and constructing does not mean the lock was acquired.
|
||||
* Construction does trigger a lock "pinging" mechanism, though.
|
||||
@ -145,16 +161,12 @@ namespace mongo {
|
||||
|
||||
private:
|
||||
|
||||
void resetLastPing(){
|
||||
scoped_lock lk( _mutex );
|
||||
_lastPingCheck = boost::tuple<string, Date_t, Date_t, OID>();
|
||||
}
|
||||
void resetLastPing(){ lastPings.setLastPing( _conn, _name, PingData() ); }
|
||||
void setLastPing( const PingData& pd ){ lastPings.setLastPing( _conn, _name, pd ); }
|
||||
PingData getLastPing(){ return lastPings.getLastPing( _conn, _name ); }
|
||||
|
||||
mongo::mutex _mutex;
|
||||
|
||||
// Data from last check of process with ping time
|
||||
boost::tuple<string, Date_t, Date_t, OID> _lastPingCheck;
|
||||
// May or may not exist, depending on startup
|
||||
mongo::mutex _mutex;
|
||||
string _threadId;
|
||||
|
||||
};
|
||||
|
||||
@ -195,6 +195,7 @@ namespace mongo {
|
||||
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomSkew(gen, boost::uniform_int<>(0, skewRange));
|
||||
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomWait(gen, boost::uniform_int<>(1, threadWait));
|
||||
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomSleep(gen, boost::uniform_int<>(1, threadSleep));
|
||||
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > randomNewLock(gen, boost::uniform_int<>(0, 3));
|
||||
|
||||
|
||||
int skew = 0;
|
||||
@ -262,7 +263,7 @@ namespace mongo {
|
||||
}
|
||||
else {
|
||||
log() << "**** Not unlocking for thread " << threadId << endl;
|
||||
DistributedLock::killPinger( *myLock );
|
||||
assert( DistributedLock::killPinger( *myLock ) );
|
||||
// We're simulating a crashed process...
|
||||
break;
|
||||
}
|
||||
@ -274,6 +275,12 @@ namespace mongo {
|
||||
break;
|
||||
}
|
||||
|
||||
// Create a new lock 1/3 of the time
|
||||
if( randomNewLock() > 1 ){
|
||||
lock.reset(new DistributedLock( hostConn, lockName, takeoverMS, true ));
|
||||
myLock = lock.get();
|
||||
}
|
||||
|
||||
sleepmillis(randomSleep());
|
||||
}
|
||||
|
||||
|
||||
@ -130,6 +130,12 @@ namespace mongo {
|
||||
}
|
||||
|
||||
BSONObj GridFS::insertFile(const string& name, const OID& id, gridfs_offset length, const string& contentType) {
|
||||
// Wait for any pending writebacks to finish
|
||||
string err = _client.getLastError();
|
||||
uassert( 16428,
|
||||
str::stream() << "Error storing GridFS chunk for file: " << name
|
||||
<< ", error: " << err,
|
||||
err == "" );
|
||||
|
||||
BSONObj res;
|
||||
if ( ! _client.runCommand( _dbName.c_str() , BSON( "filemd5" << id << "root" << _prefix ) , res ) )
|
||||
|
||||
@ -67,7 +67,7 @@ namespace mongo {
|
||||
assert( cursor );
|
||||
|
||||
if ( cursor->hasResultFlag( ResultFlag_ShardConfigStale ) ) {
|
||||
throw StaleConfigException( _ns , "ClusteredCursor::query" );
|
||||
throw StaleConfigException( _ns , "ClusteredCursor::_checkCursor" );
|
||||
}
|
||||
|
||||
if ( cursor->hasResultFlag( ResultFlag_ErrSet ) ) {
|
||||
@ -90,7 +90,7 @@ namespace mongo {
|
||||
|
||||
if ( conn.setVersion() ) {
|
||||
conn.done();
|
||||
throw StaleConfigException( _ns , "ClusteredCursor::query ShardConnection had to change" , true );
|
||||
throw StaleConfigException( _ns , "ClusteredCursor::query" , true );
|
||||
}
|
||||
|
||||
LOG(5) << "ClusteredCursor::query (" << type() << ") server:" << server
|
||||
@ -365,6 +365,7 @@ namespace mongo {
|
||||
|
||||
void ParallelSortClusteredCursor::_finishCons() {
|
||||
_numServers = _servers.size();
|
||||
_lastFrom = 0;
|
||||
_cursors = 0;
|
||||
|
||||
if ( ! _sortKey.isEmpty() && ! _fields.isEmpty() ) {
|
||||
@ -490,7 +491,7 @@ namespace mongo {
|
||||
|
||||
if ( conns[i]->setVersion() ) {
|
||||
conns[i]->done();
|
||||
staleConfigExs.push_back( StaleConfigException( _ns , "ClusteredCursor::query ShardConnection had to change" , true ).what() + errLoc );
|
||||
staleConfigExs.push_back( (string)"stale config detected for " + StaleConfigException( _ns , "ParallelCursor::_init" , true ).what() + errLoc );
|
||||
break;
|
||||
}
|
||||
|
||||
@ -503,7 +504,19 @@ namespace mongo {
|
||||
0 , // nToSkip
|
||||
_fields.isEmpty() ? 0 : &_fields , // fieldsToReturn
|
||||
_options ,
|
||||
_batchSize == 0 ? 0 : _batchSize + _needToSkip // batchSize
|
||||
// NtoReturn is weird.
|
||||
// If zero, it means use default size, so we do that for all cursors
|
||||
// If positive, it's the batch size (we don't want this cursor limiting results), tha
|
||||
// done at a higher level
|
||||
// If negative, it's the batch size, but we don't create a cursor - so we don't want
|
||||
// to create a child cursor either.
|
||||
// Either way, if non-zero, we want to pull back the batch size + the skip amount as
|
||||
// quickly as possible. Potentially, for a cursor on a single shard or if we keep be
|
||||
// chunks, we can actually add the skip value into the cursor and/or make some assump
|
||||
// return value size ( (batch size + skip amount) / num_servers ).
|
||||
_batchSize == 0 ? 0 :
|
||||
( _batchSize > 0 ? _batchSize + _needToSkip :
|
||||
_batchSize - _needToSkip ) // batchSize
|
||||
) );
|
||||
|
||||
try{
|
||||
@ -592,7 +605,7 @@ namespace mongo {
|
||||
// when we throw our exception
|
||||
allConfigStale = true;
|
||||
|
||||
staleConfigExs.push_back( e.what() + errLoc );
|
||||
staleConfigExs.push_back( (string)"stale config detected for " + e.what() + errLoc );
|
||||
_cursors[i].reset( NULL );
|
||||
conns[i]->done();
|
||||
continue;
|
||||
@ -681,7 +694,13 @@ namespace mongo {
|
||||
BSONObj best = BSONObj();
|
||||
int bestFrom = -1;
|
||||
|
||||
for ( int i=0; i<_numServers; i++) {
|
||||
for( int j = 0; j < _numServers; j++ ){
|
||||
|
||||
// Iterate _numServers times, starting one past the last server we used.
|
||||
// This means we actually start at server #1, not #0, but shouldn't matter
|
||||
|
||||
int i = ( j + _lastFrom + 1 ) % _numServers;
|
||||
|
||||
if ( ! _cursors[i].more() )
|
||||
continue;
|
||||
|
||||
@ -702,6 +721,8 @@ namespace mongo {
|
||||
bestFrom = i;
|
||||
}
|
||||
|
||||
_lastFrom = bestFrom;
|
||||
|
||||
uassert( 10019 , "no more elements" , ! best.isEmpty() );
|
||||
_cursors[bestFrom].next();
|
||||
|
||||
|
||||
@ -241,6 +241,7 @@ namespace mongo {
|
||||
virtual void _explain( map< string,list<BSONObj> >& out );
|
||||
|
||||
int _numServers;
|
||||
int _lastFrom;
|
||||
set<ServerAndQuery> _servers;
|
||||
BSONObj _sortKey;
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/** @file redef_macros.h macros the implementation uses.
|
||||
/** @file redef_macros.h macros for mongo internals
|
||||
|
||||
@see undef_macros.h undefines these after use to minimize name pollution.
|
||||
*/
|
||||
@ -20,42 +20,83 @@
|
||||
|
||||
// If you define a new global un-prefixed macro, please add it here and in undef_macros
|
||||
|
||||
// #pragma once // this file is intended to be processed multiple times
|
||||
|
||||
#if defined(MONGO_MACROS_CLEANED)
|
||||
#define MONGO_MACROS_PUSHED 1
|
||||
|
||||
// util/allocator.h
|
||||
#pragma push_macro("malloc")
|
||||
#undef malloc
|
||||
#define malloc MONGO_malloc
|
||||
#pragma push_macro("realloc")
|
||||
#undef realloc
|
||||
#define realloc MONGO_realloc
|
||||
|
||||
// util/assert_util.h
|
||||
#pragma push_macro("assert")
|
||||
#undef assert
|
||||
#define assert MONGO_assert
|
||||
#pragma push_macro("verify")
|
||||
#undef verify
|
||||
#define verify MONGO_verify
|
||||
#pragma push_macro("dassert")
|
||||
#undef dassert
|
||||
#define dassert MONGO_dassert
|
||||
#pragma push_macro("wassert")
|
||||
#undef wassert
|
||||
#define wassert MONGO_wassert
|
||||
#pragma push_macro("massert")
|
||||
#undef massert
|
||||
#define massert MONGO_massert
|
||||
#pragma push_macro("uassert")
|
||||
#undef uassert
|
||||
#define uassert MONGO_uassert
|
||||
#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION
|
||||
#pragma push_macro("DESTRUCTOR_GUARD")
|
||||
#undef DESTRUCTOR_GUARD
|
||||
#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD
|
||||
|
||||
// util/goodies.h
|
||||
#pragma push_macro("PRINT")
|
||||
#undef PRINT
|
||||
#define PRINT MONGO_PRINT
|
||||
#pragma push_macro("PRINTFL")
|
||||
#undef PRINTFL
|
||||
#define PRINTFL MONGO_PRINTFL
|
||||
#pragma push_macro("asctime")
|
||||
#undef asctime
|
||||
#define asctime MONGO_asctime
|
||||
#pragma push_macro("gmtime")
|
||||
#undef gmtime
|
||||
#define gmtime MONGO_gmtime
|
||||
#pragma push_macro("localtime")
|
||||
#undef localtime
|
||||
#define localtime MONGO_localtime
|
||||
#pragma push_macro("ctime")
|
||||
#undef ctime
|
||||
#define ctime MONGO_ctime
|
||||
|
||||
// util/debug_util.h
|
||||
#pragma push_macro("DEV")
|
||||
#undef DEV
|
||||
#define DEV MONGO_DEV
|
||||
#pragma push_macro("DEBUGGING")
|
||||
#undef DEBUGGING
|
||||
#define DEBUGGING MONGO_DEBUGGING
|
||||
#pragma push_macro("SOMETIMES")
|
||||
#undef SOMETIMES
|
||||
#define SOMETIMES MONGO_SOMETIMES
|
||||
#pragma push_macro("OCCASIONALLY")
|
||||
#undef OCCASIONALLY
|
||||
#define OCCASIONALLY MONGO_OCCASIONALLY
|
||||
#pragma push_macro("RARELY")
|
||||
#undef RARELY
|
||||
#define RARELY MONGO_RARELY
|
||||
#pragma push_macro("ONCE")
|
||||
#undef ONCE
|
||||
#define ONCE MONGO_ONCE
|
||||
|
||||
// util/log.h
|
||||
#pragma push_macro("LOG")
|
||||
#undef LOG
|
||||
#define LOG MONGO_LOG
|
||||
|
||||
#undef MONGO_MACROS_CLEANED
|
||||
#endif
|
||||
|
||||
|
||||
@ -19,43 +19,65 @@
|
||||
|
||||
// #pragma once // this file is intended to be processed multiple times
|
||||
|
||||
#if !defined (MONGO_EXPOSE_MACROS)
|
||||
|
||||
/** MONGO_EXPOSE_MACROS - when defined, indicates that you are compiling a mongo program rather
|
||||
than just using the C++ driver.
|
||||
*/
|
||||
#if !defined(MONGO_EXPOSE_MACROS) && !defined(MONGO_MACROS_CLEANED)
|
||||
#ifdef MONGO_MACROS_PUSHED
|
||||
|
||||
// util/allocator.h
|
||||
#undef malloc
|
||||
#pragma pop_macro("malloc")
|
||||
#undef realloc
|
||||
#pragma pop_macro("realloc")
|
||||
|
||||
// util/assert_util.h
|
||||
#undef assert
|
||||
#pragma pop_macro("assert")
|
||||
#undef dassert
|
||||
#pragma pop_macro("dassert")
|
||||
#undef wassert
|
||||
#pragma pop_macro("wassert")
|
||||
#undef massert
|
||||
#pragma pop_macro("massert")
|
||||
#undef uassert
|
||||
#pragma pop_macro("uassert")
|
||||
#undef BOOST_CHECK_EXCEPTION
|
||||
#undef verify
|
||||
#pragma pop_macro("verify")
|
||||
#undef DESTRUCTOR_GUARD
|
||||
#pragma pop_macro("DESTRUCTOR_GUARD")
|
||||
|
||||
// util/goodies.h
|
||||
#undef PRINT
|
||||
#pragma pop_macro("PRINT")
|
||||
#undef PRINTFL
|
||||
#pragma pop_macro("PRINTFL")
|
||||
#undef asctime
|
||||
#pragma pop_macro("asctime")
|
||||
#undef gmtime
|
||||
#pragma pop_macro("gmtime")
|
||||
#undef localtime
|
||||
#pragma pop_macro("localtime")
|
||||
#undef ctime
|
||||
#pragma pop_macro("ctime")
|
||||
|
||||
// util/debug_util.h
|
||||
#undef DEV
|
||||
#pragma pop_macro("DEV")
|
||||
#undef DEBUGGING
|
||||
#pragma pop_macro("DEBUGGING")
|
||||
#undef SOMETIMES
|
||||
#pragma pop_macro("SOMETIMES")
|
||||
#undef OCCASIONALLY
|
||||
#pragma pop_macro("OCCASIONALLY")
|
||||
#undef RARELY
|
||||
#pragma pop_macro("RARELY")
|
||||
#undef ONCE
|
||||
#pragma pop_macro("ONCE")
|
||||
|
||||
// util/log.h
|
||||
#undef LOG
|
||||
#pragma pop_macro("LOG")
|
||||
|
||||
#define MONGO_MACROS_CLEANED
|
||||
#undef MONGO_MACROS_PUSHED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -572,8 +572,8 @@ namespace mongo {
|
||||
if ( readers )
|
||||
*readers = r;
|
||||
|
||||
int time = r * 100;
|
||||
time += w * 500;
|
||||
int time = r * 10; // we have to be nice to readers since they don't have priority
|
||||
time += w; // writers are greedy, so we can be mean tot hem
|
||||
|
||||
time = min( time , 1000000 );
|
||||
|
||||
|
||||
@ -457,7 +457,9 @@ namespace mongo {
|
||||
if ( yielded ) {
|
||||
*yielded = true;
|
||||
}
|
||||
return yield( yieldSuggest() , rec );
|
||||
bool res = yield( yieldSuggest() , rec );
|
||||
_yieldSometimesTracker.resetLastTime();
|
||||
return res;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -467,12 +469,16 @@ namespace mongo {
|
||||
if ( yielded ) {
|
||||
*yielded = true;
|
||||
}
|
||||
return yield( micros , _recordForYield( need ) );
|
||||
bool res = yield( micros , _recordForYield( need ) );
|
||||
_yieldSometimesTracker.resetLastTime();
|
||||
return res;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientCursor::staticYield( int micros , const StringData& ns , Record * rec ) {
|
||||
bool haveReadLock = dbMutex.atLeastReadLocked() && ! dbMutex.isWriteLocked();
|
||||
|
||||
killCurrentOp.checkForInterrupt( false );
|
||||
{
|
||||
auto_ptr<RWLockRecursive::Shared> lk;
|
||||
@ -481,10 +487,16 @@ namespace mongo {
|
||||
|
||||
dbtempreleasecond unlock;
|
||||
if ( unlock.unlocked() ) {
|
||||
if ( micros == -1 )
|
||||
micros = Client::recommendedYieldMicros();
|
||||
if ( micros > 0 )
|
||||
sleepmicros( micros );
|
||||
if ( haveReadLock ) {
|
||||
// don't sleep with a read lock
|
||||
}
|
||||
else {
|
||||
if ( micros == -1 )
|
||||
micros = Client::recommendedYieldMicros();
|
||||
if ( micros > 0 )
|
||||
sleepmicros( micros );
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
CurOp * c = cc().curop();
|
||||
|
||||
@ -68,7 +68,7 @@ namespace mongo {
|
||||
/** copy the entire database */
|
||||
bool go(const char *masterHost, string& errmsg, const string& fromdb, bool logForRepl, bool slaveOk, bool useReplAuth, bool snapshot, bool mayYield, bool mayBeInterrupted, int *errCode = 0);
|
||||
|
||||
bool copyCollection( const string& from , const string& ns , const BSONObj& query , string& errmsg , bool mayYield, bool mayBeInterrupted, bool copyIndexes = true, bool logForRepl = true );
|
||||
bool copyCollection( const string& ns , const BSONObj& query , string& errmsg , bool mayYield, bool mayBeInterrupted, bool copyIndexes = true, bool logForRepl = true );
|
||||
};
|
||||
|
||||
/* for index info object:
|
||||
@ -83,6 +83,12 @@ namespace mongo {
|
||||
BSONElement e = i.next();
|
||||
if ( e.eoo() )
|
||||
break;
|
||||
|
||||
// for now, skip the "v" field so that v:0 indexes will be upgraded to v:1
|
||||
if ( string("v") == e.fieldName() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( string("ns") == e.fieldName() ) {
|
||||
uassert( 10024 , "bad ns field for index during dbcopy", e.type() == String);
|
||||
const char *p = strchr(e.valuestr(), '.');
|
||||
@ -163,7 +169,8 @@ namespace mongo {
|
||||
getDur().commitIfNeeded();
|
||||
}
|
||||
catch( UserException& e ) {
|
||||
log() << "warning: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n';
|
||||
error() << "error: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n';
|
||||
throw;
|
||||
}
|
||||
|
||||
RARELY if ( time( 0 ) - saveLast > 60 ) {
|
||||
@ -232,24 +239,26 @@ namespace mongo {
|
||||
getDur().commitIfNeeded();
|
||||
}
|
||||
catch( UserException& e ) {
|
||||
log() << "warning: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n';
|
||||
error() << "error: exception cloning object in " << from_collection << ' ' << e.what() << " obj:" << js.toString() << '\n';
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool copyCollectionFromRemote(const string& host, const string& ns, const BSONObj& query, string& errmsg, bool logForRepl, bool mayYield, bool mayBeInterrupted) {
|
||||
bool copyCollectionFromRemote(const string& host, const string& ns, string& errmsg) {
|
||||
Cloner c;
|
||||
return c.copyCollection(host, ns, query, errmsg, mayYield, mayBeInterrupted, /*copyIndexes*/ true, logForRepl);
|
||||
|
||||
DBClientConnection *conn = new DBClientConnection();
|
||||
// cloner owns conn in auto_ptr
|
||||
c.setConnection(conn);
|
||||
uassert(15908, errmsg, conn->connect(host, errmsg) && replAuthenticate(conn));
|
||||
|
||||
return c.copyCollection(ns, BSONObj(), errmsg, true, false, /*copyIndexes*/ true, false);
|
||||
}
|
||||
|
||||
bool Cloner::copyCollection( const string& from , const string& ns , const BSONObj& query , string& errmsg , bool mayYield, bool mayBeInterrupted, bool copyIndexes, bool logForRepl ) {
|
||||
auto_ptr<DBClientConnection> myconn;
|
||||
myconn.reset( new DBClientConnection() );
|
||||
if ( ! myconn->connect( from , errmsg ) )
|
||||
return false;
|
||||
|
||||
conn.reset( myconn.release() );
|
||||
bool Cloner::copyCollection( const string& ns, const BSONObj& query, string& errmsg,
|
||||
bool mayYield, bool mayBeInterrupted, bool copyIndexes, bool logForRepl ) {
|
||||
|
||||
writelock lk(ns); // TODO: make this lower down
|
||||
Client::Context ctx(ns);
|
||||
@ -259,7 +268,7 @@ namespace mongo {
|
||||
string temp = ctx.db()->name + ".system.namespaces";
|
||||
BSONObj config = conn->findOne( temp , BSON( "name" << ns ) );
|
||||
if ( config["options"].isABSONObj() )
|
||||
if ( ! userCreateNS( ns.c_str() , config["options"].Obj() , errmsg, true , 0 ) )
|
||||
if ( ! userCreateNS( ns.c_str() , config["options"].Obj() , errmsg, logForRepl , 0 ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -515,7 +524,14 @@ namespace mongo {
|
||||
<< " query: " << query << " " << ( copyIndexes ? "" : ", not copying indexes" ) << endl;
|
||||
|
||||
Cloner c;
|
||||
return c.copyCollection( fromhost , collection , query, errmsg , true, false, copyIndexes );
|
||||
auto_ptr<DBClientConnection> myconn;
|
||||
myconn.reset( new DBClientConnection() );
|
||||
if ( ! myconn->connect( fromhost , errmsg ) )
|
||||
return false;
|
||||
|
||||
c.setConnection( myconn.release() );
|
||||
|
||||
return c.copyCollection( collection , query, errmsg , true, false, copyIndexes );
|
||||
}
|
||||
} cmdclonecollection;
|
||||
|
||||
|
||||
@ -34,6 +34,6 @@ namespace mongo {
|
||||
bool slaveOk, bool useReplAuth, bool snapshot, bool mayYield,
|
||||
bool mayBeInterrupted, int *errCode = 0);
|
||||
|
||||
bool copyCollectionFromRemote(const string& host, const string& ns, const BSONObj& query, string& errmsg, bool logForRepl, bool mayYield, bool mayBeInterrupted);
|
||||
bool copyCollectionFromRemote(const string& host, const string& ns, string& errmsg);
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
@ -239,7 +239,7 @@ namespace mongo {
|
||||
cmdLine.noUnixSocket = true;
|
||||
}
|
||||
|
||||
if (params.count("fork")) {
|
||||
if (params.count("fork") && !params.count("shutdown")) {
|
||||
if ( ! params.count( "logpath" ) ) {
|
||||
cout << "--fork has to be used with --logpath" << endl;
|
||||
::exit(-1);
|
||||
@ -304,7 +304,7 @@ namespace mongo {
|
||||
}
|
||||
|
||||
#endif
|
||||
if (params.count("logpath")) {
|
||||
if (params.count("logpath") && !params.count("shutdown")) {
|
||||
if ( logpath.size() == 0 )
|
||||
logpath = params["logpath"].as<string>();
|
||||
uassert( 10033 , "logpath has to be non-zero" , logpath.size() );
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include "../../util/net/listen.h"
|
||||
#include "../commands.h"
|
||||
#include "../../client/dbclient.h"
|
||||
#include "../security.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
# ifndef __sunos__
|
||||
@ -211,6 +212,11 @@ namespace mongo {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!noauth && cmdLine.keyFile &&
|
||||
!conn.auth("local", internalSecurity.user, internalSecurity.pwd, errmsg, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BSONObj out;
|
||||
bool ok = conn.simpleCommand( "admin" , &out , "_isSelf" );
|
||||
|
||||
|
||||
@ -940,6 +940,7 @@ namespace mongo {
|
||||
|
||||
log(1) << "mr ns: " << config.ns << endl;
|
||||
|
||||
uassert( 16149 , "cannot run map reduce without the js engine", globalScriptEngine );
|
||||
bool shouldHaveData = false;
|
||||
|
||||
long long num = 0;
|
||||
@ -1119,6 +1120,7 @@ namespace mongo {
|
||||
|
||||
virtual LockType locktype() const { return NONE; }
|
||||
bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
|
||||
ShardedConnectionInfo::addHook();
|
||||
string shardedOutputCollection = cmdObj["shardedOutputCollection"].valuestrsafe();
|
||||
string postProcessCollection = cmdObj["postProcessCollection"].valuestrsafe();
|
||||
bool postProcessOnly = !(postProcessCollection.empty());
|
||||
@ -1205,7 +1207,7 @@ namespace mongo {
|
||||
|
||||
BSONObj res = config.reducer->finalReduce( values , config.finalizer.get());
|
||||
if (state.isOnDisk())
|
||||
state.insertToInc(res);
|
||||
state.insert( config.tempLong , res );
|
||||
else
|
||||
state.emit(res);
|
||||
values.clear();
|
||||
|
||||
@ -180,6 +180,11 @@ namespace mongo {
|
||||
d->deletedList[i].writing().Null();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Start over from scratch with our extent sizing and growth
|
||||
d->lastExtentSize=0;
|
||||
|
||||
// before dropping indexes, at least make sure we can allocate one extent!
|
||||
uassert(14025, "compact error no space available to allocate", !allocateSpaceForANewRecord(ns, d, Record::HeaderSize+1, false).isNull());
|
||||
|
||||
|
||||
72
db/db.cpp
72
db/db.cpp
@ -708,6 +708,12 @@ int main(int argc, char* argv[]) {
|
||||
else {
|
||||
dbpath = "/data/db/";
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (dbpath.size() > 1 && dbpath[dbpath.size()-1] == '/') {
|
||||
// size() check is for the unlikely possibility of --dbpath "/"
|
||||
dbpath = dbpath.erase(dbpath.size()-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if ( params.count("directoryperdb")) {
|
||||
directoryperdb = true;
|
||||
@ -983,22 +989,6 @@ int main(int argc, char* argv[]) {
|
||||
procPath = (str::stream() << "/proc/" << pid);
|
||||
if (!boost::filesystem::exists(procPath))
|
||||
failed = true;
|
||||
|
||||
string exePath = procPath + "/exe";
|
||||
if (boost::filesystem::exists(exePath)){
|
||||
char buf[256];
|
||||
int ret = readlink(exePath.c_str(), buf, sizeof(buf)-1);
|
||||
buf[ret] = '\0'; // readlink doesn't terminate string
|
||||
if (ret == -1) {
|
||||
int e = errno;
|
||||
cerr << "Error resolving " << exePath << ": " << errnoWithDescription(e);
|
||||
failed = true;
|
||||
}
|
||||
else if (!endsWith(buf, "mongod")){
|
||||
cerr << "Process " << pid << " is running " << buf << " not mongod" << endl;
|
||||
::exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e){
|
||||
cerr << "Error reading pid from lock file [" << name << "]: " << e.what() << endl;
|
||||
@ -1065,14 +1055,6 @@ namespace mongo {
|
||||
|
||||
namespace mongo {
|
||||
|
||||
void pipeSigHandler( int signal ) {
|
||||
#ifdef psignal
|
||||
psignal( signal, "Signal Received : ");
|
||||
#else
|
||||
cout << "got pipe signal:" << signal << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void abruptQuit(int x) {
|
||||
ostringstream ossSig;
|
||||
ossSig << "Got signal: " << x << " (" << strsignal( x ) << ")." << endl;
|
||||
@ -1144,7 +1126,7 @@ namespace mongo {
|
||||
|
||||
assert( signal(SIGABRT, abruptQuit) != SIG_ERR );
|
||||
assert( signal(SIGQUIT, abruptQuit) != SIG_ERR );
|
||||
assert( signal(SIGPIPE, pipeSigHandler) != SIG_ERR );
|
||||
assert( signal(SIGPIPE, SIG_IGN) != SIG_ERR );
|
||||
|
||||
setupSIGTRAPforGDB();
|
||||
|
||||
@ -1164,31 +1146,41 @@ namespace mongo {
|
||||
}
|
||||
|
||||
#else
|
||||
void ctrlCTerminate() {
|
||||
log() << "got kill or ctrl-c signal, will terminate after current cmd ends" << endl;
|
||||
Client::initThread( "ctrlCTerminate" );
|
||||
void consoleTerminate( const char* controlCodeName ) {
|
||||
Client::initThread( "consoleTerminate" );
|
||||
log() << "got " << controlCodeName << ", will terminate after current cmd ends" << endl;
|
||||
exitCleanly( EXIT_KILL );
|
||||
}
|
||||
|
||||
BOOL CtrlHandler( DWORD fdwCtrlType ) {
|
||||
|
||||
switch( fdwCtrlType ) {
|
||||
|
||||
case CTRL_C_EVENT:
|
||||
rawOut("Ctrl-C signal");
|
||||
ctrlCTerminate();
|
||||
return( TRUE );
|
||||
rawOut( "Ctrl-C signal" );
|
||||
consoleTerminate( "CTRL_C_EVENT" );
|
||||
return TRUE ;
|
||||
|
||||
case CTRL_CLOSE_EVENT:
|
||||
rawOut("CTRL_CLOSE_EVENT signal");
|
||||
ctrlCTerminate();
|
||||
return( TRUE );
|
||||
rawOut( "CTRL_CLOSE_EVENT signal" );
|
||||
consoleTerminate( "CTRL_CLOSE_EVENT" );
|
||||
return TRUE ;
|
||||
|
||||
case CTRL_BREAK_EVENT:
|
||||
rawOut("CTRL_BREAK_EVENT signal");
|
||||
ctrlCTerminate();
|
||||
rawOut( "CTRL_BREAK_EVENT signal" );
|
||||
consoleTerminate( "CTRL_BREAK_EVENT" );
|
||||
return TRUE;
|
||||
|
||||
case CTRL_LOGOFF_EVENT:
|
||||
rawOut("CTRL_LOGOFF_EVENT signal (ignored)");
|
||||
return FALSE;
|
||||
rawOut( "CTRL_LOGOFF_EVENT signal" );
|
||||
consoleTerminate( "CTRL_LOGOFF_EVENT" );
|
||||
return TRUE;
|
||||
|
||||
case CTRL_SHUTDOWN_EVENT:
|
||||
rawOut("CTRL_SHUTDOWN_EVENT signal (ignored)");
|
||||
return FALSE;
|
||||
rawOut( "CTRL_SHUTDOWN_EVENT signal" );
|
||||
consoleTerminate( "CTRL_SHUTDOWN_EVENT" );
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@ -333,6 +333,11 @@ namespace mongo {
|
||||
virtual LockType locktype() const { return WRITE; }
|
||||
CmdDropDatabase() : Command("dropDatabase") {}
|
||||
bool run(const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
|
||||
// disallow dropping the config database
|
||||
if ( cmdLine.configsvr && ( dbname == "config" ) ) {
|
||||
errmsg = "Cannot drop 'config' database if mongod started with --configsvr";
|
||||
return false;
|
||||
}
|
||||
BSONElement e = cmdObj.firstElement();
|
||||
log() << "dropDatabase " << dbname << endl;
|
||||
int p = (int) e.number();
|
||||
@ -510,9 +515,19 @@ namespace mongo {
|
||||
t.appendNumber( "mappedWithJournal" , m );
|
||||
}
|
||||
|
||||
if( v - m > 5000 ) {
|
||||
int overhead = v - m - connTicketHolder.used();
|
||||
|
||||
if( overhead > 4000 ) {
|
||||
t.append("note", "virtual minus mapped is large. could indicate a memory leak");
|
||||
log() << "warning: virtual size (" << v << "MB) - mapped size (" << m << "MB) is large. could indicate a memory leak" << endl;
|
||||
|
||||
static time_t last = 0;
|
||||
time_t now = time(0);
|
||||
|
||||
if ( last + 60 < now ) {
|
||||
last = now;
|
||||
log() << "warning: virtual size (" << v << "MB) - mapped size (" << m << "MB) is large (" << overhead << "MB). could indicate a memory leak" << endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
t.done();
|
||||
@ -949,7 +964,7 @@ namespace mongo {
|
||||
}
|
||||
|
||||
list<BSONObj> all;
|
||||
auto_ptr<DBClientCursor> i = db.getIndexes( toDeleteNs );
|
||||
auto_ptr<DBClientCursor> i = db.query( dbname + ".system.indexes" , BSON( "ns" << toDeleteNs ) , 0 , 0 , 0 , QueryOption_SlaveOk );
|
||||
BSONObjBuilder b;
|
||||
while ( i->more() ) {
|
||||
BSONObj o = i->next().removeField("v").getOwned();
|
||||
@ -1104,6 +1119,10 @@ namespace mongo {
|
||||
BSONObj sort = BSON( "files_id" << 1 << "n" << 1 );
|
||||
|
||||
shared_ptr<Cursor> cursor = bestGuessCursor(ns.c_str(), query, sort);
|
||||
if ( ! cursor ) {
|
||||
errmsg = "need an index on { files_id : 1 , n : 1 }";
|
||||
return false;
|
||||
}
|
||||
auto_ptr<ClientCursor> cc (new ClientCursor(QueryOption_NoCursorTimeout, cursor, ns.c_str()));
|
||||
|
||||
int n = 0;
|
||||
@ -1746,6 +1765,7 @@ namespace mongo {
|
||||
virtual bool slaveOk() const { return false; }
|
||||
virtual LockType locktype() const { return WRITE; }
|
||||
virtual bool requiresAuth() { return true; }
|
||||
virtual bool logTheOp() { return true; }
|
||||
virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
|
||||
string coll = cmdObj[ "emptycapped" ].valuestrsafe();
|
||||
uassert( 13428, "emptycapped must specify a collection", !coll.empty() );
|
||||
|
||||
@ -51,7 +51,8 @@ namespace mongo {
|
||||
public:
|
||||
CmdBuildInfo() : Command( "buildInfo", true, "buildinfo" ) {}
|
||||
virtual bool slaveOk() const { return true; }
|
||||
virtual bool adminOnly() const { return true; }
|
||||
virtual bool adminOnly() const { return false; }
|
||||
virtual bool requiresAuth() { return false; }
|
||||
virtual LockType locktype() const { return NONE; }
|
||||
virtual void help( stringstream &help ) const {
|
||||
help << "get version #, etc.\n";
|
||||
@ -333,7 +334,7 @@ namespace mongo {
|
||||
|
||||
virtual bool slaveOk() const { return true; }
|
||||
virtual LockType locktype() const { return NONE; }
|
||||
virtual bool requiresAuth() { return false; }
|
||||
virtual bool requiresAuth() { return true; }
|
||||
virtual bool adminOnly() const { return true; }
|
||||
|
||||
virtual void help( stringstream& help ) const {
|
||||
|
||||
@ -157,6 +157,7 @@ namespace mongo {
|
||||
}
|
||||
|
||||
DiskLoc Helpers::findById(NamespaceDetails *d, BSONObj idquery) {
|
||||
assert(d);
|
||||
int idxNo = d->findIdIndex();
|
||||
uassert(13430, "no _id index", idxNo>=0);
|
||||
IndexDetails& i = d->idx( idxNo );
|
||||
|
||||
@ -79,11 +79,10 @@ namespace mongo {
|
||||
}
|
||||
|
||||
bool allowed( const char * rq , vector<string>& headers, const SockAddr &from ) {
|
||||
if ( from.isLocalHost() )
|
||||
return true;
|
||||
|
||||
if ( ! _webUsers->haveAdminUsers() )
|
||||
if ( from.isLocalHost() || !_webUsers->haveAdminUsers() ) {
|
||||
cmdAuthenticate.authenticate( "admin", "RestUser", false );
|
||||
return true;
|
||||
}
|
||||
|
||||
string auth = getHeader( rq , "Authorization" );
|
||||
|
||||
@ -118,8 +117,10 @@ namespace mongo {
|
||||
r << ha2;
|
||||
string r1 = md5simpledigest( r.str() );
|
||||
|
||||
if ( r1 == parms["response"] )
|
||||
if ( r1 == parms["response"] ) {
|
||||
cmdAuthenticate.authenticate( "admin", user["user"].str(), user[ "readOnly" ].isBoolean() && user[ "readOnly" ].boolean() );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
db/dur.cpp
12
db/dur.cpp
@ -83,7 +83,7 @@ namespace mongo {
|
||||
*/
|
||||
static void groupCommit();
|
||||
|
||||
CommitJob commitJob;
|
||||
CommitJob& commitJob = *(new CommitJob()); // don't destroy
|
||||
|
||||
Stats stats;
|
||||
|
||||
@ -434,7 +434,17 @@ namespace mongo {
|
||||
fraction = 1;
|
||||
lastRemap = now;
|
||||
|
||||
#if defined(_WIN32)
|
||||
// Note that this negatively affects performance.
|
||||
// We must grab the exclusive lock here because remapThePrivateView() on Windows needs
|
||||
// to grab it as well, due to the lack of a non-atomic way to remap a memory mapped file.
|
||||
// See SERVER-5723 for performance improvement.
|
||||
// See SERVER-5680 to see why this code is necessary.
|
||||
RWLockRecursive::Exclusive lk(MongoFile::mmmutex);
|
||||
#else
|
||||
RWLockRecursive::Shared lk(MongoFile::mmmutex);
|
||||
#endif
|
||||
|
||||
set<MongoFile*>& files = MongoFile::getAllFiles();
|
||||
unsigned sz = files.size();
|
||||
if( sz == 0 )
|
||||
|
||||
@ -164,6 +164,8 @@ namespace mongo {
|
||||
|
||||
CommitJob();
|
||||
|
||||
~CommitJob(){ assert(!"shouldn't destroy CommitJob!"); }
|
||||
|
||||
/** record/note an intent to write */
|
||||
void note(void* p, int len);
|
||||
|
||||
@ -212,7 +214,7 @@ namespace mongo {
|
||||
unsigned _nSinceCommitIfNeededCall;
|
||||
};
|
||||
|
||||
extern CommitJob commitJob;
|
||||
extern CommitJob& commitJob;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -687,7 +687,7 @@ namespace mongo {
|
||||
// must already be open -- so that _curFileId is correct for previous buffer building
|
||||
assert( _curLogFile );
|
||||
|
||||
stats.curr->_uncompressedBytes += b.len();
|
||||
stats.curr->_uncompressedBytes += uncompressed.len();
|
||||
unsigned w = b.len();
|
||||
_written += w;
|
||||
assert( w <= L );
|
||||
|
||||
@ -18,6 +18,10 @@
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include "extsort.h"
|
||||
#include "namespace-inl.h"
|
||||
#include "../util/file.h"
|
||||
@ -25,6 +29,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
|
||||
namespace mongo {
|
||||
|
||||
IndexInterface *BSONObjExternalSorter::extSortIdxInterface;
|
||||
@ -217,24 +222,87 @@ namespace mongo {
|
||||
// -----------------------------------
|
||||
|
||||
BSONObjExternalSorter::FileIterator::FileIterator( string file ) {
|
||||
unsigned long long length;
|
||||
_buf = (char*)_file.map( file.c_str() , length , MemoryMappedFile::SEQUENTIAL );
|
||||
massert( 10308 , "mmap failed" , _buf );
|
||||
assert( length == (unsigned long long) file_size( file ) );
|
||||
_end = _buf + length;
|
||||
#ifdef _WIN32
|
||||
_file = ::_open( file.c_str(), _O_BINARY | _O_RDWR | _O_CREAT , _S_IREAD | _S_IWRITE );
|
||||
#else
|
||||
_file = ::open( file.c_str(), O_CREAT | O_RDWR | O_NOATIME , S_IRUSR | S_IWUSR );
|
||||
#endif
|
||||
massert( 16392,
|
||||
str::stream() << "FileIterator can't open file: "
|
||||
<< file << errnoWithDescription(),
|
||||
_file >= 0 );
|
||||
|
||||
#ifdef POSIX_FADV_DONTNEED
|
||||
int err = posix_fadvise(_file, 0, 0, POSIX_FADV_SEQUENTIAL );
|
||||
if ( err )
|
||||
log() << "posix_fadvise failed: " << err << endl;
|
||||
#endif
|
||||
|
||||
_length = (unsigned long long)boost::filesystem::file_size( file );
|
||||
_readSoFar = 0;
|
||||
}
|
||||
|
||||
BSONObjExternalSorter::FileIterator::~FileIterator() {
|
||||
if ( _file >= 0 ) {
|
||||
#ifdef _WIN32
|
||||
_close( _file );
|
||||
#else
|
||||
::close( _file );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
BSONObjExternalSorter::FileIterator::~FileIterator() {}
|
||||
|
||||
bool BSONObjExternalSorter::FileIterator::more() {
|
||||
return _buf < _end;
|
||||
return _readSoFar < _length;
|
||||
}
|
||||
|
||||
|
||||
bool BSONObjExternalSorter::FileIterator::_read( char* buf, long long count ) {
|
||||
long long total = 0;
|
||||
while ( total < count ) {
|
||||
#ifdef _WIN32
|
||||
long long now = ::_read( _file, buf, count );
|
||||
#else
|
||||
long long now = ::read( _file, buf, count );
|
||||
#endif
|
||||
if ( now < 0 ) {
|
||||
log() << "read failed for BSONObjExternalSorter " << errnoWithDescription() << endl;
|
||||
return false;
|
||||
}
|
||||
if ( now == 0 ) {
|
||||
return false;
|
||||
}
|
||||
total += now;
|
||||
buf += now;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BSONObjExternalSorter::Data BSONObjExternalSorter::FileIterator::next() {
|
||||
BSONObj o( _buf );
|
||||
_buf += o.objsize();
|
||||
DiskLoc * l = (DiskLoc*)_buf;
|
||||
_buf += 8;
|
||||
return Data( o , *l );
|
||||
// read BSONObj
|
||||
|
||||
int size;
|
||||
assert( _read( reinterpret_cast<char*>(&size), 4 ) );
|
||||
char* buf = reinterpret_cast<char*>( malloc( sizeof(unsigned) + size ) );
|
||||
assert( buf );
|
||||
|
||||
memset( buf, 0, 4 ); // for Holder
|
||||
memcpy( buf+sizeof(unsigned), reinterpret_cast<char*>(&size), sizeof(int) ); // size of doc
|
||||
if ( ! _read( buf + sizeof(unsigned) + sizeof(int), size-sizeof(int) ) ) { // doc content
|
||||
free( buf );
|
||||
msgasserted( 16394, std::string("reading doc for external sort failed:") + errnoWithDescription() );
|
||||
}
|
||||
|
||||
// read DiskLoc
|
||||
DiskLoc l;
|
||||
if ( ! _read( reinterpret_cast<char*>(&l), 8 ) ) {
|
||||
free( buf );
|
||||
msgasserted( 16393, std::string("reading DiskLoc for external sort failed") + errnoWithDescription() );
|
||||
}
|
||||
_readSoFar += 8 + size;
|
||||
|
||||
BSONObj::Holder* h = reinterpret_cast<BSONObj::Holder*>(buf);
|
||||
return Data( BSONObj(h), l );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -76,9 +76,11 @@ namespace mongo {
|
||||
bool more();
|
||||
Data next();
|
||||
private:
|
||||
MemoryMappedFile _file;
|
||||
char * _buf;
|
||||
char * _end;
|
||||
bool _read( char* buf, long long count );
|
||||
|
||||
int _file;
|
||||
unsigned long long _length;
|
||||
unsigned long long _readSoFar;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
@ -2647,7 +2647,10 @@ namespace mongo {
|
||||
|
||||
BSONObjBuilder bb( arr.subobjStart( BSONObjBuilder::numStr( x++ ) ) );
|
||||
bb.append( "dis" , dis );
|
||||
if( includeLocs ) bb.append( "loc" , p._pt );
|
||||
if( includeLocs ){
|
||||
if( p._pt.couldBeArray() ) bb.append( "loc", BSONArray( p._pt ) );
|
||||
else bb.append( "loc" , p._pt );
|
||||
}
|
||||
bb.append( "obj" , p._o );
|
||||
bb.done();
|
||||
}
|
||||
|
||||
@ -353,20 +353,19 @@ namespace mongo {
|
||||
}
|
||||
currentOp.ensureStarted();
|
||||
currentOp.done();
|
||||
int ms = currentOp.totalTimeMillis();
|
||||
debug.executionTime = currentOp.totalTimeMillis();
|
||||
|
||||
//DEV log = true;
|
||||
if ( log || ms > logThreshold ) {
|
||||
if( logLevel < 3 && op == dbGetMore && strstr(ns, ".oplog.") && ms < 4300 && !log ) {
|
||||
if ( log || debug.executionTime > logThreshold ) {
|
||||
if( logLevel < 3 && op == dbGetMore && strstr(ns, ".oplog.") && debug.executionTime < 4300 && !log ) {
|
||||
/* it's normal for getMore on the oplog to be slow because of use of awaitdata flag. */
|
||||
}
|
||||
else {
|
||||
debug.executionTime = ms;
|
||||
mongo::tlog() << debug << endl;
|
||||
}
|
||||
}
|
||||
|
||||
if ( currentOp.shouldDBProfile( ms ) ) {
|
||||
if ( currentOp.shouldDBProfile( debug.executionTime ) ) {
|
||||
// performance profiling is on
|
||||
if ( dbMutex.getState() < 0 ) {
|
||||
mongo::log(1) << "note: not profiling because recursive read lock" << endl;
|
||||
@ -606,6 +605,8 @@ namespace mongo {
|
||||
break;
|
||||
js = d.nextJsObj(); // TODO: refactor to do objcheck outside of writelock
|
||||
}
|
||||
|
||||
globalOpCounters.incInsertInWriteLock(n);
|
||||
}
|
||||
|
||||
void receivedInsert(Message& m, CurOp& op) {
|
||||
@ -873,6 +874,15 @@ namespace mongo {
|
||||
}
|
||||
catch (...) { }
|
||||
|
||||
#ifdef _WIN32
|
||||
// Windows Service Controller wants to be told when we are down,
|
||||
// so don't call ::exit() yet, or say "really exiting now"
|
||||
//
|
||||
if ( rc == EXIT_WINDOWS_SERVICE_STOP ) {
|
||||
if ( c ) c->shutdown();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
tryToOutputFatal( "dbexit: really exiting now" );
|
||||
if ( c ) c->shutdown();
|
||||
::exit(rc);
|
||||
|
||||
15
db/jsobj.cpp
15
db/jsobj.cpp
@ -753,6 +753,21 @@ namespace mongo {
|
||||
return n;
|
||||
}
|
||||
|
||||
bool BSONObj::couldBeArray() const {
|
||||
BSONObjIterator i( *this );
|
||||
int index = 0;
|
||||
while( i.moreWithEOO() ){
|
||||
BSONElement e = i.next();
|
||||
if( e.eoo() ) break;
|
||||
|
||||
// TODO: If actually important, may be able to do int->char* much faster
|
||||
if( strcmp( e.fieldName(), ((string)( str::stream() << index )).c_str() ) != 0 )
|
||||
return false;
|
||||
index++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BSONObj BSONObj::clientReadable() const {
|
||||
BSONObjBuilder b;
|
||||
BSONObjIterator i( *this );
|
||||
|
||||
100
db/mongommf.cpp
100
db/mongommf.cpp
@ -64,12 +64,21 @@ namespace mongo {
|
||||
size_t protectSize = protectEnd - protectStart;
|
||||
dassert(protectSize>0&&protectSize<=MemoryMappedFile::ChunkSize);
|
||||
|
||||
DWORD old;
|
||||
bool ok = VirtualProtect((void*)protectStart, protectSize, PAGE_WRITECOPY, &old);
|
||||
if( !ok ) {
|
||||
DWORD e = GetLastError();
|
||||
log() << "VirtualProtect failed (mcw) " << mmf->filename() << ' ' << chunkno << hex << protectStart << ' ' << protectSize << ' ' << errnoWithDescription(e) << endl;
|
||||
assert(false);
|
||||
DWORD oldProtection;
|
||||
bool ok = VirtualProtect( reinterpret_cast<void*>( protectStart ),
|
||||
protectSize,
|
||||
PAGE_WRITECOPY,
|
||||
&oldProtection );
|
||||
if ( !ok ) {
|
||||
DWORD dosError = GetLastError();
|
||||
log() << "VirtualProtect for " << mmf->filename()
|
||||
<< " chunk " << chunkno
|
||||
<< " failed with " << errnoWithDescription( dosError )
|
||||
<< " (chunk size is " << protectSize
|
||||
<< ", address is " << hex << protectStart << dec << ")"
|
||||
<< " in mongo::makeChunkWritable, terminating"
|
||||
<< endl;
|
||||
::abort();
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,19 +88,25 @@ namespace mongo {
|
||||
void* MemoryMappedFile::createPrivateMap() {
|
||||
assert( maphandle );
|
||||
scoped_lock lk(mapViewMutex);
|
||||
void *p = MapViewOfFile(maphandle, FILE_MAP_READ, 0, 0, 0);
|
||||
if ( p == 0 ) {
|
||||
DWORD e = GetLastError();
|
||||
log() << "createPrivateMap failed " << filename() << " " <<
|
||||
errnoWithDescription(e) << " filelen:" << len <<
|
||||
((sizeof(void*) == 4 ) ? " (32 bit build)" : "") <<
|
||||
endl;
|
||||
LPVOID thisAddress = getNextMemoryMappedFileLocation( len );
|
||||
void* privateMapAddress = MapViewOfFileEx(
|
||||
maphandle, // file mapping handle
|
||||
FILE_MAP_READ, // access
|
||||
0, 0, // file offset, high and low
|
||||
0, // bytes to map, 0 == all
|
||||
thisAddress ); // address to place file
|
||||
if ( privateMapAddress == 0 ) {
|
||||
DWORD dosError = GetLastError();
|
||||
log() << "MapViewOfFileEx for " << filename()
|
||||
<< " failed with " << errnoWithDescription( dosError )
|
||||
<< " (file size is " << len << ")"
|
||||
<< " in MemoryMappedFile::createPrivateMap, terminating"
|
||||
<< endl;
|
||||
::abort();
|
||||
}
|
||||
else {
|
||||
clearWritableBits(p);
|
||||
views.push_back(p);
|
||||
}
|
||||
return p;
|
||||
clearWritableBits( privateMapAddress );
|
||||
views.push_back( privateMapAddress );
|
||||
return privateMapAddress;
|
||||
}
|
||||
|
||||
void* MemoryMappedFile::remapPrivateView(void *oldPrivateAddr) {
|
||||
@ -100,37 +115,34 @@ namespace mongo {
|
||||
// the mapViewMutex is to assure we get the same address on the remap
|
||||
scoped_lock lk(mapViewMutex);
|
||||
|
||||
RWLockRecursive::Exclusive lockMongoFiles(mmmutex);
|
||||
|
||||
clearWritableBits(oldPrivateAddr);
|
||||
#if 1
|
||||
// https://jira.mongodb.org/browse/SERVER-2942
|
||||
DWORD old;
|
||||
bool ok = VirtualProtect(oldPrivateAddr, (SIZE_T) len, PAGE_READONLY, &old);
|
||||
if( !ok ) {
|
||||
DWORD e = GetLastError();
|
||||
log() << "VirtualProtect failed in remapPrivateView " << filename() << hex << oldPrivateAddr << ' ' << len << ' ' << errnoWithDescription(e) << endl;
|
||||
assert(false);
|
||||
}
|
||||
return oldPrivateAddr;
|
||||
#else
|
||||
if( !UnmapViewOfFile(oldPrivateAddr) ) {
|
||||
DWORD e = GetLastError();
|
||||
log() << "UnMapViewOfFile failed " << filename() << ' ' << errnoWithDescription(e) << endl;
|
||||
assert(false);
|
||||
DWORD dosError = GetLastError();
|
||||
log() << "UnMapViewOfFile for " << filename()
|
||||
<< " failed with " << errnoWithDescription( dosError )
|
||||
<< " in MemoryMappedFile::remapPrivateView, terminating"
|
||||
<< endl;
|
||||
::abort();
|
||||
}
|
||||
|
||||
// we want the new address to be the same as the old address in case things keep pointers around (as namespaceindex does).
|
||||
void *p = MapViewOfFileEx(maphandle, FILE_MAP_READ, 0, 0,
|
||||
/*dwNumberOfBytesToMap 0 means to eof*/0 /*len*/,
|
||||
oldPrivateAddr);
|
||||
|
||||
if ( p == 0 ) {
|
||||
DWORD e = GetLastError();
|
||||
log() << "MapViewOfFileEx failed " << filename() << " " << errnoWithDescription(e) << endl;
|
||||
assert(p);
|
||||
void* newPrivateView = MapViewOfFileEx(
|
||||
maphandle, // file mapping handle
|
||||
FILE_MAP_READ, // access
|
||||
0, 0, // file offset, high and low
|
||||
0, // bytes to map, 0 == all
|
||||
oldPrivateAddr ); // we want the same address we had before
|
||||
if ( oldPrivateAddr != newPrivateView ) {
|
||||
DWORD dosError = GetLastError();
|
||||
log() << "MapViewOfFileEx for " << filename()
|
||||
<< " failed with " << errnoWithDescription( dosError )
|
||||
<< " (file size is " << len << ")"
|
||||
<< " in MemoryMappedFile::remapPrivateView, terminating"
|
||||
<< endl;
|
||||
::abort();
|
||||
}
|
||||
assert(p == oldPrivateAddr);
|
||||
return p;
|
||||
#endif
|
||||
return newPrivateView;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
104
db/oplog.cpp
104
db/oplog.cpp
@ -625,9 +625,56 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void applyOperation_inlock(const BSONObj& op , bool fromRepl ) {
|
||||
bool shouldRetry(const BSONObj& o, const string& hn) {
|
||||
OplogReader missingObjReader;
|
||||
const char *ns = o.getStringField("ns");
|
||||
|
||||
// should already have write lock
|
||||
Client::Context ctx(ns);
|
||||
|
||||
// capped collections
|
||||
NamespaceDetails *nsd = nsdetails(ns);
|
||||
if (nsd && nsd->capped) {
|
||||
log() << "replication missing doc, but this is okay for a capped collection (" << ns << ")" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// we don't have the object yet, which is possible on initial sync. get it.
|
||||
log() << "replication info adding missing object" << endl; // rare enough we can log
|
||||
uassert(15916, str::stream() << "Can no longer connect to initial sync source: " << hn, missingObjReader.connect(hn));
|
||||
|
||||
// might be more than just _id in the update criteria
|
||||
BSONObj query = BSONObjBuilder().append(o.getObjectField("o2")["_id"]).obj();
|
||||
BSONObj missingObj;
|
||||
try {
|
||||
missingObj = missingObjReader.findOne(ns, query);
|
||||
} catch(DBException& e) {
|
||||
log() << "replication assertion fetching missing object: " << e.what() << endl;
|
||||
throw;
|
||||
}
|
||||
|
||||
if( missingObj.isEmpty() ) {
|
||||
log() << "replication missing object not found on source. presumably deleted later in oplog" << endl;
|
||||
log() << "replication o2: " << o.getObjectField("o2").toString() << endl;
|
||||
log() << "replication o firstfield: " << o.getObjectField("o").firstElementFieldName() << endl;
|
||||
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
DiskLoc d = theDataFileMgr.insert(ns, (void*) missingObj.objdata(), missingObj.objsize());
|
||||
uassert(15917, "Got bad disk location when attempting to insert", !d.isNull());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** @param fromRepl false if from ApplyOpsCmd
|
||||
@return true if was and update should have happened and the document DNE. see replset initial sync code.
|
||||
*/
|
||||
bool applyOperation_inlock(const BSONObj& op , bool fromRepl ) {
|
||||
assertInWriteLock();
|
||||
LOG(6) << "applying op: " << op << endl;
|
||||
bool failedUpdate = false;
|
||||
|
||||
OpCounters * opCounters = fromRepl ? &replOpCounters : &globalOpCounters;
|
||||
|
||||
@ -640,6 +687,7 @@ namespace mongo {
|
||||
o = fields[0].embeddedObject();
|
||||
|
||||
const char *ns = fields[1].valuestrsafe();
|
||||
NamespaceDetails *nsd = nsdetails(ns);
|
||||
|
||||
// operation type -- see logOp() comments for types
|
||||
const char *opType = fields[2].valuestrsafe();
|
||||
@ -660,29 +708,71 @@ namespace mongo {
|
||||
if( !o.getObjectID(_id) ) {
|
||||
/* No _id. This will be very slow. */
|
||||
Timer t;
|
||||
updateObjects(ns, o, o, true, false, false, debug );
|
||||
updateObjects(ns, o, o, true, false, false, debug, true );
|
||||
if( t.millis() >= 2 ) {
|
||||
RARELY OCCASIONALLY log() << "warning, repl doing slow updates (no _id field) for " << ns << endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* erh 10/16/2009 - this is probably not relevant any more since its auto-created, but not worth removing */
|
||||
RARELY ensureHaveIdIndex(ns); // otherwise updates will be slow
|
||||
RARELY if (nsd && !nsd->capped) { ensureHaveIdIndex(ns); } // otherwise updates will be slow
|
||||
|
||||
/* todo : it may be better to do an insert here, and then catch the dup key exception and do update
|
||||
then. very few upserts will not be inserts...
|
||||
*/
|
||||
BSONObjBuilder b;
|
||||
b.append(_id);
|
||||
updateObjects(ns, o, b.done(), true, false, false , debug );
|
||||
updateObjects(ns, o, b.done(), true, false, false , debug, true );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( *opType == 'u' ) {
|
||||
opCounters->gotUpdate();
|
||||
RARELY ensureHaveIdIndex(ns); // otherwise updates will be super slow
|
||||
// dm do we create this for a capped collection?
|
||||
// - if not, updates would be slow
|
||||
// - but if were by id would be slow on primary too so maybe ok
|
||||
// - if on primary was by another key and there are other indexes, this could be very bad w/out an index
|
||||
// - if do create, odd to have on secondary but not primary. also can cause secondary to block for
|
||||
// quite a while on creation.
|
||||
RARELY if (nsd && !nsd->capped) { ensureHaveIdIndex(ns); } // otherwise updates will be super slow
|
||||
OpDebug debug;
|
||||
updateObjects(ns, o, op.getObjectField("o2"), /*upsert*/ fields[3].booleanSafe(), /*multi*/ false, /*logop*/ false , debug );
|
||||
BSONObj updateCriteria = op.getObjectField("o2");
|
||||
bool upsert = fields[3].booleanSafe();
|
||||
UpdateResult ur = updateObjects(ns, o, updateCriteria, upsert, /*multi*/ false, /*logop*/ false , debug, true );
|
||||
if( ur.num == 0 ) {
|
||||
if( ur.mod ) {
|
||||
if( updateCriteria.nFields() == 1 ) {
|
||||
// was a simple { _id : ... } update criteria
|
||||
failedUpdate = true;
|
||||
// todo: probably should assert in these failedUpdate cases if not in initialSync
|
||||
}
|
||||
// need to check to see if it isn't present so we can set failedUpdate correctly.
|
||||
// note that adds some overhead for this extra check in some cases, such as an updateCriteria
|
||||
// of the form
|
||||
// { _id:..., { x : {$size:...} }
|
||||
// thus this is not ideal.
|
||||
else {
|
||||
|
||||
if (nsd == NULL ||
|
||||
(nsd->findIdIndex() >= 0 && Helpers::findById(nsd, updateCriteria).isNull()) ||
|
||||
// capped collections won't have an _id index
|
||||
(nsd->findIdIndex() < 0 && Helpers::findOne(ns, updateCriteria, false).isNull())) {
|
||||
failedUpdate = true;
|
||||
}
|
||||
|
||||
// Otherwise, it's present; zero objects were updated because of additional specifiers
|
||||
// in the query for idempotence
|
||||
}
|
||||
}
|
||||
else {
|
||||
// this could happen benignly on an oplog duplicate replay of an upsert
|
||||
// (because we are idempotent),
|
||||
// if an regular non-mod update fails the item is (presumably) missing.
|
||||
if( !upsert ) {
|
||||
failedUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( *opType == 'd' ) {
|
||||
opCounters->gotDelete();
|
||||
@ -703,7 +793,7 @@ namespace mongo {
|
||||
else {
|
||||
throw MsgAssertionException( 14825 , ErrorMsg("error in applyOperation : unknown opType ", *opType) );
|
||||
}
|
||||
|
||||
return failedUpdate;
|
||||
}
|
||||
|
||||
class ApplyOpsCmd : public Command {
|
||||
|
||||
@ -129,6 +129,12 @@ namespace mongo {
|
||||
* take an op and apply locally
|
||||
* used for applying from an oplog
|
||||
* @param fromRepl really from replication or for testing/internal/command/etc...
|
||||
* Returns if the op was an update that could not be applied (true on failure)
|
||||
*/
|
||||
void applyOperation_inlock(const BSONObj& op , bool fromRepl = true );
|
||||
bool applyOperation_inlock(const BSONObj& op , bool fromRepl = true );
|
||||
|
||||
/**
|
||||
* If applyOperation_inlock should be called again after an update fails.
|
||||
*/
|
||||
bool shouldRetry(const BSONObj& op , const string& hn);
|
||||
}
|
||||
|
||||
@ -221,7 +221,8 @@ namespace mongo {
|
||||
_skip( spec["skip"].numberLong() ),
|
||||
_limit( spec["limit"].numberLong() ),
|
||||
_nscanned(),
|
||||
_bc() {
|
||||
_bc(),
|
||||
_yieldRecoveryFailed() {
|
||||
}
|
||||
|
||||
virtual void _init() {
|
||||
@ -251,6 +252,7 @@ namespace mongo {
|
||||
|
||||
virtual void recoverFromYield() {
|
||||
if ( _cc && !ClientCursor::recoverFromYield( _yieldData ) ) {
|
||||
_yieldRecoveryFailed = true;
|
||||
_c.reset();
|
||||
_cc.reset();
|
||||
|
||||
@ -309,7 +311,7 @@ namespace mongo {
|
||||
}
|
||||
long long count() const { return _count; }
|
||||
virtual bool mayRecordPlan() const {
|
||||
return ( _myCount > _limit / 2 ) || ( complete() && !stopRequested() );
|
||||
return !_yieldRecoveryFailed && ( ( _myCount > _limit / 2 ) || ( complete() && !stopRequested() ) );
|
||||
}
|
||||
private:
|
||||
|
||||
@ -343,6 +345,7 @@ namespace mongo {
|
||||
|
||||
ClientCursor::CleanupPointer _cc;
|
||||
ClientCursor::YieldData _yieldData;
|
||||
bool _yieldRecoveryFailed;
|
||||
};
|
||||
|
||||
/* { count: "collectionname"[, query: <query>] }
|
||||
@ -474,7 +477,8 @@ namespace mongo {
|
||||
_oplogReplay( pq.hasOption( QueryOption_OplogReplay) ),
|
||||
_response( response ),
|
||||
_eb( eb ),
|
||||
_curop( curop )
|
||||
_curop( curop ),
|
||||
_yieldRecoveryFailed()
|
||||
{}
|
||||
|
||||
virtual void _init() {
|
||||
@ -531,6 +535,7 @@ namespace mongo {
|
||||
_findingStartCursor->recoverFromYield();
|
||||
}
|
||||
else if ( _cc && !ClientCursor::recoverFromYield( _yieldData ) ) {
|
||||
_yieldRecoveryFailed = true;
|
||||
_c.reset();
|
||||
_cc.reset();
|
||||
_so.reset();
|
||||
@ -723,7 +728,7 @@ namespace mongo {
|
||||
}
|
||||
|
||||
virtual bool mayRecordPlan() const {
|
||||
return ( _pq.getNumToReturn() != 1 ) && ( ( _n > _pq.getNumToReturn() / 2 ) || ( complete() && !stopRequested() ) );
|
||||
return !_yieldRecoveryFailed && ( _pq.getNumToReturn() != 1 ) && ( ( _n > _pq.getNumToReturn() / 2 ) || ( complete() && !stopRequested() ) );
|
||||
}
|
||||
|
||||
virtual QueryOp *_createChild() const {
|
||||
@ -791,6 +796,8 @@ namespace mongo {
|
||||
ExplainBuilder &_eb;
|
||||
CurOp &_curop;
|
||||
OpTime _slaveReadTill;
|
||||
|
||||
bool _yieldRecoveryFailed;
|
||||
};
|
||||
|
||||
/* run a query -- includes checking for and running a Command \
|
||||
@ -809,6 +816,7 @@ namespace mongo {
|
||||
|
||||
curop.debug().ns = ns;
|
||||
curop.debug().ntoreturn = pq.getNumToReturn();
|
||||
curop.debug().query = jsobj;
|
||||
curop.setQuery(jsobj);
|
||||
|
||||
if ( pq.couldBeCommand() ) {
|
||||
@ -909,6 +917,19 @@ namespace mongo {
|
||||
Client& c = cc();
|
||||
bool found = Helpers::findById( c, ns , query , resObject , &nsFound , &indexFound );
|
||||
if ( nsFound == false || indexFound == true ) {
|
||||
|
||||
if ( shardingState.needShardChunkManager( ns ) ) {
|
||||
ShardChunkManagerPtr m = shardingState.getShardChunkManager( ns );
|
||||
if ( m && ! m->belongsToMe( resObject ) ) {
|
||||
// I have something this _id
|
||||
// but it doesn't belong to me
|
||||
// so return nothing
|
||||
resObject = BSONObj();
|
||||
found = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BufBuilder bb(sizeof(QueryResult)+resObject.objsize()+32);
|
||||
bb.skip(sizeof(QueryResult));
|
||||
|
||||
|
||||
@ -623,6 +623,17 @@ namespace mongo {
|
||||
|
||||
template< class Builder >
|
||||
void ModSetState::_appendNewFromMods( const string& root , ModState& m , Builder& b , set<string>& onedownseen ) {
|
||||
Mod& m2 = *((Mod*)(m.m)); // HACK
|
||||
switch (m2.op) {
|
||||
// unset/pull/pullAll on nothing does nothing, so don't append anything
|
||||
case Mod::UNSET:
|
||||
case Mod::PULL:
|
||||
case Mod::PULL_ALL:
|
||||
return;
|
||||
default:
|
||||
;// fall through
|
||||
}
|
||||
|
||||
const char * temp = m.fieldName();
|
||||
temp += root.size();
|
||||
const char * dot = strchr( temp , '.' );
|
||||
@ -642,6 +653,14 @@ namespace mongo {
|
||||
|
||||
}
|
||||
|
||||
bool ModSetState::duplicateFieldName( const BSONElement &a, const BSONElement &b ) {
|
||||
return
|
||||
!a.eoo() &&
|
||||
!b.eoo() &&
|
||||
( a.rawdata() != b.rawdata() ) &&
|
||||
( a.fieldName() == string( b.fieldName() ) );
|
||||
}
|
||||
|
||||
template< class Builder >
|
||||
void ModSetState::createNewFromMods( const string& root , Builder& b , const BSONObj &obj ) {
|
||||
DEBUGUPDATE( "\t\t createNewFromMods root: " << root );
|
||||
@ -654,8 +673,18 @@ namespace mongo {
|
||||
ModStateHolder::iterator mend = _mods.lower_bound( buf.str() );
|
||||
|
||||
set<string> onedownseen;
|
||||
|
||||
BSONElement prevE;
|
||||
while ( e.type() && m != mend ) {
|
||||
|
||||
if ( duplicateFieldName( prevE, e ) ) {
|
||||
// Just copy through an element with a duplicate field name.
|
||||
b.append( e );
|
||||
prevE = e;
|
||||
e = es.next();
|
||||
continue;
|
||||
}
|
||||
prevE = e;
|
||||
|
||||
string field = root + e.fieldName();
|
||||
FieldCompareResult cmp = compareDottedFieldNames( m->second.m->fieldName , field );
|
||||
|
||||
@ -684,11 +713,9 @@ namespace mongo {
|
||||
m++;
|
||||
}
|
||||
else {
|
||||
// this is a very weird case
|
||||
// have seen it in production, but can't reproduce
|
||||
// this assert prevents an inf. loop
|
||||
// but likely isn't the correct solution
|
||||
assert(0);
|
||||
massert( 16062 , "ModSet::createNewFromMods - "
|
||||
"SERVER-4777 unhandled duplicate field" , 0 );
|
||||
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -1070,7 +1097,7 @@ namespace mongo {
|
||||
return UpdateResult( 1 , 0 , 1 );
|
||||
}
|
||||
|
||||
UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& updateobj, BSONObj patternOrig, bool upsert, bool multi, bool logop , OpDebug& debug, RemoveSaver* rs ) {
|
||||
UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& updateobj, BSONObj patternOrig, bool upsert, bool multi, bool logop , OpDebug& debug, RemoveSaver* rs, bool hintIdElseNatural ) {
|
||||
DEBUGUPDATE( "update: " << ns << " update: " << updateobj << " query: " << patternOrig << " upsert: " << upsert << " multi: " << multi );
|
||||
Client& client = cc();
|
||||
int profile = client.database()->profile;
|
||||
@ -1119,7 +1146,7 @@ namespace mongo {
|
||||
int numModded = 0;
|
||||
long long nscanned = 0;
|
||||
shared_ptr< MultiCursor::CursorOp > opPtr( new UpdateOp( mods.get() && mods->hasDynamicArray() ) );
|
||||
shared_ptr< MultiCursor > c( new MultiCursor( ns, patternOrig, BSONObj(), opPtr, true ) );
|
||||
shared_ptr< MultiCursor > c( new MultiCursor( ns, patternOrig, BSONObj(), opPtr, true, hintIdElseNatural ) );
|
||||
|
||||
d = nsdetails(ns);
|
||||
nsdt = &NamespaceDetailsTransient::get_w(ns);
|
||||
@ -1354,16 +1381,17 @@ namespace mongo {
|
||||
logOp( "i", ns, no );
|
||||
return UpdateResult( 0 , 0 , 1 , no );
|
||||
}
|
||||
return UpdateResult( 0 , 0 , 0 );
|
||||
|
||||
return UpdateResult( 0 , isOperatorUpdate , 0 );
|
||||
}
|
||||
|
||||
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BSONObj patternOrig, bool upsert, bool multi, bool logop , OpDebug& debug ) {
|
||||
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BSONObj patternOrig, bool upsert, bool multi, bool logop , OpDebug& debug, bool hintIdElseNatural ) {
|
||||
uassert( 10155 , "cannot update reserved $ collection", strchr(ns, '$') == 0 );
|
||||
if ( strstr(ns, ".system.") ) {
|
||||
/* dm: it's very important that system.indexes is never updated as IndexDetails has pointers into it */
|
||||
uassert( 10156 , str::stream() << "cannot update system collection: " << ns << " q: " << patternOrig << " u: " << updateobj , legalClientSystemNS( ns , true ) );
|
||||
}
|
||||
return _updateObjects(false, ns, updateobj, patternOrig, upsert, multi, logop, debug);
|
||||
return _updateObjects(false, ns, updateobj, patternOrig, upsert, multi, logop, debug, 0, hintIdElseNatural);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -50,9 +50,10 @@ namespace mongo {
|
||||
multi - update multiple objects - mostly useful with things like $set
|
||||
god - allow access to system namespaces
|
||||
*/
|
||||
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BSONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug );
|
||||
UpdateResult updateObjects(const char *ns, const BSONObj& updateobj, BSONObj pattern, bool upsert, bool multi , bool logop , OpDebug& debug, bool hintIdElseNatural = false );
|
||||
UpdateResult _updateObjects(bool god, const char *ns, const BSONObj& updateobj, BSONObj pattern,
|
||||
bool upsert, bool multi , bool logop , OpDebug& debug , RemoveSaver * rs = 0 );
|
||||
bool upsert, bool multi , bool logop , OpDebug& debug , RemoveSaver * rs = 0,
|
||||
bool hintIdElseNatural = false);
|
||||
|
||||
|
||||
|
||||
@ -623,6 +624,9 @@ namespace mongo {
|
||||
|
||||
}
|
||||
|
||||
/** @return true iff the elements aren't eoo(), are distinct, and share a field name. */
|
||||
static bool duplicateFieldName( const BSONElement &a, const BSONElement &b );
|
||||
|
||||
public:
|
||||
|
||||
bool canApplyInPlace() const {
|
||||
|
||||
@ -434,6 +434,7 @@ namespace mongo {
|
||||
}
|
||||
|
||||
Extent* MongoDataFile::createExtent(const char *ns, int approxSize, bool newCapped, int loops) {
|
||||
assert( approxSize <= Extent::maxSize() );
|
||||
{
|
||||
// make sizes align with VM page size
|
||||
int newSize = (approxSize + 0xfff) & 0xfffff000;
|
||||
@ -491,6 +492,10 @@ namespace mongo {
|
||||
// overflowed
|
||||
high = max(approxSize, Extent::maxSize());
|
||||
}
|
||||
if ( high <= Extent::minSize() ) {
|
||||
// the minimum extent size is 4097
|
||||
high = Extent::minSize() + 1;
|
||||
}
|
||||
int n = 0;
|
||||
Extent *best = 0;
|
||||
int bestDiff = 0x7fffffff;
|
||||
|
||||
@ -313,7 +313,7 @@ namespace mongo {
|
||||
Extent* getPrevExtent() { return xprev.isNull() ? 0 : DataFileMgr::getExtent(xprev); }
|
||||
|
||||
static int maxSize();
|
||||
static int minSize() { return 0x100; }
|
||||
static int minSize() { return 0x1000; }
|
||||
/**
|
||||
* @param len lengt of record we need
|
||||
* @param lastRecord size of last extent which is a factor in next extent size
|
||||
|
||||
@ -214,22 +214,6 @@ doneCheckOrder:
|
||||
if ( willScanTable() ) {
|
||||
if ( _frs.nNontrivialRanges() ) {
|
||||
checkTableScanAllowed( _frs.ns() );
|
||||
|
||||
// if we are doing a table scan on _id
|
||||
// and its a capped collection
|
||||
// we disallow as its a common user error
|
||||
// .system. and local collections are exempt
|
||||
if ( _d && _d->capped && _frs.range( "_id" ).nontrivial() ) {
|
||||
if ( cc().isSyncThread() ||
|
||||
str::contains( _frs.ns() , ".system." ) ||
|
||||
str::startsWith( _frs.ns() , "local." ) ) {
|
||||
// ok
|
||||
}
|
||||
else {
|
||||
warning() << "_id query on capped collection without an _id index, performance will be poor collection: " << _frs.ns() << endl;
|
||||
//uassert( 14820, str::stream() << "doing _id query on a capped collection without an index is not allowed: " << _frs.ns() ,
|
||||
}
|
||||
}
|
||||
}
|
||||
return findTableScan( _frs.ns(), _order, startLoc );
|
||||
}
|
||||
@ -482,16 +466,18 @@ doneCheckOrder:
|
||||
}
|
||||
|
||||
massert( 10368 , "Unable to locate previously recorded index", p.get() );
|
||||
if ( !( _bestGuessOnly && p->scanAndOrderRequired() ) ) {
|
||||
if ( !p->unhelpful() && !( _bestGuessOnly && p->scanAndOrderRequired() ) ) {
|
||||
_usingPrerecordedPlan = true;
|
||||
_mayRecordPlan = false;
|
||||
_plans.push_back( p );
|
||||
warnOnCappedIdTableScan();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addOtherPlans( false );
|
||||
warnOnCappedIdTableScan();
|
||||
}
|
||||
|
||||
void QueryPlanSet::addOtherPlans( bool checkFirst ) {
|
||||
@ -633,6 +619,31 @@ doneCheckOrder:
|
||||
}
|
||||
return _plans[0];
|
||||
}
|
||||
|
||||
void QueryPlanSet::warnOnCappedIdTableScan() const {
|
||||
// if we are doing a table scan on _id
|
||||
// and it's a capped collection
|
||||
// we warn as it's a common user error
|
||||
// .system. and local collections are exempt
|
||||
const char *ns = _frsp->ns();
|
||||
NamespaceDetails *d = nsdetails( ns );
|
||||
if ( d &&
|
||||
d->capped &&
|
||||
nPlans() == 1 &&
|
||||
firstPlan()->willScanTable() &&
|
||||
firstPlan()->multikeyFrs().range( "_id" ).nontrivial() ) {
|
||||
if ( cc().isSyncThread() ||
|
||||
str::contains( ns , ".system." ) ||
|
||||
str::startsWith( ns , "local." ) ) {
|
||||
// ok
|
||||
}
|
||||
else {
|
||||
warning()
|
||||
<< "unindexed _id query on capped collection, "
|
||||
<< "performance will be poor collection: " << ns << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QueryPlanSet::Runner::Runner( QueryPlanSet &plans, QueryOp &op ) :
|
||||
_op( op ),
|
||||
@ -995,8 +1006,12 @@ doneCheckOrder:
|
||||
return QueryUtilIndexed::uselessOr( *_org, nsd, -1 );
|
||||
}
|
||||
|
||||
MultiCursor::MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr<CursorOp> op, bool mayYield )
|
||||
: _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj(), BSONObj(), !op.get(), mayYield ) ), _nscanned() {
|
||||
MultiCursor::MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr<CursorOp> op, bool mayYield, bool hintIdElseNatural ) :
|
||||
_hint( hintIdElseNatural ? idElseNaturalHint( ns ) : BSONObj() ),
|
||||
_hintElt( _hint.firstElement() ),
|
||||
_mps( new MultiPlanScanner( ns, pattern, order, _hintElt.eoo() ? 0 : &_hintElt, true,
|
||||
BSONObj(), BSONObj(), !op.get(), mayYield ) ),
|
||||
_nscanned() {
|
||||
if ( op.get() ) {
|
||||
_op = op;
|
||||
}
|
||||
@ -1035,7 +1050,15 @@ doneCheckOrder:
|
||||
_matcher = best->matcher( _c );
|
||||
_op = best;
|
||||
}
|
||||
|
||||
|
||||
BSONObj MultiCursor::idElseNaturalHint( const char *ns ) {
|
||||
NamespaceDetails *nsd = nsdetails( ns );
|
||||
if ( !nsd || !nsd->haveIdIndex() ) {
|
||||
return BSON( "$hint" << BSON( "$natural" << 1 ) );
|
||||
}
|
||||
return BSON( "$hint" << nsd->idx( nsd->findIdIndex() ).indexName() );
|
||||
}
|
||||
|
||||
bool indexWorks( const BSONObj &idxPattern, const BSONObj &sampleKey, int direction, int firstSignificantField ) {
|
||||
BSONObjIterator p( idxPattern );
|
||||
BSONObjIterator k( sampleKey );
|
||||
@ -1247,8 +1270,12 @@ doneCheckOrder:
|
||||
void QueryUtilIndexed::clearIndexesForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order ) {
|
||||
SimpleMutex::scoped_lock lk(NamespaceDetailsTransient::_qcMutex);
|
||||
NamespaceDetailsTransient& nsd = NamespaceDetailsTransient::get_inlock( frsp.ns() );
|
||||
nsd.registerIndexForPattern( frsp._singleKey.pattern( order ), BSONObj(), 0 );
|
||||
nsd.registerIndexForPattern( frsp._multiKey.pattern( order ), BSONObj(), 0 );
|
||||
if ( frsp._singleKey.matchPossible() ) {
|
||||
nsd.registerIndexForPattern( frsp._singleKey.pattern( order ), BSONObj(), 0 );
|
||||
}
|
||||
if ( frsp._multiKey.matchPossible() ) {
|
||||
nsd.registerIndexForPattern( frsp._multiKey.pattern( order ), BSONObj(), 0 );
|
||||
}
|
||||
}
|
||||
|
||||
pair< BSONObj, long long > QueryUtilIndexed::bestIndexForPatterns( const FieldRangeSetPair &frsp, const BSONObj &order ) {
|
||||
|
||||
@ -314,6 +314,7 @@ namespace mongo {
|
||||
}
|
||||
void init();
|
||||
void addHint( IndexDetails &id );
|
||||
void warnOnCappedIdTableScan() const;
|
||||
class Runner {
|
||||
public:
|
||||
Runner( QueryPlanSet &plans, QueryOp &op );
|
||||
@ -478,7 +479,7 @@ namespace mongo {
|
||||
virtual shared_ptr<Cursor> newCursor() const = 0;
|
||||
};
|
||||
/** takes ownership of 'op' */
|
||||
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr<CursorOp> op = shared_ptr<CursorOp>(), bool mayYield = false );
|
||||
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr<CursorOp> op = shared_ptr<CursorOp>(), bool mayYield = false, bool hintIdElseNatural = false );
|
||||
/**
|
||||
* Used
|
||||
* 1. To handoff a query to a getMore()
|
||||
@ -538,8 +539,11 @@ namespace mongo {
|
||||
virtual long long nscanned() { assert( false ); return 0; }
|
||||
};
|
||||
void nextClause();
|
||||
static BSONObj idElseNaturalHint( const char *ns );
|
||||
shared_ptr<CursorOp> _op;
|
||||
shared_ptr<Cursor> _c;
|
||||
BSONObj _hint;
|
||||
BSONElement _hintElt;
|
||||
auto_ptr<MultiPlanScanner> _mps;
|
||||
shared_ptr<CoveredIndexMatcher> _matcher;
|
||||
long long _nscanned;
|
||||
|
||||
@ -35,7 +35,9 @@ namespace mongo {
|
||||
* @param aggregateNscanned - shared int counting total nscanned for
|
||||
* query ops for all cursors.
|
||||
*/
|
||||
QueryOptimizerCursorOp( long long &aggregateNscanned ) : _matchCount(), _mustAdvance(), _nscanned(), _aggregateNscanned( aggregateNscanned ) {}
|
||||
QueryOptimizerCursorOp( long long &aggregateNscanned ) :
|
||||
_matchCount(), _mustAdvance(), _nscanned(), _capped(),
|
||||
_aggregateNscanned( aggregateNscanned ), _yieldRecoveryFailed() {}
|
||||
|
||||
virtual void _init() {
|
||||
if ( qp().scanAndOrderRequired() ) {
|
||||
@ -64,6 +66,7 @@ namespace mongo {
|
||||
|
||||
virtual void recoverFromYield() {
|
||||
if ( _cc && !ClientCursor::recoverFromYield( _yieldData ) ) {
|
||||
_yieldRecoveryFailed = true;
|
||||
_c.reset();
|
||||
_cc.reset();
|
||||
|
||||
@ -113,12 +116,15 @@ namespace mongo {
|
||||
DiskLoc currLoc() const { return _c ? _c->currLoc() : DiskLoc(); }
|
||||
BSONObj currKey() const { return _c ? _c->currKey() : BSONObj(); }
|
||||
virtual bool mayRecordPlan() const {
|
||||
return complete() && !stopRequested();
|
||||
return !_yieldRecoveryFailed && complete() && !stopRequested();
|
||||
}
|
||||
shared_ptr<Cursor> cursor() const { return _c; }
|
||||
private:
|
||||
void mayAdvance() {
|
||||
if ( _mustAdvance && _c ) {
|
||||
if ( !_c ) {
|
||||
return;
|
||||
}
|
||||
if ( _mustAdvance ) {
|
||||
_c->advance();
|
||||
_mustAdvance = false;
|
||||
}
|
||||
@ -134,6 +140,7 @@ namespace mongo {
|
||||
DiskLoc _posBeforeYield;
|
||||
ClientCursor::YieldData _yieldData;
|
||||
long long &_aggregateNscanned;
|
||||
bool _yieldRecoveryFailed;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -181,36 +188,7 @@ namespace mongo {
|
||||
return DiskLoc();
|
||||
}
|
||||
virtual bool advance() {
|
||||
if ( _takeover ) {
|
||||
return _takeover->advance();
|
||||
}
|
||||
|
||||
// Ok to advance if currOp in an error state due to failed yield recovery.
|
||||
// This may be the case when advance() is called by recoverFromYield().
|
||||
if ( !( _currOp && _currOp->error() ) && !ok() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_currOp = 0;
|
||||
shared_ptr<QueryOp> op = _mps->nextOp();
|
||||
rethrowOnError( op );
|
||||
|
||||
QueryOptimizerCursorOp *qocop = dynamic_cast<QueryOptimizerCursorOp*>( op.get() );
|
||||
if ( !op->complete() ) {
|
||||
// 'qocop' will be valid until we call _mps->nextOp() again.
|
||||
_currOp = qocop;
|
||||
}
|
||||
else if ( op->stopRequested() ) {
|
||||
if ( qocop->cursor() ) {
|
||||
_takeover.reset( new MultiCursor( _mps,
|
||||
qocop->cursor(),
|
||||
op->matcher( qocop->cursor() ),
|
||||
*op,
|
||||
_nscanned - qocop->cursor()->nscanned() ) );
|
||||
}
|
||||
}
|
||||
|
||||
return ok();
|
||||
return _advance( false );
|
||||
}
|
||||
virtual BSONObj currKey() const {
|
||||
if ( _takeover ) {
|
||||
@ -252,9 +230,9 @@ namespace mongo {
|
||||
}
|
||||
if ( _currOp ) {
|
||||
_mps->recoverFromYield();
|
||||
if ( _currOp->error() ) {
|
||||
// See if we can advance to a non error op.
|
||||
advance();
|
||||
if ( _currOp->error() || !ok() ) {
|
||||
// Advance to a non error op or a following $or clause if possible.
|
||||
_advance( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -304,6 +282,36 @@ namespace mongo {
|
||||
}
|
||||
|
||||
private:
|
||||
bool _advance( bool force ) {
|
||||
if ( _takeover ) {
|
||||
return _takeover->advance();
|
||||
}
|
||||
|
||||
if ( !force && !ok() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_currOp = 0;
|
||||
shared_ptr<QueryOp> op = _mps->nextOp();
|
||||
rethrowOnError( op );
|
||||
|
||||
QueryOptimizerCursorOp *qocop = dynamic_cast<QueryOptimizerCursorOp*>( op.get() );
|
||||
if ( !op->complete() ) {
|
||||
// 'qocop' will be valid until we call _mps->nextOp() again.
|
||||
_currOp = qocop;
|
||||
}
|
||||
else if ( op->stopRequested() ) {
|
||||
if ( qocop->cursor() ) {
|
||||
_takeover.reset( new MultiCursor( _mps,
|
||||
qocop->cursor(),
|
||||
op->matcher( qocop->cursor() ),
|
||||
*op,
|
||||
_nscanned - qocop->cursor()->nscanned() ) );
|
||||
}
|
||||
}
|
||||
|
||||
return ok();
|
||||
}
|
||||
void rethrowOnError( const shared_ptr< QueryOp > &op ) {
|
||||
// If all plans have erred out, assert.
|
||||
if ( op->error() ) {
|
||||
|
||||
@ -36,7 +36,8 @@ namespace mongo {
|
||||
Equality,
|
||||
LowerBound,
|
||||
UpperBound,
|
||||
UpperAndLowerBound
|
||||
UpperAndLowerBound,
|
||||
ConstraintPresent
|
||||
};
|
||||
bool operator<( const QueryPattern &other ) const;
|
||||
/** for testing only */
|
||||
|
||||
@ -1007,6 +1007,8 @@ namespace mongo {
|
||||
qp._fieldTypes[ i->first ] = QueryPattern::UpperBound;
|
||||
else if ( lower )
|
||||
qp._fieldTypes[ i->first ] = QueryPattern::LowerBound;
|
||||
else
|
||||
qp._fieldTypes[ i->first ] = QueryPattern::ConstraintPresent;
|
||||
}
|
||||
}
|
||||
qp.setSort( sort );
|
||||
@ -1019,13 +1021,13 @@ namespace mongo {
|
||||
BoundBuilders builders;
|
||||
builders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) );
|
||||
BSONObjIterator i( keyPattern );
|
||||
bool ineq = false; // until ineq is true, we are just dealing with equality and $in bounds
|
||||
bool equalityOnly = true; // until equalityOnly is false, we are just dealing with equality (no range or $in querys).
|
||||
while( i.more() ) {
|
||||
BSONElement e = i.next();
|
||||
const FieldRange &fr = range( e.fieldName() );
|
||||
int number = (int) e.number(); // returns 0.0 if not numeric
|
||||
bool forward = ( ( number >= 0 ? 1 : -1 ) * ( direction >= 0 ? 1 : -1 ) > 0 );
|
||||
if ( !ineq ) {
|
||||
if ( equalityOnly ) {
|
||||
if ( fr.equality() ) {
|
||||
for( BoundBuilders::const_iterator j = builders.begin(); j != builders.end(); ++j ) {
|
||||
j->first->appendAs( fr.min(), "" );
|
||||
@ -1033,9 +1035,8 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( !fr.inQuery() ) {
|
||||
ineq = true;
|
||||
}
|
||||
equalityOnly = false;
|
||||
|
||||
BoundBuilders newBuilders;
|
||||
const vector<FieldInterval> &intervals = fr.intervals();
|
||||
for( BoundBuilders::const_iterator i = builders.begin(); i != builders.end(); ++i ) {
|
||||
|
||||
@ -328,7 +328,7 @@ namespace mongo {
|
||||
bool matchesElement( const BSONElement &e, int i, bool direction ) const;
|
||||
bool matchesKey( const BSONObj &key ) const;
|
||||
vector<FieldRange> _ranges;
|
||||
const IndexSpec &_indexSpec;
|
||||
IndexSpec _indexSpec;
|
||||
int _direction;
|
||||
vector<BSONObj> _queries; // make sure mem owned
|
||||
friend class FieldRangeVectorIterator;
|
||||
|
||||
@ -112,7 +112,8 @@ namespace mongo {
|
||||
class Rolling {
|
||||
|
||||
public:
|
||||
Rolling() {
|
||||
Rolling()
|
||||
: _lock( "ps::Rolling" ){
|
||||
_curSlice = 0;
|
||||
_lastRotate = Listener::getElapsedTimeMillis();
|
||||
}
|
||||
@ -126,8 +127,8 @@ namespace mongo {
|
||||
bool access( size_t region , short offset , bool doHalf ) {
|
||||
int regionHash = hash(region);
|
||||
|
||||
scoped_spinlock lk( _lock );
|
||||
|
||||
SimpleMutex::scoped_lock lk( _lock );
|
||||
|
||||
static int rarely_count = 0;
|
||||
if ( rarely_count++ % 2048 == 0 ) {
|
||||
long long now = Listener::getElapsedTimeMillis();
|
||||
@ -174,7 +175,7 @@ namespace mongo {
|
||||
long long _lastRotate;
|
||||
Slice _slices[NumSlices];
|
||||
|
||||
SpinLock _lock;
|
||||
SimpleMutex _lock;
|
||||
} rolling;
|
||||
|
||||
}
|
||||
|
||||
50
db/repl.cpp
50
db/repl.cpp
@ -508,12 +508,12 @@ namespace mongo {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DatabaseIgnorer ___databaseIgnorer;
|
||||
|
||||
|
||||
void DatabaseIgnorer::doIgnoreUntilAfter( const string &db, const OpTime &futureOplogTime ) {
|
||||
if ( futureOplogTime > _ignores[ db ] ) {
|
||||
_ignores[ db ] = futureOplogTime;
|
||||
_ignores[ db ] = futureOplogTime;
|
||||
}
|
||||
}
|
||||
|
||||
@ -533,28 +533,28 @@ namespace mongo {
|
||||
bool ReplSource::handleDuplicateDbName( const BSONObj &op, const char *ns, const char *db ) {
|
||||
if ( dbHolder.isLoaded( ns, dbpath ) ) {
|
||||
// Database is already present.
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
BSONElement ts = op.getField( "ts" );
|
||||
if ( ( ts.type() == Date || ts.type() == Timestamp ) && ___databaseIgnorer.ignoreAt( db, ts.date() ) ) {
|
||||
// Database is ignored due to a previous indication that it is
|
||||
// missing from master after optime "ts".
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
if ( Database::duplicateUncasedName( db, dbpath ).empty() ) {
|
||||
// No duplicate database names are present.
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
OpTime lastTime;
|
||||
bool dbOk = false;
|
||||
{
|
||||
dbtemprelease release;
|
||||
|
||||
|
||||
// We always log an operation after executing it (never before), so
|
||||
// a database list will always be valid as of an oplog entry generated
|
||||
// before it was retrieved.
|
||||
|
||||
|
||||
BSONObj last = oplogReader.findOne( this->ns().c_str(), Query().sort( BSON( "$natural" << -1 ) ) );
|
||||
if ( !last.isEmpty() ) {
|
||||
BSONElement ts = last.getField( "ts" );
|
||||
@ -568,34 +568,34 @@ namespace mongo {
|
||||
BSONObjIterator i( info.getField( "databases" ).embeddedObject() );
|
||||
while( i.more() ) {
|
||||
BSONElement e = i.next();
|
||||
|
||||
|
||||
const char * name = e.embeddedObject().getField( "name" ).valuestr();
|
||||
if ( strcasecmp( name, db ) != 0 )
|
||||
continue;
|
||||
|
||||
|
||||
if ( strcmp( name, db ) == 0 ) {
|
||||
// The db exists on master, still need to check that no conflicts exist there.
|
||||
dbOk = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// The master has a db name that conflicts with the requested name.
|
||||
dbOk = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( !dbOk ) {
|
||||
___databaseIgnorer.doIgnoreUntilAfter( db, lastTime );
|
||||
incompleteCloneDbs.erase(db);
|
||||
addDbNextPass.erase(db);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Check for duplicates again, since we released the lock above.
|
||||
set< string > duplicates;
|
||||
Database::duplicateUncasedName( db, dbpath, &duplicates );
|
||||
|
||||
|
||||
// The database is present on the master and no conflicting databases
|
||||
// are present on the master. Drop any local conflicts.
|
||||
for( set< string >::const_iterator i = duplicates.begin(); i != duplicates.end(); ++i ) {
|
||||
@ -605,7 +605,7 @@ namespace mongo {
|
||||
Client::Context ctx(*i);
|
||||
dropDatabase(*i);
|
||||
}
|
||||
|
||||
|
||||
massert( 14034, "Duplicate database names present after attempting to delete duplicates",
|
||||
Database::duplicateUncasedName( db, dbpath ).empty() );
|
||||
return true;
|
||||
@ -613,7 +613,11 @@ namespace mongo {
|
||||
|
||||
void ReplSource::applyOperation(const BSONObj& op) {
|
||||
try {
|
||||
applyOperation_inlock( op );
|
||||
bool failedUpdate = applyOperation_inlock( op );
|
||||
if (failedUpdate && shouldRetry(op, hostName)) {
|
||||
failedUpdate = applyOperation_inlock( op );
|
||||
uassert(15914, "Failure retrying initial sync update", ! failedUpdate );
|
||||
}
|
||||
}
|
||||
catch ( UserException& e ) {
|
||||
log() << "sync: caught user assertion " << e << " while applying op: " << op << endl;;
|
||||
@ -705,9 +709,9 @@ namespace mongo {
|
||||
}
|
||||
|
||||
if ( !handleDuplicateDbName( op, ns, clientName ) ) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Client::Context ctx( ns );
|
||||
ctx.getClient()->curop()->reset();
|
||||
|
||||
@ -943,7 +947,7 @@ namespace mongo {
|
||||
}
|
||||
// otherwise, break out of loop so we can set to completed or clone more dbs
|
||||
}
|
||||
|
||||
|
||||
if( oplogReader.awaitCapable() && tailing )
|
||||
okResultCode = 0; // don't sleep
|
||||
syncedTo = nextOpTime;
|
||||
@ -1077,7 +1081,7 @@ namespace mongo {
|
||||
|
||||
BSONObj me;
|
||||
{
|
||||
|
||||
|
||||
dblock l;
|
||||
// local.me is an identifier for a server for getLastError w:2+
|
||||
if ( ! Helpers::getSingleton( "local.me" , me ) ||
|
||||
@ -1111,7 +1115,7 @@ namespace mongo {
|
||||
|
||||
bool OplogReader::commonConnect(const string& hostName) {
|
||||
if( conn() == 0 ) {
|
||||
_conn = shared_ptr<DBClientConnection>(new DBClientConnection( false, 0, 0 /* tcp timeout */));
|
||||
_conn = shared_ptr<DBClientConnection>(new DBClientConnection( false, 0, 60*10 /* tcp timeout */));
|
||||
string errmsg;
|
||||
ReplInfo r("trying to connect to sync source");
|
||||
if ( !_conn->connect(hostName.c_str(), errmsg) ||
|
||||
@ -1123,7 +1127,7 @@ namespace mongo {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool OplogReader::connect(string hostName) {
|
||||
if (conn() != 0) {
|
||||
return true;
|
||||
|
||||
@ -122,11 +122,11 @@ namespace mongo {
|
||||
* @return true iff an op with the specified ns may be applied.
|
||||
*/
|
||||
bool handleDuplicateDbName( const BSONObj &op, const char *ns, const char *db );
|
||||
|
||||
|
||||
public:
|
||||
OplogReader oplogReader;
|
||||
|
||||
static void applyOperation(const BSONObj& op);
|
||||
void applyOperation(const BSONObj& op);
|
||||
string hostName; // ip addr or hostname plus optionally, ":<port>"
|
||||
string _sourceName; // a logical source name.
|
||||
string sourceName() const { return _sourceName.empty() ? "main" : _sourceName; }
|
||||
|
||||
@ -47,6 +47,10 @@ namespace mongo {
|
||||
~ScopedConn() {
|
||||
// conLock releases...
|
||||
}
|
||||
void reconnect() {
|
||||
conn()->port().shutdown();
|
||||
connect();
|
||||
}
|
||||
|
||||
/* If we were to run a query and not exhaust the cursor, future use of the connection would be problematic.
|
||||
So here what we do is wrapper known safe methods and not allow cursor-style queries at all. This makes
|
||||
@ -61,9 +65,6 @@ namespace mongo {
|
||||
BSONObj findOne(const string &ns, const Query& q, const BSONObj *fieldsToReturn = 0, int queryOptions = 0) {
|
||||
return conn()->findOne(ns, q, fieldsToReturn, queryOptions);
|
||||
}
|
||||
void setTimeout(double to) {
|
||||
conn()->setSoTimeout(to);
|
||||
}
|
||||
|
||||
private:
|
||||
auto_ptr<scoped_lock> connLock;
|
||||
@ -71,42 +72,63 @@ namespace mongo {
|
||||
struct X {
|
||||
mongo::mutex z;
|
||||
DBClientConnection cc;
|
||||
X() : z("X"), cc(/*reconnect*/ true, 0, /*timeout*/ 10.0) {
|
||||
bool connected;
|
||||
X() : z("X"), cc(/*reconnect*/ true, 0, /*timeout*/ 10.0), connected(false) {
|
||||
cc._logLevel = 2;
|
||||
}
|
||||
} *x;
|
||||
typedef map<string,ScopedConn::X*> M;
|
||||
static M& _map;
|
||||
DBClientConnection* conn() { return &x->cc; }
|
||||
const string _hostport;
|
||||
|
||||
// we should already be locked...
|
||||
bool connect() {
|
||||
string err;
|
||||
if (!x->cc.connect(_hostport, err)) {
|
||||
log() << "couldn't connect to " << _hostport << ": " << err << rsLog;
|
||||
return false;
|
||||
}
|
||||
x->connected = true;
|
||||
|
||||
// if we cannot authenticate against a member, then either its key file
|
||||
// or our key file has to change. if our key file has to change, we'll
|
||||
// be rebooting. if their file has to change, they'll be rebooted so the
|
||||
// connection created above will go dead, reconnect, and reauth.
|
||||
if (!noauth && !x->cc.auth("local", internalSecurity.user, internalSecurity.pwd, err, false)) {
|
||||
log() << "could not authenticate against " << _hostport << ", " << err << rsLog;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
inline ScopedConn::ScopedConn(string hostport) {
|
||||
inline ScopedConn::ScopedConn(string hostport) : _hostport(hostport) {
|
||||
bool first = false;
|
||||
{
|
||||
scoped_lock lk(mapMutex);
|
||||
x = _map[hostport];
|
||||
x = _map[_hostport];
|
||||
if( x == 0 ) {
|
||||
x = _map[hostport] = new X();
|
||||
x = _map[_hostport] = new X();
|
||||
first = true;
|
||||
connLock.reset( new scoped_lock(x->z) );
|
||||
}
|
||||
}
|
||||
if( !first ) {
|
||||
connLock.reset( new scoped_lock(x->z) );
|
||||
|
||||
// already locked connLock above
|
||||
if (first) {
|
||||
connect();
|
||||
return;
|
||||
}
|
||||
|
||||
// we already locked above...
|
||||
string err;
|
||||
if (!x->cc.connect(hostport, err)) {
|
||||
log() << "couldn't connect to " << hostport << ": " << err << rsLog;
|
||||
connLock.reset( new scoped_lock(x->z) );
|
||||
if (x->connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!noauth && !x->cc.auth("local", internalSecurity.user, internalSecurity.pwd, err, false)) {
|
||||
log() << "could not authenticate against " << conn()->toString() << ", " << err << rsLog;
|
||||
return;
|
||||
}
|
||||
// Keep trying to connect if we're not yet connected
|
||||
connect();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -402,6 +402,11 @@ namespace mongo {
|
||||
string s = m->lhb();
|
||||
if( !s.empty() )
|
||||
bb.append("errmsg", s);
|
||||
|
||||
if (m->hbinfo().authIssue) {
|
||||
bb.append("authenticated", false);
|
||||
}
|
||||
|
||||
v.push_back(bb.obj());
|
||||
m = m->next();
|
||||
}
|
||||
|
||||
@ -51,11 +51,14 @@ namespace mongo {
|
||||
/* { replSetHeartbeat : <setname> } */
|
||||
class CmdReplSetHeartbeat : public ReplSetCommand {
|
||||
public:
|
||||
virtual bool adminOnly() const { return false; }
|
||||
CmdReplSetHeartbeat() : ReplSetCommand("replSetHeartbeat") { }
|
||||
virtual bool run(const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {
|
||||
if( replSetBlind )
|
||||
if( replSetBlind ) {
|
||||
if (theReplSet) {
|
||||
errmsg = str::stream() << theReplSet->selfFullName() << " is blind";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* we don't call ReplSetCommand::check() here because heartbeat
|
||||
checks many things that are pre-initialization. */
|
||||
@ -99,8 +102,8 @@ namespace mongo {
|
||||
if( !from.empty() ) {
|
||||
replSettings.discoveredSeeds.insert(from);
|
||||
}
|
||||
errmsg = "still initializing";
|
||||
return false;
|
||||
result.append("hbmsg", "still initializing");
|
||||
return true;
|
||||
}
|
||||
|
||||
if( theReplSet->name() != cmdObj.getStringField("replSetHeartbeat") ) {
|
||||
@ -123,32 +126,54 @@ namespace mongo {
|
||||
}
|
||||
} cmdReplSetHeartbeat;
|
||||
|
||||
/* throws dbexception */
|
||||
bool requestHeartbeat(string setName, string from, string memberFullName, BSONObj& result, int myCfgVersion, int& theirCfgVersion, bool checkEmpty) {
|
||||
bool requestHeartbeat(string setName, string from, string memberFullName, BSONObj& result,
|
||||
int myCfgVersion, int& theirCfgVersion, bool checkEmpty) {
|
||||
if( replSetBlind ) {
|
||||
//sleepmillis( rand() );
|
||||
return false;
|
||||
}
|
||||
|
||||
BSONObj cmd = BSON( "replSetHeartbeat" << setName << "v" << myCfgVersion << "pv" << 1 << "checkEmpty" << checkEmpty << "from" << from );
|
||||
BSONObj cmd = BSON( "replSetHeartbeat" << setName <<
|
||||
"v" << myCfgVersion <<
|
||||
"pv" << 1 <<
|
||||
"checkEmpty" << checkEmpty <<
|
||||
"from" << from );
|
||||
|
||||
// we might be talking to ourself - generally not a great idea to do outbound waiting calls in a write lock
|
||||
assert( !dbMutex.isWriteLocked() );
|
||||
|
||||
// these are slow (multisecond to respond), so generally we don't want to be locked, at least not without
|
||||
// generally not a great idea to do outbound waiting calls in a
|
||||
// write lock. heartbeats can be slow (multisecond to respond), so
|
||||
// generally we don't want to be locked, at least not without
|
||||
// thinking acarefully about it first.
|
||||
assert( theReplSet == 0 || !theReplSet->lockedByMe() );
|
||||
uassert(15900, "can't heartbeat: too much lock",
|
||||
!dbMutex.isWriteLocked() || theReplSet == 0 || !theReplSet->lockedByMe() );
|
||||
|
||||
ScopedConn conn(memberFullName);
|
||||
return conn.runCommand("admin", cmd, result, 0);
|
||||
}
|
||||
|
||||
/* poll every other set member to check its status */
|
||||
/**
|
||||
* Poll every other set member to check its status.
|
||||
*
|
||||
* A detail about local machines and authentication: suppose we have 2
|
||||
* members, A and B, on the same machine using different keyFiles. A is
|
||||
* primary. If we're just starting the set, there are no admin users, so A
|
||||
* and B can access each other because it's local access.
|
||||
*
|
||||
* Then we add a user to A. B cannot sync this user from A, because as soon
|
||||
* as we add a an admin user, A requires auth. However, A can still
|
||||
* heartbeat B, because B *doesn't* have an admin user. So A can reach B
|
||||
* but B cannot reach A.
|
||||
*
|
||||
* Once B is restarted with the correct keyFile, everything should work as
|
||||
* expected.
|
||||
*/
|
||||
class ReplSetHealthPollTask : public task::Task {
|
||||
private:
|
||||
HostAndPort h;
|
||||
HeartbeatInfo m;
|
||||
int tries;
|
||||
const int threshold;
|
||||
public:
|
||||
ReplSetHealthPollTask(const HostAndPort& hh, const HeartbeatInfo& mm) : h(hh), m(mm) { }
|
||||
ReplSetHealthPollTask(const HostAndPort& hh, const HeartbeatInfo& mm)
|
||||
: h(hh), m(mm), tries(0), threshold(15) { }
|
||||
|
||||
string name() const { return "rsHealthPoll"; }
|
||||
void doWork() {
|
||||
@ -163,16 +188,7 @@ namespace mongo {
|
||||
BSONObj info;
|
||||
int theirConfigVersion = -10000;
|
||||
|
||||
Timer timer;
|
||||
|
||||
bool ok = requestHeartbeat(theReplSet->name(), theReplSet->selfFullName(), h.toString(), info, theReplSet->config().version, theirConfigVersion);
|
||||
|
||||
mem.ping = (unsigned int)timer.millis();
|
||||
|
||||
time_t before = timer.startTime() / 1000000;
|
||||
// we set this on any response - we don't get this far if
|
||||
// couldn't connect because exception is thrown
|
||||
time_t after = mem.lastHeartbeat = before + (mem.ping / 1000);
|
||||
bool ok = _requestHeartbeat(mem, info, theirConfigVersion);
|
||||
|
||||
// weight new ping with old pings
|
||||
// on the first ping, just use the ping value
|
||||
@ -180,68 +196,12 @@ namespace mongo {
|
||||
mem.ping = (unsigned int)((old.ping * .8) + (mem.ping * .2));
|
||||
}
|
||||
|
||||
if ( info["time"].isNumber() ) {
|
||||
long long t = info["time"].numberLong();
|
||||
if( t > after )
|
||||
mem.skew = (int) (t - after);
|
||||
else if( t < before )
|
||||
mem.skew = (int) (t - before); // negative
|
||||
}
|
||||
else {
|
||||
// it won't be there if remote hasn't initialized yet
|
||||
if( info.hasElement("time") )
|
||||
warning() << "heatbeat.time isn't a number: " << info << endl;
|
||||
mem.skew = INT_MIN;
|
||||
}
|
||||
|
||||
{
|
||||
be state = info["state"];
|
||||
if( state.ok() )
|
||||
mem.hbstate = MemberState(state.Int());
|
||||
}
|
||||
if( ok ) {
|
||||
HeartbeatInfo::numPings++;
|
||||
|
||||
if( mem.upSince == 0 ) {
|
||||
log() << "replSet info member " << h.toString() << " is up" << rsLog;
|
||||
mem.upSince = mem.lastHeartbeat;
|
||||
}
|
||||
mem.health = 1.0;
|
||||
mem.lastHeartbeatMsg = info["hbmsg"].String();
|
||||
if( info.hasElement("opTime") )
|
||||
mem.opTime = info["opTime"].Date();
|
||||
|
||||
// see if this member is in the electable set
|
||||
if( info["e"].eoo() ) {
|
||||
// for backwards compatibility
|
||||
const Member *member = theReplSet->findById(mem.id());
|
||||
if (member && member->config().potentiallyHot()) {
|
||||
theReplSet->addToElectable(mem.id());
|
||||
}
|
||||
else {
|
||||
theReplSet->rmFromElectable(mem.id());
|
||||
}
|
||||
}
|
||||
// add this server to the electable set if it is within 10
|
||||
// seconds of the latest optime we know of
|
||||
else if( info["e"].trueValue() &&
|
||||
mem.opTime >= theReplSet->lastOpTimeWritten.getSecs() - 10) {
|
||||
unsigned lastOp = theReplSet->lastOtherOpTime().getSecs();
|
||||
if (lastOp > 0 && mem.opTime >= lastOp - 10) {
|
||||
theReplSet->addToElectable(mem.id());
|
||||
}
|
||||
}
|
||||
else {
|
||||
theReplSet->rmFromElectable(mem.id());
|
||||
}
|
||||
|
||||
be cfg = info["config"];
|
||||
if( cfg.ok() ) {
|
||||
// received a new config
|
||||
boost::function<void()> f =
|
||||
boost::bind(&Manager::msgReceivedNewConfig, theReplSet->mgr, cfg.Obj().copy());
|
||||
theReplSet->mgr->send(f);
|
||||
}
|
||||
up(info, mem);
|
||||
}
|
||||
else if (!info["errmsg"].eoo() &&
|
||||
info["errmsg"].str() == "need to login") {
|
||||
authIssue(mem);
|
||||
}
|
||||
else {
|
||||
down(mem, info.getStringField("errmsg"));
|
||||
@ -271,7 +231,58 @@ namespace mongo {
|
||||
}
|
||||
|
||||
private:
|
||||
bool _requestHeartbeat(HeartbeatInfo& mem, BSONObj& info, int& theirConfigVersion) {
|
||||
if (tries++ % threshold == (threshold - 1)) {
|
||||
ScopedConn conn(h.toString());
|
||||
conn.reconnect();
|
||||
}
|
||||
|
||||
Timer timer;
|
||||
|
||||
bool ok = requestHeartbeat(theReplSet->name(), theReplSet->selfFullName(),
|
||||
h.toString(), info, theReplSet->config().version, theirConfigVersion);
|
||||
|
||||
mem.ping = (unsigned int)timer.millis();
|
||||
|
||||
time_t before = timer.startTime() / 1000000;
|
||||
// we set this on any response - we don't get this far if
|
||||
// couldn't connect because exception is thrown
|
||||
time_t after = mem.lastHeartbeat = before + (mem.ping / 1000);
|
||||
|
||||
if ( info["time"].isNumber() ) {
|
||||
long long t = info["time"].numberLong();
|
||||
if( t > after )
|
||||
mem.skew = (int) (t - after);
|
||||
else if( t < before )
|
||||
mem.skew = (int) (t - before); // negative
|
||||
}
|
||||
else {
|
||||
// it won't be there if remote hasn't initialized yet
|
||||
if( info.hasElement("time") )
|
||||
warning() << "heatbeat.time isn't a number: " << info << endl;
|
||||
mem.skew = INT_MIN;
|
||||
}
|
||||
|
||||
{
|
||||
be state = info["state"];
|
||||
if( state.ok() )
|
||||
mem.hbstate = MemberState(state.Int());
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void authIssue(HeartbeatInfo& mem) {
|
||||
mem.authIssue = true;
|
||||
mem.hbstate = MemberState::RS_UNKNOWN;
|
||||
|
||||
// set health to 0 so that this doesn't count towards majority
|
||||
mem.health = 0.0;
|
||||
theReplSet->rmFromElectable(mem.id());
|
||||
}
|
||||
|
||||
void down(HeartbeatInfo& mem, string msg) {
|
||||
mem.authIssue = false;
|
||||
mem.health = 0.0;
|
||||
mem.ping = 0;
|
||||
if( mem.upSince || mem.downSince == 0 ) {
|
||||
@ -283,6 +294,52 @@ namespace mongo {
|
||||
mem.lastHeartbeatMsg = msg;
|
||||
theReplSet->rmFromElectable(mem.id());
|
||||
}
|
||||
|
||||
void up(const BSONObj& info, HeartbeatInfo& mem) {
|
||||
HeartbeatInfo::numPings++;
|
||||
mem.authIssue = false;
|
||||
|
||||
if( mem.upSince == 0 ) {
|
||||
log() << "replSet member " << h.toString() << " is up" << rsLog;
|
||||
mem.upSince = mem.lastHeartbeat;
|
||||
}
|
||||
mem.health = 1.0;
|
||||
mem.lastHeartbeatMsg = info["hbmsg"].String();
|
||||
if( info.hasElement("opTime") )
|
||||
mem.opTime = info["opTime"].Date();
|
||||
|
||||
// see if this member is in the electable set
|
||||
if( info["e"].eoo() ) {
|
||||
// for backwards compatibility
|
||||
const Member *member = theReplSet->findById(mem.id());
|
||||
if (member && member->config().potentiallyHot()) {
|
||||
theReplSet->addToElectable(mem.id());
|
||||
}
|
||||
else {
|
||||
theReplSet->rmFromElectable(mem.id());
|
||||
}
|
||||
}
|
||||
// add this server to the electable set if it is within 10
|
||||
// seconds of the latest optime we know of
|
||||
else if( info["e"].trueValue() &&
|
||||
mem.opTime >= theReplSet->lastOpTimeWritten.getSecs() - 10) {
|
||||
unsigned lastOp = theReplSet->lastOtherOpTime().getSecs();
|
||||
if (lastOp > 0 && mem.opTime >= lastOp - 10) {
|
||||
theReplSet->addToElectable(mem.id());
|
||||
}
|
||||
}
|
||||
else {
|
||||
theReplSet->rmFromElectable(mem.id());
|
||||
}
|
||||
|
||||
be cfg = info["config"];
|
||||
if( cfg.ok() ) {
|
||||
// received a new config
|
||||
boost::function<void()> f =
|
||||
boost::bind(&Manager::msgReceivedNewConfig, theReplSet->mgr, cfg.Obj().copy());
|
||||
theReplSet->mgr->send(f);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ReplSetImpl::endOldHealthTasks() {
|
||||
|
||||
@ -98,8 +98,14 @@ namespace mongo {
|
||||
const Member *primary = rs->box.getPrimary();
|
||||
|
||||
if (primary && highestPriority &&
|
||||
highestPriority->config().priority > primary->config().priority) {
|
||||
log() << "stepping down " << primary->fullName() << endl;
|
||||
highestPriority->config().priority > primary->config().priority &&
|
||||
// if we're stepping down to allow another member to become primary, we
|
||||
// better have another member (otherOp), and it should be up-to-date
|
||||
otherOp != 0 && highestPriority->hbinfo().opTime.getSecs() >= otherOp - 10) {
|
||||
log() << "stepping down " << primary->fullName() << " (priority " <<
|
||||
primary->config().priority << "), " << highestPriority->fullName() <<
|
||||
" is priority " << highestPriority->config().priority << " and " <<
|
||||
(otherOp - highestPriority->hbinfo().opTime.getSecs()) << " seconds behind" << endl;
|
||||
|
||||
if (primary->h().isSelf()) {
|
||||
// replSetStepDown tries to acquire the same lock
|
||||
@ -119,6 +125,39 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
void Manager::checkAuth() {
|
||||
int down = 0, authIssue = 0, total = 0;
|
||||
|
||||
for( Member *m = rs->head(); m; m=m->next() ) {
|
||||
total++;
|
||||
|
||||
// all authIssue servers will also be not up
|
||||
if (!m->hbinfo().up()) {
|
||||
down++;
|
||||
if (m->hbinfo().authIssue) {
|
||||
authIssue++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if all nodes are down or failed auth AND at least one failed
|
||||
// auth, go into recovering. If all nodes are down, stay a
|
||||
// secondary.
|
||||
if (authIssue > 0 && down == total) {
|
||||
log() << "replset error could not reach/authenticate against any members" << endl;
|
||||
|
||||
if (rs->box.getPrimary() == rs->_self) {
|
||||
log() << "auth problems, relinquishing primary" << rsLog;
|
||||
rs->relinquish();
|
||||
}
|
||||
|
||||
rs->blockSync(true);
|
||||
}
|
||||
else {
|
||||
rs->blockSync(false);
|
||||
}
|
||||
}
|
||||
|
||||
/** called as the health threads get new results */
|
||||
void Manager::msgCheckNewState() {
|
||||
{
|
||||
@ -130,7 +169,8 @@ namespace mongo {
|
||||
if( busyWithElectSelf ) return;
|
||||
|
||||
checkElectableSet();
|
||||
|
||||
checkAuth();
|
||||
|
||||
const Member *p = rs->box.getPrimary();
|
||||
if( p && p != rs->_self ) {
|
||||
if( !p->hbinfo().up() ||
|
||||
|
||||
@ -100,24 +100,25 @@ namespace mongo {
|
||||
lock lk(this);
|
||||
|
||||
Member *max = 0;
|
||||
|
||||
for (set<unsigned>::iterator it = _electableSet.begin(); it != _electableSet.end(); it++) {
|
||||
set<unsigned>::iterator it = _electableSet.begin();
|
||||
while ( it != _electableSet.end() ) {
|
||||
const Member *temp = findById(*it);
|
||||
if (!temp) {
|
||||
log() << "couldn't find member: " << *it << endl;
|
||||
_electableSet.erase(*it);
|
||||
set<unsigned>::iterator it_delete = it;
|
||||
it++;
|
||||
_electableSet.erase(it_delete);
|
||||
continue;
|
||||
}
|
||||
if (!max || max->config().priority < temp->config().priority) {
|
||||
max = (Member*)temp;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
const bool closeOnRelinquish = true;
|
||||
|
||||
void ReplSetImpl::relinquish() {
|
||||
LOG(2) << "replSet attempting to relinquish" << endl;
|
||||
if( box.getState().primary() ) {
|
||||
@ -126,9 +127,7 @@ namespace mongo {
|
||||
|
||||
log() << "replSet relinquishing primary state" << rsLog;
|
||||
changeState(MemberState::RS_SECONDARY);
|
||||
}
|
||||
|
||||
if( closeOnRelinquish ) {
|
||||
|
||||
/* close sockets that were talking to us so they don't blithly send many writes that will fail
|
||||
with "not master" (of course client could check result code, but in case they are not)
|
||||
*/
|
||||
@ -329,6 +328,7 @@ namespace mongo {
|
||||
|
||||
ReplSetImpl::ReplSetImpl(ReplSetCmdline& replSetCmdline) : elect(this),
|
||||
_currentSyncTarget(0),
|
||||
_blockSync(false),
|
||||
_hbmsgTime(0),
|
||||
_self(0),
|
||||
_maintenanceMode(0),
|
||||
|
||||
@ -93,6 +93,7 @@ namespace mongo {
|
||||
|
||||
void noteARemoteIsPrimary(const Member *);
|
||||
void checkElectableSet();
|
||||
void checkAuth();
|
||||
virtual void starting();
|
||||
public:
|
||||
Manager(ReplSetImpl *rs);
|
||||
@ -348,6 +349,9 @@ namespace mongo {
|
||||
const Member* getMemberToSyncTo();
|
||||
Member* _currentSyncTarget;
|
||||
|
||||
bool _blockSync;
|
||||
void blockSync(bool block);
|
||||
|
||||
// set of electable members' _ids
|
||||
set<unsigned> _electableSet;
|
||||
protected:
|
||||
@ -491,7 +495,7 @@ namespace mongo {
|
||||
void _syncThread();
|
||||
bool tryToGoLiveAsASecondary(OpTime&); // readlocks
|
||||
void syncTail();
|
||||
void syncApply(const BSONObj &o);
|
||||
bool syncApply(const BSONObj &o);
|
||||
unsigned _syncRollback(OplogReader& r);
|
||||
void syncRollback(OplogReader& r);
|
||||
void syncFixUp(HowToFixUp& h, OplogReader& r);
|
||||
@ -577,7 +581,7 @@ namespace mongo {
|
||||
* that still need to be checked for auth.
|
||||
*/
|
||||
bool checkAuth(string& errmsg, BSONObjBuilder& result) {
|
||||
if( !noauth && adminOnly() ) {
|
||||
if( !noauth ) {
|
||||
AuthenticationInfo *ai = cc().getAuthenticationInfo();
|
||||
if (!ai->isAuthorizedForLock("admin", locktype())) {
|
||||
errmsg = "replSet command unauthorized";
|
||||
|
||||
@ -296,6 +296,26 @@ namespace mongo {
|
||||
_ok = false;
|
||||
}
|
||||
|
||||
void ReplSetConfig::setMajority() {
|
||||
int total = members.size();
|
||||
int nonArbiters = total;
|
||||
int strictMajority = total/2+1;
|
||||
|
||||
for (vector<MemberCfg>::iterator it = members.begin(); it < members.end(); it++) {
|
||||
if ((*it).arbiterOnly) {
|
||||
nonArbiters--;
|
||||
}
|
||||
}
|
||||
|
||||
// majority should be all "normal" members if we have something like 4
|
||||
// arbiters & 3 normal members
|
||||
_majority = (strictMajority > nonArbiters) ? nonArbiters : strictMajority;
|
||||
}
|
||||
|
||||
int ReplSetConfig::getMajority() const {
|
||||
return _majority;
|
||||
}
|
||||
|
||||
void ReplSetConfig::checkRsConfig() const {
|
||||
uassert(13132,
|
||||
"nonmatching repl set name in _id field; check --replSet command line",
|
||||
@ -533,6 +553,9 @@ namespace mongo {
|
||||
try { getLastErrorDefaults = settings["getLastErrorDefaults"].Obj().copy(); }
|
||||
catch(...) { }
|
||||
}
|
||||
|
||||
// figure out the majority for this config
|
||||
setMajority();
|
||||
}
|
||||
|
||||
static inline void configAssert(bool expr) {
|
||||
|
||||
@ -80,6 +80,22 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
bool operator==(const MemberCfg& r) const {
|
||||
if (!tags.empty() || !r.tags.empty()) {
|
||||
if (tags.size() != r.tags.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if they are the same size and not equal, at least one
|
||||
// element in A must be different in B
|
||||
for (map<string,string>::const_iterator lit = tags.begin(); lit != tags.end(); lit++) {
|
||||
map<string,string>::const_iterator rit = r.tags.find((*lit).first);
|
||||
|
||||
if (rit == r.tags.end() || (*lit).second != (*rit).second) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _id==r._id && votes == r.votes && h == r.h && priority == r.priority &&
|
||||
arbiterOnly == r.arbiterOnly && slaveDelay == r.slaveDelay && hidden == r.hidden &&
|
||||
buildIndexes == buildIndexes;
|
||||
@ -119,9 +135,20 @@ namespace mongo {
|
||||
|
||||
BSONObj asBson() const;
|
||||
|
||||
/**
|
||||
* Getter and setter for _majority. This is almost always
|
||||
* members.size()/2+1, but can be the number of non-arbiter members if
|
||||
* there are more arbiters than non-arbiters (writing to 3 out of 7
|
||||
* servers is safe if 4 of the servers are arbiters).
|
||||
*/
|
||||
void setMajority();
|
||||
int getMajority() const;
|
||||
|
||||
bool _constructed;
|
||||
private:
|
||||
bool _ok;
|
||||
int _majority;
|
||||
|
||||
void from(BSONObj);
|
||||
void clear();
|
||||
|
||||
|
||||
@ -43,18 +43,24 @@ namespace mongo {
|
||||
}
|
||||
|
||||
void ReplSetImpl::syncDoInitialSync() {
|
||||
const static int maxFailedAttempts = 3;
|
||||
createOplog();
|
||||
|
||||
while( 1 ) {
|
||||
int failedAttempts = 0;
|
||||
while ( failedAttempts < maxFailedAttempts ) {
|
||||
try {
|
||||
_syncDoInitialSync();
|
||||
break;
|
||||
}
|
||||
catch(DBException& e) {
|
||||
sethbmsg("initial sync exception " + e.toString(), 0);
|
||||
failedAttempts++;
|
||||
str::stream msg;
|
||||
msg << "initial sync exception: ";
|
||||
msg << e.toString() << " " << (maxFailedAttempts - failedAttempts) << " attempts remaining" ;
|
||||
sethbmsg(msg, 0);
|
||||
sleepsecs(30);
|
||||
}
|
||||
}
|
||||
if ( failedAttempts >= maxFailedAttempts ) ::abort();
|
||||
}
|
||||
|
||||
/* todo : progress metering to sethbmsg. */
|
||||
@ -80,7 +86,8 @@ namespace mongo {
|
||||
}
|
||||
|
||||
const Member* ReplSetImpl::getMemberToSyncTo() {
|
||||
Member *closest = 0;
|
||||
|
||||
bool buildIndexes = true;
|
||||
|
||||
// wait for 2N pings before choosing a sync target
|
||||
if (_cfg) {
|
||||
@ -90,16 +97,35 @@ namespace mongo {
|
||||
OCCASIONALLY log() << "waiting for " << needMorePings << " pings from other members before syncing" << endl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buildIndexes = myConfig().buildIndexes;
|
||||
}
|
||||
|
||||
Member *closest = 0;
|
||||
|
||||
// find the member with the lowest ping time that has more data than me
|
||||
for (Member *m = _members.head(); m; m = m->next()) {
|
||||
if (m->hbinfo().up() &&
|
||||
(m->state() == MemberState::RS_PRIMARY ||
|
||||
(m->state() == MemberState::RS_SECONDARY && m->hbinfo().opTime > lastOpTimeWritten)) &&
|
||||
(!closest || m->hbinfo().ping < closest->hbinfo().ping)) {
|
||||
closest = m;
|
||||
|
||||
// Make two attempts. The first attempt, we ignore those nodes with
|
||||
// slave delay higher than our own. The second attempt includes such
|
||||
// nodes, in case those are the only ones we can reach.
|
||||
for (int attempts = 0; attempts < 2; ++attempts) {
|
||||
for (Member *m = _members.head(); m; m = m->next()) {
|
||||
if (m->hbinfo().up() &&
|
||||
// make sure members with buildIndexes sync from other members w/indexes
|
||||
(!buildIndexes || (buildIndexes && m->config().buildIndexes)) &&
|
||||
(m->state() == MemberState::RS_PRIMARY ||
|
||||
(m->state() == MemberState::RS_SECONDARY &&
|
||||
m->hbinfo().opTime > lastOpTimeWritten)) &&
|
||||
(!closest || m->hbinfo().ping < closest->hbinfo().ping)) {
|
||||
|
||||
if ( attempts == 0 &&
|
||||
myConfig().slaveDelay < m->config().slaveDelay ) {
|
||||
break; // skip this one in the first attempt
|
||||
}
|
||||
closest = m;
|
||||
}
|
||||
}
|
||||
if (closest) break; // no need for second attempt
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@ -69,7 +69,8 @@ namespace mongo {
|
||||
class HeartbeatInfo {
|
||||
unsigned _id;
|
||||
public:
|
||||
HeartbeatInfo() : _id(0xffffffff),hbstate(MemberState::RS_UNKNOWN),health(-1.0),downSince(0),skew(INT_MIN) { }
|
||||
HeartbeatInfo() : _id(0xffffffff), hbstate(MemberState::RS_UNKNOWN), health(-1.0),
|
||||
downSince(0), skew(INT_MIN), authIssue(false) { }
|
||||
HeartbeatInfo(unsigned id);
|
||||
unsigned id() const { return _id; }
|
||||
MemberState hbstate;
|
||||
@ -80,6 +81,7 @@ namespace mongo {
|
||||
DiagStr lastHeartbeatMsg;
|
||||
OpTime opTime;
|
||||
int skew;
|
||||
bool authIssue;
|
||||
unsigned int ping; // milliseconds
|
||||
static unsigned int numPings;
|
||||
|
||||
@ -94,7 +96,7 @@ namespace mongo {
|
||||
bool changed(const HeartbeatInfo& old) const;
|
||||
};
|
||||
|
||||
inline HeartbeatInfo::HeartbeatInfo(unsigned id) : _id(id) {
|
||||
inline HeartbeatInfo::HeartbeatInfo(unsigned id) : _id(id), authIssue(false) {
|
||||
hbstate = MemberState::RS_UNKNOWN;
|
||||
health = -1.0;
|
||||
downSince = 0;
|
||||
|
||||
@ -388,24 +388,18 @@ namespace mongo {
|
||||
for( set<string>::iterator i = h.collectionsToResync.begin(); i != h.collectionsToResync.end(); i++ ) {
|
||||
string ns = *i;
|
||||
sethbmsg(str::stream() << "rollback 4.1 coll resync " << ns);
|
||||
Client::Context c(*i);
|
||||
try {
|
||||
|
||||
Client::Context c(ns);
|
||||
{
|
||||
bob res;
|
||||
string errmsg;
|
||||
dropCollection(ns, errmsg, res);
|
||||
{
|
||||
dbtemprelease r;
|
||||
bool ok = copyCollectionFromRemote(them->getServerAddress(), ns, bo(), errmsg, false, true, false);
|
||||
if( !ok ) {
|
||||
log() << "replSet rollback error resyncing collection " << ns << ' ' << errmsg << rsLog;
|
||||
throw "rollback error resyncing rollection [1]";
|
||||
}
|
||||
bool ok = copyCollectionFromRemote(them->getServerAddress(), ns, errmsg);
|
||||
uassert(15909, str::stream() << "replSet rollback error resyncing collection " << ns << ' ' << errmsg, ok);
|
||||
}
|
||||
}
|
||||
catch(...) {
|
||||
log() << "replset rollback error resyncing collection " << ns << rsLog;
|
||||
throw "rollback error resyncing rollection [2]";
|
||||
}
|
||||
}
|
||||
|
||||
/* we did more reading from primary, so check it again for a rollback (which would mess us up), and
|
||||
@ -423,7 +417,7 @@ namespace mongo {
|
||||
setMinValid(newMinValid);
|
||||
}
|
||||
}
|
||||
catch(...) {
|
||||
catch (DBException& e) {
|
||||
err = "can't get/set minvalid";
|
||||
}
|
||||
if( h.rbid != getRBID(r.conn()) ) {
|
||||
|
||||
@ -32,17 +32,19 @@ namespace mongo {
|
||||
}
|
||||
}
|
||||
|
||||
/* apply the log op that is in param o */
|
||||
void ReplSetImpl::syncApply(const BSONObj &o) {
|
||||
/* apply the log op that is in param o
|
||||
@return bool failedUpdate
|
||||
*/
|
||||
bool ReplSetImpl::syncApply(const BSONObj &o) {
|
||||
const char *ns = o.getStringField("ns");
|
||||
if ( *ns == '.' || *ns == 0 ) {
|
||||
blank(o);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
Client::Context ctx(ns);
|
||||
ctx.getClient()->curop()->reset();
|
||||
applyOperation_inlock(o);
|
||||
return applyOperation_inlock(o);
|
||||
}
|
||||
|
||||
/* initial oplog application, during initial sync, after cloning.
|
||||
@ -57,6 +59,7 @@ namespace mongo {
|
||||
|
||||
const string hn = source->h().toString();
|
||||
OplogReader r;
|
||||
|
||||
try {
|
||||
if( !r.connect(hn) ) {
|
||||
log() << "replSet initial sync error can't connect to " << hn << " to read " << rsoplog << rsLog;
|
||||
@ -113,12 +116,9 @@ namespace mongo {
|
||||
if( !r.more() )
|
||||
break;
|
||||
BSONObj o = r.nextSafe(); /* note we might get "not master" at some point */
|
||||
{
|
||||
ts = o["ts"]._opTime();
|
||||
ts = o["ts"]._opTime();
|
||||
|
||||
/* if we have become primary, we dont' want to apply things from elsewhere
|
||||
anymore. assumePrimary is in the db lock so we are safe as long as
|
||||
we check after we locked above. */
|
||||
{
|
||||
if( (source->state() != MemberState::RS_PRIMARY &&
|
||||
source->state() != MemberState::RS_SECONDARY) ||
|
||||
replSetForceInitialSyncFailure ) {
|
||||
@ -133,9 +133,12 @@ namespace mongo {
|
||||
throw DBException("primary changed",0);
|
||||
}
|
||||
|
||||
if( ts >= applyGTE ) {
|
||||
// optimes before we started copying need not be applied.
|
||||
syncApply(o);
|
||||
if( ts >= applyGTE ) { // optimes before we started copying need not be applied.
|
||||
bool failedUpdate = syncApply(o);
|
||||
if( failedUpdate && shouldRetry(o, hn)) {
|
||||
failedUpdate = syncApply(o);
|
||||
uassert(15915, "replSet update still fails after adding missing object", !failedUpdate);
|
||||
}
|
||||
}
|
||||
_logOpObjRS(o); /* with repl sets we write the ops to our oplog too */
|
||||
}
|
||||
@ -149,15 +152,19 @@ namespace mongo {
|
||||
start = now;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( ts > minValid ) {
|
||||
break;
|
||||
}
|
||||
|
||||
getDur().commitIfNeeded();
|
||||
}
|
||||
catch (DBException& e) {
|
||||
// skip duplicate key exceptions
|
||||
if( e.getCode() == 11000 || e.getCode() == 11001 ) {
|
||||
if( e.getCode() == 11000 || e.getCode() == 11001 || e.getCode() == 12582) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// handle cursor not found (just requery)
|
||||
if( e.getCode() == 13127 ) {
|
||||
r.resetCursor();
|
||||
@ -290,7 +297,7 @@ namespace mongo {
|
||||
target = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// no server found
|
||||
if (target == 0) {
|
||||
// if there is no one to sync from
|
||||
@ -298,7 +305,7 @@ namespace mongo {
|
||||
tryToGoLiveAsASecondary(minvalid);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
r.tailingQueryGTE(rsoplog, lastOpTimeWritten);
|
||||
// if target cut connections between connecting and querying (for
|
||||
// example, because it stepped down) we might not have a cursor
|
||||
@ -408,7 +415,7 @@ namespace mongo {
|
||||
if( !target->hbinfo().hbstate.readable() ) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if( myConfig().slaveDelay != sd ) // reconf
|
||||
break;
|
||||
}
|
||||
@ -429,7 +436,7 @@ namespace mongo {
|
||||
}
|
||||
|
||||
syncApply(o);
|
||||
_logOpObjRS(o); // with repl sets we write the ops to our oplog too
|
||||
_logOpObjRS(o); // with repl sets we write the ops to our oplog too
|
||||
}
|
||||
catch (DBException& e) {
|
||||
sethbmsg(str::stream() << "syncTail: " << e.toString() << ", syncing: " << o);
|
||||
@ -444,7 +451,7 @@ namespace mongo {
|
||||
// TODO : reuse our connection to the primary.
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if( !target->hbinfo().hbstate.readable() ) {
|
||||
return;
|
||||
}
|
||||
@ -458,7 +465,7 @@ namespace mongo {
|
||||
sleepsecs(1);
|
||||
return;
|
||||
}
|
||||
if( sp.state.fatal() || sp.state.startup() ) {
|
||||
if( _blockSync || sp.state.fatal() || sp.state.startup() ) {
|
||||
sleepsecs(5);
|
||||
return;
|
||||
}
|
||||
@ -530,6 +537,15 @@ namespace mongo {
|
||||
replLocalAuth();
|
||||
}
|
||||
|
||||
void ReplSetImpl::blockSync(bool block) {
|
||||
_blockSync = block;
|
||||
if (_blockSync) {
|
||||
// syncing is how we get into SECONDARY state, so we'll be stuck in
|
||||
// RECOVERING until we unblock
|
||||
changeState(MemberState::RS_RECOVERING);
|
||||
}
|
||||
}
|
||||
|
||||
void GhostSync::associateSlave(const BSONObj& id, const int memberId) {
|
||||
const OID rid = id["_id"].OID();
|
||||
rwlock lk( _lock , true );
|
||||
@ -556,10 +572,10 @@ namespace mongo {
|
||||
OCCASIONALLY warning() << "couldn't update slave " << rid << " no entry" << rsLog;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
GhostSlave& slave = i->second;
|
||||
if (!slave.init) {
|
||||
OCCASIONALLY log() << "couldn't update slave " << rid << " not init" << rsLog;
|
||||
OCCASIONALLY log() << "couldn't update slave " << rid << " not init" << rsLog;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -175,7 +175,7 @@ namespace mongo {
|
||||
if (wStr == "majority") {
|
||||
// use the entire set, including arbiters, to prevent writing
|
||||
// to a majority of the set but not a majority of voters
|
||||
return replicatedToNum(op, theReplSet->config().members.size()/2+1);
|
||||
return replicatedToNum(op, theReplSet->config().getMajority());
|
||||
}
|
||||
|
||||
map<string,ReplSetConfig::TagRule*>::const_iterator it = theReplSet->config().rules.find(wStr);
|
||||
|
||||
@ -83,6 +83,9 @@ namespace mongo {
|
||||
string systemUsers = dbname + ".system.users";
|
||||
// OCCASIONALLY Helpers::ensureIndex(systemUsers.c_str(), userPattern, false, "user_1");
|
||||
{
|
||||
mongolock lk(false);
|
||||
Client::Context c(systemUsers, dbpath, &lk, false);
|
||||
|
||||
BSONObjBuilder b;
|
||||
b << "user" << user;
|
||||
BSONObj query = b.done();
|
||||
@ -101,10 +104,10 @@ namespace mongo {
|
||||
AuthenticationInfo *ai = cc().getAuthenticationInfo();
|
||||
|
||||
if ( readOnly ) {
|
||||
ai->authorizeReadOnly( cc().database()->name.c_str() , user );
|
||||
ai->authorizeReadOnly( dbname.c_str() , user );
|
||||
}
|
||||
else {
|
||||
ai->authorize( cc().database()->name.c_str() , user );
|
||||
ai->authorize( dbname.c_str() , user );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -57,14 +57,16 @@ namespace mongo {
|
||||
virtual bool slaveOk() const {
|
||||
return true;
|
||||
}
|
||||
virtual LockType locktype() const { return READ; }
|
||||
virtual LockType locktype() const { return NONE; }
|
||||
virtual void help(stringstream& ss) const { ss << "internal"; }
|
||||
CmdAuthenticate() : Command("authenticate") {}
|
||||
bool run(const string& dbname , BSONObj& cmdObj, int options, string& errmsg, BSONObjBuilder& result, bool fromRepl);
|
||||
void authenticate(const string& dbname, const string& user, const bool readOnly);
|
||||
private:
|
||||
bool getUserObj(const string& dbname, const string& user, BSONObj& userObj, string& pwd);
|
||||
void authenticate(const string& dbname, const string& user, const bool readOnly);
|
||||
};
|
||||
|
||||
extern CmdAuthenticate cmdAuthenticate;
|
||||
|
||||
class CmdLogout : public Command {
|
||||
public:
|
||||
|
||||
@ -22,26 +22,16 @@
|
||||
# error malloc defined 0
|
||||
#endif
|
||||
|
||||
#ifdef assert
|
||||
# error assert defined 1
|
||||
#endif
|
||||
|
||||
#include "../client/parallel.h" //uses assert
|
||||
|
||||
#ifdef assert
|
||||
# error assert defined 2
|
||||
#endif
|
||||
|
||||
#include "../client/redef_macros.h"
|
||||
|
||||
#ifndef assert
|
||||
# error assert not defined 3
|
||||
#ifndef malloc
|
||||
# error malloc not defined
|
||||
#endif
|
||||
|
||||
#include "../client/undef_macros.h"
|
||||
|
||||
#ifdef assert
|
||||
# error assert defined 3
|
||||
#ifdef malloc
|
||||
# error malloc defined 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@ namespace mongo {
|
||||
runQuery( m, q, response );
|
||||
}
|
||||
void __forceLinkGeoPlugin();
|
||||
shared_ptr<Cursor> newQueryOptimizerCursor( const char *ns, const BSONObj &query, const BSONObj &order = BSONObj() );
|
||||
boost::shared_ptr<Cursor> newQueryOptimizerCursor( const char *ns, const BSONObj &query, const BSONObj &order = BSONObj() );
|
||||
} // namespace mongo
|
||||
|
||||
namespace QueryOptimizerTests {
|
||||
@ -51,8 +51,6 @@ namespace QueryOptimizerTests {
|
||||
|
||||
namespace QueryPlanTests {
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
class Base {
|
||||
public:
|
||||
Base() : _ctx( ns() ) , indexNum_( 0 ) {
|
||||
@ -935,9 +933,11 @@ namespace QueryOptimizerTests {
|
||||
protected:
|
||||
static const char *ns() { return "unittests.QueryOptimizerTests"; }
|
||||
static NamespaceDetails *nsd() { return nsdetails( ns() ); }
|
||||
DBDirectClient &client() { return _client; }
|
||||
private:
|
||||
dblock lk_;
|
||||
Client::Context _ctx;
|
||||
DBDirectClient _client;
|
||||
};
|
||||
|
||||
class BestGuess : public Base {
|
||||
@ -969,10 +969,81 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
};
|
||||
|
||||
namespace MultiCursorTests {
|
||||
|
||||
/**
|
||||
* Helper class for validating a set of attempted candidate plans The boiler plate
|
||||
* implementation is similar to MultiCursor::NoOp.
|
||||
*/
|
||||
class PlanValidatingCursorOp : public MultiCursor::CursorOp {
|
||||
public:
|
||||
PlanValidatingCursorOp( const BSONObj &expectedIndexKey ) :
|
||||
_expectedIndexKey( expectedIndexKey ) {
|
||||
}
|
||||
virtual void _init() {
|
||||
ASSERT_EQUALS( _expectedIndexKey, qp().indexKey() );
|
||||
}
|
||||
virtual void next() { setComplete(); }
|
||||
virtual bool mayRecordPlan() const { return false; }
|
||||
virtual QueryOp *_createChild() const {
|
||||
return new PlanValidatingCursorOp( _expectedIndexKey );
|
||||
}
|
||||
virtual boost::shared_ptr<Cursor> newCursor() const { return qp().newCursor(); }
|
||||
virtual long long nscanned() { return 0; }
|
||||
private:
|
||||
BSONObj _expectedIndexKey;
|
||||
};
|
||||
|
||||
/** Test MultiCursor with hintIdElseNatural set to true. */
|
||||
class HintIdElseNaturalBase : public Base {
|
||||
public:
|
||||
virtual ~HintIdElseNaturalBase() {}
|
||||
void run() {
|
||||
client().insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
|
||||
client().insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
|
||||
client().ensureIndex( ns(), BSON( "a" << 1 ) );
|
||||
|
||||
// Create a MultiCursor and validate its plans in the planValidator.
|
||||
boost::shared_ptr<MultiCursor::CursorOp> planValidator
|
||||
( new PlanValidatingCursorOp( expectedPlanIndexKey() ) );
|
||||
MultiCursor cursor( ns(), BSON( "a" << 1 << "b" << 1 ), BSONObj(), planValidator,
|
||||
false, /* hintIdElseNatural */ true );
|
||||
|
||||
// Validate the index key returned by the cursor iterates.
|
||||
do {
|
||||
ASSERT_EQUALS( expectedCursorIndexKey(), cursor.indexKeyPattern() );
|
||||
} while( cursor.advance() );
|
||||
}
|
||||
protected:
|
||||
virtual BSONObj expectedPlanIndexKey() const = 0;
|
||||
virtual BSONObj expectedCursorIndexKey() const { return expectedPlanIndexKey(); }
|
||||
};
|
||||
|
||||
/** An _id hint is used when possible if hintIdElseNatural is specified. */
|
||||
class IdHint : public HintIdElseNaturalBase {
|
||||
virtual BSONObj expectedPlanIndexKey() const { return BSON( "_id" << 1 ); }
|
||||
};
|
||||
|
||||
/**
|
||||
* An $natural hint is used when hintIdElseNatural is specified and no _id index is
|
||||
* available.
|
||||
*/
|
||||
class NaturalHintFallback : public HintIdElseNaturalBase {
|
||||
public:
|
||||
NaturalHintFallback() {
|
||||
client().dropCollection( ns() );
|
||||
// Recreate the collection as capped, without an _id index.
|
||||
client().createCollection( ns(), 5000, true );
|
||||
}
|
||||
private:
|
||||
virtual BSONObj expectedPlanIndexKey() const { return BSON( "$natural" << 1 ); }
|
||||
virtual BSONObj expectedCursorIndexKey() const { return BSONObj(); }
|
||||
};
|
||||
|
||||
} // namespace MultiCursorTests
|
||||
|
||||
namespace QueryOptimizerCursorTests {
|
||||
|
||||
using boost::shared_ptr;
|
||||
|
||||
class Base {
|
||||
public:
|
||||
Base() {
|
||||
@ -1019,10 +1090,10 @@ namespace QueryOptimizerTests {
|
||||
advance();
|
||||
}
|
||||
}
|
||||
shared_ptr<Cursor> c() { return _c; }
|
||||
boost::shared_ptr<Cursor> c() { return _c; }
|
||||
long long nscanned() const { return _c->nscanned(); }
|
||||
private:
|
||||
shared_ptr<Cursor> _c;
|
||||
boost::shared_ptr<Cursor> _c;
|
||||
};
|
||||
|
||||
/** No results for empty collection. */
|
||||
@ -1031,7 +1102,7 @@ namespace QueryOptimizerTests {
|
||||
void run() {
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSONObj() );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSONObj() );
|
||||
ASSERT( !c->ok() );
|
||||
ASSERT_EXCEPTION( c->_current(), AssertionException );
|
||||
ASSERT_EXCEPTION( c->current(), AssertionException );
|
||||
@ -1371,7 +1442,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
|
||||
boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "_id" << GT << 5 << "a" << GT << 5 ) );
|
||||
ASSERT( c->ok() );
|
||||
|
||||
// _id 10 {_id:1}
|
||||
@ -1464,7 +1535,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), fromjson( "{_id:/a/}" ) );
|
||||
boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), fromjson( "{_id:/a/}" ) );
|
||||
ASSERT( c->ok() );
|
||||
// "a"
|
||||
ASSERT( c->matcher()->matchesCurrent( c.get() ) );
|
||||
@ -1496,7 +1567,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 300 ) << BSON( "a" << 1 ) ) ) );
|
||||
boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 300 ) << BSON( "a" << 1 ) ) ) );
|
||||
for( int i = 0; i < 151; ++i ) {
|
||||
ASSERT( c->ok() );
|
||||
ASSERT( c->matcher()->matchesCurrent( c.get() ) );
|
||||
@ -1516,7 +1587,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "a" << GT << 1 << LT << 5 ) );
|
||||
boost::shared_ptr< Cursor > c = newQueryOptimizerCursor( ns(), BSON( "a" << GT << 1 << LT << 5 ) );
|
||||
// Two sided bounds work.
|
||||
ASSERT( !c->ok() );
|
||||
}
|
||||
@ -1551,7 +1622,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 0 << "b" << 0 ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 0 << "b" << 0 ) );
|
||||
|
||||
ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 0 << "b" << 0 ), c->current() );
|
||||
ASSERT( c->advance() );
|
||||
@ -1583,7 +1654,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "_id" << 1 ) ) ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "_id" << 1 ) ) ) );
|
||||
ASSERT( c->ok() );
|
||||
ASSERT( !c->advance() );
|
||||
}
|
||||
@ -1600,7 +1671,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 140 ) << BSON( "_id" << 145 ) << BSON( "a" << 145 ) ) ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LT << 140 ) << BSON( "_id" << 145 ) << BSON( "a" << 145 ) ) ) );
|
||||
|
||||
while( c->current().getIntField( "_id" ) < 140 ) {
|
||||
ASSERT( c->advance() );
|
||||
@ -1644,7 +1715,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LTE << 147 ) << BSON( "_id" << 148 ) << BSON( "_id" << 149 ) ) ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << LTE << 147 ) << BSON( "_id" << 148 ) << BSON( "_id" << 149 ) ) ) );
|
||||
for( int i = 0; i < 150; ++i ) {
|
||||
ASSERT( c->ok() );
|
||||
ASSERT_EQUALS( i, c->current().getIntField( "_id" ) );
|
||||
@ -1664,7 +1735,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "a" << LT << 6 << "b" << 4 ) << BSON( "a" << GTE << 6 << "b" << 4 ) ) ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "a" << LT << 6 << "b" << 4 ) << BSON( "a" << GTE << 6 << "b" << 4 ) ) ) );
|
||||
|
||||
ASSERT( c->ok() );
|
||||
|
||||
@ -1921,6 +1992,33 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
};
|
||||
|
||||
/** Yield and remove document with $or query. */
|
||||
class YieldRemoveOr : public Base {
|
||||
public:
|
||||
void run() {
|
||||
_cli.insert( ns(), BSON( "_id" << 1 ) );
|
||||
_cli.insert( ns(), BSON( "_id" << 2 ) );
|
||||
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 ) << BSON( "_id" << 2 ) ) ) );
|
||||
ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
|
||||
ASSERT( prepareToYield() );
|
||||
}
|
||||
|
||||
_cli.remove( ns(), BSON( "_id" << 1 ) );
|
||||
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
recoverFromYield();
|
||||
ASSERT( ok() );
|
||||
ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Yield and overwrite current in capped collection. */
|
||||
class YieldCappedOverwrite : public Base {
|
||||
public:
|
||||
@ -2074,6 +2172,68 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
};
|
||||
|
||||
/** Yielding with delete, multiple plans active, and $or clause. */
|
||||
class YieldMultiplePlansDeleteOr : public Base {
|
||||
public:
|
||||
void run() {
|
||||
_cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
|
||||
_cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
|
||||
_cli.ensureIndex( ns(), BSON( "a" << 1 ) );
|
||||
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 << "a" << 2 ) << BSON( "_id" << 2 << "a" << 1 ) ) ) );
|
||||
ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
|
||||
ASSERT( prepareToYield() );
|
||||
}
|
||||
|
||||
_cli.remove( ns(), BSON( "_id" << 1 ) );
|
||||
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
c()->recoverFromYield();
|
||||
ASSERT( ok() );
|
||||
ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
|
||||
ASSERT( !advance() );
|
||||
ASSERT( !ok() );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Yielding with delete, multiple plans active with advancement to the second, and $or clause. */
|
||||
class YieldMultiplePlansDeleteOrAdvance : public Base {
|
||||
public:
|
||||
void run() {
|
||||
_cli.insert( ns(), BSON( "_id" << 1 << "a" << 2 ) );
|
||||
_cli.insert( ns(), BSON( "_id" << 2 << "a" << 1 ) );
|
||||
_cli.ensureIndex( ns(), BSON( "a" << 1 ) );
|
||||
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
setQueryOptimizerCursor( BSON( "$or" << BSON_ARRAY( BSON( "_id" << 1 << "a" << 2 ) << BSON( "_id" << 2 << "a" << 1 ) ) ) );
|
||||
ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
|
||||
ASSERT( prepareToYield() );
|
||||
c()->advance();
|
||||
ASSERT_EQUALS( 1, current().getIntField( "_id" ) );
|
||||
}
|
||||
|
||||
_cli.remove( ns(), BSON( "_id" << 1 ) );
|
||||
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
c()->recoverFromYield();
|
||||
ASSERT( ok() );
|
||||
ASSERT_EQUALS( 2, current().getIntField( "_id" ) );
|
||||
ASSERT( !advance() );
|
||||
ASSERT( !ok() );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Yielding with multiple plans and capped overwrite. */
|
||||
class YieldMultiplePlansCappedOverwrite : public Base {
|
||||
public:
|
||||
@ -2118,7 +2278,7 @@ namespace QueryOptimizerTests {
|
||||
_cli.insert( ns(), BSON( "a" << 1 << "b" << 1 ) );
|
||||
_cli.ensureIndex( ns(), BSON( "a" << 1 ) );
|
||||
|
||||
shared_ptr<Cursor> c;
|
||||
boost::shared_ptr<Cursor> c;
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
@ -2159,7 +2319,7 @@ namespace QueryOptimizerTests {
|
||||
_cli.insert( ns(), BSON( "_id" << 1 << "a" << 1 ) );
|
||||
_cli.ensureIndex( ns(), BSON( "_id" << 1 ) );
|
||||
|
||||
shared_ptr<Cursor> c;
|
||||
boost::shared_ptr<Cursor> c;
|
||||
{
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
@ -2429,7 +2589,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 2 ), BSON( "b" << 1 ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "a" << 2 ), BSON( "b" << 1 ) );
|
||||
// Check that we are scanning {b:1} not {a:1}.
|
||||
for( int i = 0; i < 3; ++i ) {
|
||||
ASSERT( c->ok() );
|
||||
@ -2466,7 +2626,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
mongolock lk( false );
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "b" << GT << 0 ) ) ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "$or" << BSON_ARRAY( BSON( "_id" << GT << 0 ) << BSON( "b" << GT << 0 ) ) ) );
|
||||
ASSERT( c->ok() );
|
||||
cc().curop()->kill();
|
||||
// First advance() call throws, subsequent calls just fail.
|
||||
@ -2484,7 +2644,7 @@ namespace QueryOptimizerTests {
|
||||
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "_id" << GTE << 0 << "a" << GTE << 0 ) );
|
||||
boost::shared_ptr<Cursor> c = newQueryOptimizerCursor( ns(), BSON( "_id" << GTE << 0 << "a" << GTE << 0 ) );
|
||||
ASSERT( c->ok() );
|
||||
ASSERT_EQUALS( 2, c->nscanned() );
|
||||
c->advance();
|
||||
@ -2511,7 +2671,7 @@ namespace QueryOptimizerTests {
|
||||
void run() {
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), query(), order() );
|
||||
boost::shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), query(), order() );
|
||||
string type = c->toString().substr( 0, expectedType().length() );
|
||||
ASSERT_EQUALS( expectedType(), type );
|
||||
check( c );
|
||||
@ -2520,7 +2680,7 @@ namespace QueryOptimizerTests {
|
||||
virtual string expectedType() const = 0;
|
||||
virtual BSONObj query() const { return BSONObj(); }
|
||||
virtual BSONObj order() const { return BSONObj(); }
|
||||
virtual void check( const shared_ptr<Cursor> &c ) {
|
||||
virtual void check( const boost::shared_ptr<Cursor> &c ) {
|
||||
ASSERT( c->ok() );
|
||||
ASSERT( !c->matcher() );
|
||||
ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
|
||||
@ -2551,7 +2711,7 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
string expectedType() const { return "BtreeCursor a_1"; }
|
||||
BSONObj query() const { return BSON( "a" << GTE << 5 ); }
|
||||
void check( const shared_ptr<Cursor> &c ) {
|
||||
void check( const boost::shared_ptr<Cursor> &c ) {
|
||||
ASSERT( c->ok() );
|
||||
ASSERT( c->matcher() );
|
||||
ASSERT_EQUALS( 5, c->current().getIntField( "a" ) );
|
||||
@ -2571,7 +2731,7 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
string expectedType() const { return "GeoSearchCursor"; }
|
||||
BSONObj query() const { return fromjson( "{ loc : { $near : [50,50] } }" ); }
|
||||
void check( const shared_ptr<Cursor> &c ) {
|
||||
void check( const boost::shared_ptr<Cursor> &c ) {
|
||||
ASSERT( c->ok() );
|
||||
ASSERT( c->matcher() );
|
||||
ASSERT( c->matcher()->matchesCurrent( c.get() ) );
|
||||
@ -2586,7 +2746,7 @@ namespace QueryOptimizerTests {
|
||||
_cli.insert( ns(), BSON( "_id" << 5 ) );
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), BSONObj(), BSON( "b" << 1 ) );
|
||||
boost::shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), BSONObj(), BSON( "b" << 1 ) );
|
||||
ASSERT( !c );
|
||||
}
|
||||
};
|
||||
@ -2601,7 +2761,7 @@ namespace QueryOptimizerTests {
|
||||
ASSERT( _cli.query( ns(), QUERY( "_id" << GT << 0 << "b" << GT << 0 ).sort( "b" ) )->more() );
|
||||
dblock lk;
|
||||
Client::Context ctx( ns() );
|
||||
shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), BSON( "_id" << GT << 0 << "b" << GT << 0 ), BSON( "b" << 1 ) );
|
||||
boost::shared_ptr<Cursor> c = NamespaceDetailsTransient::getCursor( ns(), BSON( "_id" << GT << 0 << "b" << GT << 0 ), BSON( "b" << 1 ) );
|
||||
// {_id:1} requires scan and order, so {b:1} must be chosen.
|
||||
ASSERT( c );
|
||||
ASSERT_EQUALS( 5, c->current().getIntField( "_id" ) );
|
||||
@ -2615,7 +2775,7 @@ namespace QueryOptimizerTests {
|
||||
}
|
||||
string expectedType() const { return "QueryOptimizerCursor"; }
|
||||
BSONObj query() const { return BSON( "_id" << GT << 0 << "a" << GT << 0 ); }
|
||||
void check( const shared_ptr<Cursor> &c ) {}
|
||||
void check( const boost::shared_ptr<Cursor> &c ) {}
|
||||
};
|
||||
|
||||
} // namespace GetCursor
|
||||
@ -2666,6 +2826,8 @@ namespace QueryOptimizerTests {
|
||||
add<QueryPlanSetTests::EqualityThenIn>();
|
||||
add<QueryPlanSetTests::NotEqualityThenIn>();
|
||||
add<BestGuess>();
|
||||
add<MultiCursorTests::IdHint>();
|
||||
add<MultiCursorTests::NaturalHintFallback>();
|
||||
add<QueryOptimizerCursorTests::Empty>();
|
||||
add<QueryOptimizerCursorTests::Unindexed>();
|
||||
add<QueryOptimizerCursorTests::Basic>();
|
||||
@ -2703,11 +2865,14 @@ namespace QueryOptimizerTests {
|
||||
add<QueryOptimizerCursorTests::YieldUpdate>();
|
||||
add<QueryOptimizerCursorTests::YieldDrop>();
|
||||
add<QueryOptimizerCursorTests::YieldDropOr>();
|
||||
add<QueryOptimizerCursorTests::YieldRemoveOr>();
|
||||
add<QueryOptimizerCursorTests::YieldCappedOverwrite>();
|
||||
add<QueryOptimizerCursorTests::YieldDropIndex>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansNoOp>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansAdvanceNoOp>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansDelete>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansDeleteOr>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansDeleteOrAdvance>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansCappedOverwrite>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansCappedOverwriteManual>();
|
||||
add<QueryOptimizerCursorTests::YieldMultiplePlansCappedOverwriteManual2>();
|
||||
|
||||
@ -238,7 +238,14 @@ namespace QueryUtilTests {
|
||||
}
|
||||
};
|
||||
|
||||
class QueryPatternTest {
|
||||
class QueryPatternBase {
|
||||
protected:
|
||||
static QueryPattern p( const BSONObj &query, const BSONObj &sort = BSONObj() ) {
|
||||
return FieldRangeSet( "", query, true ).pattern( sort );
|
||||
}
|
||||
};
|
||||
|
||||
class QueryPatternTest : public QueryPatternBase {
|
||||
public:
|
||||
void run() {
|
||||
ASSERT( p( BSON( "a" << 1 ) ) == p( BSON( "a" << 1 ) ) );
|
||||
@ -258,12 +265,17 @@ namespace QueryUtilTests {
|
||||
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 << "c" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 ) ) );
|
||||
ASSERT( p( BSON( "a" << 1 ), BSON( "b" << 1 ) ) != p( BSON( "a" << 4 ), BSON( "b" << 1 << "c" << 1 ) ) );
|
||||
}
|
||||
private:
|
||||
static QueryPattern p( const BSONObj &query, const BSONObj &sort = BSONObj() ) {
|
||||
return FieldRangeSet( "", query, true ).pattern( sort );
|
||||
};
|
||||
|
||||
class QueryPatternNeConstraint : public QueryPatternBase {
|
||||
public:
|
||||
void run() {
|
||||
ASSERT( p( BSON( "a" << NE << 5 ) ) != p( BSON( "a" << GT << 1 ) ) );
|
||||
ASSERT( p( BSON( "a" << NE << 5 ) ) != p( BSONObj() ) );
|
||||
ASSERT( p( BSON( "a" << NE << 5 ) ) == p( BSON( "a" << NE << "a" ) ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class NoWhere {
|
||||
public:
|
||||
void run() {
|
||||
@ -902,6 +914,7 @@ namespace QueryUtilTests {
|
||||
add< FieldRangeTests::Equality >();
|
||||
add< FieldRangeTests::SimplifiedQuery >();
|
||||
add< FieldRangeTests::QueryPatternTest >();
|
||||
add< FieldRangeTests::QueryPatternNeConstraint >();
|
||||
add< FieldRangeTests::NoWhere >();
|
||||
add< FieldRangeTests::Numeric >();
|
||||
add< FieldRangeTests::InLowerBound >();
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#include "../db/oplog.h"
|
||||
#include "../db/queryoptimizer.h"
|
||||
|
||||
#include "../db/repl/rs.h"
|
||||
|
||||
namespace mongo {
|
||||
void createOplog();
|
||||
}
|
||||
@ -107,12 +109,6 @@ namespace ReplTests {
|
||||
return count;
|
||||
}
|
||||
static void applyAllOperations() {
|
||||
class Applier : public ReplSource {
|
||||
public:
|
||||
static void apply( const BSONObj &op ) {
|
||||
ReplSource::applyOperation( op );
|
||||
}
|
||||
};
|
||||
dblock lk;
|
||||
vector< BSONObj > ops;
|
||||
{
|
||||
@ -122,8 +118,13 @@ namespace ReplTests {
|
||||
}
|
||||
{
|
||||
Client::Context ctx( ns() );
|
||||
for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i )
|
||||
Applier::apply( *i );
|
||||
BSONObjBuilder b;
|
||||
b.append("host", "localhost");
|
||||
b.appendTimestamp("syncedTo", 0);
|
||||
ReplSource a(b.obj());
|
||||
for( vector< BSONObj >::iterator i = ops.begin(); i != ops.end(); ++i ) {
|
||||
a.applyOperation( *i );
|
||||
}
|
||||
}
|
||||
}
|
||||
static void printAll( const char *ns ) {
|
||||
@ -1014,7 +1015,7 @@ namespace ReplTests {
|
||||
ASSERT( !one( BSON( "_id" << 2 ) ).isEmpty() );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class DatabaseIgnorerBasic {
|
||||
public:
|
||||
void run() {
|
||||
@ -1047,10 +1048,10 @@ namespace ReplTests {
|
||||
d.doIgnoreUntilAfter( "a", OpTime( 5, 0 ) );
|
||||
ASSERT( d.ignoreAt( "a", OpTime( 5, 5 ) ) );
|
||||
ASSERT( d.ignoreAt( "a", OpTime( 6, 0 ) ) );
|
||||
ASSERT( !d.ignoreAt( "a", OpTime( 6, 1 ) ) );
|
||||
ASSERT( !d.ignoreAt( "a", OpTime( 6, 1 ) ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check against oldest document in the oplog before scanning backward
|
||||
* from the newest document.
|
||||
@ -1075,7 +1076,7 @@ namespace ReplTests {
|
||||
ASSERT_EQUALS( 0, fsc.cursor()->current()[ "o" ].Obj()[ "_id" ].Int() );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Check unsuccessful yield recovery with FindingStartCursor */
|
||||
class FindingStartCursorYield : public Base {
|
||||
public:
|
||||
@ -1101,7 +1102,26 @@ namespace ReplTests {
|
||||
ASSERT_EXCEPTION( fsc.recoverFromYield(), MsgAssertionException );
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** Check ReplSetConfig::MemberCfg equality */
|
||||
class ReplSetMemberCfgEquality : public Base {
|
||||
public:
|
||||
void run() {
|
||||
ReplSetConfig::MemberCfg m1, m2;
|
||||
assert(m1 == m2);
|
||||
m1.tags["x"] = "foo";
|
||||
assert(m1 != m2);
|
||||
m2.tags["y"] = "bar";
|
||||
assert(m1 != m2);
|
||||
m1.tags["y"] = "bar";
|
||||
assert(m1 != m2);
|
||||
m2.tags["x"] = "foo";
|
||||
assert(m1 == m2);
|
||||
m1.tags.clear();
|
||||
assert(m1 != m2);
|
||||
}
|
||||
};
|
||||
|
||||
class All : public Suite {
|
||||
public:
|
||||
All() : Suite( "repl" ) {
|
||||
@ -1158,6 +1178,7 @@ namespace ReplTests {
|
||||
add< DatabaseIgnorerUpdate >();
|
||||
add< FindingStartCursorStale >();
|
||||
add< FindingStartCursorYield >();
|
||||
add< ReplSetMemberCfgEquality >();
|
||||
}
|
||||
} myall;
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include "dbtests.h"
|
||||
#include "../util/concurrency/spin_lock.h"
|
||||
#include "../util/timer.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -73,8 +74,10 @@ namespace {
|
||||
int counter = 0;
|
||||
|
||||
const int threads = 64;
|
||||
const int incs = 10000;
|
||||
const int incs = 50000;
|
||||
LockTester* testers[threads];
|
||||
|
||||
Timer timer;
|
||||
|
||||
for ( int i = 0; i < threads; i++ ) {
|
||||
testers[i] = new LockTester( &spin, &counter );
|
||||
@ -87,7 +90,10 @@ namespace {
|
||||
ASSERT_EQUALS( testers[i]->requests(), incs );
|
||||
delete testers[i];
|
||||
}
|
||||
|
||||
|
||||
int ms = timer.millis();
|
||||
log() << "spinlock ConcurrentIncs time: " << ms << endl;
|
||||
|
||||
ASSERT_EQUALS( counter, threads*incs );
|
||||
#if defined(__linux__)
|
||||
ASSERT( SpinLock::isfast() );
|
||||
|
||||
@ -544,6 +544,15 @@ namespace ThreadedTests {
|
||||
};
|
||||
#endif
|
||||
|
||||
void sleepalittle() {
|
||||
Timer t;
|
||||
while( 1 ) {
|
||||
boost::this_thread::yield();
|
||||
if( t.micros() > 8 )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class WriteLocksAreGreedy : public ThreadedTest<3> {
|
||||
public:
|
||||
WriteLocksAreGreedy() : m("gtest") {}
|
||||
@ -579,6 +588,81 @@ namespace ThreadedTests {
|
||||
}
|
||||
};
|
||||
|
||||
// Tests waiting on the TicketHolder by running many more threads than can fit into the "hotel", but only
|
||||
// max _nRooms threads should ever get in at once
|
||||
class TicketHolderWaits : public ThreadedTest<10> {
|
||||
|
||||
static const int checkIns = 1000;
|
||||
static const int rooms = 3;
|
||||
|
||||
public:
|
||||
TicketHolderWaits() : _hotel( rooms ), _tickets( _hotel._nRooms ) {}
|
||||
|
||||
private:
|
||||
|
||||
class Hotel {
|
||||
public:
|
||||
Hotel( int nRooms ) : _frontDesk( "frontDesk" ), _nRooms( nRooms ), _checkedIn( 0 ), _maxRooms( 0 ) {}
|
||||
|
||||
void checkIn(){
|
||||
scoped_lock lk( _frontDesk );
|
||||
_checkedIn++;
|
||||
assert( _checkedIn <= _nRooms );
|
||||
if( _checkedIn > _maxRooms ) _maxRooms = _checkedIn;
|
||||
}
|
||||
|
||||
void checkOut(){
|
||||
scoped_lock lk( _frontDesk );
|
||||
_checkedIn--;
|
||||
assert( _checkedIn >= 0 );
|
||||
}
|
||||
|
||||
mongo::mutex _frontDesk;
|
||||
int _nRooms;
|
||||
int _checkedIn;
|
||||
int _maxRooms;
|
||||
};
|
||||
|
||||
Hotel _hotel;
|
||||
TicketHolder _tickets;
|
||||
|
||||
virtual void subthread(int x) {
|
||||
|
||||
string threadName = ( str::stream() << "ticketHolder" << x );
|
||||
Client::initThread( threadName.c_str() );
|
||||
|
||||
for( int i = 0; i < checkIns; i++ ){
|
||||
|
||||
_tickets.waitForTicket();
|
||||
TicketHolderReleaser whenDone( &_tickets );
|
||||
|
||||
_hotel.checkIn();
|
||||
|
||||
sleepalittle();
|
||||
if( i == checkIns - 1 ) sleepsecs( 2 );
|
||||
|
||||
_hotel.checkOut();
|
||||
|
||||
if( ( i % ( checkIns / 10 ) ) == 0 )
|
||||
log() << "checked in " << i << " times..." << endl;
|
||||
|
||||
}
|
||||
|
||||
cc().shutdown();
|
||||
|
||||
}
|
||||
|
||||
virtual void validate() {
|
||||
|
||||
// This should always be true, assuming that it takes < 1 sec for the hardware to process a check-out/check-in
|
||||
// Time for test is then ~ #threads / _nRooms * 2 seconds
|
||||
assert( _hotel._maxRooms == _hotel._nRooms );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class All : public Suite {
|
||||
public:
|
||||
All() : Suite( "threading" ) { }
|
||||
@ -600,6 +684,7 @@ namespace ThreadedTests {
|
||||
add< RWLockTest4 >();
|
||||
|
||||
add< MongoMutexTest >();
|
||||
add< TicketHolderWaits >();
|
||||
}
|
||||
} myall;
|
||||
}
|
||||
|
||||
@ -519,6 +519,48 @@ namespace UpdateTests {
|
||||
}
|
||||
};
|
||||
|
||||
/** SERVER-4777 */
|
||||
class TwoModsWithinDuplicatedField : public SetBase {
|
||||
public:
|
||||
void run() {
|
||||
client().insert( ns(), BSON( "_id" << 0 << "a" << 1
|
||||
<< "x" << BSONObj() << "x" << BSONObj()
|
||||
<< "z" << 5 ) );
|
||||
client().update( ns(), BSONObj(), BSON( "$set" << BSON( "x.b" << 1 << "x.c" << 1 ) ) );
|
||||
ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1
|
||||
<< "x" << BSON( "b" << 1 << "c" << 1 ) << "x" << BSONObj()
|
||||
<< "z" << 5 ),
|
||||
client().findOne( ns(), BSONObj() ) );
|
||||
}
|
||||
};
|
||||
|
||||
/** SERVER-4777 */
|
||||
class ThreeModsWithinDuplicatedField : public SetBase {
|
||||
public:
|
||||
void run() {
|
||||
client().insert( ns(),
|
||||
BSON( "_id" << 0
|
||||
<< "x" << BSONObj() << "x" << BSONObj() << "x" << BSONObj() ) );
|
||||
client().update( ns(), BSONObj(),
|
||||
BSON( "$set" << BSON( "x.b" << 1 << "x.c" << 1 << "x.d" << 1 ) ) );
|
||||
ASSERT_EQUALS( BSON( "_id" << 0
|
||||
<< "x" << BSON( "b" << 1 << "c" << 1 << "d" << 1 )
|
||||
<< "x" << BSONObj() << "x" << BSONObj() ),
|
||||
client().findOne( ns(), BSONObj() ) );
|
||||
}
|
||||
};
|
||||
|
||||
class TwoModsBeforeExistingField : public SetBase {
|
||||
public:
|
||||
void run() {
|
||||
client().insert( ns(), BSON( "_id" << 0 << "x" << 5 ) );
|
||||
client().update( ns(), BSONObj(),
|
||||
BSON( "$set" << BSON( "a" << 1 << "b" << 1 << "x" << 10 ) ) );
|
||||
ASSERT_EQUALS( BSON( "_id" << 0 << "a" << 1 << "b" << 1 << "x" << 10 ),
|
||||
client().findOne( ns(), BSONObj() ) );
|
||||
}
|
||||
};
|
||||
|
||||
namespace ModSetTests {
|
||||
|
||||
class internal1 {
|
||||
@ -854,6 +896,9 @@ namespace UpdateTests {
|
||||
add< PreserveIdWithIndex >();
|
||||
add< CheckNoMods >();
|
||||
add< UpdateMissingToNull >();
|
||||
add< TwoModsWithinDuplicatedField >();
|
||||
add< ThreeModsWithinDuplicatedField >();
|
||||
add< TwoModsBeforeExistingField >();
|
||||
|
||||
add< ModSetTests::internal1 >();
|
||||
add< ModSetTests::inc1 >();
|
||||
|
||||
55
debian/changelog
vendored
55
debian/changelog
vendored
@ -1,3 +1,58 @@
|
||||
mongodb (2.0.7) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see https://jira.mongodb.org/browse/SERVER/fixforversion/11201
|
||||
|
||||
-- Eric Milkie <milkie@10gen.com> Fri, 13 Jul 2012 10:52:00 -0500
|
||||
|
||||
mongodb (2.0.6) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see https://jira.mongodb.org/browse/SERVER/fixforversion/11165
|
||||
|
||||
-- Eric Milkie <milkie@10gen.com> Tue, 22 May 2012 10:52:00 -0500
|
||||
|
||||
mongodb (2.0.5) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see https://jira.mongodb.org/browse/SERVER/fixforversion/11137
|
||||
|
||||
-- Andy Schwerin <schwerin@10gen.com> Wed, 25 Apr 2012 10:52:00 -0500
|
||||
|
||||
mongodb (2.0.4) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see http://jira.mongodb.org/browse/SERVER/fixforversion/11107
|
||||
|
||||
-- Richard Kreuter <richard@10gen.com> Mon, 19 Mar 2012 16:56:28 -0500
|
||||
|
||||
mongodb (2.0.3) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see http://jira.mongodb.org/browse/SERVER/fixforversion/11001
|
||||
|
||||
-- Richard Kreuter <richard@10gen.com> Mon, 27 Feb 2012 16:56:28 -0500
|
||||
|
||||
mongodb (2.0.2) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see http://jira.mongodb.org/browse/SERVER/fixforversion/10991
|
||||
|
||||
-- Richard Kreuter <richard@10gen.com> Wed, 14 Dec 2011 16:56:28 -0500
|
||||
|
||||
mongodb (2.0.1) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
* see http://jira.mongodb.org/browse/SERVER/fixforversion/10890
|
||||
|
||||
-- Richard Kreuter <richard@10gen.com> Fri, 21 Oct 2011 16:56:28 -0500
|
||||
|
||||
mongodb (2.0.0) unstable; urgency=low
|
||||
|
||||
* see http://www.mongodb.org/display/DOCS/2.0+Release+Notes
|
||||
|
||||
-- Richard Kreuter <richard@10gen.com> Mon, 12 Sep 2011 16:56:28 -0500
|
||||
|
||||
mongodb (1.9.2) unstable; urgency=low
|
||||
|
||||
* see http://jira.mongodb.org/browse/SERVER/fixforversion/10261
|
||||
|
||||
@ -188,4 +188,46 @@ freely, subject to the following restrictions:
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
5) License notice for Snappy - http://code.google.com/p/snappy/
|
||||
---------------------------------
|
||||
Copyright 2005 and onwards Google Inc.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
A light-weight compression algorithm. It is designed for speed of
|
||||
compression and decompression, rather than for the utmost in space
|
||||
savings.
|
||||
|
||||
For getting better compression ratios when you are compressing data
|
||||
with long repeated sequences or compressing data that is similar to
|
||||
other data, while still compressing fast, you might look at first
|
||||
using BMDiff and then compressing the output of BMDiff with
|
||||
Snappy.
|
||||
|
||||
|
||||
|
||||
End
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = MongoDB
|
||||
PROJECT_NUMBER = 2.0.0-rc3-pre-
|
||||
PROJECT_NUMBER = 2.0.10-pre-
|
||||
OUTPUT_DIRECTORY = docs/doxygen
|
||||
CREATE_SUBDIRS = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
@ -16,6 +16,14 @@ db.addUser( "eliot" , "eliot" );
|
||||
db.addUser( "guest" , "guest", true );
|
||||
db.getSisterDB( "admin" ).addUser( "super", "super" );
|
||||
|
||||
print("make sure we can't run certain commands w/out auth");
|
||||
var errmsg = "need to login";
|
||||
res = db.adminCommand({getLog : "global"});
|
||||
printjson( res );
|
||||
assert( ! res.log || res.log.length == 0 , "getLog should fail: " + tojson( res ) )
|
||||
assert.eq( res.errmsg , "need to login" , tojson( res ) );
|
||||
|
||||
|
||||
assert.throws( function() { t.findOne() }, [], "read without login" );
|
||||
|
||||
assert( db.auth( "eliot" , "eliot" ) , "auth failed" );
|
||||
|
||||
@ -105,3 +105,5 @@ tzz = db.capped6;
|
||||
for( var i = 0; i < 10; ++i ) {
|
||||
doTest();
|
||||
}
|
||||
|
||||
tzz.drop();
|
||||
|
||||
@ -104,3 +104,5 @@ print("pass " + pass++);
|
||||
t.drop();
|
||||
db._dbCommand( { create:"jstests_capped8", capped: true, $nExtents: [ 10000 ] } );
|
||||
testTruncate();
|
||||
|
||||
t.drop();
|
||||
|
||||
34
jstests/distinct3.js
Normal file
34
jstests/distinct3.js
Normal file
@ -0,0 +1,34 @@
|
||||
// Yield and delete test case for query optimizer cursor.
|
||||
|
||||
t = db.jstests_distinct3;
|
||||
t.drop();
|
||||
|
||||
t.ensureIndex({a:1});
|
||||
t.ensureIndex({b:1});
|
||||
|
||||
for( i = 0; i < 50; ++i ) {
|
||||
for( j = 0; j < 20; ++j ) {
|
||||
t.save({a:i,c:i,d:j});
|
||||
}
|
||||
}
|
||||
for( i = 0; i < 1000; ++i ) {
|
||||
t.save({b:i,c:i+50});
|
||||
}
|
||||
db.getLastError();
|
||||
|
||||
// Attempt to remove the last match for the {a:1} index scan while distinct is yielding.
|
||||
p = startParallelShell( 'for( i = 0; i < 2500; ++i ) { ' +
|
||||
' db.jstests_distinct3.remove( { a:49 } ); ' +
|
||||
' for( j = 0; j < 20; ++j ) { ' +
|
||||
' db.jstests_distinct3.save( { a:49, c:49, d:j } ); ' +
|
||||
' } ' +
|
||||
'} ' +
|
||||
'// Wait for the above writes to complete. ' +
|
||||
'db.getLastError(); ' );
|
||||
|
||||
for( i = 0; i < 100; ++i ) {
|
||||
count = t.distinct( 'c', {$or:[{a:{$gte:0},d:0},{b:{$gte:0}}]} ).length;
|
||||
assert.gt( count, 1000 );
|
||||
}
|
||||
|
||||
p();
|
||||
@ -1,17 +1,40 @@
|
||||
// Check the return value of a db.eval function running a database query, and ensure the function's
|
||||
// contents are logged in the profile log.
|
||||
|
||||
t = db.evalb;
|
||||
t.drop();
|
||||
// Use a reserved database name to avoid a conflict in the parallel test suite.
|
||||
var stddb = db;
|
||||
var db = db.getSisterDB( 'evalb' );
|
||||
|
||||
t.save( { x : 3 } );
|
||||
function profileCursor() {
|
||||
return db.system.profile.find( { user:username } );
|
||||
}
|
||||
|
||||
assert.eq( 3, db.eval( function(){ return db.evalb.findOne().x; } ) , "A" );
|
||||
function lastOp() {
|
||||
return profileCursor().sort( { $natural:-1 } ).next();
|
||||
}
|
||||
|
||||
db.setProfilingLevel( 2 );
|
||||
try {
|
||||
|
||||
assert.eq( 3, db.eval( function(){ return db.evalb.findOne().x; } ) , "B" );
|
||||
username = 'jstests_evalb_user';
|
||||
db.addUser( username, 'password', false, 1 );
|
||||
db.auth( username, 'password' );
|
||||
|
||||
o = db.system.profile.find().sort( { $natural : -1 } ).limit(1).next();
|
||||
assert( tojson(o).indexOf( "findOne().x" ) > 0 , "C : " + tojson( o ) )
|
||||
t = db.evalb;
|
||||
t.drop();
|
||||
|
||||
db.setProfilingLevel( 0 );
|
||||
t.save( { x:3 } );
|
||||
|
||||
assert.eq( 3, db.eval( function() { return db.evalb.findOne().x; } ), 'A' );
|
||||
|
||||
db.setProfilingLevel( 2 );
|
||||
|
||||
assert.eq( 3, db.eval( function() { return db.evalb.findOne().x; } ), 'B' );
|
||||
|
||||
o = lastOp();
|
||||
assert( tojson( o ).indexOf( 'findOne().x' ) > 0, 'C : ' + tojson( o ) );
|
||||
}
|
||||
finally {
|
||||
|
||||
db.setProfilingLevel(0);
|
||||
db = stddb;
|
||||
}
|
||||
|
||||
@ -7,5 +7,5 @@ for ( var i=0; i<50; i++ ) { // enough iterations to break 32 bit.
|
||||
assert( t.count() == 1 );
|
||||
t.drop();
|
||||
}
|
||||
t.drop();
|
||||
|
||||
db.dropDatabase();
|
||||
|
||||
31
jstests/extent2.js
Normal file
31
jstests/extent2.js
Normal file
@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
mydb = db.getSisterDB( "test_extent2" );
|
||||
mydb.dropDatabase();
|
||||
|
||||
t = mydb.foo;
|
||||
e = mydb["$freelist"]
|
||||
|
||||
function insert(){
|
||||
t.insert( { _id : 1 , x : 1 } )
|
||||
t.insert( { _id : 2 , x : 1 } )
|
||||
t.insert( { _id : 3 , x : 1 } )
|
||||
t.ensureIndex( { x : 1 } );
|
||||
}
|
||||
|
||||
insert();
|
||||
t.drop();
|
||||
|
||||
start = e.stats();
|
||||
|
||||
for ( i=0; i<100; i++ ) {
|
||||
insert();
|
||||
t.drop();
|
||||
}
|
||||
|
||||
end = e.stats();
|
||||
|
||||
printjson( start );
|
||||
printjson( end )
|
||||
assert.eq( 4 , start.numExtents );
|
||||
assert.eq( 4 , end.numExtents );
|
||||
11
jstests/filemd5.js
Normal file
11
jstests/filemd5.js
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
db.fs.chunks.drop();
|
||||
db.fs.chunks.insert({files_id:1,n:0,data:new BinData(0,"test")})
|
||||
|
||||
x = db.runCommand({"filemd5":1,"root":"fs"});
|
||||
assert( ! x.ok , tojson(x) )
|
||||
|
||||
db.fs.chunks.ensureIndex({files_id:1,n:1})
|
||||
x = db.runCommand({"filemd5":1,"root":"fs"});
|
||||
assert( x.ok , tojson(x) )
|
||||
|
||||
41
jstests/getlog2.js
Normal file
41
jstests/getlog2.js
Normal file
@ -0,0 +1,41 @@
|
||||
// tests getlog as well as slow querying logging
|
||||
|
||||
glcol = db.getLogTest2;
|
||||
glcol.drop()
|
||||
|
||||
contains = function(arr, func) {
|
||||
var i = arr.length;
|
||||
while (i--) {
|
||||
if (func(arr[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// test doesn't work when talking to mongos
|
||||
if(db.isMaster().msg != "isdbgrid") {
|
||||
// run a slow query
|
||||
glcol.save({ "SENTINEL": 1 });
|
||||
glcol.findOne({ "SENTINEL": 1, "$where": function() { sleep(1000); return true; } });
|
||||
|
||||
// run a slow update
|
||||
glcol.update({ "SENTINEL": 1, "$where": function() { sleep(1000); return true; } }, { "x": "x" });
|
||||
|
||||
var resp = db.adminCommand({getLog:"global"});
|
||||
assert( resp.ok == 1, "error executing getLog command" );
|
||||
assert( resp.log, "no log field" );
|
||||
assert( resp.log.length > 0 , "no log lines" );
|
||||
|
||||
// ensure that slow query is logged in detail
|
||||
assert( contains(resp.log, function(v) {
|
||||
print(v);
|
||||
return v.indexOf(" query ") != -1 && v.indexOf("query:") != -1 && v.indexOf("SENTINEL") != -1;
|
||||
}) );
|
||||
|
||||
// same, but for update
|
||||
assert( contains(resp.log, function(v) {
|
||||
print(v);
|
||||
return v.indexOf(" update ") != -1 && v.indexOf("query:") != -1 && v.indexOf("SENTINEL") != -1;
|
||||
}) );
|
||||
}
|
||||
@ -5,8 +5,7 @@
|
||||
// when it doesn't move
|
||||
|
||||
|
||||
t = db.indexb;t = db.indexb;
|
||||
db.dropDatabase();
|
||||
t = db.indexb;
|
||||
t.drop();
|
||||
t.ensureIndex({a:1},true);
|
||||
|
||||
|
||||
@ -6,9 +6,11 @@ b = new ObjectId("4c17f616a707428966a2801c");
|
||||
assert.eq(a.getTimestamp(), b.getTimestamp() , "A" );
|
||||
|
||||
x = Math.floor( (new Date()).getTime() / 1000 );
|
||||
sleep(10/*ms*/)
|
||||
a = new ObjectId();
|
||||
sleep(10/*ms*/)
|
||||
z = Math.floor( (new Date()).getTime() / 1000 );
|
||||
y = a.getTimestamp().getTime() / 1000;
|
||||
|
||||
assert( x <= y , "B" );
|
||||
assert( y <= z , "C" );
|
||||
assert.lte( x , y , "B" );
|
||||
assert.lte( y , z , "C" );
|
||||
|
||||
@ -55,12 +55,18 @@ try {
|
||||
|
||||
db.eval("sleep(1)") // pre-load system.js
|
||||
|
||||
db.setProfilingLevel(2);
|
||||
before = db.system.profile.count();
|
||||
function resetProfile( level , slowms ) {
|
||||
db.setProfilingLevel(0);
|
||||
db.system.profile.drop();
|
||||
db.setProfilingLevel(level,slowms);
|
||||
}
|
||||
|
||||
resetProfile(2);
|
||||
|
||||
db.eval( "sleep(25)" )
|
||||
db.eval( "sleep(120)" )
|
||||
after = db.system.profile.count()
|
||||
assert.eq( before + 3 , after , "X1" )
|
||||
|
||||
assert.eq( 2 , db.system.profile.find( { "command.$eval" : /^sleep/ } ).count() );
|
||||
|
||||
/* sleep() could be inaccurate on certain platforms. let's check */
|
||||
print("\nsleep 2 time actual:");
|
||||
@ -90,24 +96,20 @@ try {
|
||||
return actual >= max ? 1 : 0;
|
||||
}
|
||||
|
||||
db.setProfilingLevel(1,100);
|
||||
before = db.system.profile.count();
|
||||
resetProfile(1,100);
|
||||
var delta = 0;
|
||||
delta += evalSleepMoreThan( 15 , 100 );
|
||||
delta += evalSleepMoreThan( 120 , 100 );
|
||||
after = db.system.profile.count()
|
||||
assert.eq( before + delta , after , "X2 : " + getProfileAString() )
|
||||
assert.eq( delta , db.system.profile.find( { "command.$eval" : /^sleep/ } ).count() , "X2 : " + getProfileAString() )
|
||||
|
||||
db.setProfilingLevel(1,20);
|
||||
before = db.system.profile.count();
|
||||
resetProfile(1,20);
|
||||
delta = 0;
|
||||
delta += evalSleepMoreThan( 5 , 20 );
|
||||
delta += evalSleepMoreThan( 120 , 20 );
|
||||
after = db.system.profile.count()
|
||||
assert.eq( before + delta , after , "X3 : " + getProfileAString() )
|
||||
assert.eq( delta , db.system.profile.find( { "command.$eval" : /^sleep/ } ).count() , "X3 : " + getProfileAString() )
|
||||
|
||||
db.profile.drop();
|
||||
db.setProfilingLevel(2)
|
||||
resetProfile(2);
|
||||
db.profile1.drop();
|
||||
var q = { _id : 5 };
|
||||
var u = { $inc : { x : 1 } };
|
||||
db.profile1.update( q , u );
|
||||
|
||||
@ -17,3 +17,17 @@ t.save( { a: [ 2 ] } );
|
||||
t.update( {}, { $pull: { a: 2 } } );
|
||||
t.update( {}, { $pull: { a: 6 } } );
|
||||
assert.eq( [], t.findOne().a );
|
||||
|
||||
// SERVER-6047: $pull creates empty nested docs for dotted fields
|
||||
// that don't exist.
|
||||
t.drop()
|
||||
t.save({ m : 1 } );
|
||||
t.update( { m : 1 }, { $pull : { 'a.b' : [ 1 ] } } );
|
||||
assert( ('a' in t.findOne()) == false );
|
||||
// Non-obvious bit: the implementation of non-in-place update
|
||||
// might do different things depending on whether the "new" field
|
||||
// comes before or after existing fields in the document.
|
||||
// So for now it's worth testing that too. Sorry, future; blame the past.
|
||||
t.update( { m : 1 }, { $pull : { 'x.y' : [ 1 ] } } );
|
||||
assert( ('z' in t.findOne()) == false );
|
||||
// End SERVER-6047
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user