Search code examples
ruby-on-railsrubyslack-api

Rails - multiple theads to avoid the slack 3 second API response rule


I am working with the slack API. My script does a bunch of external processing and in some cases it can take around 3-6 seconds. What is happening is the Slack API expects a 200 response within 3 seconds and because my function is not finished within 3 seconds, it retries again and then it ends up posting the same automated responses 2-3 times.

I confirmed this by commenting out all the functions and I had no issue, it posted the responses to slack fine. I then added sleep 10 and it done the same responses 3 times so the ohly thing different was it took longer.

From what I read, I need to have threaded responses. I then need to first respond to the slack API in thread 1 and then go about processing my functions.

Here is what I tried:

def events
  Thread.new do
    json = {
      "text": "Here is your 200 response immediately slack",
    }
    render(json: json)
  end
    
  puts "--------------------------------Json response started----------------------"
  sleep 30
  puts "--------------------------------Json response completed----------------------"
  puts "this is a successful response"
end

When I tested it the same issue happened so I tried using an online API tester and it hits the page, waits 30 seconds and then returns the 200 response but I need it to respond immediately with the 200, THEN process the rest otherwise I will get duplicates.

Am I using threads properly or is there another way to get around this Slack API 3 second response limit? I am new to both rails and slack API so a bit lost here.

Appreciate the eyes :)


Solution

  • I would recommend using ActionJob to run the code in the background if you don't need to use the result of the code in the response. First, create an ActiveJob job by running:

    bin/rails generate job do_stuff
    

    And then open up the file created in app/jobs/do_stuff_job.rb and edit the #perform function to include your code (so the puts statements and sleep 30 in your example). Finally, from the controller action you can call DoStuff.perform_later and your job will run in the background! Your final controller action will look something like this:

    def events
      DoStuff.perform_later # this schedules DoStuff to be done later, in
                            # the background, so it will return immediately
                            # and continue to the next line.
      json = {
        "text": "Here is your 200 response immediately slack",
      }
      render(json: json)
    end
    

    As an aside, I'd highly recommend never using Thread.new in rails. It can create some really confusing behavior especially in test scripts for a number of reasons, but usually because of how it interacts with open connections and specifically ActiveRecord.