Plans for Turbo/Job/Cache integration?

Scenario: I have a component of my page that I know to be slow to build. I want to cache it. I want it to load asynchronously on the page whenever it’s built.

It seems that we could combine Turbo/ActiveJob/SomeCache in order to achieve this in a more optimized manner than a javascript poller, a custom API and custom cache.

I continually have been reach for turbo streams in order to solve this problem, but I’ve found the process of stitching all the things together to be a bit cumbersome.

I guess the point of this post is to ask if there is development happening in this area? Either in Rails Core or is anyone familiar with a gem somewhere that might address this?


That said, I’ve built some half-baked solutions which combine the three things. The following is a bit half-baked, but hopefully you get the idea:

helper.rb
def cached_turbo_stream(identifier)
  if value = Rails.cache.read(key)
    value
  else
    turbo_stream_from identifier,
      channel: "CachedTurboStreamChannel",
      id: "cached_turbo_stream_#{identifier}"
    end
end

cached_turbo_stream_channel.rb
class CachedTurboStreamChannel < Turbo::StreamsChannel

  def subscribed
    super

    stream_name = verified_stream_name_from_params

    CachedTurboStreamJob.perform_later(stream_name)
  end
end

cached_turbo_stream_job.rb
class CachedTurboStreamJob < ApplicationJob
  def perform(stream_name)
    cached_tag =
    Rails.cache.fetch(some_cache_key) do
      content =
      ApplicationController.render(
        formats: [:html],
        partial: stream_name
      )

      CachedTurboStreamChannel.turbo_stream_action_tag(
        :replace,
        target: "cached_turbo_stream_#{stream_name}",
        template: content
      )
    end

    CachedTurboStreamChannel.broadcast_stream_to(
      stream_name,
      content: cached_tag
    )
  end
end

This thing renders the partial if it’s cached, and if it’s not, set’s up a turbo_stream_for which, of course, connects to Turbo::StreamsChannel, drops a job, and that job broadcasts the rendered partial back to the stream which injects it into the dom.

This allows the main page to load quickly, and defers the work of caching this component to ActiveJob. It is then loaded asynchronously when it is ready.

2 Likes

How slow is slow? Why not skip the caching step and just always follow the job + turbo_stream route?

Is it possible to break it down even more into several smaller jobs (still with turbo_stream)?

Have you considered a lazy loaded turbo frame? You could have a dedicated controller for the partial and simply read from the cache in that controller. This would eliminate all the complexity of a job and streams. Unless I’m missing something in your use case.

Why not skip the caching step and just always follow the job + turbo_stream route?

There’s always some line where you want to cache something. Saving all the dollas is where’s it’s at, right?

You could have a dedicated controller…

Yes, there are ways to do this. This can all be handjammed.

The point of this post to see if there are any plans that would actively integrate this so that it all plays nice together “out of the box”