diff options
Diffstat (limited to 'src/mongo/s/chunk_version.h')
-rw-r--r-- | src/mongo/s/chunk_version.h | 677 |
1 files changed, 336 insertions, 341 deletions
diff --git a/src/mongo/s/chunk_version.h b/src/mongo/s/chunk_version.h index 41b9e03bae2..fef1a581909 100644 --- a/src/mongo/s/chunk_version.h +++ b/src/mongo/s/chunk_version.h @@ -32,428 +32,423 @@ 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 { - - union { - struct { - int _minor; - int _major; - }; - unsigned long long _combined; +/** + * 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 { + union { + struct { + int _minor; + int _major; }; - OID _epoch; - - ChunkVersion() : _minor(0), _major(0), _epoch(OID()) {} - - // - // Constructors shouldn't have default parameters here, since it's vital we track from - // here on the epochs of versions, even if not used. - // + unsigned long long _combined; + }; + OID _epoch; - ChunkVersion( int major, int minor, const OID& epoch ) - : _minor(minor),_major(major), _epoch(epoch) { - } + ChunkVersion() : _minor(0), _major(0), _epoch(OID()) {} - static ChunkVersion DROPPED() { - return ChunkVersion( 0, 0, OID() ); // dropped OID is zero time, zero machineId/inc - } + // + // Constructors shouldn't have default parameters here, since it's vital we track from + // here on the epochs of versions, even if not used. + // - static ChunkVersion UNSHARDED() { - // TODO: Distinguish between these cases - return DROPPED(); - } + ChunkVersion(int major, int minor, const OID& epoch) + : _minor(minor), _major(major), _epoch(epoch) {} - static ChunkVersion IGNORED() { - ChunkVersion version = ChunkVersion(); - version._epoch.init(Date_t(), true); // ignored OID is zero time, max machineId/inc - return version; - } + static ChunkVersion DROPPED() { + return ChunkVersion(0, 0, OID()); // dropped OID is zero time, zero machineId/inc + } - static ChunkVersion fromDeprecatedLong(unsigned long long num, const OID& epoch) { - ChunkVersion version(0, 0, epoch); - version._combined = num; - return version; - } + static ChunkVersion UNSHARDED() { + // TODO: Distinguish between these cases + return DROPPED(); + } - static bool isIgnoredVersion( const ChunkVersion& version ) { - return version.majorVersion() == 0 && version.minorVersion() == 0 - && version.epoch() == IGNORED().epoch(); - } + static ChunkVersion IGNORED() { + ChunkVersion version = ChunkVersion(); + version._epoch.init(Date_t(), true); // ignored OID is zero time, max machineId/inc + return version; + } - void incMajor() { - _major++; - _minor = 0; - } + static ChunkVersion fromDeprecatedLong(unsigned long long num, const OID& epoch) { + ChunkVersion version(0, 0, epoch); + version._combined = num; + return version; + } - void incMinor() { - _minor++; - } + static bool isIgnoredVersion(const ChunkVersion& version) { + return version.majorVersion() == 0 && version.minorVersion() == 0 && + version.epoch() == IGNORED().epoch(); + } - // Incrementing an epoch creates a new, randomly generated identifier - void incEpoch() { - _epoch = OID::gen(); - _major = 0; - _minor = 0; - } + void incMajor() { + _major++; + _minor = 0; + } - // 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; - } + void incMinor() { + _minor++; + } - bool isSet() const { - return _combined > 0; - } + // Incrementing an epoch creates a new, randomly generated identifier + void incEpoch() { + _epoch = OID::gen(); + _major = 0; + _minor = 0; + } - bool isEpochSet() const { - return _epoch.isSet(); - } + // 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; + } - std::string toString() const { - std::stringstream ss; - // Similar to month/day/year. For the most part when debugging, we care about major - // so it's first - ss << _major << "|" << _minor << "||" << _epoch; - return ss.str(); - } + bool isSet() const { + return _combined > 0; + } - int majorVersion() const { return _major; } - int minorVersion() const { return _minor; } - OID epoch() const { return _epoch; } + bool isEpochSet() const { + return _epoch.isSet(); + } - // - // 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. - // + std::string toString() const { + std::stringstream ss; + // Similar to month/day/year. For the most part when debugging, we care about major + // so it's first + ss << _major << "|" << _minor << "||" << _epoch; + return ss.str(); + } - bool operator>( const ChunkVersion& otherVersion ) const { - return this->_combined > otherVersion._combined; - } + int majorVersion() const { + return _major; + } + int minorVersion() const { + return _minor; + } + OID epoch() const { + return _epoch; + } - bool operator>=( const ChunkVersion& otherVersion ) const { - return this->_combined >= otherVersion._combined; - } + // + // 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 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; - } + bool operator>=(const ChunkVersion& otherVersion) const { + return this->_combined >= otherVersion._combined; + } - // - // Equivalence comparison types. - // + 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& otherVersion ) const { - if( ! hasEqualEpoch( otherVersion ) ) return false; - return otherVersion._major == _major; - } + bool operator<=(const ChunkVersion& otherVersion) const { + return this->_combined <= otherVersion._combined; + } - // Is this the same version? - bool equals( const ChunkVersion& otherVersion ) const { - if( ! hasEqualEpoch( otherVersion ) ) return false; - return otherVersion._combined == _combined; - } + // + // Equivalence comparison types. + // - /** - * Returns true if the otherVersion is the same as this version and enforces strict epoch - * checking (empty epochs are not wildcards). - */ - bool isStrictlyEqualTo( const ChunkVersion& otherVersion ) const { - if ( otherVersion._epoch != _epoch ) - return false; - return otherVersion._combined == _combined; - } + // Can we write to this data and not have a problem? + bool isWriteCompatibleWith(const ChunkVersion& otherVersion) const { + if (!hasEqualEpoch(otherVersion)) + return false; + return otherVersion._major == _major; + } - /** - * 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 ( _major != otherVersion._major ) - return _major < otherVersion._major; - - return _minor < otherVersion._minor; - } + // Is this the same version? + bool equals(const ChunkVersion& otherVersion) const { + if (!hasEqualEpoch(otherVersion)) + return false; + return otherVersion._combined == _combined; + } - // Is this in the same epoch? - bool hasEqualEpoch( const ChunkVersion& otherVersion ) const { - return hasEqualEpoch( otherVersion._epoch ); - } + /** + * Returns true if the otherVersion is the same as this version and enforces strict epoch + * checking (empty epochs are not wildcards). + */ + bool isStrictlyEqualTo(const ChunkVersion& otherVersion) const { + if (otherVersion._epoch != _epoch) + return false; + return otherVersion._combined == _combined; + } - bool hasEqualEpoch( const OID& otherEpoch ) const { - return _epoch == otherEpoch; - } + /** + * 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; - // - // BSON input/output - // - // The idea here is to make the BSON input style very flexible right now, so we - // can then tighten it up in the next version. We can accept either a BSONObject field - // with version and epoch, or version and epoch in different fields (either is optional). - // In this case, epoch always is stored in a field name of the version field name + "Epoch" - // - - // - // { version : <TS> } and { version : [<TS>,<OID>] } format - // - - static bool canParseBSON( const BSONElement& el, const std::string& prefix="" ){ - bool canParse; - fromBSON( el, prefix, &canParse ); - return canParse; - } + if (_major != otherVersion._major) + return _major < otherVersion._major; - static ChunkVersion fromBSON( const BSONElement& el, const std::string& prefix="" ){ - bool canParse; - return fromBSON( el, prefix, &canParse ); - } + return _minor < otherVersion._minor; + } - static ChunkVersion fromBSON( const BSONElement& el, - const std::string& prefix, - bool* canParse ) - { - *canParse = true; + // Is this in the same epoch? + bool hasEqualEpoch(const ChunkVersion& otherVersion) const { + return hasEqualEpoch(otherVersion._epoch); + } - int type = el.type(); + bool hasEqualEpoch(const OID& otherEpoch) const { + return _epoch == otherEpoch; + } - if( type == Array ){ - return fromBSON( BSONArray( el.Obj() ), canParse ); - } + // + // BSON input/output + // + // The idea here is to make the BSON input style very flexible right now, so we + // can then tighten it up in the next version. We can accept either a BSONObject field + // with version and epoch, or version and epoch in different fields (either is optional). + // In this case, epoch always is stored in a field name of the version field name + "Epoch" + // + + // + // { version : <TS> } and { version : [<TS>,<OID>] } format + // + + static bool canParseBSON(const BSONElement& el, const std::string& prefix = "") { + bool canParse; + fromBSON(el, prefix, &canParse); + return canParse; + } - if( type == jstOID ){ - return ChunkVersion( 0, 0, el.OID() ); - } + static ChunkVersion fromBSON(const BSONElement& el, const std::string& prefix = "") { + bool canParse; + return fromBSON(el, prefix, &canParse); + } - if( type == bsonTimestamp || type == Date ){ - return fromDeprecatedLong( el._numberLong(), OID() ); - } + static ChunkVersion fromBSON(const BSONElement& el, const std::string& prefix, bool* canParse) { + *canParse = true; - *canParse = false; + int type = el.type(); - return ChunkVersion( 0, 0, OID() ); + if (type == Array) { + return fromBSON(BSONArray(el.Obj()), canParse); } - // - // { version : <TS>, versionEpoch : <OID> } object format - // - - static bool canParseBSON( const BSONObj& obj, const std::string& prefix="" ){ - bool canParse; - fromBSON( obj, prefix, &canParse ); - return canParse; + if (type == jstOID) { + return ChunkVersion(0, 0, el.OID()); } - static ChunkVersion fromBSON( const BSONObj& obj, const std::string& prefix="" ){ - bool canParse; - return fromBSON( obj, prefix, &canParse ); + if (type == bsonTimestamp || type == Date) { + return fromDeprecatedLong(el._numberLong(), OID()); } - static ChunkVersion fromBSON( const BSONObj& obj, - const std::string& prefixIn, - bool* canParse ) - { - *canParse = true; + *canParse = false; - std::string prefix = prefixIn; - // "version" doesn't have a "cluster constanst" because that field is never - // written to the config. - if( prefixIn == "" && ! obj[ "version" ].eoo() ){ - prefix = (std::string)"version"; - } - // TODO: use ChunkType::DEPRECATED_lastmod() - // NOTE: type_chunk.h includes this file - else if( prefixIn == "" && ! obj["lastmod"].eoo() ){ - prefix = (std::string)"lastmod"; - } + return ChunkVersion(0, 0, OID()); + } - ChunkVersion version = fromBSON( obj[ prefix ], prefixIn, canParse ); + // + // { version : <TS>, versionEpoch : <OID> } object format + // - if( obj[ prefix + "Epoch" ].type() == jstOID ){ - version._epoch = obj[ prefix + "Epoch" ].OID(); - *canParse = true; - } + static bool canParseBSON(const BSONObj& obj, const std::string& prefix = "") { + bool canParse; + fromBSON(obj, prefix, &canParse); + return canParse; + } - return version; - } + static ChunkVersion fromBSON(const BSONObj& obj, const std::string& prefix = "") { + bool canParse; + return fromBSON(obj, prefix, &canParse); + } - // - // { version : [<TS>, <OID>] } format - // + static ChunkVersion fromBSON(const BSONObj& obj, const std::string& prefixIn, bool* canParse) { + *canParse = true; - static bool canParseBSON( const BSONArray& arr ){ - bool canParse; - fromBSON( arr, &canParse ); - return canParse; + std::string prefix = prefixIn; + // "version" doesn't have a "cluster constanst" because that field is never + // written to the config. + if (prefixIn == "" && !obj["version"].eoo()) { + prefix = (std::string) "version"; + } + // TODO: use ChunkType::DEPRECATED_lastmod() + // NOTE: type_chunk.h includes this file + else if (prefixIn == "" && !obj["lastmod"].eoo()) { + prefix = (std::string) "lastmod"; } - static ChunkVersion fromBSON( const BSONArray& arr ){ - bool canParse; - return fromBSON( arr, &canParse ); + ChunkVersion version = fromBSON(obj[prefix], prefixIn, canParse); + + if (obj[prefix + "Epoch"].type() == jstOID) { + version._epoch = obj[prefix + "Epoch"].OID(); + *canParse = true; } - static ChunkVersion fromBSON( const BSONArray& arr, - bool* canParse ) - { - *canParse = false; + return version; + } - ChunkVersion version; + // + // { version : [<TS>, <OID>] } format + // - BSONObjIterator it( arr ); - if( ! it.more() ) return version; + static bool canParseBSON(const BSONArray& arr) { + bool canParse; + fromBSON(arr, &canParse); + return canParse; + } - version = fromBSON( it.next(), "", canParse ); - if( ! (*canParse) ) return version; + static ChunkVersion fromBSON(const BSONArray& arr) { + bool canParse; + return fromBSON(arr, &canParse); + } - *canParse = true; + static ChunkVersion fromBSON(const BSONArray& arr, bool* canParse) { + *canParse = false; - if( ! it.more() ) return version; - BSONElement next = it.next(); - if( next.type() != jstOID ) return version; + ChunkVersion version; - version._epoch = next.OID(); + BSONObjIterator it(arr); + if (!it.more()) + return version; + version = fromBSON(it.next(), "", canParse); + if (!(*canParse)) return version; - } - enum VersionChoice { - VersionChoice_Local, - VersionChoice_Remote, - VersionChoice_Unknown - }; + *canParse = true; - /** - * Compares a remotely-loaded version 'remoteVersion' to the latest local version of a - * collection, 'localVersion', and returns the newest. - * - * Because it isn't clear during epoch changes which epoch is newer, the local version - * before the reload occurred, 'prevLocalVersion', is used to determine whether the remote - * epoch is definitely newer, or we're not sure. - */ - static VersionChoice chooseNewestVersion( ChunkVersion prevLocalVersion, - ChunkVersion localVersion, - ChunkVersion remoteVersion ) - { - OID prevEpoch = prevLocalVersion.epoch(); - OID localEpoch = localVersion.epoch(); - OID remoteEpoch = remoteVersion.epoch(); - - // Everything changed in-flight, so we need to try again - if ( prevEpoch != localEpoch && localEpoch != remoteEpoch ) { - return VersionChoice_Unknown; - } + if (!it.more()) + return version; + BSONElement next = it.next(); + if (next.type() != jstOID) + return version; - // We're in the same (zero) epoch as the latest metadata, nothing to do - if ( localEpoch == remoteEpoch && !remoteEpoch.isSet() ) { - return VersionChoice_Local; - } + version._epoch = next.OID(); - // We're in the same (non-zero) epoch as the latest metadata, so increment the version - if ( localEpoch == remoteEpoch && remoteEpoch.isSet() ) { + return version; + } - // Use the newer version if possible - if ( localVersion < remoteVersion ) { - return VersionChoice_Remote; - } - else { - return VersionChoice_Local; - } - } + enum VersionChoice { VersionChoice_Local, VersionChoice_Remote, VersionChoice_Unknown }; - // We're now sure we're installing a new epoch and the epoch didn't change during reload - dassert( prevEpoch == localEpoch && localEpoch != remoteEpoch ); - return VersionChoice_Remote; + /** + * Compares a remotely-loaded version 'remoteVersion' to the latest local version of a + * collection, 'localVersion', and returns the newest. + * + * Because it isn't clear during epoch changes which epoch is newer, the local version + * before the reload occurred, 'prevLocalVersion', is used to determine whether the remote + * epoch is definitely newer, or we're not sure. + */ + static VersionChoice chooseNewestVersion(ChunkVersion prevLocalVersion, + ChunkVersion localVersion, + ChunkVersion remoteVersion) { + OID prevEpoch = prevLocalVersion.epoch(); + OID localEpoch = localVersion.epoch(); + OID remoteEpoch = remoteVersion.epoch(); + + // Everything changed in-flight, so we need to try again + if (prevEpoch != localEpoch && localEpoch != remoteEpoch) { + return VersionChoice_Unknown; } - // - // Currently our BSON output is to two different fields, to cleanly work with older - // versions that know nothing about epochs. - // + // We're in the same (zero) epoch as the latest metadata, nothing to do + if (localEpoch == remoteEpoch && !remoteEpoch.isSet()) { + return VersionChoice_Local; + } - BSONObj toBSONWithPrefix( const std::string& prefixIn ) const { - BSONObjBuilder b; + // We're in the same (non-zero) epoch as the latest metadata, so increment the version + if (localEpoch == remoteEpoch && remoteEpoch.isSet()) { + // Use the newer version if possible + if (localVersion < remoteVersion) { + return VersionChoice_Remote; + } else { + return VersionChoice_Local; + } + } - std::string prefix = prefixIn; - if( prefix == "" ) prefix = "version"; + // We're now sure we're installing a new epoch and the epoch didn't change during reload + dassert(prevEpoch == localEpoch && localEpoch != remoteEpoch); + return VersionChoice_Remote; + } - b.appendTimestamp( prefix, _combined ); - b.append( prefix + "Epoch", _epoch ); - return b.obj(); - } + // + // Currently our BSON output is to two different fields, to cleanly work with older + // versions that know nothing about epochs. + // - void addToBSON( BSONObjBuilder& b, const std::string& prefix="" ) const { - b.appendElements( toBSONWithPrefix( prefix ) ); - } + BSONObj toBSONWithPrefix(const std::string& prefixIn) const { + BSONObjBuilder b; - void addEpochToBSON( BSONObjBuilder& b, const std::string& prefix="" ) const { - b.append( prefix + "Epoch", _epoch ); - } + std::string prefix = prefixIn; + if (prefix == "") + prefix = "version"; - BSONObj toBSON() const { - // ChunkVersion wants to be an array. - BSONArrayBuilder b; - b.appendTimestamp(_combined); - b.append(_epoch); - return b.arr(); - } + b.appendTimestamp(prefix, _combined); + b.append(prefix + "Epoch", _epoch); + return b.obj(); + } - bool parseBSON(const BSONObj& source, std::string* errMsg) { - // ChunkVersion wants to be an array. - BSONArray arrSource = static_cast<BSONArray>(source); + void addToBSON(BSONObjBuilder& b, const std::string& prefix = "") const { + b.appendElements(toBSONWithPrefix(prefix)); + } - bool canParse; - ChunkVersion version = fromBSON(arrSource, &canParse); - if (!canParse) { - *errMsg = "Could not parse version structure"; - return false; - } + void addEpochToBSON(BSONObjBuilder& b, const std::string& prefix = "") const { + b.append(prefix + "Epoch", _epoch); + } - _minor = version._minor; - _major = version._major; - _epoch = version._epoch; - return true; - } + BSONObj toBSON() const { + // ChunkVersion wants to be an array. + BSONArrayBuilder b; + b.appendTimestamp(_combined); + b.append(_epoch); + return b.arr(); + } - void clear() { - _minor = 0; - _major = 0; - _epoch = OID(); - } + bool parseBSON(const BSONObj& source, std::string* errMsg) { + // ChunkVersion wants to be an array. + BSONArray arrSource = static_cast<BSONArray>(source); - void cloneTo(ChunkVersion* other) const { - other->clear(); - other->_minor = _minor; - other->_major = _major; - other->_epoch = _epoch; + bool canParse; + ChunkVersion version = fromBSON(arrSource, &canParse); + if (!canParse) { + *errMsg = "Could not parse version structure"; + return false; } - }; + _minor = version._minor; + _major = version._major; + _epoch = version._epoch; + return true; + } - inline std::ostream& operator<<( std::ostream &s , const ChunkVersion& v) { - s << v.toString(); - return s; + void clear() { + _minor = 0; + _major = 0; + _epoch = OID(); } -} // namespace mongo + void cloneTo(ChunkVersion* other) const { + other->clear(); + other->_minor = _minor; + other->_major = _major; + other->_epoch = _epoch; + } +}; + +inline std::ostream& operator<<(std::ostream& s, const ChunkVersion& v) { + s << v.toString(); + return s; +} + +} // namespace mongo |