mirror of
https://github.com/apple/foundationdb.git
synced 2025-05-15 10:22:20 +08:00
Added use of IndirectShadowPager. Moved MemoryPager code into .cpp because of type conflicts and its implementation doesn’t need to be externally visible anyway. Added start of a performance test. Renamed tests. Correctness/set now has random reopen of disk based pager before verification. Added asserts for when keys or values that are too large to fit in a single page.
This commit is contained in:
parent
b56f5643d5
commit
d8b82ecbe4
@ -24,6 +24,102 @@
|
|||||||
#include "flow/Arena.h"
|
#include "flow/Arena.h"
|
||||||
#include "flow/UnitTest.h"
|
#include "flow/UnitTest.h"
|
||||||
|
|
||||||
|
typedef uint8_t* PhysicalPageID;
|
||||||
|
typedef std::vector<std::pair<Version, PhysicalPageID>> PageVersionMap;
|
||||||
|
typedef std::vector<PageVersionMap> LogicalPageTable;
|
||||||
|
|
||||||
|
class MemoryPager;
|
||||||
|
|
||||||
|
class MemoryPage : public IPage, ReferenceCounted<MemoryPage> {
|
||||||
|
public:
|
||||||
|
MemoryPage();
|
||||||
|
MemoryPage(uint8_t *data);
|
||||||
|
virtual ~MemoryPage();
|
||||||
|
|
||||||
|
virtual void addref() const {
|
||||||
|
ReferenceCounted<MemoryPage>::addref();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void delref() const {
|
||||||
|
ReferenceCounted<MemoryPage>::delref();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual int size() const;
|
||||||
|
virtual uint8_t const* begin() const;
|
||||||
|
virtual uint8_t* mutate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class MemoryPager;
|
||||||
|
uint8_t *data;
|
||||||
|
bool allocated;
|
||||||
|
|
||||||
|
static const int PAGE_BYTES;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryPagerSnapshot : public IPagerSnapshot, ReferenceCounted<MemoryPagerSnapshot> {
|
||||||
|
public:
|
||||||
|
MemoryPagerSnapshot(MemoryPager *pager, Version version) : pager(pager), version(version) {}
|
||||||
|
virtual Future<Reference<const IPage>> getPhysicalPage(LogicalPageID pageID);
|
||||||
|
virtual void invalidateReturnedPages() {}
|
||||||
|
|
||||||
|
virtual void addref() {
|
||||||
|
ReferenceCounted<MemoryPagerSnapshot>::addref();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void delref() {
|
||||||
|
ReferenceCounted<MemoryPagerSnapshot>::delref();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemoryPager *pager;
|
||||||
|
Version version;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryPager : public IPager, ReferenceCounted<MemoryPager> {
|
||||||
|
public:
|
||||||
|
MemoryPager();
|
||||||
|
|
||||||
|
virtual Reference<IPage> newPageBuffer();
|
||||||
|
virtual int getUsablePageSize();
|
||||||
|
|
||||||
|
virtual Reference<IPagerSnapshot> getReadSnapshot(Version version);
|
||||||
|
|
||||||
|
virtual LogicalPageID allocateLogicalPage();
|
||||||
|
virtual void freeLogicalPage(LogicalPageID pageID, Version version);
|
||||||
|
virtual void writePage(LogicalPageID pageID, Reference<IPage> contents, Version updateVersion);
|
||||||
|
virtual void forgetVersions(Version begin, Version end);
|
||||||
|
virtual Future<Void> commit();
|
||||||
|
|
||||||
|
virtual void setLatestVersion(Version version);
|
||||||
|
virtual Future<Version> getLatestVersion();
|
||||||
|
|
||||||
|
virtual Future<Void> getError();
|
||||||
|
virtual Future<Void> onClosed();
|
||||||
|
virtual void dispose();
|
||||||
|
virtual void close();
|
||||||
|
|
||||||
|
virtual Reference<const IPage> getPage(LogicalPageID pageID, Version version);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Version latestVersion;
|
||||||
|
Version committedVersion;
|
||||||
|
Standalone<VectorRef<VectorRef<uint8_t>>> data;
|
||||||
|
LogicalPageTable pageTable;
|
||||||
|
|
||||||
|
Promise<Void> closed;
|
||||||
|
|
||||||
|
std::vector<PhysicalPageID> freeList; // TODO: is this good enough for now?
|
||||||
|
|
||||||
|
PhysicalPageID allocatePage(Reference<IPage> contents);
|
||||||
|
void extendData();
|
||||||
|
|
||||||
|
static const PhysicalPageID INVALID_PAGE;
|
||||||
|
};
|
||||||
|
|
||||||
|
IPager * createMemoryPager() {
|
||||||
|
return new MemoryPager();
|
||||||
|
}
|
||||||
|
|
||||||
MemoryPage::MemoryPage() : allocated(true) {
|
MemoryPage::MemoryPage() : allocated(true) {
|
||||||
data = (uint8_t*)FastAllocator<4096>::allocate();
|
data = (uint8_t*)FastAllocator<4096>::allocate();
|
||||||
}
|
}
|
||||||
|
@ -24,96 +24,6 @@
|
|||||||
|
|
||||||
#include "IPager.h"
|
#include "IPager.h"
|
||||||
|
|
||||||
typedef uint8_t* PhysicalPageID;
|
IPager * createMemoryPager();
|
||||||
typedef std::vector<std::pair<Version, PhysicalPageID>> PageVersionMap;
|
|
||||||
typedef std::vector<PageVersionMap> LogicalPageTable;
|
|
||||||
|
|
||||||
class MemoryPager;
|
|
||||||
|
|
||||||
class MemoryPage : public IPage, ReferenceCounted<MemoryPage> {
|
|
||||||
public:
|
|
||||||
MemoryPage();
|
|
||||||
MemoryPage(uint8_t *data);
|
|
||||||
virtual ~MemoryPage();
|
|
||||||
|
|
||||||
virtual void addref() const {
|
|
||||||
ReferenceCounted<MemoryPage>::addref();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void delref() const {
|
|
||||||
ReferenceCounted<MemoryPage>::delref();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual int size() const;
|
|
||||||
virtual uint8_t const* begin() const;
|
|
||||||
virtual uint8_t* mutate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
friend class MemoryPager;
|
|
||||||
uint8_t *data;
|
|
||||||
bool allocated;
|
|
||||||
|
|
||||||
static const int PAGE_BYTES;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MemoryPagerSnapshot : public IPagerSnapshot, ReferenceCounted<MemoryPagerSnapshot> {
|
|
||||||
public:
|
|
||||||
MemoryPagerSnapshot(MemoryPager *pager, Version version) : pager(pager), version(version) {}
|
|
||||||
virtual Future<Reference<const IPage>> getPhysicalPage(LogicalPageID pageID);
|
|
||||||
virtual void invalidateReturnedPages() {}
|
|
||||||
|
|
||||||
virtual void addref() {
|
|
||||||
ReferenceCounted<MemoryPagerSnapshot>::addref();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void delref() {
|
|
||||||
ReferenceCounted<MemoryPagerSnapshot>::delref();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
MemoryPager *pager;
|
|
||||||
Version version;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MemoryPager : public IPager, ReferenceCounted<MemoryPager> {
|
|
||||||
public:
|
|
||||||
MemoryPager();
|
|
||||||
|
|
||||||
virtual Reference<IPage> newPageBuffer();
|
|
||||||
virtual int getUsablePageSize();
|
|
||||||
|
|
||||||
virtual Reference<IPagerSnapshot> getReadSnapshot(Version version);
|
|
||||||
|
|
||||||
virtual LogicalPageID allocateLogicalPage();
|
|
||||||
virtual void freeLogicalPage(LogicalPageID pageID, Version version);
|
|
||||||
virtual void writePage(LogicalPageID pageID, Reference<IPage> contents, Version updateVersion);
|
|
||||||
virtual void forgetVersions(Version begin, Version end);
|
|
||||||
virtual Future<Void> commit();
|
|
||||||
|
|
||||||
virtual void setLatestVersion(Version version);
|
|
||||||
virtual Future<Version> getLatestVersion();
|
|
||||||
|
|
||||||
virtual Future<Void> getError();
|
|
||||||
virtual Future<Void> onClosed();
|
|
||||||
virtual void dispose();
|
|
||||||
virtual void close();
|
|
||||||
|
|
||||||
virtual Reference<const IPage> getPage(LogicalPageID pageID, Version version);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Version latestVersion;
|
|
||||||
Version committedVersion;
|
|
||||||
Standalone<VectorRef<VectorRef<uint8_t>>> data;
|
|
||||||
LogicalPageTable pageTable;
|
|
||||||
|
|
||||||
Promise<Void> closed;
|
|
||||||
|
|
||||||
std::vector<PhysicalPageID> freeList; // TODO: is this good enough for now?
|
|
||||||
|
|
||||||
PhysicalPageID allocatePage(Reference<IPage> contents);
|
|
||||||
void extendData();
|
|
||||||
|
|
||||||
static const PhysicalPageID INVALID_PAGE;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -26,11 +26,14 @@
|
|||||||
#include "flow/genericactors.actor.h"
|
#include "flow/genericactors.actor.h"
|
||||||
#include "flow/UnitTest.h"
|
#include "flow/UnitTest.h"
|
||||||
#include "MemoryPager.h"
|
#include "MemoryPager.h"
|
||||||
|
#include "IndirectShadowPager.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define INTERNAL_PAGES_HAVE_TUPLES 1
|
#define INTERNAL_PAGES_HAVE_TUPLES 1
|
||||||
|
|
||||||
|
#define debug_printf(...)
|
||||||
|
|
||||||
struct SimpleFixedSizeMapRef {
|
struct SimpleFixedSizeMapRef {
|
||||||
typedef std::vector<std::pair<std::string, std::string>> KVPairsT;
|
typedef std::vector<std::pair<std::string, std::string>> KVPairsT;
|
||||||
|
|
||||||
@ -80,9 +83,11 @@ struct SimpleFixedSizeMapRef {
|
|||||||
for(auto const &kv : kvPairs) {
|
for(auto const &kv : kvPairs) {
|
||||||
// If page would overflow, output it and start new one
|
// If page would overflow, output it and start new one
|
||||||
if(bw.getLength() + 8 + kv.first.size() + kv.second.size() > pageSize) {
|
if(bw.getLength() + 8 + kv.first.size() + kv.second.size() > pageSize) {
|
||||||
|
// Page so far can't be empty, this means a single kv pair is too big for a page.
|
||||||
|
ASSERT(bw.getLength() != sizeof(newFlags));
|
||||||
memcpy(page->mutate(), bw.getData(), bw.getLength());
|
memcpy(page->mutate(), bw.getData(), bw.getLength());
|
||||||
*(uint32_t *)(page->mutate() + mapSizeOffset) = i - start;
|
*(uint32_t *)(page->mutate() + mapSizeOffset) = i - start;
|
||||||
//printf("buildmany: writing page start=%d %s\n", start, kvPairs[start].first.c_str());
|
//debug_printf("buildmany: writing page start=%d %s\n", start, kvPairs[start].first.c_str());
|
||||||
pages.push_back({start, page});
|
pages.push_back({start, page});
|
||||||
bw = BinaryWriter(AssumeVersion(currentProtocolVersion));
|
bw = BinaryWriter(AssumeVersion(currentProtocolVersion));
|
||||||
bw << newFlags;
|
bw << newFlags;
|
||||||
@ -97,13 +102,13 @@ struct SimpleFixedSizeMapRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(bw.getLength() != sizeof(newFlags)) {
|
if(bw.getLength() != sizeof(newFlags)) {
|
||||||
//printf("buildmany: adding last page start=%d %s\n", start, kvPairs[start].first.c_str());
|
//debug_printf("buildmany: adding last page start=%d %s\n", start, kvPairs[start].first.c_str());
|
||||||
memcpy(page->mutate(), bw.getData(), bw.getLength());
|
memcpy(page->mutate(), bw.getData(), bw.getLength());
|
||||||
*(uint32_t *)(page->mutate() + mapSizeOffset) = i - start;
|
*(uint32_t *)(page->mutate() + mapSizeOffset) = i - start;
|
||||||
pages.push_back({start, page});
|
pages.push_back({start, page});
|
||||||
}
|
}
|
||||||
|
|
||||||
//printf("buildmany: returning pages.size %lu, kvpairs %lu\n", pages.size(), kvPairs.size());
|
//debug_printf("buildmany: returning pages.size %lu, kvpairs %lu\n", pages.size(), kvPairs.size());
|
||||||
return pages;
|
return pages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +198,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void writePage(LogicalPageID id, Reference<IPage> page, Version ver) {
|
void writePage(LogicalPageID id, Reference<IPage> page, Version ver) {
|
||||||
FixedSizeMap map = FixedSizeMap::decode(StringRef(page->begin(), page->size()));
|
FixedSizeMap map = FixedSizeMap::decode(StringRef(page->begin(), page->size()));
|
||||||
printf("Writing page: id=%d ver=%lld %s\n", id, ver, map.toString().c_str());
|
debug_printf("Writing page: id=%d ver=%lld %s\n", id, ver, map.toString().c_str());
|
||||||
m_pager->writePage(id, page, ver);
|
m_pager->writePage(id, page, ver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,9 +232,12 @@ private:
|
|||||||
newChildEntries.push_back( {childEntries[pages[i].first].first, std::string((char *)&logicalPageIDs[i], sizeof(uint32_t))});
|
newChildEntries.push_back( {childEntries[pages[i].first].first, std::string((char *)&logicalPageIDs[i], sizeof(uint32_t))});
|
||||||
childEntries = std::move(newChildEntries);
|
childEntries = std::move(newChildEntries);
|
||||||
|
|
||||||
|
int oldPages = pages.size();
|
||||||
pages = FixedSizeMap::buildMany( childEntries, 0, [=](){ return m_pager->newPageBuffer(); }, m_page_size_override);
|
pages = FixedSizeMap::buildMany( childEntries, 0, [=](){ return m_pager->newPageBuffer(); }, m_page_size_override);
|
||||||
|
// If there isn't a reduction in page count then we'll build new root levels forever.
|
||||||
|
ASSERT(pages.size() < oldPages);
|
||||||
|
|
||||||
printf("Writing a new root level at version %lld with %lu children across %lu pages\n", version, childEntries.size(), pages.size());
|
debug_printf("Writing a new root level at version %lld with %lu children across %lu pages\n", version, childEntries.size(), pages.size());
|
||||||
|
|
||||||
// Allocate logical page ids for the new level
|
// Allocate logical page ids for the new level
|
||||||
logicalPageIDs.clear();
|
logicalPageIDs.clear();
|
||||||
@ -251,17 +259,17 @@ private:
|
|||||||
// Returns list of (version, list of (lower_bound, list of children) )
|
// Returns list of (version, list of (lower_bound, list of children) )
|
||||||
ACTOR static Future<VersionedChildrenT> commitSubtree(VersionedBTree *self, Reference<IPagerSnapshot> snapshot, LogicalPageID root, std::string lowerBoundKey, MutationBufferT::const_iterator bufBegin, MutationBufferT::const_iterator bufEnd) {
|
ACTOR static Future<VersionedChildrenT> commitSubtree(VersionedBTree *self, Reference<IPagerSnapshot> snapshot, LogicalPageID root, std::string lowerBoundKey, MutationBufferT::const_iterator bufBegin, MutationBufferT::const_iterator bufEnd) {
|
||||||
state std::string printPrefix = format("commit subtree(lowerboundkey %s, page %u) ", lowerBoundKey.c_str(), root);
|
state std::string printPrefix = format("commit subtree(lowerboundkey %s, page %u) ", lowerBoundKey.c_str(), root);
|
||||||
printf("%s\n", printPrefix.c_str());
|
debug_printf("%s\n", printPrefix.c_str());
|
||||||
|
|
||||||
if(bufBegin == bufEnd) {
|
if(bufBegin == bufEnd) {
|
||||||
printf("%s no changes\n", printPrefix.c_str());
|
debug_printf("%s no changes\n", printPrefix.c_str());
|
||||||
return VersionedChildrenT({ {0,{{lowerBoundKey,root}}} });
|
return VersionedChildrenT({ {0,{{lowerBoundKey,root}}} });
|
||||||
}
|
}
|
||||||
|
|
||||||
state FixedSizeMap map;
|
state FixedSizeMap map;
|
||||||
Reference<const IPage> rawPage = wait(snapshot->getPhysicalPage(root));
|
Reference<const IPage> rawPage = wait(snapshot->getPhysicalPage(root));
|
||||||
map = FixedSizeMap::decode(StringRef(rawPage->begin(), rawPage->size()));
|
map = FixedSizeMap::decode(StringRef(rawPage->begin(), rawPage->size()));
|
||||||
printf("%s Read page %d: %s\n", printPrefix.c_str(), root, map.toString().c_str());
|
debug_printf("%s Read page %d: %s\n", printPrefix.c_str(), root, map.toString().c_str());
|
||||||
|
|
||||||
if(map.flags & EPageFlags::IS_LEAF) {
|
if(map.flags & EPageFlags::IS_LEAF) {
|
||||||
VersionedChildrenT results;
|
VersionedChildrenT results;
|
||||||
@ -281,7 +289,7 @@ private:
|
|||||||
while(iBuf != bufEnd) {
|
while(iBuf != bufEnd) {
|
||||||
Key k = StringRef(iBuf->first);
|
Key k = StringRef(iBuf->first);
|
||||||
for(auto const &vv : iBuf->second) {
|
for(auto const &vv : iBuf->second) {
|
||||||
printf("Inserting %s %s @%lld\n", k.toString().c_str(), vv.second.c_str(), vv.first);
|
debug_printf("Inserting %s %s @%lld\n", k.toString().c_str(), vv.second.c_str(), vv.first);
|
||||||
mutations.push_back(KeyVersionValue(k, vv.first, StringRef(vv.second)));
|
mutations.push_back(KeyVersionValue(k, vv.first, StringRef(vv.second)));
|
||||||
minVersion = std::min(minVersion, vv.first);
|
minVersion = std::min(minVersion, vv.first);
|
||||||
}
|
}
|
||||||
@ -321,7 +329,7 @@ private:
|
|||||||
logicalPages.push_back(self->m_pager->allocateLogicalPage() );
|
logicalPages.push_back(self->m_pager->allocateLogicalPage() );
|
||||||
|
|
||||||
// Write each page using its assigned page ID
|
// Write each page using its assigned page ID
|
||||||
printf("%s Writing %lu replacement pages for %d at version %lld\n", printPrefix.c_str(), pages.size(), root, minVersion);
|
debug_printf("%s Writing %lu replacement pages for %d at version %lld\n", printPrefix.c_str(), pages.size(), root, minVersion);
|
||||||
for(int i=0; i<pages.size(); i++)
|
for(int i=0; i<pages.size(); i++)
|
||||||
self->writePage(logicalPages[i], pages[i].second, minVersion);
|
self->writePage(logicalPages[i], pages[i].second, minVersion);
|
||||||
|
|
||||||
@ -341,7 +349,7 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s DONE.\n", printPrefix.c_str());
|
debug_printf("%s DONE.\n", printPrefix.c_str());
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -375,7 +383,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!modified) {
|
if(!modified) {
|
||||||
printf("%s not modified.\n", printPrefix.c_str());
|
debug_printf("%s not modified.\n", printPrefix.c_str());
|
||||||
return VersionedChildrenT({{0, {{lowerBoundKey, root}}}});
|
return VersionedChildrenT({{0, {{lowerBoundKey, root}}}});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +396,7 @@ private:
|
|||||||
FixedSizeMap::KVPairsT childEntries; // Logically std::vector<std::pair<std::string, LogicalPageID>> childEntries;
|
FixedSizeMap::KVPairsT childEntries; // Logically std::vector<std::pair<std::string, LogicalPageID>> childEntries;
|
||||||
|
|
||||||
// For each Future<VersionedChildrenT>
|
// For each Future<VersionedChildrenT>
|
||||||
printf("%s creating replacement pages for id=%d at Version %lld\n", printPrefix.c_str(), root, version);
|
debug_printf("%s creating replacement pages for id=%d at Version %lld\n", printPrefix.c_str(), root, version);
|
||||||
|
|
||||||
// If we're writing version 0, there is a chance that we don't have to write ourselves, if there are no changes
|
// If we're writing version 0, there is a chance that we don't have to write ourselves, if there are no changes
|
||||||
bool modified = version != 0;
|
bool modified = version != 0;
|
||||||
@ -397,11 +405,11 @@ private:
|
|||||||
const VersionedChildrenT &children = m_futureChildren[i].get();
|
const VersionedChildrenT &children = m_futureChildren[i].get();
|
||||||
LogicalPageID pageID = *(uint32_t*)map.entries[i].second.data();
|
LogicalPageID pageID = *(uint32_t*)map.entries[i].second.data();
|
||||||
|
|
||||||
printf(" Versioned page set that replaced page %d: %lu versions\n", pageID, children.size());
|
debug_printf(" Versioned page set that replaced page %d: %lu versions\n", pageID, children.size());
|
||||||
for(auto &versionedPageSet : children) {
|
for(auto &versionedPageSet : children) {
|
||||||
printf(" version: %lld\n", versionedPageSet.first);
|
debug_printf(" version: %lld\n", versionedPageSet.first);
|
||||||
for(auto &boundaryPage : versionedPageSet.second) {
|
for(auto &boundaryPage : versionedPageSet.second) {
|
||||||
printf(" %s -> %u\n", boundaryPage.first.c_str(), boundaryPage.second);
|
debug_printf(" %s -> %u\n", boundaryPage.first.c_str(), boundaryPage.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,34 +418,34 @@ private:
|
|||||||
|
|
||||||
// If there are no versions before the one we found, just update nextVersion and continue.
|
// If there are no versions before the one we found, just update nextVersion and continue.
|
||||||
if(cv == children.begin()) {
|
if(cv == children.begin()) {
|
||||||
printf(" First version (%lld) in set is greater than current, setting nextVersion and continuing\n", cv->first);
|
debug_printf(" First version (%lld) in set is greater than current, setting nextVersion and continuing\n", cv->first);
|
||||||
nextVersion = std::min(nextVersion, cv->first);
|
nextVersion = std::min(nextVersion, cv->first);
|
||||||
printf(" curr %lld next %lld\n", version, nextVersion);
|
debug_printf(" curr %lld next %lld\n", version, nextVersion);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a version greater than the current version being written was found, update nextVersion
|
// If a version greater than the current version being written was found, update nextVersion
|
||||||
if(cv != children.end()) {
|
if(cv != children.end()) {
|
||||||
nextVersion = std::min(nextVersion, cv->first);
|
nextVersion = std::min(nextVersion, cv->first);
|
||||||
printf(" curr %lld next %lld\n", version, nextVersion);
|
debug_printf(" curr %lld next %lld\n", version, nextVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Go back one to the last version that was valid prior to or at the current version we are writing
|
// Go back one to the last version that was valid prior to or at the current version we are writing
|
||||||
--cv;
|
--cv;
|
||||||
|
|
||||||
printf(" Using children for version %lld from this set, building version %lld\n", cv->first, version);
|
debug_printf(" Using children for version %lld from this set, building version %lld\n", cv->first, version);
|
||||||
|
|
||||||
// If page count isn't 1 then the root is definitely modified
|
// If page count isn't 1 then the root is definitely modified
|
||||||
modified = modified || cv->second.size() != 1;
|
modified = modified || cv->second.size() != 1;
|
||||||
|
|
||||||
// Add the children at this version to the child entries list for the current version being built.
|
// Add the children at this version to the child entries list for the current version being built.
|
||||||
for (auto &childPage : cv->second) {
|
for (auto &childPage : cv->second) {
|
||||||
printf(" Adding child page '%s'\n", childPage.first.c_str());
|
debug_printf(" Adding child page '%s'\n", childPage.first.c_str());
|
||||||
childEntries.push_back( {childPage.first, std::string((char *)&childPage.second, sizeof(uint32_t))});
|
childEntries.push_back( {childPage.first, std::string((char *)&childPage.second, sizeof(uint32_t))});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Finished pass through futurechildren. childEntries=%lu version=%lld nextVersion=%lld\n", childEntries.size(), version, nextVersion);
|
debug_printf("Finished pass through futurechildren. childEntries=%lu version=%lld nextVersion=%lld\n", childEntries.size(), version, nextVersion);
|
||||||
|
|
||||||
if(modified) {
|
if(modified) {
|
||||||
// TODO: Track split points across iterations of this loop, so that they don't shift unnecessarily and
|
// TODO: Track split points across iterations of this loop, so that they don't shift unnecessarily and
|
||||||
@ -458,7 +466,7 @@ private:
|
|||||||
logicalPages.push_back( self->m_pager->allocateLogicalPage() );
|
logicalPages.push_back( self->m_pager->allocateLogicalPage() );
|
||||||
|
|
||||||
// Write each page using its assigned page ID
|
// Write each page using its assigned page ID
|
||||||
printf("Writing internal pages, subtreeRoot=%u\n", root);
|
debug_printf("Writing internal pages, subtreeRoot=%u\n", root);
|
||||||
for(int i=0; i<pages.size(); i++)
|
for(int i=0; i<pages.size(); i++)
|
||||||
self->writePage( logicalPages[i], pages[i].second, version );
|
self->writePage( logicalPages[i], pages[i].second, version );
|
||||||
|
|
||||||
@ -473,12 +481,12 @@ private:
|
|||||||
result.back().second.push_back( {childEntries[pages[i].first].first, logicalPages[i]} );
|
result.back().second.push_back( {childEntries[pages[i].first].first, logicalPages[i]} );
|
||||||
|
|
||||||
if (result.size() > 1 && result.back().second == result.end()[-2].second) {
|
if (result.size() > 1 && result.back().second == result.end()[-2].second) {
|
||||||
printf("Output same as last version, popping it.\n");
|
debug_printf("Output same as last version, popping it.\n");
|
||||||
result.pop_back();
|
result.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printf("Version 0 has no changes\n");
|
debug_printf("Version 0 has no changes\n");
|
||||||
result.push_back({0, {{lowerBoundKey, root}}});
|
result.push_back({0, {{lowerBoundKey, root}}});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,7 +495,7 @@ private:
|
|||||||
version = nextVersion;
|
version = nextVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s DONE.\n", printPrefix.c_str());
|
debug_printf("%s DONE.\n", printPrefix.c_str());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,7 +565,7 @@ private:
|
|||||||
loop {
|
loop {
|
||||||
Reference<const IPage> rawPage = wait(self->m_pager->getPhysicalPage(pageNumber));
|
Reference<const IPage> rawPage = wait(self->m_pager->getPhysicalPage(pageNumber));
|
||||||
FixedSizeMap map = FixedSizeMap::decode(StringRef(rawPage->begin(), rawPage->size()));
|
FixedSizeMap map = FixedSizeMap::decode(StringRef(rawPage->begin(), rawPage->size()));
|
||||||
//printf("Read page %d @%lld: %s\n", pageNumber, self->m_version, map.toString().c_str());
|
//debug_printf("Read page %d @%lld: %s\n", pageNumber, self->m_version, map.toString().c_str());
|
||||||
|
|
||||||
// Special case of empty page (which should only happen for root)
|
// Special case of empty page (which should only happen for root)
|
||||||
if(map.entries.empty()) {
|
if(map.entries.empty()) {
|
||||||
@ -590,9 +598,9 @@ private:
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyValue randomKV() {
|
KeyValue randomKV(int keySize = 10, int valueSize = 5) {
|
||||||
int kLen = g_random->randomInt(1, 10);
|
int kLen = g_random->randomInt(1, keySize);
|
||||||
int vLen = g_random->randomInt(0, 5);
|
int vLen = g_random->randomInt(0, valueSize);
|
||||||
KeyValue kv;
|
KeyValue kv;
|
||||||
kv.key = makeString(kLen, kv.arena());
|
kv.key = makeString(kLen, kv.arena());
|
||||||
kv.value = makeString(vLen, kv.arena());
|
kv.value = makeString(vLen, kv.arena());
|
||||||
@ -603,14 +611,23 @@ KeyValue randomKV() {
|
|||||||
return kv;
|
return kv;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("/redwood/set") {
|
TEST_CASE("/redwood/correctness/memory/set") {
|
||||||
state IPager *pager = new MemoryPager();
|
state bool useDisk = true;
|
||||||
|
|
||||||
|
state IPager *pager;
|
||||||
|
if(useDisk)
|
||||||
|
pager = new IndirectShadowPager("pagerfile");
|
||||||
|
else
|
||||||
|
pager = createMemoryPager();
|
||||||
|
|
||||||
state VersionedBTree *btree = new VersionedBTree(pager, g_random->randomInt(50, 200));
|
state VersionedBTree *btree = new VersionedBTree(pager, g_random->randomInt(50, 200));
|
||||||
Void _ = wait(btree->init());
|
Void _ = wait(btree->init());
|
||||||
|
|
||||||
state std::map<std::pair<std::string, Version>, std::string> written;
|
state std::map<std::pair<std::string, Version>, std::string> written;
|
||||||
|
|
||||||
Version lastVer = wait(btree->getLatestVersion());
|
Version lastVer = wait(btree->getLatestVersion());
|
||||||
|
printf("Starting from version: %lld\n", lastVer);
|
||||||
|
|
||||||
state Version version = lastVer + 1;
|
state Version version = lastVer + 1;
|
||||||
state int commits = g_random->randomInt(1, 20);
|
state int commits = g_random->randomInt(1, 20);
|
||||||
//printf("Will do %d commits\n", commits);
|
//printf("Will do %d commits\n", commits);
|
||||||
@ -637,6 +654,23 @@ TEST_CASE("/redwood/set") {
|
|||||||
state int errors = 0;
|
state int errors = 0;
|
||||||
|
|
||||||
printf("Checking changes committed thus far.\n");
|
printf("Checking changes committed thus far.\n");
|
||||||
|
if(useDisk && g_random->coinflip()) {
|
||||||
|
printf("Reopening disk btree\n");
|
||||||
|
|
||||||
|
Future<Void> closedFuture = pager->onClosed();
|
||||||
|
pager->close();
|
||||||
|
Void _ = wait(closedFuture);
|
||||||
|
|
||||||
|
pager = new IndirectShadowPager("pagerfile");
|
||||||
|
|
||||||
|
btree = new VersionedBTree(pager, g_random->randomInt(50, 200));
|
||||||
|
Void _ = wait(btree->init());
|
||||||
|
|
||||||
|
Version lastVer = wait(btree->getLatestVersion());
|
||||||
|
printf("Starting from version %lld, last write was at version %lld\n", lastVer, version);
|
||||||
|
ASSERT(lastVer == version);
|
||||||
|
}
|
||||||
|
|
||||||
while(i != iEnd) {
|
while(i != iEnd) {
|
||||||
state std::string key = i->first.first;
|
state std::string key = i->first.first;
|
||||||
state Version ver = i->first.second;
|
state Version ver = i->first.second;
|
||||||
@ -664,6 +698,55 @@ TEST_CASE("/redwood/set") {
|
|||||||
throw internal_error();
|
throw internal_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Void> closedFuture = pager->onClosed();
|
||||||
|
pager->close();
|
||||||
|
Void _ = wait(closedFuture);
|
||||||
|
|
||||||
|
return Void();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("/redwood/performance/set") {
|
||||||
|
state IPager *pager = new IndirectShadowPager("pagerfile");
|
||||||
|
state VersionedBTree *btree = new VersionedBTree(pager);
|
||||||
|
Void _ = wait(btree->init());
|
||||||
|
|
||||||
|
state int nodeCount = 100000;
|
||||||
|
state int maxChangesPerVersion = 5;
|
||||||
|
state int versions = 4;
|
||||||
|
int maxKeySize = 100;
|
||||||
|
int maxValueSize = 500;
|
||||||
|
|
||||||
|
state std::string key(maxKeySize, 'k');
|
||||||
|
state std::string value(maxKeySize, 'v');
|
||||||
|
|
||||||
|
state double startTime = now();
|
||||||
|
while(--versions) {
|
||||||
|
Version lastVer = wait(btree->getLatestVersion());
|
||||||
|
state Version version = lastVer + 1;
|
||||||
|
printf("Writing version %lld\n", version);
|
||||||
|
btree->setWriteVersion(version);
|
||||||
|
int changes = g_random->randomInt(0, maxChangesPerVersion);
|
||||||
|
while(changes--) {
|
||||||
|
KeyValue kv;
|
||||||
|
// Change first 4 bytes of key to an int
|
||||||
|
*(uint32_t *)key.data() = g_random->randomInt(0, nodeCount);
|
||||||
|
kv.key = StringRef((uint8_t *)key.data(), g_random->randomInt(10, key.size()));
|
||||||
|
kv.value = StringRef((uint8_t *)value.data(), g_random->randomInt(0, value.size()));
|
||||||
|
btree->set(kv);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(g_random->random01() < .01) {
|
||||||
|
printf("Committing %lld\n", version);
|
||||||
|
Void _ = wait(btree->commit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Void _ = wait(btree->commit());
|
||||||
|
|
||||||
|
Future<Void> closedFuture = pager->onClosed();
|
||||||
|
pager->close();
|
||||||
|
Void _ = wait(closedFuture);
|
||||||
|
|
||||||
return Void();
|
return Void();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,4 +3,4 @@ testName=UnitTests
|
|||||||
startDelay=0
|
startDelay=0
|
||||||
useDB=false
|
useDB=false
|
||||||
maxTestCases=0
|
maxTestCases=0
|
||||||
testsMatching=/redwood
|
testsMatching=/redwood/correctness
|
6
tests/RedwoodPerfTests.txt
Normal file
6
tests/RedwoodPerfTests.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
testTitle=UnitTests
|
||||||
|
testName=UnitTests
|
||||||
|
startDelay=0
|
||||||
|
useDB=false
|
||||||
|
maxTestCases=0
|
||||||
|
testsMatching=/redwood/performance
|
Loading…
x
Reference in New Issue
Block a user