How To broadcast from a background job?

I have a session where a user is clicking buttons on the front-end. A click is accepted by the controller, and spawn a Sidekiq background job. At this point the controller updates the screen using a Turbo response. I would like to also update the screen inside the background job. Is there a way to connect the background job to the turbo stream set for this user session?

Wow. Who knew such a simple question had no answer. I can only assume it either no one uses Turbo and background jobs, or it truly is stunningly easy to broadcast a message from a job to a specific browser session.

Or quite frankly, this is impossible, and I should forget it and move on. Can any who has dabbled with this please comment and help a guy out a little bit?

You can broadcast a TurboStream (from the affected model) and if you’ve registered a turbo-stream on the page, it will do whatever you configured that broadcast to do (prepend, append to a collection; replace; remove) in any page that is still connected to the server and listening for cable traffic. Turbo-Frames respond to a browser request, not a background server request. Once you queue a job in the background, the request is over, and there can be no further reply.

Walter

Ok thanks. So unless the background job affects a model, there is no hope of updating from a job. Good to know.

Not correct. Models have convenience methods to make it easier, but you can broadcast without a model using Turbo::StreamsChannel.

Alert ALL users

<%= turbo_stream_from :alerts %>
Turbo::StreamsChannel.broadcast_render_to(:alerts, target: 'alerts', partial: 'alerts/my_partial', locals: { message: alert.message })

However, models do help limit the scope as to who receives the broadcast. For example, only broadcast alerts to a specific user

Alert Specific users

<%= turbo_stream_from current_user.id, :alerts %>
# alert specific user
Turbo::StreamsChannel.broadcast_render_to(@alert.user_id, :alerts, target: 'alerts', partial: 'alerts/my_partial', locals: { message: @alert.message })
1 Like

Ahhh, so maybe this will work? All my templates have a current_shop. The background job works on a current shop in the sense that it will do work on behalf of the shop. So if in my template where a button was clicked to inform the server “hey server, start a job”, if I have a turbo element as in

<%= turbo_stream_from current_shop, :alerts %>

and when my background job ends, I then run a

Turbo::StreamChannel.broadcast_render_to(current_shop, :alerts, target: 'alerts', partial: 'alerts/my_partial', locals: {message: 'Job completed'}

That would present an alert on the screen with that message? I am going to have to try that out. If that works, that is pretty cool.

Any update on your work here? Trying same solution, but the SteamChannel broadcast doesn’t seem to have any affect from within the background job.

I never tried what I said I would try! Ack! Oopsie. I cannot help with an answer right now.

In your browser developer tools, can you see the message broadcast over the websocket?

I have an issue almost similar to this I have a job in which I’m calling a service and in this service I’m using turbo stream channel to broadcast my question in real time. but I’m getting this error : @view_context.formats |= [:html]

this is how I’m writing code:

Turbo::StreamsChannel.broadcast_render_to(
          "draft_question",
          turbo_stream.update(
            target: dom_id(draft_question),
            partial: "admin/import_questions/draft_questions",
            locals: { question: draft_question, current_organization: draft_question.organization_id }
          )
        )

also in my service I’m including the following too:

 include Turbo::StreamsHelper
  include ActionView::RecordIdentifier

can anyone please suggest some solution to resolve this issue: @view_context.formats |= [:html]

can anyone guide me about it?

So my problem got resolved. I made a delay in job call which helped broadcast stream in getting the dom id.

Before I was calling like this:
ImportQuestionsJob.perform_later(file_id.to_i, current_organization.id)

now I’m calling like this and it is working for me in displaying questions in real time using turbo stream and job:
ImportQuestionsJob.set(wait: 2.second).perform_later(file_id.to_i, current_organization.id)

What is the reason for waiting 2 seconds?

It seems a bit hacky. I would not want to delay jobs like that unless there is a very good reason for it?