1
0
Fork 0

Bug fix/applicationserver stop (#9414)

This commit is contained in:
Jan 2019-07-08 20:30:05 +02:00 committed by GitHub
parent 66d8c01ad6
commit 1d15b50d22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 277 additions and 199 deletions

View File

@ -1346,7 +1346,7 @@ AgencyCommResult AgencyComm::sendWithFailover(arangodb::rest::RequestType method
auto waitSomeTime = [&waitInterval, &result]() -> bool {
// Returning true means timeout because of shutdown:
if (!application_features::ApplicationServer::isRetryOK()) {
if (application_features::ApplicationServer::isStopping()) {
LOG_TOPIC("53e58", INFO, Logger::AGENCYCOMM)
<< "Unsuccessful AgencyComm: Timeout because of shutdown "
<< "errorCode: " << result.errorCode()
@ -1366,7 +1366,7 @@ AgencyCommResult AgencyComm::sendWithFailover(arangodb::rest::RequestType method
}
// Check again for shutdown, since some time has passed:
if (!application_features::ApplicationServer::isRetryOK()) {
if (application_features::ApplicationServer::isStopping()) {
LOG_TOPIC("afe45", INFO, Logger::AGENCYCOMM)
<< "Unsuccessful AgencyComm: Timeout because of shutdown "
<< "errorCode: " << result.errorCode()
@ -1411,30 +1411,12 @@ AgencyCommResult AgencyComm::sendWithFailover(arangodb::rest::RequestType method
// Some reporting:
if (tries > 20) {
auto serverState = application_features::ApplicationServer::server->state();
std::string serverStateStr;
switch (serverState) {
case arangodb::application_features::ServerState::UNINITIALIZED:
case arangodb::application_features::ServerState::IN_COLLECT_OPTIONS:
case arangodb::application_features::ServerState::IN_VALIDATE_OPTIONS:
case arangodb::application_features::ServerState::IN_PREPARE:
case arangodb::application_features::ServerState::IN_START:
serverStateStr = "in startup";
break;
case arangodb::application_features::ServerState::IN_WAIT:
serverStateStr = "running";
break;
case arangodb::application_features::ServerState::IN_STOP:
case arangodb::application_features::ServerState::IN_UNPREPARE:
case arangodb::application_features::ServerState::STOPPED:
case arangodb::application_features::ServerState::ABORT:
serverStateStr = "in shutdown";
}
std::string serverState = application_features::ApplicationServer::server->stringifyState();
LOG_TOPIC("2f181", INFO, Logger::AGENCYCOMM)
<< "Flaky agency communication to " << endpoint
<< ". Unsuccessful consecutive tries: " << tries << " (" << elapsed
<< "s). Network checks advised."
<< " Server " << serverStateStr << ".";
<< " Server " << serverState << ".";
}
if (1 < tries) {

View File

@ -56,7 +56,7 @@ enum ActionState {
* @brief Action description for mainenance actions
*
* This structure holds once initialized constant parameters of a maintenance
* action. Members are declared const, thus thread safety guards are ommited.
* action. Members are declared const, thus thread safety guards are omitted.
*/
struct ActionDescription {
public:

View File

@ -130,7 +130,7 @@ bool AgencyCallback::executeByCallbackOrTimeout(double maxTimeout) {
// One needs to acquire the mutex of the condition variable
// before entering this function!
if (!_cv.wait(static_cast<uint64_t>(maxTimeout * 1000000.0)) &&
application_features::ApplicationServer::isRetryOK()) {
!application_features::ApplicationServer::isStopping()) {
LOG_TOPIC("1514e", DEBUG, Logger::CLUSTER)
<< "Waiting done and nothing happended. Refetching to be sure";
// mop: watches have not triggered during our sleep...recheck to be sure

View File

@ -661,7 +661,7 @@ std::unique_ptr<ClusterCommResult> ClusterComm::syncRequest(
communicator()->addRequest(std::move(newRequest));
while (!sharedData->wasSignaled
&& application_features::ApplicationServer::isRetryOK()) {
&& !application_features::ApplicationServer::isStopping()) {
sharedData->cv.wait(100000);
} // while

View File

@ -1479,7 +1479,7 @@ Result ClusterInfo::createDatabaseCoordinator( // create database
agencyCallback->executeByCallbackOrTimeout(getReloadServerListTimeout() / interval);
if (!application_features::ApplicationServer::isRetryOK()) {
if (application_features::ApplicationServer::isStopping()) {
return Result(TRI_ERROR_CLUSTER_TIMEOUT);
}
}
@ -1583,7 +1583,7 @@ Result ClusterInfo::dropDatabaseCoordinator( // drop database
agencyCallback->executeByCallbackOrTimeout(interval);
if (!application_features::ApplicationServer::isRetryOK()) {
if (application_features::ApplicationServer::isStopping()) {
return Result(TRI_ERROR_CLUSTER_TIMEOUT);
}
}
@ -2084,10 +2084,10 @@ Result ClusterInfo::createCollectionsCoordinator(std::string const& databaseName
}
}
} while (application_features::ApplicationServer::isRetryOK());
} while (!application_features::ApplicationServer::isStopping());
// If we get here we are not allowed to retry.
// The loop above does not contain a break
TRI_ASSERT(!application_features::ApplicationServer::isRetryOK());
TRI_ASSERT(application_features::ApplicationServer::isStopping());
for (auto const& info : infos) {
events::CreateCollection(databaseName, info.name, TRI_ERROR_SHUTTING_DOWN);
}
@ -2265,7 +2265,7 @@ Result ClusterInfo::dropCollectionCoordinator( // drop collection
agencyCallback->executeByCallbackOrTimeout(interval);
if (!application_features::ApplicationServer::isRetryOK()) {
if (application_features::ApplicationServer::isStopping()) {
events::DropCollection(dbName, collectionID, TRI_ERROR_CLUSTER_TIMEOUT);
return Result(TRI_ERROR_CLUSTER_TIMEOUT);
}
@ -3269,7 +3269,7 @@ Result ClusterInfo::dropIndexCoordinator( // drop index
agencyCallback->executeByCallbackOrTimeout(interval);
if (!application_features::ApplicationServer::isRetryOK()) {
if (application_features::ApplicationServer::isStopping()) {
return Result(TRI_ERROR_CLUSTER_TIMEOUT);
}
}

View File

@ -183,11 +183,11 @@ Result FollowerInfo::add(ServerID const& sid) {
}
std::this_thread::sleep_for(std::chrono::microseconds(500000));
} while (TRI_microtime() < startTime + 3600 &&
application_features::ApplicationServer::isRetryOK());
!application_features::ApplicationServer::isStopping());
// This is important, give it 1h if needed. We really do not want to get
// into the position to not accept a shard getting-in-sync just because
// we cannot talk to the agency temporarily.
int errorCode = (application_features::ApplicationServer::isRetryOK()) ? TRI_ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED : TRI_ERROR_SHUTTING_DOWN;
int errorCode = (application_features::ApplicationServer::isStopping()) ? TRI_ERROR_SHUTTING_DOWN : TRI_ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED;
std::string errorMessage = "unable to add follower in agency, timeout in agency CAS operation for key " + path + ": " + TRI_errno_string(errorCode);
LOG_TOPIC("6295b", ERR, Logger::CLUSTER) << errorMessage;
@ -325,7 +325,8 @@ Result FollowerInfo::remove(ServerID const& sid) {
}
std::this_thread::sleep_for(std::chrono::microseconds(500000));
} while (TRI_microtime() < startTime + 7200 &&
application_features::ApplicationServer::isRetryOK());
!application_features::ApplicationServer::isStopping());
// This is important, give it 2h if needed. We really do not want to get
// into the position to fail to drop a follower, just because we cannot
// talk to the agency temporarily. The worst would be to drop the follower
@ -337,7 +338,7 @@ Result FollowerInfo::remove(ServerID const& sid) {
// rollback:
_followers = _oldFollowers;
int errorCode = (application_features::ApplicationServer::isRetryOK()) ? TRI_ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED : TRI_ERROR_SHUTTING_DOWN;
int errorCode = (application_features::ApplicationServer::isStopping()) ? TRI_ERROR_SHUTTING_DOWN : TRI_ERROR_CLUSTER_AGENCY_COMMUNICATION_FAILED;
std::string errorMessage = "unable to remove follower from agency, timeout in agency CAS operation for key " + path + ": " + TRI_errno_string(errorCode);
LOG_TOPIC("a0dcc", ERR, Logger::CLUSTER) << errorMessage;

View File

@ -34,7 +34,7 @@ namespace application_features {
ApplicationFeature::ApplicationFeature(ApplicationServer& server, std::string const& name)
: _server(server),
_name(name),
_state(ApplicationServer::FeatureState::UNINITIALIZED),
_state(State::UNINITIALIZED),
_enabled(true),
_optional(false),
_requiresElevatedPrivileges(false),

View File

@ -44,6 +44,16 @@ class ApplicationFeature {
ApplicationFeature(ApplicationServer& server, std::string const& name);
virtual ~ApplicationFeature();
enum class State {
UNINITIALIZED,
INITIALIZED,
VALIDATED,
PREPARED,
STARTED,
STOPPED,
UNPREPARED
};
// return the feature's name
std::string const& name() const { return _name; }
@ -51,7 +61,7 @@ class ApplicationFeature {
bool isOptional() const { return _optional; }
bool isRequired() const { return !_optional; }
ApplicationServer::FeatureState state() const { return _state; }
State state() const { return _state; }
// whether or not the feature is enabled
bool isEnabled() const { return _enabled; }
@ -177,7 +187,7 @@ class ApplicationFeature {
private:
// set a feature's state. this method should be called by the
// application server only
void state(ApplicationServer::FeatureState state) { _state = state; }
void state(State state) { _state = state; }
// determine all direct and indirect ancestors of a feature
void determineAncestors();
@ -205,7 +215,7 @@ class ApplicationFeature {
std::unordered_set<std::string> _onlyEnabledWith;
// state of feature
ApplicationServer::FeatureState _state;
State _state;
// whether or not the feature is enabled
bool _enabled;

View File

@ -26,6 +26,7 @@
#include "ApplicationFeatures/PrivilegeFeature.h"
#include "Basics/ConditionLocker.h"
#include "Basics/Result.h"
#include "Basics/ScopeGuard.h"
#include "Basics/StringUtils.h"
#include "Basics/process-utils.h"
#include "Logger/Logger.h"
@ -49,9 +50,8 @@ ApplicationServer* ApplicationServer::server = nullptr;
ApplicationServer::ApplicationServer(std::shared_ptr<ProgramOptions> options,
const char* binaryPath)
: _state(ServerState::UNINITIALIZED),
: _state(State::UNINITIALIZED),
_options(options),
_stopping(false),
_binaryPath(binaryPath) {
// register callback function for failures
fail = failCallback;
@ -78,6 +78,35 @@ ApplicationServer::~ApplicationServer() {
ApplicationServer::server = nullptr;
}
bool ApplicationServer::isPrepared() {
if (server == nullptr) {
return false;
}
auto tmp = server->state();
return tmp == State::IN_START ||
tmp == State::IN_WAIT ||
tmp == State::IN_SHUTDOWN ||
tmp == State::IN_STOP;
}
bool ApplicationServer::isStopping() {
if (server == nullptr) {
return false;
}
auto tmp = server->state();
return isStoppingState(tmp);
}
bool ApplicationServer::isStoppingState(State state) {
return state == State::IN_SHUTDOWN ||
state == State::IN_STOP ||
state == State::IN_UNPREPARE ||
state == State::STOPPED ||
state == State::ABORTED;
}
void ApplicationServer::throwFeatureNotFoundException(std::string const& name) {
THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL,
"unknown feature '" + name + "'");
@ -126,7 +155,7 @@ void ApplicationServer::disableFeatures(std::vector<std::string> const& names, b
// will take ownership of the feature object and destroy it in its
// destructor
void ApplicationServer::addFeature(ApplicationFeature* feature) {
TRI_ASSERT(feature->state() == FeatureState::UNINITIALIZED);
TRI_ASSERT(feature->state() == ApplicationFeature::State::UNINITIALIZED);
_features.emplace(feature->name(), feature);
}
@ -173,8 +202,8 @@ void ApplicationServer::run(int argc, char* argv[]) {
// collect options from all features
// in this phase, all features are order-independent
_state.store(ServerState::IN_COLLECT_OPTIONS, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_COLLECT_OPTIONS);
_state.store(State::IN_COLLECT_OPTIONS, std::memory_order_relaxed);
reportServerProgress(State::IN_COLLECT_OPTIONS);
collectOptions();
// setup dependency, but ignore any failure for now
@ -193,8 +222,8 @@ void ApplicationServer::run(int argc, char* argv[]) {
_options->seal();
// validate options of all features
_state.store(ServerState::IN_VALIDATE_OPTIONS, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_VALIDATE_OPTIONS);
_state.store(State::IN_VALIDATE_OPTIONS, std::memory_order_relaxed);
reportServerProgress(State::IN_VALIDATE_OPTIONS);
validateOptions();
// setup and validate all feature dependencies
@ -212,8 +241,8 @@ void ApplicationServer::run(int argc, char* argv[]) {
// furthermore, they must not write any files under elevated privileges
// if they want other features to access them, or if they want to access
// these files with dropped privileges
_state.store(ServerState::IN_PREPARE, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_PREPARE);
_state.store(State::IN_PREPARE, std::memory_order_relaxed);
reportServerProgress(State::IN_PREPARE);
prepare();
// turn off all features that depend on other features that have been
@ -225,62 +254,79 @@ void ApplicationServer::run(int argc, char* argv[]) {
dropPrivilegesPermanently();
// start features. now features are allowed to start threads, write files etc.
_state.store(ServerState::IN_START, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_START);
_state.store(State::IN_START, std::memory_order_relaxed);
reportServerProgress(State::IN_START);
start();
// wait until we get signaled the shutdown request
_state.store(ServerState::IN_WAIT, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_WAIT);
_state.store(State::IN_WAIT, std::memory_order_relaxed);
reportServerProgress(State::IN_WAIT);
wait();
// beginShutdown is called asynchronously ----------
// stop all features
_state.store(ServerState::IN_STOP, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_STOP);
_state.store(State::IN_STOP, std::memory_order_relaxed);
reportServerProgress(State::IN_STOP);
stop();
// unprepare all features
_state.store(ServerState::IN_UNPREPARE, std::memory_order_relaxed);
reportServerProgress(ServerState::IN_UNPREPARE);
_state.store(State::IN_UNPREPARE, std::memory_order_relaxed);
reportServerProgress(State::IN_UNPREPARE);
unprepare();
// stopped
_state.store(ServerState::STOPPED, std::memory_order_relaxed);
reportServerProgress(ServerState::STOPPED);
_state.store(State::STOPPED, std::memory_order_relaxed);
reportServerProgress(State::STOPPED);
}
// signal the server to shut down
void ApplicationServer::beginShutdown() {
// fetch the old state, check if somebody already called shutdown, and only
// proceed if not.
State old = State::UNINITIALIZED;
do {
old = state();
if (isStoppingState(old)) {
// beginShutdown already called, nothing to do now
return;
}
// try to enter the new state, but make sure nobody changed it in between
} while (!_state.compare_exchange_weak(old, State::IN_SHUTDOWN, std::memory_order_relaxed));
LOG_TOPIC("c7911", TRACE, Logger::STARTUP) << "ApplicationServer::beginShutdown";
bool old = _stopping.exchange(true);
// make sure that we advance the state when we get out of here
auto waitAborter = scopeGuard([this]() {
CONDITION_LOCKER(guard, _shutdownCondition);
_abortWaiting = true;
guard.signal();
});
// now we can execute the actual shutdown sequence
// fowards the begin shutdown signal to all features
if (!old) {
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
if ((*it)->isEnabled()) {
LOG_TOPIC("e181f", TRACE, Logger::STARTUP) << (*it)->name() << "::beginShutdown";
try {
(*it)->beginShutdown();
} catch (std::exception const& ex) {
LOG_TOPIC("b2cf4", ERR, Logger::STARTUP)
<< "caught exception during beginShutdown of feature '"
<< (*it)->name() << "': " << ex.what();
} catch (...) {
LOG_TOPIC("3f708", ERR, Logger::STARTUP)
<< "caught unknown exception during beginShutdown of feature '"
<< (*it)->name() << "'";
}
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
if ((*it)->isEnabled()) {
LOG_TOPIC("e181f", TRACE, Logger::STARTUP) << (*it)->name() << "::beginShutdown";
try {
(*it)->beginShutdown();
} catch (std::exception const& ex) {
LOG_TOPIC("b2cf4", ERR, Logger::STARTUP)
<< "caught exception during beginShutdown of feature '"
<< (*it)->name() << "': " << ex.what();
} catch (...) {
LOG_TOPIC("3f708", ERR, Logger::STARTUP)
<< "caught unknown exception during beginShutdown of feature '"
<< (*it)->name() << "'";
}
}
}
CONDITION_LOCKER(guard, _shutdownCondition);
guard.signal();
}
void ApplicationServer::shutdownFatalError() {
reportServerProgress(ServerState::ABORT);
reportServerProgress(State::ABORTED);
}
// return VPack options, with optional filters applied to filter
@ -384,7 +430,7 @@ void ApplicationServer::validateOptions() {
if (feature->isEnabled()) {
LOG_TOPIC("fa73c", TRACE, Logger::STARTUP) << feature->name() << "::validateOptions";
feature->validateOptions(_options);
feature->state(FeatureState::VALIDATED);
feature->state(ApplicationFeature::State::VALIDATED);
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}
@ -487,7 +533,7 @@ void ApplicationServer::setupDependencies(bool failOnMissing) {
for (auto it = features.begin(); it != features.end(); /* no hoisting */) {
if ((*it)->isEnabled()) {
// keep feature
(*it)->state(FeatureState::INITIALIZED);
(*it)->state(ApplicationFeature::State::INITIALIZED);
++it;
} else {
// remove feature
@ -566,7 +612,7 @@ void ApplicationServer::prepare() {
try {
LOG_TOPIC("d4e57", TRACE, Logger::STARTUP) << feature->name() << "::prepare";
feature->prepare();
feature->state(FeatureState::PREPARED);
feature->state(ApplicationFeature::State::PREPARED);
} catch (std::exception const& ex) {
LOG_TOPIC("37921", ERR, Logger::STARTUP)
<< "caught exception during prepare of feature '" << feature->name()
@ -606,7 +652,7 @@ void ApplicationServer::start() {
try {
feature->start();
feature->state(FeatureState::STARTED);
feature->state(ApplicationFeature::State::STARTED);
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
} catch (basics::Exception const& ex) {
res.reset(
@ -643,13 +689,13 @@ void ApplicationServer::start() {
if (!feature->isEnabled()) {
continue;
}
if (feature->state() == FeatureState::STARTED) {
if (feature->state() == ApplicationFeature::State::STARTED) {
LOG_TOPIC("e5cfd", TRACE, Logger::STARTUP)
<< "forcefully stopping feature '" << feature->name() << "'";
try {
feature->beginShutdown();
feature->stop();
feature->state(FeatureState::STOPPED);
feature->state(ApplicationFeature::State::STOPPED);
} catch (...) {
// ignore errors on shutdown
LOG_TOPIC("13223", TRACE, Logger::STARTUP)
@ -661,12 +707,12 @@ void ApplicationServer::start() {
// try to unprepare all feature that we just started
for (auto it = _orderedFeatures.rbegin(); it != _orderedFeatures.rend(); ++it) {
auto feature = *it;
if (feature->state() == FeatureState::STOPPED) {
if (feature->state() == ApplicationFeature::State::STOPPED) {
LOG_TOPIC("6ba4f", TRACE, Logger::STARTUP)
<< "forcefully unpreparing feature '" << feature->name() << "'";
try {
feature->unprepare();
feature->state(FeatureState::UNPREPARED);
feature->state(ApplicationFeature::State::UNPREPARED);
} catch (...) {
// ignore errors on shutdown
LOG_TOPIC("7d68f", TRACE, Logger::STARTUP)
@ -707,7 +753,7 @@ void ApplicationServer::stop() {
<< "caught unknown exception during stop of feature '"
<< feature->name() << "'";
}
feature->state(FeatureState::STOPPED);
feature->state(ApplicationFeature::State::STOPPED);
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}
@ -733,7 +779,7 @@ void ApplicationServer::unprepare() {
<< "caught unknown exception during unprepare of feature '"
<< feature->name() << "'";
}
feature->state(FeatureState::UNPREPARED);
feature->state(ApplicationFeature::State::UNPREPARED);
reportFeatureProgress(_state.load(std::memory_order_relaxed), feature->name());
}
}
@ -741,9 +787,18 @@ void ApplicationServer::unprepare() {
void ApplicationServer::wait() {
LOG_TOPIC("f86df", TRACE, Logger::STARTUP) << "ApplicationServer::wait";
while (!_stopping) {
// wait here until beginShutdown has been called and finished
while (true) {
// wait until somebody calls beginShutdown and it finishes
CONDITION_LOCKER(guard, _shutdownCondition);
guard.wait(100000);
if (_abortWaiting) {
// yippieh!
break;
}
using namespace std::chrono_literals;
guard.wait(100ms);
}
}
@ -789,14 +844,44 @@ void ApplicationServer::dropPrivilegesPermanently() {
_privilegesDropped = true;
}
void ApplicationServer::reportServerProgress(ServerState state) {
void ApplicationServer::reportServerProgress(State state) {
for (auto reporter : _progressReports) {
reporter._state(state);
}
}
void ApplicationServer::reportFeatureProgress(ServerState state, std::string const& name) {
void ApplicationServer::reportFeatureProgress(State state, std::string const& name) {
for (auto reporter : _progressReports) {
reporter._feature(state, name);
}
}
char const* ApplicationServer::stringifyState() const {
switch (_state.load()) {
case State::UNINITIALIZED:
return "uninitialized";
case State::IN_COLLECT_OPTIONS:
return "in collect options";
case State::IN_VALIDATE_OPTIONS:
return "in validate options";
case State::IN_PREPARE:
return "in prepare";
case State::IN_START:
return "in start";
case State::IN_WAIT:
return "in wait";
case State::IN_SHUTDOWN:
return "in beginShutdown";
case State::IN_STOP:
return "in stop";
case State::IN_UNPREPARE:
return "in unprepare";
case State::STOPPED:
return "in stopped";
case State::ABORTED:
return "in aborted";
}
// we should never get here
TRI_ASSERT(false);
return "unknown";
}

View File

@ -39,26 +39,6 @@ class ProgramOptions;
namespace application_features {
class ApplicationFeature;
// handled i.e. in WindowsServiceFeature.cpp
enum class ServerState {
UNINITIALIZED,
IN_COLLECT_OPTIONS,
IN_VALIDATE_OPTIONS,
IN_PREPARE,
IN_START,
IN_WAIT,
IN_STOP,
IN_UNPREPARE,
STOPPED,
ABORT
};
class ProgressHandler {
public:
std::function<void(ServerState)> _state;
std::function<void(ServerState, std::string const& featureName)> _feature;
};
// the following phases exists:
//
// `collectOptions`
@ -119,39 +99,38 @@ class ApplicationServer {
ApplicationServer& operator=(ApplicationServer const&) = delete;
public:
enum class FeatureState {
// handled i.e. in WindowsServiceFeature.cpp
enum class State : int {
UNINITIALIZED,
INITIALIZED,
VALIDATED,
PREPARED,
STARTED,
IN_COLLECT_OPTIONS,
IN_VALIDATE_OPTIONS,
IN_PREPARE,
IN_START,
IN_WAIT,
IN_SHUTDOWN,
IN_STOP,
IN_UNPREPARE,
STOPPED,
UNPREPARED
ABORTED
};
class ProgressHandler {
public:
std::function<void(State)> _state;
std::function<void(State, std::string const& featureName)> _feature;
};
static ApplicationServer* server;
static bool isStopping() {
return server != nullptr && server->_stopping.load();
}
/// @brief whether or not the server has made it as least as far as the IN_START state
static bool isPrepared();
/// @brief whether or not the server has made it as least as far as the IN_SHUTDOWN state
static bool isStopping();
// Today this static function is a duplicate of isStopping(). The
// function name 'isStopping()' is defined in other classes and
// can cause scope confusion. It also causes confusion as to when
// the application versus an individual feature or thread has begun
// stopping. This function is intended to be used within communication
// retry loops where infinite retries have previously blocked clean
// "stopping".
static bool isRetryOK() { return !isStopping(); }
static bool isPrepared() {
if (server != nullptr) {
ServerState tmp = server->_state.load(std::memory_order_relaxed);
return tmp == ServerState::IN_START || tmp == ServerState::IN_WAIT ||
tmp == ServerState::IN_STOP;
}
return false;
}
/// @brief whether or not state is the shutting down state or further (i.e. stopped, aborted etc.)
static bool isStoppingState(State state);
// returns the feature with the given name if known
// throws otherwise
@ -192,6 +171,9 @@ class ApplicationServer {
std::string helpSection() const { return _helpSection; }
bool helpShown() const { return !_helpSection.empty(); }
/// @brief stringify the internal state
char const* stringifyState() const;
// adds a feature to the application server. the application server
// will take ownership of the feature object and destroy it in its
// destructor
@ -237,7 +219,7 @@ class ApplicationServer {
std::shared_ptr<options::ProgramOptions> options() const { return _options; }
// return the server state
ServerState state() const { return _state; }
State state() const { return _state; }
void addReporter(ProgressHandler reporter) {
_progressReports.emplace_back(reporter);
@ -257,7 +239,7 @@ class ApplicationServer {
return lookupFeature<T>(T::name());
}
char const* getBinaryPath() { return _binaryPath; }
char const* getBinaryPath() const { return _binaryPath; }
void registerStartupCallback(std::function<void()> const& callback) {
_startupCallbacks.emplace_back(callback);
@ -323,12 +305,12 @@ class ApplicationServer {
void dropPrivilegesTemporarily();
void dropPrivilegesPermanently();
void reportServerProgress(ServerState);
void reportFeatureProgress(ServerState, std::string const&);
void reportServerProgress(State);
void reportFeatureProgress(State, std::string const&);
private:
// the current state
std::atomic<ServerState> _state;
std::atomic<State> _state;
// the shared program options
std::shared_ptr<options::ProgramOptions> _options;
@ -339,11 +321,12 @@ class ApplicationServer {
// features order for prepare/start
std::vector<ApplicationFeature*> _orderedFeatures;
// will be signalled when the application server is asked to shut down
// will be signaled when the application server is asked to shut down
basics::ConditionVariable _shutdownCondition;
// stop flag. this is being changed by calling beginShutdown
std::atomic<bool> _stopping;
/// @brief the condition variable protects access to this flag
/// the flag is set to true when beginShutdown finishes
bool _abortWaiting = false;
// whether or not privileges have been dropped permanently
bool _privilegesDropped = false;

View File

@ -36,16 +36,17 @@ class CommunicationFeaturePhase : public ApplicationFeaturePhase {
*/
bool getCommAllowed() {
switch (state()) {
case ApplicationServer::FeatureState::UNINITIALIZED:
case ApplicationServer::FeatureState::INITIALIZED:
case ApplicationServer::FeatureState::VALIDATED:
case ApplicationServer::FeatureState::PREPARED:
case ApplicationServer::FeatureState::STARTED:
case ApplicationServer::FeatureState::STOPPED:
case ApplicationFeature::State::UNINITIALIZED:
case ApplicationFeature::State::INITIALIZED:
case ApplicationFeature::State::VALIDATED:
case ApplicationFeature::State::PREPARED:
case ApplicationFeature::State::STARTED:
case ApplicationFeature::State::STOPPED:
return true;
case ApplicationServer::FeatureState::UNPREPARED:
return false;
case ApplicationFeature::State::UNPREPARED:
break;
}
return false;
}
};

View File

@ -382,7 +382,7 @@ void SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode,
}
//////////////////////////////////////////////////////////////////////////////
/// @brief wrap ArangoDB server so we can properly emmit a status once we're
/// @brief wrap ArangoDB server so we can properly emit a status once we're
/// really up and running.
//////////////////////////////////////////////////////////////////////////////
void WindowsServiceFeature::startupProgress() {
@ -390,47 +390,54 @@ void WindowsServiceFeature::startupProgress() {
}
//////////////////////////////////////////////////////////////////////////////
/// @brief wrap ArangoDB server so we can properly emmit a status once we're
/// @brief wrap ArangoDB server so we can properly emit a status once we're
/// really up and running.
//////////////////////////////////////////////////////////////////////////////
void WindowsServiceFeature::startupFinished() {
// startup finished - signalize we're running.
// startup finished - signal that we're running.
SetServiceStatus(SERVICE_RUNNING, NO_ERROR, 0, 0, 0);
}
//////////////////////////////////////////////////////////////////////////////
/// @brief wrap ArangoDB server so we can properly emmit a status on shutdown
/// @brief wrap ArangoDB server so we can properly emit a status on shutdown
/// starting
//////////////////////////////////////////////////////////////////////////////
void WindowsServiceFeature::shutDownBegins() {
// startup finished - signalize we're running.
void WindowsServiceFeature::shutdownBegins() {
auto shutdownNoted = _shutdownNoted.exchange(true);
if (shutdownNoted) {
// we were already called before. don't note the shutdown twice
return;
}
// startup finished - signal that we are shutting down
SetServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 0, 0);
}
//////////////////////////////////////////////////////////////////////////////
/// @brief wrap ArangoDB server so we can properly emmit a status on shutdown
/// @brief wrap ArangoDB server so we can properly emit a status on shutdown
/// starting
//////////////////////////////////////////////////////////////////////////////
void WindowsServiceFeature::shutDownComplete() {
// startup finished - signalize we're running.
void WindowsServiceFeature::shutdownComplete() {
// startup finished - signal that we have shut down
SetServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);
}
//////////////////////////////////////////////////////////////////////////////
/// @brief wrap ArangoDB server so we can properly emmit a status on shutdown
/// starting
/// @brief wrap ArangoDB server so we can properly emit a status on shutdown
/// failed
//////////////////////////////////////////////////////////////////////////////
void WindowsServiceFeature::shutDownFailure() {
// startup finished - signalize we're running.
void WindowsServiceFeature::shutdownFailure() {
// startup finished - signal that shutdown has failed
SetServiceStatus(SERVICE_STOP, ERROR_SERVICE_SPECIFIC_ERROR, 0, 0, 1);
}
//////////////////////////////////////////////////////////////////////////////
/// @brief wrap ArangoDB server so we can properly emmit a status on shutdown
/// @brief wrap ArangoDB server so we can properly emit a status on shutdown
/// starting
//////////////////////////////////////////////////////////////////////////////
void WindowsServiceFeature::abortFailure(uint16_t exitCode) {
// startup finished - signalize we're running.
// startup finished - signal that the service has been aborted
SetServiceStatus(SERVICE_STOP, ERROR_SERVICE_SPECIFIC_ERROR, 0, 0, exitCode);
}
@ -471,8 +478,10 @@ void WINAPI ServiceCtrl(DWORD dwCtrlCode) {
}
WindowsServiceFeature::WindowsServiceFeature(application_features::ApplicationServer& server)
: ApplicationFeature(server, "WindowsService"), _server(&server) {
_progress = 2;
: ApplicationFeature(server, "WindowsService"),
_server(&server),
_progress(2),
_shutdownNoted(false) {
setOptional(true);
requiresElevatedPrivileges(true);
startsAfter("GreetingsPhase");
@ -502,7 +511,7 @@ void WindowsServiceFeature::collectOptions(std::shared_ptr<ProgramOptions> optio
arangodb::options::Flags::Command));
options->addOption("--uninstall-service",
"used to UNregister a service with windows",
"used to unregister a service with windows",
new BooleanParameter(&_unInstallService),
arangodb::options::makeFlags(arangodb::options::Flags::Hidden,
arangodb::options::Flags::Command));
@ -523,7 +532,7 @@ void WindowsServiceFeature::collectOptions(std::shared_ptr<ProgramOptions> optio
options->addOption(
"--servicectl-start-wait",
"command an already registered service to start and wait till its up",
"command an already registered service to start and wait till it's up",
new BooleanParameter(&_startWaitService),
arangodb::options::makeFlags(arangodb::options::Flags::Hidden,
arangodb::options::Flags::Command));
@ -536,7 +545,7 @@ void WindowsServiceFeature::collectOptions(std::shared_ptr<ProgramOptions> optio
options->addOption(
"--servicectl-stop-wait",
"command an already registered service to stop and wait till its gone",
"command an already registered service to stop and wait till it's gone",
new BooleanParameter(&_stopWaitService),
arangodb::options::makeFlags(arangodb::options::Flags::Hidden,
arangodb::options::Flags::Command));
@ -563,35 +572,36 @@ void WindowsServiceFeature::validateOptions(std::shared_ptr<ProgramOptions> opti
} else if (_startAsService) {
TRI_SetWindowsServiceAbortFunction(abortService);
ProgressHandler reporter{[this](ServerState state) {
ApplicationServer::ProgressHandler reporter{[this](ApplicationServer::State state) {
switch (state) {
case ServerState::IN_WAIT:
case ApplicationServer::State::IN_WAIT:
this->startupFinished();
break;
case ServerState::IN_STOP:
this->shutDownBegins();
case ApplicationServer::State::IN_SHUTDOWN:
case ApplicationServer::State::IN_STOP:
this->shutdownBegins();
break;
case ServerState::IN_COLLECT_OPTIONS:
case ServerState::IN_VALIDATE_OPTIONS:
case ServerState::IN_PREPARE:
case ServerState::IN_START:
case ApplicationServer::State::IN_COLLECT_OPTIONS:
case ApplicationServer::State::IN_VALIDATE_OPTIONS:
case ApplicationServer::State::IN_PREPARE:
case ApplicationServer::State::IN_START:
this->startupProgress();
break;
case ServerState::ABORT:
this->shutDownFailure();
case ApplicationServer::State::ABORTED:
this->shutdownFailure();
break;
case ServerState::UNINITIALIZED:
case ServerState::STOPPED:
case ApplicationServer::State::UNINITIALIZED:
case ApplicationServer::State::STOPPED:
break;
}
},
[this](application_features::ServerState state,
[this](application_features::ApplicationServer::State state,
std::string const& name) {
switch (state) {
case ServerState::IN_COLLECT_OPTIONS:
case ServerState::IN_VALIDATE_OPTIONS:
case ServerState::IN_PREPARE:
case ServerState::IN_START:
case ApplicationServer::State::IN_COLLECT_OPTIONS:
case ApplicationServer::State::IN_VALIDATE_OPTIONS:
case ApplicationServer::State::IN_PREPARE:
case ApplicationServer::State::IN_START:
this->startupProgress();
break;
default:

View File

@ -24,6 +24,9 @@
#define ARANGODB_APPLICATION_FEATURES_WINDOWS_SERVICE_FEATURE_H 1
#include "ApplicationFeatures/ApplicationFeature.h"
#include "ApplicationFeatures/ApplicationServer.h"
#include <atomic>
extern SERVICE_STATUS_HANDLE ServiceStatus;
@ -49,9 +52,9 @@ class WindowsServiceFeature final : public application_features::ApplicationFeat
void startupFinished();
void shutDownBegins();
void shutDownComplete();
void shutDownFailure();
void shutdownBegins();
void shutdownComplete();
void shutdownFailure();
void abortFailure(uint16_t exitCode);
static void abortService(uint16_t exitCode);
@ -69,8 +72,11 @@ class WindowsServiceFeature final : public application_features::ApplicationFeat
private:
uint16_t _progress;
/// @brief flag that tells us whether we have been informed about the shutdown before
std::atomic<bool> _shutdownNoted;
};
} // namespace arangodb
#endif
#endif

View File

@ -46,7 +46,7 @@ typedef std::vector<Expected> ExpectedVec_t;
//
// TestProgressHandler lets us know once ApplicationServer is ready
//
class TestProgressHandler : public arangodb::application_features::ProgressHandler {
class TestProgressHandler : public arangodb::application_features::ApplicationServer::ProgressHandler {
public:
TestProgressHandler() {
_serverReady=false;
@ -59,15 +59,15 @@ public:
}
void StateChange(arangodb::application_features::ServerState newState) {
if (arangodb::application_features::ServerState::IN_WAIT == newState) {
void StateChange(arangodb::application_features::ApplicationServer::State newState) {
if (arangodb::application_features::ApplicationServer::State::IN_WAIT == newState) {
CONDITION_LOCKER(clock, _serverReadyCond);
_serverReady = true;
_serverReadyCond.broadcast();
}
}
void FeatureChange(arangodb::application_features::ServerState newState, std::string const &) {
void FeatureChange(arangodb::application_features::ApplicationServer::State newState, std::string const &) {
}
arangodb::basics::ConditionVariable _serverReadyCond;