Skip to content

Conversation

@SupernaviX
Copy link
Collaborator

@SupernaviX SupernaviX commented Nov 26, 2025

Description

Right now, all the state modules handle rollbacks at the same time that they process new messages. After a rollback has occurred, the status field on the next BlockInfo will be RolledBack. When modules read this status from messages on their "synchronizer" topic (the topic they use to decide where on the chain they are), they roll back all state to right before the newly received message before processing it.

The trouble with this is

  1. If the chain rolls back, modules won't react to the rollback until the next time it rolls forward. That leaves our node in an inconsistent state until that roll forward.
  2. Nothing in the system signals which block we've rolled back to. This would be a problem for indexers, which need to know how far they've indexed for operational reasons.

What this PR does

Introduce a new StateTransition message, with a Rollback variant. The system is prepared to send Rollback state transition messages throughout itself. When a module receives a Rollback on its "synchronizer" topic, it will forward that Rollback to every topic it writes to. When a module receives a Rollback on any other topic it reads from, it will ignore it.

I've added two utilities to make this pattern easier to follow:

  • Subscription.read_ignoring_rollbacks will read from a subscription until it finds any message which is not a Rollback, and then return that.
  • RollbackAwareProducer writes messages to a specific topic, but will only write Rollback messages to that topic if they they would roll back the most recently published non-rollback message. The idea is that we only propagate rollback messages to downstream topics if there is anything from the topic to actually roll back.

I also added a Point type to use in the point message, and made PeerNetworkInterface accept that type in its FindIntersect command. That's because rollbacks can theoretically be all the way to the origin, and indexing could start at the origin.

What this PR does not do

Nothing is actually producing these new state transition messages yet. The PeerNetworkInterface will eventually publish them to cardano.block.available, but it doesn't yet.

Nothing depends on state transitions. They are forwarded around the system, but no functionality depends on them. Everything still relies on checking for block_info.status == RolledBack.

Related Issue(s)

Contributes to #421

How was this tested?

Ran omnibus, didn't see any behavioral differences from before. Which is what I expected, because the new messages aren't actually threaded through.

Checklist

  • My code builds and passes local tests
  • I added/updated tests for my changes, where applicable
  • I updated documentation (if applicable)
  • CI is green for this PR

Impact / Side effects

None

Reviewer notes / Areas to focus

I think the overall architecture is more relevant in this PR than any specific code.

@SupernaviX SupernaviX changed the title Sg/chain sync tweaks Introduce new rollback strategy Nov 26, 2025
@SupernaviX SupernaviX marked this pull request as ready for review November 26, 2025 19:55
/// Topic to publish on
topic: String,

// When did we publish our last non-rollback message
Copy link
Collaborator

Choose a reason for hiding this comment

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

When = slot number, by context? Could note here

// Get a mutable state
let mut state = history.lock().await.get_current_state();

// Read per-block topics in parallel
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah I see you're cleaning this up too, nice!

.await
}

/// Publish a rollback message, if we have anything to roll back
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there some trick to make this common across all publishers? In C++ it would just be a RollbackEnabledPublisher superclass (I miss inheritance sometimes!)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There's no formal concept of a "publisher" in this codebase at all, just a bunch of existing structs named Publisher with similar logic. I moved that logic into a RollbackAwarePublisher struct, which all of the existing structs are now just wrappers for.

context.run(async move {
// Get promise of params message so the params queue is cleared and
// the message is ready as soon as possible when we need it
let mut params_message = params_subscription.read();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ooh, more TypeScript mindset showing!

loop {
let (blk_g, gov_procs) = Self::read_governance(&mut governance_s).await?;
let (_, message) = governance_s.read().await?;
//let (blk_g, gov_procs) = Self::read_governance(&mut governance_s).await?;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Commented code alert... Is read_governance() no longer used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Removed.

read_governance is no longer used, because now this stream can emit two different messages (CardanoMessage::GovernanceProcedures or CardanoMessage::Rollback) and we want to handle both of them. Hiding the match-on-message-type logic inside a helper function would have made that complicated.

Copy link
Collaborator

@sandtreader sandtreader left a comment

Choose a reason for hiding this comment

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

Looks great!

Some minor comments, nothing big.

@SupernaviX SupernaviX merged commit a4db00d into main Dec 1, 2025
2 checks passed
@SupernaviX SupernaviX deleted the sg/chain-sync-tweaks branch December 1, 2025 15:46
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.

3 participants