-
Notifications
You must be signed in to change notification settings - Fork 72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Snapshot Optimization (https://github.com/speedb-io/speedb/issues/35) #547
Conversation
620f851
to
edcfc11
Compare
@ayulas Ready for review , thank you |
edcfc11
to
e23dbcf
Compare
e23dbcf
to
849576a
Compare
@@ -3711,7 +3711,57 @@ Status DBImpl::GetTimestampedSnapshots( | |||
timestamped_snapshots_.GetSnapshots(ts_lb, ts_ub, timestamped_snapshots); | |||
return Status::OK(); | |||
} | |||
#ifdef ROCKSDB_SNAP_OPTIMIZATION | |||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you need to check if snapshot supported first... and if not exit immediately
db/db_impl/db_impl.cc
Outdated
bool lock) { | ||
int64_t unix_time = 0; | ||
immutable_db_options_.clock->GetCurrentTime(&unix_time) | ||
.PermitUncheckedError(); // Ignore error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get as local variable the last published seq. you use it in several places, and you need to check for the same point in time . if you are taken it again as you wrote above you may get a different value!
db/db_impl/db_impl.cc
Outdated
immutable_db_options_.clock->GetCurrentTime(&unix_time) | ||
.PermitUncheckedError(); // Ignore error | ||
SnapshotImpl* s = new SnapshotImpl; | ||
std::shared_ptr<SnapshotImpl> snap = snapshots_.last_snapshot_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
last snapshot is fully. pls mention here that its atomic
db/db_impl/db_impl.cc
Outdated
.PermitUncheckedError(); // Ignore error | ||
SnapshotImpl* s = new SnapshotImpl; | ||
std::shared_ptr<SnapshotImpl> snap = snapshots_.last_snapshot_; | ||
if (snap && snap->GetSequenceNumber() == GetLastPublishedSequence() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mention GetLastPublishedSequence should be taken once in this function and use this local value
db/db_impl/db_impl.cc
Outdated
SnapshotImpl* s = new SnapshotImpl; | ||
std::shared_ptr<SnapshotImpl> snap = snapshots_.last_snapshot_; | ||
if (snap && snap->GetSequenceNumber() == GetLastPublishedSequence() && | ||
snap->is_write_conflict_boundary_ == is_write_conflict_boundary) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the all point of this feature is to use efficiently the fact that you actually dont need to take a snapshot if not needed (no new writes and the is_write_conflict_boundary is the same) so there is no point to create a new snap...... you need to use. the exist one and use the shared ptr facilities (wont be deleted as long someone holds it e.g. it still in the snap list) and this is the approach i will choose, and handling the snap time by adding a range snap time.... (time_t so its a long and can be atomic) . but even if you create a new snap by NEW , you shouldn't increase the snap list count cause its just a reference to a snap in the snap list
db/db_impl/db_impl.cc
Outdated
mutex_.AssertHeld(); | ||
} | ||
// returns null if the underlying memtable does not support snapshot. | ||
if (!is_snapshot_supported_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should be checked first, before doing something
db/db_impl/db_impl.cc
Outdated
auto snapshot_seq = GetLastPublishedSequence(); | ||
SnapshotImpl* snapshot = | ||
snapshots_.New(s, snapshot_seq, unix_time, is_write_conflict_boundary); | ||
SnapshotImpl* user_snapshot = new SnapshotImpl; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why? you have the s... why creating a new object??
you need to add in the snap structure the notion if its an holder or the original snap thats it.
you should use s and not create a new one
also why not adding to the snapshots_.New function the most of the knowledge...
we want to reduce the db mutex as much as we can
first use the list mutex
then the list should be a set order by seq number (since you can have many threads that doing snap while writing is in progress and because you are updating the snap list by lock in a short scope you might insert a snap with older seq after a new one ) then you update the last snap list object by taking the last object in the set (still under the list mutex) doing it by store atomic function and increase the snap count to 1
when you create a new snap after checking if you supported the snap creation at all, load the last snap (atomically) and on it do your checking.
db/db_impl/db_impl.cc
Outdated
// inplace_update_support enabled. | ||
return; | ||
} | ||
snapshots_.count_.fetch_sub(1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no... if its a reference snap it wasnt part of the list
db/db_impl/db_impl.cc
Outdated
InstrumentedMutexLock l(&mutex_); | ||
std::scoped_lock<std::mutex> snaplock(snapshots_.lock); | ||
snapshots_.deleteitem = false; | ||
uint64_t oldest_snapshot; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exactly the same code as in the original scope. you copied the same code...
i dont think we should divide it at all.
db/snapshot_impl.h
Outdated
}; | ||
|
||
class SnapshotList { | ||
public: | ||
#ifdef ROCKSDB_SNAP_OPTIMIZATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont think you should divide code. snap list should have mutex, last snap , the delete item is not needed and you can do the code much much simpler / now you have in some parts holes
Summarize offered changes: Fix Release and Get so we will always delete when needed. |
16ce5d0
to
523a6fa
Compare
@ayulas Please review |
2e2af9d
to
b6d6717
Compare
db/db_impl/db_impl.cc
Outdated
@@ -3714,10 +3714,16 @@ Status DBImpl::GetTimestampedSnapshots( | |||
|
|||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary, | |||
bool lock) { | |||
if (!is_snapshot_supported_) { | |||
return nullptr; | |||
} | |||
int64_t unix_time = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed the unix time should be taken inside the RefSnapshot and _New
db/db_impl/db_impl.cc
Outdated
SnapshotImpl* s = new SnapshotImpl; | ||
#ifdef SPEEDB_SNAP_OPTIMIZATION | ||
if (RefSnapshot(unix_time, is_write_conflict_boundary, s)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mention , unix_time shouldnt be an input parameter
@@ -3714,10 +3714,16 @@ Status DBImpl::GetTimestampedSnapshots( | |||
|
|||
SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary, | |||
bool lock) { | |||
if (!is_snapshot_supported_) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you didnt remove this check in line 3734
db/db_impl/db_impl.cc
Outdated
@@ -3732,6 +3738,8 @@ SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary, | |||
delete s; | |||
return nullptr; | |||
} | |||
immutable_db_options_.clock->GetCurrentTime(&unix_time) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you should change the New function signature so the seq and time ad the Ref function will be set inside
SnapshotImpl* New(SnapshotImpl* s, SequenceNumber seq, uint64_t unix_time, | ||
bool is_write_conflict_boundary, | ||
uint64_t ts = std::numeric_limits<uint64_t>::max()) { | ||
#ifdef SPEEDB_SNAP_OPTIMIZATION |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why to divide it . put the logical in not folly as well. the ifdef folly should be minimal the logical should be not under ifdef
db/snapshot_impl.h
Outdated
struct Deleter { | ||
inline void operator()(SnapshotImpl* snap) const; | ||
}; | ||
int64_t unix_time_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why you created it again under ifdef??
you need it to be public changed it to all
db/snapshot_impl.h
Outdated
SnapshotImpl* New(SnapshotImpl* s, SequenceNumber seq, uint64_t unix_time, | ||
bool is_write_conflict_boundary, | ||
uint64_t ts = std::numeric_limits<uint64_t>::max()) { | ||
#ifdef SPEEDB_SNAP_OPTIMIZATION | ||
std::unique_lock<std::mutex> l(lock); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need lock, the db mutex already protect you
db/snapshot_impl.h
Outdated
@@ -81,9 +104,29 @@ class SnapshotList { | |||
return list_.prev_; | |||
} | |||
|
|||
#ifdef SPEEDB_SNAP_OPTIMIZATION | |||
SnapshotImpl* NewSnapRef(SequenceNumber seq, uint64_t unix_time, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i dont understand why you allocated another snapshot. its not right.
db/snapshot_impl.h
Outdated
// changing count_ always under snapshot_list mutex | ||
uint64_t count_; | ||
uint64_t logical_count() const { return logical_count_; } | ||
std::atomic_uint64_t logical_count_; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be out as well
d30509c
to
3b7a705
Compare
ready for review |
3b7a705
to
6be2ecf
Compare
db/db_impl/db_impl.cc
Outdated
return nullptr; | ||
} | ||
SnapshotImpl* snapshot = snapshots_.RefSnapshot( | ||
is_write_conflict_boundary, GetLastPublishedSequence(), GetSystemClock()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetSystemClock shouldn't be a parameter.. it should be called inside. that what we discussed
db/db_impl/db_impl.cc
Outdated
auto snapshot_seq = GetLastPublishedSequence(); | ||
SnapshotImpl* snapshot = | ||
snapshots_.New(s, snapshot_seq, unix_time, is_write_conflict_boundary); | ||
snapshot = snapshots_.New(GetLastPublishedSequence(), GetSystemClock(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again... not pass the GetSystemClock
db/db_impl/db_impl.cc
Outdated
@@ -3747,7 +3742,6 @@ DBImpl::CreateTimestampedSnapshotImpl(SequenceNumber snapshot_seq, uint64_t ts, | |||
int64_t unix_time = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont need that
db/db_impl/db_impl.cc
Outdated
@@ -3747,7 +3742,6 @@ DBImpl::CreateTimestampedSnapshotImpl(SequenceNumber snapshot_seq, uint64_t ts, | |||
int64_t unix_time = 0; | |||
immutable_db_options_.clock->GetCurrentTime(&unix_time) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mention above
return std::make_pair(status, ret); | ||
} else { | ||
status.PermitUncheckedError(); | ||
} | ||
} | ||
|
||
SnapshotImpl* snapshot = | ||
snapshots_.New(s, snapshot_seq, unix_time, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the signature of the function shouldn't contains unix_time
db/snapshot_impl.h
Outdated
shared_snap->is_write_conflict_boundary_ == | ||
is_write_conflict_boundary) { | ||
SnapshotImpl* snapshot = new SnapshotImpl; | ||
int64_t unix_time; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why doing double setting? set it directly to snapshot->unix_time_
db/snapshot_impl.h
Outdated
return false; | ||
} | ||
|
||
SnapshotImpl* New(SequenceNumber seq, SystemClock* clock, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont pass the time in the function signature
db/db_impl/db_impl.cc
Outdated
{ | ||
InstrumentedMutexLock l(&mutex_); | ||
snapshots_.Delete(casted_s); | ||
std::unique_lock<std::mutex> snapshotlist_lock(snapshots_.lock); | ||
casted_s = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why you need that... you deleted the s in the Delete function. do it inside the Delete
db/snapshot_impl.h
Outdated
@@ -81,9 +121,54 @@ class SnapshotList { | |||
return list_.prev_; | |||
} | |||
|
|||
SnapshotImpl* New(SnapshotImpl* s, SequenceNumber seq, uint64_t unix_time, | |||
#ifdef SPEEDB_SNAP_OPTIMIZATION | |||
SnapshotImpl* NewSnapRef(SnapshotImpl* s, SequenceNumber seq, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NewSnapRef gets the s as an input... why passing all the parameters again in the function signature. you need to create the user_snap by passing in the constructor the base s
db/snapshot_impl.h
Outdated
@@ -94,15 +179,25 @@ class SnapshotList { | |||
s->prev_->next_ = s; | |||
s->next_->prev_ = s; | |||
count_++; | |||
#ifdef SPEEDB_SNAP_OPTIMIZATION | |||
l.unlock(); | |||
return NewSnapRef(s, seq, unix_time, is_write_conflict_boundary, ts); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as mention .. pass s and ts only
6be2ecf
to
94ed6c4
Compare
@ayulas fixed |
db/db_impl/db_impl.cc
Outdated
immutable_db_options_.clock->GetCurrentTime(&unix_time) | ||
.PermitUncheckedError(); // Ignore error | ||
SnapshotImpl* s = new SnapshotImpl; | ||
|
||
const bool need_update_seq = (snapshot_seq != kMaxSequenceNumber); | ||
|
||
if (lock) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do this after the check of is_snapshot_supported_. you will avoid lock unlock....like the GetSnapshotImpl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
94ed6c4
to
c64d847
Compare
c64d847
to
bcb828a
Compare
Motivation: The most important information inside a snapshot is its Sequence number, which allows the compaction to know if the key-value should be deleted or not. The sequence number is being changed when modification happens in the db. This feature allows the db to take a snapshot without acquiring db mutex when the last snapshot has the same sequence number as a new one. In transactional db with mostly read operations, it should improve performance when used with multithreaded environment and as well other scenarios of taking large amount of snapshots with mostly read operations. This Feature must have folly library installed. In order to cache the snapshots, there is last_snapshot_ (folly::atomic_shared_ptr, lock free atomic_shared_ptr) in order to access the last_snapshot_ created and point to it. For every GetSnapshotImpl call (where snapshots are being created), the function checks if the sequence number is different than last_snapshot_, if no, it creates new snapshot and inside this snapshot it adds a reference to last_snapshot_ (the reference is cached_snapshot), so this sequence number will remain inside SnapshotList (SnapshotList is the list of the snapshots in the system and used in compaction to show which snapshots are being used), if there are still snapshots holding this sequence number. If the sequence number as changed or the last_snapshot_ is nullptr it will create the snapshot while acquiring db_mutex. For ReleaseSnapshotImpl (deleting a snapshot). We will unref the last_snapshot_ (using comapre_exchange_weak) and if the refcount becomes 0, it will call Deleter and remove this snapshot entirely from the SnapshotList and continue with taking the db mutex. If there are still references, it will return without taking it out from the SnapshotList nor taking the db mutex
bcb828a
to
c1ddc54
Compare
* Snapshot Optimization (#35) Motivation: The most important information inside a snapshot is its Sequence number, which allows the compaction to know if the key-value should be deleted or not. The sequence number is being changed when modification happens in the db. This feature allows the db to take a snapshot without acquiring db mutex when the last snapshot has the same sequence number as a new one. In transactional db with mostly read operations, it should improve performance when used with multithreaded environment and as well other scenarios of taking large amount of snapshots with mostly read operations. This Feature must have folly library installed. In order to cache the snapshots, there is last_snapshot_ (folly::atomic_shared_ptr, lock free atomic_shared_ptr) in order to access the last_snapshot_ created and point to it. For every GetSnapshotImpl call (where snapshots are being created), the function checks if the sequence number is different than last_snapshot_, if no, it creates new snapshot and inside this snapshot it adds a reference to last_snapshot_ (the reference is cached_snapshot), so this sequence number will remain inside SnapshotList (SnapshotList is the list of the snapshots in the system and used in compaction to show which snapshots are being used), if there are still snapshots holding this sequence number. If the sequence number as changed or the last_snapshot_ is nullptr it will create the snapshot while acquiring db_mutex. For ReleaseSnapshotImpl (deleting a snapshot). We will unref the last_snapshot_ (using comapre_exchange_weak) and if the refcount becomes 0, it will call Deleter and remove this snapshot entirely from the SnapshotList and continue with taking the db mutex. If there are still references, it will return without taking it out from the SnapshotList nor taking the db mutex
* Snapshot Optimization (#35) Motivation: The most important information inside a snapshot is its Sequence number, which allows the compaction to know if the key-value should be deleted or not. The sequence number is being changed when modification happens in the db. This feature allows the db to take a snapshot without acquiring db mutex when the last snapshot has the same sequence number as a new one. In transactional db with mostly read operations, it should improve performance when used with multithreaded environment and as well other scenarios of taking large amount of snapshots with mostly read operations. This Feature must have folly library installed. In order to cache the snapshots, there is last_snapshot_ (folly::atomic_shared_ptr, lock free atomic_shared_ptr) in order to access the last_snapshot_ created and point to it. For every GetSnapshotImpl call (where snapshots are being created), the function checks if the sequence number is different than last_snapshot_, if no, it creates new snapshot and inside this snapshot it adds a reference to last_snapshot_ (the reference is cached_snapshot), so this sequence number will remain inside SnapshotList (SnapshotList is the list of the snapshots in the system and used in compaction to show which snapshots are being used), if there are still snapshots holding this sequence number. If the sequence number as changed or the last_snapshot_ is nullptr it will create the snapshot while acquiring db_mutex. For ReleaseSnapshotImpl (deleting a snapshot). We will unref the last_snapshot_ (using comapre_exchange_weak) and if the refcount becomes 0, it will call Deleter and remove this snapshot entirely from the SnapshotList and continue with taking the db mutex. If there are still references, it will return without taking it out from the SnapshotList nor taking the db mutex
Motivation:
The most important information inside a snapshot is its Sequence number, which allows the compaction to know if the key-value should be deleted or not.
The sequence number is being changed when modification happens in the db.
This feature allows the db to take a snapshot without acquiring db mutex when the last snapshot has the same sequence number as a new one.
In transactional db with mostly read operations, it should improve performance when used with multithreaded environment and as well other scenarios of taking large amount of snapshots with mostly read operations.
This Feature must have folly library installed.
In order to cache the snapshots, there is last_snapshot_
(folly::atomic_shared_ptr, lock free atomic_shared_ptr) in order to
access the last_snapshot_ created and point to it.
For every GetSnapshotImpl call (where snapshots are being created), the
function checks if the sequence number is different than last_snapshot_,
if no, it creates new snapshot and inside this snapshot it adds a
reference to last_snapshot_ (the reference is cached_snapshot), so this sequence number will remain inside
SnapshotList (SnapshotList is the list of the snapshots in the system and used in compaction to show which snapshots are being used), if there are still snapshots holding this sequence number. If the sequence number as changed or the last_snapshot_ is nullptr it will create the snapshot while acquiring db_mutex.
For ReleaseSnapshotImpl (deleting a snapshot).
We will unref the last_snapshot_ (using comapre_exchange_weak) and if the refcount becomes 0, it will
call Deleter and remove this snapshot entirely from the SnapshotList and
continue with taking the db mutex.
If there are still references, it will return without taking it out from
the SnapshotList nor taking the db mutex