#undef STATWRITER_NAMESPACE
FailoverTable::FailoverTable(size_t capacity)
- : max_entries(capacity), provider(true) {
+ : max_entries(capacity), erroneousEntriesErased(0), provider(true) {
createEntry(0);
cacheTableJSON();
}
FailoverTable::FailoverTable(const std::string& json, size_t capacity)
: max_entries(capacity),
+ erroneousEntriesErased(0),
provider(true) {
if (!loadFromJSON(json)) {
throw std::invalid_argument("FailoverTable(): unable to load from "
"JSON file '" + json + "'");
}
+ sanitizeFailoverTable();
}
FailoverTable::~FailoverTable() { }
void FailoverTable::createEntry(uint64_t high_seqno) {
LockHolder lh(lock);
+
// Our failover table represents only *our* branch of history.
// We must remove branches we've diverged from.
+ // Entries that we remove here are not erroneous entries because a
+ // diverged branch due to node(s) failure(s).
table.remove_if([high_seqno](const failover_entry_t& e) {
return (e.by_seqno > high_seqno);
});
failover_entry_t entry;
- entry.vb_uuid = (provider.next() >> 16);
+ /* In past we have seen some erroneous entries in failover table with
+ vb_uuid == 0 due to some bugs in the code which read/wrote the failover
+ table from/to the disk or due to a some unknown buggy code.
+ Hence we choose not to have 0 as a valid vb_uuid value. Loop below
+ regenerates the vb_uuid in case 0 is generated by random generator */
+ do {
+ entry.vb_uuid = (provider.next() >> 16);
+ } while(0 == entry.vb_uuid);
entry.by_seqno = high_seqno;
table.push_front(entry);
latest_uuid = entry.vb_uuid;
char statname[80] = {0};
checked_snprintf(statname, sizeof(statname), "vb_%d:num_entries", vbid);
add_casted_stat(statname, table.size(), add_stat, cookie);
+ checked_snprintf(statname, sizeof(statname),
+ "vb_%d:num_erroneous_entries_erased", vbid);
+ add_casted_stat(statname, table.size(), add_stat, cookie);
table_t::iterator it;
int entrycounter = 0;
snap_end_seqno = start_seqno;
}
}
+
+void FailoverTable::sanitizeFailoverTable()
+{
+ size_t intialTableSize = table.size();
+ for (auto itr = table.begin(); itr != table.end(); ) {
+ if (0 == itr->vb_uuid) {
+ /* 1. Prune entries with vb_uuid == 0. (From past experience we have
+ seen erroneous entries mostly have vb_uuid == 0, hence we have
+ chosen not to use 0 as valid vb_uuid) */
+ itr = table.erase(itr);
+ continue;
+ }
+ if (itr != table.begin()) {
+ auto prevItr = std::prev(itr);
+ if (itr->by_seqno > prevItr->by_seqno) {
+ /* 2. Prune any entry that has a by_seqno greater than by_seqno
+ of prev entry. (Entries are pushed at the head of the
+ table and must have seqno > seqno of following entries) */
+ itr = table.erase(itr);
+ continue;
+ }
+ }
+ ++itr;
+ }
+ erroneousEntriesErased += (intialTableSize - table.size());
+}
+
+size_t FailoverTable::getNumErroneousEntriesErased() const {
+ return erroneousEntriesErased;
+}
*/
size_t getNumEntries() const;
+ /**
+ * Returns total number of erroneous entries that were erased from the
+ * failover table.
+ *
+ * @return total number of entries
+ */
+ size_t getNumErroneousEntriesErased() const;
+
private:
bool loadFromJSON(cJSON *json);
uint64_t &snap_start_seqno,
uint64_t &snap_end_seqno);
+ /**
+ * Remove any wrong entries in failover table
+ *
+ * called only in ctor, hence does not grab lock
+ */
+ void sanitizeFailoverTable();
+
Mutex lock;
table_t table;
size_t max_entries;
+ size_t erroneousEntriesErased;
Couchbase::RandomGenerator provider;
std::string cachedTableJSON;
AtomicValue<uint64_t> latest_uuid;
cb_assert(table.getLatestEntry().by_seqno == max_seqno);
}
+static void test_sanitize_failover_table()
+{
+ const int numErroneousEntries = 4, numCorrectEntries = 2;
+ std::string failover_json(/* Erroneous entry */
+ "[{\"id\":0,\"seq\":0},"
+ "{\"id\":1356861809263,\"seq\":100},"
+ /* Erroneous entry */
+ "{\"id\":227813077095126,\"seq\":200},"
+ /* Erroneous entry */
+ "{\"id\":227813077095128,\"seq\":300},"
+ /* Erroneous entry */
+ "{\"id\":0,\"seq\":50},"
+ "{\"id\":160260368866392,\"seq\":0}]");
+ FailoverTable table(failover_json, 10 /* max_entries */);
+
+ cb_assert(numCorrectEntries == table.getNumEntries());
+ cb_assert(numErroneousEntries == table.getNumErroneousEntriesErased());
+}
+
int main(int argc, char **argv) {
(void)argc;
(void)argv;
test_edgetests_failover_log();
test_add_entry();
test_max_capacity();
+ test_sanitize_failover_table();
return 0;
}