Friends- I see more and more folks wanting an application wide context object that will work regardless of server software(webrick,mongrel, fcgi''s, scgi''s) This can be a very useful technique if done correctly. Here is a little info that I have gleaned on a great way to do this type of thing. -----Easy AppServer---------------- An easy way to get yourself an application wide context is to use drb. Ara Howard has written an awesome library called slave that takes care of forking a child process to run your drb server in and then doing a heartbeat back and forth from your rails app to the application wide classes(AppServer from here on out.). This heartbeat is needed so that when rails starts, the AppServer starts and when rails stops the AppServer stops because the heartbeat doesn''t respond anymore. This slave library allows you to publish whatever class you want as an application wide class. This means that all your requests can get the same application wide vars or objects no matter how many fcgi''s or mongrel''s you have serving up requests. You could also just as easily serve up an app wide array or hash. Lets look at a small example of this. Get the slave library here: http://codeforpeople.com/lib/ruby/slave/slave-0.0.0/README http://codeforpeople.com/lib/ruby/slave/slave-0.0.0.tgz Now lets publish a simple hash that we can use application wide. Put the following in your environment.rb file so it starts up when rails starts up. $application_wide_hash = Slave.new(Hash.new).object Thats all. Now you can use $application_wide_hash anywhere in your rails apps and it will behave just like a hash. So you are still responsible for making suitable hash keys that you can use between requests and still distinguish from one another. But since you can store any object in a ruby hash you can store anything you want in this $application_wide_hash. Just make a unique key and store it in the users session. Maybe something like this: key = "#{current_user.id}-#{Time.now}-#{rand} session[:app_server_key] = key $application_wide_hash[key] = SomeObjectOrValueYouWantToStoreInApplicationWideStorage Then on the next request you can get that object back with this: $application_wide_hash[session[:app_server_key]] This approach allows for a very flexible application wide context. It''s uses are only limited to your creativity ;) Of course you might want to publish a different class then a hash in this AppServer. If you want to do that then just replace this: $application_wide_hash = Slave.new(Hash.new).object with this: $application_wide_hash = Slave.new(MyAppServerObject.new).object The slave library will take care of forking a background process and starting a drb server inside said process. Then the heartbeat takes care of shutting down the AppServer when you shut down rails so you don''t get orphaned AppServers. Of course all the context in your $application_wide_hash will be lost when you restart or shutdown rails and you are responsible for deleting things out of the $application_wide_hash when you are done with them. If you really want the $application_wide_hash to be persistent between restarts of rails then you will need to do a little custom coding yourself. With a bit of hacking you can hook into the heartbeat code and marshall the $application_wide_hash to disk as Yaml or binary and then reload that when rails starts back up. The objects or classes that you put in the AppServer cannot contain procs, lambdas or anything else that cannot be marshalled by ruby and drb. The way around this is to use DrbUndumped. I won''t go into detail about that here but you can look up resources on how to do this with google ;) I have a full chapter on this technique in my forthcoming book that goes into detail of what this process can be used for and what is shouldn''t be used for. As well as concrete examples and code that folks might find useful if they need to do this kind of thing. Cheers- -Ezra
Thanks for this tip, Ezra. However, I think there must be something I''m not understanding. As far as I can tell, each application process (mongrel/fcgi/whatever) would spawn its own separate DRb process, thus making it roughly equivalent to a normal global variable (i.e. not shared across application processes). I didn''t inspect the code for the slave library very closely, so there''s probably something I''m missing. If you could clear this up for me, that would be great. Thanks, Gabe On 4/8/06, Ezra Zygmuntowicz <ezmobius@gmail.com> wrote:> > Friends- > > I see more and more folks wanting an application wide context > object > that will work regardless of server software(webrick,mongrel, fcgi''s, > scgi''s) This can be a very useful technique if done correctly. Here > is a little info that I have gleaned on a great way to do this type > of thing. > > -----Easy AppServer---------------- > > An easy way to get yourself an application wide context is to use > drb. Ara Howard has written an awesome library called slave that > takes care of forking a child process to run your drb server in and > then doing a heartbeat back and forth from your rails app to the > application wide classes(AppServer from here on out.). This heartbeat > is needed so that when rails starts, the AppServer starts and when > rails stops the AppServer stops because the heartbeat doesn''t respond > anymore. > > This slave library allows you to publish whatever class you want > as > an application wide class. This means that all your requests can get > the same application wide vars or objects no matter how many fcgi''s > or mongrel''s you have serving up requests. You could also just as > easily serve up an app wide array or hash. Lets look at a small > example of this. > > Get the slave library here: > > http://codeforpeople.com/lib/ruby/slave/slave-0.0.0/README > http://codeforpeople.com/lib/ruby/slave/slave-0.0.0.tgz > > Now lets publish a simple hash that we can use application wide. > Put > the following in your environment.rb file so it starts up when rails > starts up. > > > $application_wide_hash = Slave.new(Hash.new).object > > Thats all. Now you can use $application_wide_hash anywhere in your > rails apps and it will behave just like a hash. So you are still > responsible for making suitable hash keys that you can use between > requests and still distinguish from one another. But since you can > store any object in a ruby hash you can store anything you want in > this $application_wide_hash. Just make a unique key and store it in > the users session. Maybe something like this: > > > key = "#{current_user.id}-#{Time.now}-#{rand} > session[:app_server_key] = key > > $application_wide_hash[key] > SomeObjectOrValueYouWantToStoreInApplicationWideStorage > > > Then on the next request you can get that object back with this: > > $application_wide_hash[session[:app_server_key]] > > > This approach allows for a very flexible application wide context. > It''s uses are only limited to your creativity ;) Of course you might > want to publish a different class then a hash in this AppServer. If > you want to do that then just replace this: > > $application_wide_hash = Slave.new(Hash.new).object > > with this: > > $application_wide_hash = Slave.new(MyAppServerObject.new).object > > > The slave library will take care of forking a background process > and > starting a drb server inside said process. Then the heartbeat takes > care of shutting down the AppServer when you shut down rails so you > don''t get orphaned AppServers. Of course all the context in your > $application_wide_hash will be lost when you restart or shutdown > rails and you are responsible for deleting things out of the > $application_wide_hash when you are done with them. > > If you really want the $application_wide_hash to be persistent > between restarts of rails then you will need to do a little custom > coding yourself. With a bit of hacking you can hook into the > heartbeat code and marshall the $application_wide_hash to disk as > Yaml or binary and then reload that when rails starts back up. > > The objects or classes that you put in the AppServer cannot > contain > procs, lambdas or anything else that cannot be marshalled by ruby and > drb. The way around this is to use DrbUndumped. I won''t go into > detail about that here but you can look up resources on how to do > this with google ;) > > I have a full chapter on this technique in my forthcoming book > that > goes into detail of what this process can be used for and what is > shouldn''t be used for. As well as concrete examples and code that > folks might find useful if they need to do this kind of thing. > > Cheers- > -Ezra > > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060412/69206dc2/attachment-0001.html
Gabe- Hrmm. You might be right Gabe. I will have to do some more tests, This setup is working for me and I haven''t had any issue even when running multiple fcgi listeners. But it might just be luck. Now that I think about it I am not certain if there is a place in rails that will only run once on startup no matter how many fcgi processes you have running. So I think the best way to be certain of this right now until I do a little more research is to make a rake task that starts the app server. But that night not take care of the issue entirely. Let me think on this and I will come up with something and package it as a plugin so its easy to use. Need a way to make sure that only one $appserver gets created so its a singleton and that all fcgi''s get a handle to this one app server. I will do some experimenting and see what I can some up with to make this seamless. Cheers- -Ezra On Apr 12, 2006, at 9:39 AM, Gabe Boyer wrote:> Thanks for this tip, Ezra. > > However, I think there must be something I''m not understanding. As > far as I can tell, each application process (mongrel/fcgi/whatever) > would spawn its own separate DRb process, thus making it roughly > equivalent to a normal global variable ( i.e. not shared across > application processes). > > I didn''t inspect the code for the slave library very closely, so > there''s probably something I''m missing. If you could clear this up > for me, that would be great. > > Thanks, > Gabe > > On 4/8/06, Ezra Zygmuntowicz <ezmobius@gmail.com> wrote: > Friends- > > I see more and more folks wanting an application wide > context object > that will work regardless of server software(webrick,mongrel, fcgi''s, > scgi''s) This can be a very useful technique if done correctly. Here > is a little info that I have gleaned on a great way to do this type > of thing. > > -----Easy AppServer---------------- > > An easy way to get yourself an application wide context is > to use > drb. Ara Howard has written an awesome library called slave that > takes care of forking a child process to run your drb server in and > then doing a heartbeat back and forth from your rails app to the > application wide classes(AppServer from here on out.). This heartbeat > is needed so that when rails starts, the AppServer starts and when > rails stops the AppServer stops because the heartbeat doesn''t respond > anymore. > > This slave library allows you to publish whatever class you > want as > an application wide class. This means that all your requests can get > the same application wide vars or objects no matter how many fcgi''s > or mongrel''s you have serving up requests. You could also just as > easily serve up an app wide array or hash. Lets look at a small > example of this. > > Get the slave library here: > > http://codeforpeople.com/lib/ruby/slave/slave-0.0.0/README > http://codeforpeople.com/lib/ruby/slave/slave-0.0.0.tgz > > Now lets publish a simple hash that we can use application > wide. Put > the following in your environment.rb file so it starts up when rails > starts up. > > > $application_wide_hash = Slave.new(Hash.new).object > > Thats all. Now you can use $application_wide_hash anywhere > in your > rails apps and it will behave just like a hash. So you are still > responsible for making suitable hash keys that you can use between > requests and still distinguish from one another. But since you can > store any object in a ruby hash you can store anything you want in > this $application_wide_hash. Just make a unique key and store it in > the users session. Maybe something like this: > > > key = "#{current_user.id}-#{Time.now}-#{rand} > session[:app_server_key] = key > > $application_wide_hash[key] > SomeObjectOrValueYouWantToStoreInApplicationWideStorage > > > Then on the next request you can get that object back with > this: > > $application_wide_hash[session[:app_server_key]] > > > This approach allows for a very flexible application wide > context. > It''s uses are only limited to your creativity ;) Of course you might > want to publish a different class then a hash in this AppServer. If > you want to do that then just replace this: > > $application_wide_hash = Slave.new(Hash.new).object > > with this: > > $application_wide_hash = Slave.new(MyAppServerObject.new).object > > > The slave library will take care of forking a background > process and > starting a drb server inside said process. Then the heartbeat takes > care of shutting down the AppServer when you shut down rails so you > don''t get orphaned AppServers. Of course all the context in your > $application_wide_hash will be lost when you restart or shutdown > rails and you are responsible for deleting things out of the > $application_wide_hash when you are done with them. > > If you really want the $application_wide_hash to be persistent > between restarts of rails then you will need to do a little custom > coding yourself. With a bit of hacking you can hook into the > heartbeat code and marshall the $application_wide_hash to disk as > Yaml or binary and then reload that when rails starts back up. > > The objects or classes that you put in the AppServer cannot > contain > procs, lambdas or anything else that cannot be marshalled by ruby and > drb. The way around this is to use DrbUndumped. I won''t go into > detail about that here but you can look up resources on how to do > this with google ;) > > I have a full chapter on this technique in my forthcoming > book that > goes into detail of what this process can be used for and what is > shouldn''t be used for. As well as concrete examples and code that > folks might find useful if they need to do this kind of thing. > > Cheers- > -Ezra > > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060412/03a78f80/attachment-0001.html
I don''t think there is any code that Rails will run only once, regardless of the number of fcgi processes. Even if there was, the "heartbeat" that the slave library sets up would only refer back to a single process, putting one of your fcgi listeners in the uncomfortable position of being a kind of "master" process (i.e. if that process goes down, but others are still alive, the AppServer would shut down). I think the logic for running the AppServer process would probably have to look something like the following: 1. Configure a specific URI for the AppServer process 2. On start, a Rails process (fcgi, etc.) checks to see if AppServer is live 3. If it isn''t, start AppServer 4. Register with AppServer to receive a "heartbeat" AppServer would then have to check all back to all registered Rails processes to see if they are alive, and would only shut down if none were. The above would require extending the slave library, and is significantly more complex than your original solution, but I''m not sure that''s avoidable. Gabe On 4/12/06, Ezra Zygmuntowicz <ezmobius@gmail.com> wrote:> > Gabe- > Hrmm. You might be right Gabe. I will have to do some more tests, This > setup is working for me and I haven''t had any issue even when running > multiple fcgi listeners. But it might just be luck. Now that I think about > it I am not certain if there is a place in rails that will only run once on > startup no matter how many fcgi processes you have running. So I think the > best way to be certain of this right now until I do a little more research > is to make a rake task that starts the app server. > > But that night not take care of the issue entirely. Let me think on this > and I will come up with something and package it as a plugin so its easy to > use. Need a way to make sure that only one $appserver gets created so its a > singleton and that all fcgi''s get a handle to this one app server. > > I will do some experimenting and see what I can some up with to make this > seamless. > > Cheers- > -Ezra > > > On Apr 12, 2006, at 9:39 AM, Gabe Boyer wrote: > > Thanks for this tip, Ezra. > > However, I think there must be something I''m not understanding. As far as > I can tell, each application process (mongrel/fcgi/whatever) would spawn its > own separate DRb process, thus making it roughly equivalent to a normal > global variable ( i.e. not shared across application processes). > > I didn''t inspect the code for the slave library very closely, so there''s > probably something I''m missing. If you could clear this up for me, that > would be great. > > Thanks, > Gabe > > On 4/8/06, Ezra Zygmuntowicz <ezmobius@gmail.com> wrote: > > > > Friends- > > > > I see more and more folks wanting an application wide context > > object > > that will work regardless of server software(webrick,mongrel, fcgi''s, > > scgi''s) This can be a very useful technique if done correctly. Here > > is a little info that I have gleaned on a great way to do this type > > of thing. > > > > -----Easy AppServer---------------- > > > > An easy way to get yourself an application wide context is to > > use > > drb. Ara Howard has written an awesome library called slave that > > takes care of forking a child process to run your drb server in and > > then doing a heartbeat back and forth from your rails app to the > > application wide classes(AppServer from here on out.). This heartbeat > > is needed so that when rails starts, the AppServer starts and when > > rails stops the AppServer stops because the heartbeat doesn''t respond > > anymore. > > > > This slave library allows you to publish whatever class you want > > as > > an application wide class. This means that all your requests can get > > the same application wide vars or objects no matter how many fcgi''s > > or mongrel''s you have serving up requests. You could also just as > > easily serve up an app wide array or hash. Lets look at a small > > example of this. > > > > Get the slave library here: > > > > http://codeforpeople.com/lib/ruby/slave/slave-0.0.0/README > > http://codeforpeople.com/lib/ruby/slave/slave-0.0.0.tgz > > > > Now lets publish a simple hash that we can use application wide. > > Put > > the following in your environment.rb file so it starts up when rails > > starts up. > > > > > > $application_wide_hash = Slave.new(Hash.new).object > > > > Thats all. Now you can use $application_wide_hash anywhere in > > your > > rails apps and it will behave just like a hash. So you are still > > responsible for making suitable hash keys that you can use between > > requests and still distinguish from one another. But since you can > > store any object in a ruby hash you can store anything you want in > > this $application_wide_hash. Just make a unique key and store it in > > the users session. Maybe something like this: > > > > > > key = "#{current_user.id}-#{Time.now}-#{rand} > > session[:app_server_key] = key > > > > $application_wide_hash[key] > > SomeObjectOrValueYouWantToStoreInApplicationWideStorage > > > > > > Then on the next request you can get that object back with this: > > > > > > $application_wide_hash[session[:app_server_key]] > > > > > > This approach allows for a very flexible application wide > > context. > > It''s uses are only limited to your creativity ;) Of course you might > > want to publish a different class then a hash in this AppServer. If > > you want to do that then just replace this: > > > > $application_wide_hash = Slave.new(Hash.new).object > > > > with this: > > > > $application_wide_hash = Slave.new(MyAppServerObject.new).object > > > > > > The slave library will take care of forking a background process > > and > > starting a drb server inside said process. Then the heartbeat takes > > care of shutting down the AppServer when you shut down rails so you > > don''t get orphaned AppServers. Of course all the context in your > > $application_wide_hash will be lost when you restart or shutdown > > rails and you are responsible for deleting things out of the > > $application_wide_hash when you are done with them. > > > > If you really want the $application_wide_hash to be persistent > > between restarts of rails then you will need to do a little custom > > coding yourself. With a bit of hacking you can hook into the > > heartbeat code and marshall the $application_wide_hash to disk as > > Yaml or binary and then reload that when rails starts back up. > > > > The objects or classes that you put in the AppServer cannot > > contain > > procs, lambdas or anything else that cannot be marshalled by ruby and > > drb. The way around this is to use DrbUndumped. I won''t go into > > detail about that here but you can look up resources on how to do > > this with google ;) > > > > I have a full chapter on this technique in my forthcoming book > > that > > goes into detail of what this process can be used for and what is > > shouldn''t be used for. As well as concrete examples and code that > > folks might find useful if they need to do this kind of thing. > > > > Cheers- > > -Ezra > > > > > > _______________________________________________ > > Rails mailing list > > Rails@lists.rubyonrails.org > > http://lists.rubyonrails.org/mailman/listinfo/rails > > > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails > > > > _______________________________________________ > Rails mailing list > Rails@lists.rubyonrails.org > http://lists.rubyonrails.org/mailman/listinfo/rails > > >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://wrath.rubyonrails.org/pipermail/rails/attachments/20060412/0fc0adfb/attachment-0001.html