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

ScenesReact: Cache SceneQueryRunners and other scene object by a key / hashing string #788

Merged
merged 11 commits into from
Nov 7, 2024

Conversation

torkelo
Copy link
Member

@torkelo torkelo commented Jun 12, 2024

With @grafana/scenes-react creating scene object on the fly as react components are rendered (or hooks called) we need a different way to cache query results, query variables and viz panel state.

This PR explores scene object cache and key hashing mechanism.

Example, simple string key:

  const dataProvider = useQueryRunner({
    queries: [{ uid: 'gdev-testdata', refId: 'A', scenarioId: 'random_walk', alias: queryAlias ?? 'env = $env' }],
    maxDataPoints: maxDataPoints,
    cacheKey: "My unique cache key string",
  });

Example: Object as key

  const queries = [{ uid: 'gdev-testdata', refId: 'A', scenarioId: 'random_walk', alias: queryAlias ?? 'env = $env' }];

  const dataProvider = useQueryRunner({
    queries,
    maxDataPoints: maxDataPoints,
    cacheKey: queries,
  });

By default the system will get the hash key by doing JSON.stringify on objects (but with sorted properties so property order does not matter), same as react-query.

cacheKey can also be an array so you can combine multiple strings (or numbers) or object values

  const queries = [{ uid: 'gdev-testdata', refId: 'A', scenarioId: 'random_walk', alias: queryAlias ?? 'env = $env' }];

  const dataProvider = useQueryRunner({
    queries,
    maxDataPoints: maxDataPoints,
    cacheKey: [queries, maxDataPoints],
  });

If you have really large query objects and want faster hashing by reference there is a util function cacheByRef which returns a unique number for a given object reference.

The cache key hashing will also always append the scene object constructor name to the key to avoid conflicts when same cache key is used for different SceneObject types.

@torkelo torkelo changed the title ScenesReact: Cache SceneQueryRunners and other scene object by a key / hashing cache ScenesReact: Cache SceneQueryRunners and other scene object by a key / hashing string Jun 12, 2024
@torkelo
Copy link
Member Author

torkelo commented Jun 13, 2024

@leeoniya yes, the hashing by reference is for those cases. But this method described here is not to be used for core dashboards (at least not initially, as there we cache the full scene), only for custom scene apps.

@torkelo torkelo requested review from domasx2 and matyax June 13, 2024 04:51
Copy link
Contributor

@matyax matyax left a comment

Choose a reason for hiding this comment

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

Looks great! Left a couple of comments.

public constructor() {
this.#cache = new LRUCache({
max: 500,
ttl: 1000 * 60 * 5,
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it's still early, but I'd like to have control over this default TTL, so along with the cacheKey maybe you would want to consider these options.

Copy link
Contributor

Choose a reason for hiding this comment

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

Similar to https://tanstack.com/query/latest/docs/framework/react/reference/useQuery.

Unrelated with this PR, but, now that I'm reading those docs, it remind me of a conversation we recently had in Explore Logs to retry failed queries. It would be amazing to have the ability to set useQueryRunner() to retry a n times if the query fails.

Implementing this at the app level will be much more complex.

Copy link
Member Author

@torkelo torkelo Jul 4, 2024

Choose a reason for hiding this comment

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

@matyax implementing retries would be quite simple to do, backend_srv (underlying fetch abstraction) supports it I think

let cacheKeyHash = options.cacheKey ? cache.getHashKey(options.cacheKey, options.objectConstructor) : undefined;

let obj = scene.findByKey<T>(key);

Copy link
Contributor

Choose a reason for hiding this comment

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

Thinking out loud. If during runtime I purposely change the cacheKey to get a new SceneObject, I would still get the same SceneObject because it was first looked using key, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

@matyax ? not sure I understand what you mean, if the cacheKey changes you would get a new scene object

@matyax matyax requested a review from gtk-grafana June 14, 2024 17:05
@grafana grafana deleted a comment from leeoniya Sep 3, 2024
obj = cache.get<T>(cacheKeyHash);

if (obj) {
if (obj.parent !== scene) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: can just be if(obj && obj.parent !== scnee)

Copy link
Collaborator

@mdvictor mdvictor left a comment

Choose a reason for hiding this comment

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

This LGTM, I think we should go forward with it.

@torkelo torkelo marked this pull request as ready for review November 6, 2024 16:03
@torkelo torkelo merged commit 11cbd2c into main Nov 7, 2024
5 checks passed
@torkelo torkelo deleted the scenes-react-cache-poc branch November 7, 2024 09:13
@scenes-repo-bot-access-token
Copy link

🚀 PR was released in v5.23.2 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants