Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feed LatestPrice query cache #11326

Merged

Conversation

samsondav
Copy link
Collaborator

No description provided.

Copy link
Contributor

I see that you haven't updated any CHANGELOG files. Would it make sense to do so?

Copy link
Contributor

I see that you haven't updated any README files. Would it make sense to do so?

@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch 5 times, most recently from 0cca18b to 9f7adda Compare November 17, 2023 16:43
@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch 2 times, most recently from b5b3a91 to 5f84251 Compare November 21, 2023 18:21
@cl-sonarqube-production
Copy link

SonarQube Quality Gate

Quality Gate failed

Failed condition C Reliability Rating on New Code (is worse than A)
Failed condition 8.8% 8.8% Coverage on New Code (is less than 75%)

See analysis details on SonarQube

Fix issues before they fail your Quality Gate with SonarLint SonarLint in your IDE.

@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch 6 times, most recently from 6507d18 to b97d8f2 Compare November 22, 2023 21:45
@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch from b97d8f2 to c8a74c5 Compare November 22, 2023 22:06
@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch 2 times, most recently from 318cb58 to 81fda1b Compare November 24, 2023 16:52
@samsondav samsondav marked this pull request as ready for review November 24, 2023 16:57
@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch from 510b488 to 41a617a Compare November 27, 2023 18:07
@@ -100,7 +101,7 @@ func NewRelayer(lggr logger.Logger, chain evm.Chain, opts RelayerOpts) (*Relayer
chain: chain,
lggr: lggr,
ks: opts.CSAETHKeystore,
mercuryPool: wsrpc.NewPool(lggr),
mercuryPool: opts.MercuryPool,
Copy link
Contributor

@jmank88 jmank88 Nov 27, 2023

Choose a reason for hiding this comment

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

Why is this a top level pool now? Is the intention to share it across chains? That means it will cross the relay API, and I'm not sure how that is meant to work 🤔

Copy link
Collaborator Author

@samsondav samsondav Nov 27, 2023

Choose a reason for hiding this comment

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

Yes it is shared across chains. I asked @cedric-cordenier and this is what he suggested.

Copy link
Contributor

Choose a reason for hiding this comment

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

So the relay API has to proxy the shared websocket connections? Why is that better than each plugin process having it's own local pool?

Copy link
Contributor

Choose a reason for hiding this comment

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

@jmank88 The reason why I suggested adding this here was because I didn't really consider this part of the relayer LOOPP API. For me this isn't part of it -- see for example how we're passing in the database. In a LOOP world, we wouldn't do that: we'd either pass in the config values and create the pool in the relayer proper, or pass in a proxy service.

I'm trying to understand your objection: I guess you're saying that this would break the isolation LOOPPs are supposed to provide?

Copy link
Contributor

@jmank88 jmank88 Nov 27, 2023

Choose a reason for hiding this comment

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

I'm observing that this at least adds to the technical debt in the way of LOOPPifying mercury.

  1. If we intend to share these pools between host and plugins, then I guess that is just something to figure out how to do (but it does not immediately make sense to me).
  2. If we do not intend to share these pools between host and plugins, then there is no need to refactor things to be global like this in the first place since we'll just have to undo it in a few weeks. Instead, each relayer should simply own and manage a pool internally.

Copy link
Contributor

@cedric-cordenier cedric-cordenier Nov 27, 2023

Choose a reason for hiding this comment

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

Yes, good point.

One interesting thought that occurred to me is that you could look at this as a symptom of there not being a single "Mercury relayer", i.e. it's a symptom of there being a mercury provider in all relayers.

Instead, maybe a more natural way to look at this is that Mercury is a "chain", and we should give it its own relayer. If we do that, this problem goes away as calls to Mercury would go via the same relayer and therefore would benefit from the wsrpc pool which in that case would be a totally internal, but shared resource. This Mercury relayer would only implement the write APIs (i.e. the transmitter, the codec etc), and would leave the read side unimplemented or stubbed out.

The benefit of this is that it cleanly maps the relayer as a 1-1 abstraction over the underlying resource, in the same way that relayers do with the chains they target.

It does mean that we'd need to refine the relayer abstraction somewhat, to allow products to use a different relayer for reading and for writing, but that doesn't feel wrong to me, and might even be a better underlying fit (CCIP might benefit from this for example).

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah I think that separate reading and writing relayers makes sense 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

@samsondav Do you think that is a good long-term direction for Mercury?

Copy link
Collaborator Author

@samsondav samsondav Nov 28, 2023

Choose a reason for hiding this comment

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

I agree, separating the read/write relayers makes sense to me. However, note that in this case the "write" relayer would also have to support reading. Since we send reports to mercury but also query reports from it.

Treating mercury server as a "chain" is definitely the right way to go.

@samsondav samsondav requested review from brunotm and jmank88 November 27, 2023 19:01
// NOTE: must drop down to RawClient here otherwise we enter an
// infinite loop of calling a client that calls back to this same cache
// and on and on
val, err = m.client.RawClient().LatestReport(ctx, req)
Copy link
Contributor

Choose a reason for hiding this comment

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

@samsondav Did you consider inverting the dependency here? i.e. rather than having a cache which depends on a client, you have a client which depends on a cache. That would allow the cache to be totally generic and reusable and not have any notion of client, while your client is responsible for making requests and storing things in the cache as needed (even if this happens in a loop).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I thought this was how I designed it? The client does depend on the cache and calls to the cache if its there.

Copy link
Contributor

Choose a reason for hiding this comment

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

We discussed this offline: an aside cache won't work here since we're also trying to dedupe in flight requests. The main reason for this cache is to service ETH/LINK feeds, which are high volume, so this complexity is worth it.

// Context should be set carefully and timed to be the maximum time we are
// willing to wait for a result, the background thread will keep re-querying
// until it gets one even on networking errors etc.
func (m *memCache) LatestReport(ctx context.Context, req *pb.LatestReportRequest) (resp *pb.LatestReportResponse, err error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this more complicated than it needs to be? I can see that it accounts for the cache wait case, but I'm wondering if you considered using a simpler strategy (cache aside, rather than read through). This might be a bit less efficient, but I think would significantly reduce all of the edge cases you need to handle, which feels like a win.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Can you outline how the simpler strategy would work?

Copy link
Contributor

Choose a reason for hiding this comment

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

Update: as above.

Problem: Core nodes are querying LatestReport a lot (~70rps). This is not only wasteful, it potentially increases latency if Mercury server falls behind and is also completely unnecessary. We do not need such up-to-the-millisecond accuracy for billing,

Proposed solution: Introduce a price cache for mercury with a TTL e.g. of 1 minute or whatever tolerance we have for billing price and only fetch from the server when the cache becomes stale.
@samsondav samsondav force-pushed the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch from 23d746a to e359f42 Compare November 28, 2023 15:12
@cl-sonarqube-production
Copy link

@samsondav samsondav added this pull request to the merge queue Nov 28, 2023
Merged via the queue into develop with commit f47e290 Nov 28, 2023
88 checks passed
@samsondav samsondav deleted the MERC-2486-cache-for-querying-link-eth-prices-in-mercury branch November 28, 2023 17:01
fbac pushed a commit that referenced this pull request Dec 14, 2023
* Price retrieval cache for Mercury

Problem: Core nodes are querying LatestReport a lot (~70rps). This is not only wasteful, it potentially increases latency if Mercury server falls behind and is also completely unnecessary. We do not need such up-to-the-millisecond accuracy for billing,

Proposed solution: Introduce a price cache for mercury with a TTL e.g. of 1 minute or whatever tolerance we have for billing price and only fetch from the server when the cache becomes stale.

* fix

* Some PR comments

* Fix more PR comments

* PR comments

* lint
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.

4 participants