4

When user logs into my website I want to open a connection with another server. User requests to my server would translate into read/write requests to that server. The connection object with that server should be alive as long as user is logged in, so that I don't need to reconnect on every user request.

As I understand ruby on rails, all objects in Controllers/Helpers get cleared out once the request is finished. I need to keep that object alive throughout many requests to different controllers until user logs off.

Architecture-wise, is there any place in the RoR framework where such objects can be put?

  • Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results – Roman Kiselenko Apr 17 '14 at 16:09
  • @Monk_Code This isn't so much a code question; its more of a concept question. Their wording might make it seem like they are outright asking for code, but it is more that they are asking how to (conceptually) persist a Ruby object between requests. – Paul Richter Apr 17 '14 at 16:14
  • @user3545974 If every request should be delegated to this other server, what is your rails app actually doing? Also I don't think you need to worry about maintaining a persistent connection to this other server between requests; allowing connections to open and close like this is normal. Are you concerned about something, in that regard? – Paul Richter Apr 17 '14 at 16:17
  • @PaulRichter It is not that every request is directly delegated to that server but many of those. My app is a mix of that server's functionality and my own functionality. I think it would be cleaner to keep persistent connection. But anyhow, even if I decide to open/close connection on every request, is there a way to keep alive object in RoR between requests? – user3545974 Apr 17 '14 at 16:35
  • @user3545974 You'd need some sort of persistent in-memory cache. Take a look at [this answer](http://stackoverflow.com/a/8650892/877472), it has some links to gems for exactly that purpose. Ultimately, its a background process that stores your data. I've used redis myself. Alternatively, as a general suggestion, storing the object's data in the database using the regular ActiveRecord mechanism is also a good idea; that way you don't have to worry about stale states if there's a risk of multiple asynchronous requests. – Paul Richter Apr 17 '14 at 16:46

3 Answers3

2

Basically, I think the answer to your question is no. There are ways to store 'basic' data for a user between requests, whether in the DB, in memcache or redis or something similar, but all of those things are best for data that can be cleanly serialized: numbers, strings, maybe dates stored as strings. But any kind of network connection can't be serialized and deserialized reliably between requests, whether it's to a database or some other network resource like an external web service.

Without knowing any details of the sort of connection you want to keep open, I think you'll probably be better off simply making a new request from rails to that service each time. If it's a long request, one thing you could do would be to perform those requests in a background process. The web client JS code would then make the initial request, get back a request id, then ping the rails app every couple seconds with the request ID to see if the response was ready yet. When ready, it can read the response etc. That doesn't reduce the total time to make the request and get a response, but it can make a page seem a little more responsive to the user.

sockmonk
  • 4,195
  • 24
  • 40
2

The gem 'connection_pool' works pretty well.

https://github.com/mperham/connection_pool

It allows you to distribute the connections across a pool of clients.

You can create an initializer file, where it would create 10 clients to distribute the connections/work. config/initializers/connections.rb

$conn_pool = ConnectionPool::Wrapper.new(size: 10, timeout: 3) { 
   MyConnectionClass.new(my_conn_variables)
}

And you can use it in your code like this.
app/helpers/my_helper.rb

$conn_pool.send_to_my_server()
cevaris
  • 5,671
  • 2
  • 49
  • 34
0

I tend to just use a helper function that has a class level method with an or equals to set up the connection, e.g.

def server_connection
  @@connection ||= MyConnectionClass.new(variables, to, set, up, connection)
end

Dead simple and now you're persisting your connection across the sessions and setting it up if it's not already set up (with ruby's brilliant ||= operator.)

EDIT: just realised this was per user, I would possibly just modify my current version to include a hash based on user id that stores the connections, simple and easy, just remember to close the connection when the user logs out or is idle for too long. It seems like a good choice for a small class to encapsulate it actually

def server_connection(user)
  @@connection_hash ||= {}
  @@connection_hash[user.id] ||= MyConnectionClass.new(variables, to, set, up, connection, for user)
end
Mike H-R
  • 7,726
  • 5
  • 43
  • 65
  • That looks like it would set up a connection, but only for the particular worker process that handled the request. Other workers (thinking of unicorn) or even threads would not have the same connection, much less other web servers if you're balancing requests over more than one (probably virtual) server. – sockmonk Apr 17 '14 at 16:58
  • @sockmonk interesting point, I suppose that sounds right. each worker would have it's own connection which would be slightly inefficient. That's a shame because I like how simple and elegant the code for this solution is. :( – Mike H-R Apr 17 '14 at 17:02
  • It does look nice and elegant. You'd need a basic `@@connection_hash[user.id].disconnect if @@connection_hash[user.id]` on logout also, which is also nice and simple until you have to worry about all the separate workers with their separate connections to disconnect .... – sockmonk Apr 17 '14 at 17:16
  • @sockmonk exactly right, which is why I'm kinda bummed that that happens. oh well, useful trick to have, and this is basically the singleton pattern in ruby. It's just well pointed out that a more advanced singleton pattern is required to deal with multithreaded environments. – Mike H-R Apr 17 '14 at 17:41