Skip to content

Commit

Permalink
Merge branch 'gcc-4.8' into gcc-4.7
Browse files Browse the repository at this point in the history
Conflicts:
	include/leveldb/txn_db.hpp
	include/leveldb/whiteout_db.hpp
  • Loading branch information
ony committed Apr 25, 2014
2 parents c715058 + a36072a commit 889488a
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 55 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ Goals to stick with
- extreme fusion between layers to give a chance for compiler optimizer
- minimize using of ref/ptr that leads outside of this library

Notes for development
---------------------
- tests should be run under valgrind also

Credits
-------
Inspired by https://github.com/feniksa/ldblayer
Expand Down
77 changes: 76 additions & 1 deletion include/leveldb/cover_walker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ namespace leveldb
Walker(Cover<Base, Overlay> op) :
i(op.base),
j(op.overlay)
{}
{
// ensure that both iterators are initialized so notification won't
// hurt us
SeekToFirst();
}

bool Valid() const { return useOverlay() ? j.Valid() : i.Valid(); }
Slice key() const { return useOverlay() ? j.key() : i.key(); }
Expand Down Expand Up @@ -162,6 +166,77 @@ namespace leveldb
}
Activate(false);
}

protected:
void overlayPut(const Slice &key)
{
if (!Valid()) return; // do not bother
switch (state)
{
case FwdLeft:
// check range
switch (compare(i.key(), key))
{
case Order::EQ:
j.Seek(key);
state = Both;
return;
case Order::GT: return;
case Order::LT: break;

}
if (j.Valid() && j.key().compare(key) < 0) return;
// ok this is scase when key inserted between current base and
// next cover
j.Seek(key);
break;
case RevLeft:
switch (compare(i.key(), key))
{
case Order::EQ:
j.Seek(key);
state = Both;
return;
case Order::LT: return;
case Order::GT: break;
}
if (j.Valid() && j.key().compare(key) > 0) return;
j.Seek(key); // exact match so no need to do Prev()
break;
case Both:
case RevRight:
case FwdRight:
// our current value is from overaly
// no need to sync hidden
break;
}
}

void overlayDelete(const Slice &key)
{
if (!Valid()) return;
switch (state)
{
case FwdLeft:
// check if we already pointing to this record
if (!j.Valid() || j.key().compare(key) != 0) return;
j.Seek(key); // we know that this record exists
j.Next();
break;
case RevLeft:
if (!j.Valid() || j.key().compare(key) != 0) return;
j.Seek(key);
j.Prev();
break;
case Both:
case RevRight:
case FwdRight:
// even if we point to record that is about to be deleted we
// shouldn't care since client is responsible for moving away
// from ghost records
break;
}
}
};

template <typename Base, typename Overlay>
Expand Down
111 changes: 62 additions & 49 deletions include/leveldb/txn_db.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <set>

#include <leveldb/any_db.hpp>
#include <leveldb/memory_db.hpp>
#include <leveldb/whiteout_db.hpp>
Expand All @@ -11,19 +13,38 @@ namespace leveldb
template<typename Base = AnyDB>
class TxnDB final : public AnyDB
{
public:
class Walker;
private:
Base &base;
MemoryDB overlay;
WhiteoutDB whiteout;
size_t rev = 0;
std::set<Walker *> walkers;

using Collection = Cover<Subtract<Base>, MemoryDB>;

public:
TxnDB(Base &origin) : base(origin)
{}

TxnDB(TxnDB &&origin) :
base(origin.base),
overlay(std::move(origin.overlay)),
whiteout(std::move(origin.whiteout)),
walkers(std::move(origin.walkers))
{ for (auto walker : walkers) walker->parentChanged(this); }

TxnDB(const TxnDB &origin) :
base(origin.base),
overlay(origin.overlay),
whiteout(origin.whiteout)
{}

~TxnDB() noexcept override = default;

TxnDB &operator=(const TxnDB &) = delete;
TxnDB &operator=(TxnDB &&) = delete;

Status Get(const Slice &key, std::string &value) noexcept override
{
if (whiteout.Check(key))
Expand All @@ -35,65 +56,57 @@ namespace leveldb

Status Put(const Slice &key, const Slice &value) noexcept override
{
++rev;
(void) whiteout.Delete(key);
return overlay.Put(key, value);
Status s = overlay.Put(key, value);
if (s.ok())
{
for (auto walker : walkers)
{ walker->overlayPut(key); }
}
return s;
}

Status Delete(const Slice &key) noexcept override
{
++rev;
whiteout.Insert(key);
if (!whiteout.Insert(key)) return Status::OK(); // already deleted?
for (auto walker : walkers) walker->overlayDelete(key);
return overlay.Delete(key);
}

class Walker
class Walker : public Collection::Walker
{
typename Collection::Walker impl;
size_t rev;
size_t &revTxn;
std::string savepoint;

/// Sync walkers with collection.
/// \return true if jumped one record forward
bool Sync()
{
if (rev != revTxn)
{
rev = revTxn;
if (Valid())
{
impl.Seek(savepoint);
assert( impl.key().compare(savepoint) >= 0 );
return impl.key().compare(savepoint) > 0;
}

}
return false;
}
friend TxnDB;
TxnDB *txn;
typedef typename Collection::Walker Impl;

void parentChanged(TxnDB *parent = nullptr)
{ txn = parent; }

public:
Walker(TxnDB<Base> &origin) :
Impl({{origin.base, origin.whiteout}, origin.overlay}),
txn(&origin)
{ txn->walkers.insert(this); }

void Synced()
~Walker()
{ if (txn) txn->walkers.erase(this); }

Walker(Walker &&origin) :
Impl(std::move(origin)),
txn(origin.txn)
{
rev = revTxn;
if (Valid()) savepoint = impl.key().ToString();
txn->walkers.erase(&origin);
txn->walkers.insert(this);
origin.txn = nullptr;
}
public:
Walker(TxnDB<Base> &txn) :
impl({{txn.base, txn.whiteout}, txn.overlay}),
rev(txn.rev),
revTxn(txn.rev)
{}

bool Valid() const { return impl.Valid(); }
Slice key() const { return impl.key(); }
Slice value() const { return impl.value(); }
Status status() const { return impl.status(); }

void Seek(const Slice &target) { impl.Seek(target); Synced(); }
void SeekToFirst() { impl.SeekToFirst(); Synced(); }
void SeekToLast() { impl.SeekToLast(); Synced(); }
void Next() { if (!Sync()) { impl.Next(); Synced(); } }
void Prev() { Sync(); impl.Prev(); Synced(); }

Walker(const Walker &origin) :
Impl(origin),
txn(origin.txn)
{ txn->walkers.insert(this); }

Walker &operator=(const Walker &) = delete;
Walker &operator=(Walker &&) = delete;
};

std::unique_ptr<Iterator> NewIterator() noexcept override
Expand Down Expand Up @@ -124,5 +137,5 @@ namespace leveldb

template <typename Base>
constexpr TxnDB<Base> transaction(Base &base)
{ return base; }
{ return { base }; }
}
9 changes: 4 additions & 5 deletions include/leveldb/whiteout_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,12 @@ namespace leveldb
bool Check(const Slice &key)
{ return find(key.ToString()) != end(); }

Status Insert(std::string &&key)
bool Insert(std::string &&key)
{
(void) insert(std::forward<std::string>(key));
return Status::OK();
return insert(std::forward<std::string>(key)).second;
}

Status Insert(const Slice &key)
bool Insert(const Slice &key)
{ return Insert(key.ToString()); }

template <typename... Args>
Expand Down Expand Up @@ -89,7 +88,7 @@ namespace leveldb
Walker(WhiteoutDB &origin) : rows(origin), rev(origin.rev)
{}

bool Valid() const { return impl != rows.end(); }
bool Valid() const { return rev == rows.rev && impl != rows.end(); }

void SeekToFirst() { impl = rows.begin(); Synced(); }
void SeekToLast() { impl = rows.end(); if (impl != rows.begin()) --impl; Synced(); }
Expand Down
46 changes: 46 additions & 0 deletions test/test_corners.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,52 @@ TEST(TestTxnIterator, txn_walk_deleted_next_delete)
EXPECT_FALSE( w.Valid() ) << "Still have key " << PrintToString(w.key());
}

TEST(TestTxnIterator, txn_walk_deleted_next_dummy_delete)
{
leveldb::MemoryDB mem {
{ "a", "2" },
{ "c", "3" },
};

auto txn = leveldb::transaction(mem);

std::string v;

auto w = leveldb::walker(txn);
w.SeekToFirst();

ASSERT_TRUE( w.Valid() );
EXPECT_EQ( "a", w.key() );

EXPECT_OK( txn.Delete("b") );
EXPECT_STATUS( NotFound, txn.Get("b", v) );
EXPECT_STATUS( NotFound, mem.Get("b", v) );

w.SeekToFirst();
ASSERT_TRUE( w.Valid() );
EXPECT_EQ( "a", w.key() );
EXPECT_EQ( "2", w.value() );

EXPECT_OK( txn.Put("b", "4") );

ASSERT_TRUE( w.Valid() );
EXPECT_EQ( "a", w.key() );
EXPECT_EQ( "2", w.value() );

w.Next();
ASSERT_TRUE( w.Valid() );
EXPECT_EQ( "b", w.key() );
EXPECT_EQ( "4", w.value() );

w.Next();
ASSERT_TRUE( w.Valid() );
EXPECT_EQ( "c", w.key() );
EXPECT_EQ( "3", w.value() );

w.Next();
EXPECT_FALSE( w.Valid() ) << "Still have key " << PrintToString(w.key());
}


TEST(TestTxnIterator, txn_walk_deleted)
{
Expand Down

0 comments on commit 889488a

Please sign in to comment.