summaryrefslogtreecommitdiff
path: root/src/mongo/s/chunk_version.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/s/chunk_version.h')
-rw-r--r--src/mongo/s/chunk_version.h677
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