Skip to content

Add event lookup for wildcard indexing - #135

Merged
JonoPrest merged 2 commits into
mainfrom
jp/event-routing
Aug 30, 2024
Merged

Add event lookup for wildcard indexing#135
JonoPrest merged 2 commits into
mainfrom
jp/event-routing

Conversation

@JonoPrest

Copy link
Copy Markdown
Collaborator

A new way of looking up events.

EventLookup supports:

  1. Looking up by topic0 only if there is a single registration for the event
  2. Looking up wildcard events without the address in contractAddress mapping
  3. Looking up events when the address does exist in contractAddress mapping
  4. Safe API for adding registrations with no collisions
  5. Safe API for setting wildcard values after the fact without collisions

@JonoPrest
JonoPrest requested a review from DZakh August 21, 2024 16:13
Comment on lines -94 to +96
let events = Js.Dict.empty()
let events: eventModLookup = EventLookup.empty()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

When I was refactoring the chain workers, I wanted to change this place to pass eventModLookup chain worker functor factories - separately for each chain.

The reasoning is:

  • Remove the dependency of chain workers from Config - currently the events is the last thing left
  • The EventLookup structure is simpler and more optimised, since it's unique for each chain
  • It'll allow to have a different implementation of EventLookup for Fuel

Comment on lines +136 to +152
let rec find = (arr, fn, ~index=0) => {
open Belt
switch arr[index] {
| Some(item) => item->fn ? item->Some : arr->find(fn, ~index=index + 1)
| None => None
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There should be Js.Array2.find or Belt.Array.keepMap

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

keepMap will not exit early and return a whole array of matching items though.

I'm not sure of Js.Array2.find, that probably does exit early. If it's native implementation it's probably best.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, it was added in es6 and exits early

@DZakh DZakh left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good. Just the change doesn't look very reliable. Also, I think the refactoring to have a separate event lookup for each chain should simplify the code and make the lookup operation more optimal.

  let config = Config.getGenerated()
  let config = RegisterHandlers.registerAllHandlers()

Also, I'd say we can merge it as it is for now and do the refactoring later.

chains: array<ChainMap.Chain.t>,
}
type registeredEvent<'a> = {contract: contract, event: 'a, mutable isWildcard: bool}
type eventsAtTopic0<'a> = Single(registeredEvent<'a>) | Multiple(dict<registeredEvent<'a>>)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of the variant I think it's more convenient to work with the following data structure:

{
  ids: array<string>,
  entities: dict<registeredEvent<'a>>
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What would IDs contain?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The keys of entities. Maybe worth calling keys

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's useful for:

  • Saving the Object.keys call
  • Cheaply get the number entities

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Ok I understand. I think you mean events not entities right?

Also we can then just rather save an int for number of events. That can work. If it's just for checking single or multiple. That sounds good to me 👍🏼

@JonoPrest JonoPrest Aug 22, 2024

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hmmm thinking about this:

switch eventsAtTopic0 {
  | {ids: [], events} => //None case
  | {ids: [singleId], events} => //Some single case
  | {ids: multipleIds, events} =>//Some multiple case
}

I think you end up with the same cases with worse semantics. Not sure if it's an improvement.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, something like this. Also, since there are no delitions doing it like this might be more convenient:

{
  events: array<registeredEvent<'a>>,
  eventByContractName: dict<registeredEvent<'a>>
}

And your example will be:

switch eventsAtTopic0 {
  | {events: []} => //None case
  | {events: [event]} => //Some single case
  | {events} =>//Some multiple case
}

I think you end up with the same cases with worse semantics. Not sure if it's an improvement.
I agree that for the example above the semantics a little bit worse, even though I personally find it fine. There are some benefits coming along:

  • For some cases you don't need to have separate logic for Single and Multiple events, since now it's possible to do eventsAtTopic0. eventByContractName->Js.Dict.get without additional checks (also works for eventsAtTopic0.events)
  • No Js.Dict.keys and Js.Dict.values calls and additional object allocations for Single and Multiple variants. In our case this is not very important, since the code runs only on events registration, but still I think it's good to do our best with performance

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Nice, cool that sounds like a good 👌🏼 will do.

events
->Js.Dict.values
->Utils.Array.find(event =>
event.isWildcard && event.contract.chains->Utils.Array.includes(chain)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

After refactoring, we discussed splitting the event lookup by each chain. We can keep the wildcard event in a separate field and get it without function calls and iterations. It might be worth doing the part of refactoring already since it feels like it'll simplify quite some code.


let populateChainQueuesWithRandomEvents = (~runTime=1000, ~maxBlockTime=15, ()) => {
let config = Config.getGenerated()
let config = RegisterHandlers.registerAllHandlers()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is this change? It reruns the EventHandlers code, which I don't think current with the current testing implementation.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Got you...

Basically the config can only be set after handlers registered. So I should change it rather to a function that if the ref is None, it runs registerAllHandlers and then returns the value else it can return the cached value.

I'm not too worried about the performance on a test though TBH.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did you get a case when it's missing?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, all the cases were missing.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hmmm, weird how it worked before

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

No it's not weird, it's because I moved the Config.setGenerated into the register handlers function. That way you can be sure you have all the wildcard validation and entries on the config.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, missed the change

Comment on lines +225 to +231
{{chain_id}},
{{/each}}
]->Belt.Array.map(chainId => ChainMap.Chain.makeUnsafe(~chainId))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
{{chain_id}},
{{/each}}
]->Belt.Array.map(chainId => ChainMap.Chain.makeUnsafe(~chainId))
ChainMap.Chain.makeUnsafe(~chainId={{chain_id}}),
{{/each}}
]

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Happy to take the suggestion but why?

This would expand into more boilerplate than before?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't have a strong opinion about it

@JonoPrest
JonoPrest force-pushed the jp/event-routing branch 3 times, most recently from 79978bc to 04dccd4 Compare August 28, 2024 10:42
@JonoPrest
JonoPrest enabled auto-merge (squash) August 30, 2024 08:11
@JonoPrest
JonoPrest merged commit af620a9 into main Aug 30, 2024
@JonoPrest
JonoPrest deleted the jp/event-routing branch August 30, 2024 08:14
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.

2 participants