Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Update SHiP to work with RocksDB #9635

Merged
merged 35 commits into from
Nov 11, 2020
Merged

Update SHiP to work with RocksDB #9635

merged 35 commits into from
Nov 11, 2020

Conversation

victorj8
Copy link
Contributor

@victorj8 victorj8 commented Nov 3, 2020

Change Description

To update SHiP to work with RocksDB the following approach was followed:

  • 2 methods were added to the current combined_database class to access its chainbase and rocksdb members.
  • The method create_deltas was modified to take a combined_database instead of only a chainbase object.
  • The current method that creates the deltas from the chainbase object was left intact.
  • To generate the deltas for the information stored on rocksdb, some common code on the rocksdb efforts were rearranged to walk through this data and be able to gather the different information we are interested (for contracts and kv).
  • After we obtain this data, new serialization methods were also required to be able to serialize this information into the deltas.
  • Once the deltas for the data on rocksdb are generated, they are appended to the ones generated on chainbase.
  • The SHiP unit tests for delta generation were updated to be tested with chainbase and rocksdb as backing store.
  • Some modification to these unit tests was also done, specially to test the deletion of objects, for that reason a smart contract used for testing was also updated.

Change Type

Select ONE

  • Documentation
  • Stability bug fix
  • Other
  • Other - special case

Consensus Changes

  • Consensus Changes

API Changes

  • API Changes

Documentation Additions

  • Documentation Additions

victorj8 and others added 29 commits October 22, 2020 11:04
@victorj8 victorj8 requested a review from tbfleming November 3, 2020 21:01
Copy link
Contributor

@swatanabe-b1 swatanabe-b1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hit 'n run

libraries/state_history/create_deltas.cpp Show resolved Hide resolved
if (!key) {
return;
}
const auto end_key = key.next_sub_key();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be next.

// will walk through all entries with the given prefix, so if passed an exact key, it will match that key
// and any keys with that key as a prefix
template <typename Receiver, typename Function = std::decay_t < decltype(process_all)>>
void walk_any_rocksdb_entries_with_prefix(const kv_undo_stack_ptr& kv_undo_stack,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function used anywhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, we can remove it

return eosio::session::shared_bytes(buffer.data(), buffer.size());
}

inline shared_bytes shared_bytes::previous() const {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this used?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

He and I were working off the same branch. My code needs it, but since I changed it since then, it would be fine to remove and then let me deal with the merge problems.

Comment on lines +157 to +172
int index = buffer.size() - 1;
while (true) {
auto& val = buffer[index];
if (val) {
--val;
++index; // ensure we increment any indexes past this one
break;
}
if (--index < 0) {
buffer.pop_back();
index = buffer.size();
}
}
while (index < buffer.size()) {
buffer[index] = std::numeric_limits<unsigned char>::max();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intended behavior of previous? The behavior of x.next() can be defined as the lowest value y such x < y and x is not a prefix of y. The converse of this is that x.previous() is the greatest value y such that y < x (which implies that x is not a prefix of y). This algorithm does not fulfill this definition, and in fact, no such value exists.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this wrong. The logic of next is really incorrect I believe. It should just increment at the index, and then rollback to 0, and then increment the next lower index. In the corner case of 0xfff..., that is less straight forward. I am thinking that in that case we would want it to then just append 0xff at the end. The new previous() logic is to decrement each index (starting at the last), if it is greater than 0 then exit, otherwise it rolls back to 0xff and then goes to the next lower index and repeats. For 0x0000...00, it just shortens the vector by 1

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of next is correct according to the definition I gave above (with the exception of FF* for which the "correct" result is undefined) and this matches how it is actually used. Based on how previous is used, (in chain_plugin.cpp read_only::get_table_by_scope), I don't think there is a way to define previous that will work correctly.


auto buffer = std::vector<unsigned char>{ std::begin(*this), std::end(*this) };

int index = buffer.size() - 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This limits the buffer to 2GiB.

}
if (--index < 0) {
buffer.pop_back();
index = buffer.size();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to break, otherwise UB at the top of the next loop iteration.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method has been changed quite a bit in my code.

};

template <typename F>
void walk_rocksdb_entries_with_prefix(const kv_undo_stack_ptr& kv_undo_stack,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with_prefix is no longer accurate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix this in my PR to avoid having to deal with the conflicts

}
void add_kv_table_to_snapshot(const snapshot_writer_ptr& snapshot, const chainbase::database& db, const kv_undo_stack_ptr& kv_undo_stack) {
snapshot->write_section<kv_object>([&db,&kv_undo_stack](auto& section) {
if (kv_undo_stack && db.get<kv_db_config_object>().backing_store == backing_store_type::ROCKSDB) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one implies the other here so we should only have to check the backing_store.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but would like to do that in my PR, since this check occurs in multiple places besides this one.

libraries/chain/combined_database.cpp Show resolved Hide resolved
}
});
// write out any remaining key-values
kv_database->write(key_values);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might want to check if there are actually any values to write. This method won't entirely be a no-op if the container is empty.

Copy link
Contributor

@brianjohnson5972 brianjohnson5972 Nov 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a good optimization to put into that write method. Can you do it as part of your PR for the replay fixes?

libraries/chain/combined_database.cpp Show resolved Hide resolved
virtual void wrong_row(const char* type_received) = 0;
};
namespace detail {
class manage_stack {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to make a note of, with the recent changes to fix replay (https://github.com/EOSIO/eos/pull/9642/files) this will no longer be needed. The undo_stack will now always return something when top is called.

Copy link
Contributor

@brianjohnson5972 brianjohnson5972 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like the #warning added, but otherwise it is approved.

@brianjohnson5972 brianjohnson5972 merged commit 2077111 into develop Nov 11, 2020
@swatanabe-b1 swatanabe-b1 mentioned this pull request Nov 19, 2020
7 tasks
@brianjohnson5972 brianjohnson5972 deleted the rocksdb_ship branch November 21, 2020 15:04
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants