I wonder how many people (I’m one of them) started with basic Rails
applications serving HTML, JSON, or both, and eventually ran into a
where certain parts of the application became too slow, or were
to consume some 3rd party services, and ended up not being able to
synchronously serve the response in a timely manner, requiring switching
At an abstract level, the behaviour is as follows:
- Get request (could be either HTML or JSON)
- Initiate some kind of async job (“job” here is interpreted widely,
could be delayed/enqueue job or some other paradigm, point is, it’s
asynchronous to the request and has no guaranteed completion time)
- Respond to requestor with 202 Accepted status, or some other
signifying "we accepted your request but do not have a response yet)
- Include in response the URL client should check to visit response.
Note that this isn’t necessarily always mapping nicely to CRUD in
returning standard RESTful id. For example, nature of the job could
something like a very complex search/report query, of the form
/items/123?conditions=…, but we can’t just tell client to visit
/items/123 for their result, because different clients doing this
may request same resource but with different filter conditions.
- Client will poll the URL returned at last step, which will either
return “check back later” status if response is not done, or the
response if it’s finished (or, alternatively, 3rd URL to visit the
response once it’s complete, which client will then visit to get
- If response jobs need to be stored on server side, need some
mechanism to eventually clear them out.
The Rails Way
You may look at above and say “well, you have a custom requirement, so
write yourself a custom solution, Rails can’t read your mind”. And you
might be right. But, on the other hand:
- Over many projects I’ve been on, this has been a very common
requirement. For many applications which scale beyond a certain point
load-wise and 3rd-party-integration-wise, response times are often
guaranteed because of dependencies you have no direct control over,
can’t just hang the request until the job is done.
- I don’t know from the beginning when, and for which resources,
need async request/response handling. I want to be able to Just Code
using the basic simple Rails as I need it, and switch to async
later for needed endpoints only, as my application evolves. I want to
able to do this with minimal changes on both API and internal
implementation. For example, if my regular controller uses
(from session), current_account (from request), and other such
want to be able to continue using them in async controllers and not
re-write the whole controller/view after switching to async.
I was inspired to start this discussion by the latest Enqueue work added
Rails 4.2. After many years and many competing async job processors
(delayed_job, sidekiq, resque, etc.) Rails decided it made sense for
to provide a wrapper API so that code can be written in a consistent way
and the implementation be relatively easy to change with no external
impact. Just as importantly, it now becomes possible to write code that
synchronous yet uses the Enqueue API (using the “inline” adapter), and
later pick and choose which parts should become async based on the
The Enqueue API makes the backend job processing easier to make async,
the controller-level request-response handling is still a sore point:
- The URL pattern for async responses is different from standard
making migrations from sync to async requests painful and existing
- Rendering a RESTful response (either HTML or JSON) synchronously
trivial in normal Rails controllers; rendering it async is not. Even
seemingly simple things like rendering an existing model/view is not
without the familiar controller context. There are some gems that try
encapsulate it by constructing a custom controller and stubbing or
some examples Google found. Unfortunately, doing this is tedious work
makes it difficult to use existing session or request-based helpers
current_user or other methods from controller or application helpers.
of session/request caching and method re-definitions are needed. http://stackoverflow.com/questions/6318959/rails-how-to-render-a-view-partial-in-a-model
- This just doesn’t seem a “Rails Way” to solve my problem, it makes
feel like I’m fighting against the MVC/REST instead of leveraging it.
What do you think of being able to do something like:
class ItemsController < ApplicationController::Base
class ItemsController < ApplicationController::Base
render ‘show’, async: true
This would provide the ground work (e.g. REST/URL structure) to handle
requests in a way which would be possible to make asynchronous if and
needed. Similar to Enqueue, there could be an “inline” pattern that
synchronously, but allows smooth transition to true async later, e.g.
“redirect” would provide a response URL for client to visit.
Similar to the “enqueue” philosophy, the main purpose of the async
request/response API would not to actually force a specific
but to provide a wrapper API that the actual implementation can fit in.
Users can either write their custom async implementation or use a 3rd
gem, but any such implementation should conform to the expectations set
The above snippets are just hypothetical examples of what such an API
might be like, I’m open to totally different ideas to solve this
Have you previously worked with implementing SOA or other requests which
cannot be responded to immediately with final result because the job is
slow or distributed? I’d love to hear your opinion about this! Some
to consider would be:
- What was the response type of your application? HTML or JSON? Did
support normal forms, front-end JS frameworks, mobile APIs, etc?
- How did you handle such a problem? Was it similar to above or did
have a drastically different way?
- Did you design your application to have asynchronous responses
day 1, or did you start with a basic Rails application and had to
or parts of it respond asynchronously later? What was the migration
- Did you ever think that Rails could provide a more consistent
standard and easier migration path from sync to async responses?
Or perhaps you didn’t have to build such systems? Perhaps you think they
don’t even make sense and are not The Rails Way and don’t belong in
I’d love to hear from you too - if you think Rails helping to solve this
isn’t the right approach, then what might the right solution be/look
All feedback welcome!