You can disbelieve all you like Bret, but this is the behavior we observed. I believe my own eyes over your protestations. There could be some semantic confusion here over scope, what is meant by a "session" . . . what is meant by a "code run" . . . which is partly what this thread was about. Scope and globals are things that in my experience have tended to be glossed over or even overlooked in Rails documentation, so a discussion might be valuable to readers anyway. Let me explain in more detail. First off, this is not a test at all, it is an application.

I was not and am not trying to blame Ruby for anything. There are ways this could happen without it being a problem with globals . . . but if so, that is something that should not be happening, either, and the effect is the same! Thus my warning: don't use globals unless you have to, and be aware of the possible consequences. But then as programmers we should know that already.

Here are the gory details:

We had a server set up using multiple Mongrels with a lighttpd front end. A server will wait for an http request to come in, and fire off our Rails application. The program exits when done (i.e., the controller .rb runs to completion, no polling loop or anything), but of course the server does not shut down . . . it waits for more requests.

We send behavioral settings to our application via POST variables. Some of these variables may be present in one http request, but not in another. Program behavior was controlled by global variables that are loaded from these POST variables. If a POST variable was present in the request, a global variable would be instantiated and given the same value. If a POST variable is not in the request, the global does not get instantiated in the code. This is an important point.

When more requests came through (on the same Mongrel server), we found that global variables set in a previous run would carry over to the next, unless those variables were explicitly reset by the latter. These are not cookies or other session data. This was information being stored in only one place: a few global variables. And the program "exits" between "runs".

Let us say one http request contained a POST variable "foo", with the value "bar". At the beginning of the application (the first action on entering our controller) we check our params hash, and instantiate a global variable named "global_foo" and give it that value. Certain behaviors of the program are controlled by "global_foo". The program does one thing if it is nil, something else if it has a value.

The program (controller) finishes AND EXITS. The next http request comes in . . . a different "session". The appropriate action of the controller is kicked off again. But wait . . . in this request, there is no POST variable with the name "foo". So our global variable does not get instantiated in the controller's code. Yet the program behaves in the same way as it did in the previous run . . . so the variable "global_foo" is not nil. In fact, on checking, we find that not only does it exist, it still contains the value "bar".

I see three ways this could happen, there may be more: (1) The variable "global_foo" is truly "global" on that server from the moment it is instantiated, and remains in memory even when there is no "running code". This is contrary to the way I understood globals to function . . . in which case there was a misunderstanding regarding scope, which in turn is due to lack of adequate documentation. That is to say, it was my understanding that controllers constitute "top level" code, and therefore when a controller exits, all information that is "global" to that controller should be lost. If, however, a "global" functions across controllers, effecting the whole "app", then this makes sense . . . but should be better documented. (2) The variable "global_foo" is supposed to be global only up to the controller context, but something went wrong and it is getting improperly preserved. (3) The POST variables are being improperly preserved (cached) between sessions.

In order of decreasing liklihood, I would rank these (1), (3), (2). When I stated that this could be due to some kind of server caching, I was referring to situation (3) . . . POST variables being improperly cached. That could be tested but I do not think it is very likely.

Which still justifies my warning. Even if this is intended behavior (1), Rails programmers should be aware that globals can be preserved across what web developers normally consider to be "sessions", and even when there is not any "running code" in the sense of a traditional application. And they can.  Values that were set in global variables WERE carrying over from one "session" (incoming http request) to the next. And we definitely did not want that!

You can protest that this was some kind of coding error . . . but what kind?  The only possible error of that kind was a misunderstanding of what constitutes global scope . . . which was the point of my warning.

When we took the exact same code and made only one change -- changed the globals to instance variables -- the code worked exactly as expected. There was then no carryover from one session to the next. It is really very simple: when we stored a value in an instance variable, it worked exactly as expected. When it was stored in a global, it carried over to the next code "run". Changing them back to globals recreated the problem. Changing them to instance variables got rid of it again. No other code was changed, or even the names . . . just the @.

I think part of the problem here is terminology. What is meant by a "session"? If we mean anything like a PHP "session", i.e, something like one visit from one browser on one IP address (close enough), then we have observed "globals" being preserved across sessions. If by "session" you mean from the time the server is started until it shuts down . . . then we have a terminology gap.

It is my understanding that Mongrel loads the code and holds it in memory . . . but after our application sends its reply, the program exits completely. The "session" should be done. There is no polling loop or anything of that nature. It just ends.

I was defining a "session" as an http request coming in, being processed, and then completed. The program exits. The next http request to come in is a different "session".  We are not storing cookies or any other "session" data in any way. Yet there is no doubt that global variables were being preserved between two different "sessions", even when separated by several minutes and coming from different IP addresses.

Once again: We agree that this should not happen. But it did. Which is exactly why I posted that warning.

Lonny Eachus
=========

Subject:
Re: [Wtr-general] BUG: New IE windows share session state with existingopen windows
From:
"Bret Pettichord" <[EMAIL PROTECTED]>
Date:
Tue, 6 Jun 2006 23:10:35 -0500

. . .

Lonny, I don't mean any disrespect, but i frankly have a hard time believing this explanation. There is no server caching of global variables.

If you type "ruby foo.rb" to run your tests and then do "ruby bar.rb" to run more tests (or "ruby foo.rb" again) there is no way the global variables from the first run are going to affect the global variables in a later run.

You either are talking about cross-effects between two tests executed in the same ruby session (which is what would be expected to happen) or else some side effects of the first test are persisting (in a cache or something) so that they affect a later run (in which case using globals vs. locals wouldn't make a difference).

If globals really were as unreliable as you report, then Ruby would be a shit language and no one would use it. (Or maybe it would be a magic language and everyone would want to use it?)

I believe that you had a problem and then got rid of the global variables and the problem went away, but i think there must have been something else involved besides that.

Bret
_______________________________________________
Wtr-general mailing list
Wtr-general@rubyforge.org
http://rubyforge.org/mailman/listinfo/wtr-general

Reply via email to