/** * Copyright (C) 2017 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 #include "mongo/base/string_data.h" #include "mongo/bson/bsonobj.h" #include "mongo/bson/timestamp.h" #include "mongo/db/pipeline/document.h" #include "mongo/db/pipeline/value.h" #include "mongo/util/uuid.h" namespace mongo { struct ResumeTokenData { /** * Flag to indicate if the resume token is from an invalidate notification. */ enum FromInvalidate : bool { kFromInvalidate = true, kNotFromInvalidate = false, }; ResumeTokenData(){}; ResumeTokenData(Timestamp clusterTimeIn, int versionIn, size_t applyOpsIndexIn, Value documentKeyIn, const boost::optional& uuidIn) : clusterTime(clusterTimeIn), version(versionIn), applyOpsIndex(applyOpsIndexIn), documentKey(std::move(documentKeyIn)), uuid(uuidIn){}; bool operator==(const ResumeTokenData& other) const; bool operator!=(const ResumeTokenData& other) const { return !(*this == other); }; Timestamp clusterTime; int version = 1; size_t applyOpsIndex = 0; Value documentKey; boost::optional uuid; // Flag to indicate that this resume token is from an "invalidate" entry. This will not be set // on a token from a command that *would* invalidate a change stream, but rather the invalidate // notification itself. FromInvalidate fromInvalidate = FromInvalidate::kNotFromInvalidate; }; std::ostream& operator<<(std::ostream& out, const ResumeTokenData& tokenData); /** * A token passed in by the user to indicate where in the oplog we should start for $changeStream. * This token has the following format: * { * _data: String, A hex encoding of the binary generated by keystring encoding the clusterTime, * version, applyOpsIndex, UUID, then documentKey in that order. * _typeBits: BinData - The keystring type bits used for deserialization. * } * The _data field data is encoded such that string comparisons provide the correct ordering of * tokens. Unlike the BinData, this can be sorted correctly using a MongoDB sort. BinData * unfortunately orders by the length of the data first, then by the contents. * * As an optimization, the _typeBits field may be missing and should not affect token comparison. */ class ResumeToken { public: constexpr static StringData kDataFieldName = "_data"_sd; constexpr static StringData kTypeBitsFieldName = "_typeBits"_sd; /** * Parse a resume token from a BSON object; used as an interface to the IDL parser. */ static ResumeToken parse(const BSONObj& resumeBson) { return ResumeToken::parse(Document(resumeBson)); } static ResumeToken parse(const Document& document); /** * The default no-argument constructor is required by the IDL for types used as non-optional * fields. */ ResumeToken() = default; /** * Parses 'resumeValue' into a ResumeToken using the hex-encoded string format. */ explicit ResumeToken(const ResumeTokenData& resumeValue); Document toDocument() const; /** * Because we use the IDL we require a serializer. However, the serialization format depends on * the feature compatibility version, so a serializer without an argument doesn't make sense. * This should never be used. */ BSONObj toBSON_do_not_use() const { MONGO_UNREACHABLE; } ResumeTokenData getData() const; Timestamp getClusterTime() const { return getData().clusterTime; } bool operator==(const ResumeToken&) const; bool operator!=(const ResumeToken& other) const { return !(*this == other); } friend std::ostream& operator<<(std::ostream& out, const ResumeToken& token) { return out << token.getData(); } private: explicit ResumeToken(const Document& resumeData); // This is the hex-encoded string encoding all the pieces of the resume token. std::string _hexKeyString; // Since we are using a KeyString encoding, we might lose some information about what the // original types of the serialized values were. For example, the integer 2 and the double 2.0 // will generate the same KeyString. We keep the type bits around so we can deserialize without // losing information. Value _typeBits; }; } // namespace mongo