waku-org / js-waku

JavaScript implementation of Waku v2
https://js.waku.org
Apache License 2.0
171 stars 42 forks source link

feat: filter subscription API #1683

Open danisharora099 opened 1 year ago

danisharora099 commented 1 year ago

Problem

The current Filter implementation has a few gaps:

Dev Ex

Readability and Maintainability

Proposed Solutions

API:

  const subscription = await waku.filter.createSubscription(decoders);

  subscription.addEventListener(
        createKeyFromDecoder(decoder),
        (evt) => {
         const decodedMessage = evt.detail;
         }
      );

which users are more familiar with.

Along with that, improvements like:

Notes

fryorcraken commented 1 year ago

Maybe await is unuintitive but the event listener is not setup until the promise is resolved and the network interaction is done. So not sure we can hide that away from a developer that may think that the event listener is setup as soon as the call is done.

an event listener usually has the following signature:

addEventListener(eventType: string, callback: (event: Event) => void)

but here, we are need to have the decoder instead of a string. To me this feels even less intuitive.

fryorcraken commented 1 year ago

Maybe it's should be a two step process?


// get event listener ready
filter.addEventListener("new-message", (msg) => {...})

// Subscribe to messages
await filter.subscribe(decoder)
..
vpavlin commented 1 year ago

@danisharora099 Looking at this in more detail now

In your proposed API, are you actually subscribing during the createSubscription call? I.e. the async operation still has the await, but then the addEventListener call is there just to setup the actual action for when a new message is received?

If that is the case, then it sounds good to me, as that would match the approach I took in dispatcher:

  1. Subscribe during initialization
  2. Then setup the callback(s) later - see the actual usage here

That said, dispatcher is higher level abstraction, so I have many more event types, but I could still see js-waku Filter API have events like:

On the other hand, if the flow is

  1. createSubscription
  2. addEventListener
  3. subscribe

Then it may not really help the devexp

danisharora099 commented 1 year ago

@vpavlin

On the other hand, if the flow is

  1. createSubscription
  2. addEventListener
  3. subscribe

Then it may not really help the devexp

The flow is simply:

  1. filter.createSubscription()
  2. addEventListener

That's it. No subscribe() call necessary.


@fryorcraken

but here, we are need to have the decoder instead of a string. To me this feels even less intuitive.

we're not actually passing the decoder, but creating a unique key identifier (which is a string) using the decoder. The way key gen happens is by concatenating the pubsubTopic_contentTopic. I'll look into how we can improve this, especially since each Subscription object is specific to one pubsub topic, so just passing the contentTopic as key should be enough :)

fryorcraken commented 1 year ago

@fryorcraken

but here, we are need to have the decoder instead of a string. To me this feels even less intuitive.

we're not actually passing the decoder, but creating a unique key identifier (which is a string) using the decoder. The way key gen happens is by concatenating the pubsubTopic_contentTopic. I'll look into how we can improve this, especially since each Subscription object is specific to one pubsub topic, so just passing the contentTopic as key should be enough :)

With autosharding only the content topic would be needed. Hence, I suggest to only pass the content topic to addEventListener:

function addEventListener(contentTopic: string, (event: Event) => void): void

Exception should be thrown if there are no active or pending subscription for said content topic to help developer ensure they use the API right.

There is one corner case where a developer uses the same content topic on 2 different static shard and wants different event processing. In this case, the API does not allow the developer to register different callback.

I think it's a rare scenario and can easily be mitigated by having the developer call addEventListener twice for each processing and do a shard check at the start of the callback.

weboko commented 1 year ago

Independently from this discussion I implemented addEventListener for REST API here

Takes I have so far (some of which are relevant to existing API too):

Also points from previous posts are valid.

And to confirm other opinions in this thread - I don't see anything bad in addEventListener API but it should be the same as in the browser, e.g follow this declaration: addEventListener(contentTopic: string, (event: CustomEvent) => void): void

danisharora099 commented 1 year ago

With autosharding only the content topic would be needed. Hence, I suggest to only pass the content topic to addEventListener:

function addEventListener(contentTopic: string, (event: Event) => void): void Exception should be thrown if there are no active or pending subscription for said content topic to help developer ensure they use the API right.

I agree with your point and we can easily remove the overhead of managing pubsub topics for key, especially since each Subscription object represents a unique pubsub topic.

addressed this architecture here: https://github.com/waku-org/js-waku/pull/1682/commits/1e732a946a9637985d054a644d1be3df53eff72c

fryorcraken commented 1 year ago
  • not possible to handle error if subscribe request fails;

This would be handled with createSubscription throwing/rejecting, no?

danisharora099 commented 1 year ago
  • not possible to handle error if subscribe request fails;

This would be handled with createSubscription throwing/rejecting, no?

yes

weboko commented 1 year ago

Yes, for the subscribe request but not invocation - wrong wording. These places are hard to reach and if something happens - user has no way to react to error: here and here and maybe other places.

I see how we can handle - is to expose error event to Emit API or we can experiment and try to combine events with Node.js approach:

type CustomEvent = Event & {
  detail: {
    payload?: any,
    error?: any,
  }
};
filter.addEventListener("/content/topic", (event: CustomEvent) => {
  ...
});
chair28980 commented 1 year ago

Notes from js-waku pm 2023-11-07

danisharora099 commented 1 year ago

tracking followup as discussed in PM meeting here: https://github.com/waku-org/js-waku/issues/1713

fryorcraken commented 1 year ago

https://github.com/waku-org/docs.waku.org/pull/132#discussion_r1393600276

ping API can also be improved

danisharora099 commented 1 year ago

Weekly update:

danisharora099 commented 11 months ago

Weekly update:

weboko commented 11 months ago

Moved to Triage till 11th of Jan.

To discuss by team:

What work is ongoing:

cc @waku-org/js-waku-developers

fryorcraken commented 9 months ago

I would recommend to keep the filter API as close as possible to the protocol. Even if it means the dev ex is not the best,

Instead, I would focus on https://github.com/waku-org/pm/issues/114 and from there, provide the best API to the user.

This API can then be simple and abstract a lot:

API to dev: pass callback and content topic

In the background:

weboko commented 6 months ago

@danisharora099 removing this feat from reliability milestone