diff --git a/UnitTests/Makefile.unittests b/UnitTests/Makefile.unittests index 8ef5689f3d..6dd3bf9178 100755 --- a/UnitTests/Makefile.unittests +++ b/UnitTests/Makefile.unittests @@ -208,6 +208,7 @@ unittests-recovery: @echo "================================================================================" @echo + $(MAKE) execute-recovery-test PID=$(PID) RECOVERY_SCRIPT="collection-properties" $(MAKE) execute-recovery-test PID=$(PID) RECOVERY_SCRIPT="empty-logfiles" $(MAKE) execute-recovery-test PID=$(PID) RECOVERY_SCRIPT="many-logs" $(MAKE) execute-recovery-test PID=$(PID) RECOVERY_SCRIPT="multiple-logs" diff --git a/arangod/Wal/RecoverState.cpp b/arangod/Wal/RecoverState.cpp index a3bb4712b0..fd423d3c58 100644 --- a/arangod/Wal/RecoverState.cpp +++ b/arangod/Wal/RecoverState.cpp @@ -932,6 +932,85 @@ bool RecoverState::ReplayMarker (TRI_df_marker_t const* marker, state->runningRemoteTransactions.insert(std::make_pair(m->_externalId, trx)); break; } + + + case TRI_WAL_MARKER_RENAME_COLLECTION: { + break; + } + + case TRI_WAL_MARKER_CHANGE_COLLECTION: { + collection_change_marker_t const* m = reinterpret_cast(marker); + TRI_voc_cid_t collectionId = m->_collectionId; + TRI_voc_tick_t databaseId = m->_databaseId; + + if (state->isDropped(databaseId)) { + return true; + } + + TRI_vocbase_t* vocbase = state->useDatabase(databaseId); + + if (vocbase == nullptr) { + // if the underlying database is gone, we can go on + LOG_TRACE("cannot open database %llu", (unsigned long long) databaseId); + return true; + } + + TRI_document_collection_t* document = state->getCollection(databaseId, collectionId); + + if (document == nullptr) { + // if the underlying collection is gone, we can go on + LOG_TRACE("cannot change properties of collection %llu in database %llu: %s", + (unsigned long long) collectionId, + (unsigned long long) databaseId, + TRI_errno_string(TRI_ERROR_ARANGO_COLLECTION_NOT_FOUND)); + return true; + } + + char const* properties = reinterpret_cast(m) + sizeof(collection_change_marker_t); + TRI_json_t* json = triagens::basics::JsonHelper::fromString(properties); + + if (! TRI_IsArrayJson(json)) { + if (json != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + } + LOG_WARNING("cannot unpack collection properties for collection %llu in database %llu", + (unsigned long long) collectionId, + (unsigned long long) databaseId); + return state->canContinue(); + } + + TRI_json_t const* value; + + TRI_col_info_t parameters; + parameters._doCompact = true; + parameters._waitForSync = vocbase->_settings.defaultWaitForSync; + parameters._maximalSize = vocbase->_settings.defaultMaximalSize; + + value = TRI_LookupArrayJson(json, "doCompact"); + if (TRI_IsBooleanJson(value)) { + parameters._doCompact = value->_value._boolean; + } + + value = TRI_LookupArrayJson(json, "waitForSync"); + if (TRI_IsBooleanJson(value)) { + parameters._waitForSync = value->_value._boolean; + } + + value = TRI_LookupArrayJson(json, "maximalSize"); + if (TRI_IsNumberJson(value)) { + parameters._maximalSize = static_cast(value->_value._number); + } + + int res = TRI_UpdateCollectionInfo(vocbase, document, ¶meters); + if (res != TRI_ERROR_NO_ERROR) { + LOG_WARNING("cannot change collection properties for collection %llu in database %llu: %s", + (unsigned long long) collectionId, + (unsigned long long) databaseId, + TRI_errno_string(res)); + return state->canContinue(); + } + break; + } // ----------------------------------------------------------------------------- @@ -973,7 +1052,19 @@ bool RecoverState::ReplayMarker (TRI_df_marker_t const* marker, char const* properties = reinterpret_cast(m) + sizeof(index_create_marker_t); TRI_json_t* json = triagens::basics::JsonHelper::fromString(properties); - if (json == nullptr) { + if (! TRI_IsArrayJson(json)) { + if (json != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + } + LOG_WARNING("cannot unpack index properties for index %llu, collection %llu in database %llu", + (unsigned long long) indexId, + (unsigned long long) collectionId, + (unsigned long long) databaseId); + return state->canContinue(); + } + + if (! TRI_IsArrayJson(json)) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); LOG_WARNING("cannot unpack index properties for index %llu, collection %llu in database %llu", (unsigned long long) indexId, (unsigned long long) collectionId, @@ -1045,7 +1136,10 @@ bool RecoverState::ReplayMarker (TRI_df_marker_t const* marker, char const* properties = reinterpret_cast(m) + sizeof(collection_create_marker_t); TRI_json_t* json = triagens::basics::JsonHelper::fromString(properties); - if (json == nullptr) { + if (! TRI_IsArrayJson(json)) { + if (json != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + } LOG_WARNING("cannot unpack collection properties for collection %llu in database %llu", (unsigned long long) collectionId, (unsigned long long) databaseId); @@ -1110,7 +1204,10 @@ bool RecoverState::ReplayMarker (TRI_df_marker_t const* marker, char const* properties = reinterpret_cast(m) + sizeof(database_create_marker_t); TRI_json_t* json = triagens::basics::JsonHelper::fromString(properties); - if (json == nullptr) { + if (! TRI_IsArrayJson(json)) { + if (json != nullptr) { + TRI_FreeJson(TRI_UNKNOWN_MEM_ZONE, json); + } LOG_WARNING("cannot unpack database properties for database %llu", (unsigned long long) databaseId); return state->canContinue(); } diff --git a/js/server/tests/recovery/collection-properties.js b/js/server/tests/recovery/collection-properties.js new file mode 100644 index 0000000000..b76984f126 --- /dev/null +++ b/js/server/tests/recovery/collection-properties.js @@ -0,0 +1,85 @@ + +var db = require("org/arangodb").db; +var internal = require("internal"); +var jsunity = require("jsunity"); + + +function runSetup () { + internal.debugClearFailAt(); + var c; + + db._drop("UnitTestsRecovery1"); + c = db._create("UnitTestsRecovery1"); + c.properties({ waitForSync: true, journalSize: 8 * 1024 * 1024, doCompact: false }); + + db._drop("UnitTestsRecovery2"); + c = db._create("UnitTestsRecovery2"); + c.properties({ waitForSync: false, journalSize: 16 * 1024 * 1024, doCompact: true }); + + db._drop("UnitTestsRecovery3"); + c = db._create("UnitTestsRecovery3"); + c.properties({ waitForSync: false, journalSize: 16 * 1024 * 1024, doCompact: true }); + c.properties({ waitForSync: true, journalSize: 4 * 1024 * 1024, doCompact: false }); + + c.save({ _key: "foo" }, true); + + internal.debugSegfault("crashing server"); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test suite +//////////////////////////////////////////////////////////////////////////////// + +function recoverySuite () { + jsunity.jsUnity.attachAssertions(); + + return { + setUp: function () { + }, + tearDown: function () { + }, + +//////////////////////////////////////////////////////////////////////////////// +/// @brief test whether we can restore the trx data +//////////////////////////////////////////////////////////////////////////////// + + testCollectionProperties : function () { + var c, prop; + + c = db._collection("UnitTestsRecovery1"); + prop = c.properties(); + assertTrue(prop.waitForSync); + assertEqual(8 * 1024 * 1024, prop.journalSize); + assertFalse(prop.doCompact); + + c = db._collection("UnitTestsRecovery2"); + prop = c.properties(); + assertFalse(prop.waitForSync); + assertEqual(16 * 1024 * 1024, prop.journalSize); + assertTrue(prop.doCompact); + + c = db._collection("UnitTestsRecovery3"); + prop = c.properties(); + assertTrue(prop.waitForSync); + assertEqual(4 * 1024 * 1024, prop.journalSize); + assertFalse(prop.doCompact); + } + + }; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief executes the test suite +//////////////////////////////////////////////////////////////////////////////// + +function main (argv) { + if (argv[1] === "setup") { + runSetup(); + return 0; + } + else { + jsunity.run(recoverySuite); + return jsunity.done() ? 0 : 1; + } +} +