jeffthibault/python-nostr

Handling bad relays in relay_manager.py

vikbtc opened this issue · 6 comments

When posting to multiple relays, sometimes there are failures like the below:

2023-02-09 11:09:12 Start posting to 16 relays
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Handshake status 500 Internal Server Error - goodbye
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:12 Websocket connected
2023-02-09 11:09:13 Websocket connected
2023-02-09 11:09:17 Error posting to nostr
Traceback (most recent call last):
  File "/home/zz/ooo/util_nostr.py", line 71, in _post
    relay_manager.publish_event(event)
  File "/home/zz/.local/share/virtualenvs/ooo-gj3JA2ic/lib/python3.10/site-packages/nostr/relay_manager.py", line 63, in publish_event
    self.publish_message(event.to_message())
  File "/home/zz/.local/share/virtualenvs/ooo-gj3JA2ic/lib/python3.10/site-packages/nostr/relay_manager.py", line 53, in publish_message
    relay.publish(message)
  File "/home/zz/.local/share/virtualenvs/ooo-gj3JA2ic/lib/python3.10/site-packages/nostr/relay.py", line 47, in publish
    self.ws.send(message)
  File "/home/zz/.local/share/virtualenvs/ooo-gj3JA2ic/lib/python3.10/site-packages/websocket/_app.py", line 240, in send
    raise WebSocketConnectionClosedException(
websocket._exceptions.WebSocketConnectionClosedException: Connection is already closed.
  1. Is it possible to add better logging to tell which relays connected and which failed?
  2. In case of one bad relay, I would still like to be able to continue posting to the rest without an exception being thrown.

Thanks

i was just about to open an issue for this. This piece of code takes care of sending, and errors out incase the connection goes boom

It's located in /nostr/relay_manager.py, line 50.

def publish_message(self, message: str):
        for relay in self.relays.values():
            if relay.policy.should_write:
                relay.publish(message)
  • We can wrap it in a try: except: statement, and popping the bad relay from the list
  • Or perhaps add a configurable setting to simply ignore the exception and continue

I was hitting this as well. The websocket-client library even has a FAQ for this: https://websocket-client.readthedocs.io/en/latest/faq.html#how-to-solve-the-connection-is-already-closed-error

I patched this library to wrap the call with try: except WebSocketConnectionClosedException: and it solved the problem. Code has been running for many days now with no issues.

I am running into the same problem. Hopefully I can fix. Where is your patch?

Jxck-S commented

Can we merge a fix for this into the main? When a relay goes down, this makes the program really unstable, if this fix is not integrated.

Jxck-S commented

i was just about to open an issue for this. This piece of code takes care of sending, and errors out incase the connection goes boom

It's located in /nostr/relay_manager.py, line 50.

def publish_message(self, message: str):
        for relay in self.relays.values():
            if relay.policy.should_write:
                relay.publish(message)
  • We can wrap it in a try: except: statement, and popping the bad relay from the list
  • Or perhaps add a configurable setting to simply ignore the exception and continue

Here's what I did with your try-except, PSA for anyone trying to fix this, the try-except must be implemented in this library, not within your own program otherwise if your first relay is bad your post won't post at all.

    def publish_message(self, message: str) -> dict:
        from websocket import WebSocketConnectionClosedException
        post_stats = {}
        for relay in self.relays.values():
            if relay.policy.should_write:
                try:
                    relay.publish(message)
                    post_stats[relay.url] = True
                except WebSocketConnectionClosedException:
                    post_stats[relay.url] = False
                    print(f"Disconnected from {relay.url} couldn't post to this relay.")
                    pass
        return post_stats

    def publish_event(self, event: Event):
        """ Verifies that the Event is publishable before submitting it to relays """
        if event.signature is None:
            raise RelayException(f"Could not publish {event.id}: must be signed")

        if not event.verify():
            raise RelayException(f"Could not publish {event.id}: failed to verify signature {event.signature}")

        return self.publish_message(event.to_message())

Noting that publish_event now needs to return publish_message

In this case, publish event now says which relay fails, and it returns a dictionary of which relays it posted to and which it didn't.
Disconnected from wss://nostr-pub.wellorder.net couldn't post to this relay.

{'wss://nostr-pub.wellorder.net': False, 'wss://nostr.mutinywallet.com': True, 'wss://relay.snort.social': True, 'wss://nostr.wine': True, 'wss://nos.lol': True, 'wss://relay.damus.io': True}

Jxck-S commented

PSA: this issue is fixed, it's just that we didn't know because the pip package isn't updated to the latest code from the git. The pip needs to be updated. Look at #65