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

Discussion: Futurism Improvements #64

Closed
rickychilcott opened this issue Oct 9, 2020 · 10 comments
Closed

Discussion: Futurism Improvements #64

rickychilcott opened this issue Oct 9, 2020 · 10 comments

Comments

@rickychilcott
Copy link
Contributor

rickychilcott commented Oct 9, 2020

Feature Request

I'm in the process of vetting futurism for use with Rails russian doll caching to solve some long-standing performance issues that I have an app that I've built out that has fairly deeply nested relationships (4 or 5 levels deep, some polymorphic) along with some fairly complex permission requirements currently implemented with CanCanCan.

It seems to me that pairing futurize and some judicious caching could allow extremely smart and lazy caching to significantly speed up page load times and greatly improve the user experience. Thank you this gem is amazing and I'm excited to see where it can go.

While working on evaluating whether this will work or not, I've developed an example app that might be helpful to use in further testing and refining futurism. You can find it at https://github.com/rickychilcott/futurism-auth-spike and can test it at https://sleepy-sea-13861.herokuapp.com/ (note I'm using free Heroku so spin up times initially will be slow). The app isn't that interesting, but to get a sense of the interesting bits please see:

The short explanation is that we lazily load several nested sets of resources, going to the database and randomly sleeping a certain amount of time to determine whether or not the "current user" has the permission to edit a given resource. The user/resource permission mapping is cached in Redis for 10 minutes so that future lookups of a given resource will only take the time to read from the cache. All partials are cached, but since they are really only generating <futurism-element> tags, they are lazily rendered on the page sometimes quickly (i.e. from cache) or slower (i.e. newly generated server side).

While this app isn't a perfect test, it's good enough to highlight some of the challenges and areas to improve futurism.

While playing with this in "production", I've noticed a few things:

  1. Occasionally <futurism-element> never resolves. I have no idea why, but perhaps some sort of retry after a default/settable interval would be good.
  2. The initial page load requires all "top-level resources" to be generated server-side before any of them are triggered. This, is caused by https://github.com/julianrubisch/futurism/blob/abb5bcf31c9cb381777b623c5f4768ddc8eda67d/lib/futurism/channel.rb#L23-L32 the cable_ready.broadcast line being called after all of the resources are rendered.

Some other things that would be nice:

  1. A mechanism to implement poll/updated a <futurism-element> on a timer or to use javascript to trigger a rerender (i.e. document.queryselector('furism-element').reRender().
  2. A mechanism to client-side cache the rendered content for some period of time and not go back to the server to ask for the server-side rendered content. For instance, I could see all of my permissions based partials could be cached for 10 minutes and NEVER ask the server until that time expires. This would reduce chatter over the wire.
  3. A small thing, but sometimes you don't want a placeholder at all for a given element, so ideally a block would not be required as a placeholder.

Describe the solution you'd like

To fix the 2nd item on the "things I've noticed" list, I wonder if we could move the cable_ready.broadcast line into the loop. For quickly generating partials, this would have likely a small performance hit. For slower generating partials, it will have a significantly improved user experience.

For the other items, this is largely a set of discussion points that I'm open to working through, so I don't have a solution. However, as I've thought about these things, it feels like many of them could be made easier if instead of replacing <futurism-element> using cable ready's outer_html operating, what if we used inner_html and kept the <futurism-element> around for the entire duration of the page.

We'd likely need to clean up or change the API to remove classes/styling after the server side render is complete, but it may make caching, rerendering, and even #57 could be made simpler by not caching the previous <futurism-element> but instead just resetting it back to the pre-rendered state and setting element.innerHtml = ''. I also wonder if we could build out a small function API to allow things like element.reset(), element.render(), element.cache(1000*60*60), element.reRender(), element.reRender(force: true), element.reRender(every: 1000*60*5), etc.

Also, note that web components do allow you to implement a shadow DOM, so some of the "state" might be able to be kept within the component itself instead of localStorage, sessionStorage, etc. I don't fully understand whether or not turbolinks snapshotting or how styling is affected by the shadow DOM.

I'm happy to submit PRs (or ideally pair) for these items to get something started, but perhaps I've missed something in implementation details or specific reasons that some of these approaches haven't been taken. I love this gem and what it will enable, so I'm not intending any criticism. Just trying to help!

@julianrubisch
Copy link
Contributor

Awesome! It feels so great that other folks recognize the need and help me along with this 🙏

I have to go through all this but want to add to the list an obvious thing that could speed up things dramatically, is to use locate_many in case of a collection: https://github.com/rails/globalid#locating-many-global-ids

@julianrubisch
Copy link
Contributor

@rickychilcott I'm going to go over this again in the next few days.

@julianrubisch
Copy link
Contributor

Finally getting round to addressing some of the points you made. My goal is to finally get to a stable v1 some time soon.

retry after a default/settable interval would be good.

yep, I think this should be easily done.

The initial page load requires all "top-level resources" to be generated server-side before any of them are triggered.

This is intentional, to prevent some race conditions with CableReady I’ve encountered.

A mechanism to client-side cache the rendered content for some period of time and not go back to the server to ask for the server-side rendered content.

This is a perfect use case for a PWA. I could see that implemented like an additional JS module you can pull in if desired. (And added by the generator)

sometimes you don't want a placeholder at all for a given element, so ideally a block would not be required as a placeholder.

Do you have an idea how you’d implement this? An invisible/non-existent will never trigger the IntersectionObserver

shadow DOM, so some of the "state" might be able to be kept within the component itself instead of localStorage, sessionStorage, etc. I don't fully understand whether or not turbolinks snapshotting or how styling is affected by the shadow DOM.

Right, styling is the elephant in the room, which is why I wanted to keep the shadow DOM out of the equation here so as to not confuse users...

@julianrubisch
Copy link
Contributor

Re: PWA

Right after writing this I'm wondering if we should maybe just provide general instructions rather than an implementation because there can only be 1 service-worker for any given domain...

@julianrubisch
Copy link
Contributor

We could easily use the same pattern that I use for turbolinks caching (sessionStorage) because it destroys itself once the session is destroyed.

The problem is that it's not expireable and can only hold a few MB https://stackoverflow.com/questions/15171711/expiry-of-sessionstorage

@julianrubisch
Copy link
Contributor

@rickychilcott I decided to pull out some issues from this discussion, because I’m still getting a lot of positive feedback about futurism and think it should stay in the CableReady ecosystem, even with the advent of Turbo lazily loaded frames.

So please let me know if you see yourself taking on one or more of these issues :-)

@rickychilcott
Copy link
Contributor Author

Good stuff. You generated #78, #77, #76, #75. I also just added #79.

Thanks for pulling these out. I'm happy to work on them and will try to do so in the next week or so.

@julianrubisch
Copy link
Contributor

Awesome. I would like to take on #78, #77 is more a discussion right now.

Looking at #79 shortly

@julianrubisch
Copy link
Contributor

I've successfully fixed the Appraisals, so we're good to go now 👍

@julianrubisch
Copy link
Contributor

I've added the 1.0.0 milestone to the issues I'd like to see closed so we can finally do the major version bump.

Closing this for now

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

No branches or pull requests

2 participants