Skip to content

feat(indexer): Introduce basic optimistic indexing #7235

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

Open
wants to merge 2 commits into
base: infra/feat/indexer-optimistic-indexing
Choose a base branch
from

Conversation

tomxey
Copy link
Contributor

@tomxey tomxey commented Jun 5, 2025

Description of change

Introduce basic optimistic indexing logic.

This basic implementation doesn't solve all known issues (race conditions, dependency checks, pruning, etc.), those will be solved later on the feature branch.

Links to any relevant issues

fixes #7212

How the change has been tested

Describe the tests that you ran to verify your changes.

Make sure to provide instructions for the maintainer as well as any relevant configurations.

  • Basic tests (linting, compilation, formatting, unit/integration tests)
  • Patch-specific tests (correctness, functionality coverage) - Adapted tests to see if basic optimistic indexing logic is working

Infrastructure QA (only required for crates that are maintained by @iotaledger/infrastructure)

  • Synchronization of the indexer from genesis for a network including migration objects.
  • Restart of indexer synchronization locally without resetting the database.
  • Restart of indexer synchronization on a production-like database.
  • Deployment of services using Docker.
  • Verification of API backward compatibility.

Release Notes

  • Protocol:
  • Nodes (Validators and Full nodes):
  • Indexer:
  • JSON-RPC:
  • GraphQL:
  • CLI:
  • Rust SDK:
  • REST API:

@tomxey tomxey self-assigned this Jun 5, 2025
Copy link

vercel bot commented Jun 5, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

4 Skipped Deployments
Name Status Preview Comments Updated (UTC)
apps-backend ⬜️ Ignored (Inspect) Visit Preview Jun 11, 2025 7:23am
apps-ui-kit ⬜️ Ignored (Inspect) Visit Preview 💬 Add feedback Jun 11, 2025 7:23am
rebased-explorer ⬜️ Ignored (Inspect) Visit Preview Jun 11, 2025 7:23am
wallet-dashboard ⬜️ Ignored (Inspect) Visit Preview Jun 11, 2025 7:23am

@iota-ci iota-ci added infrastructure Issues related to the Infrastructure Team sc-platform Issues related to the Smart Contract Platform group. labels Jun 5, 2025
@tomxey tomxey force-pushed the sc-platform/introduce-basic-optimistic-indexing branch from e4c92fa to 77a6f95 Compare June 5, 2025 07:07
@tomxey tomxey marked this pull request as ready for review June 5, 2025 08:38
@tomxey tomxey requested a review from a team as a code owner June 5, 2025 08:38
@tomxey tomxey force-pushed the sc-platform/introduce-basic-optimistic-indexing branch from 77a6f95 to 61689b2 Compare June 5, 2025 08:48
Comment on lines 452 to 472
let iota_transaction_response = match request_type {
None | Some(ExecuteTransactionRequestType::WaitForEffectsCert) => {
let mut node_response = self
.fullnode
.execute_transaction_block(tx_bytes, signatures, options.clone(), request_type)
.await
.map_err(error_object_from_rpc)?;
// it's not locally executed in indexer, no matter what is the status in the
// node
node_response.confirmed_local_execution = Some(false);
node_response
}
Some(ExecuteTransactionRequestType::WaitForLocalExecution) => {
self.execute_and_optimistically_index_tx_effects(
tx_bytes,
signatures,
options.clone(),
)
.await?
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need to bind the logic to the request_type that is defined in the context of the node.

Instead it is ok to execute always optimistically here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Users could gain a bit of performance that way by not requesting local execution if they don't need it.
Or do we assume that anyone who wants performance will just use the node directly?

Comment on lines +102 to +103
// TODO: shouldn't return type below be from rust-sdk types?
// Is this type correct?
Copy link
Contributor

Choose a reason for hiding this comment

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

This is ok. Why not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The iota-rest-api is producing return value using type defined in

pub struct TransactionExecutionResponse {
which contains TransactionEffects that comes from iota-rust-sdk.
The return value is serialized to bcs here:
AcceptFormat::Bcs => ResponseContent::Bcs(response),


Then the value is deserialized from bcs in

self.inner.bcs(response).await.map(Response::into_inner)

But the type used here is

pub struct TransactionExecutionResponse {
which is a different type (despite the same name) and the TransactionEffects there come from iota-types.


That's how the client was implemented before, and it works so far, but I wonder what guarantees we have about this.

Comment on lines +228 to +235
sql_query(
r#"
INSERT INTO tx_global_order (tx_digest, global_sequence_number)
SELECT $1, MAX(tx_sequence_number) FROM tx_digests
ON CONFLICT (tx_digest) DO NOTHING
"#,
)
.bind::<sql_types::Bytea, _>(&tx_digest_bytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

You can insert a TxGlobalOrder value directly, no? Why using the raw query?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Raw query makes it possible to do the read of tx_digests and insert in one query, otherwise it would be split into two queries.

Comment on lines +228 to +246
sql_query(
r#"
INSERT INTO tx_global_order (tx_digest, global_sequence_number)
SELECT $1, MAX(tx_sequence_number) FROM tx_digests
ON CONFLICT (tx_digest) DO NOTHING
"#,
)
.bind::<sql_types::Bytea, _>(&tx_digest_bytes)
.execute(conn)
},
Duration::from_secs(30)
)?;

run_query_async!(&pool, |conn| {
tx_global_order::table
.select(TxGlobalOrder::as_select())
.filter(tx_global_order::tx_digest.eq(tx_digest_bytes))
.first::<TxGlobalOrder>(conn)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

You can use returning to get the assigned value in one go.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would be possible, but with a bit more complex raw query, since with a basic one no value would be returned in case row is already present (ON CONFLICT (tx_digest) DO NOTHING).
I'll keep this comment on hold until it is decided what to do with remaining comments in this function.

Comment on lines 254 to 259
IndexedTransaction,
TxIndex,
Vec<IndexedEvent>,
Vec<EventIndex>,
BTreeMap<String, StoredDisplay>,
TransactionObjectChangesToCommit,
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider wrapping into a type TransactionDataToCommit for conciseness.

Copy link
Contributor

Choose a reason for hiding this comment

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

You can then also wrap CheckpointTransaction into a TransactionExtractor, implement methods to extract each individual component to index and implement TryFrom or From for CheckpointTransaction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think creting TransactionDataToCommitis a good idea.
Not sure about the TransactionExtractor though. Not all components that are being indexed are extracted independently, so it seems to me that implementation of the extractor would not be that simple.

Ok(())
}

fn optimistic_event_indices_from_indexed_event_indices(
Copy link
Contributor

Choose a reason for hiding this comment

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

Please consider simplifying: Copilot suggests

fn optimistic_event_indices_from_indexed_event_indices(
    event_indices: Vec<EventIndex>,
) -> OptimisticEventIndices {
    let splits: Vec<_> = event_indices.into_iter().map(|i| i.split()).collect();

    OptimisticEventIndices {
        optimistic_event_emit_packages: splits.iter().map(|t| t.0.clone().into()).collect(),
        optimistic_event_emit_modules: splits.iter().map(|t| t.1.clone().into()).collect(),
        optimistic_event_senders: splits.iter().map(|t| t.2.clone().into()).collect(),
        optimistic_event_struct_packages: splits.iter().map(|t| t.3.clone().into()).collect(),
        optimistic_event_struct_modules: splits.iter().map(|t| t.4.clone().into()).collect(),
        optimistic_event_struct_names: splits.iter().map(|t| t.5.clone().into()).collect(),
        optimistic_event_struct_instantiations: splits.iter().map(|t| t.6.clone().into()).collect(),
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is some cloning in the simplified version, but I suppose it should be ok. Applying.

Comment on lines +234 to +239
store: PgIndexerStore,
prometheus_registry: &Registry,
reader: IndexerReader,
config: &IndexerConfig,
custom_runtime: Option<Handle>,
metrics: IndexerMetrics,
Copy link
Contributor

Choose a reason for hiding this comment

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

This breaks the public API of the library.

Alternatively we can add a new OptimisticWriteApi that wraps WriteApi and holds the new optimistic methods.

Then we can add a new function here with the new signature to build the optimistic rpc-server.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this library intended to be used by users outside of IF?

@kodemartin kodemartin linked an issue Jun 10, 2025 that may be closed by this pull request
@tomxey tomxey requested a review from kodemartin June 11, 2025 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
infrastructure Issues related to the Infrastructure Team sc-platform Issues related to the Smart Contract Platform group.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[indexer]: Introduce basic optimistic indexing logic
3 participants