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

Add unrecorded blocks abstraction to gas price algo #2468

Open
wants to merge 99 commits into
base: master
Choose a base branch
from

Conversation

MitchTurner
Copy link
Member

@MitchTurner MitchTurner commented Nov 29, 2024

Linked Issues/PRs

Closes #2454

Description

This PR:

  • Adds a trait to the V1 algorithm for storing unrecorded blocks
  • Adds TransactionableStorage trait to gas price service to allow abstraction for atomic commits to storage (once per L2 block
  • Separates Get and Set traits for service to specify which need a transaction to execute
  • Add a GetSequenceNumber and SetSequence number concept for tracking the DA sequence number the DA service should start on at startup (I can't remember why this got included in this PR, but it felt logical at the time...)

We only need a key-value lookup for the unrecorded blocks. Instead of storing all the data in the metadata, we just moved it to its own abstraction that can be accessed directly from the algorithm. This will also help with historical view, since historical view doesn't have a way to iterate over all values for that height, just do KV lookups. We were iterating before this change.

Also added the unrecorded_block_bytes field to metadata so we can still track that in the case of historical view.

This exposed a problem with the design, which is that we need changes to the algorithm to be atomic per l2 block, which is how our Fuel Storage is designed. This led us to introducing a TransactionableStorage trait for the service, which can be handed to the algorithm updater before being "commit"ed. The same applies to the MetadataStorage, so the service was redesigned slightly to accomodate this change.

Checklist

  • New behavior is reflected in tests

Before requesting review

  • I have reviewed the code myself

MitchTurner and others added 30 commits October 30, 2024 17:26
…to feature/init-task-for-v1-gas-price-service
fn set_metadata(&mut self, metadata: &UpdaterMetadata) -> GasPriceResult<()> {
let block_height = metadata.l2_block_height();
let mut tx = self.write_transaction();
tx.storage_as_mut::<GasPriceMetadata>()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we creating a tx for 1 insert ?

Copy link
Member Author

Choose a reason for hiding this comment

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

I feel like that's what we do most places.

Copy link
Member Author

Choose a reason for hiding this comment

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

If we constrained with KeyValueMutate we could use put directly, but that's not what we have access to.


impl Mappable for SequenceNumberTable {
type Key = Self::OwnedKey;
type OwnedKey = u32;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we use BlockHeight type here to be more explicit ?

)?;

self.set_metadata().await?;
let metadata = self.algorithm_updater.clone().into();
Copy link
Contributor

Choose a reason for hiding this comment

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

AlgorithmUpdater have algorithm() method to get an algorithm. I find it strange to not have a metadata() but a into() to get metadata

Copy link
Member Author

Choose a reason for hiding this comment

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

Metadata doesn't exist as a concept from the algorithm updater's perspective. We have the metadata at the service level to be able to reproduce the updater in the case the service stops.

Copy link
Contributor

@rafal-ch rafal-ch left a comment

Choose a reason for hiding this comment

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

So far looks good, but I didn't finish the review yet, sorry. I'll do another pass ASAP. Currently leaving just a few minor comments.

@@ -34,4 +34,8 @@ impl DaBlockCostsSource for DummyDaBlockCosts {
}
}
}

async fn set_last_value(&mut self, sequence_number: u32) -> DaBlockCostsResult<()> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can DummyDaBlockCosts be behind the test flag?

crates/services/gas_price_service/src/v1/service.rs Outdated Show resolved Hide resolved

async fn handle_normal_block(
&mut self,
async fn handle_normal_block<'a>(
Copy link
Contributor

Choose a reason for hiding this comment

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

What does "normal" mean in this context? Maybe "non_genesis" would be more clear?

Copy link
Member Author

Choose a reason for hiding this comment

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

As opposed to genesis block. I know it's not a great name, but it's true that normally it will be this block. Haha. I'd be open to other names. "non-genesis" seems strange.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe just "handle_block()"?

crates/fuel-gas-price-algorithm/src/v1.rs Outdated Show resolved Hide resolved
crates/services/gas_price_service/src/v1/service.rs Outdated Show resolved Hide resolved
crates/services/gas_price_service/src/v1/service.rs Outdated Show resolved Hide resolved
Base automatically changed from feature/init-task-for-v1-gas-price-service to master December 6, 2024 22:56
@rafal-ch
Copy link
Contributor

rafal-ch commented Dec 9, 2024

Generic comment, probably not related to this PR.

It seems like some functions do not need to be async, for example: SharedGasPriceAlgo::update(). As a consequence, we might also remove async from some of the callers. We could revisit that, unless there is a plan for async processing in follow-up PRs.

@MitchTurner MitchTurner requested a review from a team December 9, 2024 23:33
Copy link
Collaborator

@xgreenx xgreenx left a comment

Choose a reason for hiding this comment

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

Almost done=) Some questions and suggestions

Comment on lines +171 to +176
if let Some(sequence_number) = self
.gas_price_db
.get_sequence_number(&metadata_height.into())?
{
self.da_source.set_last_value(sequence_number).await?;
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't understand how this part should work. You only set the sequencer number when you receive the bundle from the DA.

image

Let's say you received the bundle with sequencer number 10 at L2 block height X. After that, you processed the L2 block at height X + 1. After that, the node was restarted.

During start up the metadata_height will be X + 1, but the get_sequence_number will return None in this case. So you will not continue from the sequencer number 10.

Copy link
Member Author

@MitchTurner MitchTurner Dec 12, 2024

Choose a reason for hiding this comment

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

Ah. I see. So we need to set the most recent value again if it hasn't changed.

Or look for the most recent value... I think setting it every time would be better performance.

pub mod dummy_costs;
pub mod service;

#[derive(Debug, Default, Clone, Eq, Hash, PartialEq)]
pub struct DaBlockCosts {
pub sequence_number: u32,
Copy link
Collaborator

Choose a reason for hiding this comment

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

It would be better if it was bundle_id or if we somehow connected it to the block committer(comment can help)=)

tx.set_metadata(&metadata).map_err(|err| anyhow!(err))?;
AtomicStorage::commit_transaction(tx)?;
let new_algo = self.algorithm_updater.algorithm();
// self.update(new_algo).await;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// self.update(new_algo).await;

let capacity = Self::validate_block_gas_capacity(block_gas_capacity)?;
let mut storage_tx = self.storage_tx_provider.begin_transaction()?;

for da_block_costs in self.da_block_costs_buffer.drain(..) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

In the case of an error, you will lose da_block_costs_buffer data. Maybe it is better just to iterate here and clear it later after you committed changes to the database.

@MitchTurner MitchTurner requested a review from xgreenx December 12, 2024 05:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Modify MetadataStorage impl to store unrecorded_blocks efficiently
5 participants