/**
* Copyright (C) 2015 MongoDB 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 .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kSharding
#include "mongo/platform/basic.h"
#include "mongo/s/chunk_diff.h"
#include "mongo/s/catalog/type_chunk.h"
#include "mongo/s/chunk_version.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
namespace mongo {
template
ConfigDiffTracker::ConfigDiffTracker() {
_ns.clear();
_currMap = NULL;
_maxVersion = NULL;
_maxShardVersions = NULL;
_validDiffs = 0;
}
template
ConfigDiffTracker::~ConfigDiffTracker() = default;
template
void ConfigDiffTracker::attach(const std::string& ns,
RangeMap& currMap,
ChunkVersion& maxVersion,
MaxChunkVersionMap& maxShardVersions) {
_ns = ns;
_currMap = &currMap;
_maxVersion = &maxVersion;
_maxShardVersions = &maxShardVersions;
_validDiffs = 0;
}
template
bool ConfigDiffTracker::_isOverlapping(const BSONObj& min, const BSONObj& max) {
RangeOverlap overlap = _overlappingRange(min, max);
return overlap.first != overlap.second;
}
template
typename ConfigDiffTracker::RangeOverlap ConfigDiffTracker::_overlappingRange(
const BSONObj& min, const BSONObj& max) {
_assertAttached();
typename RangeMap::iterator low;
typename RangeMap::iterator high;
if (isMinKeyIndexed()) {
// Returns the first chunk with a min key that is >= min - implies the
// previous chunk cannot overlap min
low = _currMap->lower_bound(min);
// Returns the first chunk with a min key that is >= max - implies the
// chunk does not overlap max
high = _currMap->lower_bound(max);
} else {
// Returns the first chunk with a max key that is > min - implies that
// the chunk overlaps min
low = _currMap->upper_bound(min);
// Returns the first chunk with a max key that is > max - implies that
// the next chunk cannot not overlap max
high = _currMap->upper_bound(max);
}
return RangeOverlap(low, high);
}
template
int ConfigDiffTracker::calculateConfigDiff(OperationContext* txn,
const std::vector& chunks) {
_assertAttached();
// Apply the chunk changes to the ranges and versions
//
// Overall idea here is to work in two steps :
// 1. For all the new chunks we find, increment the maximum version per-shard and
// per-collection, and remove any conflicting chunks from the ranges.
// 2. For all the new chunks we're interested in (all of them for mongos, just chunks on
// the shard for mongod) add them to the ranges.
std::vector newTracked;
// Store epoch now so it doesn't change when we change max
OID currEpoch = _maxVersion->epoch();
_validDiffs = 0;
for (const ChunkType& chunk : chunks) {
ChunkVersion chunkVersion =
ChunkVersion::fromBSON(chunk.toBSON(), ChunkType::DEPRECATED_lastmod());
if (!chunkVersion.isSet() || !chunkVersion.hasEqualEpoch(currEpoch)) {
warning() << "got invalid chunk version " << chunkVersion << " in document "
<< chunk.toString() << " when trying to load differing chunks at version "
<< ChunkVersion(
_maxVersion->majorVersion(), _maxVersion->minorVersion(), currEpoch);
// Don't keep loading, since we know we'll be broken here
return -1;
}
_validDiffs++;
// Get max changed version and chunk version
if (chunkVersion > *_maxVersion) {
*_maxVersion = chunkVersion;
}
// Chunk version changes
ShardId shard = shardFor(txn, chunk.getShard());
typename MaxChunkVersionMap::const_iterator shardVersionIt = _maxShardVersions->find(shard);
if (shardVersionIt == _maxShardVersions->end() || shardVersionIt->second < chunkVersion) {
(*_maxShardVersions)[shard] = chunkVersion;
}
// See if we need to remove any chunks we are currently tracking because of this chunk's
// changes
{
RangeOverlap overlap = _overlappingRange(chunk.getMin(), chunk.getMax());
_currMap->erase(overlap.first, overlap.second);
}
// Figure out which of the new chunks we need to track
// Important - we need to actually own this doc, in case the cursor decides to getMore
// or unbuffer.
if (isTracked(chunk)) {
newTracked.push_back(chunk);
}
}
LOG(3) << "found " << _validDiffs << " new chunks for collection " << _ns << " (tracking "
<< newTracked.size() << "), new version is " << *_maxVersion;
for (const ChunkType& chunk : newTracked) {
// Invariant enforced by sharding - it's possible to read inconsistent state due to
// getMore and yielding, so we want to detect it as early as possible.
//
// TODO: This checks for overlap, we also should check for holes here iff we're
// tracking all chunks.
if (_isOverlapping(chunk.getMin(), chunk.getMax())) {
return -1;
}
_currMap->insert(rangeFor(txn, chunk));
}
return _validDiffs;
}
template
typename ConfigDiffTracker::QueryAndSort ConfigDiffTracker::configDiffQuery()
const {
_assertAttached();
// The query has to find all the chunks $gte the current max version. Currently, any splits and
// merges will increment the current max version.
BSONObjBuilder queryB;
queryB.append(ChunkType::ns(), _ns);
{
BSONObjBuilder tsBuilder(queryB.subobjStart(ChunkType::DEPRECATED_lastmod()));
tsBuilder.appendTimestamp("$gte", _maxVersion->toLong());
tsBuilder.done();
}
// NOTE: IT IS IMPORTANT FOR CONSISTENCY THAT WE SORT BY ASC VERSION, IN ORDER TO HANDLE CURSOR
// YIELDING BETWEEN CHUNKS BEING MIGRATED.
//
// This ensures that changes to chunk version (which will always be higher) will always come
// *after* our current position in the chunk cursor.
QueryAndSort queryObj(queryB.obj(), BSON(ChunkType::DEPRECATED_lastmod() << 1));
LOG(2) << "major version query from " << *_maxVersion << " and over "
<< _maxShardVersions->size() << " shards is " << queryObj;
return queryObj;
}
template
void ConfigDiffTracker::_assertAttached() const {
invariant(_currMap);
invariant(_maxVersion);
invariant(_maxShardVersions);
}
std::string ConfigDiffTrackerBase::QueryAndSort::toString() const {
return str::stream() << "query: " << query << ", sort: " << sort;
}
// Ensures that these instances of the template are compiled
class Chunk;
template class ConfigDiffTracker;
template class ConfigDiffTracker>;
} // namespace mongo