~ubuntu-branches/ubuntu/utopic/mongodb/utopic

« back to all changes in this revision

Viewing changes to src/mongo/s/collection_manager.cpp

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2014-07-03 09:23:46 UTC
  • mfrom: (1.3.10) (44.1.14 sid)
  • Revision ID: package-import@ubuntu.com-20140703092346-c5bvt46wnzougyly
Tags: 1:2.6.3-0ubuntu1
* New upstream stable release:
  - Dropped patches, included upstream:
    + 0003-All-platforms-but-Windows-find-hash-in-std-tr1.patch
    + 0008-Use-system-libstemmer.patch
    + 0011-Use-a-signed-char-to-store-BSONType-enumerations.patch
    + 0001-SERVER-12064-Atomic-operations-for-gcc-non-Intel-arc.patch
    + 0002-SERVER-12065-Support-ARM-and-AArch64-builds.patch
  - d/p/*: Refreshed/rebased remaining patches.
  - Use system provided libyaml-cpp:
    + d/control: Add libyaml-cpp-dev to BD's.
    + d/rules: Enable --with-system-yaml option.
    + d/p/fix-yaml-detection.patch: Fix detection of libyaml-cpp library.
  - d/mongodb-server.mongodb.upstart: Sync changes from upstream.
  - d/control,mongodb-dev.*: Drop mongodb-dev package; it has no reverse
    dependencies and upstream no longer install header files.
  - d/NEWS: Point users to upstream upgrade documentation for upgrades
    from 2.4 to 2.6.
* Merge from Debian unstable.
* d/control: BD on libv8-3.14-dev to ensure that transitioning to new v8
  versions is a explicit action due to changes in behaviour in >= 3.25
  (LP: #1295723).
* d/mongodb-server.prerm: Dropped debug echo call from maintainer script
  (LP: #1294455).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 *    Copyright (C) 2012 10gen Inc.
3
 
 *
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.
7
 
 *
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.
12
 
 *
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/>.
15
 
 */
16
 
 
17
 
#include "mongo/s/collection_manager.h"
18
 
 
19
 
#include "mongo/bson/util/builder.h" // for StringBuilder
20
 
#include "mongo/util/mongoutils/str.h"
21
 
 
22
 
namespace mongo {
23
 
 
24
 
    using mongoutils::str::stream;
25
 
 
26
 
    CollectionManager::CollectionManager() { }
27
 
 
28
 
    CollectionManager::~CollectionManager() { }
29
 
 
30
 
    CollectionManager* CollectionManager::cloneMinus(const ChunkType& chunk,
31
 
                                                     const ChunkVersion& newShardVersion,
32
 
                                                     string* errMsg) const {
33
 
        // The error message string is optional.
34
 
        string dummy;
35
 
        if (errMsg == NULL) {
36
 
            errMsg = &dummy;
37
 
        }
38
 
 
39
 
        // Check that we have the exact chunk that will be subtracted.
40
 
        if (!chunkExists(chunk, errMsg)) {
41
 
            // Message was filled in by chunkExists().
42
 
            return NULL;
43
 
        }
44
 
 
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";
50
 
                return NULL;
51
 
            }
52
 
        }
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();
58
 
            return NULL;
59
 
        }
60
 
 
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();
70
 
 
71
 
        dassert(manager->isValid());
72
 
 
73
 
        return manager.release();
74
 
    }
75
 
 
76
 
    static bool overlap(const ChunkType& chunk,
77
 
                        const BSONObj& l2,
78
 
                        const BSONObj& h2) {
79
 
        return ! ((chunk.getMax().woCompare(l2) <= 0) ||
80
 
                  (h2.woCompare(chunk.getMin()) <= 0));
81
 
    }
82
 
 
83
 
    CollectionManager* CollectionManager::clonePlus(const ChunkType& chunk,
84
 
                                                    const ChunkVersion& newShardVersion,
85
 
                                                    string* errMsg) const {
86
 
        // The error message string is optional.
87
 
        string dummy;
88
 
        if (errMsg == NULL) {
89
 
            errMsg = &dummy;
90
 
        }
91
 
 
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";
96
 
            return NULL;
97
 
        }
98
 
 
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()) {
103
 
                --it;
104
 
            }
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();
111
 
                return NULL;
112
 
            }
113
 
        }
114
 
 
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();
124
 
 
125
 
        dassert(manager->isValid());
126
 
 
127
 
        return manager.release();
128
 
    }
129
 
 
130
 
    static bool contains(const BSONObj& min, const BSONObj& max, const BSONObj& point) {
131
 
        return point.woCompare(min) >= 0 && point.woCompare(max) < 0;
132
 
    }
133
 
 
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.
139
 
        string dummy;
140
 
        if (errMsg == NULL) {
141
 
            errMsg = &dummy;
142
 
        }
143
 
 
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.
149
 
        //
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();
155
 
            return NULL;
156
 
        }
157
 
 
158
 
        // Check that we have the exact chunk that will be split and that the split point is
159
 
        // valid.
160
 
        if (!chunkExists(chunk, errMsg)) {
161
 
            return NULL;
162
 
        }
163
 
 
164
 
        for (vector<BSONObj>::const_iterator it = splitKeys.begin();
165
 
             it != splitKeys.end();
166
 
             ++it) {
167
 
            if (!contains(chunk.getMin(), chunk.getMax(), *it)) {
168
 
                *errMsg = stream() << "can split " << chunk.getMin()
169
 
                                   << " -> " << chunk.getMax() << " on " << *it;
170
 
                return NULL;
171
 
            }
172
 
        }
173
 
 
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
179
 
 
180
 
        BSONObj startKey = chunk.getMin();
181
 
        for (vector<BSONObj>::const_iterator it = splitKeys.begin();
182
 
             it != splitKeys.end();
183
 
             ++it) {
184
 
            BSONObj split = *it;
185
 
            manager->_chunksMap[chunk.getMin()] = split.getOwned();
186
 
            manager->_chunksMap.insert(make_pair(split.getOwned(), chunk.getMax().getOwned()));
187
 
            manager->_maxShardVersion.incMinor();
188
 
            startKey = split;
189
 
        }
190
 
 
191
 
        manager->_maxCollVersion = manager->_maxShardVersion > _maxCollVersion ?
192
 
                        manager->_maxShardVersion : this->_maxCollVersion;
193
 
        manager->fillRanges();
194
 
 
195
 
        dassert(manager->isValid());
196
 
        return manager.release();
197
 
    }
198
 
 
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()) {
203
 
            return true;
204
 
        }
205
 
 
206
 
        if (_rangesMap.size() <= 0) {
207
 
            return false;
208
 
        }
209
 
 
210
 
        RangeMap::const_iterator it = _rangesMap.upper_bound(point);
211
 
        if (it != _rangesMap.begin())
212
 
            it--;
213
 
 
214
 
        bool good = contains(it->first, it->second, point);
215
 
 
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;
221
 
 
222
 
            for (RangeMap::const_iterator i=_rangesMap.begin(); i!=_rangesMap.end(); ++i) {
223
 
                log() << "\t" << i->first << "\t" << i->second << "\t" << endl;
224
 
            }
225
 
        }
226
 
 
227
 
        return good;
228
 
    }
229
 
 
230
 
    bool CollectionManager::getNextChunk(const BSONObj& lookupKey,
231
 
                                         ChunkType* chunk) const {
232
 
        if (_chunksMap.empty()) {
233
 
            return true;
234
 
        }
235
 
 
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;
242
 
        }
243
 
 
244
 
        it = _chunksMap.upper_bound(lookupKey);
245
 
        if (it != _chunksMap.end()) {
246
 
            chunk->setMin(it->first);
247
 
            chunk->setMax(it->second);
248
 
            return false;
249
 
        }
250
 
 
251
 
        return true;
252
 
    }
253
 
 
254
 
    string CollectionManager::toString() const {
255
 
        StringBuilder ss;
256
 
        ss << " CollectionManager version: " << _maxShardVersion.toString() << " key: " << _key;
257
 
        if (_rangesMap.empty()) {
258
 
            return ss.str();
259
 
        }
260
 
 
261
 
        RangeMap::const_iterator it = _rangesMap.begin();
262
 
        ss << it->first << " -> " << it->second;
263
 
        while (it != _rangesMap.end()) {
264
 
            ss << ", "<< it->first << " -> " << it->second;
265
 
        }
266
 
        return ss.str();
267
 
    }
268
 
 
269
 
    bool CollectionManager::isValid() const {
270
 
        if (_maxShardVersion > _maxCollVersion) {
271
 
            return false;
272
 
        }
273
 
 
274
 
        if (_maxCollVersion.majorVersion() == 0)
275
 
            return false;
276
 
 
277
 
        return true;
278
 
    }
279
 
 
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();
286
 
            return false;
287
 
        }
288
 
 
289
 
        if (it->second.woCompare(chunk.getMax()) != 0) {
290
 
            *errMsg = stream() << "ranges differ, "
291
 
                               << "requested: "  << chunk.getMin()
292
 
                               << " -> " << chunk.getMax() << " "
293
 
                               << "existing: "
294
 
                               << ((it == _chunksMap.end()) ?
295
 
                                   "<empty>" :
296
 
                                   it->first.toString() + " -> " + it->second.toString());
297
 
            return false;
298
 
        }
299
 
 
300
 
        return true;
301
 
    }
302
 
 
303
 
    void CollectionManager::fillRanges() {
304
 
        if (_chunksMap.empty())
305
 
            return;
306
 
 
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();
310
 
        BSONObj min,max;
311
 
        while (it != _chunksMap.end()) {
312
 
            BSONObj currMin = it->first;
313
 
            BSONObj currMax = it->second;
314
 
            ++it;
315
 
 
316
 
            // coalesce the chunk's bounds in ranges if they are adjacent chunks
317
 
            if (min.isEmpty()) {
318
 
                min = currMin;
319
 
                max = currMax;
320
 
                continue;
321
 
            }
322
 
            if (max == currMin) {
323
 
                max = currMax;
324
 
                continue;
325
 
            }
326
 
 
327
 
            _rangesMap.insert(make_pair(min, max));
328
 
 
329
 
            min = currMin;
330
 
            max = currMax;
331
 
        }
332
 
        dassert(!min.isEmpty());
333
 
 
334
 
        _rangesMap.insert(make_pair(min, max));
335
 
    }
336
 
 
337
 
} // namespace mongo