DEV Community

HELP NEEDED: Understanding Rails ActionController::Live Module (and Async Limitations)

Isa Levine on November 11, 2019

The Situation: Practicing Data Ingestion in Rails (and Trying to be Async) So, for reasons that (ahem) may or may not be job-interview-r...
Collapse
 
derrelldurrett profile image
Derrell Durrett

If by chance you still have this project's code available, could you share the configurations you used? I'm trying to use server-sent events, and I can't even duplicate the behavior you demonstrate here (I'm interested in creating an API that pumps out events while waiting for something else to finish, and am only attempting to see the events via curl, to no avail).

Collapse
 
isalevine profile image
Isa Levine

Hi Derrell! Here are the repos for the broadcaster and listener, please feel to poke around for the configurations you need:

Broadcaster: github.com/isalevine/rails-data-in...

Listener: github.com/isalevine/rails-data-in...

Let me know if you run into specific issues setting these up and running them, and I'll help as best I can! :)

Collapse
 
derrelldurrett profile image
Derrell Durrett

Many thanks!

Collapse
 
elyalvarado profile image
Ely Alvarado

It looks like the issue is not in your broadcaster server, but in your listener server.

From the Net::HTTP docs:

By default Net::HTTP reads an entire response into memory

So you need to use response.read_body and pass a block to it, instead of response.body in your listener:

See the docs: ruby-doc.org/stdlib-2.6.5/libdoc/n...

Collapse
 
isalevine profile image
Isa Levine

Hi Ely, great call on this! response.read_body is exactly what I was missing. I really appreciate you pointing me to the right place in the docs! :)

In the end, here's the listener_controller code I used:

require 'net/http'

class ListenerController < ApplicationController
    def index
        url = URI.parse('http://localhost:3000/broadcaster')

        Net::HTTP.start(url.host, url.port) do |http|
            request = Net::HTTP::Get.new(url.to_s)
            http.request(request) do |response|

                puts <<-READOUT

                res.read_body:
                ==============================

                READOUT

                response.read_body do |data_str|
                    if data_str != ""
                        data_hash = eval(data_str.slice!(6..-1))    # slice to remove leading "data: " substring
                        char_hash = data_hash[:character]
                        Character.create("uuid": char_hash[:uuid], "name": char_hash[:name], "hp": char_hash[:hp], "magic": char_hash[:magic])
                    end
                end
            end
        end
    end
end

Thank you again!

Collapse
 
nikolalsvk profile image
Nikola Đuza

Hi, Isa. I ran into similar problems like you did. Here's a blog post that explained it well jvns.ca/blog/2021/01/12/day-36--se...

I had to enforce Last-Modified header to get SSE to actually stream instead of showing up at once. Hope this helps. Thanks for an awesome article!

Collapse
 
256hz profile image
Abe Dolinger

Maybe it's waiting for the end of the stream? What happens if you open and close the stream each time you generate a character?

Collapse
 
isalevine profile image
Isa Levine

That's what I thought too! One of the things I tried was moving the 5.times do loop outside of the begin statement, so that a new SSE is opened and closed once per loop:

        5.times do  
            sse = SSE.new(response.stream)
            begin
                # 5.times do      
                    character_hash = {
                        "uuid": SecureRandom.uuid,
                        "name": name_array.sample,
                        "hp": hp_array.sample,
                        "magic": magic_array.sample
                    }
                    sse.write({ character: character_hash })
                    sleep 1
                # end
            rescue IOError
                # client disconnected
            ensure
                sse.close
            end
        end

And fascinatingly, only ONE character is generated and sent now! Whyyyyy Rails, why won't you let me send multiple SSEs?!

Collapse
 
256hz profile image
Abe Dolinger • Edited

That's interesting. Well, at least it confirms it's waiting for the end of the stream to finish receiving. I wonder if the receiver can be somehow switched back into a state of listening for a new connection at that point?

Collapse
 
fuentesjr profile image
Salvador Fuentes Jr

I'm having trouble with this as well ... what version of rails are you using?

Collapse
 
isalevine profile image
Isa Levine

Hi Salvador! Both the broadcaster and listener repos (linked below) are using Rails 5.2.3.

Broadcaster: github.com/isalevine/rails-data-in...

Listener: github.com/isalevine/rails-data-in...