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
| return Status::OK(); | ||
| } | ||
| #ifdef ROCKSDB_SNAP_OPTIMIZATION | ||
| SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary, |
There was a problem hiding this comment.
you need to check if snapshot supported first... and if not exit immediately
| bool lock) { | ||
| int64_t unix_time = 0; | ||
| immutable_db_options_.clock->GetCurrentTime(&unix_time) | ||
| .PermitUncheckedError(); // Ignore error |
There was a problem hiding this comment.
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!
| 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.
last snapshot is fully. pls mention here that its atomic
| .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.
as mention GetLastPublishedSequence should be taken once in this function and use this local value
| 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.
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
| mutex_.AssertHeld(); | ||
| } | ||
| // returns null if the underlying memtable does not support snapshot. | ||
| if (!is_snapshot_supported_) { |
There was a problem hiding this comment.
this should be checked first, before doing something
| 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.
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.
| // inplace_update_support enabled. | ||
| return; | ||
| } | ||
| snapshots_.count_.fetch_sub(1); |
There was a problem hiding this comment.
no... if its a reference snap it wasnt part of the list
| 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.
exactly the same code as in the original scope. you copied the same code...
i dont think we should divide it at all.
|
|
||
| class SnapshotList { | ||
| public: | ||
| #ifdef ROCKSDB_SNAP_OPTIMIZATION |
There was a problem hiding this comment.
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
| if (!is_snapshot_supported_) { | ||
| return nullptr; | ||
| } | ||
| int64_t unix_time = 0; |
There was a problem hiding this comment.
not needed the unix time should be taken inside the RefSnapshot and _New
| .PermitUncheckedError(); // Ignore error | ||
| SnapshotImpl* s = new SnapshotImpl; | ||
| #ifdef SPEEDB_SNAP_OPTIMIZATION | ||
| if (RefSnapshot(unix_time, is_write_conflict_boundary, s)) { |
There was a problem hiding this comment.
as mention , unix_time shouldnt be an input parameter
|
|
||
| SnapshotImpl* DBImpl::GetSnapshotImpl(bool is_write_conflict_boundary, | ||
| bool lock) { | ||
| if (!is_snapshot_supported_) { |
There was a problem hiding this comment.
you didnt remove this check in line 3734
| delete s; | ||
| return nullptr; | ||
| } | ||
| immutable_db_options_.clock->GetCurrentTime(&unix_time) |
There was a problem hiding this comment.
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.
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
| struct Deleter { | ||
| inline void operator()(SnapshotImpl* snap) const; | ||
| }; | ||
| int64_t unix_time_; |
There was a problem hiding this comment.
why you created it again under ifdef??
you need it to be public changed it to all
| 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.
no need lock, the db mutex already protect you
| } | ||
|
|
||
| #ifdef SPEEDB_SNAP_OPTIMIZATION | ||
| SnapshotImpl* NewSnapRef(SequenceNumber seq, uint64_t unix_time, |
There was a problem hiding this comment.
i dont understand why you allocated another snapshot. its not right.
| // changing count_ always under snapshot_list mutex | ||
| uint64_t count_; | ||
| uint64_t logical_count() const { return logical_count_; } | ||
| std::atomic_uint64_t logical_count_; |
d30509c to
3b7a705
Compare
|
ready for review |
3b7a705 to
6be2ecf
Compare
| return nullptr; | ||
| } | ||
| SnapshotImpl* snapshot = snapshots_.RefSnapshot( | ||
| is_write_conflict_boundary, GetLastPublishedSequence(), GetSystemClock()); |
There was a problem hiding this comment.
GetSystemClock shouldn't be a parameter.. it should be called inside. that what we discussed
| 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.
again... not pass the GetSystemClock
| @@ -3747,7 +3742,6 @@ DBImpl::CreateTimestampedSnapshotImpl(SequenceNumber snapshot_seq, uint64_t ts, | |||
| int64_t unix_time = 0; | |||
| @@ -3747,7 +3742,6 @@ DBImpl::CreateTimestampedSnapshotImpl(SequenceNumber snapshot_seq, uint64_t ts, | |||
| int64_t unix_time = 0; | |||
| immutable_db_options_.clock->GetCurrentTime(&unix_time) | |||
| } | ||
|
|
||
| SnapshotImpl* snapshot = | ||
| snapshots_.New(s, snapshot_seq, unix_time, |
There was a problem hiding this comment.
the signature of the function shouldn't contains unix_time
| 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.
why doing double setting? set it directly to snapshot->unix_time_
| return false; | ||
| } | ||
|
|
||
| SnapshotImpl* New(SequenceNumber seq, SystemClock* clock, |
There was a problem hiding this comment.
dont pass the time in the function signature
| 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.
why you need that... you deleted the s in the Delete function. do it inside the Delete
|
|
||
| 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.
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
| 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.
as mention .. pass s and ts only
6be2ecf to
94ed6c4
Compare
|
@ayulas fixed |
|
|
||
| const bool need_update_seq = (snapshot_seq != kMaxSequenceNumber); | ||
|
|
||
| if (lock) { |
There was a problem hiding this comment.
do this after the check of is_snapshot_supported_. you will avoid lock unlock....like the GetSnapshotImpl
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