diff options
author | daveh86 <howsdav@gmail.com> | 2014-08-05 10:13:49 +1000 |
---|---|---|
committer | Benety Goh <benety@mongodb.com> | 2014-08-11 10:30:06 -0400 |
commit | cf2917a1e94959a696642407425f89fe14ccb1b4 (patch) | |
tree | 713039bd3ef82f4978f44dfa8d6a0cb130e60342 /src/mongo | |
parent | 4f5703ae45f05576fac5261dc985aabd142a0a78 (diff) | |
download | mongo-cf2917a1e94959a696642407425f89fe14ccb1b4.tar.gz |
SERVER-8752 Add config.actionlog
Closes #739
Signed-off-by: Benety Goh <benety@mongodb.com>
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/s/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/s/balance.cpp | 110 | ||||
-rw-r--r-- | src/mongo/s/type_actionlog.cpp | 153 | ||||
-rw-r--r-- | src/mongo/s/type_actionlog.h | 204 |
4 files changed, 468 insertions, 0 deletions
diff --git a/src/mongo/s/SConscript b/src/mongo/s/SConscript index 1514261b5f2..3b2852e8332 100644 --- a/src/mongo/s/SConscript +++ b/src/mongo/s/SConscript @@ -8,6 +8,7 @@ Import("env") env.Library('base', ['mongo_version_range.cpp', 'range_arithmetic.cpp', + 'type_actionlog.cpp', 'type_changelog.cpp', 'type_chunk.cpp', 'type_collection.cpp', diff --git a/src/mongo/s/balance.cpp b/src/mongo/s/balance.cpp index 87f71781afb..7c47dd4ba54 100644 --- a/src/mongo/s/balance.cpp +++ b/src/mongo/s/balance.cpp @@ -43,6 +43,7 @@ #include "mongo/s/grid.h" #include "mongo/s/server.h" #include "mongo/s/shard.h" +#include "mongo/s/type_actionlog.h" #include "mongo/s/type_chunk.h" #include "mongo/s/type_collection.h" #include "mongo/s/type_mongos.h" @@ -50,6 +51,7 @@ #include "mongo/s/type_tags.h" #include "mongo/util/fail_point_service.h" #include "mongo/util/log.h" +#include "mongo/util/timer.h" #include "mongo/util/version.h" namespace mongo { @@ -157,6 +159,95 @@ namespace mongo { NULL ); } + /* + * Builds the details object for the actionlog. + * Current formats for detail are: + * Success: { + * "candidateChunks" : , + * "chunksMoved" : , + * "executionTimeMillis" : , + * "errorOccured" : false + * } + * Failure: { + * "executionTimeMillis" : , + * "errmsg" : , + * "errorOccured" : true + * } + * @param didError, did this round end in an error? + * @param executionTime, the time this round took to run + * @param candidateChunks, the number of chunks identified to be moved + * @param chunksMoved, the number of chunks moved + * @param errmsg, the error message for this round + */ + + static BSONObj _buildDetails( bool didError, int executionTime, + int candidateChunks, int chunksMoved, const std::string& errmsg ) { + + BSONObjBuilder builder; + builder.append("executionTimeMillis", executionTime); + builder.append("errorOccured", didError); + + if ( didError ) { + builder.append("errmsg", errmsg); + } else { + builder.append("candidateChunks", candidateChunks); + builder.append("chunksMoved", chunksMoved); + } + return builder.obj(); + } + + /** + * Reports the result of the balancer round into config.actionlog + * + * @param actionLog, which contains the balancer round information to be logged + * + */ + + static void _reportRound( ActionLogType& actionLog) { + try { + ScopedDbConnection conn( configServer.getConnectionString(), 30 ); + + // send a copy of the message to the log in case it doesn't reach config.actionlog + actionLog.setTime(jsTime()); + + LOG(1) << "about to log balancer result: " << actionLog; + + // The following method is not thread safe. However, there is only one balancer + // thread per mongos process. The create collection is a a no-op when the collection + // already exists + static bool createActionlog = false; + if ( ! createActionlog ) { + try { + static const int actionLogSizeBytes = 1024 * 1024 * 2; + conn->createCollection( ActionLogType::ConfigNS , actionLogSizeBytes , true ); + } + catch ( const DBException& ex ) { + LOG(1) << "config.actionlog could not be created, another mongos process " + << "may have done so" << causedBy(ex); + + } + createActionlog = true; + } + + Status result = clusterInsert( ActionLogType::ConfigNS, + actionLog.toBSON(), + WriteConcernOptions::AllConfigs, + NULL ); + + if ( !result.isOK() ) { + log() << "Error encountered while logging action from balancer " + << result.reason(); + } + + conn.done(); + } + catch ( const DBException& ex ) { + // if we got here, it means the config change is only in the log; + // the change didn't make it to config.actionlog + warning() << "could not log balancer result" << causedBy(ex); + } + } + bool Balancer::_checkOIDs() { vector<Shard> all; Shard::getAllShards( all ); @@ -458,6 +549,12 @@ namespace mongo { while ( ! inShutdown() ) { + Timer balanceRoundTimer; + ActionLogType actionLog; + + actionLog.setServer(getHostNameCached()); + actionLog.setWhat("balancer.round"); + try { ScopedDbConnection conn(config.toString(), 30); @@ -465,6 +562,8 @@ namespace mongo { // ping has to be first so we keep things in the config server in sync _ping(); + BSONObj balancerResult; + // use fresh shard state Shard::reloadShardInfo(); @@ -552,6 +651,11 @@ namespace mongo { waitForDelete ); } + actionLog.setDetails( _buildDetails( false, balanceRoundTimer.millis(), + static_cast<int>(candidateChunks.size()), _balancedLastTime, "") ); + + _reportRound( actionLog ); + LOG(1) << "*** end of balancing round" << endl; } @@ -568,6 +672,12 @@ namespace mongo { // Just to match the opening statement if in log level 1 LOG(1) << "*** End of balancing round" << endl; + // This round failed, tell the world! + actionLog.setDetails( _buildDetails( true, balanceRoundTimer.millis(), + 0, 0, e.what()) ); + + _reportRound( actionLog ); + sleepsecs( sleepTime ); // sleep a fair amount b/c of error continue; } diff --git a/src/mongo/s/type_actionlog.cpp b/src/mongo/s/type_actionlog.cpp new file mode 100644 index 00000000000..29dc9d97d66 --- /dev/null +++ b/src/mongo/s/type_actionlog.cpp @@ -0,0 +1,153 @@ +/** + * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>. + * + * 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. + */ +#include "mongo/s/type_actionlog.h" + +#include "mongo/db/field_parser.h" +#include "mongo/util/mongoutils/str.h" + +namespace mongo { + + using mongo::str::stream; + + const std::string ActionLogType::ConfigNS = "config.actionlog"; + + const BSONField<std::string> ActionLogType::server("server"); + const BSONField<std::string> ActionLogType::what("what"); + const BSONField<Date_t> ActionLogType::time("time"); + const BSONField<BSONObj> ActionLogType::details("details"); + + ActionLogType::ActionLogType() { + clear(); + } + + ActionLogType::~ActionLogType() { + } + + bool ActionLogType::isValid(std::string* errMsg) const { + std::string dummy; + if (errMsg == NULL) { + errMsg = &dummy; + } + + // All the mandatory fields must be present. + if (!_isServerSet) { + *errMsg = stream() << "missing " << server.name() << " field"; + return false; + } + if (!_isTimeSet) { + *errMsg = stream() << "missing " << time.name() << " field"; + return false; + } + if (!_isWhatSet) { + *errMsg = stream() << "missing " << what.name() << " field"; + return false; + } + if (!_isDetailsSet) { + *errMsg = stream() << "missing " << details.name() << " field"; + return false; + } + + return true; + } + + BSONObj ActionLogType::toBSON() const { + BSONObjBuilder builder; + + if (_isServerSet) builder.append(server(), _server); + if (_isTimeSet) builder.append(time(), _time); + if (_isWhatSet) builder.append(what(), _time); + if (_isDetailsSet) builder.append(details(), _details); + + return builder.obj(); + } + + bool ActionLogType::parseBSON(const BSONObj& source, string* errMsg) { + clear(); + + std::string dummy; + if (!errMsg) errMsg = &dummy; + + FieldParser::FieldState fieldState; + + fieldState = FieldParser::extract(source, server, &_server, errMsg); + if (fieldState == FieldParser::FIELD_INVALID) return false; + _isServerSet = fieldState == FieldParser::FIELD_SET; + + fieldState = FieldParser::extract(source, time, &_time, errMsg); + if (fieldState == FieldParser::FIELD_INVALID) return false; + _isTimeSet = fieldState == FieldParser::FIELD_SET; + + fieldState = FieldParser::extract(source, what, &_what, errMsg); + if (fieldState == FieldParser::FIELD_INVALID) return false; + _isWhatSet = fieldState == FieldParser::FIELD_SET; + + fieldState = FieldParser::extract(source, details, &_details, errMsg); + if (fieldState == FieldParser::FIELD_INVALID) return false; + _isDetailsSet = fieldState == FieldParser::FIELD_SET; + + return true; + } + + void ActionLogType::clear() { + + _server.clear(); + _isServerSet = false; + + _what.clear(); + _isWhatSet = false; + + _time = 0ULL; + _isTimeSet = false; + + _details = BSONObj(); + _isDetailsSet = false; + + } + + void ActionLogType::cloneTo(ActionLogType* other) const { + other->clear(); + + other->_server = _server; + other->_isServerSet = _isServerSet; + + other->_what = _what; + other->_isWhatSet = _isWhatSet; + + other->_time = _time; + other->_isTimeSet = _isTimeSet; + + other->_details = _details; + other->_isDetailsSet = _isDetailsSet; + + } + + std::string ActionLogType::toString() const { + return toBSON().toString(); + } + +} // namespace mongo diff --git a/src/mongo/s/type_actionlog.h b/src/mongo/s/type_actionlog.h new file mode 100644 index 00000000000..3053657722e --- /dev/null +++ b/src/mongo/s/type_actionlog.h @@ -0,0 +1,204 @@ +/** + * Copyright (C) 2014 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 <http://www.gnu.org/licenses/>. + * + * 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 <string> + +#include "mongo/base/disallow_copying.h" +#include "mongo/base/string_data.h" +#include "mongo/db/jsobj.h" + +namespace mongo { + + /** + * This class represents the layout and contents of documents contained in the + * config.actionlog collection. All manipulation of documents coming from that + * collection should be done with this class. + * + * Usage Example: + * + * // Contact the config. 'conn' has been obtained before. + * DBClientBase* conn; + * BSONObj query = QUERY(ActionLogType::exampleField("exampleFieldName")); + * exampleDoc = conn->findOne(ActionLogType::ConfigNS, query); + * + * // Process the response. + * ActionLogType exampleType; + * std::string errMsg; + * if (!exampleType.parseBSON(exampleDoc, &errMsg) || !exampleType.isValid(&errMsg)) { + * // Can't use 'exampleType'. Take action. + * } + * // use 'exampleType' + * + */ + class ActionLogType { + MONGO_DISALLOW_COPYING(ActionLogType); + public: + + // + // schema declarations + // + + // Name of the actionlog collection in the config server. + static const std::string ConfigNS; + + // Field names and types in the actionlog collection type. + static const BSONField<std::string> server; + static const BSONField<std::string> what; + static const BSONField<Date_t> time; + static const BSONField<BSONObj> details; + + // Common field names included under details + static const BSONField<int> candidateChunks; + static const BSONField<int> chunksMoved; + static const BSONField<bool> didError; + static const BSONField<long long> executionTimeMicros; + static const BSONField<std::string> errmsg; + + + // + // actionlog type methods + // + + ActionLogType(); + ~ActionLogType(); + + /** + * Returns true if all the mandatory fields are present and have valid + * representations. Otherwise returns false and fills in the optional 'errMsg' string. + */ + bool isValid(std::string* errMsg) const; + + /** + * Returns the BSON representation of the entry. + */ + BSONObj toBSON() const; + + void buildDetails(); + + /** + * Clears and populates the internal state using the 'source' BSON object if the + * latter contains valid values. Otherwise sets errMsg and returns false. + */ + bool parseBSON(const BSONObj& source, std::string* errMsg); + + /** + * Clears the internal state. + */ + void clear(); + + /** + * Copies all the fields present in 'this' to 'other'. + */ + void cloneTo(ActionLogType* other) const; + + /** + * Returns a std::string representation of the current internal state. + */ + std::string toString() const; + + // + // individual field accessors + // + + void setServer(const StringData& server) { + _server = server.toString(); + _isServerSet = true; + } + + void unsetServer() { _isServerSet = false; } + + bool isServerSet() const { return _isServerSet; } + + // Calling get*() methods when the member is not set results in undefined behavior + const std::string& getServer() const { + dassert(_isServerSet); + return _what; + } + + void setWhat(const StringData& what) { + _server = what.toString(); + _isWhatSet = true; + } + + void unsetWhat() { _isWhatSet = false; } + + bool isWhatSet() const { return _isWhatSet; } + + // Calling get*() methods when the member is not set results in undefined behavior + const std::string& getWhat() const { + dassert(_isWhatSet); + return _what; + } + + void setTime(const Date_t time) { + _time = time; + _isTimeSet = true; + } + + void unsetTime() { _isTimeSet = false; } + + bool isTimeSet() const { return _isTimeSet; } + + // Calling get*() methods when the member is not set results in undefined behavior + const Date_t getTime() const { + dassert(_isTimeSet); + return _time; + } + + void setDetails(const BSONObj& details) { + _details = details.getOwned(); + _isDetailsSet = true; + } + + void unsetDetails() { _isDetailsSet = false; } + + bool isDetailsSet() const { return _isDetailsSet; } + + // Calling get*() methods when the member is not set results in undefined behavior + const BSONObj getDetails() const { + dassert(_isDetailsSet); + return _details; + } + + private: + // Convention: (M)andatory, (O)ptional, (S)pecial rule. + std::string _server; // (M) hostname of server that we are making the change on. + // Does not include port. + bool _isServerSet; + std::string _what; // (M) what the action being performed was. + bool _isWhatSet; + Date_t _time; // (M) time this change was made + bool _isTimeSet; + BSONObj _details; // (M) A BSONObj containing extra information about some operations + bool _isDetailsSet; + + }; + +} // namespace mongo |