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

[experiment] Cache manager #3737

Closed
wants to merge 4 commits into from
Closed

[experiment] Cache manager #3737

wants to merge 4 commits into from

Conversation

steveruizok
Copy link
Collaborator

@steveruizok steveruizok commented May 11, 2024

This PR adds a central manager for the weak caches that we use in different places around our shapes.

We have a lot of caches in tldraw—computed caches in the editor, little WeakMaps in our shapes, that recycle values when their inputs haven't changed. This PR creates a centralized place for creating and storing those caches so that a developer has more of a blessed path to create, update, and use those caches.

  • Is this necessary?
  • Is it helpful?
  • Could we create a hook for when shapes are mounted that creates any necessary caches?
  • Could we create a nicer API for creating computed caches?

Change Type

  • sdk — Changes the tldraw SDK
  • feature — New feature

Test Plan

  • Unit Tests

Release Notes

  • SDK: adds Editor.caches, a manager for various caches used by the editor or shapes.

Copy link

vercel bot commented May 11, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Updated (UTC)
examples ✅ Ready (Inspect) Visit Preview May 11, 2024 10:26am
1 Ignored Deployment
Name Status Preview Updated (UTC)
tldraw-docs ⬜️ Ignored (Inspect) Visit Preview May 11, 2024 10:26am

@mimecuvalo
Copy link
Collaborator

I think this makes sense in general! My only high-level feedback is that it's maybe the case where we don't always want a WeakMap but maybe sometimes want a Map. It's possible that we'd need an option for the type of cache we want to request. And then it can start getting really more diverse if we start thinking about MRU caches, or caches dependent constrained on other time / size considerations...

Copy link
Contributor

@SomeHats SomeHats left a comment

Choose a reason for hiding this comment

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

Personally, I don't think a manager is the right design for this. It forces a some pieces of unneeded complexity into the DX that could be avoided fairly easily. These are:

  • having to check and create the cache if it doesn't exists
  • having to manage cache names
  • no type-checking between creating the cache and reading values from it. having to write out the types each time is a bit annoying and error-prone

The main thing that this seems to do is create caches that are scoped to a single Editor, where today they're mostly just sort of floating and aren't tied to any specific editor instance. I think if made a helper for this, we could achieve the same thing without pushing that extra management onto developers:

function createEditorCache<In, Out>(fn: (editor: Editor, input: In) => Out) {
    const cache = new WeakCache<Editor, WeakCache<In, Out>>()
    return {
        get(editor: Editor, input: In) {
            return cache
                .get(editor, () => new WeakCache())
                .get(input, () => fn(editor, input))
        }
    }
}

// usage
// 1. define, probably at the top-level of your module
const textShapeSizeCache = createEditorCache((editor, props) => /* ... */)

// 2. use, e.g. in a shape util
const textShapeSize = textShapeSizeCache.get(editor, shape.props)

This is the same API as the new free-floating version of createComputedCache we introduced to support bindings work recently, which looks exactly the same except the In types are limited to records in the store.

@steveruizok
Copy link
Collaborator Author

Hm, I like that API Alex, especially for multiple editors, but it still means our own caches aren't really accessible.

Let me add more context. On Discord, I talked with a user who wanted to bust all of the caches, iirc after changing a font file in a way that would cause text measurement to produce different sizes from the same props. We solved their particular case by pre-loading the font instead, so that the initial value in the cache for the shapes' props was measured correctly, but it did make me think that these caches are mostly scattered around and inaccessible and may have some dependencies (like the text measurement thing) that are hard to catch.

This could be too niche to matter, and Mime's reply makes me think that we might be opening a box of many worm cans. It would be nice to say "ah yeah here's an obscure part of our API that lets you access / reset all the little weakmap caches we use"... but we could also live without it.

@steveruizok steveruizok marked this pull request as draft May 19, 2024 01:17
@steveruizok
Copy link
Collaborator Author

Closing this one, I think we'll address it when we loop back through shapes / tools in the future.

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

Successfully merging this pull request may close these issues.

None yet

3 participants