Compare commits

...

59 Commits

Author SHA1 Message Date
Dan Pasette
f570771a5d BUMP 2.2.3 2013-01-31 10:18:33 -05:00
Dan Pasette
94377aa2b3 post 2.2.3-rc1 2013-01-24 23:17:18 -05:00
Dan Pasette
e753f6e482 BUMP 2.2.3-rc1 2013-01-24 10:22:47 -05:00
Andy Schwerin
0a3b5f08e6 SERVER-7434 Do not spawn a thread in grandparent process when using --fork. 2013-01-23 10:13:24 -05:00
Dan Pasette
0adb962353 SERVER-8132: removed unordered_* and re-ordered imports in hostandport.h
to enable backport to 2.2.
2013-01-22 18:31:46 -05:00
Eliot Horowitz
0bab4de510 SERVER-8097: make test more stable 2013-01-22 17:37:16 -05:00
Greg Studer
ea9c49e06a SERVER-8255 warn and abort if starting from-side zero-version migration 2013-01-22 16:46:39 -05:00
Greg Studer
ffb92e80ed SERVER-8255: buildbot sharding_balance4.js DEBUG sorting chunks by lastmod
necessary when retrieving otherwise cursor yields can miss newer versions
2013-01-22 16:46:12 -05:00
Eliot Horowitz
31150cee4c SERVER-8132: fix caching of writeback init 2013-01-22 16:10:49 -05:00
Eliot Horowitz
109f6fbde0 SERVER-8132: speed up getServerAddress 2013-01-22 16:10:39 -05:00
Scott Hernandez
ededb03f21 SERVER-8117: listDatabases - config/admin db should come from config servers 2013-01-22 16:10:18 -05:00
Eliot Horowitz
5c2f3da947 post 2.2.3-rc0 2013-01-12 00:40:10 -05:00
Kristina
511cbba4d2 Revert "SERVER-7652 Use minvalid to determine if initial sync is needed"
This reverts commit 832a4c0ccf.
2013-01-10 16:26:16 -05:00
Greg Studer
baaa9118f9 SERVER-8128 require a database be specified for ShardConnection::sync() 2013-01-09 16:58:25 -05:00
Eliot Horowitz
0a6bac4569 SERVER-7511: fix backport for 2.2 2013-01-09 14:47:34 -05:00
Eliot Horowitz
38f42b2a42 SERVER-7511: fix update with positional mod or index offset 2013-01-09 14:44:43 -05:00
Dan Pasette
c0ce2108cc Revert "post 2.2.3-rc0"
This reverts commit c5c64e3ace.
2013-01-09 15:09:12 +11:00
Dan Pasette
c5c64e3ace post 2.2.3-rc0 2013-01-09 14:38:15 +11:00
Greg Studer
e330e5269e SERVER-8112 use a scopeddbconnection for auth to unsharded auth collection 2013-01-08 15:30:28 -05:00
Dan Pasette
d857206880 BUMP 2.2.3-rc0 2013-01-08 03:52:43 -05:00
Spencer T Brody
64d9280591 During a chunk migration, log when receiving shard begins cloning data SERVER-8081 2013-01-08 03:46:32 -05:00
Tad Marshall
c814cb2868 SERVER-7787 Do not read character before checking count
Change the order of tests in copyString32to8counted() to check the count
of input characters before reading a possibly uninitialized character.
2013-01-08 03:11:16 -05:00
Tad Marshall
d5a4514a06 SERVER-7869 Report errno and description on file rename failure
Also report target filename on rename failure at startup, add
missing 'endl' to logRotate rename failure log line.
2013-01-08 03:10:32 -05:00
Tad Marshall
f7afbab2c3 SERVER-7435 Set symbol path in call to SymInitialize()
Set a symbol search path for Windows stack trace, starting with the
directory where the running executable is located.  Include Windows
directories as well, using default locations.
2013-01-08 03:10:02 -05:00
Ian Whalen
e4d0c89135 SERVER-7523 Add license notice for linenoise, s2, murmurhash 2013-01-08 03:09:40 -05:00
Eliot Horowitz
2d43e87a51 top command doesn't need a global lock as it has its own SERVER-7614 2013-01-08 03:08:56 -05:00
Eliot Horowitz
aecd951b3d SERVER-7660 findAndModify with auth + sort fix 2013-01-08 03:05:23 -05:00
Kristina
832a4c0ccf SERVER-7652 Use minvalid to determine if initial sync is needed 2013-01-08 02:12:40 -05:00
Eliot Horowitz
6f22c3af01 SERVER-7704 - linux compile warning 2013-01-08 02:07:40 -05:00
Eliot Horowitz
f33a8ce322 SERVER-7704 extra comment in ConnectionString 2013-01-08 02:07:29 -05:00
Eliot Horowitz
97df5708b9 SERVER-7704 - clean some of the equality semantics for sync and set ConnectionString 2013-01-08 02:07:17 -05:00
Eliot Horowitz
b7b2db71ba SERVER-7704 - make Shard == handle replica set changes
Conflicts:

	src/mongo/SConscript
    note: removed mocklib from shard_test.cpp deps
2013-01-08 02:06:21 -05:00
Eric Milkie
8885784cf1 SERVER-5487 do not call SSL_shutdown on an SSL context while another thread is using it 2013-01-08 01:54:22 -05:00
Andy Schwerin
4473320aca SERVER-7428 Regression test. 2013-01-08 01:54:05 -05:00
Andy Schwerin
9094e68b9b SERVER-7428 Only obtain a ReadContext on the "local" database when performing replAuthenticate.
No need for global write privilege, just database read to read the local.system.users collection.
2013-01-08 01:53:42 -05:00
Siddharth Singh
0878621290 SERVER-6915 use ::_exit() in tools
use ::_exit() to prevent static destructors from running in tools
to prevent static destructors.

Conflicts:

	src/mongo/tools/tool.cpp
2013-01-08 01:46:35 -05:00
Dan Pasette
82755a0350 SERVER-6167 - reduce distlock verbosity, add logging on lock_try
Convert LOG() to log()
2013-01-08 01:45:29 -05:00
Greg Studer
c28a406b1f SERVER-6167 reduce distlock verbosity, add logging on lock_try
Conflicts:

	src/mongo/client/distlock.cpp
2013-01-08 01:16:02 -05:00
Greg Studer
c5f1c1474b SERVER-8097 increment writebackSince on every request but GLE 2013-01-07 18:20:42 -05:00
Greg Studer
cdc2db7f6b SERVER-8000 minor other cleanup sharding_balance4.js 2012-12-27 15:06:45 -05:00
Greg Studer
c31da72e8a SERVER-8000 make sharding_balance4.js test deterministic 2012-12-27 15:06:35 -05:00
Eliot Horowitz
5baf992b90 SERVER-7958 - make test more resilient to gle field ordering 2012-12-19 22:20:10 -05:00
Eliot Horowitz
8014832a4a SERVER-7958 - when an old operation has a writeback,
make sure not to use that info in the user writeback
              just block
2012-12-19 11:18:43 -05:00
Eliot Horowitz
d03939e182 SERVER-4532 can't call ClientInfo::addShard on things you don't really use
Conflicts:

	src/mongo/s/s_only.cpp
2012-12-14 13:13:03 -05:00
Hari Khalsa
2758842136 SERVER-7343 turn within on in queryoptimizer, add jstests 2012-12-04 11:47:04 -05:00
Andrew Morrow
e342644da3 SERVER-7405 Stage banners as requested in modules
Backported from dfeb366b82
2012-12-03 13:43:36 -05:00
Hari Khalsa
c0888b3579 CS-4068 add additional tests to within-in-matcher, allow points to be array or key/val 2012-11-30 13:25:50 -05:00
Hari Khalsa
f98f20603b CS-4068 add to matcher 2012-11-30 11:16:29 -05:00
Eliot Horowitz
79a3b1cada post 2.2.2 2012-11-28 00:30:33 -05:00
Dan Pasette
d1b43b61a5 BUMP 2.2.2 2012-11-26 23:08:44 -05:00
Dan Pasette
69655c6c6e post 2.2.2-rc1 2012-11-19 19:35:40 -05:00
Dan Pasette
f3700f4ec3 BUMP 2.2.2-rc1 2012-11-18 23:18:04 -05:00
Spencer T Brody
5a4b6a0acc SERVER-7665 Use getInternalScopedDbConnection for updating RS configuration in config servers 2012-11-17 12:08:08 -05:00
Spencer T Brody
fed35f0c08 Use global lock when exiting critical section because it is greedier. Also add verbose logging around exiting critical section. SERVER-7500 SERVER-7493 2012-11-16 17:36:46 -05:00
Spencer T Brody
cb2e7e34d5 Decrease timeout on replication catching up in migration SERVER-7472 2012-11-16 17:35:21 -05:00
Spencer T Brody
c527cc73e2 Use write lock instead of read lock when exiting critical section so lock acquisition will be greedy 2012-11-16 17:34:49 -05:00
Spencer T Brody
9032d392d0 Fix how we abort a commit if _recvChunkVersion throws an exception 2012-11-16 17:34:19 -05:00
Greg Studer
448ef26e3d SERVER-7666 don't drain maxSize shards, only prevent to-migrations 2012-11-15 12:38:52 -05:00
Eric Milkie
c6039b222e post 2.2.2-rc0 2012-11-14 16:32:08 -05:00
69 changed files with 2181 additions and 677 deletions

View File

@@ -294,6 +294,7 @@ env = Environment( BUILD_DIR=variantDir,
CLIENT_SCONSTRUCT='#distsrc/client/SConstruct',
DIST_ARCHIVE_SUFFIX='.tgz',
EXTRAPATH=get_option("extrapath"),
MODULE_BANNERS=[],
MODULETEST_LIST='#build/moduletests.txt',
MSVS_ARCH=msarch ,
PYTHON=utils.find_python(),

View File

@@ -2,7 +2,7 @@ MongoDB uses third-party libraries or other resources that may
be distributed under licenses different than the MongoDB software.
In the event that we accidentally failed to list a required notice,
please bring it to our attention through any of the ways detailed here :
please bring it to our attention through any of the ways detailed here :
mongodb-dev@googlegroups.com
@@ -235,20 +235,20 @@ New BSD License
Copyright (c) 1998-2006, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the following
disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior written
contributors may be used to endorse or promote products
derived from this software without specific prior written
permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
@@ -287,9 +287,9 @@ For applicable files:
Netscape Communications Corporation.
Portions created by the Initial Developer are Copyright (C) 1998
the Initial Developer. All Rights Reserved.
Contributor(s):
Alternatively, the contents of this file may be used under the terms of
either of the GNU General Public License Version 2 or later (the "GPL"),
or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -302,4 +302,75 @@ For applicable files:
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
7) License notice for Linenoise
-------------------------------
Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
8) License notice for S2 Geometry Library
-----------------------------------------
Copyright 2005 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
9) License notice for MurmurHash
--------------------------------
Copyright (c) 2010-2012 Austin Appleby
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
End

View File

@@ -3,7 +3,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = MongoDB
PROJECT_NUMBER = 2.2.2-rc0
PROJECT_NUMBER = 2.2.3
OUTPUT_DIRECTORY = docs/doxygen
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English

View File

@@ -0,0 +1,18 @@
t = db.find_and_modify_server7660;
t.drop();
a = t.findAndModify({
query : { foo : 'bar' },
update : { $set : { bob : 'john' } },
sort: { foo : 1},
upsert: true,
new : true
});
b = t.findOne();
assert.eq( a, b );
assert.eq( "bar", a.foo );
assert.eq( "john", a.bob )

View File

@@ -0,0 +1,33 @@
// SERVER-7343: allow $within without a geo index.
t = db.geo_box1_noindex;
t.drop();
num = 0;
for ( x=0; x<=20; x++ ){
for ( y=0; y<=20; y++ ){
o = { _id : num++ , loc : [ x , y ] }
t.save( o )
}
}
searches = [
[ [ 1 , 2 ] , [ 4 , 5 ] ] ,
[ [ 1 , 1 ] , [ 2 , 2 ] ] ,
[ [ 0 , 2 ] , [ 4 , 5 ] ] ,
[ [ 1 , 1 ] , [ 2 , 8 ] ] ,
];
for ( i=0; i<searches.length; i++ ){
b = searches[i];
q = { loc : { $within : { $box : b } } }
numWanted = ( 1 + b[1][0] - b[0][0] ) * ( 1 + b[1][1] - b[0][1] );
assert.eq( numWanted , t.find(q).itcount() , "itcount: " + tojson( q ) );
printjson( t.find(q).explain() )
}
assert.eq( 0 , t.find( { loc : { $within : { $box : [ [100 , 100 ] , [ 110 , 110 ] ] } } } ).itcount() , "E1" )
assert.eq( 0 , t.find( { loc : { $within : { $box : [ [100 , 100 ] , [ 110 , 110 ] ] } } } ).count() , "E2" )
assert.eq( num , t.find( { loc : { $within : { $box : [ [ 0 , 0 ] , [ 110 , 110 ] ] } } } ).count() , "E3" )
assert.eq( num , t.find( { loc : { $within : { $box : [ [ 0 , 0 ] , [ 110 , 110 ] ] } } } ).itcount() , "E4" )
assert.eq( 57 , t.find( { loc : { $within : { $box : [ [ 0 , 0 ] , [ 110 , 110 ] ] } } } ).limit(57).itcount() , "E5" )

View File

@@ -0,0 +1,29 @@
// SERVER-7343: allow $within without a geo index.
t = db.geo_circle1_noindex;
t.drop();
searches = [
[ [ 5 , 5 ] , 3 ] ,
[ [ 5 , 5 ] , 1 ] ,
[ [ 5 , 5 ] , 5 ] ,
[ [ 0 , 5 ] , 5 ] ,
];
correct = searches.map( function(z){ return []; } );
num = 0;
for ( x=0; x<=20; x++ ){
for ( y=0; y<=20; y++ ){
o = { _id : num++ , loc : [ x , y ] }
t.save( o )
for ( i=0; i<searches.length; i++ )
if ( Geo.distance( [ x , y ] , searches[i][0] ) <= searches[i][1] )
correct[i].push( o );
}
}
for ( i=0; i<searches.length; i++ ){
q = { loc : { $within : { $center : searches[i] } } }
assert.eq( correct[i].length , t.find( q ).itcount() , "itcount : " + tojson( searches[i] ) );
assert.eq( correct[i].length , t.find( q ).count() , "count : " + tojson( searches[i] ) );
}

View File

@@ -0,0 +1,47 @@
// SERVER-7343: allow $within without a geo index.
t = db.geo_polygon1_noindex;
t.drop();
num = 0;
for ( x=1; x < 9; x++ ){
for ( y= 1; y < 9; y++ ){
o = { _id : num++ , loc : [ x , y ] };
t.save( o );
}
}
triangle = [[0,0], [1,1], [0,2]];
// Look at only a small slice of the data within a triangle
assert.eq( 1 , t.find( { loc: { "$within": { "$polygon" : triangle }}} ).count() , "Triangle Test" );
boxBounds = [ [0,0], [0,10], [10,10], [10,0] ];
assert.eq( num , t.find( { loc : { "$within" : { "$polygon" : boxBounds } } } ).count() , "Bounding Box Test" );
//Make sure we can add object-based polygons
assert.eq( num, t.find( { loc : { $within : { $polygon : { a : [-10, -10], b : [-10, 10], c : [10, 10], d : [10, -10] } } } } ).count() )
// Look in a box much bigger than the one we have data in
boxBounds = [[-100,-100], [-100, 100], [100,100], [100,-100]];
assert.eq( num , t.find( { loc : { "$within" : { "$polygon" : boxBounds } } } ).count() , "Big Bounding Box Test" );
t.drop();
pacman = [
[0,2], [0,4], [2,6], [4,6], // Head
[6,4], [4,3], [6,2], // Mouth
[4,0], [2,0] // Bottom
];
t.save({loc: [1,3] }); // Add a point that's in
assert.isnull( db.getLastError() )
assert.eq( 1 , t.find({loc : { $within : { $polygon : pacman }}} ).count() , "Pacman single point" );
t.save({ loc : [5, 3] }) // Add a point that's out right in the mouth opening
t.save({ loc : [3, 7] }) // Add a point above the center of the head
t.save({ loc : [3,-1] }) // Add a point below the center of the bottom
assert.eq( 1 , t.find({loc : { $within : { $polygon : pacman }}} ).count() , "Pacman double point" );

View File

@@ -0,0 +1,15 @@
// SERVER-7343: allow $within without a geo index.
t = db.geo_withinquery;
t.drop();
num = 0;
for ( x=0; x<=20; x++ ){
for ( y=0; y<=20; y++ ){
o = { _id : num++ , loc : [ x , y ] }
t.save( o )
}
}
assert.eq(21 * 21 - 1, t.find({ $and: [ {loc: {$ne:[0,0]}},
{loc: {$within: {$box: [[0,0], [100,100]]}}},
]}).itcount(), "UHOH!")

View File

@@ -176,7 +176,9 @@ catch ( e ){
y = e;
}
assert.eq( x , y , "assert format" )
assert.eq( x.code , y.code , "assert format" )
assert.eq( x.errmsg , y.errmsg , "assert format" )
assert.eq( x.ok , y.ok , "assert format" )
// isMaster and query-wrapped-command
isMaster = db.runCommand({isMaster:1});

View File

@@ -11,14 +11,6 @@ function writeToConfigTest(){
assert( gleObj.ok );
printjson( gleObj );
assert( gleObj.hasOwnProperty( 'shardRawGLE' ),
'missing shardRawGLE from get last error fields!' );
var shardGLE = gleObj.shardRawGLE[ st.config0.host ];
assert( shardGLE.ok );
assert.neq( null, shardGLE.err );
st.stop();
}

View File

@@ -0,0 +1,46 @@
// tests that listDatabases doesn't show config db on a shard, even if it is there
var test = new ShardingTest({shards: 1, mongos: 1, config: 1, other: {chunksize:1, separateConfig:true}})
var mongos = test.s0
var mongod = test.shard0;
//grab the config db instance by name
var getDBSection = function (dbsArray, dbToFind) {
for(var pos in dbsArray) {
if (dbsArray[pos].name && dbsArray[pos].name === dbToFind)
return dbsArray[pos];
}
return null;
}
mongos.getDB("blah").foo.insert({_id:1})
mongos.getDB("foo").foo.insert({_id:1})
mongos.getDB("raw").foo.insert({_id:1})
//wait for writes to finish
mongos.getDB("raw").getLastError()
//verify that the config db is not on a shard
var res = mongos.adminCommand("listDatabases");
var dbArray = res.databases;
assert(getDBSection(dbArray, "config"), "config db not found! 1")
assert(!getDBSection(dbArray, "config").shards, "config db is on a shard! 1")
//add doc in config/admin db on the shard
mongod.getDB("config").foo.insert({_id:1})
mongod.getDB("admin").foo.insert({_id:1})
//add doc in admin db (via mongos)
mongos.getDB("admin").foo.insert({_id:1})
//verify that the config db is not on a shard
var res = mongos.adminCommand("listDatabases");
var dbArray = res.databases;
//check config db
assert(getDBSection(dbArray, "config"), "config db not found! 2")
assert(!getDBSection(dbArray, "config").shards, "config db is on a shard! 2")
//check admin db
assert(getDBSection(dbArray, "admin"), "admin db not found! 2")
assert(!getDBSection(dbArray, "admin").shards, "admin db is on a shard! 2")
test.stop()

View File

@@ -0,0 +1,60 @@
function debug( str ) {
print( "---\n" + str + "\n-----" );
}
var name = "badNonUpdate";
debug("Starting sharded cluster test stuff");
s = new ShardingTest( {name: name, shards : 2, mongos : 2, separateConfig : true, verbose:5, nopreallocj : true });
var mongosA=s.s0;
var mongosB=s.s1;
ns = "test.coll";
ns2 = "test.coll2";
adminSA = mongosA.getDB( "admin" );
adminSA.runCommand({ enableSharding : "test"});
adminSA.runCommand( { moveprimary : "test", to : "shard0000" } );
adminSA.runCommand( { moveprimary : "test2", to : "shard0001" } );
adminSA.runCommand({ shardCollection : ns, key : { _id : 1 } });
try {
s.stopBalancer();
} catch (e) {
print("coundn't stop balancer via command");
}
adminSA.settings.update({ _id: 'balancer' }, { $set: { stopped: true }});
var db = mongosA.getDB( "test" );
var coll = db.coll;
var coll2 = db.coll2;
numDocs = 10;
for (var i = 1; i < numDocs; i++) {
coll.insert({_id:i, control:0});
coll2.insert({_id:i, control:0});
}
debug("Inserted docs, now split chunks");
adminSA.runCommand( { split: ns, find : { _id : 3} });
adminSA.runCommand( { movechunk: ns, find : { _id : 10}, to: "shard0001" });
var command = 'db.coll.update({_id:9},{$set:{"a":"9"}},true);printjson(db.getLastErrorObj())';
// without this first query through mongo, the second time doesn't "fail"
debug("Try query first time");
var GLE2=runMongoProgram( "mongo", "--quiet", "--port", "" + s._mongos[1].port, "--eval", command );
mongosB.getDB("test").coll2.update({_id:0}, {$set:{"c":"333"}});
var GLE3=mongosB.getDB("test").getLastErrorObj();
assert.eq( 0, GLE3.n );
s.stop();

View File

@@ -5,7 +5,7 @@
var NODE_COUNT = 3;
var st = new ShardingTest({ shards: { rs0: { nodes: NODE_COUNT, oplogSize: 10 }},
separateConfig: true });
separateConfig: true, config : 3 });
var replTest = st.rs0;
var mongos = st.s;

View File

@@ -0,0 +1,63 @@
/**
* Tests whether the WBL gets reset without subsequent events.
*/
var st = new ShardingTest({ shards : 2, mongos : 2, other : { mongosOptions : { verbose : 5 } } });
st.stopBalancer();
var mongos = st.s0;
var staleMongos = st.s1;
var admin = mongos.getDB("admin");
var config = mongos.getDB("config");
var shards = config.shards.find().toArray();
var coll = mongos.getCollection("foo.bar");
jsTest.log("Sharding collection...");
printjson(admin.runCommand({ enableSharding : coll.getDB() + "" }));
printjson(admin.runCommand({ movePrimary : coll.getDB() + "", to : shards[0]._id }));
printjson(admin.runCommand({ shardCollection : coll + "", key : { _id : 1 } }));
printjson(admin.runCommand({ split : coll + "", middle : { _id : 0 } }));
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[1]._id }));
jsTest.log("Collection now sharded...");
st.printShardingStatus();
jsTest.log("Making mongos stale...");
coll.insert({ _id : 0 });
coll.getDB().getLastErrorObj();
// Make sure the stale mongos knows about the collection at the original version
assert.neq(null, staleMongos.getCollection(coll + "").findOne());
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[0]._id, _waitForDelete : true }));
printjson(admin.runCommand({ moveChunk : coll + "", find : { _id : 0 }, to : shards[1]._id, _waitForDelete : true }));
jsTest.log("Running a stale insert...");
staleMongos.getCollection(coll + "").insert({ _id : 0, dup : "key" });
jsTest.log("Getting initial GLE result...");
printjson(staleMongos.getDB(coll.getDB() + "").getLastErrorObj());
printjson(staleMongos.getDB(coll.getDB() + "").getLastErrorObj());
st.printShardingStatus();
jsTest.log("Performing insert op on the same shard...");
staleMongos.getCollection(coll + "").insert({ _id : 1, key : "isOk" })
jsTest.log("Getting GLE result...");
printjson(staleMongos.getDB(coll.getDB() + "").getLastErrorObj());
assert.eq(null, staleMongos.getDB(coll.getDB() + "").getLastError());
jsTest.log("DONE!");
st.stop();

View File

@@ -0,0 +1,94 @@
jsTest.log("Starting sharded cluster for wrong duplicate error setup");
s = new ShardingTest( name="writeback_server7958", shards = 2, verbose=0, mongos = 4 );
var mongosA=s.s0;
var mongosB=s.s1;
var mongosC=s.s2;
var mongosD=s.s3;
ns1 = "test.trans";
ns2 = "test.node";
adminSA = mongosA.getDB( "admin" );
adminSB = mongosB.getDB( "admin" );
adminSD = mongosD.getDB( "admin" );
adminSA.runCommand({ enableSharding : "test"});
adminSA.runCommand({ shardCollection : ns1, key : { owner : 1 }, unique: true });
//adminSA.runCommand({ shardCollection : ns1, key : { owner : 1 } });
try {
s.stopBalancer();
} catch (e) {
print("coundn't stop balancer via command");
}
adminSA.settings.update({ _id: 'balancer' }, { $set: { stopped: true }});
var db = mongosA.getDB( "test" );
var dbB = mongosB.getDB( "test" );
var dbC = mongosC.getDB( "test" );
var dbD = mongosD.getDB( "test" );
var trans = db.trans;
var node = db.node;
var transB = dbB.trans;
var nodeB = dbB.node;
var transC = dbC.trans;
var nodeC = dbC.node;
var transD = dbD.trans;
var nodeD = dbD.node;
var primary = s.getServerName("test");
var shard1 = s._shardNames[0];
var shard2 = s._shardNames[1];
if (primary == shard1) {
other = shard2;
} else {
other = shard1;
}
trans.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
db.runCommand({getLastError:1, j:1});
node.insert({"owner":NumberLong("1234567890"),"parent":NumberLong("0"),_id:NumberLong("1234567890"), "counts":0});
db.runCommand({getLastError:1, j:1});
for (var i=0; i<1000; i++) {
trans.insert({"owner":NumberLong(i),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
node.insert({"owner":NumberLong(i),"parent":NumberLong(i+1000),_id:NumberLong(i+1234567890), "counts":0});
}
transB.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
var r1=dbB.runCommand( { getLastError: 1, w: 1 } );
assert( r1.n == 0 && r1.err.length > 0 && r1.hasOwnProperty("code"), tojson( r1 ) );
jsTest.log("Inserted dup (failed), now split chunks and move data");
adminSD.runCommand( { split: ns1, middle : { owner : 100} });
adminSD.runCommand( { movechunk: ns1, find : { owner : 105}, to: other});
jsTest.log("Kicking off dup inserts and updates");
errors=[];
i=0;
trans.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
var r1=db.runCommand( { getLastError: 1, w: 1 } );
assert( r1.n == 0 && r1.err.length > 0 && r1.hasOwnProperty("code"), tojson( r1 ) );
transB.insert({"owner":NumberLong("1234567890"),"tid":"2c4ba280-450a-11e2-bcfd-0800200c9a66"});
var rB1=dbB.runCommand( { getLastError: 1, w: 1 } );
assert( rB1.n == 0 && rB1.err.length > 0 && rB1.hasOwnProperty("code"), tojson( r1 ) );
nodeB.update({"owner":NumberLong("1234567890"),"parent":NumberLong("0"),_id:NumberLong("1234567890")},{"$inc":{"counts":1}});
var resultB = dbB.runCommand( { getLastError: 1, w: 1 } )
node.update({"owner":NumberLong("1234567890"),"parent":NumberLong("0"),_id:NumberLong("1234567890")},{"$inc":{"counts":1}});
var result = db.runCommand( { getLastError: 1, w: 1 } )
assert.eq( 2, node.findOne().counts );
printjson( result )
printjson( resultB )
assert( result.n==1 && result.updatedExisting==true && result.err == null, "update succeeded on collection node on mongos A but GLE was\nn=" + result.n + ",\nupdatedExisting=" + result.updatedExisting + ",\nerr=" + result.err);
assert( resultB.n==1 && resultB.updatedExisting==true && resultB.err == null, "update succeeded on collection node on mongos B but GLE was\nn=" + resultB.n + ",\nupdatedExisting=" + resultB.updatedExisting + ",\nerr=" + resultB.err);
s.stop();

View File

@@ -0,0 +1,16 @@
// Regression test for SERVER-7428.
//
// Verify that the copyDatabase command works appropriately when the
// target mongo instance has authentication enabled.
// Setup fromDb with no auth
var fromDb = MongoRunner.runMongod({ port: 29000 });
// Setup toDb with auth
var toDb = MongoRunner.runMongod({auth : "", port : 31001});
var admin = toDb.getDB("admin");
admin.addUser("foo","bar");
admin.auth("foo","bar");
admin.copyDatabase('test', 'test', fromDb.host)

View File

@@ -23,19 +23,37 @@ num = 0;
counts = {}
function doUpdate( includeString ){
//
// TODO: Rewrite to make much clearer.
//
// The core behavior of this test is to add a bunch of documents to a sharded collection, then
// incrementally update each document and make sure the counts in the document match our update
// counts while balancing occurs (doUpdate()). Every once in a while we also check (check())
// our counts via a query.
//
// If during a chunk migration an update is missed, we trigger an assertion and fail.
//
function doUpdate( includeString, optionalId ){
var up = { $inc : { x : 1 } }
if ( includeString )
up["$set"] = { s : bigString };
var myid = Random.randInt( N )
var myid = optionalId == undefined ? Random.randInt( N ) : optionalId
db.foo.update( { _id : myid } , up , true );
counts[myid] = ( counts[myid] ? counts[myid] : 0 ) + 1;
return myid;
}
for ( i=0; i<N*10; i++ ){
doUpdate( true )
// Initially update all documents from 1 to N, otherwise later checks can fail because no document
// previously existed
for ( i = 0; i < N; i++ ){
doUpdate( true, i )
}
for ( i=0; i<N*9; i++ ){
doUpdate( false )
}
db.getLastError();
@@ -117,11 +135,6 @@ function diff1(){
return Math.max( x.shard0000 , x.shard0001 ) - Math.min( x.shard0000 , x.shard0001 );
}
function sum(){
var x = s.chunkCounts( "foo" )
return x.shard0000 + x.shard0001;
}
assert.lt( 20 , diff1() ,"initial load" );
print( diff1() )

View File

@@ -0,0 +1,79 @@
/*
* Test to ensure that (dump/restore/export/import/oplog) works with a replica set connection string
* 1. Start a replica set.
* 2. Add data to a collection.
* 3. Take a dump of the database.
* 4. Drop the db.
* 5. Restore the db.
* 6. Export a collection.
* 7. Drop the collection.
* 8. Import the collection.
* 9. Add data to the oplog.rs collection.
* 10. Ensure that the document doesn't exist yet.
* 11. Now play the mongooplog tool.
* 12. Make sure that the oplog was played
*/
// Load utility methods for replica set tests
load("jstests/replsets/rslib.js");
var replTest = new ReplSetTest({ name: 'rs', nodes: 2, oplogSize: 5 });
var nodes = replTest.startSet();
replTest.initiate();
var master = replTest.getMaster();
var docNum = 100;
for (var i = 0; i < docNum; i++) {
master.getDB("foo").bar.insert({ a: i });
}
replTest.awaitReplication();
var replSetConnString = "rs/127.0.0.1:" + replTest.ports[0] + ",127.0.0.1:" + replTest.ports[1];
// Test with mongodump/mongorestore
print("dump the db");
var data = "/data/db/dumprestore11-dump1/";
runMongoProgram("mongodump", "--host", replSetConnString, "--out", data);
master.getDB("foo").dropDatabase();
replTest.awaitReplication();
print("restore the db");
runMongoProgram("mongorestore", "--host", replSetConnString, "--dir", data);
var x = master.getDB("foo").getCollection("bar").count();
assert.eq(x, docNum, "mongorestore should have successfully restored the collection" + docNum);
replTest.awaitReplication();
// Test with mongoexport/mongoimport
print("export the collection");
var extFile = "/data/db/exportimport_replSet/export";
runMongoProgram("mongoexport", "--host", replSetConnString, "--out", extFile,
"-d", "foo", "-c", "bar");
master.getDB("foo").getCollection("bar").drop();
replTest.awaitReplication();
print("import the collection");
runMongoProgram("mongoimport", "--host", replSetConnString, "--file", extFile,
"-d", "foo", "-c", "bar");
var x = master.getDB("foo").getCollection("bar").count();
assert.eq(x, docNum, "mongoimport should have successfully imported the collection" + docNum);
master.getDB("foo").getCollection("bar").drop();
// Test with mongooplog
var doc = { _id : 5, x : 17 };
master.getDB("local").oplog.rs.insert({ ts : new Timestamp(), "op" : "i", "ns" : "foo.bar",
"o" : doc });
assert.eq(0, master.getDB("foo").getCollection("bar").count(), "before");
var replSetConnString = "rs/127.0.0.1:" + replTest.ports[0] + ",127.0.0.1:" + replTest.ports[1];
runMongoProgram("mongooplog" , "--from", "127.0.0.1:" + replTest.ports[0],
"--host", replSetConnString);
assert.eq(101, master.getDB("foo").getCollection("bar").count(), "after")
replTest.stopSet();

View File

@@ -0,0 +1,113 @@
// Checking for positional array updates with either .$ or .0 at the end
// SERVER-7511
// array.$.name
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'name': 'old'}]} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {'array.name': 'old'}, {$set: {'array.$.name': 'new'}} );
assert( t.findOne({'array.name': 'new'}) );
assert( !t.findOne({'array.name': 'old'}) );
// array.$ (failed in 2.2.2)
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'name': 'old'}]} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {'array.name': 'old'}, {$set: {'array.$': {'name':'new'}}} );
assert( t.findOne({'array.name': 'new'}) );
assert( !t.findOne({'array.name': 'old'}) );
// array.0.name
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'name': 'old'}]} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {'array.name': 'old'}, {$set: {'array.0.name': 'new'}} );
assert( t.findOne({'array.name': 'new'}) );
assert( !t.findOne({'array.name': 'old'}) );
// array.0 (failed in 2.2.2)
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'name': 'old'}]} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {'array.name': 'old'}, {$set: {'array.0': {'name':'new'}}} );
assert( t.findOne({'array.name': 'new'}) );
assert( !t.findOne({'array.name': 'old'}) );
// // array.12.name
t = db.jstests_update_arraymatch8;
t.drop();
arr = new Array();
for (var i=0; i<20; i++) {
arr.push({'name': 'old'});
}
t.ensureIndex( {'array.name': 1} );
t.insert( {_id:0, 'array': arr} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {_id:0}, {$set: {'array.12.name': 'new'}} );
// note: both documents now have to be in the array
assert( t.findOne({'array.name': 'new'}) );
assert( t.findOne({'array.name': 'old'}) );
// array.12 (failed in 2.2.2)
t = db.jstests_update_arraymatch8;
t.drop();
arr = new Array();
for (var i=0; i<20; i++) {
arr.push({'name': 'old'});
}
t.ensureIndex( {'array.name': 1} );
t.insert( {_id:0, 'array': arr} );
assert( t.findOne({'array.name': 'old'}) );
t.update( {_id:0}, {$set: {'array.12': {'name':'new'}}} );
// note: both documents now have to be in the array
assert( t.findOne({'array.name': 'new'}) );
assert( t.findOne({'array.name': 'old'}) );
// array.$.123a.name
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.123a.name': 1} );
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
assert( t.findOne({'array.123a.name': 'old'}) );
t.update( {'array.123a.name': 'old'}, {$set: {'array.$.123a.name': 'new'}} );
assert( t.findOne({'array.123a.name': 'new'}) );
assert( !t.findOne({'array.123a.name': 'old'}) );
// array.$.123a
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
assert( t.findOne({'array.123a.name': 'old'}) );
t.update( {'array.123a.name': 'old'}, {$set: {'array.$.123a': {'name': 'new'}}} );
assert( t.findOne({'array.123a.name': 'new'}) );
assert( !t.findOne({'array.123a.name': 'old'}) );
// array.0.123a.name
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.123a.name': 1} );
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
assert( t.findOne({'array.123a.name': 'old'}) );
t.update( {'array.123a.name': 'old'}, {$set: {'array.0.123a.name': 'new'}} );
assert( t.findOne({'array.123a.name': 'new'}) );
assert( !t.findOne({'array.123a.name': 'old'}) );
// array.0.123a
t = db.jstests_update_arraymatch8;
t.drop();
t.ensureIndex( {'array.name': 1} );
t.insert( {'array': [{'123a':{'name': 'old'}}]} );
assert( t.findOne({'array.123a.name': 'old'}) );
t.update( {'array.123a.name': 'old'}, {$set: {'array.0.123a': {'name': 'new'}}} );
assert( t.findOne({'array.123a.name': 'new'}) );
assert( !t.findOne({'array.123a.name': 'old'}) );

View File

@@ -1,7 +1,7 @@
Name: mongo-10gen
Conflicts: mongo, mongo-10gen-unstable
Obsoletes: mongo-stable
Version: 2.2.1
Version: 2.2.3
Release: mongodb_1%{?dist}
Summary: mongodb client shell and tools
License: AGPL 3.0

View File

@@ -3,6 +3,7 @@
# This SConscript describes build rules for the "mongo" project.
import os
import itertools
from buildscripts import utils
Import("env")
@@ -343,6 +344,16 @@ env.CppUnitTest( "balancer_policy_test" , [ "s/balancer_policy_tests.cpp" ] ,
LIBDEPS=["mongoscore", "coreshard","mongocommon","coreserver","coredb","dbcmdline","mongodandmongos"] ,
NO_CRUTCH=True)
env.CppUnitTest("shard_test", [ "s/shard_test.cpp" ],
LIBDEPS=[ "mongoscore",
"coreshard",
"mongocommon",
"coreserver",
"coredb",
"dbcmdline",
"mongodandmongos"],
NO_CRUTCH=True)
serverOnlyFiles += [ "s/d_logic.cpp",
"s/d_writeback.cpp",
"s/d_migrate.cpp",
@@ -564,18 +575,36 @@ if installSetup.libraries:
if has_option( "sharedclient" ):
env.Install( "$INSTALL_DIR/$NIX_LIB_DIR", '#${SHLIBPREFIX}mongoclient${SHLIBSUFFIX}')
# Stage the top-level mongodb banners
distsrc = env.Dir('#distsrc')
env.Append(MODULE_BANNERS = [distsrc.File('README'),
distsrc.File('THIRD-PARTY-NOTICES')])
# If no module has introduced a file named LICENSE.txt, then inject the AGPL.
if sum(itertools.imap(lambda x: x.name == "LICENSE.txt", env['MODULE_BANNERS'])) == 0:
env.Append(MODULE_BANNERS = [distsrc.File('GNU-AGPL-3.0')])
# All module banners get staged to the top level of the tarfile, so we
# need to fail if we are going to have a name collision.
module_banner_filenames = set([f.name for f in env['MODULE_BANNERS']])
if not len(module_banner_filenames) == len(env['MODULE_BANNERS']):
# TODO: Be nice and identify conflicts in error.
print "ERROR: Filename conflicts exist in module banners."
Exit(-1)
# Build a set of directories containing module banners, and use that
# to build a --transform option for each directory so that the files
# are tar'ed up to the proper location.
module_banner_dirs = set([Dir('#').rel_path(f.get_dir()) for f in env['MODULE_BANNERS']])
module_banner_transforms = ["--transform %s=$SERVER_DIST_BASENAME" % d for d in module_banner_dirs]
env.Command(
'#/${SERVER_ARCHIVE}',
['#buildscripts/make_archive.py',
'#distsrc/GNU-AGPL-3.0',
'#distsrc/README',
'#distsrc/THIRD-PARTY-NOTICES',
distBinaries],
'$PYTHON ${SOURCES[0]} -o $TARGET '
'--transform distsrc=$SERVER_DIST_BASENAME '
'--transform ${str(Dir(BUILD_DIR))}/mongo/stripped=$SERVER_DIST_BASENAME/bin '
'--transform ${str(Dir(BUILD_DIR))}/mongo=$SERVER_DIST_BASENAME/bin '
'${TEMPFILE(SOURCES[1:])}')
['#buildscripts/make_archive.py'] + env["MODULE_BANNERS"] + distBinaries,
' '.join(['$PYTHON ${SOURCES[0]} -o $TARGET'] + module_banner_transforms + [
'--transform ${str(Dir(BUILD_DIR))}/mongo/stripped=$SERVER_DIST_BASENAME/bin',
'--transform ${str(Dir(BUILD_DIR))}/mongo=$SERVER_DIST_BASENAME/bin',
'${TEMPFILE(SOURCES[1:])}']))
#final alias
env.Alias( "install", "$INSTALL_DIR" )

View File

@@ -154,6 +154,45 @@ namespace mongo {
return 0;
}
bool ConnectionString::sameLogicalEndpoint( const ConnectionString& other ) const {
if ( _type != other._type )
return false;
switch ( _type ) {
case INVALID:
return true;
case MASTER:
return _servers[0] == other._servers[0];
case PAIR:
if ( _servers[0] == other._servers[0] )
return _servers[1] == other._servers[1];
return
( _servers[0] == other._servers[1] ) &&
( _servers[1] == other._servers[0] );
case SET:
return _setName == other._setName;
case SYNC:
// The servers all have to be the same in each, but not in the same order.
if ( _servers.size() != other._servers.size() )
return false;
for ( unsigned i = 0; i < _servers.size(); i++ ) {
bool found = false;
for ( unsigned j = 0; j < other._servers.size(); j++ ) {
if ( _servers[i] == other._servers[j] ) {
found = true;
break;
}
}
if ( ! found )
return false;
}
return true;
case CUSTOM:
return _string == other._string;
}
verify( false );
}
ConnectionString ConnectionString::parse( const string& host , string& errmsg ) {
string::size_type i = host.find( '/' );

View File

@@ -369,7 +369,7 @@ namespace mongo {
for ( unsigned i=0; i<_nodes.size(); i++ ) {
if ( i > 0 )
ss << ",";
ss << _nodes[i].addr.toString();
_nodes[i].addr.append( ss );
}
return ss.str();

View File

@@ -248,6 +248,14 @@ namespace mongo {
ConnectionType type() const { return _type; }
/**
* This returns true if this and other point to the same logical entity.
* For single nodes, thats the same address.
* For replica sets, thats just the same replica set name.
* For pair (deprecated) or sync cluster connections, that's the same hosts in any ordering.
*/
bool sameLogicalEndpoint( const ConnectionString& other ) const;
static ConnectionString parse( const string& url , string& errmsg );
static string typeToString( ConnectionType type );

View File

@@ -319,9 +319,9 @@ namespace mongo {
_lockTimeout( lockTimeout == 0 ? LOCK_TIMEOUT : lockTimeout ), _maxClockSkew( _lockTimeout / LOCK_SKEW_FACTOR ), _maxNetSkew( _maxClockSkew ), _lockPing( _maxClockSkew ),
_mutex( "DistributedLock" )
{
log( logLvl - 1 ) << "created new distributed lock for " << name << " on " << conn
<< " ( lock timeout : " << _lockTimeout
<< ", ping interval : " << _lockPing << ", process : " << asProcess << " )" << endl;
log( logLvl ) << "created new distributed lock for " << name << " on " << conn
<< " ( lock timeout : " << _lockTimeout
<< ", ping interval : " << _lockPing << ", process : " << asProcess << " )" << endl;
}
@@ -492,6 +492,11 @@ namespace mongo {
// This should always be true, if not, we are using the lock incorrectly.
verify( _name != "" );
log( logLvl ) << "trying to acquire new distributed lock for " << _name << " on " << _conn
<< " ( lock timeout : " << _lockTimeout
<< ", ping interval : " << _lockPing << ", process : " << _processId << " )"
<< endl;
// write to dummy if 'other' is null
BSONObj dummyOther;
if ( other == NULL )

View File

@@ -384,11 +384,9 @@ namespace mongo {
cout << "Cant reassign stdin while forking server process: " << strerror(errno) << endl;
::_exit(-1);
}
setupCoreSignals();
setupSignals( true );
}
setupSignals( true );
if (params.count("syslog")) {
StringBuilder sb;
sb << cmdLine.binaryName << "." << cmdLine.port;

View File

@@ -270,7 +270,7 @@ namespace mongo {
uassert(13330, "upsert mode requires query field", !origQuery.isEmpty());
db.update(ns, origQuery, update.embeddedObjectUserCheck(), true);
BSONObj gle = db.getLastErrorDetailed();
BSONObj gle = db.getLastErrorDetailed(dbname);
result.append("lastErrorObject", gle);
if (gle["err"].type() == String) {
errmsg = gle["err"].String();
@@ -292,7 +292,7 @@ namespace mongo {
uassert(12515, "can't remove and update", cmdObj["update"].eoo());
db.remove(ns, QUERY("_id" << out["_id"]), 1);
BSONObj gle = db.getLastErrorDetailed();
BSONObj gle = db.getLastErrorDetailed(dbname);
result.append("lastErrorObject", gle);
if (gle["err"].type() == String) {
errmsg = gle["err"].String();
@@ -324,7 +324,7 @@ namespace mongo {
uassert(12516, "must specify remove or update", !update.eoo());
db.update(ns, q, update.embeddedObjectUserCheck());
BSONObj gle = db.getLastErrorDetailed();
BSONObj gle = db.getLastErrorDetailed(dbname);
result.append("lastErrorObject", gle);
if (gle["err"].type() == String) {
errmsg = gle["err"].String();

View File

@@ -76,7 +76,8 @@ namespace mongo {
extern bool checkNsFilesOnLoad;
extern string repairpath;
void setupSignals( bool inFork );
void setupQuittingSignals();
void setupSignals( bool ignored );
void startReplication();
void exitCleanly( ExitCode code );
@@ -743,7 +744,7 @@ static int mongoDbMain(int argc, char* argv[]) {
setupCoreSignals();
setupSignals( false );
setupQuittingSignals();
dbExecCommand = argv[0];
@@ -1229,7 +1230,7 @@ namespace mongo {
void setupSignals_ignoreHelper( int signal ) {}
void setupSignals( bool inFork ) {
void setupQuittingSignals() {
struct sigaction addrSignals;
memset( &addrSignals, 0, sizeof( struct sigaction ) );
addrSignals.sa_sigaction = abruptQuitWithAddrSignal;
@@ -1247,20 +1248,16 @@ namespace mongo {
setupSIGTRAPforGDB();
set_terminate( myterminate );
set_new_handler( my_new_handler );
}
void setupSignals( bool ignored ) {
sigemptyset( &asyncSignals );
if ( inFork )
verify( signal( SIGHUP , setupSignals_ignoreHelper ) != SIG_ERR );
else
sigaddset( &asyncSignals, SIGHUP );
sigaddset( &asyncSignals, SIGINT );
sigaddset( &asyncSignals, SIGTERM );
verify( pthread_sigmask( SIG_SETMASK, &asyncSignals, 0 ) == 0 );
boost::thread it( interruptThread );
set_terminate( myterminate );
set_new_handler( my_new_handler );
}
#else // WIN32
@@ -1414,7 +1411,9 @@ namespace mongo {
mongoAbort("pure virtual");
}
void setupSignals( bool inFork ) {
void setupSignals( bool inFork ) { }
void setupQuittingSignals() {
reportEventToSystem = reportEventToSystemImpl;
filtLast = SetUnhandledExceptionFilter(exceptionFilter);
massert(10297 , "Couldn't register Windows Ctrl-C handler", SetConsoleCtrlHandler((PHANDLER_ROUTINE) CtrlHandler, TRUE));

View File

@@ -458,349 +458,23 @@ namespace mongo {
double _errorSphere;
};
class Box {
public:
Box( const Geo2dType * g , const GeoHash& hash )
: _min( g , hash ) ,
_max( _min._x + g->sizeEdge( hash ) , _min._y + g->sizeEdge( hash ) ) {
}
Box( double x , double y , double size )
: _min( x , y ) ,
_max( x + size , y + size ) {
}
Box( Point min , Point max )
: _min( min ) , _max( max ) {
}
Box() {}
BSONArray toBSON() const {
return BSON_ARRAY( BSON_ARRAY( _min._x << _min._y ) << BSON_ARRAY( _max._x << _max._y ) );
}
string toString() const {
StringBuilder buf;
buf << _min.toString() << " -->> " << _max.toString();
return buf.str();
}
bool between( double min , double max , double val , double fudge=0) const {
return val + fudge >= min && val <= max + fudge;
}
bool onBoundary( double bound, double val, double fudge = 0 ) const {
return ( val >= bound - fudge && val <= bound + fudge );
}
bool mid( double amin , double amax , double bmin , double bmax , bool min , double& res ) const {
verify( amin <= amax );
verify( bmin <= bmax );
if ( amin < bmin ) {
if ( amax < bmin )
return false;
res = min ? bmin : amax;
return true;
}
if ( amin > bmax )
return false;
res = min ? amin : bmax;
return true;
}
double intersects( const Box& other ) const {
Point boundMin(0,0);
Point boundMax(0,0);
if ( mid( _min._x , _max._x , other._min._x , other._max._x , true , boundMin._x ) == false ||
mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false ||
mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false ||
mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false )
return 0;
Box intersection( boundMin , boundMax );
return intersection.area() / area();
}
double area() const {
return ( _max._x - _min._x ) * ( _max._y - _min._y );
}
double maxDim() const {
return max( _max._x - _min._x, _max._y - _min._y );
}
Point center() const {
return Point( ( _min._x + _max._x ) / 2 ,
( _min._y + _max._y ) / 2 );
}
void truncate( const Geo2dType* g ) {
if( _min._x < g->_min ) _min._x = g->_min;
if( _min._y < g->_min ) _min._y = g->_min;
if( _max._x > g->_max ) _max._x = g->_max;
if( _max._y > g->_max ) _max._y = g->_max;
}
void fudge( const Geo2dType* g ) {
_min._x -= g->_error;
_min._y -= g->_error;
_max._x += g->_error;
_max._y += g->_error;
}
bool onBoundary( Point p, double fudge = 0 ) {
return onBoundary( _min._x, p._x, fudge ) ||
onBoundary( _max._x, p._x, fudge ) ||
onBoundary( _min._y, p._y, fudge ) ||
onBoundary( _max._y, p._y, fudge );
}
bool inside( Point p , double fudge = 0 ) {
bool res = inside( p._x , p._y , fudge );
//cout << "is : " << p.toString() << " in " << toString() << " = " << res << endl;
return res;
}
bool inside( double x , double y , double fudge = 0 ) {
return
between( _min._x , _max._x , x , fudge ) &&
between( _min._y , _max._y , y , fudge );
}
bool contains(const Box& other, double fudge=0) {
return inside(other._min, fudge) && inside(other._max, fudge);
}
Point _min;
Point _max;
};
class Polygon {
public:
Polygon( void ) : _centroidCalculated( false ) {}
Polygon( vector<Point> points ) : _centroidCalculated( false ),
_points( points ) { }
void add( Point p ) {
_centroidCalculated = false;
_points.push_back( p );
}
int size( void ) const {
return _points.size();
}
/**
* Determine if the point supplied is contained by the current polygon.
*
* The algorithm uses a ray casting method.
*/
bool contains( const Point& p ) const {
return contains( p, 0 ) > 0;
}
int contains( const Point &p, double fudge ) const {
Box fudgeBox( Point( p._x - fudge, p._y - fudge ), Point( p._x + fudge, p._y + fudge ) );
int counter = 0;
Point p1 = _points[0];
for ( int i = 1; i <= size(); i++ ) {
Point p2 = _points[i % size()];
GEODEBUG( "Doing intersection check of " << fudgeBox.toString() << " with seg " << p1.toString() << " to " << p2.toString() );
// We need to check whether or not this segment intersects our error box
if( fudge > 0 &&
// Points not too far below box
fudgeBox._min._y <= std::max( p1._y, p2._y ) &&
// Points not too far above box
fudgeBox._max._y >= std::min( p1._y, p2._y ) &&
// Points not too far to left of box
fudgeBox._min._x <= std::max( p1._x, p2._x ) &&
// Points not too far to right of box
fudgeBox._max._x >= std::min( p1._x, p2._x ) ) {
GEODEBUG( "Doing detailed check" );
// If our box contains one or more of these points, we need to do an exact check.
if( fudgeBox.inside(p1) ) {
GEODEBUG( "Point 1 inside" );
return 0;
}
if( fudgeBox.inside(p2) ) {
GEODEBUG( "Point 2 inside" );
return 0;
}
// Do intersection check for vertical sides
if ( p1._y != p2._y ) {
double invSlope = ( p2._x - p1._x ) / ( p2._y - p1._y );
double xintersT = ( fudgeBox._max._y - p1._y ) * invSlope + p1._x;
if( fudgeBox._min._x <= xintersT && fudgeBox._max._x >= xintersT ) {
GEODEBUG( "Top intersection @ " << xintersT );
return 0;
}
double xintersB = ( fudgeBox._min._y - p1._y ) * invSlope + p1._x;
if( fudgeBox._min._x <= xintersB && fudgeBox._max._x >= xintersB ) {
GEODEBUG( "Bottom intersection @ " << xintersB );
return 0;
}
}
// Do intersection check for horizontal sides
if( p1._x != p2._x ) {
double slope = ( p2._y - p1._y ) / ( p2._x - p1._x );
double yintersR = ( p1._x - fudgeBox._max._x ) * slope + p1._y;
if( fudgeBox._min._y <= yintersR && fudgeBox._max._y >= yintersR ) {
GEODEBUG( "Right intersection @ " << yintersR );
return 0;
}
double yintersL = ( p1._x - fudgeBox._min._x ) * slope + p1._y;
if( fudgeBox._min._y <= yintersL && fudgeBox._max._y >= yintersL ) {
GEODEBUG( "Left intersection @ " << yintersL );
return 0;
}
}
}
else if( fudge == 0 ){
// If this is an exact vertex, we won't intersect, so check this
if( p._y == p1._y && p._x == p1._x ) return 1;
else if( p._y == p2._y && p._x == p2._x ) return 1;
// If this is a horizontal line we won't intersect, so check this
if( p1._y == p2._y && p._y == p1._y ){
// Check that the x-coord lies in the line
if( p._x >= std::min( p1._x, p2._x ) && p._x <= std::max( p1._x, p2._x ) ) return 1;
}
}
// Normal intersection test.
// TODO: Invert these for clearer logic?
if ( p._y > std::min( p1._y, p2._y ) ) {
if ( p._y <= std::max( p1._y, p2._y ) ) {
if ( p._x <= std::max( p1._x, p2._x ) ) {
if ( p1._y != p2._y ) {
double xinters = (p._y-p1._y)*(p2._x-p1._x)/(p2._y-p1._y)+p1._x;
// Special case of point on vertical line
if ( p1._x == p2._x && p._x == p1._x ){
// Need special case for the vertical edges, for example:
// 1) \e pe/----->
// vs.
// 2) \ep---e/----->
//
// if we count exact as intersection, then 1 is in but 2 is out
// if we count exact as no-int then 1 is out but 2 is in.
return 1;
}
else if( p1._x == p2._x || p._x <= xinters ) {
counter++;
}
}
}
}
}
p1 = p2;
}
if ( counter % 2 == 0 ) {
return -1;
}
else {
return 1;
}
}
/**
* Calculate the centroid, or center of mass of the polygon object.
*/
Point centroid( void ) {
/* Centroid is cached, it won't change betwen points */
if ( _centroidCalculated ) {
return _centroid;
}
Point cent;
double signedArea = 0.0;
double area = 0.0; // Partial signed area
/// For all vertices except last
int i = 0;
for ( i = 0; i < size() - 1; ++i ) {
area = _points[i]._x * _points[i+1]._y - _points[i+1]._x * _points[i]._y ;
signedArea += area;
cent._x += ( _points[i]._x + _points[i+1]._x ) * area;
cent._y += ( _points[i]._y + _points[i+1]._y ) * area;
}
// Do last vertex
area = _points[i]._x * _points[0]._y - _points[0]._x * _points[i]._y;
cent._x += ( _points[i]._x + _points[0]._x ) * area;
cent._y += ( _points[i]._y + _points[0]._y ) * area;
signedArea += area;
signedArea *= 0.5;
cent._x /= ( 6 * signedArea );
cent._y /= ( 6 * signedArea );
_centroidCalculated = true;
_centroid = cent;
return cent;
}
Box bounds( void ) {
// TODO: Cache this
_bounds._max = _points[0];
_bounds._min = _points[0];
for ( int i = 1; i < size(); i++ ) {
_bounds._max._x = max( _bounds._max._x, _points[i]._x );
_bounds._max._y = max( _bounds._max._y, _points[i]._y );
_bounds._min._x = min( _bounds._min._x, _points[i]._x );
_bounds._min._y = min( _bounds._min._y, _points[i]._y );
}
return _bounds;
}
private:
bool _centroidCalculated;
Point _centroid;
Box _bounds;
vector<Point> _points;
};
// Equal to Point::Point(args)
inline Point pointFromHash(const GeoConvert* g, const GeoHash& hash) {
double x, y;
g->unhash(hash, x, y);
return Point(x, y);
}
// Point::hash
inline GeoHash pointToHash(Point p, const GeoConvert* g) {
return g->hash(p._x, p._y);
}
// Box::Box(...)
inline Box boxFromHash(const Geo2dType* g, const GeoHash& hash) {
Point min = pointFromHash(g, hash);
double edgeSize = g->sizeEdge(hash);
Point max = Point(min._x + edgeSize, min._y + edgeSize);
return Box(min, max);
}
class Geo2dPlugin : public IndexPlugin {
public:
@@ -1024,7 +698,7 @@ namespace mongo {
// Approximate distance check using key data
////
double keyD = 0;
Point keyP( _g, GeoHash( node._key.firstElement(), _g->_bits ) );
Point keyP(pointFromHash(_g, GeoHash( node._key.firstElement(), _g->_bits )));
KeyResult keyOk = approxKeyCheck( keyP, keyD );
if ( keyOk == BAD ) {
GEODEBUG( "\t\t\t\t bad distance : " << node.recordLoc.obj() << "\t" << keyD );
@@ -1664,7 +1338,7 @@ namespace mongo {
if( ! isNeighbor ) {
_centerPrefix = _prefix;
_centerBox = Box( _g, _centerPrefix );
_centerBox = boxFromHash( _g, _centerPrefix );
isNeighbor = true;
}
@@ -1702,7 +1376,7 @@ namespace mongo {
while( _fringe.size() > 0 ) {
_prefix = _neighborPrefix + _fringe.back();
Box cur( _g , _prefix );
Box cur(boxFromHash( _g , _prefix ));
PREFIXDEBUG( _prefix, _g );
@@ -1874,7 +1548,7 @@ namespace mongo {
BSONObjBuilder bob;
BSONArrayBuilder bab;
for( i = _expPrefixes.begin(); i != _expPrefixes.end(); ++i ){
bab << Box( _g, *i ).toBSON();
bab << Box(boxFromHash( _g, *i )).toBSON();
}
bob << _g->_geo << bab.arr();
@@ -2626,7 +2300,7 @@ namespace mongo {
_want._max = Point( i.next() );
_wantRegion = _want;
_wantRegion.fudge( g ); // Need to make sure we're checking regions within error bounds of where we want
_wantRegion.fudge( g->_error ); // Need to make sure we're checking regions within error bounds of where we want
fixBox( g, _wantRegion );
fixBox( g, _want );
@@ -2716,8 +2390,8 @@ namespace mongo {
uassert( 14030, "polygon must be defined by three points or more", _poly.size() >= 3 );
_bounds = _poly.bounds();
_bounds.fudge( g ); // We need to check regions within the error bounds of these bounds
_bounds.truncate( g ); // We don't need to look anywhere outside the space
_bounds.fudge( g->_error ); // We need to check regions within the error bounds of these bounds
_bounds.truncate( g->_min, g->_max ); // We don't need to look anywhere outside the space
_maxDim = _g->_error + _bounds.maxDim() / 2;
@@ -2767,6 +2441,9 @@ namespace mongo {
else if ( numWanted == 0 )
numWanted = 100;
// false means we want to filter OUT geoFieldsToNuke, not filter to include only that.
BSONObj filteredQuery = query.filterFieldsUndotted(BSON(_geo << ""), false);
BSONObjIterator i(query);
while ( i.more() ) {
BSONElement e = i.next();
@@ -2776,14 +2453,10 @@ namespace mongo {
if ( e.type() == Array ) {
// If we get an array query, assume it is a location, and do a $within { $center : [[x, y], 0] } search
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), query.filterFieldsUndotted( BSON( _geo << "" ), false ), "$center", true ) );
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), filteredQuery, "$center", true ) );
return c;
}
else if ( e.type() == Object ) {
// TODO: Filter out _geo : { $special... } field so it doesn't get matched accidentally,
// if matcher changes
switch ( e.embeddedObject().firstElement().getGtLtOp() ) {
case BSONObj::opNEAR: {
BSONObj n = e.embeddedObject();
@@ -2820,7 +2493,8 @@ namespace mongo {
bool uniqueDocs = false;
if( ! n["$uniqueDocs"].eoo() ) uniqueDocs = n["$uniqueDocs"].trueValue();
shared_ptr<GeoSearch> s( new GeoSearch( this , Point( e ) , numWanted , query , maxDistance, type, uniqueDocs ) );
shared_ptr<GeoSearch> s( new GeoSearch( this , Point( e ) , numWanted ,
filteredQuery , maxDistance, type, uniqueDocs ) );
s->exec();
shared_ptr<Cursor> c;
c.reset( new GeoSearchCursor( s ) );
@@ -2840,24 +2514,27 @@ namespace mongo {
if ( startsWith(type, "$center") ) {
uassert( 13059 , "$center has to take an object or array" , e.isABSONObj() );
shared_ptr<Cursor> c( new GeoCircleBrowse( this , e.embeddedObjectUserCheck() , query , type, uniqueDocs ) );
shared_ptr<Cursor> c(new GeoCircleBrowse(this, e.embeddedObjectUserCheck(),
filteredQuery, type, uniqueDocs));
return c;
}
else if ( type == "$box" ) {
uassert( 13065 , "$box has to take an object or array" , e.isABSONObj() );
shared_ptr<Cursor> c( new GeoBoxBrowse( this , e.embeddedObjectUserCheck() , query, uniqueDocs ) );
shared_ptr<Cursor> c(new GeoBoxBrowse(this, e.embeddedObjectUserCheck(),
filteredQuery, uniqueDocs));
return c;
}
else if ( startsWith( type, "$poly" ) ) {
uassert( 14029 , "$polygon has to take an object or array" , e.isABSONObj() );
shared_ptr<Cursor> c( new GeoPolygonBrowse( this , e.embeddedObjectUserCheck() , query, uniqueDocs ) );
shared_ptr<Cursor> c(new GeoPolygonBrowse(this, e.embeddedObjectUserCheck(),
filteredQuery, uniqueDocs));
return c;
}
throw UserException( 13058 , str::stream() << "unknown $within information : " << context << ", a shape must be specified." );
}
default:
// Otherwise... assume the object defines a point, and we want to do a zero-radius $within $center
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), query.filterFieldsUndotted( BSON( _geo << "" ), false ) ) );
shared_ptr<Cursor> c( new GeoCircleBrowse( this , BSON( "0" << e.embeddedObjectUserCheck() << "1" << 0 ), filteredQuery));
return c;
}
}

View File

@@ -20,6 +20,7 @@
#include "mongo/pch.h"
#include "../jsobj.h"
#include "shapes.h"
#include <cmath>
@@ -401,106 +402,6 @@ namespace mongo {
virtual GeoHash hash( double x , double y ) const = 0;
};
class Point {
public:
Point( const GeoConvert * g , const GeoHash& hash ) {
g->unhash( hash , _x , _y );
}
explicit Point( const BSONElement& e ) {
BSONObjIterator i(e.Obj());
_x = i.next().number();
_y = i.next().number();
}
explicit Point( const BSONObj& o ) {
BSONObjIterator i(o);
_x = i.next().number();
_y = i.next().number();
}
Point( double x , double y )
: _x( x ) , _y( y ) {
}
Point() : _x(0),_y(0) {
}
GeoHash hash( const GeoConvert * g ) {
return g->hash( _x , _y );
}
double distance( const Point& p ) const {
double a = _x - p._x;
double b = _y - p._y;
// Avoid numerical error if possible...
if( a == 0 ) return abs( _y - p._y );
if( b == 0 ) return abs( _x - p._x );
return sqrt( ( a * a ) + ( b * b ) );
}
/**
* Distance method that compares x or y coords when other direction is zero,
* avoids numerical error when distances are very close to radius but axis-aligned.
*
* An example of the problem is:
* (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
* but:
* 51.9999 + 0.0001 <= 52.0
*
* This avoids some (but not all!) suprising results in $center queries where points are
* ( radius + center.x, center.y ) or vice-versa.
*/
bool distanceWithin( const Point& p, double radius ) const {
double a = _x - p._x;
double b = _y - p._y;
if( a == 0 ) {
//
// Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
// calculated imprecisely. We need to force the compiler to always evaluate it correctly,
// hence the weirdness.
//
// On some 32-bit linux machines, removing the volatile keyword or calculating the sum inline
// will make certain geo tests fail. Of course this check will force volatile for all 32-bit systems,
// not just affected systems.
if( sizeof(void*) <= 4 ){
volatile double sum = _y > p._y ? p._y + radius : _y + radius;
return _y > p._y ? sum >= _y : sum >= p._y;
}
else {
// Original math, correct for most systems
return _y > p._y ? p._y + radius >= _y : _y + radius >= p._y;
}
}
if( b == 0 ) {
if( sizeof(void*) <= 4 ){
volatile double sum = _x > p._x ? p._x + radius : _x + radius;
return _x > p._x ? sum >= _x : sum >= p._x;
}
else {
return _x > p._x ? p._x + radius >= _x : _x + radius >= p._x;
}
}
return sqrt( ( a * a ) + ( b * b ) ) <= radius;
}
string toString() const {
StringBuilder buf;
buf << "(" << _x << "," << _y << ")";
return buf.str();
}
double _x;
double _y;
};
extern const double EARTH_RADIUS_KM;
extern const double EARTH_RADIUS_MILES;

455
src/mongo/db/geo/shapes.h Normal file
View File

@@ -0,0 +1,455 @@
/**
* Copyright (C) 2012 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/>.
*/
#pragma once
#ifndef GEODEBUG
#define GEODEBUG(x)
#endif
namespace mongo {
class Point {
public:
explicit Point( const BSONElement& e ) {
BSONObjIterator i(e.Obj());
_x = i.next().number();
_y = i.next().number();
}
explicit Point( const BSONObj& o ) {
BSONObjIterator i(o);
_x = i.next().number();
_y = i.next().number();
}
Point( double x , double y )
: _x( x ) , _y( y ) {
}
Point() : _x(0),_y(0) {
}
double distance( const Point& p ) const {
double a = _x - p._x;
double b = _y - p._y;
// Avoid numerical error if possible...
if( a == 0 ) return abs( _y - p._y );
if( b == 0 ) return abs( _x - p._x );
return sqrt( ( a * a ) + ( b * b ) );
}
/**
* Distance method that compares x or y coords when other direction is zero,
* avoids numerical error when distances are very close to radius but axis-aligned.
*
* An example of the problem is:
* (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
* but:
* 51.9999 + 0.0001 <= 52.0
*
* This avoids some (but not all!) suprising results in $center queries where points are
* ( radius + center.x, center.y ) or vice-versa.
*/
bool distanceWithin( const Point& p, double radius ) const {
double a = _x - p._x;
double b = _y - p._y;
if( a == 0 ) {
//
// Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
// calculated imprecisely. We need to force the compiler to always evaluate it correctly,
// hence the weirdness.
//
// On some 32-bit linux machines, removing the volatile keyword or calculating the sum inline
// will make certain geo tests fail. Of course this check will force volatile for all 32-bit systems,
// not just affected systems.
if( sizeof(void*) <= 4 ){
volatile double sum = _y > p._y ? p._y + radius : _y + radius;
return _y > p._y ? sum >= _y : sum >= p._y;
}
else {
// Original math, correct for most systems
return _y > p._y ? p._y + radius >= _y : _y + radius >= p._y;
}
}
if( b == 0 ) {
if( sizeof(void*) <= 4 ){
volatile double sum = _x > p._x ? p._x + radius : _x + radius;
return _x > p._x ? sum >= _x : sum >= p._x;
}
else {
return _x > p._x ? p._x + radius >= _x : _x + radius >= p._x;
}
}
return sqrt( ( a * a ) + ( b * b ) ) <= radius;
}
string toString() const {
StringBuilder buf;
buf << "(" << _x << "," << _y << ")";
return buf.str();
}
double _x;
double _y;
};
class Box {
public:
Box( double x , double y , double size )
: _min( x , y ) ,
_max( x + size , y + size ) {
}
Box( Point min , Point max )
: _min( min ) , _max( max ) {
}
Box() {}
BSONArray toBSON() const {
return BSON_ARRAY( BSON_ARRAY( _min._x << _min._y ) << BSON_ARRAY( _max._x << _max._y ) );
}
string toString() const {
StringBuilder buf;
buf << _min.toString() << " -->> " << _max.toString();
return buf.str();
}
bool between( double min , double max , double val , double fudge=0) const {
return val + fudge >= min && val <= max + fudge;
}
bool onBoundary( double bound, double val, double fudge = 0 ) const {
return ( val >= bound - fudge && val <= bound + fudge );
}
bool mid( double amin , double amax , double bmin , double bmax , bool min , double& res ) const {
verify( amin <= amax );
verify( bmin <= bmax );
if ( amin < bmin ) {
if ( amax < bmin )
return false;
res = min ? bmin : amax;
return true;
}
if ( amin > bmax )
return false;
res = min ? amin : bmax;
return true;
}
double intersects( const Box& other ) const {
Point boundMin(0,0);
Point boundMax(0,0);
if ( mid( _min._x , _max._x , other._min._x , other._max._x , true , boundMin._x ) == false ||
mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false ||
mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false ||
mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false )
return 0;
Box intersection( boundMin , boundMax );
return intersection.area() / area();
}
double area() const {
return ( _max._x - _min._x ) * ( _max._y - _min._y );
}
double maxDim() const {
return max( _max._x - _min._x, _max._y - _min._y );
}
// pointFromHash
// pointToHash
// boxFromHash
Point center() const {
return Point( ( _min._x + _max._x ) / 2 ,
( _min._y + _max._y ) / 2 );
}
void truncate(double min, double max) {
if( _min._x < min ) _min._x = min;
if( _min._y < min ) _min._y = min;
if( _max._x > max ) _max._x = max;
if( _max._y > max ) _max._y = max;
}
void fudge(double error) {
_min._x -= error;
_min._y -= error;
_max._x += error;
_max._y += error;
}
bool onBoundary( Point p, double fudge = 0 ) {
return onBoundary( _min._x, p._x, fudge ) ||
onBoundary( _max._x, p._x, fudge ) ||
onBoundary( _min._y, p._y, fudge ) ||
onBoundary( _max._y, p._y, fudge );
}
bool inside( Point p , double fudge = 0 ) const {
bool res = inside( p._x , p._y , fudge );
//cout << "is : " << p.toString() << " in " << toString() << " = " << res << endl;
return res;
}
bool inside( double x , double y , double fudge = 0 ) const {
return
between( _min._x , _max._x , x , fudge ) &&
between( _min._y , _max._y , y , fudge );
}
bool contains(const Box& other, double fudge=0) {
return inside(other._min, fudge) && inside(other._max, fudge);
}
Point _min;
Point _max;
};
class Polygon {
public:
Polygon( void ) : _centroidCalculated( false ) {}
Polygon( vector<Point> points ) : _centroidCalculated( false ),
_points( points ) { }
void add( Point p ) {
_centroidCalculated = false;
_points.push_back( p );
}
int size( void ) const {
return _points.size();
}
/**
* Determine if the point supplied is contained by the current polygon.
*
* The algorithm uses a ray casting method.
*/
bool contains( const Point& p ) const {
return contains( p, 0 ) > 0;
}
int contains( const Point &p, double fudge ) const {
Box fudgeBox( Point( p._x - fudge, p._y - fudge ), Point( p._x + fudge, p._y + fudge ) );
int counter = 0;
Point p1 = _points[0];
for ( int i = 1; i <= size(); i++ ) {
Point p2 = _points[i % size()];
GEODEBUG( "Doing intersection check of " << fudgeBox.toString() << " with seg " << p1.toString() << " to " << p2.toString() );
// We need to check whether or not this segment intersects our error box
if( fudge > 0 &&
// Points not too far below box
fudgeBox._min._y <= std::max( p1._y, p2._y ) &&
// Points not too far above box
fudgeBox._max._y >= std::min( p1._y, p2._y ) &&
// Points not too far to left of box
fudgeBox._min._x <= std::max( p1._x, p2._x ) &&
// Points not too far to right of box
fudgeBox._max._x >= std::min( p1._x, p2._x ) ) {
GEODEBUG( "Doing detailed check" );
// If our box contains one or more of these points, we need to do an exact check.
if( fudgeBox.inside(p1) ) {
GEODEBUG( "Point 1 inside" );
return 0;
}
if( fudgeBox.inside(p2) ) {
GEODEBUG( "Point 2 inside" );
return 0;
}
// Do intersection check for vertical sides
if ( p1._y != p2._y ) {
double invSlope = ( p2._x - p1._x ) / ( p2._y - p1._y );
double xintersT = ( fudgeBox._max._y - p1._y ) * invSlope + p1._x;
if( fudgeBox._min._x <= xintersT && fudgeBox._max._x >= xintersT ) {
GEODEBUG( "Top intersection @ " << xintersT );
return 0;
}
double xintersB = ( fudgeBox._min._y - p1._y ) * invSlope + p1._x;
if( fudgeBox._min._x <= xintersB && fudgeBox._max._x >= xintersB ) {
GEODEBUG( "Bottom intersection @ " << xintersB );
return 0;
}
}
// Do intersection check for horizontal sides
if( p1._x != p2._x ) {
double slope = ( p2._y - p1._y ) / ( p2._x - p1._x );
double yintersR = ( p1._x - fudgeBox._max._x ) * slope + p1._y;
if( fudgeBox._min._y <= yintersR && fudgeBox._max._y >= yintersR ) {
GEODEBUG( "Right intersection @ " << yintersR );
return 0;
}
double yintersL = ( p1._x - fudgeBox._min._x ) * slope + p1._y;
if( fudgeBox._min._y <= yintersL && fudgeBox._max._y >= yintersL ) {
GEODEBUG( "Left intersection @ " << yintersL );
return 0;
}
}
}
else if( fudge == 0 ){
// If this is an exact vertex, we won't intersect, so check this
if( p._y == p1._y && p._x == p1._x ) return 1;
else if( p._y == p2._y && p._x == p2._x ) return 1;
// If this is a horizontal line we won't intersect, so check this
if( p1._y == p2._y && p._y == p1._y ){
// Check that the x-coord lies in the line
if( p._x >= std::min( p1._x, p2._x ) && p._x <= std::max( p1._x, p2._x ) ) return 1;
}
}
// Normal intersection test.
// TODO: Invert these for clearer logic?
if ( p._y > std::min( p1._y, p2._y ) ) {
if ( p._y <= std::max( p1._y, p2._y ) ) {
if ( p._x <= std::max( p1._x, p2._x ) ) {
if ( p1._y != p2._y ) {
double xinters = (p._y-p1._y)*(p2._x-p1._x)/(p2._y-p1._y)+p1._x;
// Special case of point on vertical line
if ( p1._x == p2._x && p._x == p1._x ){
// Need special case for the vertical edges, for example:
// 1) \e pe/----->
// vs.
// 2) \ep---e/----->
//
// if we count exact as intersection, then 1 is in but 2 is out
// if we count exact as no-int then 1 is out but 2 is in.
return 1;
}
else if( p1._x == p2._x || p._x <= xinters ) {
counter++;
}
}
}
}
}
p1 = p2;
}
if ( counter % 2 == 0 ) {
return -1;
}
else {
return 1;
}
}
/**
* Calculate the centroid, or center of mass of the polygon object.
*/
Point centroid( void ) {
/* Centroid is cached, it won't change betwen points */
if ( _centroidCalculated ) {
return _centroid;
}
Point cent;
double signedArea = 0.0;
double area = 0.0; // Partial signed area
/// For all vertices except last
int i = 0;
for ( i = 0; i < size() - 1; ++i ) {
area = _points[i]._x * _points[i+1]._y - _points[i+1]._x * _points[i]._y ;
signedArea += area;
cent._x += ( _points[i]._x + _points[i+1]._x ) * area;
cent._y += ( _points[i]._y + _points[i+1]._y ) * area;
}
// Do last vertex
area = _points[i]._x * _points[0]._y - _points[0]._x * _points[i]._y;
cent._x += ( _points[i]._x + _points[0]._x ) * area;
cent._y += ( _points[i]._y + _points[0]._y ) * area;
signedArea += area;
signedArea *= 0.5;
cent._x /= ( 6 * signedArea );
cent._y /= ( 6 * signedArea );
_centroidCalculated = true;
_centroid = cent;
return cent;
}
Box bounds( void ) {
// TODO: Cache this
_bounds._max = _points[0];
_bounds._min = _points[0];
for ( int i = 1; i < size(); i++ ) {
_bounds._max._x = max( _bounds._max._x, _points[i]._x );
_bounds._max._y = max( _bounds._max._y, _points[i]._y );
_bounds._min._x = min( _bounds._min._x, _points[i]._x );
_bounds._min._y = min( _bounds._min._y, _points[i]._y );
}
return _bounds;
}
private:
bool _centroidCalculated;
Point _centroid;
Box _bounds;
vector<Point> _points;
};
} // namespace mongo

View File

@@ -48,6 +48,7 @@ namespace mongo {
void LastError::appendSelfStatus( BSONObjBuilder &b ) {
if ( writebackId.isSet() ) {
b.append( "writeback" , writebackId );
b.append( "writebackSince", writebackSince );
b.append( "instanceIdent" , prettyHostName() ); // this can be any unique string
}
}
@@ -55,7 +56,7 @@ namespace mongo {
bool LastError::appendSelf( BSONObjBuilder &b , bool blankErr ) {
appendSelfStatus( b );
if ( !valid ) {
if ( blankErr )
b.appendNull( "err" );
@@ -93,6 +94,7 @@ namespace mongo {
uassert(13649, "no operation yet", le);
le->disabled = true;
le->nPrev--; // caller is a command that shouldn't count as an operation
le->writebackSince--; // same as above
return le;
}
@@ -135,6 +137,7 @@ namespace mongo {
else {
err->disabled = false;
err->nPrev++;
err->writebackSince++;
}
}

View File

@@ -29,6 +29,7 @@ namespace mongo {
enum UpdatedExistingType { NotUpdate, True, False } updatedExisting;
OID upsertedId;
OID writebackId; // this shouldn't get reset so that old GLE are handled
int writebackSince;
long long nObjects;
int nPrev;
bool valid;
@@ -36,6 +37,7 @@ namespace mongo {
void writeback( OID& oid ) {
reset( true );
writebackId = oid;
writebackSince = 0;
}
void raiseError(int _code , const char *_msg) {
reset( true );
@@ -56,6 +58,7 @@ namespace mongo {
}
LastError() {
reset();
writebackSince = 0;
}
void reset( bool _valid = false ) {
code = 0;

65
src/mongo/db/matcher.cpp Executable file → Normal file
View File

@@ -256,7 +256,7 @@ namespace mongo {
ss << "elemMatchKey: " << ( _elemMatchKeyFound ? _elemMatchKey : "NONE" ) << " ";
return ss.str();
}
void Matcher::addRegex(const char *fieldName, const char *regex, const char *flags, bool isNot) {
RegexMatcher rm;
@@ -363,8 +363,52 @@ namespace mongo {
flags = fe.valuestrsafe();
break;
}
case BSONObj::opWITHIN: {
BSONObj shapeObj = fe.embeddedObject();
BSONObjIterator argIt(shapeObj);
uassert(16481, "Empty obj for $within: " + shapeObj.toString(), argIt.more());
BSONElement elt = argIt.next();
uassert(16466, "Within must be provided a BSONObj: " + elt.toString(),
elt.isABSONObj());
BSONObj obj = elt.Obj();
BSONObjIterator coordIt(elt.Obj());
uassert(16482, "Malformed $within: ", coordIt.more());
if (str::equals(elt.fieldName(), "$box")) {
BSONElement minE = coordIt.next();
uassert(16467, "Malformed $box: " + obj.toString(), minE.isABSONObj());
uassert(16468, "Malformed $box: " + obj.toString(), coordIt.more());
BSONElement maxE = coordIt.next();
uassert(16469, "Malformed $box: " + obj.toString(), minE.isABSONObj());
_geo.push_back(GeoMatcher::makeBox(e.fieldName(), minE.Obj(), maxE.Obj()));
} else if (str::equals(elt.fieldName(), "$center")) {
BSONElement center = coordIt.next();
uassert(16473, "Malformed $center: " + obj.toString(), center.isABSONObj());
uassert(16474, "Malformed $center: " + obj.toString(), coordIt.more());
BSONElement radius = coordIt.next();
uassert(16475, "Malformed $center: " + obj.toString(), radius.isNumber());
_geo.push_back(
GeoMatcher::makeCircle(e.fieldName(), center.Obj(), radius.number()));
} else if (str::equals(elt.fieldName(), "$polygon")) {
while (coordIt.more()) {
BSONElement coord = coordIt.next();
uassert(16476, "Malformed $polygon: " + obj.toString(), coord.isABSONObj());
BSONObjIterator numIt(coord.Obj());
uassert(16477, "Malformed $polygon: " + obj.toString(), numIt.more());
BSONElement x = numIt.next();
uassert(16478, "Malformed $polygon: " + obj.toString(), x.isNumber());
uassert(16484, "Malformed $polygon: " + obj.toString(), numIt.more());
BSONElement y = numIt.next();
uassert(16479, "Malformed $polygon: " + obj.toString(), y.isNumber());
}
_geo.push_back(GeoMatcher::makePolygon(e.fieldName(), elt.Obj()));
} else {
uasserted(16483, "Couldn't pull any geometry out of $within query: " + obj.toString());
}
break;
}
case BSONObj::opNEAR:
case BSONObj::opWITHIN:
case BSONObj::opMAX_DISTANCE:
break;
default:
@@ -936,6 +980,20 @@ namespace mongo {
}
}
for (vector<GeoMatcher>::const_iterator it = _geo.begin(); it != _geo.end(); ++it) {
verify(_constrainIndexKey.isEmpty());
BSONElementSet s;
jsobj.getFieldsDotted(it->getFieldName().c_str(), s, false);
int matches = 0;
for (BSONElementSet::const_iterator i = s.begin(); i != s.end(); ++i) {
if (!i->isABSONObj()) { continue; }
Point p;
if (!GeoMatcher::pointFrom(i->Obj(), &p)) { continue; }
if (it->containsPoint(p)) { ++matches; break; }
}
if (0 == matches) { return false; }
}
for (vector<RegexMatcher>::const_iterator it = _regexs.begin();
it != _regexs.end();
++it) {
@@ -1177,6 +1235,9 @@ namespace mongo {
if ( !( _basics.size() == docMatcher._basics.size() && _regexs.size() == docMatcher._regexs.size() && !docMatcher._where ) ) {
return false;
}
if (_geo.size() != docMatcher._geo.size()) {
return false;
}
if ( _andMatchers.size() != docMatcher._andMatchers.size() ) {
return false;
}

View File

@@ -22,6 +22,7 @@
#include "jsobj.h"
#include "pcrecpp.h"
#include "geo/shapes.h"
namespace mongo {
@@ -42,6 +43,94 @@ namespace mongo {
RegexMatcher() : _isNot() {}
};
class GeoMatcher {
private:
GeoMatcher(const string& field) : _isBox(false), _isCircle(false), _isPolygon(false),
_fieldName(field) {}
bool _isBox;
Box _box;
bool _isCircle;
Point _center;
double _radius;
bool _isPolygon;
Polygon _polygon;
string _fieldName;
public:
const string& getFieldName() const { return _fieldName; }
static GeoMatcher makeBox(const string& field, const BSONObj &min, const BSONObj &max) {
GeoMatcher m(field);
m._isBox = true;
uassert(16470, "Malformed coord: " + min.toString(), pointFrom(min, &m._box._min));
uassert(16471, "Malformed coord: " + max.toString(), pointFrom(max, &m._box._max));
return m;
}
static GeoMatcher makeCircle(const string& field, const BSONObj &center, double rad) {
GeoMatcher m(field);
m._isCircle = true;
uassert(16472, "Malformed coord: " + center.toString(), pointFrom(center, &m._center));
m._radius = rad;
return m;
}
static GeoMatcher makePolygon(const string& field, const BSONObj &poly) {
GeoMatcher m(field);
vector<Point> points;
m._isPolygon = true;
BSONObjIterator coordIt(poly);
while (coordIt.more()) {
BSONElement coord = coordIt.next();
const BSONObj& obj = coord.Obj();
Point p;
uassert(16480, "Malformed coord: " + obj.toString(), pointFrom(obj, &p));
points.push_back(p);
}
m._polygon = Polygon(points);
return m;
}
bool containsPoint(Point p) const {
if (_isBox) {
return _box.inside(p, 0);
} else if (_isCircle) {
return _center.distance(p) <= _radius;
} else if (_isPolygon) {
return _polygon.contains(p);
} else {
return false;
}
}
string toString() const {
stringstream ss;
if (_isBox) {
ss << "GeoMatcher Box: " << _box.toString();
} else if (_isCircle) {
ss << "GeoMatcher Circle @ " << _center.toString() << " r = " << _radius;
} else {
ss << "GeoMatcher UNKNOWN";
}
return ss.str();
}
static bool pointFrom(const BSONObj o, Point *p) {
BSONObjIterator i(o);
if (!i.more()) { return false; }
BSONElement xe = i.next();
if (!i.more()) { return false; }
BSONElement ye = i.next();
if (!xe.isNumber() || !ye.isNumber()) { return false; }
p->_x = xe.number();
p->_y = ye.number();
return true;
}
};
struct element_lt {
bool operator()(const BSONElement& l, const BSONElement& r) const {
int x = (int) l.canonicalType() - (int) r.canonicalType();
@@ -266,6 +355,7 @@ namespace mongo {
bool _atomic;
vector<RegexMatcher> _regexs;
vector<GeoMatcher> _geo;
// so we delete the mem when we're done:
vector< shared_ptr< BSONObjBuilder > > _builders;

View File

@@ -1113,4 +1113,54 @@ namespace mongo {
updateIsIndexed( i->second, idxKeys , backgroundKeys );
}
bool getCanonicalIndexField( const string& fullName, string* out ) {
// check if fieldName contains ".$" or ".###" substrings (#=digit) and skip them
if ( fullName.find( '.' ) == string::npos )
return false;
bool modified = false;
StringBuilder buf;
for ( size_t i=0; i<fullName.size(); i++ ) {
char c = fullName[i];
if ( c != '.' ) {
buf << c;
continue;
}
// check for ".$", skip if present
if ( fullName[i+1] == '$' ) {
i++;
modified = true;
continue;
}
// check for ".###" for any number of digits (no letters)
if ( isdigit( fullName[i+1] ) ) {
size_t j = i;
// skip digits
while ( j+1 < fullName.size() && isdigit( fullName[j+1] ) )
j++;
if ( j+1 == fullName.size() || fullName[j+1] == '.' ) {
// only digits found, skip forward
i = j;
modified = true;
continue;
}
}
buf << c;
}
if ( !modified )
return false;
*out = buf.str();
return true;
}
} // namespace mongo

View File

@@ -30,6 +30,12 @@ namespace mongo {
class ModState;
class ModSetState;
/**
* a.$ -> a
* @return true if out is set and we made a change
*/
bool getCanonicalIndexField( const string& fullName, string* out );
/* Used for modifiers such as $inc, $set, $push, ...
* stores the info about a single operation
* once created should never be modified
@@ -143,6 +149,7 @@ namespace mongo {
// check if there is an index key equal to mod
if ( idxKeys.count(fullName) )
return true;
// check if there is an index key that is a child of mod
set< string >::const_iterator j = idxKeys.upper_bound( fullName );
if ( j != idxKeys.end() && j->find( fullName ) == 0 && (*j)[fullName.size()] == '.' )
@@ -151,51 +158,22 @@ namespace mongo {
return false;
}
/**
* checks if mod is in the index by inspecting fieldName, and removing
* .$ or .### substrings (#=digit) with any number of digits.
*
* @return true iff the mod is indexed
*/
bool isIndexed( const set<string>& idxKeys ) const {
string fullName = fieldName;
string myFieldName = fieldName;
if ( isIndexed( fullName , idxKeys ) )
// first, check if full name is in idxKeys
if ( isIndexed( myFieldName , idxKeys ) )
return true;
if ( strstr( fieldName , "." ) ) {
// check for a.0.1
StringBuilder buf;
for ( size_t i=0; i<fullName.size(); i++ ) {
char c = fullName[i];
if ( c == '$' &&
i > 0 && fullName[i-1] == '.' &&
i+1<fullName.size() &&
fullName[i+1] == '.' ) {
i++;
continue;
}
buf << c;
if ( c != '.' )
continue;
if ( ! isdigit( fullName[i+1] ) )
continue;
bool possible = true;
size_t j=i+2;
for ( ; j<fullName.size(); j++ ) {
char d = fullName[j];
if ( d == '.' )
break;
if ( isdigit( d ) )
continue;
possible = false;
break;
}
if ( possible )
i = j;
}
string x = buf.str();
if ( isIndexed( x , idxKeys ) )
string x;
if ( getCanonicalIndexField( myFieldName, &x ) ) {
if ( isIndexed( x, idxKeys ) )
return true;
}

View File

@@ -642,6 +642,7 @@ doneCheckOrder:
bool QueryPlanGenerator::addSpecialPlan( NamespaceDetails *d ) {
DEBUGQO( "\t special : " << _qps.frsp().getSpecial() );
// If an index exists for the special query component, use it.
if ( _qps.frsp().getSpecial().size() ) {
string special = _qps.frsp().getSpecial();
NamespaceDetails::IndexIterator i = d->ii();
@@ -656,6 +657,12 @@ doneCheckOrder:
return true;
}
}
// If no index exists but the index is not mandatory (Matcher has functionality to
// support it), have the caller fall through to using a normal query plan.
if (!_qps.frsp().hasSpecialThatNeedsIndex()) { return false; }
// Otherwise, error.
uassert( 13038, (string)"can't find special index: " + special +
" for: " + _qps.originalQuery().toString(), false );
}

View File

@@ -163,7 +163,7 @@ namespace mongo {
FieldRange::FieldRange( const BSONElement &e, bool isNot, bool optimize ) :
_exactMatchRepresentation() {
_specialNeedsIndex(true), _exactMatchRepresentation() {
int op = e.getGtLtOp();
// NOTE with $not, we could potentially form a complementary set of intervals.
@@ -428,8 +428,12 @@ namespace mongo {
log() << "warning: shouldn't get here?" << endl;
break;
}
case BSONObj::opNEAR:
case BSONObj::opWITHIN:
_specialNeedsIndex = false;
_special = "2d";
break;
case BSONObj::opNEAR:
_specialNeedsIndex = true;
_special = "2d";
break;
case BSONObj::opEXISTS: {
@@ -465,8 +469,10 @@ namespace mongo {
_intervals = newIntervals;
for( vector<BSONObj>::const_iterator i = other._objData.begin(); i != other._objData.end(); ++i )
_objData.push_back( *i );
if ( _special.size() == 0 && other._special.size() )
if ( _special.size() == 0 && other._special.size() ) {
_special = other._special;
_specialNeedsIndex = other._specialNeedsIndex;
}
_exactMatchRepresentation = exactMatchRepresentation;
}
@@ -767,6 +773,17 @@ namespace mongo {
return s;
}
bool FieldRangeSet::hasSpecialThatNeedsIndex() const {
for ( map<string,FieldRange>::const_iterator i=_ranges.begin(); i!=_ranges.end(); i++ ) {
if ( i->second.getSpecial().size() == 0 )
continue;
if ( i->second.hasSpecialThatNeedsIndex() )
return true;
}
return false;
}
/**
* Btree scanning for a multidimentional key range will yield a
* multidimensional box. The idea here is that if an 'other'

View File

@@ -359,6 +359,8 @@ namespace mongo {
*/
void reverse( FieldRange &ret ) const;
bool hasSpecialThatNeedsIndex() const { verify( _special.size() ); return _specialNeedsIndex; }
string toString() const;
private:
BSONObj addObj( const BSONObj &o );
@@ -368,6 +370,7 @@ namespace mongo {
// Owns memory for our BSONElements.
vector<BSONObj> _objData;
string _special;
bool _specialNeedsIndex;
bool _exactMatchRepresentation;
};
@@ -457,6 +460,7 @@ namespace mongo {
QueryPattern pattern( const BSONObj &sort = BSONObj() ) const;
string getSpecial() const;
bool hasSpecialThatNeedsIndex() const;
/**
* @return a FieldRangeSet approximation of the documents in 'this' but
@@ -578,6 +582,7 @@ namespace mongo {
const char *ns() const { return _singleKey.ns(); }
string getSpecial() const { return _singleKey.getSpecial(); }
bool hasSpecialThatNeedsIndex() const { return _singleKey.hasSpecialThatNeedsIndex(); }
/** Intersect with another FieldRangeSetPair. */
FieldRangeSetPair &operator&=( const FieldRangeSetPair &other );

View File

@@ -1066,8 +1066,7 @@ namespace mongo {
else {
BSONObj user;
{
Lock::GlobalWrite lk;
Client::Context ctxt("local.");
Client::ReadContext ctxt("local.");
if( !Helpers::findOne("local.system.users", userReplQuery, user) ||
// try the first user in local
!Helpers::getSingleton("local.system.users", user) ) {

View File

@@ -153,7 +153,7 @@ namespace mongo {
virtual bool slaveOk() const { return true; }
virtual bool adminOnly() const { return true; }
virtual LockType locktype() const { return READ; }
virtual LockType locktype() const { return NONE; }
virtual void help( stringstream& help ) const { help << "usage by collection, in micros "; }
virtual bool run(const string& , BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl) {

View File

@@ -132,6 +132,44 @@ namespace MatcherTests {
}
};
class WithinBox {
public:
void run() {
Matcher m(fromjson("{loc:{$within:{$box:[{x:4,y:4},[6,6]]}}}"));
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
ASSERT(m.matches(fromjson("{loc: [5,5]}")));
ASSERT(m.matches(fromjson("{loc: [5,5.1]}")));
ASSERT(m.matches(fromjson("{loc: {x: 5, y:5.1}}")));
}
};
class WithinPolygon {
public:
void run() {
Matcher m(fromjson("{loc:{$within:{$polygon:[[0,0],[0,5],[5,5],[5,0]]}}}"));
ASSERT(m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: [4,4]}")));
ASSERT(m.matches(fromjson("{loc: {x:5,y:5}}")));
ASSERT(!m.matches(fromjson("{loc: [5,5.1]}")));
ASSERT(!m.matches(fromjson("{loc: {}}")));
}
};
class WithinCenter {
public:
void run() {
Matcher m(fromjson("{loc:{$within:{$center:[{x:30,y:30},10]}}}"));
ASSERT(!m.matches(fromjson("{loc: [3,4]}")));
ASSERT(m.matches(fromjson("{loc: {x:30,y:30}}")));
ASSERT(m.matches(fromjson("{loc: [20,30]}")));
ASSERT(m.matches(fromjson("{loc: [30,20]}")));
ASSERT(m.matches(fromjson("{loc: [40,30]}")));
ASSERT(m.matches(fromjson("{loc: [30,40]}")));
ASSERT(!m.matches(fromjson("{loc: [31,40]}")));
}
};
/** Test that MatchDetails::elemMatchKey() is set correctly after a match. */
class ElemMatchKey {
public:
@@ -371,6 +409,9 @@ namespace MatcherTests {
add<Covered::ElemMatchKeyIndexedSingleKey>();
add<AllTiming>();
add<Visit>();
add<WithinBox>();
add<WithinCenter>();
add<WithinPolygon>();
}
} dball;

View File

@@ -1295,6 +1295,36 @@ namespace UpdateTests {
};
class IndexFieldNameTest {
public:
void run() {
string x;
ASSERT_FALSE( getCanonicalIndexField( "a", &x ) );
ASSERT_FALSE( getCanonicalIndexField( "aaa", &x ) );
ASSERT_FALSE( getCanonicalIndexField( "a.b", &x ) );
ASSERT_TRUE( getCanonicalIndexField( "a.$", &x ) );
ASSERT_EQUALS( x, "a" );
ASSERT_TRUE( getCanonicalIndexField( "a.0", &x ) );
ASSERT_EQUALS( x, "a" );
ASSERT_TRUE( getCanonicalIndexField( "a.123", &x ) );
ASSERT_EQUALS( x, "a" );
ASSERT_TRUE( getCanonicalIndexField( "a.$.b", &x ) );
ASSERT_EQUALS( x, "a.b" );
ASSERT_TRUE( getCanonicalIndexField( "a.0.b", &x ) );
ASSERT_EQUALS( x, "a.b" );
ASSERT_TRUE( getCanonicalIndexField( "a.123.b", &x ) );
ASSERT_EQUALS( x, "a.b" );
ASSERT_FALSE( getCanonicalIndexField( "a.123a", &x ) );
ASSERT_FALSE( getCanonicalIndexField( "a.a123", &x ) );
ASSERT_FALSE( getCanonicalIndexField( "a.123a.b", &x ) );
ASSERT_FALSE( getCanonicalIndexField( "a.a123.b", &x ) );
}
};
class All : public Suite {
public:
All() : Suite( "update" ) {
@@ -1401,6 +1431,8 @@ namespace UpdateTests {
add< basic::bit1 >();
add< basic::unset >();
add< basic::setswitchint >();
add< IndexFieldNameTest >();
}
} myall;

View File

@@ -212,8 +212,8 @@ namespace mongo {
int balancedLastTime ) {
// 1) check for shards that policy require to us to move off of
// draining, maxSize
// 1) check for shards that policy require to us to move off of:
// draining only
// 2) check tag policy violations
// 3) then we make sure chunks are balanced for each tag
@@ -226,7 +226,7 @@ namespace mongo {
string shard = *z;
const ShardInfo& info = distribution.shardInfo( shard );
if ( ! info.isSizeMaxed() && ! info.isDraining() )
if ( ! info.isDraining() )
continue;
if ( distribution.numberOfChunksInShard( shard ) == 0 )

View File

@@ -80,6 +80,10 @@ namespace mongo {
*/
bool hasOpsQueued() const { return _hasOpsQueued; }
long long getMaxSize() const { return _maxSize; }
long long getCurrSize() const { return _currSize; }
string toString() const;
private:

View File

@@ -291,5 +291,221 @@ namespace mongo {
}
/**
* Idea for this test is to set up three shards, one of which is overloaded (too much data).
*
* Even though the overloaded shard has less chunks, we shouldn't move chunks to that shard.
*/
TEST( BalancerPolicyTests, MaxSizeRespect ) {
ShardToChunksMap chunks;
addShard( chunks, 3 , false );
addShard( chunks, 4 , false );
addShard( chunks, 6 , true );
// Note that maxSize of shard0 is 1, and it is therefore overloaded with currSize = 3.
// Other shards have maxSize = 0 = unset.
ShardInfoMap shards;
// ShardInfo(maxSize, currSize, draining, opsQueued)
shards["shard0"] = ShardInfo( 1, 3, false, false );
shards["shard1"] = ShardInfo( 0, 4, false, false );
shards["shard2"] = ShardInfo( 0, 6, false, false );
DistributionStatus d( shards, chunks );
MigrateInfo* m = BalancerPolicy::balance( "ns", d, 0 );
ASSERT( m );
ASSERT_EQUALS( "shard2" , m->from );
ASSERT_EQUALS( "shard1" , m->to );
}
/**
* Here we check that being over the maxSize is *not* equivalent to draining, we don't want
* to empty shards for no other reason than they are over this limit.
*/
TEST( BalancerPolicyTests, MaxSizeNoDrain ) {
ShardToChunksMap chunks;
// Shard0 will be overloaded
addShard( chunks, 4 , false );
addShard( chunks, 4 , false );
addShard( chunks, 4 , true );
// Note that maxSize of shard0 is 1, and it is therefore overloaded with currSize = 4.
// Other shards have maxSize = 0 = unset.
ShardInfoMap shards;
// ShardInfo(maxSize, currSize, draining, opsQueued)
shards["shard0"] = ShardInfo( 1, 4, false, false );
shards["shard1"] = ShardInfo( 0, 4, false, false );
shards["shard2"] = ShardInfo( 0, 4, false, false );
DistributionStatus d( shards, chunks );
MigrateInfo* m = BalancerPolicy::balance( "ns", d, 0 );
ASSERT( !m );
}
// Note: Only in 2.2, 2.4 has utility class
class PseudoRandom {
public:
PseudoRandom(unsigned int seed) {
_seed = seed;
}
int nextInt32( int max = -1 ){
#if !defined(_WIN32)
int r = rand_r( &_seed ) ;
#else
int r = ::rand(); // seed not used in this case
#endif
return max > 0 ? r % max : r;
}
private:
unsigned int _seed;
};
/**
* Idea behind this test is that we set up several shards, the first two of which are
* draining and the second two of which have a data size limit. We also simulate a random
* number of chunks on each shard.
*
* Once the shards are setup, we virtually migrate numChunks times, or until there are no
* more migrations to run. Each chunk is assumed to have a size of 1 unit, and we increment
* our currSize for each shard as the chunks move.
*
* Finally, we ensure that the drained shards are drained, the data-limited shards aren't
* overloaded, and that all shards (including the data limited shard if the baseline isn't
* over the limit are balanced to within 1 unit of some baseline.
*
*/
TEST( BalancerPolicyTests, Simulation ) {
// Hardcode seed here, make test deterministic.
int64_t seed = 1337;
PseudoRandom rng(seed);
// Run test 10 times
for (int test = 0; test < 10; test++) {
//
// Setup our shards as draining, with maxSize, and normal
//
int numShards = 7;
int numChunks = 0;
ShardToChunksMap chunks;
ShardInfoMap shards;
map<string,int> expected;
for (int i = 0; i < numShards; i++) {
int numShardChunks = rng.nextInt32(100);
bool draining = i < 2;
bool maxed = i >= 2 && i < 4;
if (draining) expected[str::stream() << "shard" << i] = 0;
if (maxed) expected[str::stream() << "shard" << i] = numShardChunks + 1;
addShard(chunks, numShardChunks, false);
numChunks += numShardChunks;
shards[str::stream() << "shard" << i] =
ShardInfo(maxed ? numShardChunks + 1 : 0,
numShardChunks, draining, false);
}
for (ShardInfoMap::iterator it = shards.begin(); it != shards.end(); ++it) {
log() << it->first << " : " << it->second.toString() << endl;
}
//
// Perform migrations and increment data size as chunks move
//
for (int i = 0; i < numChunks; i++) {
DistributionStatus d( shards, chunks );
MigrateInfo* m = BalancerPolicy::balance( "ns", d, i != 0 );
if (!m) {
log() << "Finished with test moves." << endl;
break;
}
moveChunk(chunks, m);
{
ShardInfo& info = shards[m->from];
shards[m->from] = ShardInfo(info.getMaxSize(),
info.getCurrSize() - 1,
info.isDraining(),
info.hasOpsQueued());
}
{
ShardInfo& info = shards[m->to];
shards[m->to] = ShardInfo(info.getMaxSize(),
info.getCurrSize() + 1,
info.isDraining(),
info.hasOpsQueued());
}
}
//
// Make sure our balance is correct and our data size is low.
//
// The balanced value is the count on the last shard, since it's not draining or
// limited
int balancedSize = (--shards.end())->second.getCurrSize();
for (ShardInfoMap::iterator it = shards.begin(); it != shards.end(); ++it) {
log() << it->first << " : " << it->second.toString() << endl;
}
for (ShardInfoMap::iterator it = shards.begin(); it != shards.end(); ++it) {
log() << it->first << " : " << it->second.toString() << endl;
map<string,int>::iterator expectedIt = expected.find(it->first);
if (expectedIt == expected.end()) {
bool isInRange = it->second.getCurrSize() >= balancedSize - 1 &&
it->second.getCurrSize() <= balancedSize + 1;
if (!isInRange) {
warning() << "non-limited and non-draining shard had "
<< it->second.getCurrSize() << " chunks, expected near "
<< balancedSize << endl;
}
ASSERT(isInRange);
}
else {
int expectedSize = expectedIt->second;
bool isInRange = it->second.getCurrSize() <= expectedSize;
if (isInRange && expectedSize >= balancedSize) {
isInRange = it->second.getCurrSize() >= balancedSize - 1 &&
it->second.getCurrSize() <= balancedSize + 1;
}
if (!isInRange) {
warning() << "limited or draining shard had "
<< it->second.getCurrSize() << " chunks, expected less than "
<< expectedSize << " and (if less than expected) near "
<< balancedSize << endl;
}
ASSERT(isInRange);
}
}
}
}
}
}

View File

@@ -23,6 +23,7 @@
#include "../util/startup_test.h"
#include "../util/timer.h"
#include "mongo/client/dbclientcursor.h"
#include "mongo/db/namespacestring.h"
#include "chunk.h"
#include "chunk_diff.h"
@@ -364,7 +365,7 @@ namespace mongo {
// this does mean mongos has more back pressure than mongod alone
// since it nots 100% tcp queue bound
// this was implicit before since we did a splitVector on the same socket
ShardConnection::sync();
ShardConnection::sync( NamespaceString(getManager()->getns()).db );
LOG(1) << "about to initiate autosplit: " << *this << " dataWritten: " << _dataWritten << " splitThreshold: " << splitThreshold << endl;

View File

@@ -284,7 +284,16 @@ namespace mongo {
BSONObj query = queryB.obj();
// log() << "major version query from " << *_maxVersion << " and over " << _maxShardVersions->size() << " shards is " << query << endl;
LOG(2) << "major version query from " << *_maxVersion << " and over "
<< _maxShardVersions->size() << " shards is " << query << endl;
//
// NOTE: IT IS IMPORTANT FOR CONSISTENCY THAT WE SORT BY ASC VERSION, TO HANDLE
// CURSOR YIELDING BETWEEN CHUNKS BEING MIGRATED.
//
Query queryObj(query);
queryObj.sort(BSON( "lastmod" << 1 ));
return Query( query );
}

View File

@@ -92,7 +92,7 @@ namespace mongo {
_lastAccess = 0;
}
void ClientInfo::_addWriteBack( vector<WBInfo>& all , const BSONObj& gle ) {
void ClientInfo::_addWriteBack( vector<WBInfo>& all, const BSONObj& gle, bool fromLastOperation ) {
BSONElement w = gle["writeback"];
if ( w.type() != jstOID )
@@ -109,10 +109,12 @@ namespace mongo {
if ( gle["instanceIdent"].type() == String )
ident = gle["instanceIdent"].String();
all.push_back( WBInfo( WriteBackListener::ConnectionIdent( ident , cid.numberLong() ) , w.OID() ) );
all.push_back( WBInfo( WriteBackListener::ConnectionIdent( ident , cid.numberLong() ),
w.OID(),
fromLastOperation ) );
}
vector<BSONObj> ClientInfo::_handleWriteBacks( vector<WBInfo>& all , bool fromWriteBackListener ) {
vector<BSONObj> ClientInfo::_handleWriteBacks( const vector<WBInfo>& all , bool fromWriteBackListener ) {
vector<BSONObj> res;
if ( all.size() == 0 )
@@ -190,7 +192,7 @@ namespace mongo {
conn.done();
}
_addWriteBack( writebacks , res );
_addWriteBack( writebacks, res, true );
LOG(4) << "gathering writebacks from " << sinceLastGetError().size() << " hosts for"
<< " gle (" << theShard << ")" << endl;
@@ -206,7 +208,7 @@ namespace mongo {
try {
ShardConnection conn( temp , "" );
ON_BLOCK_EXIT_OBJ( conn, &ShardConnection::done );
_addWriteBack( writebacks , conn->getLastErrorDetailed() );
_addWriteBack( writebacks, conn->getLastErrorDetailed(), false );
}
catch( std::exception &e ){
@@ -232,10 +234,25 @@ namespace mongo {
// since that's the current write
// but we block for all
verify( v.size() >= 1 );
result.appendElements( v[0] );
result.appendElementsUnique( res );
result.append( "writebackGLE" , v[0] );
result.append( "initialGLEHost" , theShard );
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 );
}
}
}
else {
@@ -288,7 +305,7 @@ namespace mongo {
return false;
}
_addWriteBack( writebacks, res );
_addWriteBack( writebacks, res, true );
string temp = DBClientWithCommands::getLastErrorString( res );
if ( (*conn)->type() != ConnectionString::SYNC && ( ok == false || temp.size() ) ) {
@@ -327,7 +344,7 @@ namespace mongo {
ShardConnection conn( temp , "" );
try {
_addWriteBack( writebacks, conn->getLastErrorDetailed() );
_addWriteBack( writebacks, conn->getLastErrorDetailed(), false );
}
catch( std::exception &e ){
warning() << "could not clear last error from a shard " << temp << causedBy( e ) << endl;

View File

@@ -107,14 +107,16 @@ namespace mongo {
private:
AuthenticationInfo _ai;
struct WBInfo {
WBInfo( const WriteBackListener::ConnectionIdent& c , OID o ) : ident( c ) , id( o ) {}
WBInfo( const WriteBackListener::ConnectionIdent& c, OID o, bool fromLastOperation )
: ident( c ), id( o ), fromLastOperation( fromLastOperation ) {}
WriteBackListener::ConnectionIdent ident;
OID id;
bool fromLastOperation;
};
// for getLastError
void _addWriteBack( vector<WBInfo>& all , const BSONObj& o );
vector<BSONObj> _handleWriteBacks( vector<WBInfo>& all , bool fromWriteBackListener );
void _addWriteBack( vector<WBInfo>& all , const BSONObj& o, bool fromLastOperation );
vector<BSONObj> _handleWriteBacks( const vector<WBInfo>& all , bool fromWriteBackListener );
int _id; // unique client id

View File

@@ -35,6 +35,7 @@
#include "../client/connpool.h"
#include "mongo/client/dbclientcursor.h"
#include "mongo/db/namespacestring.h"
#include "../db/dbmessage.h"
#include "../db/commands.h"
@@ -712,14 +713,14 @@ namespace mongo {
if ( ! okForConfigChanges( errmsg ) )
return false;
ShardConnection::sync();
string ns = cmdObj.firstElement().valuestrsafe();
if ( ns.size() == 0 ) {
errmsg = "no ns";
return false;
}
ShardConnection::sync( NamespaceString(ns).db );
DBConfigPtr config = grid.getDBConfig( ns );
if ( ! config->isSharded( ns ) ) {
config->reload();
@@ -794,15 +795,16 @@ namespace mongo {
if ( ! okForConfigChanges( errmsg ) )
return false;
ShardConnection::sync();
Timer t;
string ns = cmdObj.firstElement().valuestrsafe();
if ( ns.size() == 0 ) {
errmsg = "no ns";
return false;
}
ShardConnection::sync( NamespaceString(ns).db );
Timer t;
DBConfigPtr config = grid.getDBConfig( ns );
if ( ! config->isSharded( ns ) ) {
config->reload();
@@ -1281,6 +1283,11 @@ namespace mongo {
continue;
}
if ( name == "config" || name == "admin" ) {
//always get this from the config servers
continue;
}
long long size = i->second;
totalSize += size;
@@ -1293,7 +1300,7 @@ namespace mongo {
bb.append( temp.obj() );
}
if ( sizes.find( "config" ) == sizes.end() ){
{ // get config db from the config servers (first one)
scoped_ptr<ScopedDbConnection> conn(
ScopedDbConnection::getInternalScopedDbConnection( configServer.getPrimary()
.getConnString() ) );
@@ -1314,6 +1321,27 @@ namespace mongo {
conn->done();
}
{ // get admin db from the config servers (first one)
scoped_ptr<ScopedDbConnection> conn(
ScopedDbConnection::getInternalScopedDbConnection(
configServer.getPrimary().getConnString(), 30));
BSONObj x;
if ( conn->get()->simpleCommand( "admin" , &x , "dbstats" ) ){
BSONObjBuilder b;
b.append( "name" , "admin" );
b.appendBool( "empty" , false );
if ( x["fileSize"].type() )
b.appendAs( x["fileSize"] , "sizeOnDisk" );
else
b.append( "sizeOnDisk" , 1 );
bb.append( b.obj() );
}
else {
bb.append( BSON( "name" << "admin" ) );
}
conn->done();
}
bb.done();
result.appendNumber( "totalSize" , totalSize );

View File

@@ -1033,15 +1033,16 @@ namespace mongo {
log(1) << "replicaSetChange: shard not found for set: " << monitor->getServerAddress() << endl;
return;
}
scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getScopedDbConnection(
scoped_ptr<ScopedDbConnection> conn( ScopedDbConnection::getInternalScopedDbConnection(
configServer.getConnectionString().toString(), 30.0 ) );
conn->get()->update( ShardNS::shard,
BSON( "_id" << s.getName() ),
BSON( "$set" << BSON( "host" << monitor->getServerAddress() ) ) );
conn->done();
}
catch ( DBException & ) {
error() << "RSChangeWatcher: could not update config db for set: " << monitor->getName() << " to: " << monitor->getServerAddress() << endl;
catch (DBException& e) {
error() << "RSChangeWatcher: could not update config db for set: " << monitor->getName()
<< " to: " << monitor->getServerAddress() << causedBy(e) << endl;
}
}

View File

@@ -294,7 +294,10 @@ namespace mongo {
}
void done() {
Lock::DBRead lk( _ns );
log() << "MigrateFromStatus::done About to acquire global write lock to exit critical "
"section" << endl;
Lock::GlobalWrite lk;
log() << "MigrateFromStatus::done Global lock acquired" << endl;
{
scoped_spinlock lk( _trackerLocks );
@@ -913,6 +916,7 @@ namespace mongo {
configServer.logChange( "moveChunk.start" , ns , chunkInfo );
ShardChunkVersion maxVersion;
ShardChunkVersion startingVersion;
string myOldShard;
{
scoped_ptr<ScopedDbConnection> conn(
@@ -981,10 +985,16 @@ namespace mongo {
// it's possible this shard will be *at* zero version from a previous migrate and
// no refresh will be done
// TODO: Make this less fragile
ShardChunkVersion shardVersion = maxVersion;
shardingState.trySetVersion( ns , shardVersion /* will return updated */ );
startingVersion = maxVersion;
shardingState.trySetVersion( ns , startingVersion /* will return updated */ );
log() << "moveChunk request accepted at version " << shardVersion << migrateLog;
if (startingVersion.majorVersion() == 0) {
// It makes no sense to migrate if our version is zero and we have no chunks, so return
warning() << "moveChunk cannot start migration with zero version" << endl;
return false;
}
log() << "moveChunk request accepted at version " << startingVersion << migrateLog;
}
timing.done(2);
@@ -1099,8 +1109,7 @@ namespace mongo {
// 5.a
// we're under the collection lock here, so no other migrate can change maxVersion or ShardChunkManager state
migrateFromStatus.setInCriticalSection( true );
ShardChunkVersion currVersion = maxVersion;
ShardChunkVersion myVersion = currVersion;
ShardChunkVersion myVersion = maxVersion;
myVersion.incMajor();
{
@@ -1120,7 +1129,8 @@ namespace mongo {
{
BSONObj res;
scoped_ptr<ScopedDbConnection> connTo(
ScopedDbConnection::getScopedDbConnection( toShard.getConnString() ) );
ScopedDbConnection::getScopedDbConnection( toShard.getConnString(),
35.0 ) );
bool ok;
@@ -1132,21 +1142,24 @@ namespace mongo {
catch( DBException& e ){
errmsg = str::stream() << "moveChunk could not contact to: shard " << toShard.getConnString() << " to commit transfer" << causedBy( e );
warning() << errmsg << endl;
return false;
ok = false;
}
connTo->done();
if ( ! ok ) {
log() << "moveChunk migrate commit not accepted by TO-shard: " << res
<< " resetting shard version to: " << startingVersion << migrateLog;
{
Lock::DBWrite lk( ns );
Lock::GlobalWrite lk;
log() << "moveChunk global lock acquired to reset shard version from "
"failed migration" << endl;
// revert the chunk manager back to the state before "forgetting" about the chunk
shardingState.undoDonateChunk( ns , min , max , currVersion );
shardingState.undoDonateChunk( ns , min , max , startingVersion );
}
log() << "moveChunk migrate commit not accepted by TO-shard: " << res
<< " resetting shard version to: " << currVersion << migrateLog;
log() << "Shard version successfully reset to clean up failed migration"
<< endl;
errmsg = "_recvChunkCommit failed!";
result.append( "cause" , res );
@@ -1425,6 +1438,10 @@ namespace mongo {
slaveCount = ( getSlaveCount() / 2 ) + 1;
log() << "starting receiving-end of migration of chunk " << min << " -> " << max <<
" for collection " << ns << " from " << from <<
" (" << getSlaveCount() << " slaves detected)" << endl;
string errmsg;
MoveTimingHelper timing( "to" , ns , min , max , 5 /* steps */ , errmsg );
@@ -1597,6 +1614,8 @@ namespace mongo {
// this will prevent us from going into critical section until we're ready
Timer t;
while ( t.minutes() < 600 ) {
log() << "Waiting for replication to catch up before entering critical section"
<< endl;
if ( flushPendingWrites( lastOpApplied ) )
break;
sleepsecs(1);
@@ -1786,7 +1805,8 @@ namespace mongo {
Timer t;
// we wait for the commit to succeed before giving up
while ( t.minutes() <= 5 ) {
while ( t.seconds() <= 30 ) {
log() << "Waiting for commit to finish" << endl;
sleepmillis(1);
if ( state == DONE )
return true;

View File

@@ -162,6 +162,7 @@ namespace mongo {
void ShardingState::undoDonateChunk( const string& ns , const BSONObj& min , const BSONObj& max , ShardChunkVersion version ) {
scoped_lock lk( _mutex );
log() << "ShardingState::undoDonateChunk acquired _mutex" << endl;
ChunkManagersMap::const_iterator it = _chunks.find( ns );
verify( it != _chunks.end() ) ;
@@ -634,7 +635,7 @@ namespace mongo {
if ( version < globalVersion && version.hasCompatibleEpoch( globalVersion ) ) {
while ( shardingState.inCriticalMigrateSection() ) {
dbtemprelease r;
sleepmillis(2);
sleepmillis(20);
OCCASIONALLY log() << "waiting till out of critical section" << endl;
}
errmsg = "shard global version for collection is higher than trying to set to '" + ns + "'";
@@ -819,7 +820,7 @@ namespace mongo {
}
void ShardingConnectionHook::onHandedOut( DBClientBase * conn ) {
// no-op for mongod
void usingAShardConnection( const string& addr ) {
}
}

View File

@@ -28,10 +28,16 @@
*/
namespace mongo {
void ShardingConnectionHook::onHandedOut( DBClientBase * conn ) {
if( _shardedConnections ){
ClientInfo::get()->addShard( conn->getServerAddress() );
}
void* remapPrivateView(void *oldPrivateAddr) {
log() << "remapPrivateView called in mongos, aborting" << endl;
fassertFailed(16462);
}
/** When this callback is run, we record a shard that we've used for useful work
* in an operation to be read later by getLastError()
*/
void usingAShardConnection( const string& addr ) {
ClientInfo::get()->addShard( addr );
}
TSP_DEFINE(Client,currentClient)

View File

@@ -79,7 +79,9 @@ namespace mongo {
DBConfigPtr config = grid.getDBConfig( adminNs );
Shard s = config->getShard( adminNs );
ShardConnection conn( s, adminNs );
scoped_ptr<ScopedDbConnection> connPtr(
ScopedDbConnection::getInternalScopedDbConnection(s.getConnString(), 30.0));
ScopedDbConnection& conn = *connPtr;
BSONObj result = conn->findOne("admin.system.users", Query());
if( result.isEmpty() ) {
if( ! _warned ) {

View File

@@ -99,20 +99,15 @@ namespace mongo {
}
bool operator==( const Shard& s ) const {
bool n = _name == s._name;
bool a = _addr == s._addr;
verify( n == a ); // names and address are 1 to 1
return n;
if ( _name != s._name )
return false;
return _cs.sameLogicalEndpoint( s._cs );
}
bool operator!=( const Shard& s ) const {
bool n = _name == s._name;
bool a = _addr == s._addr;
return ! ( n && a );
return ! ( *this == s );
}
bool operator==( const string& s ) const {
return _name == s || _addr == s;
}
@@ -272,7 +267,7 @@ namespace mongo {
return _setVersion;
}
static void sync();
static void sync( const string& db );
void donotCheckVersion() {
_setVersion = false;
@@ -314,7 +309,6 @@ namespace mongo {
}
virtual void onCreate( DBClientBase * conn );
virtual void onHandedOut( DBClientBase * conn );
virtual void onDestroy( DBClientBase * conn );
bool _shardedConnections;

View File

@@ -0,0 +1,69 @@
// shard_test.cpp
/**
* Copyright (C) 2008 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/>.
*/
#include "mongo/s/shard.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
// Note: these are all crutch and hopefully will eventually go away
CmdLine cmdLine;
bool inShutdown() {
return false;
}
void setupSignals(bool inFork) {}
DBClientBase *createDirectClient() { return NULL; }
void dbexit(ExitCode rc, const char *why){
::_exit(-1);
}
bool haveLocalShardingInfo(const string& ns) {
return false;
}
// -----------------------------------
TEST( Shard, EqualityRs ) {
Shard a( "foo", "bar/a,b" );
Shard b( "foo", "bar/a,b" );
ASSERT_EQUALS( a, b );
b = Shard( "foo", "bar/b,a" );
ASSERT_EQUALS( a, b );
}
TEST( Shard, EqualitySingle ) {
ASSERT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foo", "b.foo.com:123") );
ASSERT_NOT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foo", "a.foo.com:123") );
ASSERT_NOT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foo", "b.foo.com:124") );
ASSERT_NOT_EQUALS( Shard( "foo", "b.foo.com:123"), Shard( "foa", "b.foo.com:123") );
}
TEST( Shard, EqualitySync ) {
ConnectionString cs( ConnectionString::SYNC, "a,b,c" );
ASSERT( cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "a,b,c" ) ) );
ASSERT( cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "c,b,a" ) ) );
ASSERT( cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "c,a,b" ) ) );
ASSERT( ! cs.sameLogicalEndpoint( ConnectionString( ConnectionString::SYNC, "d,a,b" ) ) );
}
}

View File

@@ -98,12 +98,12 @@ namespace mongo {
s->avail = conn;
}
void sync() {
void sync( const string& db ) {
for ( HostMap::iterator i=_hosts.begin(); i!=_hosts.end(); ++i ) {
string addr = i->first;
Status* ss = i->second;
if ( ss->avail )
ss->avail->getLastError();
ss->avail->getLastError(db);
}
}
@@ -177,10 +177,13 @@ namespace mongo {
_init();
}
void usingAShardConnection( const string& addr );
void ShardConnection::_init() {
verify( _addr.size() );
_conn = ClientConnections::threadInstance()->get( _addr , _ns );
_finishedInit = false;
usingAShardConnection( _addr );
}
void ShardConnection::_finishInit() {
@@ -218,8 +221,8 @@ namespace mongo {
}
}
void ShardConnection::sync() {
ClientConnections::threadInstance()->sync();
void ShardConnection::sync( const string& db ) {
ClientConnections::threadInstance()->sync(db);
}
bool ShardConnection::runCommand( const string& db , const BSONObj& cmd , BSONObj& res ) {

View File

@@ -57,12 +57,14 @@ namespace mongo {
return;
}
const string addr = conn.getServerAddress();
{
scoped_lock lk( _cacheLock );
if ( _seenSets.count( conn.getServerAddress() ) )
if ( _seenSets.count( addr ) ) {
return;
}
}
// we want to do writebacks on all rs nodes
string errmsg;
ConnectionString cs = ConnectionString::parse( conn.getServerAddress() , errmsg );
@@ -73,6 +75,9 @@ namespace mongo {
for ( unsigned i=0; i<hosts.size(); i++ )
init( hosts[i].toString() );
scoped_lock lk( _cacheLock );
_seenSets.insert( addr );
}
/* static */

View File

@@ -238,7 +238,7 @@ size_t copyString32to8counted( UChar8* dest8, const UChar32* source32, size_t ou
size_t outputUTF8ByteCount = 0;
if ( outputBufferSizeInBytes ) {
size_t reducedBufferSize = outputBufferSizeInBytes - 4;
while ( *source32 && charCount-- && outputUTF8ByteCount < reducedBufferSize ) {
while ( charCount-- && *source32 && outputUTF8ByteCount < reducedBufferSize ) {
UChar32 c = *source32++;
if ( c <= 0x7F ) {
*dest8++ = c;

View File

@@ -145,7 +145,7 @@ namespace mongo {
catch (po::error &e) {
cerr << "ERROR: " << e.what() << endl << endl;
printHelp(cerr);
return EXIT_BADOPTIONS;
::_exit(EXIT_BADOPTIONS);
}
// hide password from ps output
@@ -160,12 +160,12 @@ namespace mongo {
if ( _params.count( "help" ) ) {
printHelp(cout);
return 0;
::_exit(0);
}
if ( _params.count( "version" ) ) {
printVersion(cout);
return 0;
::_exit(0);
}
if ( _params.count( "verbose" ) ) {
@@ -206,13 +206,13 @@ namespace mongo {
ConnectionString cs = ConnectionString::parse( _host , errmsg );
if ( ! cs.isValid() ) {
cerr << "invalid hostname [" << _host << "] " << errmsg << endl;
return -1;
::_exit(-1);
}
_conn = cs.connect( errmsg );
if ( ! _conn ) {
cerr << "couldn't connect to [" << _host << "] " << errmsg << endl;
return -1;
::_exit(-1);
}
(_usesstdout ? cout : cerr ) << "connected to: " << _host << endl;
@@ -242,7 +242,7 @@ namespace mongo {
"path you should connect to that instead of direct data "
"file access" << endl << endl;
dbexit( EXIT_CLEAN );
return -1;
::_exit(-1);
}
FileAllocator::get()->start();
@@ -311,7 +311,7 @@ namespace mongo {
fflush(stdout);
fflush(stderr);
return ret;
::_exit(ret);
}
DBClientBase& Tool::conn( bool slaveIfPaired ) {

View File

@@ -90,10 +90,13 @@ namespace mongo {
string s = ss.str();
if ( ! rename( lp.c_str() , s.c_str() ) ) {
cout << "log file [" << lp << "] exists; copied to temporary file [" << s << "]" << endl;
cout << "log file [" << lp
<< "] exists; copied to temporary file [" << s << "]" << endl;
} else {
cout << "log file [" << lp << "] exists and couldn't make backup; run with --logappend or manually remove file (" << strerror(errno) << ")" << endl;
cout << "log file [" << lp
<< "] exists and couldn't make backup [" << s
<< "]; run with --logappend or manually remove file: "
<< errnoWithDescription() << endl;
return false;
}
}
@@ -137,7 +140,9 @@ namespace mongo {
ss << _path << "." << terseCurrentTime( false );
string s = ss.str();
if (0 != rename(_path.c_str(), s.c_str())) {
error() << "Failed to rename " << _path << " to " << s;
error() << "Failed to rename '" << _path
<< "' to '" << s
<< "': " << errnoWithDescription() << endl;
return false;
}
}

View File

@@ -17,9 +17,10 @@
#pragma once
#include "sock.h"
#include "../../db/cmdline.h"
#include "../mongoutils/str.h"
#include "mongo/db/cmdline.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/net/sock.h"
#include "mongo/bson/util/builder.h"
namespace mongo {
@@ -72,6 +73,8 @@ namespace mongo {
operator string() const { return toString(); }
void append( StringBuilder& ss ) const;
bool empty() const {
return _host.empty() && _port < 0;
}
@@ -122,14 +125,19 @@ namespace mongo {
}
inline string HostAndPort::toString( bool includePort ) const {
string h = host();
if ( ! includePort )
return host();
StringBuilder ss;
append( ss );
return ss.str();
}
inline void HostAndPort::append( StringBuilder& ss ) const {
ss << host();
int p = port();
if ( ! includePort )
return h;
stringstream ss;
ss << h;
if ( p != -1 ) {
ss << ':';
#if defined(_DEBUG)
@@ -143,9 +151,10 @@ namespace mongo {
ss << p;
#endif
}
return ss.str();
}
inline bool HostAndPort::isLocalHost() const {
string _host = host();
return ( _host == "localhost"

View File

@@ -502,6 +502,17 @@ namespace mongo {
_timeout = timeout;
_init();
}
Socket::~Socket() {
close();
#ifdef MONGO_SSL
if ( _ssl ) {
SSL_shutdown( _ssl );
SSL_free( _ssl );
_ssl = 0;
}
#endif
}
void Socket::_init() {
_bytesOut = 0;
@@ -513,13 +524,6 @@ namespace mongo {
}
void Socket::close() {
#ifdef MONGO_SSL
if ( _ssl ) {
SSL_shutdown( _ssl );
SSL_free( _ssl );
_ssl = 0;
}
#endif
if ( _fd >= 0 ) {
closesocket( _fd );
_fd = -1;

View File

@@ -204,9 +204,7 @@ namespace mongo {
*/
Socket(double so_timeout = 0, int logLevel = 0 );
~Socket() {
close();
}
~Socket();
bool connect(SockAddr& farEnd);
void close();

View File

@@ -11,6 +11,7 @@
#ifdef _WIN32
#include <sstream>
#include <stdio.h>
#include <boost/filesystem/operations.hpp>
#include <boost/smart_ptr/scoped_array.hpp>
#include "mongo/platform/windows_basic.h"
#include <DbgHelp.h>
@@ -53,6 +54,26 @@ namespace mongo {
namespace mongo {
/**
* Get the path string to be used when searching for PDB files.
*
* @param process Process handle
* @return searchPath Returned search path string
*/
static const char* getSymbolSearchPath(HANDLE process) {
static std::string symbolSearchPath;
if (symbolSearchPath.empty()) {
static const size_t bufferSize = 1024;
boost::scoped_array<char> pathBuffer(new char[bufferSize]);
GetModuleFileNameA(NULL, pathBuffer.get(), bufferSize);
boost::filesystem::path exePath(pathBuffer.get());
symbolSearchPath = exePath.parent_path().string();
symbolSearchPath += ";C:\\Windows\\System32;C:\\Windows";
}
return symbolSearchPath.c_str();
}
/**
* Get the display name of the executable module containing the specified address.
*
@@ -172,7 +193,7 @@ namespace mongo {
void printWindowsStackTrace( CONTEXT& context, std::ostream& os ) {
SimpleMutex::scoped_lock lk(_stackTraceMutex);
HANDLE process = GetCurrentProcess();
BOOL ret = SymInitialize( process, NULL, TRUE );
BOOL ret = SymInitialize(process, getSymbolSearchPath(process), TRUE);
if ( ret == FALSE ) {
DWORD dosError = GetLastError();
log() << "Stack trace failed, SymInitialize failed with error " <<

View File

@@ -43,7 +43,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.2.2-rc0";
const char versionString[] = "2.2.3";
// See unit test for example outputs
static BSONArray _versionArray(const char* version){