python-nostr icon indicating copy to clipboard operation
python-nostr copied to clipboard

Handling bad relays in relay_manager.py

Open vikbtc opened this issue 2 years ago • 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

vikbtc avatar Feb 09 '23 21:02 vikbtc

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

ghost avatar Feb 09 '23 21:02 ghost

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.

jeremywhelchel avatar Feb 10 '23 02:02 jeremywhelchel

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

trbouma avatar Mar 01 '23 13:03 trbouma

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 avatar Jul 11 '23 20:07 Jxck-S

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 avatar Jul 12 '23 04:07 Jxck-S

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

Jxck-S avatar Jul 26 '23 07:07 Jxck-S