Skip to content
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

Moved StorageTransaction to the fuel-core-storage crate #1694

Merged
merged 42 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3783579
Extraction of the `FuelBlockSecondaryKeyBlockHeights` table to an off…
xgreenx Feb 15, 2024
3c2d424
Updated CHANGELOG.md
xgreenx Feb 15, 2024
7e8689c
Merge branch 'master' into feature/blocks-second-key-extraction
xgreenx Feb 17, 2024
1d95e63
Merge with `master`
xgreenx Feb 17, 2024
95dfa58
Fix CI
xgreenx Feb 17, 2024
111efbd
Apply comments from the PR
xgreenx Feb 18, 2024
cabca80
Merge branch 'master' into feature/blocks-second-key-extraction
xgreenx Feb 18, 2024
0e20633
Merge branch 'master' into feature/blocks-second-key-extraction
xgreenx Feb 18, 2024
4e8ebc6
Merge branch 'master' into feature/blocks-second-key-extraction
Voxelot Feb 20, 2024
ad09341
Moved `StorageTransaction` to the `fuel-core-storage` crate
xgreenx Feb 21, 2024
b619743
Merge branch 'feature/blocks-second-key-extraction' into feature/comm…
xgreenx Feb 21, 2024
71dbc41
Merge branch 'master' into feature/blocks-second-key-extraction
xgreenx Feb 21, 2024
593ad52
Apply comments from the review
xgreenx Feb 21, 2024
97a96d5
Renamed `FuelBlockSecondaryKeyBlockHeights`
xgreenx Feb 21, 2024
3ce8c17
Remove contstrain
xgreenx Feb 21, 2024
802578e
Merge branch 'master' into feature/blocks-second-key-extraction
xgreenx Feb 21, 2024
5cc6c33
Merge branch 'feature/blocks-second-key-extraction' into feature/comm…
xgreenx Feb 22, 2024
0137658
Merge conflicts
xgreenx Feb 22, 2024
cc86bb6
Merge branch 'master' into feature/commit-changes
xgreenx Feb 22, 2024
5b9bd78
Generalized the implementation of the iterator functionality.
xgreenx Feb 22, 2024
7b1141a
Implemented tracking of the height during committing of the changes.
xgreenx Feb 23, 2024
305e567
Small nits
xgreenx Feb 23, 2024
4716c15
Make clippy happy
xgreenx Feb 23, 2024
518a842
Updated CHANGELOG.md
xgreenx Feb 23, 2024
253d188
Merge branch 'master' into feature/commit-changes
xgreenx Feb 27, 2024
f44b903
Removed TODOs
xgreenx Feb 27, 2024
00ef712
Apply comments from the PR
xgreenx Feb 27, 2024
0c27ff9
Apply comments from the PR
xgreenx Feb 28, 2024
fb5bc50
Merge branch 'master' into feature/commit-changes
xgreenx Feb 28, 2024
25b9f57
Merge branch 'master' into feature/commit-changes
xgreenx Mar 6, 2024
309a8bf
Merge with `master` before regenesis
xgreenx Mar 6, 2024
2e64af8
Merge branch 'master' into feature/commit-changes
xgreenx Mar 7, 2024
d682dd8
Merged with the `master`
xgreenx Mar 7, 2024
9344069
Address comemnts form the PR
xgreenx Mar 7, 2024
154328c
Merge branch 'master' into feature/commit-changes
xgreenx Mar 11, 2024
f8b9c07
Merge branch 'master' into feature/commit-changes
xgreenx Mar 11, 2024
d0b95df
Merge branch 'master' into feature/commit-changes
xgreenx Mar 11, 2024
ec92011
Merged `master`
xgreenx Mar 11, 2024
ffecd1d
Fixed tests
xgreenx Mar 11, 2024
8b3be42
Applied comments from the PR
xgreenx Mar 13, 2024
551baf6
Updated the implementation to show the relationship to the semaphore.
xgreenx Mar 13, 2024
f909992
Small nits
xgreenx Mar 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

Description of the upcoming release here.

### Added

- [#1740](https://github.com/FuelLabs/fuel-core/pull/1740): Remove optional fields from genesis configs
Expand All @@ -15,6 +17,30 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
### Changed

#### Breaking
- [#1694](https://github.com/FuelLabs/fuel-core/pull/1694): The change moves the database transaction logic from the `fuel-core` to the `fuel-core-storage` level. The corresponding [issue](https://github.com/FuelLabs/fuel-core/issues/1589) described the reason behind it.

## Technical details of implementation

- The change splits the `KeyValueStore` into `KeyValueInspect` and `KeyValueMutate`, as well the `Blueprint` into `BlueprintInspect` and `BlueprintMutate`. It allows requiring less restricted constraints for any read-related operations.

- One of the main ideas of the change is to allow for the actual storage only to implement `KeyValueInspect` and `Modifiable` without the `KeyValueMutate`. It simplifies work with the databases and provides a safe way of interacting with them (Modification into the database can only go through the `Modifiable::commit_changes`). This feature is used to [track the height](https://github.com/FuelLabs/fuel-core/pull/1694/files#diff-c95a3d57a39feac7c8c2f3b193a24eec39e794413adc741df36450f9a4539898) of each database during commits and even limit how commits are done, providing additional safety. This part of the change was done as a [separate commit](https://github.com/FuelLabs/fuel-core/pull/1694/commits/7b1141ac838568e3590f09dd420cb24a6946bd32).

- The `StorageTransaction` is a `StructuredStorage` that uses `InMemoryTransaction` inside to accumulate modifications. Only `InMemoryTransaction` has a real implementation of the `KeyValueMutate`(Other types only implement it in tests).

- The implementation of the `Modifiable` for the `Database` contains a business logic that provides additional safety but limits the usage of the database. The `Database` now tracks its height and is responsible for its updates. In the `commit_changes` function, it analyzes the changes that were done and tries to find a new height(For example, in the case of the `OnChain` database, we are looking for a new `Block` in the `FuelBlocks` table).

- As was planned in the issue, now the executor has full control over how commits to the storage are done.

- All mutation methods now require `&mut self` - exclusive ownership over the object to be able to write into it. It almost negates the chance of concurrent modification of the storage, but it is still possible since the `Database` implements the `Clone` trait. To be sure that we don't corrupt the state of the database, the `commit_changes` function implements additional safety checks to be sure that we commit updates per each height only once time.

- Side changes:
- The `drop` function was moved from `Database` to `RocksDB` as a preparation for the state rewind since the read view should also keep the drop function until it is destroyed.
- The `StatisticTable` table lives in the off-chain worker.
- Removed duplication of the `Database` from the `dap::ConcreteStorage` since it is already available from the VM.
- The executor return only produced `Changes` instead of the storage transaction, which simplifies the interaction between modules and port definition.
- The logic related to the iteration over the storage is moved to the `fuel-core-storage` crate and is now reusable. It provides an `interator` method that duplicates the logic from `MemoryStore` on iterating over the `BTreeMap` and methods like `iter_all`, `iter_all_by_prefix`, etc. It was done in a separate revivable [commit](https://github.com/FuelLabs/fuel-core/pull/1694/commits/5b9bd78320e6f36d0650ec05698f12f7d1b3c7c9).
- The `MemoryTransactionView` is fully replaced by the `StorageTransactionInner`.
- Removed `flush` method from the `Database` since it is not needed after https://github.com/FuelLabs/fuel-core/pull/1664.

- [#1693](https://github.com/FuelLabs/fuel-core/pull/1693): The change separates the initial chain state from the chain config and stores them in separate files when generating a snapshot. The state snapshot can be generated in a new format where parquet is used for compression and indexing while postcard is used for encoding. This enables importing in a stream like fashion which reduces memory requirements. Json encoding is still supported to enable easy manual setup. However, parquet is prefered for large state files.

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion benches/benches/block_target_gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ use ed25519_dalek::Signer;
use ethnum::U256;
use fuel_core::{
combined_database::CombinedDatabase,
database::{
balances::BalancesInitializer,
state::StateInitializer,
},
service::{
config::Trigger,
Config,
Expand Down Expand Up @@ -256,7 +260,7 @@ fn service_with_many_contracts(
.build()
.unwrap();
let _drop = rt.enter();
let mut database = Database::rocksdb();
let mut database = Database::rocksdb_temp();
let mut config = Config::local_node();
config
.chain_config
Expand Down
60 changes: 37 additions & 23 deletions benches/benches/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,19 @@ use criterion::{
BenchmarkGroup,
Criterion,
};
use fuel_core::database::Database;
use fuel_core_storage::vm_storage::VmStorage;
use fuel_core::database::{
database_description::on_chain::OnChain,
state::StateInitializer,
Database,
};
use fuel_core_storage::{
transactional::{
IntoTransaction,
ReadTransaction,
WriteTransaction,
},
vm_storage::VmStorage,
};
use fuel_core_types::{
blockchain::header::GeneratedConsensusFields,
fuel_tx::Bytes32,
Expand All @@ -28,7 +39,10 @@ use std::{
#[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;

fn setup(db: &mut Database, contract: &ContractId, n: usize) {
fn setup<D>(db: &mut D, contract: &ContractId, n: usize)
where
D: StateInitializer,
{
let mut rng_keys = thread_rng();
let gen_keys = || -> Bytes32 { rng_keys.gen() };
let state_keys = iter::repeat_with(gen_keys).take(n);
Expand All @@ -51,16 +65,16 @@ fn insert_state_single_contract_database(c: &mut Criterion) {

let mut bench_state = |group: &mut BenchmarkGroup<WallTime>, name: &str, n: usize| {
group.bench_function(name, |b| {
let mut db = VmStorage::default();
let mut db = Database::<OnChain>::default();
let contract: ContractId = rng.gen();
setup(db.database_mut(), &contract, n);
let outer = db.database_mut().transaction();
setup(&mut db, &contract, n);
let outer = db.write_transaction();
b.iter_custom(|iters| {
let mut elapsed_time = Duration::default();
for _ in 0..iters {
let mut inner = outer.transaction();
let inner = outer.read_transaction();
let mut inner_db = VmStorage::new::<GeneratedConsensusFields>(
inner.as_mut().clone(),
inner,
&Default::default(),
Default::default(),
);
Expand Down Expand Up @@ -112,16 +126,16 @@ fn insert_state_single_contract_transaction(c: &mut Criterion) {

let mut bench_state = |group: &mut BenchmarkGroup<WallTime>, name: &str, n: usize| {
group.bench_function(name, |b| {
let mut db = VmStorage::<Database>::default();
let db = Database::<OnChain>::default();
let contract: ContractId = rng.gen();
let mut outer = db.database_mut().transaction();
setup(outer.as_mut(), &contract, n);
let mut outer = db.into_transaction();
setup(&mut outer, &contract, n);
b.iter_custom(|iters| {
let mut elapsed_time = Duration::default();
for _ in 0..iters {
let mut inner = outer.transaction();
let inner = outer.read_transaction();
let mut inner_db = VmStorage::new::<GeneratedConsensusFields>(
inner.as_mut().clone(),
inner,
&Default::default(),
Default::default(),
);
Expand Down Expand Up @@ -173,19 +187,19 @@ fn insert_state_multiple_contracts_database(c: &mut Criterion) {

let mut bench_state = |group: &mut BenchmarkGroup<WallTime>, name: &str, n: usize| {
group.bench_function(name, |b| {
let mut db = VmStorage::<Database>::default();
let mut db = Database::<OnChain>::default();
for _ in 0..n {
let contract: ContractId = rng.gen();
setup(db.database_mut(), &contract, 1);
setup(&mut db, &contract, 1);
}
let outer = db.database_mut().transaction();
let outer = db.into_transaction();
b.iter_custom(|iters| {
let mut elapsed_time = Duration::default();
let contract: ContractId = rng.gen();
for _ in 0..iters {
let mut inner = outer.transaction();
let inner = outer.read_transaction();
let mut inner_db = VmStorage::new::<GeneratedConsensusFields>(
inner.as_mut().clone(),
inner,
&Default::default(),
Default::default(),
);
Expand Down Expand Up @@ -237,19 +251,19 @@ fn insert_state_multiple_contracts_transaction(c: &mut Criterion) {

let mut bench_state = |group: &mut BenchmarkGroup<WallTime>, name: &str, n: usize| {
group.bench_function(name, |b| {
let mut db = VmStorage::<Database>::default();
let mut outer = db.database_mut().transaction();
let db = Database::<OnChain>::default();
let mut outer = db.into_transaction();
for _ in 0..n {
let contract: ContractId = rng.gen();
setup(outer.as_mut(), &contract, 1);
setup(&mut outer, &contract, 1);
}
b.iter_custom(|iters| {
let mut elapsed_time = Duration::default();
let contract: ContractId = rng.gen();
for _ in 0..iters {
let mut inner = outer.transaction();
let inner = outer.read_transaction();
let mut inner_db = VmStorage::new::<GeneratedConsensusFields>(
inner.as_mut().clone(),
inner,
&Default::default(),
Default::default(),
);
Expand Down
25 changes: 16 additions & 9 deletions benches/benches/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ use criterion::{
BenchmarkGroup,
Criterion,
};
use std::sync::Arc;

use crate::vm_initialization::vm_initialization;
use contract::*;
use fuel_core_benches::*;
use fuel_core_storage::transactional::IntoTransaction;
use fuel_core_types::fuel_asm::Instruction;
use vm_set::*;

Expand All @@ -37,15 +39,19 @@ where

let clock = quanta::Clock::new();

let original_db = vm.as_mut().database_mut().clone();
// During block production/validation for each state, which may affect the state of the database,
// we create a new storage transaction. The code here simulates the same behavior to have
// the same nesting level and the same performance.
let block_database_tx = original_db.clone().into_transaction();
let relayer_database_tx = block_database_tx.into_transaction();
let thread_database_tx = relayer_database_tx.into_transaction();
let tx_database_tx = thread_database_tx.into_transaction();
MitchTurner marked this conversation as resolved.
Show resolved Hide resolved
let database = Database::new(Arc::new(tx_database_tx));
*vm.as_mut().database_mut() = database.into_transaction();

let mut total = core::time::Duration::ZERO;
for _ in 0..iters {
let original_db = vm.as_mut().database_mut().clone();
// Simulates the block production/validation with three levels of database transaction.
let block_database_tx = original_db.transaction().as_ref().clone();
let tx_database_tx = block_database_tx.transaction().as_ref().clone();
let vm_tx_database_tx = tx_database_tx.transaction().as_ref().clone();
*vm.as_mut().database_mut() = vm_tx_database_tx;

let start = black_box(clock.raw());
match instruction {
Instruction::CALL(call) => {
Expand All @@ -60,9 +66,10 @@ where
let end = black_box(clock.raw());
total += clock.delta(start, end);
vm.reset_vm_state(diff);
// restore original db
*vm.as_mut().database_mut() = original_db;
// Reset database changes.
vm.as_mut().database_mut().reset_changes();
}
*vm.as_mut().database_mut() = original_db;
total
})
});
Expand Down
21 changes: 17 additions & 4 deletions benches/benches/vm_set/blockchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ use criterion::{
Throughput,
};
use fuel_core::{
database::{
balances::BalancesInitializer,
database_description::on_chain::OnChain,
state::StateInitializer,
},
service::Config,
state::rocks_db::{
RocksDb,
Expand All @@ -21,6 +26,10 @@ use fuel_core::{
use fuel_core_benches::*;
use fuel_core_storage::{
tables::FuelBlocks,
transactional::{
IntoTransaction,
StorageTransaction,
},
vm_storage::{
IncreaseStorageKey,
VmStorage,
Expand Down Expand Up @@ -60,7 +69,8 @@ impl BenchDb {
fn new(contract_id: &ContractId) -> anyhow::Result<Self> {
let tmp_dir = ShallowTempDir::new();

let db = Arc::new(RocksDb::default_open(tmp_dir.path(), None).unwrap());
let db =
Arc::new(RocksDb::<OnChain>::default_open(tmp_dir.path(), None).unwrap());
let mut storage_key = primitive_types::U256::zero();
let mut key_bytes = Bytes32::zeroed();

Expand Down Expand Up @@ -103,7 +113,6 @@ impl BenchDb {
&block.compress(&config.chain_config.consensus_parameters.chain_id),
)
.unwrap();
database.clone().flush()?;

Ok(Self {
_tmp_dir: tmp_dir,
Expand All @@ -112,14 +121,18 @@ impl BenchDb {
}

/// Creates a `VmDatabase` instance.
fn to_vm_database(&self) -> VmStorage<Database> {
fn to_vm_database(&self) -> VmStorage<StorageTransaction<Database>> {
let header = ConsensusHeader {
prev_root: Default::default(),
height: 1.into(),
time: Tai64::UNIX_EPOCH,
generated: (),
};
VmStorage::new(self.db.clone(), &header, ContractId::zeroed())
VmStorage::new(
self.db.clone().into_transaction(),
&header,
ContractId::zeroed(),
)
}
}

Expand Down
13 changes: 6 additions & 7 deletions benches/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ use fuel_core_types::{
},
};

use fuel_core_storage::transactional::StorageTransaction;
pub use rand::Rng;
use std::iter;

const LARGE_GAS_LIMIT: u64 = u64::MAX - 1001;

fn new_db() -> VmStorage<Database> {
fn new_db() -> VmStorage<StorageTransaction<Database>> {
// when rocksdb is enabled, this creates a new db instance with a temporary path
VmStorage::default()
}
Expand Down Expand Up @@ -89,7 +90,7 @@ pub struct VmBench {
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
pub witnesses: Vec<Witness>,
pub db: Option<VmStorage<Database>>,
pub db: Option<VmStorage<StorageTransaction<Database>>>,
pub instruction: Instruction,
pub prepare_call: Option<PrepareCall>,
pub dummy_contract: Option<ContractId>,
Expand All @@ -100,7 +101,7 @@ pub struct VmBench {

#[derive(Debug, Clone)]
pub struct VmBenchPrepared {
pub vm: Interpreter<VmStorage<Database>, Script>,
pub vm: Interpreter<VmStorage<StorageTransaction<Database>>, Script>,
pub instruction: Instruction,
pub diff: diff::Diff<diff::InitialVmState>,
}
Expand Down Expand Up @@ -148,7 +149,7 @@ impl VmBench {

pub fn contract_using_db<R>(
rng: &mut R,
mut db: VmStorage<Database>,
mut db: VmStorage<StorageTransaction<Database>>,
instruction: Instruction,
) -> anyhow::Result<Self>
where
Expand Down Expand Up @@ -207,7 +208,7 @@ impl VmBench {
.with_prepare_call(prepare_call))
}

pub fn with_db(mut self, db: VmStorage<Database>) -> Self {
pub fn with_db(mut self, db: VmStorage<StorageTransaction<Database>>) -> Self {
self.db.replace(db);
self
}
Expand Down Expand Up @@ -468,8 +469,6 @@ impl TryFrom<VmBench> for VmBenchPrepared {

let start_vm = vm.clone();
let original_db = vm.as_mut().database_mut().clone();
let database_tx = original_db.transaction().as_ref().clone();
*vm.as_mut().database_mut() = database_tx;
let mut vm = vm.add_recording();
match instruction {
Instruction::CALL(call) => {
Expand Down
2 changes: 1 addition & 1 deletion bin/fuel-core/src/cli/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ fn load_chain_config(
}

fn open_db(path: &Path) -> anyhow::Result<Database> {
Database::<OnChain>::open(path, None)
Database::<OnChain>::open_rocksdb(path, None)
.map_err(Into::<anyhow::Error>::into)
.context(format!("failed to open database at path {path:?}",))
}
Expand Down
Loading
Loading