diff --git a/arangod/RestServer/CheckVersionFeature.cpp b/arangod/RestServer/CheckVersionFeature.cpp index d360ef5d9f..eb0377bed7 100644 --- a/arangod/RestServer/CheckVersionFeature.cpp +++ b/arangod/RestServer/CheckVersionFeature.cpp @@ -22,19 +22,24 @@ #include "CheckVersionFeature.h" +#include "Basics/FileUtils.h" #include "Logger/Logger.h" #include "Logger/LoggerFeature.h" #include "ProgramOptions/ProgramOptions.h" #include "ProgramOptions/Section.h" #include "Replication/ReplicationFeature.h" #include "RestServer/DatabaseFeature.h" -#include "V8Server/V8Context.h" -#include "V8Server/V8DealerFeature.h" -#include "V8Server/v8-query.h" -#include "V8Server/v8-vocbase.h" +#include "Rest/Version.h" +#include "StorageEngine/EngineSelectorFeature.h" +#include "StorageEngine/StorageEngine.h" #include "VocBase/vocbase.h" #include "Basics/exitcodes.h" +#include +#include +#include +#include + using namespace arangodb; using namespace arangodb::application_features; using namespace arangodb::basics; @@ -50,7 +55,9 @@ CheckVersionFeature::CheckVersionFeature( setOptional(false); requiresElevatedPrivileges(false); startsAfter("Database"); - startsAfter("V8Dealer"); + startsAfter("StorageEngine"); + startsAfter("Logger"); + startsAfter("Replication"); } void CheckVersionFeature::collectOptions( @@ -64,13 +71,70 @@ void CheckVersionFeature::collectOptions( new BooleanParameter(&_checkVersion)); } +CheckVersionFeature::CheckVersionResult +CheckVersionFeature::checkVersionFileForDB(TRI_vocbase_t* vocbase, + StorageEngine* engine, + uint32_t currentVersion) const { + std::string const versionFilePath = engine->versionFilename(vocbase->id()); + if (!FileUtils::exists(versionFilePath) || + !FileUtils::isRegularFile(versionFilePath)) { + // File not existing + LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) + << "version file (" << versionFilePath << ") not found"; + return NO_VERSION_FILE; + } + std::string const versionFileData = FileUtils::slurp(versionFilePath); + LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "found version file " + << versionFileData; + std::shared_ptr versionBuilder; + try { + versionBuilder = VPackParser::fromJson(versionFileData); + } catch (...) { + LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Cannot parse VERSION file '" + << versionFilePath << "': '" + << versionFileData << "'"; + return CANNOT_PARSE_VERSION_FILE; + } + VPackSlice version = versionBuilder->slice(); + if (!version.isObject() || !version.hasKey("version")) { + LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Cannot parse VERSION file '" + << versionFilePath << "': '" + << versionFileData << "'"; + return CANNOT_PARSE_VERSION_FILE; + } + version = version.get("version"); + if (!version.isNumber()) { + LOG_TOPIC(ERR, arangodb::Logger::FIXME) << "Cannot parse VERSION file '" + << versionFilePath << "': '" + << versionFileData << "'"; + return CANNOT_PARSE_VERSION_FILE; + } + uint32_t lastVersion = version.getNumericValue(); + + // Integer division + if (lastVersion / 100 == currentVersion / 100) { + LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "version match: last version" << lastVersion << ", current version" << currentVersion; + return VERSION_MATCH; + } + if (lastVersion > currentVersion) { + LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "downgrade: last version" << lastVersion << ", current version" << currentVersion; + return DOWNGRADE_NEEDED; + } + TRI_ASSERT(lastVersion < currentVersion); + LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "upgrade: last version" << lastVersion << ", current version" << currentVersion; + return UPGRADE_NEEDED; +} + void CheckVersionFeature::validateOptions( std::shared_ptr options) { if (!_checkVersion) { return; } + // This disables cluster feature, so the role will not be set ApplicationServer::forceDisableFeatures(_nonServerFeatures); + // HardCode it to single server + ServerState::instance()->setRole(ServerState::ROLE_SINGLE); LoggerFeature* logger = ApplicationServer::getFeature("Logger"); @@ -83,10 +147,6 @@ void CheckVersionFeature::validateOptions( DatabaseFeature* databaseFeature = ApplicationServer::getFeature("Database"); databaseFeature->enableCheckVersion(); - - V8DealerFeature* v8dealer = - ApplicationServer::getFeature("V8Dealer"); - v8dealer->setMaximumContexts(1); } void CheckVersionFeature::start() { @@ -114,73 +174,41 @@ void CheckVersionFeature::checkVersion() { // run version check LOG_TOPIC(TRACE, arangodb::Logger::FIXME) << "starting version check"; - auto* vocbase = DatabaseFeature::DATABASE->systemDatabase(); + DatabaseFeature* databaseFeature = application_features::ApplicationServer::getFeature("Database"); - // enter context and isolate - { - V8Context* context = V8DealerFeature::DEALER->enterContext(vocbase, true, 0); - - if (context == nullptr) { - LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "could not enter context #0"; - FATAL_ERROR_EXIT(); - } - - TRI_DEFER(V8DealerFeature::DEALER->exitContext(context)); - - { - v8::HandleScope scope(context->_isolate); - auto localContext = - v8::Local::New(context->_isolate, context->_context); - localContext->Enter(); - - { - v8::Context::Scope contextScope(localContext); - - // run version-check script - LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "running database version check"; - - // can do this without a lock as this is the startup - DatabaseFeature* databaseFeature = application_features::ApplicationServer::getFeature("Database"); - - // iterate over all databases - for (auto& name : databaseFeature->getDatabaseNames()) { - TRI_vocbase_t* vocbase = databaseFeature->lookupDatabase(name); - - TRI_ASSERT(vocbase != nullptr); - - // special check script to be run just once in first thread (not in - // all) but for all databases - int status = TRI_CheckDatabaseVersion(vocbase, localContext); - - LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "version check return status " << status; - - if (status < 0) { - LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "Database version check failed for '" - << vocbase->name() - << "'. Please inspect the logs for any errors"; - FATAL_ERROR_EXIT_CODE(TRI_EXIT_VERSION_CHECK_FAILED); - } else if (status == 3) { - // this is safe to do even if further databases will be checked - // because we will never set the status back to success - *_result = 3; - LOG_TOPIC(WARN, arangodb::Logger::FIXME) << "Database version check failed for '" - << vocbase->name() << "': upgrade needed"; - } else if (status == 2 && *_result == 1) { - // this is safe to do even if further databases will be checked - // because we will never set the status back to success - *_result = 2; - LOG_TOPIC(WARN, arangodb::Logger::FIXME) << "Database version check failed for '" - << vocbase->name() << "': downgrade needed"; - } + int32_t currentVersion = arangodb::rest::Version::getNumericServerVersion(); + StorageEngine* engine = EngineSelectorFeature::ENGINE; + auto checkDBVersion = [&] (TRI_vocbase_t* vocbase) { + auto res = checkVersionFileForDB(vocbase, engine, currentVersion); + switch (res) { + case VERSION_MATCH: + // all good + break; + case UPGRADE_NEEDED: + // this is safe to do even if further databases will be checked + // because we will never set the status back to success + *_result = 3; + LOG_TOPIC(WARN, arangodb::Logger::FIXME) << "Database version check failed for '" + << vocbase->name() << "': upgrade needed"; + break; + case DOWNGRADE_NEEDED: + if (*_result == 1) { + // this is safe to do even if further databases will be checked + // because we will never set the status back to success + *_result = 2; + LOG_TOPIC(WARN, arangodb::Logger::FIXME) << "Database version check failed for '" + << vocbase->name() << "': downgrade needed"; } - } - - // issue #391: when invoked with --database.auto-upgrade, the server will - // not always shut down - localContext->Exit(); + break; + default: + LOG_TOPIC(FATAL, arangodb::Logger::FIXME) << "Database version check failed for '" + << vocbase->name() + << "'. Please inspect the logs for any errors"; + FATAL_ERROR_EXIT_CODE(TRI_EXIT_VERSION_CHECK_FAILED); } - } - + }; + databaseFeature->enumerateDatabases(checkDBVersion); + LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "final result of version check: " << *_result; if (*_result == 1) { diff --git a/arangod/RestServer/CheckVersionFeature.h b/arangod/RestServer/CheckVersionFeature.h index 492f30b670..6a88749ea5 100644 --- a/arangod/RestServer/CheckVersionFeature.h +++ b/arangod/RestServer/CheckVersionFeature.h @@ -25,9 +25,26 @@ #include "ApplicationFeatures/ApplicationFeature.h" +struct TRI_vocbase_t; + namespace arangodb { + +class StorageEngine; + class CheckVersionFeature final : public application_features::ApplicationFeature { + private: + enum CheckVersionResult : int { + NO_SERVER_VERSION = -5, + NO_VERSION_FILE = -4, + CANNOT_READ_VERSION_FILE = -3, + CANNOT_PARSE_VERSION_FILE = -2, + IS_CLUSTER = -1, + VERSION_MATCH = 1, + DOWNGRADE_NEEDED = 2, + UPGRADE_NEEDED = 3, + }; + public: explicit CheckVersionFeature( application_features::ApplicationServer* server, int* result, @@ -44,6 +61,10 @@ class CheckVersionFeature final private: void checkVersion(); + CheckVersionResult checkVersionFileForDB(TRI_vocbase_t* vocbase, + StorageEngine* engine, + uint32_t currentVersion) const; + private: int* _result; std::vector _nonServerFeatures; diff --git a/arangod/RestServer/LockfileFeature.cpp b/arangod/RestServer/LockfileFeature.cpp index 3d092e2aed..b3c4c5458a 100644 --- a/arangod/RestServer/LockfileFeature.cpp +++ b/arangod/RestServer/LockfileFeature.cpp @@ -92,3 +92,7 @@ void LockfileFeature::start() { void LockfileFeature::unprepare() { TRI_DestroyLockFile(_lockFilename.c_str()); } + +void LockfileFeature::beginShutdown() { + TRI_DestroyLockFile(_lockFilename.c_str()); +} diff --git a/arangod/RestServer/LockfileFeature.h b/arangod/RestServer/LockfileFeature.h index f768728582..fdf50470c0 100644 --- a/arangod/RestServer/LockfileFeature.h +++ b/arangod/RestServer/LockfileFeature.h @@ -33,6 +33,7 @@ class LockfileFeature final : public application_features::ApplicationFeature { public: void start() override final; void unprepare() override final; + void beginShutdown() override final; private: std::string _lockFilename; diff --git a/arangod/V8Server/V8DealerFeature.cpp b/arangod/V8Server/V8DealerFeature.cpp index f2bdd7e2b6..a445d01c50 100644 --- a/arangod/V8Server/V8DealerFeature.cpp +++ b/arangod/V8Server/V8DealerFeature.cpp @@ -109,6 +109,7 @@ V8DealerFeature::V8DealerFeature( requiresElevatedPrivileges(false); startsAfter("Action"); startsAfter("Authentication"); + startsAfter("CheckVersion"); startsAfter("Database"); startsAfter("Random"); startsAfter("Scheduler");