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

Add caching for PriceReader #372

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open

Add caching for PriceReader #372

wants to merge 20 commits into from

Conversation

0xnogo
Copy link
Contributor

@0xnogo 0xnogo commented Dec 17, 2024

We are currently making many RPC calls to get token prices, which can slow down our system. This PR introduces a simple caching solution to store and reuse these values.

Changes made:

  • Added caching to store fetched token prices in PriceReader
  • Split price fetching logic into smaller functions
  • Used "never expire" policy since we refresh prices each time

The implementation wraps go-cache and can be extended for other use cases. It supports:

  • Basic cache operations (Get/Set/Delete)
  • Custom eviction functions
  • Thread-safe operations
  • Generic value types

@0xnogo 0xnogo marked this pull request as ready for review December 17, 2024 07:59
@0xnogo 0xnogo requested a review from a team as a code owner December 17, 2024 07:59
internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
asoliman92
asoliman92 previously approved these changes Dec 17, 2024
internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
tokenInfo map[ccipocr3.UnknownEncodedAddress]pluginconfig.TokenInfo
ccipReader CCIPReader
feedChain ccipocr3.ChainSelector
decimalsCache cache.Cache[string, uint8]
Copy link
Contributor

Choose a reason for hiding this comment

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

i believe it can be just inMemCache no need to tie it to decimals

Copy link
Contributor Author

@0xnogo 0xnogo Dec 17, 2024

Choose a reason for hiding this comment

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

It depends if we have more than one cache. I'll leave it as-is for now and potentially update the name if this is the only cache of the class. wdyt?

pkg/reader/price_reader.go Outdated Show resolved Hide resolved
commit/factory.go Outdated Show resolved Hide resolved
)

// Cache defines the interface for cache operations
type Cache[K comparable, V any] interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I checked the libs you provided and they look quite advanced. I'm not sure about our future requirements but our current needs are quite simple. It might makes sense to go homemade for our usecase.

Do you have any experience with those libs? What do you recommend?

Copy link
Contributor

Choose a reason for hiding this comment

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

I almost used ristretto for a project that needed high performance, but you're probably right that it's excessive for our needs.

Two trains of thought right now:

  1. go-cache seems like an excellent fit. It's extremely lightweight (a single file with ~1000 LOC), the interface is almost identical to the one you have, the project has the most stars.

  2. If we do roll our own, I bet we could come up with a clever optimization to time based expiration utilizing sequence numbers and multiple maps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can go with a with go-cache and overload it with a custom logic to implement the customPolicy. Something like this:

type CustomCache[K comparable, V any] struct {
	cache        *cache.Cache
	customPolicy func(V) bool
	mutex        sync.RWMutex
}

func (c *CustomCache[K, V]) Get(key K) (V, bool) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

        ...

	// Check custom policy
	if c.customPolicy != nil && c.customPolicy(typedValue) {
		c.cache.Delete(interface{}(key))
		return zero, false
	}

	return typedValue, true
}

Copy link
Contributor

@asoliman92 asoliman92 left a comment

Choose a reason for hiding this comment

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

I'd like to see tests for the time based eviction with one test that is skipped with a TODO to make a proper background task to evict.

internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
internal/cache/cache.go Outdated Show resolved Hide resolved
@0xnogo 0xnogo changed the title Add caching system for decimals in PriceReader Add caching for PriceReader Dec 21, 2024
Copy link

Metric ng/cache-decimals main
Coverage 77.1% 76.4%

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