Turbo Stream vs polling for updates

Hi all,

This is more of a conceptual question to see how others might decide to handle this. I have a Rails app (recently upgraded to 7) which has historically been mostly static. Few tiny bits of JS here and there. It has about 150 users, and most of the features are what I would describe as being “single user” - i.e. CRUD resources for things that only the current user has access to anyway, so there’s very little need to update content dynamically.

However I’ve recently added a booking component, where users can book and pay for events. It works well, but most events have a max capacity so updating things like “X places left” and hiding the “Book Now” button when full would be great to do in real-time.

The question is whether conceptually it makes more sense to spin up Redis and use Turbo Streams, or just poll for updates when the user is on the correct page and use Stimulus to reload the frame(s)? I’m just not sure I want to added complexity and cost of ActionCable/Redis when simply reloading the frame every, say, 5 seconds would be workable.

It feels overkill to have extra infrastructure just to manage this relatively small thing. I’d appreciate some thoughts on the topic.

Thanks!

Mecure is a really simple and reliable solution for real time feature.
You can implement it via Stimulus : Mercure.rocks: Real-time APIs Made Easy

1 Like

Hard to answer, it’s a trade-off. I’m in more or less the same boat (mostly static app that got a booking component recently), but I was already using Redis as both cache store and sidekiq instance, so expanding it for ActionCable purposes wasn’t that big of a deal. (I’m implementing CableReady now since it, unlike Turbo Streams, supports Rails 5.2 properly). So far it’s worth it for me, but for a small site it should be usable to reload a turbo frame on an interval or whatever. There would probably be scaling issues with many concurrent users doing this. Then again, premature optimisation is bad, so… :man_shrugging:

Yeah this is the thing, I don’t know if there’s a correct answer exactly, mostly just something to discuss with people who are also potentially in the same boat. I think I’m of the mind that right now, a reload of the frame is enough and like you said optimising too early isn’t a good idea.

For reference, here’s what I’ve done so far in case anyone else needs it. It’s based off a few other controllers and snippets I’ve found around the web.

Firstly, I’ve got a really simple ‘reload’ controller in Stimulus:

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static values = {
    src: String
  }

  connect() {
    console.log("Connected to reloader...")
    window.reload_interval = setInterval(function(obj){
      console.log("Reloading")
      obj.element.setAttribute('src', obj.srcValue)
    }, 7000, this);
  }

  disconnect() {
    console.log("Clearing reloader")
    clearInterval(window.reload_interval);
  }
}

That you can simply connect to any turbo frame with:

<%= turbo_frame_tag 'events_list', data: { controller: 'refresh', refresh_src_value: request.path } do %>
   <%= render 'fixture_grid_table', events: @all_events, title: 'Upcoming events' %>
<% end %>

I’m happy with a fixed 7s interval for all reloaders right now, but you could also add a second value to allow you to define that separately for each instance of the controller that’s loaded to make it more flexible.

I knocked this together very quickly, and it doesn’t ‘cost’ me anything in additional infrastructure or complexity. It’s very early for me on the Hotwire journey, but so far this is really very cool. Let’s you add a LOT of functionality with very little effort.

Interesting, looks like that could pretty useful if I outgrow the polling solution. Thanks!

Just a thought… Why don’t you reload just the turbo-frame on an interval? There’s also a builtin reload function for frames and since you attach the controller to the turbo-frame itself, you could just put this.element.reload() inside the setInterval. Unless I’m missing some complexity in your implementation, that is.

turbo_stream_from 

should do the trick

I actually tried that and ran into some issues, not sure why but setting the src works reliably, yet calling the .reload() function doesn’t? I’ll dig into it at some stage but for now I’m happy, it works.