2
* Copyright (C) 2012 10gen Inc.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU Affero General Public License, version 3,
6
* as published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Affero General Public License for more details.
13
* You should have received a copy of the GNU Affero General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
#include "mongo/s/collection_manager.h"
19
#include "mongo/bson/util/builder.h" // for StringBuilder
20
#include "mongo/util/mongoutils/str.h"
24
using mongoutils::str::stream;
26
CollectionManager::CollectionManager() { }
28
CollectionManager::~CollectionManager() { }
30
CollectionManager* CollectionManager::cloneMinus(const ChunkType& chunk,
31
const ChunkVersion& newShardVersion,
32
string* errMsg) const {
33
// The error message string is optional.
39
// Check that we have the exact chunk that will be subtracted.
40
if (!chunkExists(chunk, errMsg)) {
41
// Message was filled in by chunkExists().
45
// If left with no chunks, check that the version is zero.
46
if (_chunksMap.size() == 1) {
47
if (newShardVersion.isSet()) {
48
*errMsg = stream() << "setting version to " << newShardVersion.toString()
49
<< " on removing last chunk";
53
// Can't move version backwards when subtracting chunks. This is what guarantees that
54
// no read or write would be taken once we subtract data from the current shard.
55
else if (newShardVersion <= _maxShardVersion) {
56
*errMsg = stream() << "version " << newShardVersion.toString()
57
<< " not greater than " << _maxShardVersion.toString();
61
auto_ptr<CollectionManager> manager(new CollectionManager);
62
manager->_key = this->_key;
63
manager->_key.getOwned();
64
manager->_chunksMap = this->_chunksMap;
65
manager->_chunksMap.erase(chunk.getMin());
66
manager->_maxShardVersion = newShardVersion;
67
manager->_maxCollVersion = newShardVersion > _maxCollVersion ?
68
newShardVersion : this->_maxCollVersion;
69
manager->fillRanges();
71
dassert(manager->isValid());
73
return manager.release();
76
static bool overlap(const ChunkType& chunk,
79
return ! ((chunk.getMax().woCompare(l2) <= 0) ||
80
(h2.woCompare(chunk.getMin()) <= 0));
83
CollectionManager* CollectionManager::clonePlus(const ChunkType& chunk,
84
const ChunkVersion& newShardVersion,
85
string* errMsg) const {
86
// The error message string is optional.
92
// It is acceptable to move version backwards (e.g., undoing a migration that went bad
93
// during commit) but only cloning away the last chunk may reset the version to 0.
94
if (!newShardVersion.isSet()) {
95
*errMsg = "version can't be set to zero";
99
// Check that there isn't any chunk on the interval to be added.
100
if (!_chunksMap.empty()) {
101
RangeMap::const_iterator it = _chunksMap.lower_bound(chunk.getMax());
102
if (it != _chunksMap.begin()) {
105
if (overlap(chunk, it->first, it->second)) {
106
*errMsg = stream() << "ranges overlap, "
107
<< "requested: " << chunk.getMin()
108
<<" -> " << chunk.getMax() << " "
109
<< "existing: " << it->first.toString()
110
<< " -> " + it->second.toString();
115
auto_ptr<CollectionManager> manager(new CollectionManager);
116
manager->_key = this->_key;
117
manager->_key.getOwned();
118
manager->_chunksMap = this->_chunksMap;
119
manager->_chunksMap.insert(make_pair(chunk.getMin().getOwned(), chunk.getMax().getOwned()));
120
manager->_maxShardVersion = newShardVersion;
121
manager->_maxCollVersion = newShardVersion > _maxCollVersion ?
122
newShardVersion : this->_maxCollVersion;
123
manager->fillRanges();
125
dassert(manager->isValid());
127
return manager.release();
130
static bool contains(const BSONObj& min, const BSONObj& max, const BSONObj& point) {
131
return point.woCompare(min) >= 0 && point.woCompare(max) < 0;
134
CollectionManager* CollectionManager::cloneSplit(const ChunkType& chunk,
135
const vector<BSONObj>& splitKeys,
136
const ChunkVersion& newShardVersion,
137
string* errMsg) const {
138
// The error message string is optional.
140
if (errMsg == NULL) {
144
// The version required in both resulting chunks could be simply an increment in the
145
// minor portion of the current version. However, we are enforcing uniqueness over the
146
// attributes <ns, version> of the configdb collection 'chunks'. So in practice, a
147
// migrate somewhere may force this split to pick up a version that has the major
148
// portion higher than the one that this shard has been using.
150
// TODO drop the uniqueness constraint and tighten the check below so that only the
151
// minor portion of version changes
152
if (newShardVersion <= _maxShardVersion) {
153
*errMsg = stream()<< "version " << newShardVersion.toString()
154
<< " not greater than " << _maxShardVersion.toString();
158
// Check that we have the exact chunk that will be split and that the split point is
160
if (!chunkExists(chunk, errMsg)) {
164
for (vector<BSONObj>::const_iterator it = splitKeys.begin();
165
it != splitKeys.end();
167
if (!contains(chunk.getMin(), chunk.getMax(), *it)) {
168
*errMsg = stream() << "can split " << chunk.getMin()
169
<< " -> " << chunk.getMax() << " on " << *it;
174
auto_ptr<CollectionManager> manager(new CollectionManager);
175
manager->_key = this->_key;
176
manager->_key.getOwned();
177
manager->_chunksMap = this->_chunksMap;
178
manager->_maxShardVersion = newShardVersion; // will increment 2nd, 3rd,... chunks below
180
BSONObj startKey = chunk.getMin();
181
for (vector<BSONObj>::const_iterator it = splitKeys.begin();
182
it != splitKeys.end();
185
manager->_chunksMap[chunk.getMin()] = split.getOwned();
186
manager->_chunksMap.insert(make_pair(split.getOwned(), chunk.getMax().getOwned()));
187
manager->_maxShardVersion.incMinor();
191
manager->_maxCollVersion = manager->_maxShardVersion > _maxCollVersion ?
192
manager->_maxShardVersion : this->_maxCollVersion;
193
manager->fillRanges();
195
dassert(manager->isValid());
196
return manager.release();
199
bool CollectionManager::belongsToMe(const BSONObj& point) const {
200
// For now, collections don't move. So if the collection is not sharded, assume
201
// the documet ca be accessed.
202
if (_key.isEmpty()) {
206
if (_rangesMap.size() <= 0) {
210
RangeMap::const_iterator it = _rangesMap.upper_bound(point);
211
if (it != _rangesMap.begin())
214
bool good = contains(it->first, it->second, point);
216
// Logs if in debugging mode and the point doesn't belong here.
217
if(dcompare(!good)) {
218
log() << "bad: " << point << " "
219
<< it->first << " " << point.woCompare(it->first) << " "
220
<< point.woCompare(it->second) << endl;
222
for (RangeMap::const_iterator i=_rangesMap.begin(); i!=_rangesMap.end(); ++i) {
223
log() << "\t" << i->first << "\t" << i->second << "\t" << endl;
230
bool CollectionManager::getNextChunk(const BSONObj& lookupKey,
231
ChunkType* chunk) const {
232
if (_chunksMap.empty()) {
236
RangeMap::const_iterator it;
237
if (lookupKey.isEmpty()) {
238
it = _chunksMap.begin();
239
chunk->setMin(it->first);
240
chunk->setMax(it->second);
241
return _chunksMap.size() == 1;
244
it = _chunksMap.upper_bound(lookupKey);
245
if (it != _chunksMap.end()) {
246
chunk->setMin(it->first);
247
chunk->setMax(it->second);
254
string CollectionManager::toString() const {
256
ss << " CollectionManager version: " << _maxShardVersion.toString() << " key: " << _key;
257
if (_rangesMap.empty()) {
261
RangeMap::const_iterator it = _rangesMap.begin();
262
ss << it->first << " -> " << it->second;
263
while (it != _rangesMap.end()) {
264
ss << ", "<< it->first << " -> " << it->second;
269
bool CollectionManager::isValid() const {
270
if (_maxShardVersion > _maxCollVersion) {
274
if (_maxCollVersion.majorVersion() == 0)
280
bool CollectionManager::chunkExists(const ChunkType& chunk,
281
string* errMsg) const {
282
RangeMap::const_iterator it = _chunksMap.find(chunk.getMin());
283
if (it == _chunksMap.end()) {
284
*errMsg = stream() << "couldn't find chunk " << chunk.getMin()
285
<< "->" << chunk.getMax();
289
if (it->second.woCompare(chunk.getMax()) != 0) {
290
*errMsg = stream() << "ranges differ, "
291
<< "requested: " << chunk.getMin()
292
<< " -> " << chunk.getMax() << " "
294
<< ((it == _chunksMap.end()) ?
296
it->first.toString() + " -> " + it->second.toString());
303
void CollectionManager::fillRanges() {
304
if (_chunksMap.empty())
307
// Load the chunk information, coallesceing their ranges. The version for this shard
308
// would be the highest version for any of the chunks.
309
RangeMap::const_iterator it = _chunksMap.begin();
311
while (it != _chunksMap.end()) {
312
BSONObj currMin = it->first;
313
BSONObj currMax = it->second;
316
// coalesce the chunk's bounds in ranges if they are adjacent chunks
322
if (max == currMin) {
327
_rangesMap.insert(make_pair(min, max));
332
dassert(!min.isEmpty());
334
_rangesMap.insert(make_pair(min, max));