summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/cleanbb.py21
-rwxr-xr-xbuildscripts/packager-enterprise.py997
-rw-r--r--doxygenConfig2
-rw-r--r--jstests/aggregation/mongos_slaveok.js38
-rw-r--r--jstests/auth/js_scope_leak.js139
-rw-r--r--jstests/dbhash2.js22
-rw-r--r--jstests/dropdb.js15
-rw-r--r--jstests/eval2.js4
-rw-r--r--jstests/geo_circle2a.js37
-rw-r--r--jstests/replsets/gle_explicit_optime.js57
-rw-r--r--jstests/sharding/dbhash_cache.js38
-rw-r--r--jstests/sharding/forget_mr_temp_ns.js46
-rw-r--r--jstests/sharding/mongos_rs_auth_shard_failure_tolerance.js341
-rw-r--r--jstests/sharding/mongos_rs_shard_failure_tolerance.js460
-rw-r--r--jstests/sharding/mongos_shard_failure_tolerance.js186
-rw-r--r--jstests/slowNightly/sharding_passthrough.js1
-rw-r--r--jstests/slowNightly/ttl1.js38
-rwxr-xr-xrpm/mongo.spec2
-rw-r--r--src/mongo/SConscript1
-rw-r--r--src/mongo/base/error_codes.err3
-rw-r--r--src/mongo/client/dbclient_rs.cpp250
-rw-r--r--src/mongo/client/dbclient_rs.h65
-rw-r--r--src/mongo/client/parallel.cpp51
-rw-r--r--src/mongo/db/auth/auth_external_state_s.cpp4
-rw-r--r--src/mongo/db/auth/authorization_manager.cpp12
-rw-r--r--src/mongo/db/auth/authorization_manager.h4
-rw-r--r--src/mongo/db/cmdline.cpp11
-rw-r--r--src/mongo/db/commands/dbhash.cpp216
-rw-r--r--src/mongo/db/commands/dbhash.h67
-rw-r--r--src/mongo/db/commands/group.cpp6
-rw-r--r--src/mongo/db/commands/mr.cpp36
-rw-r--r--src/mongo/db/db.cpp1
-rw-r--r--src/mongo/db/dbcommands.cpp129
-rw-r--r--src/mongo/db/dbeval.cpp4
-rw-r--r--src/mongo/db/fts/fts_spec.cpp21
-rw-r--r--src/mongo/db/fts/fts_spec_test.cpp30
-rw-r--r--src/mongo/db/geo/geoparser.cpp11
-rw-r--r--src/mongo/db/matcher.cpp7
-rw-r--r--src/mongo/db/namespace_details.cpp4
-rw-r--r--src/mongo/db/oplog.cpp8
-rw-r--r--src/mongo/db/pdfile.cpp2
-rw-r--r--src/mongo/db/pipeline/document.h14
-rw-r--r--src/mongo/db/pipeline/value.cpp42
-rw-r--r--src/mongo/db/pipeline/value_internal.h5
-rw-r--r--src/mongo/db/repl/consensus.cpp4
-rw-r--r--src/mongo/db/repl/heartbeat.cpp8
-rw-r--r--src/mongo/db/repl/rs_rollback.cpp10
-rw-r--r--src/mongo/db/ttl.cpp6
-rw-r--r--src/mongo/dbtests/documenttests.cpp16
-rw-r--r--src/mongo/s/client_info.cpp68
-rw-r--r--src/mongo/s/commands_public.cpp4
-rw-r--r--src/mongo/s/shard.cpp25
-rw-r--r--src/mongo/s/shard.h9
-rw-r--r--src/mongo/s/shardconnection.cpp39
-rw-r--r--src/mongo/s/strategy_shard.cpp17
-rw-r--r--src/mongo/s/type_shard.cpp2
-rw-r--r--src/mongo/s/type_shard_test.cpp11
-rw-r--r--src/mongo/s/version_manager.cpp47
-rw-r--r--src/mongo/s/writeback_listener.cpp3
-rw-r--r--src/mongo/scripting/engine.cpp135
-rw-r--r--src/mongo/scripting/engine.h17
-rw-r--r--src/mongo/shell/assert.js38
-rw-r--r--src/mongo/shell/dbshell.cpp4
-rw-r--r--src/mongo/tools/restore.cpp43
-rw-r--r--src/mongo/util/net/listen.cpp16
-rw-r--r--src/mongo/util/net/ssl_manager.cpp19
-rw-r--r--src/mongo/util/progress_meter.cpp8
-rw-r--r--src/mongo/util/progress_meter.h12
-rw-r--r--src/mongo/util/version.cpp2
-rw-r--r--src/third_party/v8/src/spaces.h3
70 files changed, 3529 insertions, 485 deletions
diff --git a/buildscripts/cleanbb.py b/buildscripts/cleanbb.py
index dd52020d7e4..589370e33ae 100644
--- a/buildscripts/cleanbb.py
+++ b/buildscripts/cleanbb.py
@@ -3,6 +3,7 @@ import sys
import os, os.path
import utils
import time
+import exceptions
from optparse import OptionParser
# set cwd to the root mongo dir, one level up from this
@@ -14,7 +15,6 @@ if os.path.basename(cwd) == 'buildscripts':
print( "cwd [" + cwd + "]" )
def shouldKill( c ):
-
if "smoke.py" in c:
return False
@@ -36,7 +36,6 @@ def shouldKill( c ):
return False
def killprocs( signal="" ):
-
killed = 0
if sys.platform == 'win32':
@@ -55,7 +54,7 @@ def killprocs( signal="" ):
x = x.lstrip()
if not shouldKill( x ):
continue
-
+
pid = x.split( " " )[0]
print( "killing: " + x )
utils.execsys( "/bin/kill " + signal + " " + pid )
@@ -65,7 +64,6 @@ def killprocs( signal="" ):
def cleanup( root , nokill ):
-
if nokill:
print "nokill requested, not killing anybody"
else:
@@ -76,11 +74,18 @@ def cleanup( root , nokill ):
# delete all regular files, directories can stay
# NOTE: if we delete directories later, we can't delete diskfulltest
for ( dirpath , dirnames , filenames ) in os.walk( root , topdown=False ):
- for x in filenames:
+ for x in filenames:
foo = dirpath + "/" + x
print( "removing: " + foo )
- os.remove( foo )
-
+ try:
+ os.remove(foo)
+ except exceptions.OSError, e:
+ # SERVER-10462 compensate for Windows file locking race
+ # We want to catch WindowsError but can't use that name on other platforms
+ print(repr(e))
+ print("os.remove(%s) failed, retrying once." % foo)
+ time.sleep(1)
+ os.remove(foo)
if __name__ == "__main__":
parser = OptionParser(usage="read the script")
@@ -90,5 +95,5 @@ if __name__ == "__main__":
root = "/data/db/"
if len(args) > 0:
root = args[0]
-
+
cleanup( root , options.nokill )
diff --git a/buildscripts/packager-enterprise.py b/buildscripts/packager-enterprise.py
new file mode 100755
index 00000000000..4ccb262a0d8
--- /dev/null
+++ b/buildscripts/packager-enterprise.py
@@ -0,0 +1,997 @@
+#!/usr/bin/python
+
+# This program makes Debian and RPM repositories for MongoDB, by
+# downloading our tarballs of statically linked executables and
+# insinuating them into Linux packages. It must be run on a
+# Debianoid, since Debian provides tools to make RPMs, but RPM-based
+# systems don't provide debian packaging crud.
+
+# Notes:
+#
+# * Almost anything that you want to be able to influence about how a
+# package construction must be embedded in some file that the
+# packaging tool uses for input (e.g., debian/rules, debian/control,
+# debian/changelog; or the RPM specfile), and the precise details are
+# arbitrary and silly. So this program generates all the relevant
+# inputs to the packaging tools.
+#
+# * Once a .deb or .rpm package is made, there's a separate layer of
+# tools that makes a "repository" for use by the apt/yum layers of
+# package tools. The layouts of these repositories are arbitrary and
+# silly, too.
+#
+# * Before you run the program on a new host, these are the
+# prerequisites:
+#
+# apt-get install dpkg-dev rpm debhelper fakeroot ia32-libs createrepo git-core libsnmp15
+# echo "Now put the dist gnupg signing keys in ~root/.gnupg"
+
+import errno
+import getopt
+from packager import httpget
+import os
+import re
+import stat
+import subprocess
+import sys
+import tempfile
+import time
+import urlparse
+
+# For the moment, this program runs on the host that also serves our
+# repositories to the world, so the last thing the program does is
+# move the repositories into place. Make this be the path where the
+# web server will look for repositories.
+REPOPATH="/var/www/repo"
+
+# The 10gen names for the architectures we support.
+ARCHES=["x86_64"]
+
+# Made up names for the flavors of distribution we package for.
+DISTROS=["debian-sysvinit", "ubuntu-upstart", "redhat"]
+
+# When we're preparing a directory containing packaging tool inputs
+# and our binaries, use this relative subdirectory for placing the
+# binaries.
+BINARYDIR="BINARIES"
+
+sys.stderr.write("BINARYDIR: %s, REPOPATH: %s\n" % (BINARYDIR, REPOPATH))
+
+class Spec(object):
+ def __init__(self, specstr):
+ tup = specstr.split(":")
+ self.ver = tup[0]
+ # Hack: the second item in the tuple is treated as a suffix if
+ # it lacks an equals sign; otherwise it's the start of named
+ # parameters.
+ self.suf = None
+ if len(tup) > 1 and tup[1].find("=") == -1:
+ self.suf = tup[1]
+ # Catch-all for any other parameters to the packaging.
+ i = 2 if self.suf else 1
+ self.params = dict([s.split("=") for s in tup[i:]])
+ for key in self.params.keys():
+ assert(key in ["suffix", "revision"])
+
+ def version(self):
+ return self.ver
+
+ def version_better_than(self, version_string):
+ # FIXME: this is wrong, but I'm in a hurry.
+ # e.g., "1.8.2" < "1.8.10", "1.8.2" < "1.8.2-rc1"
+ return self.ver > version_string
+
+ def suffix(self):
+ # suffix is what we tack on after pkgbase.
+ if self.suf:
+ return self.suf
+ elif "suffix" in self.params:
+ return self.params["suffix"]
+ else:
+ return "-10gen-enterprise" if int(self.ver.split(".")[1])%2==0 else "-10gen-unstable"
+
+
+ def pversion(self, distro):
+ # Note: Debian packages have funny rules about dashes in
+ # version numbers, and RPM simply forbids dashes. pversion
+ # will be the package's version number (but we need to know
+ # our upstream version too).
+ if re.search("^(debian|ubuntu)", distro.name()):
+ return re.sub("-", "~", self.ver)
+ elif re.search("(redhat|fedora|centos)", distro.name()):
+ return re.sub("\\d+-", "", self.ver)
+ else:
+ raise Exception("BUG: unsupported platform?")
+
+ def param(self, param):
+ if param in self.params:
+ return self.params[param]
+ return None
+
+class Distro(object):
+ def __init__(self, string):
+ self.n=string
+
+ def name(self):
+ return self.n
+
+ def pkgbase(self):
+ # pkgbase is the first part of the package's name on
+ # this distro.
+ return "mongo" if re.search("(redhat|fedora|centos)", self.n) else "mongodb"
+
+ def archname(self, arch):
+ if re.search("^(debian|ubuntu)", self.n):
+ return "i386" if arch.endswith("86") else "amd64"
+ elif re.search("^(centos|redhat|fedora)", self.n):
+ return "i686" if arch.endswith("86") else "x86_64"
+ else:
+ raise Exception("BUG: unsupported platform?")
+
+ def repodir(self, arch):
+ """Return the directory where we'll place the package files for
+ (distro, distro_version) in that distro's preferred repository
+ layout (as distinct from where that distro's packaging building
+ tools place the package files)."""
+ if re.search("^(debian|ubuntu)", self.n):
+ return "repo/%s/dists/dist/10gen/binary-%s/" % (self.n, self.archname(arch))
+ elif re.search("(redhat|fedora|centos)", self.n):
+ return "repo/%s/os/%s/RPMS/" % (self.n, self.archname(arch))
+ else:
+ raise Exception("BUG: unsupported platform?")
+
+ def make_pkg(self, arch, spec, srcdir):
+ if re.search("^(debian|ubuntu)", self.n):
+ return make_deb(self, arch, spec, srcdir)
+ elif re.search("^(centos|redhat|fedora)", self.n):
+ return make_rpm(self, arch, spec, srcdir)
+ else:
+ raise Exception("BUG: unsupported platform?")
+
+ def build_os(self):
+ """Return the build os label in the binary package to download ("rhel62"
+ for redhat, "ubuntu1204" for Ubuntu and Debian)"""
+
+ if re.search("^(debian|ubuntu)", self.n):
+ return "ubuntu1204"
+ elif re.search("(redhat|fedora|centos)", self.n):
+ return "rhel62"
+ else:
+ raise Exception("BUG: unsupported platform?")
+def main(argv):
+ (flags, specs) = parse_args(argv[1:])
+ distros=[Distro(distro) for distro in DISTROS]
+
+ oldcwd=os.getcwd()
+ srcdir=oldcwd+"/../"
+
+ # We do all our work in a randomly-created directory. You can set
+ # TEMPDIR to influence where this program will do stuff.
+ prefix=tempfile.mkdtemp()
+ print "Working in directory %s" % prefix
+
+ # This will be a list of directories where we put packages in
+ # "repository layout".
+ repos=[]
+
+ os.chdir(prefix)
+ try:
+ # Download the binaries.
+ urlfmt="http://downloads.10gen.com/linux/mongodb-linux-%s-subscription-%s-%s.tgz"
+
+ # Build a pacakge for each distro/spec/arch tuple, and
+ # accumulate the repository-layout directories.
+ for (distro, spec, arch) in crossproduct(distros, specs, ARCHES):
+
+ httpget(urlfmt % (arch, distro.build_os(), spec.version()), ensure_dir(tarfile(distro, arch, spec)))
+
+ repos.append(make_package(distro, arch, spec, srcdir))
+
+ # Build the repos' metadatas.
+ for repo in set(repos):
+ print repo
+ make_repo(repo)
+
+ finally:
+ os.chdir(oldcwd)
+ if "-n" not in flags:
+ move_repos_into_place(prefix+"/repo", REPOPATH)
+ # FIXME: try shutil.rmtree some day.
+ sysassert(["rm", "-rv", prefix])
+
+
+def parse_args(args):
+ if len(args) == 0:
+ print """Usage: packager.py [OPTS] SPEC1 SPEC2 ... SPECn
+
+Options:
+
+ -n: Just build the packages, don't publish them as a repo
+ or clean out the working directory
+
+Each SPEC is a mongodb version string optionally followed by a colon
+and some parameters, of the form <paramname>=<value>. Supported
+parameters:
+
+ suffix -- suffix to append to the package's base name. (If
+ unsupplied, suffixes default based on the parity of the
+ middle number in the version.)
+
+ revision -- least-order version number to packaging systems
+"""
+ sys.exit(0)
+
+ try:
+ (flags, args) = getopt.getopt(args, "n")
+ except getopt.GetoptError, err:
+ print str(err)
+ sys.exit(2)
+ flags=dict(flags)
+ specs=[Spec(arg) for arg in args]
+ return (flags, specs)
+
+def crossproduct(*seqs):
+ """A generator for iterating all the tuples consisting of elements
+ of seqs."""
+ l = len(seqs)
+ if l == 0:
+ pass
+ elif l == 1:
+ for i in seqs[0]:
+ yield [i]
+ else:
+ for lst in crossproduct(*seqs[:-1]):
+ for i in seqs[-1]:
+ lst2=list(lst)
+ lst2.append(i)
+ yield lst2
+
+def sysassert(argv):
+ """Run argv and assert that it exited with status 0."""
+ print "In %s, running %s" % (os.getcwd(), " ".join(argv))
+ sys.stdout.flush()
+ sys.stderr.flush()
+ assert(subprocess.Popen(argv).wait()==0)
+
+def backtick(argv):
+ """Run argv and return its output string."""
+ print "In %s, running %s" % (os.getcwd(), " ".join(argv))
+ sys.stdout.flush()
+ sys.stderr.flush()
+ return subprocess.Popen(argv, stdout=subprocess.PIPE).communicate()[0]
+
+def ensure_dir(filename):
+ """Make sure that the directory that's the dirname part of
+ filename exists, and return filename."""
+ dirpart = os.path.dirname(filename)
+ try:
+ os.makedirs(dirpart)
+ except OSError: # as exc: # Python >2.5
+ exc=sys.exc_value
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise exc
+ return filename
+
+
+def tarfile(distro, arch, spec):
+ """Return the location where we store the downloaded tarball for
+ (arch, spec)"""
+ return "dl/mongodb-linux-%s-subscription-%s-%s.tar.gz" % (spec.version(), distro.build_os(), arch)
+
+def setupdir(distro, arch, spec):
+ # The setupdir will be a directory containing all inputs to the
+ # distro's packaging tools (e.g., package metadata files, init
+ # scripts, etc), along with the already-built binaries). In case
+ # the following format string is unclear, an example setupdir
+ # would be dst/x86_64/debian-sysvinit/mongodb-10gen-unstable/
+ return "dst/%s/%s/%s%s-%s/" % (arch, distro.name(), distro.pkgbase(), spec.suffix(), spec.pversion(distro))
+
+def unpack_binaries_into(distro, arch, spec, where):
+ """Unpack the tarfile for (distro, arch, spec) into directory where."""
+ rootdir=os.getcwd()
+ ensure_dir(where)
+ # Note: POSIX tar doesn't require support for gtar's "-C" option,
+ # and Python's tarfile module prior to Python 2.7 doesn't have the
+ # features to make this detail easy. So we'll just do the dumb
+ # thing and chdir into where and run tar there.
+ os.chdir(where)
+ try:
+ sysassert(["tar", "xvzf", rootdir+"/"+tarfile(distro, arch, spec), "mongodb-linux-%s-subscription-%s-%s/bin" % (arch, distro.build_os(), spec.version())])
+ os.rename("mongodb-linux-%s-subscription-%s-%s/bin" % (arch, distro.build_os(), spec.version()), "bin")
+ os.rmdir("mongodb-linux-%s-subscription-%s-%s" % (arch, distro.build_os(), spec.version()))
+ except Exception:
+ exc=sys.exc_value
+ os.chdir(rootdir)
+ raise exc
+ os.chdir(rootdir)
+
+def make_package(distro, arch, spec, srcdir):
+ """Construct the package for (arch, distro, spec), getting
+ packaging files from srcdir and any user-specified suffix from
+ suffixes"""
+
+ sdir=setupdir(distro, arch, spec)
+ ensure_dir(sdir)
+ # Note that the RPM packages get their man pages from the debian
+ # directory, so the debian directory is needed in all cases (and
+ # innocuous in the debianoids' sdirs).
+ for pkgdir in ["debian", "rpm"]:
+ print "Copying packaging files from %s to %s" % ("%s/%s" % (srcdir, pkgdir), sdir)
+ # FIXME: sh-dash-cee is bad. See if tarfile can do this.
+ sysassert(["sh", "-c", "(cd \"%s\" && git archive r%s %s/ ) | (cd \"%s\" && tar xvf -)" % (srcdir, spec.version(), pkgdir, sdir)])
+ # Splat the binaries under sdir. The "build" stages of the
+ # packaging infrastructure will move the binaries to wherever they
+ # need to go.
+ unpack_binaries_into(distro, arch, spec, sdir+("%s/usr/"%BINARYDIR))
+ # Remove the mongosniff binary due to libpcap dynamic
+ # linkage. FIXME: this removal should go away
+ # eventually.
+ if os.path.exists(sdir+("%s/usr/bin/mongosniff"%BINARYDIR)):
+ os.unlink(sdir+("%s/usr/bin/mongosniff"%BINARYDIR))
+ return distro.make_pkg(arch, spec, srcdir)
+
+def make_repo(repodir):
+ if re.search("(debian|ubuntu)", repodir):
+ make_deb_repo(repodir)
+ elif re.search("(centos|redhat|fedora)", repodir):
+ make_rpm_repo(repodir)
+ else:
+ raise Exception("BUG: unsupported platform?")
+
+def make_deb(distro, arch, spec, srcdir):
+ # I can't remember the details anymore, but the initscript/upstart
+ # job files' names must match the package name in some way; and
+ # see also the --name flag to dh_installinit in the generated
+ # debian/rules file.
+ suffix=spec.suffix()
+ sdir=setupdir(distro, arch, spec)
+ if re.search("sysvinit", distro.name()):
+ os.link(sdir+"debian/init.d", sdir+"debian/%s%s.mongodb.init" % (distro.pkgbase(), suffix))
+ os.unlink(sdir+"debian/mongodb.upstart")
+ elif re.search("upstart", distro.name()):
+ os.link(sdir+"debian/mongodb.upstart", sdir+"debian/%s%s.upstart" % (distro.pkgbase(), suffix))
+ os.unlink(sdir+"debian/init.d")
+ else:
+ raise Exception("unknown debianoid flavor: not sysvinit or upstart?")
+ # Rewrite the control and rules files
+ write_debian_control_file(sdir+"debian/control", spec)
+ write_debian_rules_file(sdir+"debian/rules", spec)
+ write_debian_changelog(sdir+"debian/changelog", spec, srcdir)
+ distro_arch=distro.archname(arch)
+ # Do the packaging.
+ oldcwd=os.getcwd()
+ try:
+ os.chdir(sdir)
+ sysassert(["dpkg-buildpackage", "-a"+distro_arch, "-k Richard Kreuter <richard@10gen.com>"])
+ finally:
+ os.chdir(oldcwd)
+ r=distro.repodir(arch)
+ ensure_dir(r)
+ # FIXME: see if shutil.copyfile or something can do this without
+ # much pain.
+ sysassert(["cp", "-v", sdir+"../%s%s_%s%s_%s.deb"%(distro.pkgbase(), suffix, spec.pversion(distro), "-"+spec.param("revision") if spec.param("revision") else"", distro_arch), r])
+ return r
+
+def make_deb_repo(repo):
+ # Note: the Debian repository Packages files must be generated
+ # very carefully in order to be usable.
+ oldpwd=os.getcwd()
+ os.chdir(repo+"../../../../")
+ try:
+ dirs=set([os.path.dirname(deb)[2:] for deb in backtick(["find", ".", "-name", "*.deb"]).split()])
+ for d in dirs:
+ s=backtick(["dpkg-scanpackages", d, "/dev/null"])
+ f=open(d+"/Packages", "w")
+ try:
+ f.write(s)
+ finally:
+ f.close()
+ b=backtick(["gzip", "-9c", d+"/Packages"])
+ f=open(d+"/Packages.gz", "wb")
+ try:
+ f.write(b)
+ finally:
+ f.close()
+ finally:
+ os.chdir(oldpwd)
+ # Notes: the Release{,.gpg} files must live in a special place,
+ # and must be created after all the Packages.gz files have been
+ # done.
+ s="""
+Origin: 10gen
+Label: 10gen
+Suite: 10gen
+Codename: %s
+Version: %s
+Architectures: i386 amd64
+Components: 10gen
+Description: 10gen packages
+""" % ("dist", "dist")
+ if os.path.exists(repo+"../../Release"):
+ os.unlink(repo+"../../Release")
+ if os.path.exists(repo+"../../Release.gpg"):
+ os.unlink(repo+"../../Release.gpg")
+ oldpwd=os.getcwd()
+ os.chdir(repo+"../../")
+ s2=backtick(["apt-ftparchive", "release", "."])
+ try:
+ f=open("Release", 'w')
+ try:
+ f.write(s)
+ f.write(s2)
+ finally:
+ f.close()
+
+ arg=None
+ for line in backtick(["gpg", "--list-keys"]).split("\n"):
+ tokens=line.split()
+ if len(tokens)>0 and tokens[0] == "uid":
+ arg=tokens[-1]
+ break
+ # Note: for some reason, I think --no-tty might be needed
+ # here, but maybe not.
+ sysassert(["gpg", "-r", arg, "--no-secmem-warning", "-abs", "--output", "Release.gpg", "Release"])
+ finally:
+ os.chdir(oldpwd)
+
+
+def move_repos_into_place(src, dst):
+ # Find all the stuff in src/*, move it to a freshly-created
+ # directory beside dst, then play some games with symlinks so that
+ # dst is a name the new stuff and dst+".old" names the previous
+ # one. This feels like a lot of hooey for something so trivial.
+
+ # First, make a crispy fresh new directory to put the stuff in.
+ i=0
+ while True:
+ date_suffix=time.strftime("%Y-%m-%d")
+ dname=dst+".%s.%d" % (date_suffix, i)
+ try:
+ os.mkdir(dname)
+ break
+ except OSError:
+ exc=sys.exc_value
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise exc
+ i=i+1
+
+ # Put the stuff in our new directory.
+ for r in os.listdir(src):
+ sysassert(["cp", "-rv", src + "/" + r, dname])
+
+ # Make a symlink to the new directory; the symlink will be renamed
+ # to dst shortly.
+ i=0
+ while True:
+ tmpnam=dst+".TMP.%d" % i
+ try:
+ os.symlink(dname, tmpnam)
+ break
+ except OSError: # as exc: # Python >2.5
+ exc=sys.exc_value
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise exc
+ i=i+1
+
+ # Make a symlink to the old directory; this symlink will be
+ # renamed shortly, too.
+ oldnam=None
+ if os.path.exists(dst):
+ i=0
+ while True:
+ oldnam=dst+".old.%d" % i
+ try:
+ os.symlink(os.readlink(dst), oldnam)
+ break
+ except OSError: # as exc: # Python >2.5
+ exc=sys.exc_value
+ if exc.errno == errno.EEXIST:
+ pass
+ else:
+ raise exc
+
+ os.rename(tmpnam, dst)
+ if oldnam:
+ os.rename(oldnam, dst+".old")
+
+
+def write_debian_changelog(path, spec, srcdir):
+ oldcwd=os.getcwd()
+ os.chdir(srcdir)
+ preamble=""
+ if spec.param("revision"):
+ preamble="""mongodb%s (%s-%s) unstable; urgency=low
+
+ * Bump revision number
+
+ -- Richard Kreuter <richard@10gen.com> %s
+
+""" % (spec.suffix(), spec.pversion(Distro("debian")), spec.param("revision"), time.strftime("%a, %d %b %Y %H:%m:%S %z"))
+ try:
+ s=preamble+backtick(["sh", "-c", "git archive r%s debian/changelog | tar xOf -" % spec.version()])
+ finally:
+ os.chdir(oldcwd)
+ f=open(path, 'w')
+ lines=s.split("\n")
+ # If the first line starts with "mongodb", it's not a revision
+ # preamble, and so frob the version number.
+ lines[0]=re.sub("^mongodb \\(.*\\)", "mongodb (%s)" % (spec.pversion(Distro("debian"))), lines[0])
+ # Rewrite every changelog entry starting in mongodb<space>
+ lines=[re.sub("^mongodb ", "mongodb%s " % (spec.suffix()), l) for l in lines]
+ lines=[re.sub("^ --", " --", l) for l in lines]
+ s="\n".join(lines)
+ try:
+ f.write(s)
+ finally:
+ f.close()
+
+def write_debian_control_file(path, spec):
+ s="""Source: @@PACKAGE_BASENAME@@
+Section: devel
+Priority: optional
+Maintainer: Richard Kreuter <richard@10gen.com>
+Build-Depends:
+Standards-Version: 3.8.0
+Homepage: http://www.mongodb.org
+
+Package: @@PACKAGE_BASENAME@@
+Conflicts: @@PACKAGE_CONFLICTS@@
+Architecture: any
+Depends: libc6 (>= 2.3.2), libgcc1 (>= 1:4.1.1), libstdc++6 (>= 4.1.1), libsnmp15, libsasl2-2, libssl1.0.0
+Description: An object/document-oriented database
+ MongoDB is a high-performance, open source, schema-free
+ document-oriented data store that's easy to deploy, manage
+ and use. It's network accessible, written in C++ and offers
+ the following features :
+ .
+ * Collection oriented storage - easy storage of object-
+ style data
+ * Full index support, including on inner objects
+ * Query profiling
+ * Replication and fail-over support
+ * Efficient storage of binary data including large
+ objects (e.g. videos)
+ * Auto-sharding for cloud-level scalability (Q209)
+ .
+ High performance, scalability, and reasonable depth of
+ functionality are the goals for the project.
+"""
+ s=re.sub("@@PACKAGE_BASENAME@@", "mongodb%s" % spec.suffix(), s)
+ conflict_suffixes=["", "-stable", "-unstable", "-nightly", "-10gen", "-10gen-unstable", "-10gen-enterprise"]
+ conflict_suffixes = [suff for suff in conflict_suffixes if suff != spec.suffix()]
+ s=re.sub("@@PACKAGE_CONFLICTS@@", ", ".join(["mongodb"+suffix for suffix in conflict_suffixes] + [ "mongodb18"+suffix for suffix in conflict_suffixes] + ["mongodb20"+suffix for suffix in conflict_suffixes]), s)
+
+ f=open(path, 'w')
+ try:
+ f.write(s)
+ finally:
+ f.close()
+
+def write_debian_rules_file(path, spec):
+ # Note debian/rules is a makefile, so for visual disambiguation we
+ # make all tabs here \t.
+ s="""#!/usr/bin/make -f
+# -*- makefile -*-
+# Sample debian/rules that uses debhelper.
+# This file was originally written by Joey Hess and Craig Small.
+# As a special exception, when this file is copied by dh-make into a
+# dh-make output file, you may use that output file without restriction.
+# This special exception was added by Craig Small in version 0.37 of dh-make.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+
+configure: configure-stamp
+configure-stamp:
+\tdh_testdir
+ # Add here commands to configure the package.
+
+\ttouch configure-stamp
+
+
+build: build-stamp
+
+build-stamp: configure-stamp
+\tdh_testdir
+
+ # Add here commands to compile the package.
+# THE FOLLOWING LINE IS INTENTIONALLY COMMENTED.
+\t# scons
+ #docbook-to-man debian/mongodb.sgml > mongodb.1
+\tls debian/*.1 > debian/@@PACKAGE_NAME@@.manpages
+
+\ttouch $@
+
+clean:
+\tdh_testdir
+\tdh_testroot
+\trm -f build-stamp configure-stamp
+
+\t# FIXME: scons freaks out at the presence of target files
+\t# under debian/mongodb.
+\t#scons -c
+\trm -rf $(CURDIR)/debian/@@PACKAGE_NAME@@
+\trm -f config.log
+\trm -f mongo
+\trm -f mongod
+\trm -f mongoimportjson
+\trm -f mongoexport
+\trm -f mongorestore
+\trm -f mongodump
+\trm -f mongofiles
+\trm -f .sconsign.dblite
+\trm -f libmongoclient.a
+\trm -rf client/*.o
+\trm -rf tools/*.o
+\trm -rf shell/*.o
+\trm -rf .sconf_temp
+\trm -f buildscripts/*.pyc
+\trm -f *.pyc
+\trm -f buildinfo.cpp
+\tdh_clean debian/files
+
+install: build
+\tdh_testdir
+\tdh_testroot
+\tdh_prep
+\tdh_installdirs
+
+# THE FOLLOWING LINE IS INTENTIONALLY COMMENTED.
+\t# scons --prefix=$(CURDIR)/debian/mongodb/usr install
+\tcp -v $(CURDIR)/@@BINARYDIR@@/usr/bin/* $(CURDIR)/debian/@@PACKAGE_NAME@@/usr/bin
+\tmkdir -p $(CURDIR)/debian/@@PACKAGE_NAME@@/etc
+\tcp $(CURDIR)/debian/mongodb.conf $(CURDIR)/debian/@@PACKAGE_NAME@@/etc/mongodb.conf
+
+\tmkdir -p $(CURDIR)/debian/@@PACKAGE_NAME@@/usr/share/lintian/overrides/
+\tinstall -m 644 $(CURDIR)/debian/lintian-overrides \
+\t\t$(CURDIR)/debian/@@PACKAGE_NAME@@/usr/share/lintian/overrides/@@PACKAGE_NAME@@
+
+# Build architecture-independent files here.
+binary-indep: build install
+# We have nothing to do by default.
+
+# Build architecture-dependent files here.
+binary-arch: build install
+\tdh_testdir
+\tdh_testroot
+\tdh_installchangelogs
+\tdh_installdocs
+\tdh_installexamples
+#\tdh_install
+#\tdh_installmenu
+#\tdh_installdebconf\t
+#\tdh_installlogrotate
+#\tdh_installemacsen
+#\tdh_installpam
+#\tdh_installmime
+\tdh_installinit --name=@@PACKAGE_BASENAME@@
+#\tdh_installinfo
+\tdh_installman
+\tdh_link
+# Appears to be broken on Ubuntu 11.10...?
+#\tdh_strip
+\tdh_compress
+\tdh_fixperms
+\tdh_installdeb
+\tdh_shlibdeps
+\tdh_gencontrol
+\tdh_md5sums
+\tdh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install configure
+"""
+ s=re.sub("@@PACKAGE_NAME@@", "mongodb%s" % spec.suffix(), s)
+ s=re.sub("@@PACKAGE_BASENAME@@", "mongodb", s)
+ s=re.sub("@@BINARYDIR@@", BINARYDIR, s)
+ f=open(path, 'w')
+ try:
+ f.write(s)
+ finally:
+ f.close()
+ # FIXME: some versions of debianoids seem to
+ # need the rules file to be 755?
+ os.chmod(path, stat.S_IXUSR|stat.S_IWUSR|stat.S_IRUSR|stat.S_IXGRP|stat.S_IRGRP|stat.S_IXOTH|stat.S_IWOTH)
+
+def make_rpm(distro, arch, spec, srcdir):
+ # Create the specfile.
+ suffix=spec.suffix()
+ sdir=setupdir(distro, arch, spec)
+ specfile=sdir+"rpm/mongo%s.spec" % suffix
+ write_rpm_spec_file(specfile, spec)
+ topdir=ensure_dir(os.getcwd()+'/rpmbuild/')
+ for subdir in ["BUILD", "RPMS", "SOURCES", "SPECS", "SRPMS"]:
+ ensure_dir("%s/%s/" % (topdir, subdir))
+ distro_arch=distro.archname(arch)
+ # RPM tools take these macro files that define variables in
+ # RPMland. Unfortunately, there's no way to tell RPM tools to use
+ # a given file *in addition* to the files that it would already
+ # load, so we have to figure out what it would normally load,
+ # augment that list, and tell RPM to use the augmented list. To
+ # figure out what macrofiles ordinarily get loaded, older RPM
+ # versions had a parameter called "macrofiles" that could be
+ # extracted from "rpm --showrc". But newer RPM versions don't
+ # have this. To tell RPM what macros to use, older versions of
+ # RPM have a --macros option that doesn't work; on these versions,
+ # you can put a "macrofiles" parameter into an rpmrc file. But
+ # that "macrofiles" setting doesn't do anything for newer RPM
+ # versions, where you have to use the --macros flag instead. And
+ # all of this is to let us do our work with some guarantee that
+ # we're not clobbering anything that doesn't belong to us. Why is
+ # RPM so braindamaged?
+ macrofiles=[l for l in backtick(["rpm", "--showrc"]).split("\n") if l.startswith("macrofiles")]
+ flags=[]
+ macropath=os.getcwd()+"/macros"
+ write_rpm_macros_file(macropath, topdir)
+ if len(macrofiles)>0:
+ macrofiles=macrofiles[0]+":"+macropath
+ rcfile=os.getcwd()+"/rpmrc"
+ write_rpmrc_file(rcfile, macrofiles)
+ flags=["--rpmrc", rcfile]
+ else:
+ # This hard-coded hooey came from some box running RPM
+ # 4.4.2.3. It may not work over time, but RPM isn't sanely
+ # configurable.
+ flags=["--macros", "/usr/lib/rpm/macros:/usr/lib/rpm/%s-linux/macros:/etc/rpm/macros.*:/etc/rpm/macros:/etc/rpm/%s-linux/macros:~/.rpmmacros:%s" % (distro_arch, distro_arch, macropath)]
+ # Put the specfile and the tar'd up binaries and stuff in
+ # place. FIXME: see if shutil.copyfile can do this without too
+ # much hassle.
+ sysassert(["cp", "-v", specfile, topdir+"SPECS/"])
+ oldcwd=os.getcwd()
+ os.chdir(sdir+"/../")
+ try:
+ sysassert(["tar", "-cpzf", topdir+"SOURCES/mongo%s-%s.tar.gz" % (suffix, spec.pversion(distro)), os.path.basename(os.path.dirname(sdir))])
+ finally:
+ os.chdir(oldcwd)
+ # Do the build.
+ sysassert(["rpmbuild", "-ba", "--target", distro_arch] + flags + ["%s/SPECS/mongo%s.spec" % (topdir, suffix)])
+ r=distro.repodir(arch)
+ ensure_dir(r)
+ # FIXME: see if some combination of shutil.copy<hoohah> and glob
+ # can do this without shelling out.
+ sysassert(["sh", "-c", "cp -v \"%s/RPMS/%s/\"*.rpm \"%s\""%(topdir, distro_arch, r)])
+ return r
+
+def make_rpm_repo(repo):
+ oldpwd=os.getcwd()
+ os.chdir(repo+"../")
+ try:
+ sysassert(["createrepo", "."])
+ finally:
+ os.chdir(oldpwd)
+
+
+def write_rpmrc_file(path, string):
+ f=open(path, 'w')
+ try:
+ f.write(string)
+ finally:
+ f.close()
+
+def write_rpm_macros_file(path, topdir):
+ f=open(path, 'w')
+ try:
+ f.write("%%_topdir %s" % topdir)
+ finally:
+ f.close()
+
+def write_rpm_spec_file(path, spec):
+ s="""Name: @@PACKAGE_BASENAME@@
+Conflicts: @@PACKAGE_CONFLICTS@@
+Obsoletes: @@PACKAGE_OBSOLETES@@
+Version: @@PACKAGE_VERSION@@
+Release: mongodb_@@PACKAGE_REVISION@@%{?dist}
+Summary: mongo client shell and tools
+License: AGPL 3.0
+URL: http://www.mongodb.org
+Group: Applications/Databases
+Requires: cyrus-sasl, net-snmp-libs
+
+Source0: %{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+
+%description
+Mongo (from "huMONGOus") is a schema-free document-oriented database.
+It features dynamic profileable queries, full indexing, replication
+and fail-over support, efficient storage of large binary data objects,
+and auto-sharding.
+
+This package provides the mongo shell, import/export tools, and other
+client utilities.
+
+%package server
+Summary: mongo server, sharding server, and support scripts
+Group: Applications/Databases
+Requires: @@PACKAGE_BASENAME@@
+
+%description server
+Mongo (from "huMONGOus") is a schema-free document-oriented database.
+
+This package provides the mongo server software, mongo sharding server
+softwware, default configuration files, and init.d scripts.
+
+%package devel
+Summary: Headers and libraries for mongo development.
+Group: Applications/Databases
+
+%description devel
+Mongo (from "huMONGOus") is a schema-free document-oriented database.
+
+This package provides the mongo static library and header files needed
+to develop mongo client software.
+
+%prep
+%setup
+
+%build
+#scons --prefix=$RPM_BUILD_ROOT/usr all
+# XXX really should have shared library here
+
+%install
+#scons --prefix=$RPM_BUILD_ROOT/usr install
+mkdir -p $RPM_BUILD_ROOT/usr
+cp -rv @@BINARYDIR@@/usr/bin $RPM_BUILD_ROOT/usr
+mkdir -p $RPM_BUILD_ROOT/usr/share/man/man1
+cp debian/*.1 $RPM_BUILD_ROOT/usr/share/man/man1/
+# FIXME: remove this rm when mongosniff is back in the package
+rm -v $RPM_BUILD_ROOT/usr/share/man/man1/mongosniff.1*
+mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d
+cp -v rpm/init.d-mongod $RPM_BUILD_ROOT/etc/rc.d/init.d/mongod
+chmod a+x $RPM_BUILD_ROOT/etc/rc.d/init.d/mongod
+mkdir -p $RPM_BUILD_ROOT/etc
+cp -v rpm/mongod.conf $RPM_BUILD_ROOT/etc/mongod.conf
+mkdir -p $RPM_BUILD_ROOT/etc/sysconfig
+cp -v rpm/mongod.sysconfig $RPM_BUILD_ROOT/etc/sysconfig/mongod
+mkdir -p $RPM_BUILD_ROOT/var/lib/mongo
+mkdir -p $RPM_BUILD_ROOT/var/log/mongo
+touch $RPM_BUILD_ROOT/var/log/mongo/mongod.log
+
+%clean
+#scons -c
+rm -rf $RPM_BUILD_ROOT
+
+%pre server
+if ! /usr/bin/id -g mongod &>/dev/null; then
+ /usr/sbin/groupadd -r mongod
+fi
+if ! /usr/bin/id mongod &>/dev/null; then
+ /usr/sbin/useradd -M -r -g mongod -d /var/lib/mongo -s /bin/false \
+ -c mongod mongod > /dev/null 2>&1
+fi
+
+%post server
+if test $1 = 1
+then
+ /sbin/chkconfig --add mongod
+fi
+
+%preun server
+if test $1 = 0
+then
+ /sbin/chkconfig --del mongod
+fi
+
+%postun server
+if test $1 -ge 1
+then
+ /sbin/service mongod condrestart >/dev/null 2>&1 || :
+fi
+
+%files
+%defattr(-,root,root,-)
+#%doc README GNU-AGPL-3.0.txt
+
+%{_bindir}/bsondump
+%{_bindir}/mongo
+%{_bindir}/mongodump
+%{_bindir}/mongoexport
+#@@VERSION!=2.1.0@@%{_bindir}/mongofiles
+%{_bindir}/mongoimport
+#@@VERSION>=2.1.0@@%{_bindir}/mongooplog
+#@@VERSION>=2.1.0@@%{_bindir}/mongoperf
+%{_bindir}/mongorestore
+#@@VERSION>1.9@@%{_bindir}/mongotop
+%{_bindir}/mongostat
+# FIXME: uncomment when mongosniff is back in the package
+#%{_bindir}/mongosniff
+
+# FIXME: uncomment this when there's a stable release whose source
+# tree contains a bsondump man page.
+#@@VERSION>1.9@@%{_mandir}/man1/bsondump.1*
+%{_mandir}/man1/mongo.1*
+%{_mandir}/man1/mongodump.1*
+%{_mandir}/man1/mongoexport.1*
+%{_mandir}/man1/mongofiles.1*
+%{_mandir}/man1/mongoimport.1*
+%{_mandir}/man1/mongorestore.1*
+%{_mandir}/man1/mongostat.1*
+# FIXME: uncomment when mongosniff is back in the package
+#%{_mandir}/man1/mongosniff.1*
+#@@VERSION>=2.4.0@@%{_mandir}/man1/mongotop.1*
+#@@VERSION>=2.4.0@@%{_mandir}/man1/mongoperf.1*
+#@@VERSION>=2.4.0@@%{_mandir}/man1/mongooplog.1*
+
+%files server
+%defattr(-,root,root,-)
+%config(noreplace) /etc/mongod.conf
+%{_bindir}/mongod
+%{_bindir}/mongos
+%{_mandir}/man1/mongod.1*
+%{_mandir}/man1/mongos.1*
+/etc/rc.d/init.d/mongod
+/etc/sysconfig/mongod
+#/etc/rc.d/init.d/mongos
+%attr(0755,mongod,mongod) %dir /var/lib/mongo
+%attr(0755,mongod,mongod) %dir /var/log/mongo
+%attr(0640,mongod,mongod) %config(noreplace) %verify(not md5 size mtime) /var/log/mongo/mongod.log
+
+%changelog
+* Thu Jan 28 2010 Richard M Kreuter <richard@10gen.com>
+- Minor fixes.
+
+* Sat Oct 24 2009 Joe Miklojcik <jmiklojcik@shopwiki.com> -
+- Wrote mongo.spec.
+"""
+ suffix=spec.suffix()
+ s=re.sub("@@PACKAGE_BASENAME@@", "mongo%s" % suffix, s)
+ s=re.sub("@@PACKAGE_VERSION@@", spec.pversion(Distro("redhat")), s)
+ # FIXME, maybe: the RPM guide says that Release numbers ought to
+ # be integers starting at 1, but we use "mongodb_1{%dist}",
+ # whatever the hell that means.
+ s=re.sub("@@PACKAGE_REVISION@@", str(int(spec.param("revision"))+1) if spec.param("revision") else "1", s)
+ s=re.sub("@@BINARYDIR@@", BINARYDIR, s)
+ conflict_suffixes=["", "-10gen", "-10gen-unstable", "-10gen-enterprise"]
+ conflict_suffixes = [suff for suff in conflict_suffixes if suff != spec.suffix()]
+ s=re.sub("@@PACKAGE_CONFLICTS@@", ", ".join(["mongo"+suffix for suffix in conflict_suffixes] + [ "mongo18"+suffix for suffix in conflict_suffixes] + ["mongo20"+suffix for suffix in conflict_suffixes]), s)
+ if suffix.endswith("-10gen"):
+ s=re.sub("@@PACKAGE_PROVIDES@@", "mongo-stable", s)
+ s=re.sub("@@PACKAGE_OBSOLETES@@", "mongo-stable", s)
+ elif suffix == "-10gen-unstable":
+ s=re.sub("@@PACKAGE_PROVIDES@@", "mongo-unstable", s)
+ s=re.sub("@@PACKAGE_OBSOLETES@@", "mongo-unstable", s)
+ elif suffix == "-10gen-enterprise":
+ s=re.sub("@@PACKAGE_PROVIDES@@", "mongo-enterprise", s)
+ s=re.sub("@@PACKAGE_OBSOLETES@@", "mongo-enterprise", s)
+ else:
+ raise Exception("BUG: unknown suffix %s" % suffix)
+
+ lines=[]
+ for line in s.split("\n"):
+ m = re.search("@@VERSION(>|>=|!=)(\d.*)@@(.*)", line)
+ if m:
+ op = m.group(1)
+ ver = m.group(2)
+ fn = m.group(3)
+ if op == '>':
+ if spec.version_better_than(ver):
+ lines.append(fn)
+ elif op == '>=':
+ if spec.version() == ver or spec.version_better_than(ver):
+ lines.append(fn)
+ elif op == '!=':
+ if spec.version() != ver:
+ lines.append(fn)
+ else:
+ # Since we're inventing our own template system for RPM
+ # specfiles here, we oughtn't use template syntax we don't
+ # support.
+ raise Exception("BUG: probable bug in packager script: %s, %s, %s" % (m.group(1), m.group(2), m.group(3)))
+ else:
+ lines.append(line)
+ s="\n".join(lines)
+
+ f=open(path, 'w')
+ try:
+ f.write(s)
+ finally:
+ f.close()
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/doxygenConfig b/doxygenConfig
index 1eebb6c10b9..90b26608766 100644
--- a/doxygenConfig
+++ b/doxygenConfig
@@ -3,7 +3,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = MongoDB
-PROJECT_NUMBER = 2.4.6
+PROJECT_NUMBER = 2.4.9
OUTPUT_DIRECTORY = docs/doxygen
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
diff --git a/jstests/aggregation/mongos_slaveok.js b/jstests/aggregation/mongos_slaveok.js
new file mode 100644
index 00000000000..e5d0f6692ae
--- /dev/null
+++ b/jstests/aggregation/mongos_slaveok.js
@@ -0,0 +1,38 @@
+/**
+ * Tests aggregate command against mongos with slaveOk. For more tests on read preference,
+ * please refer to jstests/sharding/read_pref_cmd.js.
+ */
+
+var NODES = 2;
+
+var doTest = function(st, doSharded) {
+var testDB = st.s.getDB('test');
+
+if (doSharded) {
+ testDB.adminCommand({ enableSharding: 'test' });
+ testDB.adminCommand({ shardCollection: 'test.user', key: { x: 1 }});
+}
+
+testDB.user.insert({ x: 10 });
+testDB.runCommand({ getLastError: 1, w: NODES });
+testDB.setSlaveOk(true);
+
+var secNode = st.rs0.getSecondary();
+secNode.getDB('test').setProfilingLevel(2);
+
+var res = testDB.runCommand({ aggregate: 'user', pipeline: [{ $project: { x: 1 }}]});
+assert(res.ok, 'aggregate command failed: ' + tojson(res));
+
+var profileQuery = { op: 'command', ns: 'test.$cmd', 'command.aggregate': 'user' };
+var profileDoc = secNode.getDB('test').system.profile.findOne(profileQuery);
+
+assert(profileDoc != null);
+testDB.dropDatabase();
+};
+
+var st = new ShardingTest({ shards: { rs0: { oplogSize: 10, verbose: 1, nodes: NODES }}});
+
+doTest(st, false);
+doTest(st, true);
+
+st.stop();
diff --git a/jstests/auth/js_scope_leak.js b/jstests/auth/js_scope_leak.js
new file mode 100644
index 00000000000..c8c19b61aef
--- /dev/null
+++ b/jstests/auth/js_scope_leak.js
@@ -0,0 +1,139 @@
+// Test for SERVER-9129
+// Verify global scope data does not persist past logout or auth.
+// NOTE: Each test case covers 3 state transitions:
+// no auth -> auth user 'a'
+// auth user 'a' -> auth user 'b'
+// auth user 'b' -> logout
+//
+// These transitions are tested for dbEval, $where, MapReduce and $group
+
+var conn = MongoRunner.runMongod({ auth: "", smallfiles: ""});
+var test = conn.getDB("test");
+
+// insert a single document and add two test users
+test.foo.insert({a:1});
+test.getLastError();
+assert.eq(1, test.foo.findOne().a);
+test.addUser('a', 'a');
+test.addUser('b', 'b');
+
+function missingOrEquals(string) {
+ return 'function() { '
+ + 'var global = function(){return this;}.call();'
+ // Uncomment the next line when debugging.
+ // + 'print(global.hasOwnProperty("someGlobal") ? someGlobal : "MISSING" );'
+ + 'return !global.hasOwnProperty("someGlobal")'
+ + ' || someGlobal == unescape("' + escape(string) + '");'
+ +'}()'
+}
+
+function testDbEval() {
+ // set the global variable 'someGlobal' before authenticating
+ test.eval('someGlobal = "noUsers";');
+
+ // test new user auth causes scope to be cleared
+ test.auth('a', 'a');
+ assert(test.eval('return ' + missingOrEquals('a')), "dbEval: Auth user 'a'");
+
+ // test auth as another user causes scope to be cleared
+ test.eval('someGlobal = "a";');
+ test.auth('b', 'b');
+ assert(test.eval('return ' + missingOrEquals('a&b')), "dbEval: Auth user 'b'");
+
+ // test user logout causes scope to be cleared
+ test.eval('someGlobal = "a&b";');
+ test.logout();
+ assert(test.eval('return ' + missingOrEquals('noUsers')), "dbEval: log out");
+}
+testDbEval();
+testDbEval();
+
+// test $where
+function testWhere() {
+ // set the global variable 'someGlobal' before authenticating
+ test.foo.findOne({$where:'someGlobal = "noUsers";'});
+
+ // test new user auth causes scope to be cleared
+ test.auth('a', 'a');
+ assert.eq(1,
+ test.foo.count({$where: 'return ' + missingOrEquals('a')}),
+ "$where: Auth user 'a");
+
+ // test auth as another user causes scope to be cleared
+ test.foo.findOne({$where:'someGlobal = "a";'});
+ test.auth('b', 'b');
+ assert(test.foo.count({$where: 'return ' + missingOrEquals('a&b')}), "$where: Auth user 'b'");
+ // test user logout causes scope to be cleared
+ test.foo.findOne({$where:'someGlobal = "a&b";'});
+ test.logout();
+ assert(test.foo.count({$where: 'return ' + missingOrEquals('noUsers')}), "$where: log out");
+}
+testWhere();
+testWhere();
+
+function testMapReduce() {
+ var mapSet = function(string) { return Function('someGlobal = "' + string + '"'); }
+ var mapGet = function(string) { return Function('assert(' + missingOrEquals(string) +')'); }
+ var reduce = function(k, v) { }
+ var setGlobalInMap = function(string) {
+ test.foo.mapReduce(mapSet(string), reduce, {out:{inline:1}});
+ }
+ var getGlobalFromMap = function(string) {
+ test.foo.mapReduce(mapGet(string), reduce, {out:{inline:1}});
+ }
+
+ // set the global variable 'someGlobal' before authenticating
+ setGlobalInMap('noUsers');
+
+ // test new user auth causes scope to be cleared
+ test.auth('a', 'a');
+ getGlobalFromMap('a'); // throws on fail
+
+ // test auth as another user causes scope to be cleared
+ setGlobalInMap('a');
+ test.auth('b', 'b');
+ getGlobalFromMap('a&b'); // throws on fail
+
+ // test user logout causes scope to be cleared
+ setGlobalInMap('a&b');
+ test.logout();
+ getGlobalFromMap('noUsers'); // throws on fail
+}
+testMapReduce();
+testMapReduce();
+
+function testGroup() {
+ var setGlobalInGroup = function(string) {
+ return test.foo.group({key: 'a',
+ reduce: Function('doc1', 'agg',
+ 'someGlobal = "' + string + '"'),
+ initial:{}});
+ }
+ var getGlobalFromGroup = function(string) {
+ return test.foo.group({key: 'a',
+ reduce: Function('doc1', 'agg',
+ 'assert(' + missingOrEquals(string) +')'),
+ initial:{}});
+ }
+
+ // set the global variable 'someGlobal' before authenticating
+ setGlobalInGroup('noUsers');
+
+ // test new user auth causes scope to be cleared
+ test.auth('a', 'a');
+ getGlobalFromGroup('a'); // throws on fail
+
+ // test auth as another user causes scope to be cleared
+ setGlobalInGroup('a');
+ test.auth('b', 'b');
+ getGlobalFromGroup('a&b'); // throws on fail
+
+ // test user logout causes scope to be cleared
+ setGlobalInGroup('a&b');
+ test.logout();
+ getGlobalFromGroup('noUsers'); // throws on fail
+}
+testGroup();
+testGroup();
+
+
diff --git a/jstests/dbhash2.js b/jstests/dbhash2.js
new file mode 100644
index 00000000000..ac491291c2b
--- /dev/null
+++ b/jstests/dbhash2.js
@@ -0,0 +1,22 @@
+
+mydb = db.getSisterDB( "config" );
+
+t = mydb.foo;
+t.drop();
+
+t.insert( { x : 1 } );
+res1 = mydb.runCommand( "dbhash" );
+assert( res1.fromCache.indexOf( "config.foo" ) == -1 );
+
+res2 = mydb.runCommand( "dbhash" );
+assert( res2.fromCache.indexOf( "config.foo" ) >= 0 );
+assert.eq( res1.collections.foo, res2.collections.foo );
+
+t.insert( { x : 2 } );
+res3 = mydb.runCommand( "dbhash" );
+assert( res3.fromCache.indexOf( "config.foo" ) < 0 );
+assert.neq( res1.collections.foo, res3.collections.foo );
+
+
+
+
diff --git a/jstests/dropdb.js b/jstests/dropdb.js
index 0b838846bde..6b5fa6e2fe5 100644
--- a/jstests/dropdb.js
+++ b/jstests/dropdb.js
@@ -6,12 +6,21 @@ m = db.getMongo();
baseName = "jstests_dropdb";
ddb = db.getSisterDB( baseName );
+print("initial dbs: " + tojson(m.getDBNames()));
+
+function check(shouldExist) {
+ var dbs = m.getDBNames();
+ assert.eq(Array.contains(dbs, baseName), shouldExist,
+ "DB " + baseName + " should " + (shouldExist ? "" : "not ") + "exist."
+ + " dbs: " + tojson(dbs));
+}
+
ddb.c.save( {} );
ddb.getLastError();
-assert.neq( -1, m.getDBNames().indexOf( baseName ) );
+check(true);
ddb.dropDatabase();
-assert.eq( -1, m.getDBNames().indexOf( baseName ) );
+check(false);
ddb.dropDatabase();
-assert.eq( -1, m.getDBNames().indexOf( baseName ) );
+check(false);
diff --git a/jstests/eval2.js b/jstests/eval2.js
index c3a7499ae47..6e39bb4a7bd 100644
--- a/jstests/eval2.js
+++ b/jstests/eval2.js
@@ -1,12 +1,12 @@
-t = db.test;
+t = db.eval2;
t.drop();
t.save({a:1});
t.save({a:1});
var f = db.group(
{
- ns: "test",
+ ns: t.getName(),
key: { a:true},
cond: { a:1 },
reduce: function(obj,prev) { prev.csum++; } ,
diff --git a/jstests/geo_circle2a.js b/jstests/geo_circle2a.js
index 1597033c01b..67a6ba17243 100644
--- a/jstests/geo_circle2a.js
+++ b/jstests/geo_circle2a.js
@@ -2,35 +2,36 @@
// Tests to make sure that nested multi-key indexing works for geo indexes and is not used for direct position
// lookups
-db.test.drop()
-db.test.insert({ p : [1112,3473], t : [{ k : 'a', v : 'b' }, { k : 'c', v : 'd' }] })
-db.test.ensureIndex({ p : '2d', 't.k' : 1 }, { min : 0, max : 10000 })
+var coll = db.geo_circle2a;
+coll.drop();
+coll.insert({ p : [1112,3473], t : [{ k : 'a', v : 'b' }, { k : 'c', v : 'd' }] })
+coll.ensureIndex({ p : '2d', 't.k' : 1 }, { min : 0, max : 10000 })
// Succeeds, since on direct lookup should not use the index
-assert(1 == db.test.find({p:[1112,3473],'t.k':'a'}).count(), "A")
+assert(1 == coll.find({p:[1112,3473],'t.k':'a'}).count(), "A")
// Succeeds and uses the geo index
-assert(1 == db.test.find({p:{$within:{$box:[[1111,3472],[1113,3475]]}}, 't.k' : 'a' }).count(), "B")
+assert(1 == coll.find({p:{$within:{$box:[[1111,3472],[1113,3475]]}}, 't.k' : 'a' }).count(), "B")
-db.test.drop()
-db.test.insert({ point:[ 1, 10 ], tags : [ { k : 'key', v : 'value' }, { k : 'key2', v : 123 } ] })
-db.test.insert({ point:[ 1, 10 ], tags : [ { k : 'key', v : 'value' } ] })
+coll.drop()
+coll.insert({ point:[ 1, 10 ], tags : [ { k : 'key', v : 'value' }, { k : 'key2', v : 123 } ] })
+coll.insert({ point:[ 1, 10 ], tags : [ { k : 'key', v : 'value' } ] })
-db.test.ensureIndex({ point : "2d" , "tags.k" : 1, "tags.v" : 1 })
+coll.ensureIndex({ point : "2d" , "tags.k" : 1, "tags.v" : 1 })
// Succeeds, since should now lookup multi-keys correctly
-assert(2 == db.test.find({ point : { $within : { $box : [[0,0],[12,12]] } } }).count(), "C")
+assert(2 == coll.find({ point : { $within : { $box : [[0,0],[12,12]] } } }).count(), "C")
// Succeeds, and should not use geoindex
-assert(2 == db.test.find({ point : [1, 10] }).count(), "D")
-assert(2 == db.test.find({ point : [1, 10], "tags.v" : "value" }).count(), "E")
-assert(1 == db.test.find({ point : [1, 10], "tags.v" : 123 }).count(), "F")
+assert(2 == coll.find({ point : [1, 10] }).count(), "D")
+assert(2 == coll.find({ point : [1, 10], "tags.v" : "value" }).count(), "E")
+assert(1 == coll.find({ point : [1, 10], "tags.v" : 123 }).count(), "F")
-db.test.drop()
-db.test.insert({ point:[ 1, 10 ], tags : [ { k : { 'hello' : 'world'}, v : 'value' }, { k : 'key2', v : 123 } ] })
-db.test.insert({ point:[ 1, 10 ], tags : [ { k : 'key', v : 'value' } ] })
+coll.drop()
+coll.insert({ point:[ 1, 10 ], tags : [ { k : { 'hello' : 'world'}, v : 'value' }, { k : 'key2', v : 123 } ] })
+coll.insert({ point:[ 1, 10 ], tags : [ { k : 'key', v : 'value' } ] })
-db.test.ensureIndex({ point : "2d" , "tags.k" : 1, "tags.v" : 1 })
+coll.ensureIndex({ point : "2d" , "tags.k" : 1, "tags.v" : 1 })
// Succeeds, should be able to look up the complex element
-assert(1 == db.test.find({ point : { $within : { $box : [[0,0],[12,12]] } }, 'tags.k' : { 'hello' : 'world' } }).count(), "G") \ No newline at end of file
+assert(1 == coll.find({ point : { $within : { $box : [[0,0],[12,12]] } }, 'tags.k' : { 'hello' : 'world' } }).count(), "G") \ No newline at end of file
diff --git a/jstests/replsets/gle_explicit_optime.js b/jstests/replsets/gle_explicit_optime.js
new file mode 100644
index 00000000000..77c526e981d
--- /dev/null
+++ b/jstests/replsets/gle_explicit_optime.js
@@ -0,0 +1,57 @@
+//
+// Tests the use of the wOpTime option in getLastError
+//
+
+var rst = new ReplSetTest({ nodes : 2 });
+rst.startSet();
+rst.initiate();
+
+var primary = rst.getPrimary();
+var secondary = rst.getSecondary();
+
+var coll = primary.getCollection( "foo.bar" );
+
+// Insert a doc and replicate it to two servers
+coll.insert({ some : "doc" });
+var gleObj = coll.getDB().getLastErrorObj( 2 ); // w : 2
+assert.eq( null, gleObj.err );
+var opTimeBeforeFailure = gleObj.lastOp;
+
+// Lock the secondary
+secondary.getDB("admin").fsyncLock();
+
+// Insert a doc and replicate it to the primary only
+coll.insert({ some : "doc" });
+gleObj = coll.getDB().getLastErrorObj( 1 ); // w : 1
+assert.eq( null, gleObj.err );
+var opTimeAfterFailure = gleObj.lastOp;
+
+printjson(opTimeBeforeFailure);
+printjson(opTimeAfterFailure);
+printjson( primary.getDB("admin").runCommand({ replSetGetStatus : true }) );
+
+// Create a new connection with new client and no opTime
+var newClientConn = new Mongo( primary.host );
+
+// New client has no set opTime, so w : 2 has no impact
+gleObj = newClientConn.getCollection( coll.toString() ).getDB().getLastErrorObj( 2 ); // w : 2
+assert.eq( null, gleObj.err );
+
+// Using an explicit optime on the new client should work if the optime is earlier than the
+// secondary was locked
+var gleOpTimeBefore = { getLastError : true, w : 2, wOpTime : opTimeBeforeFailure };
+gleObj = newClientConn.getCollection( coll.toString() ).getDB().runCommand( gleOpTimeBefore );
+assert.eq( null, gleObj.err );
+
+// Using an explicit optime on the new client should not work if the optime is later than the
+// secondary was locked
+var gleOpTimeAfter = { getLastError : true, w : 2, wtimeout : 1000, wOpTime : opTimeAfterFailure };
+gleObj = newClientConn.getCollection( coll.toString() ).getDB().runCommand( gleOpTimeAfter );
+assert.neq( null, gleObj.err );
+assert( gleObj.wtimeout );
+
+jsTest.log("DONE!");
+
+// Unlock the secondary
+secondary.getDB("admin").fsyncUnlock();
+rst.stopSet();
diff --git a/jstests/sharding/dbhash_cache.js b/jstests/sharding/dbhash_cache.js
new file mode 100644
index 00000000000..7ba95361cf2
--- /dev/null
+++ b/jstests/sharding/dbhash_cache.js
@@ -0,0 +1,38 @@
+/**
+* Test that split/move chunk update the dbhash on the config server
+*/
+
+st = new ShardingTest({ name: "dbhash", shards : 2, mongos : 2, verbose : 2, other: {separateConfig : 1 } });
+st.stopBalancer();
+
+var mongos = st.s0;
+var shards = mongos.getCollection( "config.shards" ).find().toArray();
+var admin = mongos.getDB( "admin" );
+var configs = st._configServers
+
+assert(admin.runCommand({ enablesharding : "test" }).ok);
+printjson(admin.runCommand({ movePrimary : "test", to : shards[0]._id }));
+assert(admin.runCommand({ shardcollection : "test.foo" , key : { x : 1 } }).ok);
+
+mongos.getCollection("test.foo").insert({x:1});
+assert.eq(1, st.config.chunks.count(), "there should only be 1 chunk")
+
+var dbhash1 = configs[0].getDB("config").runCommand( "dbhash");
+printjson("dbhash before split and move is " + dbhash1.collections.chunks);
+
+// split the chunk and move one chunk to the non-primary shard
+assert(admin.runCommand({ split : "test.foo", middle : { x : 0 } }).ok);
+assert( admin.runCommand({ moveChunk : "test.foo",
+ find : { x : 0 },
+ to : shards[1]._id,
+ _waitForDelete : true }).ok );
+
+st.printShardingStatus();
+assert.eq(2, st.config.chunks.count(), "there should be 2 chunks")
+
+var dbhash2 = configs[0].getDB("config").runCommand("dbhash");
+printjson("dbhash after split and move is " + dbhash2.collections.chunks);
+
+assert.neq(dbhash1.collections.chunks, dbhash2.collections.chunks, "The hash should be different after split and move." )
+
+st.stop();
diff --git a/jstests/sharding/forget_mr_temp_ns.js b/jstests/sharding/forget_mr_temp_ns.js
new file mode 100644
index 00000000000..54eeb88d9b5
--- /dev/null
+++ b/jstests/sharding/forget_mr_temp_ns.js
@@ -0,0 +1,46 @@
+//
+// Tests whether we forget M/R's temporary namespaces for sharded output
+//
+
+var options = { separateConfig : true };
+
+var st = new ShardingTest({ shards : 1, mongos : 1, other : options });
+
+var mongos = st.s0;
+var admin = mongos.getDB( "admin" );
+var coll = mongos.getCollection( "foo.bar" );
+var outputColl = mongos.getCollection( (coll.getDB() + "") + ".mrOutput" );
+
+for ( var i = 0; i < 10; i++ ) {
+ coll.insert({ _id : i, even : (i % 2 == 0) });
+}
+assert.eq( null, coll.getDB().getLastError() );
+
+var map = function() { emit( this.even, 1 ); };
+var reduce = function( key, values ) { return Array.sum(values); };
+
+out = coll.mapReduce( map, reduce, { out: { reduce : outputColl.getName(), sharded: true } } );
+
+printjson( out );
+printjson( outputColl.find().toArray() );
+
+var mongodThreadStats = st.shard0.getDB( "admin" ).runCommand({ shardConnPoolStats : 1 }).threads;
+var mongosThreadStats = admin.runCommand({ shardConnPoolStats : 1 }).threads;
+
+printjson( mongodThreadStats );
+printjson( mongosThreadStats );
+
+var checkForSeenNS = function( threadStats, regex ) {
+ for ( var i = 0; i < threadStats.length; i++ ) {
+ var seenNSes = threadStats[i].seenNS;
+ for ( var j = 0; j < seenNSes.length; j++ ) {
+ assert( !( regex.test( seenNSes ) ) );
+ }
+ }
+}
+
+checkForSeenNS( mongodThreadStats, /^foo.tmp/ );
+checkForSeenNS( mongosThreadStats, /^foo.tmp/ );
+
+st.stop();
+
diff --git a/jstests/sharding/mongos_rs_auth_shard_failure_tolerance.js b/jstests/sharding/mongos_rs_auth_shard_failure_tolerance.js
new file mode 100644
index 00000000000..007f60f6d2e
--- /dev/null
+++ b/jstests/sharding/mongos_rs_auth_shard_failure_tolerance.js
@@ -0,0 +1,341 @@
+//
+// Tests mongos's failure tolerance for authenticated replica set shards and slaveOk queries
+//
+// Sets up a cluster with three shards, the first shard of which has an unsharded collection and
+// half a sharded collection. The second shard has the second half of the sharded collection, and
+// the third shard has nothing. The primary of the unsharded database is on the first shard, and
+// the primary of the sharded database is on the second shard. Progressively shuts down the shards
+// to test the impact on the cluster with user authentication.
+//
+// Three different connection states are tested - active (connection is active through whole
+// sequence), idle (connection is connected but not used before a shard change), and new
+// (connection connected after shard change).
+//
+
+var options = { separateConfig : true,
+ rs : true,
+ rsOptions : { nodes : 2 },
+ keyFile : "jstests/libs/key1" };
+
+var st = new ShardingTest({shards : 3, mongos : 1, other : options});
+st.stopBalancer();
+
+var mongos = st.s0;
+var admin = mongos.getDB( "admin" );
+var shards = mongos.getDB( "config" ).shards.find().toArray();
+
+assert.commandWorked( admin.runCommand({ setParameter : 1, traceExceptions : true }) );
+assert.commandWorked( admin.runCommand({ setParameter : 1, ignoreInitialVersionFailure : true }) );
+assert.commandWorked( admin.runCommand({ setParameter : 1, authOnPrimaryOnly : false }) );
+
+var collSharded = mongos.getCollection( "fooSharded.barSharded" );
+var collUnsharded = mongos.getCollection( "fooUnsharded.barUnsharded" );
+
+// Create the unsharded database with shard0 primary
+collUnsharded.insert({ some : "doc" });
+assert.eq( null, collUnsharded.getDB().getLastError() );
+collUnsharded.remove({});
+assert.eq( null, collUnsharded.getDB().getLastError() );
+printjson( admin.runCommand({ movePrimary : collUnsharded.getDB().toString(), to : shards[0]._id }) );
+
+// Create the sharded database with shard1 primary
+assert.commandWorked( admin.runCommand({ enableSharding : collSharded.getDB().toString() }) );
+printjson( admin.runCommand({ movePrimary : collSharded.getDB().toString(), to : shards[1]._id }) );
+assert.commandWorked( admin.runCommand({ shardCollection : collSharded.toString(),
+ key : { _id : 1 } }) );
+assert.commandWorked( admin.runCommand({ split : collSharded.toString(), middle : { _id : 0 } }) );
+assert.commandWorked( admin.runCommand({ moveChunk : collSharded.toString(),
+ find : { _id : -1 },
+ to : shards[0]._id }) );
+
+st.printShardingStatus();
+
+var adminUser = "adminUser";
+var shardedDBUser = "shardedDBUser";
+var unshardedDBUser = "unshardedDBUser";
+var password = "password";
+
+jsTest.log("Setting up initial admin user...");
+
+// Create a user
+admin.addUser({ user : adminUser, pwd : password, roles: [ "userAdminAnyDatabase" ] });
+
+// There's an admin user now, so we need to login to do anything
+
+// Login as admin user
+admin.auth(adminUser, password);
+
+jsTest.log("Setting up database users...");
+
+// Create db users
+collSharded.getDB().addUser({ user : shardedDBUser,
+ pwd : password, roles : [ "readWrite" ] });
+collUnsharded.getDB().addUser({ user : unshardedDBUser,
+ pwd : password, roles : [ "readWrite" ] });
+
+admin.logout();
+
+function authDBUsers( conn ) {
+ conn.getDB( collSharded.getDB().toString() ).auth(shardedDBUser, password);
+ conn.getDB( collUnsharded.getDB().toString() ).auth(unshardedDBUser, password);
+ return conn;
+}
+
+function authUnshardedUser( conn ) {
+ conn.getDB( collUnsharded.getDB().toString() ).auth(unshardedDBUser, password);
+ return conn;
+}
+
+// Needed b/c the GLE command itself can fail if the shard is down ("write result unknown") - we
+// don't care if this happens in this test, we only care that we did not get "write succeeded".
+// Depending on the connection pool state, we could get either.
+function gleErrorOrThrow(database, msg) {
+ var gle;
+ try {
+ gle = database.getLastErrorObj();
+ }
+ catch (ex) {
+ return;
+ }
+ if (!gle.err) doassert("getLastError is null: " + tojson(gle) + " :" + msg);
+ return;
+};
+
+//
+// Setup is complete
+//
+
+jsTest.log("Inserting initial data...");
+
+var mongosConnActive = authDBUsers( new Mongo( mongos.host ) );
+authDBUsers(mongosConnActive);
+var mongosConnIdle = null;
+var mongosConnNew = null;
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -1 });
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 1 });
+assert.eq(null, mongosConnActive.getCollection( collSharded.toString() ).getDB().getLastError());
+
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 1 });
+assert.eq(null, mongosConnActive.getCollection( collUnsharded.toString() ).getDB().getLastError());
+
+jsTest.log("Stopping primary of third shard...");
+
+mongosConnIdle = authDBUsers( new Mongo( mongos.host ) );
+
+st.rs2.stop(st.rs2.getPrimary(), true ); // wait for stop
+
+jsTest.log("Testing active connection with third primary down...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -2 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 2 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 2 });
+assert.gleSuccess(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with third primary down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with third primary down...");
+
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -4 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 4 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 4 });
+assert.gleSuccess(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping primary of second shard...");
+
+mongosConnActive.setSlaveOk();
+mongosConnIdle = authDBUsers( new Mongo( mongos.host ) );
+mongosConnIdle.setSlaveOk();
+
+// Need to save this node for later
+var rs1Secondary = st.rs1.getSecondary();
+
+st.rs1.stop(st.rs1.getPrimary(), true ); // wait for stop
+
+jsTest.log("Testing active connection with second primary down...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -5 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 5 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 5 });
+assert.gleSuccess(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with second primary down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -6 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 6 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 6 });
+assert.gleSuccess(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with second primary down...");
+
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -7 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 7 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 7 });
+assert.gleSuccess(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping primary of first shard...");
+
+mongosConnActive.setSlaveOk();
+mongosConnIdle = authDBUsers( new Mongo( mongos.host ) );
+mongosConnIdle.setSlaveOk();
+
+st.rs0.stop(st.rs0.getPrimary(), true ); // wait for stop
+
+jsTest.log("Testing active connection with first primary down...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with first primary down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with first primary down...");
+
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = authDBUsers( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping second shard...");
+
+mongosConnActive.setSlaveOk();
+mongosConnIdle = authDBUsers( new Mongo( mongos.host ) );
+mongosConnIdle.setSlaveOk();
+
+st.rs1.stop(rs1Secondary, true ); // wait for stop
+
+jsTest.log("Testing active connection with second shard down...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with second shard down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with second shard down...");
+
+// Note that this would fail for the sharded database with a primary on the second shard
+
+mongosConnNew = authUnshardedUser( new Mongo( mongos.host ) );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = authUnshardedUser( new Mongo( mongos.host ) );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("DONE!");
+st.stop();
diff --git a/jstests/sharding/mongos_rs_shard_failure_tolerance.js b/jstests/sharding/mongos_rs_shard_failure_tolerance.js
new file mode 100644
index 00000000000..61743d1b450
--- /dev/null
+++ b/jstests/sharding/mongos_rs_shard_failure_tolerance.js
@@ -0,0 +1,460 @@
+//
+// Tests mongos's failure tolerance for replica set shards and read preference queries
+//
+// Sets up a cluster with three shards, the first shard of which has an unsharded collection and
+// half a sharded collection. The second shard has the second half of the sharded collection, and
+// the third shard has nothing. Progressively shuts down the primary of each shard to see the
+// impact on the cluster.
+//
+// Three different connection states are tested - active (connection is active through whole
+// sequence), idle (connection is connected but not used before a shard change), and new
+// (connection connected after shard change).
+//
+
+var options = {separateConfig : true,
+ rs : true,
+ rsOptions : { nodes : 2 }};
+
+var st = new ShardingTest({shards : 3, mongos : 1, other : options});
+st.stopBalancer();
+
+var mongos = st.s0;
+var admin = mongos.getDB( "admin" );
+var shards = mongos.getDB( "config" ).shards.find().toArray();
+
+assert.commandWorked( admin.runCommand({ setParameter : 1, traceExceptions : true }) );
+assert.commandWorked( admin.runCommand({ setParameter : 1, ignoreInitialVersionFailure : true }) );
+assert.commandWorked( admin.runCommand({ setParameter : 1, authOnPrimaryOnly : false }) );
+
+var collSharded = mongos.getCollection( "fooSharded.barSharded" );
+var collUnsharded = mongos.getCollection( "fooUnsharded.barUnsharded" );
+
+// Create the unsharded database
+collUnsharded.insert({ some : "doc" });
+assert.eq( null, collUnsharded.getDB().getLastError() );
+collUnsharded.remove({});
+assert.eq( null, collUnsharded.getDB().getLastError() );
+printjson( admin.runCommand({ movePrimary : collUnsharded.getDB().toString(),
+ to : shards[0]._id }) );
+
+// Create the sharded database
+assert.commandWorked( admin.runCommand({ enableSharding : collSharded.getDB().toString() }) );
+printjson( admin.runCommand({ movePrimary : collSharded.getDB().toString(), to : shards[0]._id }) );
+assert.commandWorked( admin.runCommand({ shardCollection : collSharded.toString(),
+ key : { _id : 1 } }) );
+assert.commandWorked( admin.runCommand({ split : collSharded.toString(), middle : { _id : 0 } }) );
+assert.commandWorked( admin.runCommand({ moveChunk : collSharded.toString(),
+ find : { _id : 0 },
+ to : shards[1]._id }) );
+
+st.printShardingStatus();
+
+// Needed b/c the GLE command itself can fail if the shard is down ("write result unknown") - we
+// don't care if this happens in this test, we only care that we did not get "write succeeded".
+// Depending on the connection pool state, we could get either.
+function gleErrorOrThrow(database, msg) {
+ var gle;
+ try {
+ gle = database.getLastErrorObj();
+ }
+ catch (ex) {
+ return;
+ }
+ if (!gle.err) doassert("getLastError is null: " + tojson(gle) + " :" + msg);
+ return;
+};
+
+//
+// Setup is complete
+//
+
+jsTest.log("Inserting initial data...");
+
+var mongosConnActive = new Mongo( mongos.host );
+var mongosConnIdle = null;
+var mongosConnNew = null;
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -1 });
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 1 });
+assert.eq(null, mongosConnActive.getCollection( collSharded.toString() ).getDB().getLastError());
+
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 1 });
+assert.eq(null, mongosConnActive.getCollection( collUnsharded.toString() ).getDB().getLastError());
+
+jsTest.log("Stopping primary of third shard...");
+
+mongosConnIdle = new Mongo( mongos.host );
+
+st.rs2.stop(st.rs2.getPrimary(), true /*wait for stop*/ );
+
+jsTest.log("Testing active connection with third primary down...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -2 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 2 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 2 });
+assert.gleSuccess(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with third primary down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with third primary down...");
+
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -4 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 4 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 4 });
+assert.gleSuccess(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping primary of second shard...");
+
+mongosConnIdle = new Mongo( mongos.host );
+
+// Need to save this node for later
+var rs1Secondary = st.rs1.getSecondary();
+
+st.rs1.stop(st.rs1.getPrimary(), true /* wait for stop */);
+
+jsTest.log("Testing active connection with second primary down...");
+
+// Reads with read prefs
+mongosConnActive.setSlaveOk();
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+mongosConnActive.setSlaveOk(false);
+
+mongosConnActive.setReadPref("primary");
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.throws(function() {
+ mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 });
+});
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+//Ensure read prefs override slaveOK
+mongosConnActive.setSlaveOk();
+mongosConnActive.setReadPref("primary");
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.throws(function() {
+ mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 });
+});
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+mongosConnActive.setSlaveOk(false);
+
+mongosConnActive.setReadPref("secondary");
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.setReadPref("primaryPreferred");
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.setReadPref("secondaryPreferred");
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.setReadPref("nearest");
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+// Writes
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -5 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 5 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 5 });
+assert.gleSuccess(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with second primary down...");
+
+// Writes
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -6 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 6 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 6 });
+assert.gleSuccess(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+// Reads with read prefs
+mongosConnIdle.setSlaveOk();
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+mongosConnIdle.setSlaveOk(false);
+
+mongosConnIdle.setReadPref("primary");
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.throws(function() {
+ mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 });
+});
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+// Ensure read prefs override slaveOK
+mongosConnIdle.setSlaveOk();
+mongosConnIdle.setReadPref("primary");
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.throws(function() {
+ mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 });
+});
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+mongosConnIdle.setSlaveOk(false);
+
+mongosConnIdle.setReadPref("secondary");
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnIdle.setReadPref("primaryPreferred");
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnIdle.setReadPref("secondaryPreferred");
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnIdle.setReadPref("nearest");
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+jsTest.log("Testing new connections with second primary down...");
+
+// Reads with read prefs
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("primary");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("primary");
+assert.throws(function() {
+ mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 });
+});
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("primary");
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+// Ensure read prefs override slaveok
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+mongosConnNew.setReadPref("primary");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+mongosConnNew.setReadPref("primary");
+assert.throws(function() {
+ mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 });
+});
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+mongosConnNew.setReadPref("primary");
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("secondary");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("secondary");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("secondary");
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("primaryPreferred");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("primaryPreferred");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("primaryPreferred");
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("secondaryPreferred");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("secondaryPreferred");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("secondaryPreferred");
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("nearest");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("nearest");
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setReadPref("nearest");
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+// Writes
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -7 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 7 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 7 });
+assert.gleSuccess(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping primary of first shard...");
+
+mongosConnIdle = new Mongo( mongos.host );
+
+st.rs0.stop(st.rs0.getPrimary(), true /*wait for stop*/ );
+
+jsTest.log("Testing active connection with first primary down...");
+
+mongosConnActive.setSlaveOk();
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 8 });
+gleErrorOrThrow(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with first primary down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 9 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+mongosConnIdle.setSlaveOk();
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with first primary down...");
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : 1 }) );
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 10 });
+gleErrorOrThrow(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping second shard...");
+
+mongosConnIdle = new Mongo( mongos.host );
+
+st.rs1.stop(rs1Secondary, true /* wait for stop */);
+
+jsTest.log("Testing active connection with second shard down...");
+
+mongosConnActive.setSlaveOk();
+assert.neq(null, mongosConnActive.getCollection( collSharded.toString() ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : -11 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collSharded.toString() ).insert({ _id : 11 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded.toString() ).getDB());
+mongosConnActive.getCollection( collUnsharded.toString() ).insert({ _id : 11 });
+gleErrorOrThrow(mongosConnActive.getCollection( collUnsharded.toString() ).getDB());
+
+jsTest.log("Testing idle connection with second shard down...");
+
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : -12 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collSharded.toString() ).insert({ _id : 12 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded.toString() ).getDB());
+mongosConnIdle.getCollection( collUnsharded.toString() ).insert({ _id : 12 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collUnsharded.toString() ).getDB());
+
+mongosConnIdle.setSlaveOk();
+assert.neq(null, mongosConnIdle.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections with second shard down...");
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collSharded.toString() ).findOne({ _id : -1 }) );
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.setSlaveOk();
+assert.neq(null, mongosConnNew.getCollection( collUnsharded.toString() ).findOne({ _id : 1 }) );
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : -13 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded.toString() ).insert({ _id : 13 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded.toString() ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collUnsharded.toString() ).insert({ _id : 13 });
+gleErrorOrThrow(mongosConnNew.getCollection( collUnsharded.toString() ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("DONE!");
+st.stop();
diff --git a/jstests/sharding/mongos_shard_failure_tolerance.js b/jstests/sharding/mongos_shard_failure_tolerance.js
new file mode 100644
index 00000000000..9f5ffefca28
--- /dev/null
+++ b/jstests/sharding/mongos_shard_failure_tolerance.js
@@ -0,0 +1,186 @@
+//
+// Tests mongos's failure tolerance for single-node shards
+//
+//
+// Sets up a cluster with three shards, the first shard of which has an unsharded collection and
+// half a sharded collection. The second shard has the second half of the sharded collection, and
+// the third shard has nothing. Progressively shuts down each shard to see the impact on the
+// cluster.
+//
+// Three different connection states are tested - active (connection is active through whole
+// sequence), idle (connection is connected but not used before a shard change), and new
+// (connection connected after shard change).
+//
+
+var options = {separateConfig : true};
+var st = new ShardingTest({shards : 3, mongos : 1, other : options});
+st.stopBalancer();
+
+var mongos = st.s0;
+var admin = mongos.getDB( "admin" );
+var shards = mongos.getDB( "config" ).shards.find().toArray();
+
+assert.commandWorked( admin.runCommand({ setParameter : 1, traceExceptions : true }) );
+assert.commandWorked( admin.runCommand({ setParameter : 1, ignoreInitialVersionFailure : true }) );
+
+var collSharded = mongos.getCollection( "fooSharded.barSharded" );
+var collUnsharded = mongos.getCollection( "fooUnsharded.barUnsharded" );
+
+assert.commandWorked( admin.runCommand({ enableSharding : collSharded.getDB() + "" }) );
+printjson( admin.runCommand({ movePrimary : collSharded.getDB() + "", to : shards[0]._id }) );
+assert.commandWorked( admin.runCommand({ shardCollection : collSharded + "", key : { _id : 1 } }) );
+assert.commandWorked( admin.runCommand({ split : collSharded + "", middle : { _id : 0 } }) );
+assert.commandWorked( admin.runCommand({ moveChunk : collSharded + "",
+ find : { _id : 0 },
+ to : shards[1]._id }) );
+
+// Create the unsharded database
+collUnsharded.insert({ some : "doc" });
+assert.eq( null, collUnsharded.getDB().getLastError() );
+collUnsharded.remove({});
+assert.eq( null, collUnsharded.getDB().getLastError() );
+printjson( admin.runCommand({ movePrimary : collUnsharded.getDB() + "", to : shards[0]._id }) );
+
+st.printShardingStatus();
+
+// Needed b/c the GLE command itself can fail if the shard is down ("write result unknown") - we
+// don't care if this happens in this test, we only care that we did not get "write succeeded".
+// Depending on the connection pool state, we could get either.
+function gleErrorOrThrow(database, msg) {
+ var gle;
+ try {
+ gle = database.getLastErrorObj();
+ }
+ catch (ex) {
+ return;
+ }
+ if (!gle.err) doassert("getLastError is null: " + tojson(gle) + " :" + msg);
+ return;
+};
+
+//
+// Setup is complete
+//
+
+jsTest.log("Inserting initial data...");
+
+var mongosConnActive = new Mongo( mongos.host );
+var mongosConnIdle = null;
+var mongosConnNew = null;
+
+mongosConnActive.getCollection( collSharded + "" ).insert({ _id : -1 });
+mongosConnActive.getCollection( collSharded + "" ).insert({ _id : 1 });
+assert.eq(null, mongosConnActive.getCollection( collSharded + "" ).getDB().getLastError());
+
+mongosConnActive.getCollection( collUnsharded + "" ).insert({ _id : 1 });
+assert.eq(null, mongosConnActive.getCollection( collUnsharded + "" ).getDB().getLastError());
+
+jsTest.log("Stopping third shard...");
+
+mongosConnIdle = new Mongo( mongos.host );
+
+MongoRunner.stopMongod( st.shard2 );
+
+jsTest.log("Testing active connection...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded + "" ).findOne({ _id : -1 }));
+assert.neq(null, mongosConnActive.getCollection( collSharded + "" ).findOne({ _id : 1 }));
+assert.neq(null, mongosConnActive.getCollection( collUnsharded + "" ).findOne({ _id : 1 }));
+
+mongosConnActive.getCollection( collSharded + "" ).insert({ _id : -2 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded + "" ).getDB());
+mongosConnActive.getCollection( collSharded + "" ).insert({ _id : 2 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded + "" ).getDB());
+mongosConnActive.getCollection( collUnsharded + "" ).insert({ _id : 2 });
+assert.gleSuccess(mongosConnActive.getCollection( collUnsharded + "" ).getDB());
+
+jsTest.log("Testing idle connection...");
+
+mongosConnIdle.getCollection( collSharded + "" ).insert({ _id : -3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded + "" ).getDB());
+mongosConnIdle.getCollection( collSharded + "" ).insert({ _id : 3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded + "" ).getDB());
+mongosConnIdle.getCollection( collUnsharded + "" ).insert({ _id : 3 });
+assert.gleSuccess(mongosConnIdle.getCollection( collUnsharded + "" ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded + "" ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collSharded + "" ).findOne({ _id : 1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded + "" ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections...");
+
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collSharded + "" ).findOne({ _id : -1 }) );
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collSharded + "" ).findOne({ _id : 1 }) );
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collUnsharded + "" ).findOne({ _id : 1 }) );
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded + "" ).insert({ _id : -4 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded + "" ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded + "" ).insert({ _id : 4 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded + "" ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collUnsharded + "" ).insert({ _id : 4 });
+assert.gleSuccess(mongosConnNew.getCollection( collUnsharded + "" ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("Stopping second shard...");
+
+mongosConnIdle = new Mongo( mongos.host );
+
+MongoRunner.stopMongod( st.shard1 );
+
+jsTest.log("Testing active connection...");
+
+assert.neq(null, mongosConnActive.getCollection( collSharded + "" ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnActive.getCollection( collUnsharded + "" ).findOne({ _id : 1 }) );
+
+mongosConnActive.getCollection( collSharded + "" ).insert({ _id : -5 });
+assert.gleSuccess(mongosConnActive.getCollection( collSharded + "" ).getDB());
+mongosConnActive.getCollection( collSharded + "" ).insert({ _id : 5 });
+gleErrorOrThrow(mongosConnActive.getCollection( collSharded + "" ).getDB());
+mongosConnActive.getCollection( collUnsharded + "" ).insert({ _id : 5 });
+assert.gleSuccess(mongosConnActive.getCollection( collUnsharded + "" ).getDB());
+
+jsTest.log("Testing idle connection...");
+
+mongosConnIdle.getCollection( collSharded + "" ).insert({ _id : -6 });
+assert.gleSuccess(mongosConnIdle.getCollection( collSharded + "" ).getDB());
+mongosConnIdle.getCollection( collSharded + "" ).insert({ _id : 6 });
+gleErrorOrThrow(mongosConnIdle.getCollection( collSharded + "" ).getDB());
+mongosConnIdle.getCollection( collUnsharded + "" ).insert({ _id : 6 });
+assert.gleSuccess(mongosConnIdle.getCollection( collUnsharded + "" ).getDB());
+
+assert.neq(null, mongosConnIdle.getCollection( collSharded + "" ).findOne({ _id : -1 }) );
+assert.neq(null, mongosConnIdle.getCollection( collUnsharded + "" ).findOne({ _id : 1 }) );
+
+jsTest.log("Testing new connections...");
+
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collSharded + "" ).findOne({ _id : -1 }) );
+mongosConnNew = new Mongo( mongos.host );
+assert.neq(null, mongosConnNew.getCollection( collUnsharded + "" ).findOne({ _id : 1 }) );
+
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded + "" ).insert({ _id : -7 });
+assert.gleSuccess(mongosConnNew.getCollection( collSharded + "" ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collSharded + "" ).insert({ _id : 7 });
+gleErrorOrThrow(mongosConnNew.getCollection( collSharded + "" ).getDB());
+mongosConnNew = new Mongo( mongos.host );
+mongosConnNew.getCollection( collUnsharded + "" ).insert({ _id : 7 });
+assert.gleSuccess(mongosConnNew.getCollection( collUnsharded + "" ).getDB());
+
+gc(); // Clean up new connections
+
+jsTest.log("DONE!");
+st.stop();
+
+
+
+
+
diff --git a/jstests/slowNightly/sharding_passthrough.js b/jstests/slowNightly/sharding_passthrough.js
index 56f60d23a51..67db88b7b0d 100644
--- a/jstests/slowNightly/sharding_passthrough.js
+++ b/jstests/slowNightly/sharding_passthrough.js
@@ -70,6 +70,7 @@ files.forEach(function(x) {
'copydb-auth|' +
'profile\\d*|' +
'dbhash|' +
+ 'dbhash2|' +
'median|' +
'apitest_dbcollection|' +
'evalb|' +
diff --git a/jstests/slowNightly/ttl1.js b/jstests/slowNightly/ttl1.js
index f795d8bc0ff..50dae5670e3 100644
--- a/jstests/slowNightly/ttl1.js
+++ b/jstests/slowNightly/ttl1.js
@@ -1,21 +1,33 @@
/**
* Part 1: Simple test of TTL. Create a new collection with 24 docs, with timestamps at one hour
* intervals, from now-minus-23 hours ago until now. Also add some docs with non-date
- * values. Then create a TTL index that expires all docs older than ~5.5 hours (20000
- * seconds). Wait 70 seconds (TTL monitor runs every 60) and check that 18 docs deleted.
- * Part 2: Add a second TTL index on an identical field. The second index expires docs older than
+ * values. Then create a TTL index that expires all docs older than a string. Wait 70
+ * seconds (TTL monitor runs every 60) and check that no documents were deleted.
+ * Part 2: Add a second TTL index that expires all docs older than ~5.5 hours (20000
+ * seconds). Wait 70 seconds and check that 18 docs deleted.
+ * Part 3: Add a third TTL index on an identical field. The second index expires docs older than
* ~2.8 hours (10000 seconds). Wait 70 seconds and check that 3 more docs deleted.
*/
+assertEntryMatches = function(array, regex) {
+ var found = false;
+ for (i=0; i<array.length; i++) {
+ if (regex.test(array[i])) {
+ found = true;
+ }
+ }
+ assert(found,
+ "The regex: " + regex + " did not match any entries in the array: " + array.join('\n'));
+}
// Part 1
var t = db.ttl1;
t.drop();
var now = (new Date()).getTime();
-for ( i=0; i<24; i++ ){
- var past = new Date( now - ( 3600 * 1000 * i ) );
- t.insert( { x : past , y : past } );
+for (i=0; i<24; i++) {
+ var past = new Date(now - (3600 * 1000 * i));
+ t.insert({x: past, y: past, z: past});
}
t.insert( { a : 1 } ) //no x value
t.insert( { x: null } ) //non-date value
@@ -27,6 +39,18 @@ db.getLastError();
assert.eq( 30 , t.count() );
+t.ensureIndex( { z : 1 } , { expireAfterSeconds : "20000" } );
+
+sleep(70 * 1000);
+
+assert.eq(t.count(), 30);
+
+var loggedWarning = false;
+var log = db.adminCommand({getLog: "global"}).log;
+var msg = RegExp("ttl indexes require the expireAfterSeconds" +
+ " field to be numeric but received a type of:");
+assertEntryMatches(log, msg);
+// Part 2
t.ensureIndex( { x : 1 } , { expireAfterSeconds : 20000 } );
assert.soon(
@@ -41,7 +65,7 @@ assert.eq( 12 , t.count() );
assert.lte( 18, db.serverStatus().metrics.ttl.deletedDocuments );
assert.lte( 1, db.serverStatus().metrics.ttl.passes );
-// Part 2
+// Part 3
t.ensureIndex( { y : 1 } , { expireAfterSeconds : 10000 } );
assert.soon(
diff --git a/rpm/mongo.spec b/rpm/mongo.spec
index 4b8185a0055..d8a9a121bed 100755
--- a/rpm/mongo.spec
+++ b/rpm/mongo.spec
@@ -1,7 +1,7 @@
Name: mongo-10gen
Conflicts: mongo, mongo-10gen-unstable
Obsoletes: mongo-stable
-Version: 2.4.6
+Version: 2.4.8
Release: mongodb_1%{?dist}
Summary: mongo client shell and tools
License: AGPL 3.0
diff --git a/src/mongo/SConscript b/src/mongo/SConscript
index 816a6bfacf8..265cd4800e0 100644
--- a/src/mongo/SConscript
+++ b/src/mongo/SConscript
@@ -433,6 +433,7 @@ serverOnlyFiles = [ "db/curop.cpp",
"db/dbcommands_admin.cpp",
# most commands are only for mongod
+ "db/commands/dbhash.cpp",
"db/commands/fsync.cpp",
"db/commands/distinct.cpp",
"db/commands/find_and_modify.cpp",
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err
index 1dfa1702b64..5fa8cde17e2 100644
--- a/src/mongo/base/error_codes.err
+++ b/src/mongo/base/error_codes.err
@@ -27,4 +27,7 @@ error_code("AlreadyInitialized", 23)
error_code("LockTimeout", 24)
error_code("RemoteValidationError", 25)
+# Non-sequential error codes (for compatibility only)
+error_code("NodeNotFound", 74)
+
error_class("NetworkError", ["HostUnreachable", "HostNotFound"])
diff --git a/src/mongo/client/dbclient_rs.cpp b/src/mongo/client/dbclient_rs.cpp
index 21a5b0810f4..cc82c2aa948 100644
--- a/src/mongo/client/dbclient_rs.cpp
+++ b/src/mongo/client/dbclient_rs.cpp
@@ -126,54 +126,6 @@ namespace mongo {
} _populateReadPrefSecOkCmdList;
/**
- * @param ns the namespace of the query.
- * @param queryOptionFlags the flags for the query.
- * @param queryObj the query object to check.
- *
- * @return true if the given query can be sent to a secondary node without taking the
- * slaveOk flag into account.
- */
- bool _isQueryOkToSecondary(const string& ns, int queryOptionFlags, const BSONObj& queryObj) {
- if (queryOptionFlags & QueryOption_SlaveOk) {
- return true;
- }
-
- if (!Query::hasReadPreference(queryObj)) {
- return false;
- }
-
- if (ns.find(".$cmd") == string::npos) {
- return true;
- }
-
- BSONObj actualQueryObj;
- if (strcmp(queryObj.firstElement().fieldName(), "query") == 0) {
- actualQueryObj = queryObj["query"].embeddedObject();
- }
- else {
- actualQueryObj = queryObj;
- }
-
- const string cmdName = actualQueryObj.firstElementFieldName();
- if (_secOkCmdList.count(cmdName) == 1) {
- return true;
- }
-
- if (cmdName == "mapReduce" || cmdName == "mapreduce") {
- if (!actualQueryObj.hasField("out")) {
- return false;
- }
-
- BSONElement outElem(actualQueryObj["out"]);
- if (outElem.isABSONObj() && outElem["inline"].trueValue()) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
* Selects the right node given the nodes to pick from and the preference.
* This method does strict tag matching, and will not implicitly fallback
* to matching anything.
@@ -275,15 +227,18 @@ namespace mongo {
*
* @param query the raw query document
*
- * @return the read preference setting. If the tags field was not present, it will contain one
- * empty tag document {} which matches any tag.
+ * @return the read preference setting if a read preference exists, otherwise the default read
+ * preference of Primary_Only. If the tags field was not present, it will contain one
+ * empty tag document {} which matches any tag.
*
* @throws AssertionException if the read preference object is malformed
*/
- ReadPreferenceSetting* _extractReadPref(const BSONObj& query) {
- ReadPreference pref = mongo::ReadPreference_SecondaryPreferred;
+ ReadPreferenceSetting* _extractReadPref(const BSONObj& query, int queryOptions) {
if (Query::hasReadPreference(query)) {
+
+ ReadPreference pref = mongo::ReadPreference_SecondaryPreferred;
+
BSONElement readPrefElement;
if (query.hasField(Query::ReadPrefField.name())) {
@@ -334,9 +289,17 @@ namespace mongo {
return new ReadPreferenceSetting(pref, tags);
}
+ else {
+ TagSet tags(BSON_ARRAY(BSONObj()));
+ return new ReadPreferenceSetting(pref, tags);
+ }
}
+ // Default read pref is primary only or secondary preferred with slaveOK
TagSet tags(BSON_ARRAY(BSONObj()));
+ ReadPreference pref =
+ queryOptions & QueryOption_SlaveOk ?
+ mongo::ReadPreference_SecondaryPreferred : mongo::ReadPreference_PrimaryOnly;
return new ReadPreferenceSetting(pref, tags);
}
@@ -405,7 +368,6 @@ namespace mongo {
// delete ReplicaSetMonitors from ReplicaSetMonitor::remove.
ReplicaSetMonitor::~ReplicaSetMonitor() {
scoped_lock lk ( _lock );
- log() << "deleting replica set monitor for: " << _getServerAddress_inlock() << endl;
_cacheServerAddresses_inlock();
pool.removeHost( _getServerAddress_inlock() );
_nodes.clear();
@@ -1501,6 +1463,55 @@ namespace mongo {
return rsm->getServerAddress();
}
+ // Internal implementation of isSecondaryQuery, takes previously-parsed read preference
+ static bool _isSecondaryQuery( const string& ns,
+ const BSONObj& queryObj,
+ const ReadPreferenceSetting& readPref ) {
+
+ // If the read pref is primary only, this is not a secondary query
+ if (readPref.pref == ReadPreference_PrimaryOnly) return false;
+
+ if (ns.find(".$cmd") == string::npos) {
+ return true;
+ }
+
+ // This is a command with secondary-possible read pref
+ // Only certain commands are supported for secondary operation.
+
+ BSONObj actualQueryObj;
+ if (strcmp(queryObj.firstElement().fieldName(), "query") == 0) {
+ actualQueryObj = queryObj["query"].embeddedObject();
+ }
+ else {
+ actualQueryObj = queryObj;
+ }
+
+ const string cmdName = actualQueryObj.firstElementFieldName();
+ if (_secOkCmdList.count(cmdName) == 1) {
+ return true;
+ }
+
+ if (cmdName == "mapReduce" || cmdName == "mapreduce") {
+ if (!actualQueryObj.hasField("out")) {
+ return false;
+ }
+
+ BSONElement outElem(actualQueryObj["out"]);
+ if (outElem.isABSONObj() && outElem["inline"].trueValue()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool DBClientReplicaSet::isSecondaryQuery( const string& ns,
+ const BSONObj& queryObj,
+ int queryOptions ) {
+ auto_ptr<ReadPreferenceSetting> readPref( _extractReadPref( queryObj, queryOptions ) );
+ return _isSecondaryQuery( ns, queryObj, *readPref );
+ }
+
DBClientConnection * DBClientReplicaSet::checkMaster() {
ReplicaSetMonitorPtr monitor = _getMonitor();
HostAndPort h = monitor->getMaster();
@@ -1593,6 +1604,18 @@ namespace mongo {
return _getMonitor()->isAnyNodeOk();
}
+ void DBClientReplicaSet::authPrimary(const BSONObj& params) {
+ _auth(params);
+ }
+
+ bool DBClientReplicaSet::authPrimary( const string &dbname,
+ const string &username,
+ const string &password_text,
+ string& errmsg,
+ bool digestPassword ) {
+ return auth( dbname, username, password_text, errmsg, digestPassword );
+ }
+
void DBClientReplicaSet::_auth(const BSONObj& params) {
DBClientConnection * m = checkMaster();
@@ -1620,6 +1643,98 @@ namespace mongo {
_auths[params[saslCommandPrincipalSourceFieldName].str()] = params.getOwned();
}
+ bool DBClientReplicaSet::authAny( const string &dbname,
+ const string &username,
+ const string &password_text,
+ string& errmsg,
+ bool digestPassword ) {
+ try {
+ authAny(BSON(saslCommandMechanismFieldName << "MONGODB-CR" <<
+ saslCommandPrincipalSourceFieldName << dbname <<
+ saslCommandPrincipalFieldName << username <<
+ saslCommandPasswordFieldName << password_text <<
+ saslCommandDigestPasswordFieldName << digestPassword));
+ return true;
+ } catch(const UserException& ex) {
+ if (ex.getCode() != ErrorCodes::AuthenticationFailed)
+ throw;
+ errmsg = ex.what();
+ return false;
+ }
+ }
+
+ static bool isAuthenticationException( const DBException& ex ) {
+ return ex.getCode() == ErrorCodes::AuthenticationFailed;
+ }
+
+ void DBClientReplicaSet::authAny( const BSONObj& params ) {
+
+ // We prefer to authenticate against a primary, but otherwise a secondary is ok too
+ // Empty tag matches every secondary
+ TagSet tags(BSON_ARRAY(BSONObj()));
+ shared_ptr<ReadPreferenceSetting> readPref(
+ new ReadPreferenceSetting( ReadPreference_PrimaryPreferred, tags ) );
+
+ LOG(3) << "dbclient_rs authentication of " << _getMonitor()->getName() << endl;
+
+ // NOTE that we retry MAX_RETRY + 1 times, since we're always primary preferred we don't
+ // fallback to the primary.
+ Status lastNodeStatus = Status::OK();
+ for ( size_t retry = 0; retry < MAX_RETRY + 1; retry++ ) {
+ try {
+ DBClientConnection* conn = selectNodeUsingTags( readPref );
+
+ if ( conn == NULL ) {
+ break;
+ }
+
+ conn->auth( params );
+
+ // Cache the new auth information since we've now validated it's good
+ _auths[params[saslCommandPrincipalSourceFieldName].str()] = params.getOwned();
+
+ // Ensure the only child connection open is the one we authenticated against - other
+ // child connections may not have full authentication information.
+ // NOTE: _lastSlaveOkConn may or may not be the same as _master
+ dassert(_lastSlaveOkConn.get() == conn || _master.get() == conn);
+ if ( conn != _lastSlaveOkConn.get() ) {
+ _lastSlaveOkHost = HostAndPort();
+ _lastSlaveOkConn.reset();
+ }
+ if ( conn != _master.get() ) {
+ _masterHost = HostAndPort();
+ _master.reset();
+ }
+
+ return;
+ }
+ catch ( const DBException &ex ) {
+
+ // We care if we can't authenticate (i.e. bad password) in credential params.
+ if ( isAuthenticationException( ex ) ) {
+ throw;
+ }
+
+ StringBuilder errMsgB;
+ errMsgB << "can't authenticate against replica set node "
+ << _lastSlaveOkHost.toString();
+ lastNodeStatus = ex.toStatus( errMsgB.str() );
+
+ LOG(1) << lastNodeStatus.reason() << endl;
+ invalidateLastSlaveOkCache();
+ }
+ }
+
+ if ( lastNodeStatus.isOK() ) {
+ StringBuilder assertMsgB;
+ assertMsgB << "Failed to authenticate, no good nodes in " << _getMonitor()->getName();
+ uasserted( ErrorCodes::NodeNotFound, assertMsgB.str() );
+ }
+ else {
+ uasserted( lastNodeStatus.code(), lastNodeStatus.reason() );
+ }
+ }
+
void DBClientReplicaSet::logout(const string &dbname, BSONObj& info) {
DBClientConnection* priConn = checkMaster();
@@ -1668,9 +1783,8 @@ namespace mongo {
int queryOptions,
int batchSize) {
- if ( _isQueryOkToSecondary( ns, queryOptions, query.obj ) ) {
-
- shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(query.obj));
+ shared_ptr<ReadPreferenceSetting> readPref( _extractReadPref( query.obj, queryOptions ) );
+ if ( _isSecondaryQuery( ns, query.obj, *readPref ) ) {
LOG( 3 ) << "dbclient_rs query using secondary or tagged node selection in "
<< _getMonitor()->getName() << ", read pref is "
@@ -1718,9 +1832,9 @@ namespace mongo {
const Query& query,
const BSONObj *fieldsToReturn,
int queryOptions) {
- if (_isQueryOkToSecondary(ns, queryOptions, query.obj)) {
- shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(query.obj));
+ shared_ptr<ReadPreferenceSetting> readPref( _extractReadPref( query.obj, queryOptions ) );
+ if ( _isSecondaryQuery( ns, query.obj, *readPref ) ) {
LOG( 3 ) << "dbclient_rs findOne using secondary or tagged node selection in "
<< _getMonitor()->getName() << ", read pref is "
@@ -1872,17 +1986,15 @@ namespace mongo {
_lazyState = LazyState();
const int lastOp = toSend.operation();
- bool slaveOk = false;
if (lastOp == dbQuery) {
// TODO: might be possible to do this faster by changing api
DbMessage dm(toSend);
QueryMessage qm(dm);
- const bool slaveOk = qm.queryOptions & QueryOption_SlaveOk;
- if (_isQueryOkToSecondary(qm.ns, qm.queryOptions, qm.query)) {
-
- shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(qm.query));
+ shared_ptr<ReadPreferenceSetting> readPref( _extractReadPref( qm.query,
+ qm.queryOptions ) );
+ if ( _isSecondaryQuery( qm.ns, qm.query, *readPref ) ) {
LOG( 3 ) << "dbclient_rs say using secondary or tagged node selection in "
<< _getMonitor()->getName() << ", read pref is "
@@ -1910,7 +2022,7 @@ namespace mongo {
conn->say(toSend);
_lazyState._lastOp = lastOp;
- _lazyState._slaveOk = slaveOk;
+ _lazyState._isSecondaryQuery = true;
_lazyState._lastClient = conn;
}
catch ( const DBException& DBExcep ) {
@@ -1936,7 +2048,7 @@ namespace mongo {
*actualServer = master->getServerAddress();
_lazyState._lastOp = lastOp;
- _lazyState._slaveOk = slaveOk;
+ _lazyState._isSecondaryQuery = false;
// Don't retry requests to primary since there is only one host to try
_lazyState._retries = MAX_RETRY;
_lazyState._lastClient = master;
@@ -1981,7 +2093,7 @@ namespace mongo {
if( nReturned == 1 ) dataObj = BSONObj( data );
// Check if we should retry here
- if( _lazyState._lastOp == dbQuery && _lazyState._slaveOk ){
+ if( _lazyState._lastOp == dbQuery && _lazyState._isSecondaryQuery ){
// Check the error code for a slave not secondary error
if( nReturned == -1 ||
@@ -2036,9 +2148,9 @@ namespace mongo {
QueryMessage qm(dm);
ns = qm.ns;
- if (_isQueryOkToSecondary(ns, qm.queryOptions, qm.query)) {
-
- shared_ptr<ReadPreferenceSetting> readPref(_extractReadPref(qm.query));
+ shared_ptr<ReadPreferenceSetting> readPref( _extractReadPref( qm.query,
+ qm.queryOptions ) );
+ if ( _isSecondaryQuery( ns, qm.query, *readPref ) ) {
LOG( 3 ) << "dbclient_rs call using secondary or tagged node selection in "
<< _getMonitor()->getName() << ", read pref is "
diff --git a/src/mongo/client/dbclient_rs.h b/src/mongo/client/dbclient_rs.h
index 05f7faa9b2b..d4c9512576f 100644
--- a/src/mongo/client/dbclient_rs.h
+++ b/src/mongo/client/dbclient_rs.h
@@ -518,10 +518,65 @@ namespace mongo {
virtual bool call( Message &toSend, Message &response, bool assertOk=true , string * actualServer = 0 );
virtual bool callRead( Message& toSend , Message& response ) { return checkMaster()->callRead( toSend , response ); }
+ /**
+ * Authenticate using supplied credentials. Authenticates against the primary node, fails
+ * if node is down.
+ * Credentials are cached for future connections.
+ *
+ * See DBClientWithCommands::auth() for more details.
+ *
+ * This is the default authentication mode for DBClientReplicaSet connections.
+ */
+ void authPrimary(const BSONObj& params);
+
+ /**
+ * Same as above, but authorizes access to a particular database.
+ *
+ * See DBClientWithCommands::auth() for more details.
+ */
+ bool authPrimary(const string &dbname,
+ const string &username,
+ const string &password_text,
+ string& errmsg,
+ bool digestPassword);
+
+ /**
+ * Authenticate using supplied credentials. Prefers authentication against the primary
+ * node, will fall back to a secondary and retry if the primary node is down but
+ * secondaries are still available.
+ * Credentials are cached for future connections.
+ *
+ * See DBClientWithCommands::auth() for more details.
+ */
+ void authAny(const BSONObj& params);
+
+ /**
+ * Same as above, but authorizes access to a particular database.
+ *
+ * See DBClientWithCommands::auth() for more details.
+ */
+ bool authAny( const string &dbname,
+ const string &username,
+ const string &password_text,
+ string& errmsg,
+ bool digestPassword );
+
+ /*
+ * Returns whether a query or command can be sent to secondaries based on the query object
+ * and options.
+ *
+ * @param ns the namespace of the query.
+ * @param queryObj the query object to check.
+ * @param queryOptions the query options
+ *
+ * @return true if the query/cmd could potentially be sent to a secondary, false otherwise
+ */
+ static bool isSecondaryQuery( const string& ns,
+ const BSONObj& queryObj,
+ int queryOptions );
protected:
- /** Authorize. Authorizes all nodes as needed
- */
+
virtual void _auth(const BSONObj& params);
virtual void sayPiggyBack( Message &toSend ) { checkMaster()->say( toSend ); }
@@ -606,10 +661,12 @@ namespace mongo {
*/
class LazyState {
public:
- LazyState() : _lastClient( NULL ), _lastOp( -1 ), _slaveOk( false ), _retries( 0 ) {}
+ LazyState() :
+ _lastClient( NULL ), _lastOp( -1 ), _isSecondaryQuery( false ), _retries( 0 ) {
+ }
DBClientConnection* _lastClient;
int _lastOp;
- bool _slaveOk;
+ bool _isSecondaryQuery;
int _retries;
} _lazyState;
diff --git a/src/mongo/client/parallel.cpp b/src/mongo/client/parallel.cpp
index 18649d9c6b9..7dcc7573df0 100644
--- a/src/mongo/client/parallel.cpp
+++ b/src/mongo/client/parallel.cpp
@@ -715,26 +715,28 @@ namespace mongo {
}
const DBClientBase* rawConn = state->conn->getRawConn();
- if (( _options & QueryOption_SlaveOk ) &&
- rawConn->type() == ConnectionString::SET &&
- rawConn->isFailed() ) {
- /* A side effect of this short circuiting is this will not be
- * able figure out that the primary is now up on it's own and
- * has to rely on other threads to refresh the node states.
- */
+ bool allowShardVersionFailure =
+ rawConn->type() == ConnectionString::SET &&
+ DBClientReplicaSet::isSecondaryQuery( _qSpec.ns(), _qSpec.query(), _qSpec.options() );
+
+ if ( allowShardVersionFailure && rawConn->isFailed() ) {
+
+ state->conn->donotCheckVersion();
+
+ // A side effect of this short circuiting is the mongos will not be able figure out that
+ // the primary is now up on it's own and has to rely on other threads to refresh node
+ // states.
OCCASIONALLY {
- const DBClientReplicaSet* repl =
- dynamic_cast<const DBClientReplicaSet*>( rawConn );
+ const DBClientReplicaSet* repl = dynamic_cast<const DBClientReplicaSet*>( rawConn );
+ dassert(repl);
warning() << "Primary for " << repl->getServerAddress()
<< " was down before, bypassing setShardVersion."
- << " Local config view can be stale." << endl;
+ << " The local replica set view and targeting may be stale." << endl;
}
- } else {
+ }
+ else {
try {
- /* TODO: Undo SERVER-5797. This try-catch is a temporary hack until
- * secondaries can properly handle shard versioning
- */
if ( state->conn->setVersion() ) {
// It's actually okay if we set the version here, since either the
// manager will be verified as compatible, or if the manager doesn't
@@ -742,19 +744,20 @@ namespace mongo {
LOG( pc ) << "needed to set remote version on connection to value "
<< "compatible with " << vinfo << endl;
}
- } catch ( const DBException& dbEx ) {
- if ( (dbEx.getCode() == 10009 /* no master */ &&
- ( _options & QueryOption_SlaveOk )) ) {
+ }
+ catch ( const DBException& dbEx ) {
+ if ( allowShardVersionFailure ) {
+
+ // It's okay if we don't set the version when talking to a secondary, we can
+ // be stale in any case.
OCCASIONALLY {
const DBClientReplicaSet* repl =
- dynamic_cast<const DBClientReplicaSet*>(
- state->conn->getRawConn() );
-
- warning() << "Cannot contact primary for "
- << repl->getServerAddress()
- << " to check shard version. "
- << "SlaveOk query can be sent to the wrong shard."
+ dynamic_cast<const DBClientReplicaSet*>( state->conn->getRawConn() );
+ dassert(repl);
+ warning() << "Cannot contact primary for " << repl->getServerAddress()
+ << " to check shard version."
+ << " The local replica set view and targeting may be stale."
<< endl;
}
}
diff --git a/src/mongo/db/auth/auth_external_state_s.cpp b/src/mongo/db/auth/auth_external_state_s.cpp
index b7167cef509..7e890636cdf 100644
--- a/src/mongo/db/auth/auth_external_state_s.cpp
+++ b/src/mongo/db/auth/auth_external_state_s.cpp
@@ -49,9 +49,11 @@ namespace mongo {
}
bool AuthExternalStateMongos::_findUser(const string& usersNamespace,
- const BSONObj& query,
+ const BSONObj& queryDoc,
BSONObj* result) const {
scoped_ptr<ScopedDbConnection> conn(getConnectionForUsersCollection(usersNamespace));
+ Query query(queryDoc);
+ query.readPref(ReadPreference_PrimaryPreferred, BSONArray());
*result = conn->get()->findOne(usersNamespace, query).getOwned();
conn->done();
return !result->isEmpty();
diff --git a/src/mongo/db/auth/authorization_manager.cpp b/src/mongo/db/auth/authorization_manager.cpp
index 1cf8efede39..f30443eda1b 100644
--- a/src/mongo/db/auth/authorization_manager.cpp
+++ b/src/mongo/db/auth/authorization_manager.cpp
@@ -443,6 +443,18 @@ namespace {
return _authenticatedPrincipals.getNames();
}
+ std::string AuthorizationManager::getAuthenticatedPrincipalNamesToken() {
+ std::string ret;
+ for (PrincipalSet::NameIterator nameIter = getAuthenticatedPrincipalNames();
+ nameIter.more();
+ nameIter.next()) {
+ ret += '\0'; // Using a NUL byte which isn't valid in usernames to separate them.
+ ret += nameIter->getFullName();
+ }
+
+ return ret;
+ }
+
Status AuthorizationManager::acquirePrivilege(const Privilege& privilege,
const PrincipalName& authorizingPrincipal) {
if (!_authenticatedPrincipals.lookup(authorizingPrincipal)) {
diff --git a/src/mongo/db/auth/authorization_manager.h b/src/mongo/db/auth/authorization_manager.h
index a32710557dd..7131d7624a6 100644
--- a/src/mongo/db/auth/authorization_manager.h
+++ b/src/mongo/db/auth/authorization_manager.h
@@ -91,6 +91,10 @@ namespace mongo {
// Gets an iterator over the names of all authenticated principals stored in this manager.
PrincipalSet::NameIterator getAuthenticatedPrincipalNames();
+ // Returns a string representing all logged-in principals on the current session.
+ // WARNING: this string will contain NUL bytes so don't call c_str()!
+ std::string getAuthenticatedPrincipalNamesToken();
+
// Removes any authenticated principals whose authorization credentials came from the given
// database, and revokes any privileges that were granted via that principal.
void logoutDatabase(const std::string& dbname);
diff --git a/src/mongo/db/cmdline.cpp b/src/mongo/db/cmdline.cpp
index 0dea50faeef..c14cc603b56 100644
--- a/src/mongo/db/cmdline.cpp
+++ b/src/mongo/db/cmdline.cpp
@@ -151,9 +151,14 @@ namespace {
if ( s.find( "FASTSYNC" ) != string::npos )
cout << "warning \"fastsync\" should not be put in your configuration file" << endl;
- if ( s.c_str()[0] == '#' ) {
- // skipping commented line
- } else if ( s.find( "=FALSE" ) == string::npos ) {
+ // skip commented lines
+ if ( s.c_str()[0] == '#' ) {
+ // In this block, we copy the actual line into our intermediate buffer to actually be
+ // parsed later only if the string does not contain the substring "=FALSE" OR the option
+ // is a setParameter option. Note that this is done after we call boost::to_upper
+ // above.
+ } else if ( s.find( "=FALSE" ) == string::npos ||
+ s.find( "SETPARAMETER" ) == 0 ) {
ss << line << endl;
} else {
cout << "warning: remove or comment out this line by starting it with \'#\', skipping now : " << line << endl;
diff --git a/src/mongo/db/commands/dbhash.cpp b/src/mongo/db/commands/dbhash.cpp
new file mode 100644
index 00000000000..38a07cace56
--- /dev/null
+++ b/src/mongo/db/commands/dbhash.cpp
@@ -0,0 +1,216 @@
+// dbhash.cpp
+
+/**
+* Copyright (C) 2013 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* As a special exception, the copyright holders give permission to link the
+* code of portions of this program with the OpenSSL library under certain
+* conditions as described in each individual source file and distribute
+* linked combinations including the program with the OpenSSL library. You
+* must comply with the GNU Affero General Public License in all respects for
+* all of the code used other than as permitted herein. If you modify file(s)
+* with this exception, you may extend this exception to your version of the
+* file(s), but you are not obligated to do so. If you do not wish to do so,
+* delete this exception statement from your version. If you delete this
+* exception statement from all source files in the program, then also delete
+* it in the license file.
+*/
+
+#include "mongo/db/commands/dbhash.h"
+
+#include "mongo/db/btreecursor.h"
+#include "mongo/db/client.h"
+#include "mongo/db/commands.h"
+#include "mongo/db/database.h"
+#include "mongo/db/pdfile.h"
+#include "mongo/util/md5.hpp"
+#include "mongo/util/timer.h"
+
+namespace mongo {
+
+ DBHashCmd dbhashCmd;
+
+
+ void logOpForDbHash( const char* opstr,
+ const char* ns,
+ const BSONObj& obj,
+ BSONObj* patt ) {
+ dbhashCmd.wipeCacheForCollection( ns );
+ }
+
+ // ----
+
+ DBHashCmd::DBHashCmd()
+ : Command( "dbHash", false, "dbhash" ),
+ _cachedHashedMutex( "_cachedHashedMutex" ){
+ }
+
+ void DBHashCmd::addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out) {
+ ActionSet actions;
+ actions.addAction(ActionType::dbHash);
+ out->push_back(Privilege(dbname, actions));
+ }
+
+ string DBHashCmd::hashCollection( const string& fullCollectionName, bool* fromCache ) {
+
+ scoped_ptr<scoped_lock> cachedHashedLock;
+
+ if ( isCachable( fullCollectionName ) ) {
+ cachedHashedLock.reset( new scoped_lock( _cachedHashedMutex ) );
+ string hash = _cachedHashed[fullCollectionName];
+ if ( hash.size() > 0 ) {
+ *fromCache = true;
+ return hash;
+ }
+ }
+
+ *fromCache = false;
+ NamespaceDetails * nsd = nsdetails( fullCollectionName );
+ verify( nsd );
+
+ // debug SERVER-761
+ NamespaceDetails::IndexIterator ii = nsd->ii();
+ while( ii.more() ) {
+ const IndexDetails &idx = ii.next();
+ if ( !idx.head.isValid() || !idx.info.isValid() ) {
+ log() << "invalid index for ns: " << fullCollectionName << " " << idx.head << " " << idx.info;
+ if ( idx.info.isValid() )
+ log() << " " << idx.info.obj();
+ log() << endl;
+ }
+ }
+
+ int idNum = nsd->findIdIndex();
+
+ shared_ptr<Cursor> cursor;
+
+ if ( idNum >= 0 ) {
+ cursor.reset( BtreeCursor::make( nsd,
+ nsd->idx( idNum ),
+ BSONObj(),
+ BSONObj(),
+ false,
+ 1 ) );
+ }
+ else if ( nsd->isCapped() ) {
+ cursor = findTableScan( fullCollectionName.c_str() , BSONObj() );
+ }
+ else {
+ log() << "can't find _id index for: " << fullCollectionName << endl;
+ return "no _id _index";
+ }
+
+ md5_state_t st;
+ md5_init(&st);
+
+ long long n = 0;
+
+ while ( cursor->ok() ) {
+ BSONObj c = cursor->current();
+ md5_append( &st , (const md5_byte_t*)c.objdata() , c.objsize() );
+ n++;
+ cursor->advance();
+ }
+
+ md5digest d;
+ md5_finish(&st, d);
+ string hash = digestToString( d );
+
+ if ( cachedHashedLock.get() ) {
+ _cachedHashed[fullCollectionName] = hash;
+ }
+
+ return hash;
+ }
+
+ bool DBHashCmd::run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
+ Timer timer;
+
+ set<string> desiredCollections;
+ if ( cmdObj["collections"].type() == Array ) {
+ BSONObjIterator i( cmdObj["collections"].Obj() );
+ while ( i.more() ) {
+ BSONElement e = i.next();
+ if ( e.type() != String ) {
+ errmsg = "collections entries have to be strings";
+ return false;
+ }
+ desiredCollections.insert( e.String() );
+ }
+ }
+
+ list<string> colls;
+ Database* db = cc().database();
+ if ( db )
+ db->namespaceIndex.getNamespaces( colls );
+ colls.sort();
+
+ result.appendNumber( "numCollections" , (long long)colls.size() );
+ result.append( "host" , prettyHostName() );
+
+ md5_state_t globalState;
+ md5_init(&globalState);
+
+ vector<string> cached;
+
+ BSONObjBuilder bb( result.subobjStart( "collections" ) );
+ for ( list<string>::iterator i=colls.begin(); i != colls.end(); i++ ) {
+ string fullCollectionName = *i;
+ string shortCollectionName = fullCollectionName.substr( dbname.size() + 1 );
+
+ if ( shortCollectionName.find( "system." ) == 0 )
+ continue;
+
+ if ( desiredCollections.size() > 0 &&
+ desiredCollections.count( shortCollectionName ) == 0 )
+ continue;
+
+ bool fromCache = false;
+ string hash = hashCollection( fullCollectionName, &fromCache );
+
+ bb.append( shortCollectionName, hash );
+
+ md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() );
+ if ( fromCache )
+ cached.push_back( fullCollectionName );
+ }
+ bb.done();
+
+ md5digest d;
+ md5_finish(&globalState, d);
+ string hash = digestToString( d );
+
+ result.append( "md5" , hash );
+ result.appendNumber( "timeMillis", timer.millis() );
+
+ result.append( "fromCache", cached );
+
+ return 1;
+ }
+
+ void DBHashCmd::wipeCacheForCollection( const StringData& ns ) {
+ if ( !isCachable( ns ) )
+ return;
+ scoped_lock lk( _cachedHashedMutex );
+ _cachedHashed.erase( ns.toString() );
+ }
+
+ bool DBHashCmd::isCachable( const StringData& ns ) const {
+ return ns.startsWith( "config." );
+ }
+
+}
diff --git a/src/mongo/db/commands/dbhash.h b/src/mongo/db/commands/dbhash.h
new file mode 100644
index 00000000000..262c6609868
--- /dev/null
+++ b/src/mongo/db/commands/dbhash.h
@@ -0,0 +1,67 @@
+// dbhash.h
+
+/**
+* Copyright (C) 2013 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*
+* As a special exception, the copyright holders give permission to link the
+* code of portions of this program with the OpenSSL library under certain
+* conditions as described in each individual source file and distribute
+* linked combinations including the program with the OpenSSL library. You
+* must comply with the GNU Affero General Public License in all respects for
+* all of the code used other than as permitted herein. If you modify file(s)
+* with this exception, you may extend this exception to your version of the
+* file(s), but you are not obligated to do so. If you do not wish to do so,
+* delete this exception statement from your version. If you delete this
+* exception statement from all source files in the program, then also delete
+* it in the license file.
+*/
+
+#pragma once
+
+#include "mongo/db/commands.h"
+
+namespace mongo {
+
+ void logOpForDbHash( const char* opstr,
+ const char* ns,
+ const BSONObj& obj,
+ BSONObj* patt );
+
+ class DBHashCmd : public Command {
+ public:
+ DBHashCmd();
+
+ virtual bool slaveOk() const { return true; }
+ virtual LockType locktype() const { return READ; }
+ virtual void addRequiredPrivileges(const std::string& dbname,
+ const BSONObj& cmdObj,
+ std::vector<Privilege>* out);
+
+ virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool);
+
+ void wipeCacheForCollection( const StringData& ns );
+
+ private:
+
+ bool isCachable( const StringData& ns ) const;
+
+ string hashCollection( const string& fullCollectionName, bool* fromCache );
+
+ map<string,string> _cachedHashed;
+ mutex _cachedHashedMutex;
+
+ };
+
+}
diff --git a/src/mongo/db/commands/group.cpp b/src/mongo/db/commands/group.cpp
index 441a1192905..20481521e91 100644
--- a/src/mongo/db/commands/group.cpp
+++ b/src/mongo/db/commands/group.cpp
@@ -20,9 +20,11 @@
#include <vector>
+#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/action_set.h"
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/privilege.h"
+#include "mongo/db/client_basic.h"
#include "mongo/db/commands.h"
#include "mongo/db/instance.h"
#include "mongo/scripting/engine.h"
@@ -72,7 +74,9 @@ namespace mongo {
string& errmsg,
BSONObjBuilder& result ) {
- auto_ptr<Scope> s = globalScriptEngine->getPooledScope( realdbname, "group");
+ const string userToken = ClientBasic::getCurrent()->getAuthorizationManager()
+ ->getAuthenticatedPrincipalNamesToken();
+ auto_ptr<Scope> s = globalScriptEngine->getPooledScope(realdbname, "group" + userToken);
if ( reduceScope )
s->init( reduceScope );
diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp
index 9528e495ded..1b237d3ed7f 100644
--- a/src/mongo/db/commands/mr.cpp
+++ b/src/mongo/db/commands/mr.cpp
@@ -22,6 +22,7 @@
#include "mongo/client/connpool.h"
#include "mongo/client/parallel.h"
+#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/clientcursor.h"
#include "mongo/db/commands.h"
#include "mongo/db/db.h"
@@ -300,8 +301,13 @@ namespace mongo {
*/
void State::dropTempCollections() {
_db.dropCollection(_config.tempNamespace);
- if (_useIncremental)
+ // Always forget about temporary namespaces, so we don't cache lots of them
+ ShardConnection::forgetNS( _config.tempNamespace );
+ if (_useIncremental) {
_db.dropCollection(_config.incLong);
+ ShardConnection::forgetNS( _config.incLong );
+ }
+
}
/**
@@ -622,7 +628,10 @@ namespace mongo {
*/
void State::init() {
// setup js
- _scope.reset(globalScriptEngine->getPooledScope( _config.dbname, "mapreduce" ).release() );
+ const string userToken = ClientBasic::getCurrent()->getAuthorizationManager()
+ ->getAuthenticatedPrincipalNamesToken();
+ _scope.reset(globalScriptEngine->getPooledScope(
+ _config.dbname, "mapreduce" + userToken).release());
if ( ! _config.scopeSetup.isEmpty() )
_scope->init( &_config.scopeSetup );
@@ -1165,9 +1174,23 @@ namespace mongo {
state.init();
state.prepTempCollection();
ON_BLOCK_EXIT_OBJ(state, &State::dropTempCollections);
- ProgressMeterHolder pm(op->setMessage("m/r: (1/3) emit phase",
- "M/R: (1/3) Emit Progress",
- state.incomingDocuments()));
+
+ int progressTotal = 0;
+ bool showTotal = true;
+ if ( state.config().filter.isEmpty() ) {
+ progressTotal = state.incomingDocuments();
+ }
+ else {
+ showTotal = false;
+ // Set an arbitrary total > 0 so the meter will be activated.
+ progressTotal = 1;
+ }
+
+ ProgressMeter& progress( op->setMessage("m/r: (1/3) emit phase",
+ "M/R: (1/3) Emit Progress",
+ progressTotal ));
+ progress.showTotal(showTotal);
+ ProgressMeterHolder pm(progress);
wassert( config.limit < 0x4000000 ); // see case on next line to 32 bit unsigned
long long mapTime = 0;
@@ -1457,6 +1480,9 @@ namespace mongo {
break;
}
+ // Forget temporary input collection, if output is sharded collection
+ ShardConnection::forgetNS( inputNS );
+
result.append( "chunkSizes" , chunkSizes.arr() );
long long outputCount = state.postProcessCollection(op, pm);
diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp
index 2361ec2a945..ec337c89885 100644
--- a/src/mongo/db/db.cpp
+++ b/src/mongo/db/db.cpp
@@ -232,7 +232,6 @@ namespace mongo {
virtual void disconnected( AbstractMessagingPort* p ) {
Client * c = currentClient.get();
if( c ) c->shutdown();
- globalScriptEngine->threadDone();
}
};
diff --git a/src/mongo/db/dbcommands.cpp b/src/mongo/db/dbcommands.cpp
index 33b77f455d6..fb6be132171 100644
--- a/src/mongo/db/dbcommands.cpp
+++ b/src/mongo/db/dbcommands.cpp
@@ -178,9 +178,9 @@ namespace mongo {
}
}
- if ( err ) {
+ if ( err && cmdObj["wOpTime"].eoo() ) {
// doesn't make sense to wait for replication
- // if there was an error
+ // if there was an error and we aren't explicitly waiting for another wOpTime
return true;
}
@@ -200,7 +200,14 @@ namespace mongo {
long long passes = 0;
char buf[32];
- OpTime op(c.getLastOp());
+
+ OpTime op;
+ if ( cmdObj["wOpTime"].type() == Timestamp ) {
+ op = OpTime( cmdObj["wOpTime"].date() );
+ }
+ else {
+ op = c.getLastOp();
+ }
if ( op.isNull() ) {
if ( anyReplEnabled() ) {
@@ -1689,122 +1696,6 @@ namespace mongo {
}
return Status::OK();
}
-
- class DBHashCmd : public Command {
- public:
- DBHashCmd() : Command( "dbHash", false, "dbhash" ) {}
- virtual bool slaveOk() const { return true; }
- virtual LockType locktype() const { return READ; }
- virtual void addRequiredPrivileges(const std::string& dbname,
- const BSONObj& cmdObj,
- std::vector<Privilege>* out) {
- ActionSet actions;
- actions.addAction(ActionType::dbHash);
- out->push_back(Privilege(dbname, actions));
- }
- virtual bool run(const string& dbname , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool) {
- Timer timer;
-
- set<string> desiredCollections;
- if ( cmdObj["collections"].type() == Array ) {
- BSONObjIterator i( cmdObj["collections"].Obj() );
- while ( i.more() ) {
- BSONElement e = i.next();
- if ( e.type() != String ) {
- errmsg = "collections entries have to be strings";
- return false;
- }
- desiredCollections.insert( e.String() );
- }
- }
-
- list<string> colls;
- Database* db = cc().database();
- if ( db )
- db->namespaceIndex.getNamespaces( colls );
- colls.sort();
-
- result.appendNumber( "numCollections" , (long long)colls.size() );
- result.append( "host" , prettyHostName() );
-
- md5_state_t globalState;
- md5_init(&globalState);
-
- BSONObjBuilder bb( result.subobjStart( "collections" ) );
- for ( list<string>::iterator i=colls.begin(); i != colls.end(); i++ ) {
- string fullCollectionName = *i;
- string shortCollectionName = fullCollectionName.substr( dbname.size() + 1 );
-
- if ( shortCollectionName.find( "system." ) == 0 )
- continue;
-
- if ( desiredCollections.size() > 0 &&
- desiredCollections.count( shortCollectionName ) == 0 )
- continue;
-
- shared_ptr<Cursor> cursor;
-
- NamespaceDetails * nsd = nsdetails( fullCollectionName );
-
- // debug SERVER-761
- NamespaceDetails::IndexIterator ii = nsd->ii();
- while( ii.more() ) {
- const IndexDetails &idx = ii.next();
- if ( !idx.head.isValid() || !idx.info.isValid() ) {
- log() << "invalid index for ns: " << fullCollectionName << " " << idx.head << " " << idx.info;
- if ( idx.info.isValid() )
- log() << " " << idx.info.obj();
- log() << endl;
- }
- }
-
- int idNum = nsd->findIdIndex();
- if ( idNum >= 0 ) {
- cursor.reset( BtreeCursor::make( nsd,
- nsd->idx( idNum ),
- BSONObj(),
- BSONObj(),
- false,
- 1 ) );
- }
- else if ( nsd->isCapped() ) {
- cursor = findTableScan( fullCollectionName.c_str() , BSONObj() );
- }
- else {
- log() << "can't find _id index for: " << fullCollectionName << endl;
- continue;
- }
-
- md5_state_t st;
- md5_init(&st);
-
- long long n = 0;
- while ( cursor->ok() ) {
- BSONObj c = cursor->current();
- md5_append( &st , (const md5_byte_t*)c.objdata() , c.objsize() );
- n++;
- cursor->advance();
- }
- md5digest d;
- md5_finish(&st, d);
- string hash = digestToString( d );
-
- bb.append( shortCollectionName, hash );
-
- md5_append( &globalState , (const md5_byte_t*)hash.c_str() , hash.size() );
- }
- bb.done();
-
- md5digest d;
- md5_finish(&globalState, d);
- string hash = digestToString( d );
-
- result.append( "md5" , hash );
- result.appendNumber( "timeMillis", timer.millis() );
- return 1;
- }
-
- } dbhashCmd;
/* for diagnostic / testing purposes. Enabled via command line. */
class CmdSleep : public Command {
diff --git a/src/mongo/db/dbeval.cpp b/src/mongo/db/dbeval.cpp
index 5a6cc464c34..9e6a3360a49 100644
--- a/src/mongo/db/dbeval.cpp
+++ b/src/mongo/db/dbeval.cpp
@@ -57,7 +57,9 @@ namespace mongo {
return false;
}
- auto_ptr<Scope> s = globalScriptEngine->getPooledScope( dbName, "dbeval" );
+ const string userToken = ClientBasic::getCurrent()->getAuthorizationManager()
+ ->getAuthenticatedPrincipalNamesToken();
+ auto_ptr<Scope> s = globalScriptEngine->getPooledScope( dbName, "dbeval" + userToken );
ScriptingFunction f = s->createFunction(code);
if ( f == 0 ) {
errmsg = (string)"compile failed: " + s->getError();
diff --git a/src/mongo/db/fts/fts_spec.cpp b/src/mongo/db/fts/fts_spec.cpp
index eb534f10a3e..f1829104796 100644
--- a/src/mongo/db/fts/fts_spec.cpp
+++ b/src/mongo/db/fts/fts_spec.cpp
@@ -30,10 +30,25 @@ namespace mongo {
const double MAX_WEIGHT = 1000000000.0;
const double MAX_WORD_WEIGHT = MAX_WEIGHT / 10000;
+ const int TEXT_INDEX_VERSION = 1;
FTSSpec::FTSSpec( const BSONObj& indexInfo ) {
- massert( 16739, "found invalid spec for text index",
+ // indexInfo is a text index spec. Text index specs pass through fixSpec() before
+ // being saved to the system.indexes collection. fixSpec() enforces a schema, such that
+ // required fields must exist and be of the correct type (e.g. weights,
+ // textIndexVersion).
+ massert( 16739,
+ "found invalid spec for text index, expected object for weights",
indexInfo["weights"].isABSONObj() );
+ BSONElement textIndexVersionElt = indexInfo["textIndexVersion"];
+ massert( 17287,
+ "found invalid spec for text index, expected number for textIndexVersion",
+ textIndexVersionElt.isNumber() );
+ massert( 17288,
+ str::stream() << "attempt to use unsupported textIndexVersion " <<
+ textIndexVersionElt.numberInt() << ", only textIndexVersion " <<
+ TEXT_INDEX_VERSION << " supported",
+ textIndexVersionElt.numberInt() == TEXT_INDEX_VERSION );
_defaultLanguage = indexInfo["default_language"].valuestrsafe();
_languageOverrideField = indexInfo["language_override"].valuestrsafe();
@@ -357,7 +372,7 @@ namespace mongo {
language_override = "language";
int version = -1;
- int textIndexVersion = 1;
+ int textIndexVersion = TEXT_INDEX_VERSION;
BSONObjBuilder b;
BSONObjIterator i( spec );
@@ -385,7 +400,7 @@ namespace mongo {
textIndexVersion = e.numberInt();
uassert( 16730,
str::stream() << "bad textIndexVersion: " << textIndexVersion,
- textIndexVersion == 1 );
+ textIndexVersion == TEXT_INDEX_VERSION );
}
else {
b.append( e );
diff --git a/src/mongo/db/fts/fts_spec_test.cpp b/src/mongo/db/fts/fts_spec_test.cpp
index df4ff719f02..00c95261207 100644
--- a/src/mongo/db/fts/fts_spec_test.cpp
+++ b/src/mongo/db/fts/fts_spec_test.cpp
@@ -24,6 +24,36 @@
namespace mongo {
namespace fts {
+ const BSONObj makeFixedSpec( int textIndexVersion ) {
+ return BSON( "v" << 1 <<
+ "key" << BSON( "_fts" << "text" <<
+ "_ftsx" << 1 ) <<
+ "name" << "a_text" <<
+ "ns" << "test.foo" <<
+ "weights" << BSON( "a" << 1 ) <<
+ "default_language" << "english" <<
+ "language_override" << "language" <<
+ "textIndexVersion" << textIndexVersion );
+ }
+
+ TEST( FTSSpec, TextIndexVersionCheck1 ) {
+ const int currentVersion = 1;
+ const int unsupportedVersion = 2;
+
+ // Constructing an FTSSpec with the current textIndexVersion should succeed.
+ BSONObj validTextSpec = makeFixedSpec( currentVersion );
+ try {
+ FTSSpec spec( validTextSpec );
+ }
+ catch ( DBException& e ) {
+ ASSERT( false );
+ }
+
+ // Constructing an FTSSpec with an unsupported textIndexVersion should fail.
+ BSONObj invalidTextSpec = makeFixedSpec( unsupportedVersion );
+ ASSERT_THROWS( FTSSpec spec( invalidTextSpec ), DBException );
+ }
+
TEST( FTSSpec, Fix1 ) {
BSONObj user = BSON( "key" << BSON( "title" << "fts" <<
"text" << "fts" ) <<
diff --git a/src/mongo/db/geo/geoparser.cpp b/src/mongo/db/geo/geoparser.cpp
index 3de520be0be..ed86a2c3fa9 100644
--- a/src/mongo/db/geo/geoparser.cpp
+++ b/src/mongo/db/geo/geoparser.cpp
@@ -39,8 +39,15 @@ namespace mongo {
static const string GEOJSON_COORDINATES = "coordinates";
//// Utility functions used by GeoParser functions below.
- static S2Point coordToPoint(double p0, double p1) {
- return S2LatLng::FromDegrees(p1, p0).Normalized().ToPoint();
+ static S2Point coordToPoint(double lng, double lat) {
+ // Note that it's (lat, lng) for S2 but (lng, lat) for MongoDB.
+ S2LatLng ll = S2LatLng::FromDegrees(lat, lng).Normalized();
+ if (!ll.is_valid()) {
+ stringstream ss;
+ ss << "coords invalid after normalization, lng = " << lng << " lat = " << lat << endl;
+ uasserted(17125, ss.str());
+ }
+ return ll.ToPoint();
}
static S2Point coordsToPoint(const vector<BSONElement>& coordElt) {
diff --git a/src/mongo/db/matcher.cpp b/src/mongo/db/matcher.cpp
index 2e679591f2c..532c18e1fe0 100644
--- a/src/mongo/db/matcher.cpp
+++ b/src/mongo/db/matcher.cpp
@@ -27,6 +27,7 @@
#include "db.h"
#include "queryutil.h"
#include "client.h"
+#include "mongo/db/auth/authorization_manager.h"
#include "pdfile.h"
@@ -74,8 +75,10 @@ namespace mongo {
return;
_initCalled = true;
+ const string userToken = ClientBasic::getCurrent()->getAuthorizationManager()
+ ->getAuthenticatedPrincipalNamesToken();
NamespaceString ns( _ns );
- _scope = globalScriptEngine->getPooledScope( ns.db.c_str(), "where" );
+ _scope = globalScriptEngine->getPooledScope( ns.db.c_str(), "where" + userToken );
massert( 10341 , "code has to be set first!" , ! _jsCode.empty() );
@@ -429,6 +432,8 @@ namespace mongo {
uassert( 10066 , "$where may only appear once in query", _where == 0 );
uassert( 10067 , "$where query, but no script engine", globalScriptEngine );
massert( 13089 , "no current client needed for $where" , haveClient() );
+ uassert( 17126 , "no valid context found for $where", cc().getContext());
+
_where = new Where( cc().ns() );
if ( e.type() == CodeWScope ) {
diff --git a/src/mongo/db/namespace_details.cpp b/src/mongo/db/namespace_details.cpp
index 94adac276a6..0fc7bdf5eb5 100644
--- a/src/mongo/db/namespace_details.cpp
+++ b/src/mongo/db/namespace_details.cpp
@@ -976,6 +976,10 @@ namespace mongo {
bool legalClientSystemNS( const string& ns , bool write ) {
if( ns == "local.system.replset" ) return true;
+ if( ns == "admin.system.version" ) return true;
+ if( ns == "admin.system.roles" ) return true;
+ if( ns == "admin.system.new_users" ) return true;
+ if( ns == "admin.system.backup_users" ) return true;
if ( ns.find( ".system.users" ) != string::npos )
return true;
diff --git a/src/mongo/db/oplog.cpp b/src/mongo/db/oplog.cpp
index 12f4742c256..b88ad66ac03 100644
--- a/src/mongo/db/oplog.cpp
+++ b/src/mongo/db/oplog.cpp
@@ -26,6 +26,7 @@
#include "mongo/db/auth/action_type.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/commands.h"
+#include "mongo/db/commands/dbhash.h"
#include "mongo/db/index_update.h"
#include "mongo/db/instance.h"
#include "mongo/db/namespacestring.h"
@@ -334,6 +335,7 @@ namespace mongo {
}
logOpForSharding( opstr , ns , obj , patt );
+ logOpForDbHash( opstr , ns , obj , patt );
}
void createOplog() {
@@ -977,13 +979,17 @@ namespace mongo {
BSONElement e = i.next();
const BSONObj& temp = e.Obj();
- Client::Context ctx(temp["ns"].String());
+ string ns = temp["ns"].String();
+ Client::Context ctx(ns);
+
bool failed = applyOperation_inlock(temp, false, alwaysUpsert);
ab.append(!failed);
if ( failed )
errors++;
num++;
+
+ logOpForDbHash( "u", ns.c_str(), BSONObj(), NULL );
}
result.append( "applied" , num );
diff --git a/src/mongo/db/pdfile.cpp b/src/mongo/db/pdfile.cpp
index 928b8560ced..4085dfd2b3f 100644
--- a/src/mongo/db/pdfile.cpp
+++ b/src/mongo/db/pdfile.cpp
@@ -267,7 +267,7 @@ namespace mongo {
BSONElement e = options.getField("size");
if ( e.isNumber() ) {
size = e.numberLong();
- uassert( 10083 , "create collection invalid size spec", size > 0 );
+ uassert( 10083 , "create collection invalid size spec", size >= 0 );
size += 0xff;
size &= 0xffffffffffffff00LL;
diff --git a/src/mongo/db/pipeline/document.h b/src/mongo/db/pipeline/document.h
index b51efa91497..5e7cf3f0579 100644
--- a/src/mongo/db/pipeline/document.h
+++ b/src/mongo/db/pipeline/document.h
@@ -162,6 +162,7 @@ namespace mongo {
friend class FieldIterator;
friend class ValueStorage;
friend class MutableDocument;
+ friend class MutableValue;
const DocumentStorage& storage() const {
return (_storage ? *_storage : DocumentStorage::emptyDoc());
@@ -194,8 +195,17 @@ namespace mongo {
/// Used by MutableDocument(MutableValue)
const RefCountable*& getDocPtr() {
- if (_val.getType() != Object)
- *this = Value(Document());
+ if (_val.getType() != Object || _val._storage.genericRCPtr == NULL) {
+ // If the current value isn't an object we replace it with a Object-typed Value.
+ // Note that we can't just use Document() here because that is a NULL pointer and
+ // Value doesn't refcount NULL pointers. This led to a memory leak (SERVER-10554)
+ // because MutableDocument::newStorage() would set a non-NULL pointer into the Value
+ // without setting the refCounter bit. While allocating a DocumentStorage here could
+ // result in an allocation where none is needed, in practice this is only called
+ // when we are about to add a field to the sub-document so this just changes where
+ // the allocation is done.
+ _val = Value(Document(new DocumentStorage()));
+ }
return _val._storage.genericRCPtr;
}
diff --git a/src/mongo/db/pipeline/value.cpp b/src/mongo/db/pipeline/value.cpp
index 5561cb41a59..207dc4e37b8 100644
--- a/src/mongo/db/pipeline/value.cpp
+++ b/src/mongo/db/pipeline/value.cpp
@@ -28,6 +28,48 @@
namespace mongo {
using namespace mongoutils;
+ void ValueStorage::verifyRefCountingIfShould() const {
+ switch (type) {
+ case MinKey:
+ case MaxKey:
+ case jstOID:
+ case Date:
+ case Timestamp:
+ case EOO:
+ case jstNULL:
+ case Undefined:
+ case Bool:
+ case NumberInt:
+ case NumberLong:
+ case NumberDouble:
+ // the above types never reference external data
+ verify(!refCounter);
+ break;
+
+ case String:
+ case RegEx:
+ case Code:
+ case Symbol:
+ // the above types reference data when not using short-string optimization
+ verify(refCounter == !shortStr);
+ break;
+
+ case BinData: // TODO this should probably support short-string optimization
+ case Array: // TODO this should probably support empty-is-NULL optimization
+ case DBRef:
+ case CodeWScope:
+ // the above types always reference external data.
+ verify(refCounter);
+ verify(bool(genericRCPtr));
+ break;
+
+ case Object:
+ // Objects either hold a NULL ptr or should be ref-counting
+ verify(refCounter == bool(genericRCPtr));
+ break;
+ }
+ }
+
void ValueStorage::putString(const StringData& s) {
// Note: this also stores data portion of BinData
const size_t sizeNoNUL = s.size();
diff --git a/src/mongo/db/pipeline/value_internal.h b/src/mongo/db/pipeline/value_internal.h
index e3481951f9e..ffc7f42a7f7 100644
--- a/src/mongo/db/pipeline/value_internal.h
+++ b/src/mongo/db/pipeline/value_internal.h
@@ -89,6 +89,7 @@ namespace mongo {
}
~ValueStorage() {
+ DEV verifyRefCountingIfShould();
if (refCounter)
intrusive_ptr_release(genericRCPtr);
DEV memset(this, 0xee, sizeof(*this));
@@ -109,6 +110,7 @@ namespace mongo {
/// Call this after memcpying to update ref counts if needed
void memcpyed() const {
+ DEV verifyRefCountingIfShould();
if (refCounter)
intrusive_ptr_add_ref(genericRCPtr);
}
@@ -140,6 +142,7 @@ namespace mongo {
intrusive_ptr_add_ref(genericRCPtr);
refCounter = true;
}
+ DEV verifyRefCountingIfShould();
}
StringData getString() const {
@@ -191,6 +194,8 @@ namespace mongo {
&& i64[1] == other.i64[1]);
}
+ void verifyRefCountingIfShould() const;
+
// This data is public because this should only be used by Value which would be a friend
union {
struct {
diff --git a/src/mongo/db/repl/consensus.cpp b/src/mongo/db/repl/consensus.cpp
index f056befb605..dcb31408c11 100644
--- a/src/mongo/db/repl/consensus.cpp
+++ b/src/mongo/db/repl/consensus.cpp
@@ -61,7 +61,9 @@ namespace mongo {
return true;
}
- if (primary && primary->hbinfo().opTime >= hopeful->hbinfo().opTime) {
+ if (primary &&
+ (hopeful->hbinfo().id() != primary->hbinfo().id()) &&
+ (primary->hbinfo().opTime >= hopeful->hbinfo().opTime)) {
// other members might be aware of more up-to-date nodes
errmsg = str::stream() << hopeful->fullName() <<
" is trying to elect itself but " << primary->fullName() <<
diff --git a/src/mongo/db/repl/heartbeat.cpp b/src/mongo/db/repl/heartbeat.cpp
index f1bc18168f6..95bde6d185b 100644
--- a/src/mongo/db/repl/heartbeat.cpp
+++ b/src/mongo/db/repl/heartbeat.cpp
@@ -264,11 +264,13 @@ namespace mongo {
down(mem, info.getStringField("errmsg"));
}
}
- catch(DBException& e) {
+ catch (const DBException& e) {
+ log() << "replSet health poll task caught a DBException: " << e.what();
down(mem, e.what());
}
- catch(...) {
- down(mem, "replSet unexpected exception in ReplSetHealthPollTask");
+ catch (const std::exception& e) {
+ log() << "replSet health poll task caught an exception: " << e.what();
+ down(mem, e.what());
}
m = mem;
diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp
index 68b7fc17529..841dcf7d898 100644
--- a/src/mongo/db/repl/rs_rollback.cpp
+++ b/src/mongo/db/repl/rs_rollback.cpp
@@ -177,6 +177,16 @@ namespace mongo {
log() << "replSet " << o.toString() << rsLog;
throw rsfatal();
}
+ else if( cmdname == "collMod" ) {
+ if ( o.nFields() == 2 &&
+ o["usePowerOf2Sizes"].type() == Bool ) {
+ log() << "replSet not rolling back change of usePowerOf2Sizes: " << o;
+ }
+ else {
+ log() << "replSet error cannot rollback a collMod command: " << o;
+ throw rsfatal();
+ }
+ }
else {
log() << "replSet error can't rollback this command yet: " << o.toString() << rsLog;
log() << "replSet cmdname=" << cmdname << rsLog;
diff --git a/src/mongo/db/ttl.cpp b/src/mongo/db/ttl.cpp
index 66c0ee7ee7f..4019a550b2b 100644
--- a/src/mongo/db/ttl.cpp
+++ b/src/mongo/db/ttl.cpp
@@ -81,6 +81,12 @@ namespace mongo {
error() << "key for ttl index can only have 1 field" << endl;
continue;
}
+ if (!idx[secondsExpireField].isNumber()) {
+ error() << "ttl indexes require the " << secondsExpireField << " field to be "
+ << "numeric but received a type of: "
+ << typeName(idx[secondsExpireField].type()) << endl;
+ continue;
+ }
BSONObj query;
{
diff --git a/src/mongo/dbtests/documenttests.cpp b/src/mongo/dbtests/documenttests.cpp
index 57bf7f8b55d..56e51176f1d 100644
--- a/src/mongo/dbtests/documenttests.cpp
+++ b/src/mongo/dbtests/documenttests.cpp
@@ -170,6 +170,22 @@ namespace DocumentTests {
ASSERT( md.peek().getValue( "c" ).missing() );
assertRoundTrips( md.peek() );
+ // Set a nested field using []
+ md["x"]["y"]["z"] = Value("nested");
+ ASSERT_EQUALS(md.peek()["x"]["y"]["z"], Value("nested"));
+
+ // Set a nested field using setNestedField
+ FieldPath xxyyzz = string("xx.yy.zz");
+ md.setNestedField(xxyyzz, Value("nested"));
+ ASSERT_EQUALS(md.peek().getNestedField(xxyyzz), Value("nested") );
+
+ // Set a nested fields through an existing empty document
+ md["xxx"] = Value(Document());
+ md["xxx"]["yyy"] = Value(Document());
+ FieldPath xxxyyyzzz = string("xxx.yyy.zzz");
+ md.setNestedField(xxxyyyzzz, Value("nested"));
+ ASSERT_EQUALS(md.peek().getNestedField(xxxyyyzzz), Value("nested") );
+
// Make sure nothing moved
ASSERT_EQUALS(apos, md.peek().positionOf("a"));
ASSERT_EQUALS(bpos, md.peek().positionOf("c"));
diff --git a/src/mongo/s/client_info.cpp b/src/mongo/s/client_info.cpp
index b373e54c010..7dc83aff003 100644
--- a/src/mongo/s/client_info.cpp
+++ b/src/mongo/s/client_info.cpp
@@ -268,41 +268,39 @@ namespace mongo {
}
clearSinceLastGetError();
- LOG(4) << "checking " << writebacks.size() << " writebacks for"
- << " gle (" << theShard << ")" << endl;
-
- if ( writebacks.size() ){
- vector<BSONObj> v = _handleWriteBacks( writebacks , fromWriteBackListener );
- if ( v.size() == 0 && fromWriteBackListener ) {
- // ok
+ // We never need to handle writebacks if we're coming from the wbl itself
+ if ( writebacks.size() && !fromWriteBackListener ){
+
+ LOG(4) << "checking " << writebacks.size() << " writebacks for"
+ << " gle (" << theShard << ")" << endl;
+
+ vector<BSONObj> v = _handleWriteBacks( writebacks , false );
+
+ // this will usually be 1
+ // it can be greater than 1 if a write to a different shard
+ // than the last write op had a writeback
+ // all we're going to report is the first
+ // since that's the current write
+ // but we block for all
+ verify( v.size() >= 1 );
+
+ if ( res["writebackSince"].numberInt() > 0 ) {
+ // got writeback from older op
+ // ignore the result from it, just needed to wait
+ result.appendElements( res );
+ }
+ else if ( writebacks[0].fromLastOperation ) {
+ result.appendElements( v[0] );
+ result.appendElementsUnique( res );
+ result.append( "writebackGLE" , v[0] );
+ result.append( "initialGLEHost" , theShard );
+ result.append( "initialGLE", res );
}
else {
- // this will usually be 1
- // it can be greater than 1 if a write to a different shard
- // than the last write op had a writeback
- // all we're going to report is the first
- // since that's the current write
- // but we block for all
- verify( v.size() >= 1 );
-
- if ( res["writebackSince"].numberInt() > 0 ) {
- // got writeback from older op
- // ignore the result from it, just needed to wait
- result.appendElements( res );
- }
- else if ( writebacks[0].fromLastOperation ) {
- result.appendElements( v[0] );
- result.appendElementsUnique( res );
- result.append( "writebackGLE" , v[0] );
- result.append( "initialGLEHost" , theShard );
- result.append( "initialGLE", res );
- }
- else {
- // there was a writeback
- // but its from an old operations
- // so all that's important is that we block, not that we return stats
- result.appendElements( res );
- }
+ // there was a writeback
+ // but its from an old operations
+ // so all that's important is that we block, not that we return stats
+ result.appendElements( res );
}
}
else {
@@ -406,6 +404,10 @@ namespace mongo {
LOG(4) << "checking " << writebacks.size() << " writebacks for"
<< " gle (" << shards->size() << " shards)" << endl;
+ // Multi-shard results from the writeback listener implicitly means that:
+ // A) no versioning was used (multi-update/delete)
+ // B) internal GLE was used (bulk insert)
+
if ( errors.size() == 0 ) {
result.appendNull( "err" );
_handleWriteBacks( writebacks , fromWriteBackListener );
diff --git a/src/mongo/s/commands_public.cpp b/src/mongo/s/commands_public.cpp
index 1c2ff52364a..f69ef9fe1bb 100644
--- a/src/mongo/s/commands_public.cpp
+++ b/src/mongo/s/commands_public.cpp
@@ -1771,6 +1771,8 @@ namespace mongo {
int options, string &errmsg,
BSONObjBuilder &result, bool fromRepl);
+ virtual bool passOptions() const { return true; }
+
private:
};
@@ -1815,7 +1817,7 @@ namespace mongo {
*/
DBConfigPtr conf(grid.getDBConfig(dbName , false));
if (!conf || !conf->isShardingEnabled() || !conf->isSharded(fullns))
- return passthrough(conf, cmdObj, result);
+ return passthrough(conf, cmdObj, options, result);
/* split the pipeline into pieces for mongods and this mongos */
intrusive_ptr<Pipeline> pShardPipeline(
diff --git a/src/mongo/s/shard.cpp b/src/mongo/s/shard.cpp
index df620865603..28b281f0cbd 100644
--- a/src/mongo/s/shard.cpp
+++ b/src/mongo/s/shard.cpp
@@ -30,6 +30,7 @@
#include "mongo/db/auth/privilege.h"
#include "mongo/db/commands.h"
#include "mongo/db/jsobj.h"
+#include "mongo/db/server_parameters.h"
#include "mongo/s/client_info.h"
#include "mongo/s/config.h"
#include "mongo/s/request.h"
@@ -39,6 +40,8 @@
namespace mongo {
+ MONGO_EXPORT_SERVER_PARAMETER(authOnPrimaryOnly, bool, true);
+
class StaticShardInfo {
public:
StaticShardInfo() : _mutex("StaticShardInfo"), _rsMutex("RSNameMap") { }
@@ -411,14 +414,26 @@ namespace mongo {
void ShardingConnectionHook::onCreate( DBClientBase * conn ) {
if( !noauth ) {
+ bool result;
string err;
LOG(2) << "calling onCreate auth for " << conn->toString() << endl;
- bool result = conn->auth( "local",
- internalSecurity.user,
- internalSecurity.pwd,
- err,
- false );
+ if ( conn->type() == ConnectionString::SET && !authOnPrimaryOnly ) {
+ DBClientReplicaSet* setConn = dynamic_cast<DBClientReplicaSet*>(conn);
+ verify(setConn);
+ result = setConn->authAny( "local",
+ internalSecurity.user,
+ internalSecurity.pwd,
+ err,
+ false );
+ }
+ else {
+ result = conn->auth( "local",
+ internalSecurity.user,
+ internalSecurity.pwd,
+ err,
+ false );
+ }
uassert( 15847, str::stream() << "can't authenticate to server "
<< conn->getServerAddress() << causedBy( err ), result );
diff --git a/src/mongo/s/shard.h b/src/mongo/s/shard.h
index e917baa0986..41f819d3ae9 100644
--- a/src/mongo/s/shard.h
+++ b/src/mongo/s/shard.h
@@ -290,8 +290,12 @@ namespace mongo {
*/
bool runCommand( const string& db , const BSONObj& cmd , BSONObj& res );
+ // Whether or not we release connections from the thread-local cache after a read
static bool releaseConnectionsAfterResponse;
+ // Controls whether we throw on initially failing to set a version
+ static bool ignoreInitialVersionFailure;
+
/** checks all of my thread local connections for the version of this ns */
static void checkMyConnectionVersions( const string & ns );
@@ -307,6 +311,11 @@ namespace mongo {
*/
static void clearPool();
+ /**
+ * Forgets a namespace to prevent future versioning.
+ */
+ static void forgetNS( const string& ns );
+
private:
void _init();
void _finishInit();
diff --git a/src/mongo/s/shardconnection.cpp b/src/mongo/s/shardconnection.cpp
index 61b47904147..66a105f05e8 100644
--- a/src/mongo/s/shardconnection.cpp
+++ b/src/mongo/s/shardconnection.cpp
@@ -22,6 +22,7 @@
#include "mongo/db/client.h"
#include "mongo/db/commands.h"
+#include "mongo/db/lasterror.h"
#include "mongo/db/server_parameters.h"
#include "mongo/s/config.h"
#include "mongo/s/request.h"
@@ -33,6 +34,14 @@
namespace mongo {
+ bool ShardConnection::ignoreInitialVersionFailure( false );
+ ExportedServerParameter<bool>
+ _ignoreInitialVersionFailure( ServerParameterSet::getGlobal(),
+ "ignoreInitialVersionFailure",
+ &ShardConnection::ignoreInitialVersionFailure,
+ true,
+ true );
+
DBConnectionPool shardConnectionPool;
class ClientConnections;
@@ -235,6 +244,12 @@ namespace mongo {
vector<Shard> all;
Shard::getAllShards( all );
+ scoped_ptr<LastError::Disabled> ignoreForGLE;
+ if ( ShardConnection::ignoreInitialVersionFailure ) {
+ // Don't report exceptions here as errors in GetLastError if ignoring failures
+ ignoreForGLE.reset( new LastError::Disabled( lastError.get( false ) ) );
+ }
+
// Now only check top-level shard connections
for ( unsigned i=0; i<all.size(); i++ ) {
@@ -250,11 +265,18 @@ namespace mongo {
versionManager.checkShardVersionCB( s->avail, ns, false, 1 );
}
- catch ( const std::exception& e ) {
+ catch ( const DBException& ex ) {
- warning() << "problem while initially checking shard versions on"
- << " " << shard.getName() << causedBy(e) << endl;
- throw;
+ warning() << "problem while initially checking shard versions on "
+ << shard.getName() << causedBy( ex ) << endl;
+
+ if ( !ShardConnection::ignoreInitialVersionFailure ) {
+ throw;
+ }
+ else {
+ // We swallow the error here, checking shard version here is a heuristic to
+ // prevent later stale config exceptions, not required for correctness.
+ }
}
}
}
@@ -322,6 +344,11 @@ namespace mongo {
_hosts.clear();
}
+ void forgetNS( const string& ns ) {
+ scoped_spinlock lock( _lock );
+ _seenNS.erase( ns );
+ }
+
// -----
static thread_specific_ptr<ClientConnections> _perThread;
@@ -487,4 +514,8 @@ namespace mongo {
shardConnectionPool.clear();
ClientConnections::threadInstance()->clearPool();
}
+
+ void ShardConnection::forgetNS( const string& ns ) {
+ ClientConnections::threadInstance()->forgetNS( ns );
+ }
}
diff --git a/src/mongo/s/strategy_shard.cpp b/src/mongo/s/strategy_shard.cpp
index 0d4adcef155..560a4ac72d4 100644
--- a/src/mongo/s/strategy_shard.cpp
+++ b/src/mongo/s/strategy_shard.cpp
@@ -192,15 +192,10 @@ namespace mongo {
string host = cursorCache.getRef( id );
if( host.size() == 0 ){
-
- //
- // Match legacy behavior here by throwing an exception when we can't find
- // the cursor, but make the exception more informative
- //
-
- uasserted( 16336,
- str::stream() << "could not find cursor in cache for id " << id
- << " over collection " << ns );
+ LOG(3) << "could not find cursor in cache for id " << id
+ << " over collection " << ns << endl;
+ replyToQuery( ResultFlag_CursorNotFound , r.p() , r.m() , 0 , 0 , 0 );
+ return;
}
// we used ScopedDbConnection because we don't get about config versions
@@ -227,11 +222,9 @@ namespace mongo {
int ntoreturn = r.d().pullInt();
long long id = r.d().pullInt64();
- LOG(6) << "want cursor : " << id << endl;
-
ShardedClientCursorPtr cursor = cursorCache.get( id );
if ( ! cursor ) {
- LOG(6) << "\t invalid cursor :(" << endl;
+ LOG(3) << "Invalid cursor:" << id << endl;
replyToQuery( ResultFlag_CursorNotFound , r.p() , r.m() , 0 , 0 , 0 );
return;
}
diff --git a/src/mongo/s/type_shard.cpp b/src/mongo/s/type_shard.cpp
index 9b6fd2fdb78..a8f4236aa5d 100644
--- a/src/mongo/s/type_shard.cpp
+++ b/src/mongo/s/type_shard.cpp
@@ -87,7 +87,7 @@ namespace mongo {
if (fieldState == FieldParser::FIELD_INVALID) return false;
_isDrainingSet = fieldState == FieldParser::FIELD_SET;
- fieldState = FieldParser::extract(source, maxSize, &_maxSize, errMsg);
+ fieldState = FieldParser::extractNumber(source, maxSize, &_maxSize, errMsg);
if (fieldState == FieldParser::FIELD_INVALID) return false;
_isMaxSizeSet = fieldState == FieldParser::FIELD_SET;
diff --git a/src/mongo/s/type_shard_test.cpp b/src/mongo/s/type_shard_test.cpp
index dda55a5daaf..c75db61ec26 100644
--- a/src/mongo/s/type_shard_test.cpp
+++ b/src/mongo/s/type_shard_test.cpp
@@ -74,6 +74,17 @@ namespace {
ASSERT_TRUE(shard.isValid(NULL));
}
+ TEST(Validity, MaxSizeAsFloat) {
+ ShardType shard;
+ BSONObj obj = BSON(ShardType::name("shard0000") <<
+ ShardType::host("localhost:27017") <<
+ ShardType::maxSize() << 100.0);
+ string errMsg;
+ ASSERT(shard.parseBSON(obj, &errMsg));
+ ASSERT_EQUALS(errMsg, "");
+ ASSERT_TRUE(shard.isValid(NULL));
+ }
+
TEST(Validity, BadType) {
ShardType shard;
BSONObj obj = BSON(ShardType::name() << 0);
diff --git a/src/mongo/s/version_manager.cpp b/src/mongo/s/version_manager.cpp
index 1496ebc1407..3ba6254813b 100644
--- a/src/mongo/s/version_manager.cpp
+++ b/src/mongo/s/version_manager.cpp
@@ -103,23 +103,46 @@ namespace mongo {
WriteBackListener::init( *conn_in );
- DBClientBase* conn = getVersionable( conn_in );
- verify( conn ); // errors thrown above
+ bool ok;
+ DBClientBase* conn = NULL;
+ try {
+ // May throw if replica set primary is down
+ conn = getVersionable( conn_in );
+ dassert( conn ); // errors thrown above
+
+ BSONObjBuilder cmdBuilder;
+
+ cmdBuilder.append( "setShardVersion" , "" );
+ cmdBuilder.appendBool( "init", true );
+ cmdBuilder.append( "configdb" , configServer.modelServer() );
+ cmdBuilder.appendOID( "serverID" , &serverID );
+ cmdBuilder.appendBool( "authoritative" , true );
- BSONObjBuilder cmdBuilder;
+ BSONObj cmd = cmdBuilder.obj();
- cmdBuilder.append( "setShardVersion" , "" );
- cmdBuilder.appendBool( "init", true );
- cmdBuilder.append( "configdb" , configServer.modelServer() );
- cmdBuilder.appendOID( "serverID" , &serverID );
- cmdBuilder.appendBool( "authoritative" , true );
+ LOG(1) << "initializing shard connection to " << conn->toString() << endl;
+ LOG(2) << "initial sharding settings : " << cmd << endl;
+
+ ok = conn->runCommand("admin", cmd, result, 0);
+ }
+ catch( const DBException& ex ) {
- BSONObj cmd = cmdBuilder.obj();
+ bool ignoreFailure = ShardConnection::ignoreInitialVersionFailure
+ && conn_in->type() == ConnectionString::SET;
+ if ( !ignoreFailure )
+ throw;
- LOG(1) << "initializing shard connection to " << conn->toString() << endl;
- LOG(2) << "initial sharding settings : " << cmd << endl;
+ // Using initShardVersion is not strictly required when talking to replica sets - it is
+ // preferred to do so because it registers mongos early with the mongod. This info is
+ // also sent by checkShardVersion before a connection is used for a write or read.
- bool ok = conn->runCommand("admin", cmd, result, 0);
+ OCCASIONALLY {
+ warning() << "failed to initialize new replica set connection version, "
+ << "will initialize on first use" << endl;
+ }
+
+ return true;
+ }
// HACK for backwards compatibility with v1.8.x, v2.0.0 and v2.0.1
// Result is false, but will still initialize serverID and configdb
diff --git a/src/mongo/s/writeback_listener.cpp b/src/mongo/s/writeback_listener.cpp
index 255ab0160e5..0695c4cec56 100644
--- a/src/mongo/s/writeback_listener.cpp
+++ b/src/mongo/s/writeback_listener.cpp
@@ -345,6 +345,9 @@ namespace mongo {
gle = b.obj();
}
+ dassert( !gle.isEmpty() );
+ verify( !gle.isEmpty() );
+
if ( gle["code"].numberInt() == 9517 ) {
log() << "new version change detected, "
diff --git a/src/mongo/scripting/engine.cpp b/src/mongo/scripting/engine.cpp
index fc4d042c2af..e6bb8be819e 100644
--- a/src/mongo/scripting/engine.cpp
+++ b/src/mongo/scripting/engine.cpp
@@ -39,7 +39,7 @@ namespace mongo {
Scope::Scope() : _localDBName(""),
_loadedVersion(0),
- _numTimeUsed(0),
+ _numTimesUsed(0),
_lastRetIsNativeCode(false) {
}
@@ -259,90 +259,80 @@ namespace mongo {
injectNative("benchFinish", BenchRunner::benchFinish);
}
- typedef map<string, list<Scope*> > PoolToScopes;
-
+namespace {
class ScopeCache {
public:
- ScopeCache() : _mutex("ScopeCache") {
- }
+ ScopeCache() : _mutex("ScopeCache") {}
- ~ScopeCache() {
- if (inShutdown())
- return;
- clear();
- }
-
- void done(const string& pool, Scope* s) {
+ void release(const string& poolName, const boost::shared_ptr<Scope>& scope) {
scoped_lock lk(_mutex);
- list<Scope*>& l = _pools[pool];
- bool oom = s->hasOutOfMemoryException();
- // do not keep too many contexts, or use them for too long
- if (l.size() > 10 || s->getTimeUsed() > 10 || oom || !s->getError().empty()) {
- delete s;
- }
- else {
- l.push_back(s);
- s->reset();
+ if (scope->hasOutOfMemoryException()) {
+ // make some room
+ log() << "Clearing all idle JS contexts due to out of memory" << endl;
+ _pools.clear();
+ return;
}
- if (oom) {
- // out of mem, make some room
- log() << "Clearing all idle JS contexts due to out of memory" << endl;
- clear();
+ if (scope->getTimesUsed() > kMaxScopeReuse)
+ return; // used too many times to save
+
+ if (!scope->getError().empty())
+ return; // not saving errored scopes
+
+ if (_pools.size() >= kMaxPoolSize) {
+ // prefer to keep recently-used scopes
+ _pools.pop_back();
}
+
+ ScopeAndPool toStore = {scope, poolName};
+ _pools.push_front(toStore);
}
- Scope* get(const string& pool) {
+ boost::shared_ptr<Scope> tryAcquire(const string& poolName) {
scoped_lock lk(_mutex);
- list<Scope*>& l = _pools[pool];
- if (l.size() == 0)
- return 0;
-
- Scope* s = l.back();
- l.pop_back();
- s->reset();
- s->incTimeUsed();
- return s;
- }
- void clear() {
- set<Scope*> seen;
- for (PoolToScopes::iterator i = _pools.begin(); i != _pools.end(); ++i) {
- for (list<Scope*>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
- Scope* s = *j;
- fassert(16652, seen.insert(s).second);
- delete s;
+ for (Pools::iterator it = _pools.begin(); it != _pools.end(); ++it) {
+ if (it->poolName == poolName) {
+ boost::shared_ptr<Scope> scope = it->scope;
+ _pools.erase(it);
+ scope->incTimesUsed();
+ scope->reset();
+ return scope;
}
}
- _pools.clear();
+
+ return boost::shared_ptr<Scope>();
}
private:
- PoolToScopes _pools;
+ struct ScopeAndPool {
+ boost::shared_ptr<Scope> scope;
+ string poolName;
+ };
+
+ // Note: if these numbers change, reconsider choice of datastructure for _pools
+ static const unsigned kMaxPoolSize = 10;
+ static const int kMaxScopeReuse = 10;
+
+ typedef deque<ScopeAndPool> Pools; // More-recently used Scopes are kept at the front.
+ Pools _pools; // protected by _mutex
mongo::mutex _mutex;
};
- thread_specific_ptr<ScopeCache> scopeCache;
+ ScopeCache scopeCache;
+} // anonymous namespace
class PooledScope : public Scope {
public:
- PooledScope(const std::string& pool, Scope* real) : _pool(pool), _real(real) {
+ PooledScope(const std::string& pool, const boost::shared_ptr<Scope>& real)
+ : _pool(pool)
+ , _real(real) {
_real->loadStored(true);
- };
+ }
+
virtual ~PooledScope() {
- ScopeCache* sc = scopeCache.get();
- if (sc) {
- sc->done(_pool, _real);
- _real = NULL;
- }
- else {
- // this means that the Scope was killed from a different thread
- // for example a cursor got timed out that has a $where clause
- LOG(3) << "warning: scopeCache is empty!" << endl;
- delete _real;
- _real = 0;
- }
+ scopeCache.release(_pool, _real);
}
// wrappers for the derived (_real) scope
@@ -404,31 +394,24 @@ namespace mongo {
private:
string _pool;
- Scope* _real;
+ boost::shared_ptr<Scope> _real;
};
/** Get a scope from the pool of scopes matching the supplied pool name */
- auto_ptr<Scope> ScriptEngine::getPooledScope(const string& pool, const string& scopeType) {
- if (!scopeCache.get())
- scopeCache.reset(new ScopeCache());
-
- Scope* s = scopeCache->get(pool + scopeType);
- if (!s)
- s = newScope();
+ auto_ptr<Scope> ScriptEngine::getPooledScope(const string& db, const string& scopeType) {
+ const string fullPoolName = db + scopeType;
+ boost::shared_ptr<Scope> s = scopeCache.tryAcquire(fullPoolName);
+ if (!s) {
+ s.reset(newScope());
+ }
auto_ptr<Scope> p;
- p.reset(new PooledScope(pool + scopeType, s));
- p->setLocalDB(pool);
+ p.reset(new PooledScope(fullPoolName, s));
+ p->setLocalDB(db);
p->loadStored(true);
return p;
}
- void ScriptEngine::threadDone() {
- ScopeCache* sc = scopeCache.get();
- if (sc)
- sc->clear();
- }
-
void (*ScriptEngine::_connectCallback)(DBClientWithCommands&) = 0;
const char* (*ScriptEngine::_checkInterruptCallback)() = 0;
unsigned (*ScriptEngine::_getCurrentOpIdCallback)() = 0;
diff --git a/src/mongo/scripting/engine.h b/src/mongo/scripting/engine.h
index 8233128e736..e0a3486a6ae 100644
--- a/src/mongo/scripting/engine.h
+++ b/src/mongo/scripting/engine.h
@@ -135,10 +135,10 @@ namespace mongo {
static void validateObjectIdString(const string& str);
/** increments the number of times a scope was used */
- void incTimeUsed() { ++_numTimeUsed; }
+ void incTimesUsed() { ++_numTimesUsed; }
/** gets the number of times a scope was used */
- int getTimeUsed() { return _numTimeUsed; }
+ int getTimesUsed() { return _numTimesUsed; }
/** return true if last invoke() return'd native code */
virtual bool isLastRetNativeCode() { return _lastRetIsNativeCode; }
@@ -168,7 +168,7 @@ namespace mongo {
set<string> _storedNames;
static long long _lastVersion;
FunctionCacheMap _cachedFunctions;
- int _numTimeUsed;
+ int _numTimesUsed;
bool _lastRetIsNativeCode; // v8 only: set to true if eval'd script returns a native func
};
@@ -188,15 +188,12 @@ namespace mongo {
static void setup();
/** gets a scope from the pool or a new one if pool is empty
- * @param pool An identifier for the pool, usually the db name
+ * @param db The db name
+ * @param scopeType A unique id to limit scope sharing.
+ * This must include authenticated users.
* @return the scope
*/
- auto_ptr<Scope> getPooledScope(const string& pool, const string& scopeType);
-
- /**
- * call this method to release some JS resources when a thread is done
- */
- void threadDone();
+ auto_ptr<Scope> getPooledScope(const string& db, const string& scopeType);
void setScopeInitCallback(void (*func)(Scope&)) { _scopeInitCallback = func; }
static void setConnectCallback(void (*func)(DBClientWithCommands&)) {
diff --git a/src/mongo/shell/assert.js b/src/mongo/shell/assert.js
index b1279028307..0e84c52d11c 100644
--- a/src/mongo/shell/assert.js
+++ b/src/mongo/shell/assert.js
@@ -225,3 +225,41 @@ assert.close = function(a, b, msg, places){
doassert(a + " is not equal to " + b + " within " + places +
" places, diff: " + (a-b) + " : " + msg);
};
+
+assert.gleSuccess = function(db, msg) {
+ var gle = db.getLastErrorObj();
+ if (gle.err) {
+ if (typeof(msg) == "function")
+ msg = msg(gle);
+ doassert("getLastError not null:" + tojson(gle) + " :" + msg);
+ }
+}
+
+assert.gleError = function(db, msg) {
+ var gle = db.getLastErrorObj();
+ if (!gle.err) {
+ if (typeof(msg) == "function")
+ msg = msg(gle);
+ doassert("getLastError is null: " + tojson(gle) + " :" + msg);
+ }
+}
+
+assert.gleErrorCode = function(db, code, msg) {
+ var gle = db.getLastErrorObj();
+ if (gle.err && (gle.code == code)) {
+ if (typeof(msg) == "function")
+ msg = msg(gle);
+ doassert("getLastError not null or missing code( " + code + "): "
+ + tojson(gle) + " :" + msg);
+ }
+}
+
+assert.gleErrorRegex = function(db, regex, msg) {
+ var gle = db.getLastErrorObj();
+ if (!gle.err || !regex.test(gle.err)) {
+ if (typeof(msg) == "function")
+ msg = msg(gle);
+ doassert("getLastError is null or doesn't match regex (" + regex + "): "
+ + tojson(gle) + " :" + msg);
+ }
+}
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index 5934e862076..6a790ee2adb 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -75,7 +75,9 @@ void generateCompletions( const string& prefix , vector<string>& all ) {
try {
BSONObj args = BSON( "0" << prefix );
- shellMainScope->invokeSafe( "function callShellAutocomplete(x) {shellAutocomplete(x)}", &args, 0, 1000 );
+ shellMainScope->invokeSafe("function callShellAutocomplete(x) {shellAutocomplete(x)}",
+ &args,
+ NULL);
BSONObjBuilder b;
shellMainScope->append( b , "" , "__autocomplete__" );
BSONObj res = b.obj();
diff --git a/src/mongo/tools/restore.cpp b/src/mongo/tools/restore.cpp
index abd7f868afe..625902224ef 100644
--- a/src/mongo/tools/restore.cpp
+++ b/src/mongo/tools/restore.cpp
@@ -484,34 +484,29 @@ private:
return nfields == obj2.nFields();
}
- void createCollectionWithOptions(BSONObj cmdObj) {
+ void createCollectionWithOptions(BSONObj obj) {
+ BSONObjIterator i(obj);
- // Create a new cmdObj to skip undefined fields and fix collection name
+ // Rebuild obj as a command object for the "create" command.
+ // - {create: <name>} comes first, where <name> is the new name for the collection
+ // - elements with type Undefined get skipped over
BSONObjBuilder bo;
-
- // Add a "create" field if it doesn't exist
- if (!cmdObj.hasField("create")) {
- bo.append("create", _curcoll);
- }
-
- BSONObjIterator i(cmdObj);
- while ( i.more() ) {
+ bo.append("create", _curcoll);
+ while (i.more()) {
BSONElement e = i.next();
- // Replace the "create" field with the name of the collection we are actually creating
if (strcmp(e.fieldName(), "create") == 0) {
- bo.append("create", _curcoll);
+ continue;
}
- else {
- if (e.type() == Undefined) {
- log() << _curns << ": skipping undefined field: " << e.fieldName() << endl;
- }
- else {
- bo.append(e);
- }
+
+ if (e.type() == Undefined) {
+ log() << _curns << ": skipping undefined field: " << e.fieldName() << endl;
+ continue;
}
+
+ bo.append(e);
}
- cmdObj = bo.obj();
+ obj = bo.obj();
BSONObj fields = BSON("options" << 1);
scoped_ptr<DBClientCursor> cursor(conn().query(_curdb + ".system.namespaces", Query(BSON("name" << _curns)), 0, 0, &fields));
@@ -519,8 +514,8 @@ private:
bool createColl = true;
if (cursor->more()) {
createColl = false;
- BSONObj obj = cursor->next();
- if (!obj.hasField("options") || !optionsSame(cmdObj, obj["options"].Obj())) {
+ BSONObj nsObj = cursor->next();
+ if (!nsObj.hasField("options") || !optionsSame(obj, nsObj["options"].Obj())) {
log() << "WARNING: collection " << _curns << " exists with different options than are in the metadata.json file and not using --drop. Options in the metadata file will be ignored." << endl;
}
}
@@ -530,10 +525,10 @@ private:
}
BSONObj info;
- if (!conn().runCommand(_curdb, cmdObj, info)) {
+ if (!conn().runCommand(_curdb, obj, info)) {
uasserted(15936, "Creating collection " + _curns + " failed. Errmsg: " + info["errmsg"].String());
} else {
- log() << "\tCreated collection " << _curns << " with options: " << cmdObj.jsonString() << endl;
+ log() << "\tCreated collection " << _curns << " with options: " << obj.jsonString() << endl;
}
}
diff --git a/src/mongo/util/net/listen.cpp b/src/mongo/util/net/listen.cpp
index 4ba94d19c90..b92a3266a14 100644
--- a/src/mongo/util/net/listen.cpp
+++ b/src/mongo/util/net/listen.cpp
@@ -262,10 +262,14 @@ namespace mongo {
int s = accept(*it, from.raw(), &from.addressSize);
if ( s < 0 ) {
int x = errno; // so no global issues
- if ( x == ECONNABORTED || x == EBADF ) {
- log() << "Listener on port " << _port << " aborted" << endl;
+ if (x == EBADF) {
+ log() << "Port " << _port << " is no longer valid" << endl;
return;
}
+ else if (x == ECONNABORTED) {
+ log() << "Connection on port " << _port << " aborted" << endl;
+ continue;
+ }
if ( x == 0 && inShutdown() ) {
return; // socket closed
}
@@ -461,9 +465,13 @@ namespace mongo {
int s = accept(socks[eventIndex], from.raw(), &from.addressSize);
if ( s < 0 ) {
int x = errno; // so no global issues
- if ( x == ECONNABORTED || x == EBADF ) {
+ if (x == EBADF) {
+ log() << "Port " << _port << " is no longer valid" << endl;
+ continue;
+ }
+ else if (x == ECONNABORTED) {
log() << "Listener on port " << _port << " aborted" << endl;
- return;
+ continue;
}
if ( x == 0 && inShutdown() ) {
return; // socket closed
diff --git a/src/mongo/util/net/ssl_manager.cpp b/src/mongo/util/net/ssl_manager.cpp
index 83ecd5923da..dd8b3a2fe6f 100644
--- a/src/mongo/util/net/ssl_manager.cpp
+++ b/src/mongo/util/net/ssl_manager.cpp
@@ -51,13 +51,9 @@ namespace mongo {
SSLThreadInfo() {
_id = ++_next;
- CRYPTO_set_id_callback(_ssl_id_callback);
- CRYPTO_set_locking_callback(_ssl_locking_callback);
}
- ~SSLThreadInfo() {
- CRYPTO_set_id_callback(0);
- }
+ ~SSLThreadInfo() {}
unsigned long id() const { return _id; }
@@ -150,14 +146,11 @@ namespace mongo {
// Note: this is for blocking sockets only.
SSL_CTX_set_mode(_context, SSL_MODE_AUTO_RETRY);
- // Set context within which session can be reused
- int status = SSL_CTX_set_session_id_context(
- _context,
- static_cast<unsigned char*>(static_cast<void*>(&_context)),
- sizeof(_context));
- if (!status) {
- uasserted(16768,"ssl initialization problem");
- }
+ // Disable session caching (see SERVER-10261)
+ SSL_CTX_set_session_cache_mode(_context, SSL_SESS_CACHE_OFF);
+
+ CRYPTO_set_id_callback(_ssl_id_callback);
+ CRYPTO_set_locking_callback(_ssl_locking_callback);
SSLThreadInfo::init();
SSLThreadInfo::get();
diff --git a/src/mongo/util/progress_meter.cpp b/src/mongo/util/progress_meter.cpp
index 57ea9f728ec..d0f99ddff94 100644
--- a/src/mongo/util/progress_meter.cpp
+++ b/src/mongo/util/progress_meter.cpp
@@ -53,9 +53,13 @@ namespace mongo {
if ( _total > 0 ) {
int per = (int)( ( (double)_done * 100.0 ) / (double)_total );
- Nullstream& out = log() << "\t\t" << _name << ": " << _done
- << '/' << _total << '\t' << per << '%';
+ Nullstream& out = log();
+ out << "\t\t" << _name << ": " << _done;
+ if (_showTotal) {
+ out << '/' << _total << '\t' << per << '%';
+ }
+
if ( ! _units.empty() ) {
out << "\t(" << _units << ")";
}
diff --git a/src/mongo/util/progress_meter.h b/src/mongo/util/progress_meter.h
index 88dbe29a0f5..baeb4680391 100644
--- a/src/mongo/util/progress_meter.h
+++ b/src/mongo/util/progress_meter.h
@@ -30,12 +30,13 @@ namespace mongo {
int checkInterval = 100,
std::string units = "",
std::string name = "Progress")
- : _units(units)
- , _name(name) {
+ : _showTotal(true),
+ _units(units),
+ _name(name) {
reset( total , secondsBetween , checkInterval );
}
- ProgressMeter() : _active(0), _units(""), _name("Progress") {}
+ ProgressMeter() : _active(0), _showTotal(true), _units(""), _name("Progress") {}
// typically you do ProgressMeterHolder
void reset( unsigned long long total , int secondsBetween = 3 , int checkInterval = 100 );
@@ -65,6 +66,10 @@ namespace mongo {
unsigned long long total() const { return _total; }
+ void showTotal(bool doShow) {
+ _showTotal = doShow;
+ }
+
std::string toString() const;
bool operator==( const ProgressMeter& other ) const { return this == &other; }
@@ -74,6 +79,7 @@ namespace mongo {
bool _active;
unsigned long long _total;
+ bool _showTotal;
int _secondsBetween;
int _checkInterval;
diff --git a/src/mongo/util/version.cpp b/src/mongo/util/version.cpp
index 25fe64b5811..a5622e4dbbb 100644
--- a/src/mongo/util/version.cpp
+++ b/src/mongo/util/version.cpp
@@ -47,7 +47,7 @@ namespace mongo {
* 1.2.3-rc4-pre-
* If you really need to do something else you'll need to fix _versionArray()
*/
- const char versionString[] = "2.4.6";
+ const char versionString[] = "2.4.9";
// See unit test for example outputs
BSONArray toVersionArray(const char* version){
diff --git a/src/third_party/v8/src/spaces.h b/src/third_party/v8/src/spaces.h
index 6602c899dfb..d7a79c6f983 100644
--- a/src/third_party/v8/src/spaces.h
+++ b/src/third_party/v8/src/spaces.h
@@ -321,7 +321,8 @@ class MemoryChunk {
Space* owner() const {
if ((reinterpret_cast<intptr_t>(owner_) & kFailureTagMask) ==
kFailureTag) {
- return reinterpret_cast<Space*>(owner_ - kFailureTag);
+ return reinterpret_cast<Space*>(reinterpret_cast<intptr_t>(owner_) -
+ kFailureTag);
} else {
return NULL;
}