/** * Copyright (C) 2012-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. */ #pragma once #include "mongo/base/status_with.h" #include "mongo/db/jsobj.h" namespace mongo { /** * ChunkVersions consist of a major/minor version scoped to a version epoch * * Version configurations (format: major version, epoch): * * 1. (0, 0) - collection is dropped. * 2. (0, n), n > 0 - applicable only to shardVersion; shard has no chunk. * 3. (n, 0), n > 0 - invalid configuration. * 4. (n, m), n > 0, m > 0 - normal sharded collection version. * * TODO: This is a "manual type" but, even so, still needs to comform to what's * expected from types. */ struct ChunkVersion { public: /** * The name for the shard version information field, which shard-aware commands should include * if they want to convey shard version. */ static constexpr StringData kShardVersionField = "shardVersion"_sd; ChunkVersion() : _combined(0), _epoch(OID()) {} ChunkVersion(int major, int minor, const OID& epoch) : _combined(static_cast(minor) | (static_cast(major) << 32)), _epoch(epoch) {} static StatusWith parseFromCommand(const BSONObj& obj) { return parseWithField(obj, kShardVersionField); } /** * Parses the BSON formatted by appendWithField. If the field is missing, returns 'NoSuchKey', * otherwise if the field is not properly formatted can return any relevant parsing error * (BadValue, TypeMismatch, etc). */ static StatusWith parseWithField(const BSONObj& obj, StringData field); /** * NOTE: This format is being phased out. Use parseWithField instead. * * Parses the BSON formatted by appendLegacyWithField. If the field is missing, returns * 'NoSuchKey', otherwise if the field is not properly formatted can return any relevant parsing * error (BadValue, TypeMismatch, etc). */ static StatusWith parseLegacyWithField(const BSONObj& obj, StringData field); /** * Indicates a dropped collection. All components are zeroes (OID is zero time, zero * machineId/inc). */ static ChunkVersion DROPPED() { return ChunkVersion(0, 0, OID()); } /** * Indicates that the collection is not sharded. Same as DROPPED. */ static ChunkVersion UNSHARDED() { return ChunkVersion(0, 0, OID()); } /** * Indicates that the shard version checking must be skipped. */ static ChunkVersion IGNORED() { ChunkVersion version = ChunkVersion(); version._epoch.init(Date_t(), true); // ignored OID is zero time, max machineId/inc return version; } static bool isIgnoredVersion(const ChunkVersion& version) { return version.majorVersion() == 0 && version.minorVersion() == 0 && version.epoch() == IGNORED().epoch(); } void incMajor() { _combined = static_cast(majorVersion() + 1) << 32; } void incMinor() { _combined++; } // Note: this shouldn't be used as a substitute for version except in specific cases - // epochs make versions more complex unsigned long long toLong() const { return _combined; } bool isSet() const { return _combined > 0; } int majorVersion() const { return _combined >> 32; } int minorVersion() const { return _combined & 0xFFFF; } const OID& epoch() const { return _epoch; } // // Explicit comparison operators - versions with epochs have non-trivial comparisons. // > < operators do not check epoch cases. Generally if using == we need to handle // more complex cases. // bool operator==(const ChunkVersion& otherVersion) const { return otherVersion.epoch() == epoch() && otherVersion._combined == _combined; } bool operator!=(const ChunkVersion& otherVersion) const { return !(otherVersion == *this); } bool operator>(const ChunkVersion& otherVersion) const { return this->_combined > otherVersion._combined; } bool operator>=(const ChunkVersion& otherVersion) const { return this->_combined >= otherVersion._combined; } bool operator<(const ChunkVersion& otherVersion) const { return this->_combined < otherVersion._combined; } // Can we write to this data and not have a problem? bool isWriteCompatibleWith(const ChunkVersion& other) const { return epoch() == other.epoch() && majorVersion() == other.majorVersion(); } /** * Returns true if this version is (strictly) in the same epoch as the other version and * this version is older. Returns false if we're not sure because the epochs are different * or if this version is newer. */ bool isOlderThan(const ChunkVersion& otherVersion) const { if (otherVersion._epoch != _epoch) return false; if (majorVersion() != otherVersion.majorVersion()) return majorVersion() < otherVersion.majorVersion(); return minorVersion() < otherVersion.minorVersion(); } void appendToCommand(BSONObjBuilder* out) const { appendWithField(out, kShardVersionField); } /** * Serializes the version held by this object to 'out' in the form: * { ..., : [ , ], ... }. */ void appendWithField(BSONObjBuilder* out, StringData field) const; /** * NOTE: This format is being phased out. Use appendWithField instead. * * Serializes the version held by this object to 'out' in the legacy form: * { ..., : [ ], Epoch: [ ], ... } */ void appendLegacyWithField(BSONObjBuilder* out, StringData field) const; BSONObj toBSON() const; std::string toString() const; private: uint64_t _combined; OID _epoch; }; inline std::ostream& operator<<(std::ostream& s, const ChunkVersion& v) { s << v.toString(); return s; } } // namespace mongo