diff options
Diffstat (limited to 'storageserver/src/apps/storaged/storage.cpp')
-rw-r--r-- | storageserver/src/apps/storaged/storage.cpp | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/storageserver/src/apps/storaged/storage.cpp b/storageserver/src/apps/storaged/storage.cpp new file mode 100644 index 00000000000..8eb955ae930 --- /dev/null +++ b/storageserver/src/apps/storaged/storage.cpp @@ -0,0 +1,220 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * \class storage::StorageApp + * \ingroup serverapp + * + * \brief The storage daemon application. + * + * This code is NOT unit tested and should be as minimal as possible. + * + * It should handle process signals and have the main method for the + * application, but as little as possible else. + */ + +#include <vespa/fastos/fastos.h> +#include <vespa/log/log.h> +#include <signal.h> +#include <vespa/persistence/spi/exceptions.h> +#include <vespa/storage/storageutil/utils.h> +#include <vespa/storageserver/app/distributorprocess.h> +#include "forcelink.h" +#include <vespa/storageserver/app/memfileservicelayerprocess.h> +#include <vespa/storageserver/app/dummyservicelayerprocess.h> +#include <vespa/storageserver/app/rpcservicelayerprocess.h> +#include <vespa/vespalib/util/programoptions.h> +#include <vespa/vespalib/util/shutdownguard.h> +#include <vespa/config/config.h> + +LOG_SETUP("vds.application"); + +namespace storage { + +namespace { + +Process::UP createProcess(vespalib::stringref configId) { + // FIXME: Rewrite parameter to config uri and pass when all subsequent configs are converted. + config::ConfigUri uri(configId); + std::unique_ptr<vespa::config::content::core::StorServerConfig> serverConfig = config::ConfigGetter<vespa::config::content::core::StorServerConfig>::getConfig(uri.getConfigId(), uri.getContext()); + if (serverConfig->isDistributor) { + return Process::UP(new DistributorProcess(configId)); + } else switch (serverConfig->persistenceProvider.type) { + case vespa::config::content::core::StorServerConfig::PersistenceProvider::STORAGE: + return Process::UP(new MemFileServiceLayerProcess(configId)); + case vespa::config::content::core::StorServerConfig::PersistenceProvider::DUMMY: + return Process::UP(new DummyServiceLayerProcess(configId)); + case vespa::config::content::core::StorServerConfig::PersistenceProvider::RPC: + return Process::UP(new RpcServiceLayerProcess(configId)); + default: + throw vespalib::IllegalStateException( + "Unknown persistence provider.", VESPA_STRLOC); + } +} + +} // End of anonymous namespace + +class StorageApp : public FastOS_Application, + private vespalib::ProgramOptions +{ + std::string _configId; + bool _showSyntax; + uint32_t _maxShutdownTime; + int _lastSignal; + vespalib::Monitor _signalLock; + Process::UP _process; + +public: + StorageApp(); + + void handleSignal(int signal) { + LOG(info, "Got signal %d, waiting for lock", signal); + vespalib::MonitorGuard sync(_signalLock); + + LOG(info, "Got lock for signal %d", signal); + _lastSignal = signal; + sync.signal(); + } + void handleSignals(); + +private: + bool Init(); + int Main(); + bool gotSignal() { return _lastSignal != 0; } +}; + +StorageApp::StorageApp() + : _showSyntax(false), _maxShutdownTime(120000), _lastSignal(0), _signalLock() +{ + setSyntaxMessage( + "This is the main daemon used to start the storage nodes. The same " + "actual binary is used for both storage and distributor nodes, but " + "it is duplicated when installing, such that one can hotfix a " + "distributor bug without restarting storage nodes."); + addOption("c config-id", _configId, + "The config identifier this storage node should use to request " + "config. This identifier specifies whether the binary will behave " + "as a storage or distributor, what cluster it belongs to, and the " + "index it has in the cluster."); + addOption("h help", _showSyntax, false, "Show this syntax help page."); + addOption("t maxshutdowntime", _maxShutdownTime, uint32_t(120000), + "Maximum amount of milliseconds we allow proper shutdown to run before " + "abruptly killing the process."); +} + +bool StorageApp::Init() +{ + FastOS_Application::Init(); + setCommandLineArguments( + FastOS_Application::_argc, FastOS_Application::_argv); + try{ + parse(); + } catch (vespalib::InvalidCommandLineArgumentsException& e) { + std::cerr << e.getMessage() << "\n\n"; + writeSyntaxPage(std::cerr); + exit(EXIT_FAILURE); + } + if (_showSyntax) { + writeSyntaxPage(std::cerr); + exit(0); + } + return true; +} + +namespace { + storage::StorageApp *sigtramp = 0; + uint32_t _G_signalCount = 0; + + void killHandler(int sig) { + if (_G_signalCount == 0) { + _G_signalCount++; + if (sigtramp == 0) _exit(EXIT_FAILURE); + // note: this is not totally safe, sigtramp is not protected by a lock + sigtramp->handleSignal(sig); + } else { + fprintf(stderr, "Received another shutdown signal %u while " + "shutdown in progress (count=%u)", + sig, _G_signalCount); + } + } + + void setupKillHandler() { + struct sigaction usr_action; + sigset_t block_mask; + + /* Establish the signal handler. */ + sigfillset (&block_mask); + usr_action.sa_handler = killHandler; + usr_action.sa_mask = block_mask; + usr_action.sa_flags = 0; + sigaction (SIGTERM, &usr_action, NULL); + sigaction (SIGINT, &usr_action, NULL); + } +} + +void StorageApp::handleSignals() +{ + if (gotSignal()) { + int signal = _lastSignal; + LOG(debug, "starting controlled shutdown of storage " + "(received signal %d)", signal); + _process->getNode().requestShutdown("controlled shutdown"); + } +} + +int StorageApp::Main() +{ + try{ + _process = createProcess(_configId); + _process->setupConfig(600000); + _process->createNode(); + } catch (const spi::HandledException & e) { + LOG(warning, "Died due to known cause: %s", e.what()); + return 1; + } catch (const vespalib::NetworkSetupFailureException & e) { + LOG(warning, "Network failure: '%s'", e.what()); + return 1; + } catch (const vespalib::IllegalStateException & e) { + LOG(error, "Unknown IllegalStateException: '%s'", e.what()); + return 1; + } catch (const vespalib::Exception & e) { + LOG(error, "Caught exception when starting: %s", e.what()); + return 1; + } + + // Not setting up kill handlers before storage is up. Before that + // we can just die quickly with default handlers. + LOG(debug, "Node created. Setting up kill handler."); + setupKillHandler(); + + // main loop - wait for termination signal + while (!_process->getNode().attemptedStopped()) { + if (_process->configUpdated()) { + LOG(debug, "Config updated. Progagating config updates"); + ResumeGuard guard(_process->getNode().pause()); + _process->updateConfig(); + } + // Wait until we get a kill signal. + vespalib::MonitorGuard lock(_signalLock); + lock.wait(1000); + handleSignals(); + } + LOG(debug, "Server was attempted stopped, shutting down"); + // Create guard that will forcifully kill storage if destruction takes longer + // time than given timeout. + vespalib::ShutdownGuard shutdownGuard(_maxShutdownTime); + LOG(debug, "Attempting proper shutdown"); + _process.reset(0); + LOG(debug, "Completed controlled shutdown."); + return 0; +} + +} // storage + +int main(int argc, char **argv) +{ + storage::StorageApp app; + storage::sigtramp = &app; + int retval = app.Entry(argc,argv); + storage::sigtramp = NULL; + LOG(debug, "Exiting"); + return retval; +} |