1
0
Fork 0
arangodb/js/server/arango-dfdb.js

593 lines
16 KiB
JavaScript

////////////////////////////////////////////////////////////////////////////////
/// @brief arango datafile debugger
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2012 triAGENS 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 triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
var internal = require("internal");
var console = require("console");
var fs = require("fs");
var printf = internal.printf;
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup ArangoDB
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief remove datafile
////////////////////////////////////////////////////////////////////////////////
function RemoveDatafile (collection, type, datafile) {
var backup = datafile + ".corrupt";
fs.move(datafile, backup);
printf("Removed %s at %s\n", type, datafile);
printf("Backup is in %s\n", backup);
printf("\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief wipe entries
////////////////////////////////////////////////////////////////////////////////
function WipeDatafile (collection, type, datafile, lastGoodPos) {
collection.truncateDatafile(datafile, lastGoodPos);
printf("Truncated and sealed datafile\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief queries user to wipe a datafile
////////////////////////////////////////////////////////////////////////////////
function QueryWipeDatafile (collection, type, datafile, scan, lastGoodPos) {
var entries = scan.entries;
if (entries.length == 0) {
if (type === "journal" || type === "compactor") {
printf("WARNING: The journal is empty. Even the header is missing. Going\n");
printf(" to remove the file.\n");
}
else {
printf("WARNING: The datafile is empty. Even the header is missing. Going\n");
printf(" to remove the datafile. This should never happen. Datafiles\n");
printf(" are append-only. Make sure your hard disk does not contain\n");
printf(" any hardware errors.\n");
}
printf("\n");
RemoveDatafile(collection, type, datafile);
return;
}
var ask = true;
if (type === "journal") {
if (entries.length === lastGoodPos + 3 && entries[lastGoodPos + 2].status === 2) {
printf("WARNING: The journal was not closed properly, the last entry is corrupted.\n");
printf(" This might happen ArangoDB was killed and the last entry was not\n");
printf(" fully written to disk. Going to remove the last entry.\n");
ask = false;
}
else {
printf("WARNING: The journal was not closed properly, the last entries are corrupted.\n");
printf(" This might happen ArangoDB was killed and the last entries were not\n");
printf(" fully written to disk.\n");
}
}
else {
printf("WARNING: The datafile contains corrupt entries. This should never happen.\n");
printf(" Datafiles are append-only. Make sure your hard disk does not contain\n");
printf(" any hardware errors.\n");
}
printf("\n");
if (ask) {
printf("Wipe the last entries (Y/N)? ");
var line = console.getline();
if (line !== "yes" && line !== "YES" && line !== "y" && line !== "Y") {
printf("ABORTING\n");
return;
}
}
var entry = entries[lastGoodPos];
WipeDatafile(collection, type, datafile, entry.position + entry.size);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief prints details about entries
////////////////////////////////////////////////////////////////////////////////
function PrintEntries (entries, amount) {
var start, end;
if (amount > 0) {
start = 0;
end = amount;
if (end > entries.length) {
end = entries.length;
}
}
else {
start = entries.length + amount - 1;
if (start < 0) {
return;
}
if (start < Math.abs(amount)) {
return;
}
end = entries.length;
}
for (var i = start; i < end; ++i) {
var entry = entries[i];
var s = "unknown";
switch (entry.status) {
case 1: s = "OK"; break;
case 2: s = "OK (end)"; break;
case 3: s = "FAILED (empty)"; break;
case 4: s = "FAILED (too small)"; break;
case 5: s = "FAILED (crc mismatch)"; break;
}
printf(" %d: status %s type %d size %d, tick %s\n", i, s, entry.type, entry.size, entry.tick);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks a datafile deeply
////////////////////////////////////////////////////////////////////////////////
function DeepCheckDatafile (collection, type, datafile, scan, details) {
var entries = scan.entries;
printf("Entries\n");
var lastGood = 0;
var lastGoodPos = 0;
var stillGood = true;
if (details) {
// print details
PrintEntries(entries, 10);
PrintEntries(entries, -10);
}
for (var i = 0; i < entries.length; ++i) {
var entry = entries[i];
if (entry.status === 1 || entry.status === 2) {
if (stillGood) {
lastGood = entry;
lastGoodPos = i;
}
}
else {
stillGood = false;
}
}
if (! stillGood) {
printf(" Last good position: %d\n", lastGood.position + lastGood.size);
printf("\n");
QueryWipeDatafile(collection, type, datafile, scan, lastGoodPos);
}
printf("\n");
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks a datafile
////////////////////////////////////////////////////////////////////////////////
function CheckDatafile (collection, type, datafile, issues, details) {
printf("Datafile\n");
printf(" path: %s\n", datafile);
printf(" type: %s\n", type);
var scan = collection.datafileScan(datafile);
printf(" current size: %d\n", scan.currentSize);
printf(" maximal size: %d\n", scan.maximalSize);
printf(" total used: %d\n", scan.endPosition);
printf(" # of entries: %d\n", scan.numberMarkers);
printf(" status: %d\n", scan.status);
printf(" isSealed: %s\n", scan.isSealed ? "yes" : "no");
// set default value to unknown
var statusMessage = "UNKNOWN (" + scan.status + ")";
var color = internal.COLORS.COLOR_YELLOW;
switch (scan.status) {
case 1:
statusMessage = "OK";
color = internal.COLORS.COLOR_GREEN;
if (! scan.isSealed && type === "datafile") {
color = internal.COLORS.COLOR_YELLOW;
}
break;
case 2:
statusMessage = "NOT OK (reached empty marker)";
color = internal.COLORS.COLOR_RED;
break;
case 3:
statusMessage = "FATAL (reached corrupt marker)";
color = internal.COLORS.COLOR_RED;
break;
case 4:
statusMessage = "FATAL (crc failed)";
color = internal.COLORS.COLOR_RED;
break;
case 5:
statusMessage = "FATAL (cannot open datafile or too small)";
color = internal.COLORS.COLOR_RED;
break;
}
printf(color);
printf(" status: %s\n\n", statusMessage);
printf(internal.COLORS.COLOR_RESET);
if (scan.status !== 1) {
issues.push({
collection: collection.name(),
path: datafile,
type: type,
status: scan.status,
message: statusMessage,
color: color
});
}
if (scan.numberMarkers === 0) {
statusMessage = "datafile contains no entries";
color = internal.COLORS.COLOR_YELLOW;
issues.push({
collection: collection.name(),
path: datafile,
type: type,
status: scan.status,
message: statusMessage,
color: color
});
printf(color);
printf("WARNING: %s\n", statusMessage);
printf(internal.COLORS.COLOR_RESET);
RemoveDatafile(collection, type, datafile);
return;
}
if (scan.entries[0].type !== 1000) {
// asserting a TRI_DF_MARKER_HEADER as first marker
statusMessage = "datafile contains no datafile header marker at pos #0!";
color = internal.COLORS.COLOR_YELLOW;
issues.push({
collection: collection.name(),
path: datafile,
type: type,
status: scan.status,
message: statusMessage,
color: color
});
printf(color);
printf("WARNING: %s\n", statusMessage);
printf(internal.COLORS.COLOR_RESET);
RemoveDatafile(collection, type, datafile);
return;
}
if (scan.entries.length === 2 && scan.entries[1].type !== 2000) {
// asserting a TRI_COL_MARKER_HEADER as second marker
statusMessage = "datafile contains no collection header marker at pos #1!";
color = internal.COLORS.COLOR_YELLOW;
issues.push({
collection: collection.name(),
path: datafile,
type: type,
status: scan.status,
message: statusMessage,
color: color
});
printf(color);
printf("WARNING: %s\n", statusMessage);
printf(internal.COLORS.COLOR_RESET);
RemoveDatafile(collection, type, datafile);
return;
}
if (type !== "journal" && scan.entries.length === 3 && scan.entries[2].type === 0) {
// got the two initial header markers but nothing else...
statusMessage = "datafile is empty but not sealed";
color = internal.COLORS.COLOR_YELLOW;
issues.push({
collection: collection.name(),
path: datafile,
type: type,
status: scan.status,
message: statusMessage,
color: color
});
printf(color);
printf("WARNING: %s\n", statusMessage);
printf(internal.COLORS.COLOR_RESET);
RemoveDatafile(collection, type, datafile);
return;
}
if (scan.status === 1 && scan.isSealed) {
return;
}
DeepCheckDatafile(collection, type, datafile, scan, details);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief checks a collection
////////////////////////////////////////////////////////////////////////////////
function CheckCollection (collection, issues, details) {
printf("Database\n");
printf(" name: %s\n", internal.db._name());
printf(" path: %s\n", internal.db._path());
printf("\n");
printf("Collection\n");
printf(" name: %s\n", collection.name());
printf(" identifier: %s\n", collection._id);
printf("\n");
var datafiles = collection.datafiles();
printf("Datafiles\n");
printf(" # of journals: %d\n", datafiles.journals.length);
printf(" # of compactors: %d\n", datafiles.compactors.length);
printf(" # of datafiles: %d\n", datafiles.datafiles.length);
printf("\n");
for (var i = 0; i < datafiles.journals.length; ++i) {
CheckDatafile(collection, "journal", datafiles.journals[i], issues, details);
}
for (var i = 0; i < datafiles.datafiles.length; ++i) {
CheckDatafile(collection, "datafile", datafiles.datafiles[i], issues, details);
}
for (var i = 0; i < datafiles.compactors.length; ++i) {
CheckDatafile(collection, "compactor", datafiles.compactors[i], issues, details);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief select and check a collection
////////////////////////////////////////////////////////////////////////////////
function main (argv) {
var databases = internal.db._listDatabases();
var i;
var collectionSorter = function (l, r) {
var lName = l.name().toLowerCase();
var rName = r.name().toLowerCase();
if (lName !== rName) {
return lName < rName ? -1 : 1;
}
return 0;
};
printf("%s\n", " ___ _ __ _ _ ___ ___ ___ ");
printf("%s\n", " / \\__ _| |_ __ _ / _(_) | ___ / \\/ __\\ / _ \\");
printf("%s\n", " / /\\ / _` | __/ _` | |_| | |/ _ \\ / /\\ /__\\// / /_\\/");
printf("%s\n", " / /_// (_| | || (_| | _| | | __/ / /_// \\/ \\/ /_\\\\ ");
printf("%s\n", "/___,' \\__,_|\\__\\__,_|_| |_|_|\\___| /___,'\\_____/\\____/ ");
printf("\n");
if (databases.length == 0) {
printf("No databases available. Exiting\n");
return;
}
databases.sort();
printf("Available databases:\n");
for (i = 0; i < databases.length; ++i) {
printf(" %d: %s\n", i, databases[i]);
}
var line;
printf("Database to check: ");
while (true) {
line = console.getline();
if (line == "") {
printf("Exiting. Please wait.\n");
return;
}
else {
var l = parseInt(line);
if (l < 0 || l >= databases.length || l === null || l === undefined || isNaN(l)) {
printf("Please select a number between 0 and %d: ", databases.length - 1);
}
else {
internal.db._useDatabase(databases[l]);
break;
}
}
}
printf("\n");
var collections = internal.db._collections();
if (collections.length == 0) {
printf("No collections available. Exiting\n");
return;
}
// sort the collections
collections.sort(collectionSorter);
printf("Available collections:\n");
for (i = 0; i < collections.length; ++i) {
printf(" %d: %s\n", i, collections[i].name());
}
printf(" *: all\n");
printf("\n");
printf("Collection to check: ");
var a = [];
while (true) {
line = console.getline();
if (line == "") {
printf("Exiting. Please wait.\n");
return;
}
if (line === "*") {
for (i = 0; i < collections.length; ++i) {
a.push(i);
}
break;
}
else {
var l = parseInt(line);
if (l < 0 || l >= collections.length || l === null || l === undefined || isNaN(l)) {
printf("Please select a number between 0 and %d: ", collections.length - 1);
}
else {
a.push(l);
break;
}
}
}
printf("\n");
printf("Prints details (yes/no)? ");
var details = false;
while (true) {
line = console.getline();
if (line === "") {
printf("Exiting. Please wait.\n");
return;
}
if (line === "yes" || line === "YES" || line === "y" || line === "Y") {
details = true;
}
break;
}
var issues = [ ];
for (i = 0; i < a.length; ++i) {
var collection = collections[a[i]];
printf("Checking collection #%d: %s\n", a[i], collection.name());
printf("-------------------------------------------------------------------\n");
var last = Math.round(internal.time());
// unload all collections not yet unloaded (2) & not corrupted (0)
while (collection.status() !== 2 && collection.status() !== 0) {
collection.unload();
var next = Math.round(internal.time());
if (next != last) {
printf("Trying to unload collection '%s'\n", collection.name());
last = next;
}
}
printf("\n");
CheckCollection(collection, issues, details);
}
if (issues.length > 0) {
// report issues
printf("\n%d issue(s) found:\n------------------------------------------\n", issues.length);
for (i = 0; i < issues.length; ++i) {
var issue = issues[i];
printf(" issue #%d\n collection: %s\n path: %s\n type: %s\n",
i,
issue.collection,
issue.path,
String(issue.type));
printf("%s", issue.color);
printf(" message: %s", issue.message);
printf("%s", internal.COLORS.COLOR_RESET);
printf("\n\n");
}
}
else {
printf("\nNo issues found.\n");
}
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End: