Skip to content

Feat/fix mplex stream muxer issues #742

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 16 commits into
base: main
Choose a base branch
from

Conversation

Aldorax
Copy link

@Aldorax Aldorax commented Jul 2, 2025

PR: Mplex Stream Muxer TODOs Resolution

This PR addresses several TODOs in the Mplex stream muxer implementation, significantly improving type safety, logging, and overall robustness.

What was wrong?

The mplex.py implementation had four outstanding TODO items that detracted from code quality and made debugging difficult:

  1. Return Type Inconsistency: The send_message function was type-hinted to return an int, but its underlying call to write_to_stream returned None. This led to type checker warnings and an unclear API contract.

  2. Missing Logging for Unknown Flags: The system would silently handle and reset streams that received messages with unknown header flags, providing no visibility into this protocol violation. This made diagnosing unexpected behavior challenging.

  3. Missing Warning for Unknown Stream Messages: Messages received for a stream that wasn't recognized by the host were silently dropped, potentially hiding issues with peer behavior or connection state.

  4. Missing Warning for Data After Stream Close: If a remote peer sent data after it had already closed its end of the stream, the data was silently ignored. This could obscure misbehaving peers or protocol errors.

How was it fixed?

This PR resolves all four issues by implementing direct and effective solutions:

  1. Return Type Fixed: The write_to_stream method was updated to return len(_bytes), which represents the number of bytes successfully written. Consequently, the type hints for both write_to_stream and send_message were aligned to correctly return an int, resolving the type inconsistency and removing the # type: ignore comment.

  2. Logging Added: logger.warning calls were strategically added to the handlers for:

    • Unknown flags: When messages with unrecognized header flags are received.

    • Messages to unknown streams: When data arrives for a stream ID that the local muxer does not recognize.

    • Data received after a stream was closed: When a peer attempts to send data on a stream that has already been locally closed.
      This makes protocol violations visible in the logs, which is critical for debugging and understanding peer behavior.

  3. Tests Added: A comprehensive suite of tests was added to validate the new behavior. This includes:

    • Runtime type checks for the send_message return value to ensure it consistently returns an int.

    • Logging verification tests using the capsys fixture to confirm that the correct warning messages are emitted under the specified error conditions (unknown flags, unknown streams, data after close).

Summary of Approach

The approach was to systematically address each TODO item in the Mplex implementation. The core of the work involved aligning function signatures with their behavior for better type safety and injecting informative logging statements at key points where protocol violations can occur. This significantly enhances the debuggability and robustness of the stream muxer. A corresponding test suite was developed to lock in these fixes and prevent future regressions. The final step was a cleanup pass to ensure all code adhered to the project's linting and static analysis standards.

To-Do

  • Clean up commit history

  • Add or update documentation related to these changes

  • Add entry to the release notes

Cute Animal Picture

Cute Puppy

The issue this PR #741

@@ -2016,39 +2112,6 @@ def is_expired(self) -> bool:
# ------------------ multiselect_communicator interface.py ------------------
Copy link
Member

Choose a reason for hiding this comment

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

this line should move along with the class definition

Copy link
Author

Choose a reason for hiding this comment

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

Thanks @pacrob Will handle that as I clock in tommorrow.

@@ -286,6 +288,25 @@ async def _swarm_stream_handler(self, net_stream: INetStream) -> None:
)
await net_stream.reset()
return

# --- NEW CODE: Handle case where protocol is None ---
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# --- NEW CODE: Handle case where protocol is None ---
# Handle case where protocol is None

# raise StreamFailure(f"No protocol selected from peer {peer_id}")
# But the 'return' is consistent with the `except` block's handling.
return
# --- END NEW CODE ---
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# --- END NEW CODE ---

@@ -276,6 +277,7 @@ async def close(self) -> None:
async def _swarm_stream_handler(self, net_stream: INetStream) -> None:
# Perform protocol muxing to determine protocol to use
try:
# The protocol returned here can now be None
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
# The protocol returned here can now be None

@@ -48,7 +48,6 @@ def add_handler(
"""
self.handlers[protocol] = handler

# FIXME: Make TProtocol Optional[TProtocol] to keep types consistent
async def negotiate(
self,
communicator: IMultiselectCommunicator,
Copy link
Member

Choose a reason for hiding this comment

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

While you're here, please update the docstring to match the arguments (specifically communicator vs stream).

Copy link
Author

Choose a reason for hiding this comment

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

Sure. Will do.

@@ -48,7 +48,6 @@ def add_handler(
"""
self.handlers[protocol] = handler

# FIXME: Make TProtocol Optional[TProtocol] to keep types consistent
Copy link
Member

Choose a reason for hiding this comment

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

You've removed the FIXME comment, but not updated the return type accordingly.

@pacrob
Copy link
Member

pacrob commented Jul 10, 2025

General note on code comments - they should briefly describe what is happening in the following code if it not clear. They should not describe the changes happening in a particular PR. You can add those as comments here on the PR page for things you think need additional clarity.

@pacrob
Copy link
Member

pacrob commented Jul 11, 2025

@Aldorax it looks like this PR duplicates work from #738. Can that one be closed in favor of this one?

@seetadev
Copy link
Contributor

@Aldorax it looks like this PR duplicates work from #738. Can that one be closed in favor of this one?

@pacrob : Good find, indeed. Thank you for sharing the feedback to @Aldorax .

@Aldorax : Could you please let us know which PR should be closed out of the 2 PRs? Thanks.

@Aldorax
Copy link
Author

Aldorax commented Jul 13, 2025

@Aldorax it looks like this PR duplicates work from #738. Can that one be closed in favor of this one?

@pacrob : Good find, indeed. Thank you for sharing the feedback to @Aldorax .

@Aldorax : Could you please let us know which PR should be closed out of the 2 PRs? Thanks.

@seetadev The other PR can be closed in favor of this one.

@Aldorax
Copy link
Author

Aldorax commented Jul 13, 2025

General note on code comments - they should briefly describe what is happening in the following code if it not clear. They should not describe the changes happening in a particular PR. You can add those as comments here on the PR page for things you think need additional clarity.

My bad. Thanks for letting me know. Will follow this comment format going forward.

@seetadev seetadev mentioned this pull request Jul 13, 2025
3 tasks
@Aldorax
Copy link
Author

Aldorax commented Jul 21, 2025

@seetadev Changes have been made accordingly

@acul71
Copy link
Contributor

acul71 commented Aug 8, 2025

@Aldorax Are you still working on this PR or can be take over ?

@Aldorax
Copy link
Author

Aldorax commented Aug 8, 2025

@Aldorax Are you still working on this PR or can be take over ?

I am waiting on a review from @seetadev . I had made changes as request from me in the discussions above. I am still very much active in this.

@acul71
Copy link
Contributor

acul71 commented Aug 8, 2025

@Aldorax Are you still working on this PR or can be take over ?

I am waiting on a review from @seetadev . I had made changes as request from me in the discussions above. I am still very much active in this.

Good to hear.
I've seen that some tests are failing, merge last upstream main and go from there.
You can test your PR doing make pr and make docs (or make linux-docs)
Ping me if you need help

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.

5 participants