Skip to content

feat: client reconnection management - #1179

Open
ganymedio wants to merge 6 commits into
feature/da-sequencer-nodefrom
andygolay/client-connection-tests
Open

feat: client reconnection management#1179
ganymedio wants to merge 6 commits into
feature/da-sequencer-nodefrom
andygolay/client-connection-tests

Conversation

@ganymedio

@ganymedio ganymedio commented Apr 3, 2025

Copy link
Copy Markdown
Contributor

Summary

Changelog

Add two tests to da sequencer client crate:

  • test_client_reconnect_if_connection_fails
  • test_reopen_block_stream_at_correct_height

with mock RPC methods.

Testing

  • cargo test -p movement-da-sequencer-client

@ganymedio ganymedio linked an issue Apr 3, 2025 that may be closed by this pull request
@ganymedio ganymedio self-assigned this Apr 3, 2025
@ganymedio
ganymedio requested a review from sebtomba April 3, 2025 11:35
@ganymedio
ganymedio marked this pull request as ready for review April 3, 2025 11:35

@musitdev musitdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The test is a good start but:

  • the server start and shutdown doesn't work
  • the client reconnection logic must be implemented in the client. It's not done currently.
    So you should update the test that failed, then implements the logic to make the test pass.


let service = DaSequencerNodeServiceServer::new(MockService);

tokio::spawn(async move {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see the purpose of the oneshot channel. To shut down the server you've to abort the task or stop the server but here, the channel just waits before starting the server.

@ganymedio
ganymedio marked this pull request as draft April 3, 2025 14:22
@ganymedio ganymedio changed the title feat: client connection tests feat: client reconnection management Apr 3, 2025
@ganymedio
ganymedio marked this pull request as ready for review April 3, 2025 22:16
@ganymedio
ganymedio requested a review from musitdev April 3, 2025 22:16

@musitdev musitdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The code is becoming complicated and doesn’t fully address our needs. I think some parts should be managed by the caller (the fullnode), while others should be handled here. I need to think about it more.

Simple methods like batch_write can be handled easily, but if batch_write fails, it means the stream has an issue and should be restarted. Perhaps we should use a heartbeat from the client to the server in parallel with the current one to detect issues and bypass connection errors.

In parallel, we also need to manage the current server heartbeat, which could be used to detect an idle connection that arrives intermittently.

Let’s keep this PR for now. I’ll open a specific issue to describe the entire behavior. We will see what we do with the PR after.

Err(err) => {
tracing::warn!(
"DA sequencer Http2 connection failed: {}. Retrying in 10s...",
"DA sequencer HTTP/2 connection failed: {}. Retrying in 2s...",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Set to 10 sec because it takes more than 2s to restart the DA.

client: DaSequencerNodeServiceClient<tonic::transport::Channel>,
client: DaSequencerNodeServiceClient<Channel>,
connection_string: String,
last_received_height: Arc<Mutex<Option<u64>>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

last_received_height is a u64 to you can use AtomicU64 that is more efficient than a mutex.

@ganymedio ganymedio Apr 4, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It should still use Arc though? In that case, in try_connect, what value should be used in the returned client?

We had

last_received_height: Arc::new(Mutex::new(None)),

So I'm thinking either

last_received_height: Arc::new(AtomicU64::new(0)),

or

last_received_height: Arc::new(AtomicU64::new(u64::MAX))

would make sense?

I'm thinking MAX would be better, to represent that no value is set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes Max is better for a non Value. You still need the Arc to share the Atomic reference.

)),
Err(e) => {
tracing::warn!("stream_read_from_height failed, trying reconnect: {e}");
self.reconnect()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You try only one time immediately. You should have more retry and wait a little before retrying.

tracing::warn!("stream_read_from_height failed, trying reconnect: {e}");
self.reconnect()
.await
.map_err(|e| Status::unavailable(format!("Reconnect failed: {e}")))?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No error should be return (ie: no ?) until the end of the reconnect loop.

stream: Streaming<StreamReadFromHeightResponse>,
last_received_height: Arc<Mutex<Option<u64>>>,
) -> Pin<Box<dyn Stream<Item = Result<StreamReadFromHeightResponse, Status>> + Send>> {
let wrapped = unfold((stream, last_received_height), |(mut s, tracker)| async move {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You should call tracker last_received_height

Some((Ok(msg), (s, tracker)))
}
Ok(None) => None,
Err(e) => Some((Err(e), (s, tracker))),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the stream break or return an error, we should try to reconnect. But it starts to be very complicated.

@larryl3u
larryl3u force-pushed the feature/da-sequencer-node branch from 42111f1 to 120d1e9 Compare October 6, 2025 20:31
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.

da-squencer: Client reconnection management

2 participants