More bug fixes in handling upperBound changes in modified pages and worst-case delta size calculation. Normalized some formatting in debug statements. Fixed compile error on linux. Updated test specs.
This commit is contained in:
parent
6aad34620d
commit
100789b354
|
@ -581,7 +581,7 @@ struct RedwoodRecordRef {
|
||||||
return compare(rhs) >= 0;
|
return compare(rhs) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int deltaSize(const RedwoodRecordRef &base) const {
|
int deltaSize(const RedwoodRecordRef &base, bool worstCase = true) const {
|
||||||
int size = sizeof(Delta);
|
int size = sizeof(Delta);
|
||||||
|
|
||||||
if(value.present()) {
|
if(value.present()) {
|
||||||
|
@ -590,19 +590,22 @@ struct RedwoodRecordRef {
|
||||||
}
|
}
|
||||||
|
|
||||||
int prefixLen = getCommonPrefixLen(base, 0);
|
int prefixLen = getCommonPrefixLen(base, 0);
|
||||||
size += (prefixLen >= 128) ? 2 : 1;
|
size += (worstCase || prefixLen >= 128) ? 2 : 1;
|
||||||
|
|
||||||
int intFieldPrefixLen;
|
int intFieldPrefixLen;
|
||||||
|
|
||||||
// Currently using a worst-guess guess where int fields in suffix are stored in their entirety if nonzero.
|
// Currently using a worst-guess guess where int fields in suffix are stored in their entirety if nonzero.
|
||||||
if(prefixLen < key.size()) {
|
if(prefixLen < key.size()) {
|
||||||
int keySuffixLen = key.size() - prefixLen;
|
int keySuffixLen = key.size() - prefixLen;
|
||||||
size += (keySuffixLen >= 128) ? 2 : 1;
|
size += (worstCase || keySuffixLen >= 128) ? 2 : 1;
|
||||||
size += keySuffixLen;
|
size += keySuffixLen;
|
||||||
intFieldPrefixLen = 0;
|
intFieldPrefixLen = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
intFieldPrefixLen = prefixLen - key.size();
|
intFieldPrefixLen = prefixLen - key.size();
|
||||||
|
if(worstCase) {
|
||||||
|
size += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(version == 0 && chunk.total == 0 && chunk.start == 0) {
|
if(version == 0 && chunk.total == 0 && chunk.start == 0) {
|
||||||
|
@ -705,11 +708,11 @@ struct RedwoodRecordRef {
|
||||||
std::string toString(int hexLimit = 15) const {
|
std::string toString(int hexLimit = 15) const {
|
||||||
std::string r;
|
std::string r;
|
||||||
r += format("'%s'@%" PRId64, kvformat(key, hexLimit).c_str(), version);
|
r += format("'%s'@%" PRId64, kvformat(key, hexLimit).c_str(), version);
|
||||||
r += format("[%d/%d]->", chunk.start, chunk.total);
|
r += format("[%u/%u]->", chunk.start, chunk.total);
|
||||||
if(value.present()) {
|
if(value.present()) {
|
||||||
// Assume that values the size of a page ID are page IDs. It's not perfect but it's just for debugging.
|
// Assume that values the size of a page ID are page IDs. It's not perfect but it's just for debugging.
|
||||||
if(value.get().size() == sizeof(LogicalPageID)) {
|
if(value.get().size() == sizeof(LogicalPageID)) {
|
||||||
r += format("[Page id=%u]", getPageID());
|
r += format("[PageID=%u]", getPageID());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
r += format("'%s'", kvformat(value.get(), hexLimit).c_str());
|
r += format("'%s'", kvformat(value.get(), hexLimit).c_str());
|
||||||
|
@ -773,14 +776,26 @@ struct BTreePage {
|
||||||
c.moveFirst();
|
c.moveFirst();
|
||||||
ASSERT(c.valid());
|
ASSERT(c.valid());
|
||||||
|
|
||||||
|
bool anyOutOfRange = false;
|
||||||
do {
|
do {
|
||||||
r += " ";
|
r += " ";
|
||||||
r += c.get().toString();
|
r += c.get().toString();
|
||||||
|
|
||||||
|
bool tooLow = c.get().key < lowerBound->key;
|
||||||
|
bool tooHigh = c.get().key > upperBound->key;
|
||||||
|
if(tooLow || tooHigh) {
|
||||||
|
anyOutOfRange = true;
|
||||||
|
if(tooLow) {
|
||||||
|
r += " (too low)";
|
||||||
|
}
|
||||||
|
if(tooHigh) {
|
||||||
|
r += " (too high)";
|
||||||
|
}
|
||||||
|
}
|
||||||
r += "\n";
|
r += "\n";
|
||||||
|
|
||||||
ASSERT(c.get().key >= lowerBound->key && c.get().key <= upperBound->key);
|
|
||||||
|
|
||||||
} while(c.moveNext());
|
} while(c.moveNext());
|
||||||
|
ASSERT(!anyOutOfRange);
|
||||||
}
|
}
|
||||||
} catch (Error& e) {
|
} catch (Error& e) {
|
||||||
debug_printf("BTreePage::toString ERROR: %s\n", e.what());
|
debug_printf("BTreePage::toString ERROR: %s\n", e.what());
|
||||||
|
@ -1310,24 +1325,32 @@ private:
|
||||||
// This is only done if modified is set to avoid rewriting this page for this purpose only.
|
// This is only done if modified is set to avoid rewriting this page for this purpose only.
|
||||||
//
|
//
|
||||||
// After this call, lastUpperBound is internal page's upper bound.
|
// After this call, lastUpperBound is internal page's upper bound.
|
||||||
void finalize(const RedwoodRecordRef &newUpperBound) {
|
void finalize(const RedwoodRecordRef &upperBound, const RedwoodRecordRef &decodeUpperBound) {
|
||||||
if(!modified) {
|
debug_printf("InternalPageBuilder::end modified=%d upperBound=%s decodeUpperBound=%s lastUpperBound=%s\n", modified, upperBound.toString().c_str(), decodeUpperBound.toString().c_str(), lastUpperBound.toString().c_str());
|
||||||
// If the original set has any more entries then the page content has been modified.
|
modified = modified || cursor.valid();
|
||||||
if(cursor.valid()) {
|
debug_printf("InternalPageBuilder::end modified=%d after cursor check\n", modified);
|
||||||
|
|
||||||
|
// If there are boundary key entries and the last one has a child page then the
|
||||||
|
// upper bound for this internal page must match the required upper bound for
|
||||||
|
// the last child entry.
|
||||||
|
if(!entries.empty() && entries.back().value.present()) {
|
||||||
|
debug_printf("InternalPageBuilder::end last entry is not null\n");
|
||||||
|
|
||||||
|
// If the page contents were not modified so far and the upper bound required
|
||||||
|
// for the last child page (lastUpperBound) does not match what the page
|
||||||
|
// was encoded with then the page must be modified.
|
||||||
|
if(!modified && lastUpperBound != decodeUpperBound) {
|
||||||
|
debug_printf("InternalPageBuilder::end modified set true because lastUpperBound does not match decodeUpperBound\n");
|
||||||
modified = true;
|
modified = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
debug_printf("InternalPageBuilder: finalizing. modified=%d newUpperBound=%s\n", modified, newUpperBound.toString().c_str());
|
if(modified && lastUpperBound != upperBound) {
|
||||||
if(modified) {
|
debug_printf("InternalPageBuilder::end Modified is true but lastUpperBound does not match upperBound so adding placeholder\n");
|
||||||
if(!entries.empty() && entries.back().value.present() && lastUpperBound != newUpperBound) {
|
|
||||||
debug_printf("InternalPageBuilder: Added placeholder %s\n", lastUpperBound.withoutValue().toString().c_str());
|
|
||||||
addEntry(lastUpperBound.withoutValue());
|
addEntry(lastUpperBound.withoutValue());
|
||||||
|
lastUpperBound = upperBound;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastUpperBound = newUpperBound;
|
|
||||||
debug_printf("InternalPageBuilder: New upper bound: %s\n", lastUpperBound.toString().c_str());
|
|
||||||
}
|
}
|
||||||
|
debug_printf("InternalPageBuilder::end exit. modified=%d upperBound=%s decodeUpperBound=%s lastUpperBound=%s\n", modified, upperBound.toString().c_str(), decodeUpperBound.toString().c_str(), lastUpperBound.toString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
BTreePage::BinaryTree::Cursor cursor;
|
BTreePage::BinaryTree::Cursor cursor;
|
||||||
|
@ -1344,8 +1367,7 @@ private:
|
||||||
return o.toString();
|
return o.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
static std::string toString(const VersionedChildPageSet &c) {
|
||||||
std::string toString(const VersionedChildPageSet &c) {
|
|
||||||
return format("Version=%" PRId64 " children=%s upperBound=%s", c.version, toString(c.children).c_str(), c.upperBound.toString().c_str());
|
return format("Version=%" PRId64 " children=%s upperBound=%s", c.version, toString(c.children).c_str(), c.upperBound.toString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1581,7 +1603,7 @@ private:
|
||||||
|
|
||||||
for(int e = 0, eEnd = extPages.size(); e < eEnd; ++e) {
|
for(int e = 0, eEnd = extPages.size(); e < eEnd; ++e) {
|
||||||
LogicalPageID eid = m_pager->allocateLogicalPage();
|
LogicalPageID eid = m_pager->allocateLogicalPage();
|
||||||
debug_printf("%p: writePages(): Writing extension page op=write id=%u @%" PRId64 " (%d of %lu) referencePage=%u\n", actor_debug, eid, version, e + 1, extPages.size(), id);
|
debug_printf("%p: writePages(): Writing extension page op=write id=%u @%" PRId64 " (%d of %lu) referencePageID=%u\n", actor_debug, eid, version, e + 1, extPages.size(), id);
|
||||||
newPage->extensionPages[e] = bigEndian32(eid);
|
newPage->extensionPages[e] = bigEndian32(eid);
|
||||||
// If replacing the primary page below (version == 0) then pass the primary page's ID as the reference page ID
|
// If replacing the primary page below (version == 0) then pass the primary page's ID as the reference page ID
|
||||||
m_pager->writePage(eid, extPages[e], version, (version == 0) ? id : invalidLogicalPageID);
|
m_pager->writePage(eid, extPages[e], version, (version == 0) ? id : invalidLogicalPageID);
|
||||||
|
@ -1674,7 +1696,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(result->userData == nullptr) {
|
if(result->userData == nullptr) {
|
||||||
debug_printf("readPage() Creating Reader for page id=%u @%" PRId64 " lower=%s upper=%s\n", id, snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str());
|
debug_printf("readPage() Creating Reader for PageID=%u @%" PRId64 " lower=%s upper=%s\n", id, snapshot->getVersion(), lowerBound->toString().c_str(), upperBound->toString().c_str());
|
||||||
result->userData = new BTreePage::BinaryTree::Reader(&pTreePage->tree(), lowerBound, upperBound);
|
result->userData = new BTreePage::BinaryTree::Reader(&pTreePage->tree(), lowerBound, upperBound);
|
||||||
result->userDataDestructor = [](void *ptr) { delete (BTreePage::BinaryTree::Reader *)ptr; };
|
result->userDataDestructor = [](void *ptr) { delete (BTreePage::BinaryTree::Reader *)ptr; };
|
||||||
}
|
}
|
||||||
|
@ -1995,7 +2017,7 @@ private:
|
||||||
|
|
||||||
const RedwoodRecordRef &childUpperBound = cursor.valid() ? cursor.get() : *upperBound;
|
const RedwoodRecordRef &childUpperBound = cursor.valid() ? cursor.get() : *upperBound;
|
||||||
|
|
||||||
debug_printf("%s recursing to page id=%u lower=%s upper=%s decodeLower=%s decodeUpper=%s\n",
|
debug_printf("%s recursing to PageID=%u lower=%s upper=%s decodeLower=%s decodeUpper=%s\n",
|
||||||
context.c_str(), pageID, childLowerBound.toString().c_str(), childUpperBound.toString().c_str(), decodeChildLowerBound.toString().c_str(), decodeChildUpperBound.toString().c_str());
|
context.c_str(), pageID, childLowerBound.toString().c_str(), childUpperBound.toString().c_str(), decodeChildLowerBound.toString().c_str(), decodeChildUpperBound.toString().c_str());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2039,10 +2061,10 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if(REDWOOD_DEBUG) {
|
if(REDWOOD_DEBUG) {
|
||||||
debug_printf("%s Subtree update results for root Page id=%u\n", context.c_str(), root);
|
debug_printf("%s Subtree update results for root PageID=%u\n", context.c_str(), root);
|
||||||
for(int i = 0; i < futureChildren.size(); ++i) {
|
for(int i = 0; i < futureChildren.size(); ++i) {
|
||||||
const VersionedChildrenT &vc = futureChildren[i].get();
|
const VersionedChildrenT &vc = futureChildren[i].get();
|
||||||
debug_printf("%s subtree result parent=%u %s\n", context.c_str(), root, toString(vc).c_str());
|
debug_printf("%s subtree result %s\n", context.c_str(), toString(vc).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2060,7 +2082,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pageBuilder.finalize(*upperBound);
|
pageBuilder.finalize(*upperBound, *decodeUpperBound);
|
||||||
|
|
||||||
// If page contents have changed
|
// If page contents have changed
|
||||||
if(pageBuilder.modified) {
|
if(pageBuilder.modified) {
|
||||||
|
@ -2083,7 +2105,7 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
debug_printf("%s Internal page id=%u modified, creating replacements.\n", context.c_str(), root);
|
debug_printf("%s Internal PageID=%u modified, creating replacements.\n", context.c_str(), root);
|
||||||
debug_printf("%s newChildren=%s lastUpperBound=%s upperBound=%s\n", context.c_str(), toString(pageBuilder.entries).c_str(), pageBuilder.lastUpperBound.toString().c_str(), upperBound->toString().c_str());
|
debug_printf("%s newChildren=%s lastUpperBound=%s upperBound=%s\n", context.c_str(), toString(pageBuilder.entries).c_str(), pageBuilder.lastUpperBound.toString().c_str(), upperBound->toString().c_str());
|
||||||
|
|
||||||
ASSERT(pageBuilder.lastUpperBound == *upperBound);
|
ASSERT(pageBuilder.lastUpperBound == *upperBound);
|
||||||
|
@ -2114,13 +2136,13 @@ private:
|
||||||
c.children.push_back(pages[i].lowerBound.withPageID(newPageIDs[i]));
|
c.children.push_back(pages[i].lowerBound.withPageID(newPageIDs[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_printf("%s Internal Page id=%u modified, returning %s\n", context.c_str(), root, toString(c).c_str());
|
debug_printf("%s Internal PageID=%u modified, returning %s\n", context.c_str(), root, toString(c).c_str());
|
||||||
return vc;
|
return vc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
VersionedChildrenT c( { {0, {*decodeLowerBound}, *decodeUpperBound} });
|
VersionedChildrenT c( { {0, {*decodeLowerBound}, *decodeUpperBound} });
|
||||||
debug_printf("%s Page id=%u has no changes, returning %s\n", context.c_str(), root, toString(c).c_str());
|
debug_printf("%s PageID=%u has no changes, returning %s\n", context.c_str(), root, toString(c).c_str());
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2223,7 +2245,7 @@ printf("\nCommitted: %s\n", self->counts.toString(true).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
return format("Page %lu, %s", pageID, cursor.valid() ? cursor.get().toString().c_str() : "<invalid>");
|
return format("PageID=%u, %s", pageID, cursor.valid() ? cursor.get().toString().c_str() : "<invalid>");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3251,7 +3273,7 @@ void deltaTest(RedwoodRecordRef rec, RedwoodRecordRef base) {
|
||||||
RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)buf;
|
RedwoodRecordRef::Delta &d = *(RedwoodRecordRef::Delta *)buf;
|
||||||
|
|
||||||
Arena mem;
|
Arena mem;
|
||||||
int expectedSize = rec.deltaSize(base);
|
int expectedSize = rec.deltaSize(base, false);
|
||||||
int deltaSize = rec.writeDelta(d, base);
|
int deltaSize = rec.writeDelta(d, base);
|
||||||
RedwoodRecordRef decoded = d.apply(base, mem);
|
RedwoodRecordRef decoded = d.apply(base, mem);
|
||||||
|
|
||||||
|
@ -3677,7 +3699,7 @@ TEST_CASE("!/redwood/correctness/btree") {
|
||||||
state int maxValueSize = g_random->randomInt(0, pageSize * 4);
|
state int maxValueSize = g_random->randomInt(0, pageSize * 4);
|
||||||
state int maxCommitSize = shortTest ? 1000 : randomSize(10e6);
|
state int maxCommitSize = shortTest ? 1000 : randomSize(10e6);
|
||||||
state int mutationBytesTarget = shortTest ? 5000 : randomSize(50e6);
|
state int mutationBytesTarget = shortTest ? 5000 : randomSize(50e6);
|
||||||
state double clearChance = g_random->random01() * .01; // at most 1 in 100
|
state double clearChance = g_random->random01() * .1;
|
||||||
|
|
||||||
printf("Using page size %d, max key size %d, max value size %d, clearchance %f, total mutation byte target %d\n", pageSize, maxKeySize, maxValueSize, clearChance, mutationBytesTarget);
|
printf("Using page size %d, max key size %d, max value size %d, clearchance %f, total mutation byte target %d\n", pageSize, maxKeySize, maxValueSize, clearChance, mutationBytesTarget);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
testTitle=UnitTests
|
||||||
|
testName=UnitTests
|
||||||
|
startDelay=0
|
||||||
|
useDB=false
|
||||||
|
maxTestCases=0
|
||||||
|
testsMatching=!/redwood/correctness/btree
|
|
@ -0,0 +1,6 @@
|
||||||
|
testTitle=UnitTests
|
||||||
|
testName=UnitTests
|
||||||
|
startDelay=0
|
||||||
|
useDB=false
|
||||||
|
maxTestCases=0
|
||||||
|
testsMatching=!/redwood/correctness/unit/
|
|
@ -0,0 +1,6 @@
|
||||||
|
testTitle=UnitTests
|
||||||
|
testName=UnitTests
|
||||||
|
startDelay=0
|
||||||
|
useDB=false
|
||||||
|
maxTestCases=0
|
||||||
|
testsMatching=!/redwood/correctness/btree
|
Loading…
Reference in New Issue