//////////////////////////////////////////////////////////////////////////////// /// DISCLAIMER /// /// Copyright 2016 ArangoDB GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is ArangoDB GmbH, Cologne, Germany /// /// @author Max Neunhoeffer //////////////////////////////////////////////////////////////////////////////// #include "RestServer/BootstrapFeature.h" #include "Agency/AgencyComm.h" #include "Aql/QueryList.h" #include "Cluster/ClusterInfo.h" #include "Cluster/ServerState.h" #include "GeneralServer/RestHandlerFactory.h" #include "Logger/Logger.h" #include "ProgramOptions/Parameters.h" #include "ProgramOptions/ProgramOptions.h" #include "Rest/GeneralResponse.h" #include "Rest/Version.h" #include "RestServer/DatabaseFeature.h" #include "V8Server/V8DealerFeature.h" using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::options; BootstrapFeature::BootstrapFeature( application_features::ApplicationServer* server) : ApplicationFeature(server, "Bootstrap"), _isReady(false), _bark(false) { startsAfter("Endpoint"); startsAfter("Scheduler"); startsAfter("Server"); startsAfter("MMFilesLogfileManager"); startsAfter("Database"); startsAfter("Upgrade"); startsAfter("CheckVersion"); startsAfter("FoxxQueues"); startsAfter("GeneralServer"); } void BootstrapFeature::collectOptions(std::shared_ptr options) { options->addHiddenOption("hund", "make ArangoDB bark on startup", new BooleanParameter(&_bark)); } static void raceForClusterBootstrap() { AgencyComm agency; auto ci = ClusterInfo::instance(); while (true) { AgencyCommResult result = agency.getValues("Bootstrap"); if (!result.successful()) { // Error in communication, note that value not found is not an error LOG_TOPIC(TRACE, Logger::STARTUP) << "raceForClusterBootstrap: no agency communication"; sleep(1); continue; } VPackSlice value = result.slice()[0].get( std::vector({AgencyCommManager::path(), "Bootstrap"})); if (value.isString()) { // key was found and is a string if (value.copyString().find("done") != std::string::npos) { // all done, let's get out of here: LOG_TOPIC(TRACE, Logger::STARTUP) << "raceForClusterBootstrap: bootstrap already done"; return; } LOG_TOPIC(DEBUG, Logger::STARTUP) << "raceForClusterBootstrap: somebody else does the bootstrap"; sleep(1); continue; } // No value set, we try to do the bootstrap ourselves: VPackBuilder b; b.add(VPackValue(arangodb::ServerState::instance()->getId())); result = agency.casValue("Bootstrap", b.slice(), false, 300, 15); if (!result.successful()) { LOG_TOPIC(DEBUG, Logger::STARTUP) << "raceForClusterBootstrap: lost race, somebody else will bootstrap"; // Cannot get foot into the door, try again later: sleep(1); continue; } // OK, we handle things now, let's see whether a DBserver is there: auto dbservers = ci->getCurrentDBServers(); if (dbservers.size() == 0) { LOG_TOPIC(TRACE, Logger::STARTUP) << "raceForClusterBootstrap: no DBservers, waiting"; agency.removeValues("Bootstrap", false); sleep(1); continue; } LOG_TOPIC(DEBUG, Logger::STARTUP) << "raceForClusterBootstrap: race won, we do the bootstrap"; auto vocbase = DatabaseFeature::DATABASE->systemDatabase(); V8DealerFeature::DEALER->loadJavascriptFiles( vocbase, "server/bootstrap/cluster-bootstrap.js", 0); LOG_TOPIC(DEBUG, Logger::STARTUP) << "raceForClusterBootstrap: bootstrap done"; b.clear(); b.add(VPackValue(arangodb::ServerState::instance()->getId() + ": done")); result = agency.setValue("Bootstrap", b.slice(), 0); if (result.successful()) { return; } LOG_TOPIC(TRACE, Logger::STARTUP) << "raceForClusterBootstrap: could not indicate success"; sleep(1); } } void BootstrapFeature::start() { auto vocbase = DatabaseFeature::DATABASE->systemDatabase(); auto ss = ServerState::instance(); if (!ss->isRunningInCluster()) { LOG_TOPIC(DEBUG, Logger::STARTUP) << "Running server/server.js"; V8DealerFeature::DEALER->loadJavascript(vocbase, "server/server.js"); } else if (ss->isCoordinator()) { LOG_TOPIC(DEBUG, Logger::STARTUP) << "Racing for cluster bootstrap..."; raceForClusterBootstrap(); LOG_TOPIC(DEBUG, Logger::STARTUP) << "Running server/bootstrap/coordinator.js"; V8DealerFeature::DEALER->loadJavascript(vocbase, "server/bootstrap/coordinator.js"); } else if (ss->isDBServer()) { LOG_TOPIC(DEBUG, Logger::STARTUP) << "Running server/bootstrap/db-server.js"; V8DealerFeature::DEALER->loadJavascript(vocbase, "server/bootstrap/db-server.js"); } // Start service properly: rest::RestHandlerFactory::setMaintenance(false); LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "ArangoDB (version " << ARANGODB_VERSION_FULL << ") is ready for business. Have fun!"; if (_bark) { LOG_TOPIC(INFO, arangodb::Logger::FIXME) << "The dog says: wau wau!"; } _isReady = true; } void BootstrapFeature::unprepare() { // notify all currently running queries about the shutdown auto databaseFeature = application_features::ApplicationServer::getFeature( "Database"); if (ServerState::instance()->isCoordinator()) { for (auto& id : databaseFeature->getDatabaseIdsCoordinator(true)) { TRI_vocbase_t* vocbase = databaseFeature->useDatabase(id); if (vocbase != nullptr) { vocbase->queryList()->killAll(true); vocbase->release(); } } } else { for (auto& name : databaseFeature->getDatabaseNames()) { TRI_vocbase_t* vocbase = databaseFeature->useDatabase(name); if (vocbase != nullptr) { vocbase->queryList()->killAll(true); vocbase->release(); } } } }