rust-nostr / nostr

Nostr protocol implementation, SDK and FFI
https://rust-nostr.org/
MIT License
399 stars 87 forks source link

Reliably receiving past and future events without duplication #508

Closed xeruf closed 1 month ago

xeruf commented 1 month ago

Is there a way to atomically subscribe to new events and obtain past events matching a certain filter thus that you can be sure not to have missed an event, but also not to receive duplicates?

While we are at it, is there a way to create a subscription that does not receive events submitted by self? I can use Filter::remove_authors(), but that would exclude events sent by other programs logged into the same user. Or do I have to manually filter on the client-side, probably with one of those database methods?

And I am a bit confused about auto-closing and dropping subscriptions - do I need to call client.unsubscribe_all before ending my program or is that implicit on exit? I see no implementation of Drop so I am a bit dumbstruck. Also, are the subscriptions in client only for the current instance of Client or do they somehow come from the relay and are shared between instances?

xeruf commented 1 month ago

I thought about using Filters::new().since to minimize duplication, but for my application it is totally valid for somebody to sent an event dated in the past, and I still would want to receive that.

xeruf commented 1 month ago

Ah I think I got the answer to the original question: client.subscribe receives old events too.

But Filter::remove_authors wasn't what I thought, so it seems I have to manually filter out, which unfortunately might incur unnecessary traffic.

yukibtc commented 1 month ago

do I need to call client.unsubscribe_all before ending my program or is that implicit on exit?

No, when websocket connection is closed, the subscriptions are automatically dropped by the relay

Ah I think I got the answer to the original question: client.subscribe receives old events too.

Yeah, that depends by the filters you set.

While we are at it, is there a way to create a subscription that does not receive events submitted by self?

Yes if using RelayPoolNotification::Event variant.

To avoid to receive event duplicates you can subscribe and listen for RelayPoolNotification::Event variant. BUT, the RelayPoolNotification::Event variant is sent only for the first time the event is seen, so if you query the same filters again you'll not receive again that event notification.

yukibtc commented 1 month ago
client
    .handle_notifications(|notification| async {
        if let RelayPoolNotification::Event { event, .. } = notification {
            // This notification is sent ONLY the first time the event is seen
        }
        Ok(false) // Set to true to exit from the loop
    })
    .await?;
xeruf commented 1 month ago

That is what I am already using - but if I run Client::send_event in another thread, I still get a notification from this. I would ideally like a subscription that returns me all events except the ones I submitted from this program. Are there negative filters? Then I could maybe use a kind of session-tag.

yukibtc commented 1 month ago

Ah ok, that shouldn't happen (as specified in RelayPoolNotification::Event variant docs). I'm going to publish the fix.

yukibtc commented 1 month ago

Let me know if you still receive events sent by the SDK

xeruf commented 1 month ago

Checking right now, but realized I first need to find out how to migrate from 0.30 - especially how to get tag values as I am doing right now: https://forge.ftt.gmbh/janek/mostr/src/branch/main/src/tasks.rs#L104

xeruf commented 1 month ago

What is the point of the Arc there? A tag can just be cloned easily when needed?

yukibtc commented 1 month ago

What is the point of the Arc there?

Where?

A tag can just be cloned easily when needed?

Yes, the Tag can be cloned.

xeruf commented 1 month ago

See https://github.com/rust-nostr/nostr/issues/522

w3irdrobot commented 3 weeks ago

i'm seeing similar issues using 0.33.0. i receive an event notification reliably twice. it appears it comes from two different relays.

loop {
    let (event, relay_url) = match notifications.recv().await {
        Ok(RelayPoolNotification::Event {
            event, relay_url, ..
        }) => (event, relay_url),
        Ok(RelayPoolNotification::Shutdown) => break,
        _ => continue,
    };

    info!(
        "Received event from relay {}: {:?}",
        relay_url,
        event.as_json()
    );
}

this will output a new event at least more than once. this may differ between filters and such, but with my setup, my code is notified twice.

yukibtc commented 3 weeks ago

i'm seeing similar issues using 0.33.0. i receive an event notification reliably twice. it appears it comes from two different relays.

loop {
    let (event, relay_url) = match notifications.recv().await {
        Ok(RelayPoolNotification::Event {
            event, relay_url, ..
        }) => (event, relay_url),
        Ok(RelayPoolNotification::Shutdown) => break,
        _ => continue,
    };

    info!(
        "Received event from relay {}: {:?}",
        relay_url,
        event.as_json()
    );
}

this will output a new event at least more than once. this may differ between filters and such, but with my setup, my code is notified twice.

I'll publish v0.34 this week, it'll include the fix for this (commit 5627c403bb3a35936c266e2f561ba47dfa9a89ae).