unicorn Ruby/Rack server user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
* Thread.current
@ 2011-01-08  2:00 Jimmy Soho
  2011-01-08  2:57 ` Thread.current Eric Wong
  2011-01-08  3:09 ` Thread.current Curtis j Schofield
  0 siblings, 2 replies; 10+ messages in thread
From: Jimmy Soho @ 2011-01-08  2:00 UTC (permalink / raw)
  To: mongrel-unicorn

Hi All,

Having an akward noob moment... I have unicorn_rails (1.1.5) running
with 2 workers, with rails 2.3.10 in development mode. In
environment.rb at the bottom I have this line of code:

    puts "#{Time.current} #{Thread.current.object_id}:
#{Thread.current.keys.inspect}"

In a simple controller I have this:

   def index
     puts "#{Time.current} #{Thread.current.object_id}:
#{Thread.current.keys.inspect}"
     sleep 5
     puts "#{Time.current} #{Thread.current.object_id}:
#{Thread.current.keys.inspect}"
     render :text => "foo"
   end

In window 1 I tail log/unicorn.log.
In windows 2 and 3 I start at about the same time:  curl http://localhost:3000

The output is this:

2011-01-08 01:53:56 UTC 2148444460: [:__inspect_key__, :i18n_config,
:__recursive_key__]
worker=1 ready
2011-01-08 01:53:56 UTC 2148444460: [:__inspect_key__, :i18n_config,
:__recursive_key__]
worker=0 ready
2011-01-08 01:53:57 UTC 2148444460: [:__inspect_key__, :i18n_config,
:__recursive_key__]
2011-01-08 01:53:59 UTC 2148444460: [:__inspect_key__, :i18n_config,
:__recursive_key__]
2011-01-08 01:54:02 UTC 2148444460: [:__inspect_key__, :i18n_config,
:__recursive_key__]
2011-01-08 01:54:04 UTC 2148444460: [:__inspect_key__, :i18n_config,
:__recursive_key__]

Looking at the timings the 2 requests seem to have been handled in
parallel, as expected.

However, the Thread.current value within those parallel requests is
always the same.

Are two separate requests not handled by different threads? How does that work??


Cheers,
Jim
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-08  2:00 Thread.current Jimmy Soho
@ 2011-01-08  2:57 ` Eric Wong
  2011-01-08  3:09 ` Thread.current Curtis j Schofield
  1 sibling, 0 replies; 10+ messages in thread
From: Eric Wong @ 2011-01-08  2:57 UTC (permalink / raw)
  To: unicorn list

Jimmy Soho <jimmy.soho@gmail.com> wrote:
> Hi All,
> 
> Having an akward noob moment... I have unicorn_rails (1.1.5) running
> with 2 workers, with rails 2.3.10 in development mode. In
> environment.rb at the bottom I have this line of code:
> 
>     puts "#{Time.current} #{Thread.current.object_id}: #{Thread.current.keys.inspect}"

Add the PID ($$), too:

   puts "#{Time.current} #$$ #{Thread.current.object_id}: #{Thread.current.keys.inspect}"

> In a simple controller I have this:
> 
>    def index
>      puts "#{Time.current} #{Thread.current.object_id}: #{Thread.current.keys.inspect}"
>      sleep 5
>      puts "#{Time.current} #{Thread.current.object_id}: #{Thread.current.keys.inspect}"
>      render :text => "foo"
>    end
> 
> In window 1 I tail log/unicorn.log.
> In windows 2 and 3 I start at about the same time:  curl http://localhost:3000
> 
> The output is this:
> 
> 2011-01-08 01:53:56 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> worker=1 ready
> 2011-01-08 01:53:56 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> worker=0 ready
> 2011-01-08 01:53:57 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 2011-01-08 01:53:59 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 2011-01-08 01:54:02 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 2011-01-08 01:54:04 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 
> Looking at the timings the 2 requests seem to have been handled in
> parallel, as expected.

Yes, that's because you have two worker *processes*

> However, the Thread.current value within those parallel requests is
> always the same.

Just a lucky coincidence :)

> Are two separate requests not handled by different threads? How does
> that work??

Threads (and any other object) object_ids are unique to each process.
They are not unique within all the processes of a machine.

When dealing with native threads under Linux, gettid() is a non-portable
Linux syscall to get the unique identifier of a thread throughout the
entire system.  That's the only way I know of if you want a single
unique identifier (and of course prepending the PID to it).

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-08  2:00 Thread.current Jimmy Soho
  2011-01-08  2:57 ` Thread.current Eric Wong
@ 2011-01-08  3:09 ` Curtis j Schofield
  2011-01-08  5:54   ` Thread.current Jimmy Soho
  1 sibling, 1 reply; 10+ messages in thread
From: Curtis j Schofield @ 2011-01-08  3:09 UTC (permalink / raw)
  To: unicorn list

On Fri, Jan 7, 2011 at 6:00 PM, Jimmy Soho <jimmy.soho@gmail.com> wrote:
> Hi All,
>
> Having an akward noob moment... I have unicorn_rails (1.1.5) running
> with 2 workers, with rails 2.3.10 in development mode. In
> environment.rb at the bottom I have this line of code:
>
>    puts "#{Time.current} #{Thread.current.object_id}:
> #{Thread.current.keys.inspect}"
>
> In a simple controller I have this:
>
>   def index
>     puts "#{Time.current} #{Thread.current.object_id}:
> #{Thread.current.keys.inspect}"
>     sleep 5
>     puts "#{Time.current} #{Thread.current.object_id}:
> #{Thread.current.keys.inspect}"
>     render :text => "foo"
>   end
>
> In window 1 I tail log/unicorn.log.
> In windows 2 and 3 I start at about the same time:  curl http://localhost:3000
>
> The output is this:
>
> 2011-01-08 01:53:56 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> worker=1 ready
> 2011-01-08 01:53:56 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> worker=0 ready
> 2011-01-08 01:53:57 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 2011-01-08 01:53:59 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 2011-01-08 01:54:02 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
> 2011-01-08 01:54:04 UTC 2148444460: [:__inspect_key__, :i18n_config,
> :__recursive_key__]
>
> Looking at the timings the 2 requests seem to have been handled in
> parallel, as expected.
>
> However, the Thread.current value within those parallel requests is
> always the same.
>
> Are two separate requests not handled by different threads? How does that work??
>


Unicorn is a multi-process model with Inter-process communication -
more akin to a unix service - threads are not considered a wise
investment in the ruby community.

Worker1 / Worker0 are entirely separate processes - as is the master
unicorn process.

Review  Fork for more details.

http://en.wikipedia.org/wiki/Fork_(operating_system)
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-08  3:09 ` Thread.current Curtis j Schofield
@ 2011-01-08  5:54   ` Jimmy Soho
  2011-01-11 22:52     ` Thread.current Jimmy Soho
  0 siblings, 1 reply; 10+ messages in thread
From: Jimmy Soho @ 2011-01-08  5:54 UTC (permalink / raw)
  To: unicorn list

>>> However, the Thread.current value within those parallel requests is
>>> always the same.

EW> Just a lucky coincidence :)

I guess. Got confused there for a moment because of it. :)  Using $$
made things more clear.



Thanks

Jimmy
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-08  5:54   ` Thread.current Jimmy Soho
@ 2011-01-11 22:52     ` Jimmy Soho
  2011-01-11 23:12       ` Thread.current Eric Wong
  2011-01-11 23:12       ` Thread.current Jordan Ritter
  0 siblings, 2 replies; 10+ messages in thread
From: Jimmy Soho @ 2011-01-11 22:52 UTC (permalink / raw)
  To: unicorn list

Hi,

Some more questions still:

It seems a worker uses the exact same thread to handle each request.

Is that guaranteed to happen for the lifetime of a worker? Or are
there cases where a unicorn worker might spin a new thread to handle
the next requests?

If the same thread is always used, isn't that a potential issue when
programmers use thread local variables, which are not reset at the
next request?  (I know, the usage of thread local variables is not
recommended, but take a random rails project, go into their $GEM_HOME
and do grep -r Thread.current . , see what I mean..)


Cheers,
Jimmy



On Sat, Jan 8, 2011 at 4:54 PM, Jimmy Soho <jimmy.soho@gmail.com> wrote:
>>>> However, the Thread.current value within those parallel requests is
>>>> always the same.
>
> EW> Just a lucky coincidence :)
>
> I guess. Got confused there for a moment because of it. :)  Using $$
> made things more clear.
>
>
>
> Thanks
>
> Jimmy
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-11 22:52     ` Thread.current Jimmy Soho
@ 2011-01-11 23:12       ` Eric Wong
  2011-01-11 23:12       ` Thread.current Jordan Ritter
  1 sibling, 0 replies; 10+ messages in thread
From: Eric Wong @ 2011-01-11 23:12 UTC (permalink / raw)
  To: unicorn list

Jimmy Soho <jimmy.soho@gmail.com> wrote:
> Hi,
> 
> Some more questions still:
> 
> It seems a worker uses the exact same thread to handle each request.

Correct.

> Is that guaranteed to happen for the lifetime of a worker? Or are
> there cases where a unicorn worker might spin a new thread to handle
> the next requests?

Unicorn itself is always single-threaded and never spawns new threads.

> If the same thread is always used, isn't that a potential issue when
> programmers use thread local variables, which are not reset at the
> next request?  (I know, the usage of thread local variables is not
> recommended, but take a random rails project, go into their $GEM_HOME
> and do grep -r Thread.current . , see what I mean..)

Thats the problem of those libraries/apps, not Unicorn.

They can try Rainbows! using the :ThreadSpawn option which behaves much
like Mongrel 1, but uses Rainbows! in production that I know of.

The Rack +env+ hash is the safe/universal way to store request-local
variables across different web servers.

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-11 22:52     ` Thread.current Jimmy Soho
  2011-01-11 23:12       ` Thread.current Eric Wong
@ 2011-01-11 23:12       ` Jordan Ritter
  2011-01-12  3:07         ` Thread.current Jimmy Soho
  1 sibling, 1 reply; 10+ messages in thread
From: Jordan Ritter @ 2011-01-11 23:12 UTC (permalink / raw)
  To: unicorn list

Unicorn is purely about employing a multi-process model, not a multi-thread model; it specifically avoids spawning threads to handle inbound requests.   In fact, I'll bet that inside each request, Thread.current == Thread.main.

Separate from Unicorn, when running a rack-compatilbe app in multithreaded mode (the default when the app is invoked directly via rackup + config.ru), there's no guarantee about which thread will service a given request.  This fact may not matter to you, depending on what you're trying to do.

That said, you *could* use Thread local storage for per-request storage in either unicorn or multithreaded situations, so long as you wiped your storage at the beginning/end of each request -- but that's a crappy idiom, even if it might be "common" (don't know what you're referring to offhand).  Can't suggest a more appropriate pattern without knowing more about what you're actually trying to do.

cheers,
--jordan

On Jan 11, 2011, at 2:52 PM, Jimmy Soho wrote:

> Hi,
> 
> Some more questions still:
> 
> It seems a worker uses the exact same thread to handle each request.
> 
> Is that guaranteed to happen for the lifetime of a worker? Or are
> there cases where a unicorn worker might spin a new thread to handle
> the next requests?
> 
> If the same thread is always used, isn't that a potential issue when
> programmers use thread local variables, which are not reset at the
> next request?  (I know, the usage of thread local variables is not
> recommended, but take a random rails project, go into their $GEM_HOME
> and do grep -r Thread.current . , see what I mean..)
> 

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-11 23:12       ` Thread.current Jordan Ritter
@ 2011-01-12  3:07         ` Jimmy Soho
  2011-01-13  4:26           ` Thread.current Eric Wong
  0 siblings, 1 reply; 10+ messages in thread
From: Jimmy Soho @ 2011-01-12  3:07 UTC (permalink / raw)
  To: unicorn list

Some component don't always have to work within a webserver context,
and therefor assume no access to a Rack +env+ hash. Widely used
examples are the i18n and the active_support gem. In our case we have
for example queued jobs that are executed with the full rails
environment loaded, which does not have a rack context. This is not an
issue, but does explain (to some extent) why some components use
thread local storage instead of the rack +env+ context.

I'm not trying to use Thread local storage myself, it is forced upon
us. ;-) I'm trying to determine if the components we must use due to
dependencies, and which do use Thread local storage, if they are
leaking data from one request into the next request if you are within
a unicorn context. There are cases where we want this, and there are
cases where we don't want this leakage.

Take for example activesupport's usage of Time.zone. Under water this
is set in a thread local var. If you set Time.zone in one request, but
not in the next request, using unicorn the next request will use the
time zone of the previous request. Using rack or mongrel (in
multithreaded mode) you don't have this issue perse (though they have
other issues then).

Same for the i18n gem and it's usage of the I18n.locale value, which
is also set in a thread local var.

So yeah, unfortunately I have to take into account this "crappy idiom"
and need to know exactly which thread local vars are set by all the
components we use, and determine which of those must be reset before
each request.


Cheers,
Jimmy


On Wed, Jan 12, 2011 at 10:12 AM, Jordan Ritter <jpr5@darkridge.com> wrote:
> Unicorn is purely about employing a multi-process model, not a multi-thread model; it specifically avoids spawning threads to handle inbound requests.   In fact, I'll bet that inside each request, Thread.current == Thread.main.
>
> Separate from Unicorn, when running a rack-compatilbe app in multithreaded mode (the default when the app is invoked directly via rackup + config.ru), there's no guarantee about which thread will service a given request.  This fact may not matter to you, depending on what you're trying to do.
>
> That said, you *could* use Thread local storage for per-request storage in either unicorn or multithreaded situations, so long as you wiped your storage at the beginning/end of each request -- but that's a crappy idiom, even if it might be "common" (don't know what you're referring to offhand).  Can't suggest a more appropriate pattern without knowing more about what you're actually trying to do.
>
> cheers,
> --jordan
>
> On Jan 11, 2011, at 2:52 PM, Jimmy Soho wrote:
>
>> Hi,
>>
>> Some more questions still:
>>
>> It seems a worker uses the exact same thread to handle each request.
>>
>> Is that guaranteed to happen for the lifetime of a worker? Or are
>> there cases where a unicorn worker might spin a new thread to handle
>> the next requests?
>>
>> If the same thread is always used, isn't that a potential issue when
>> programmers use thread local variables, which are not reset at the
>> next request?  (I know, the usage of thread local variables is not
>> recommended, but take a random rails project, go into their $GEM_HOME
>> and do grep -r Thread.current . , see what I mean..)
>>
>
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying
>
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-12  3:07         ` Thread.current Jimmy Soho
@ 2011-01-13  4:26           ` Eric Wong
  2011-01-13 16:46             ` Thread.current Jordan Ritter
  0 siblings, 1 reply; 10+ messages in thread
From: Eric Wong @ 2011-01-13  4:26 UTC (permalink / raw)
  To: unicorn list

Jimmy Soho <jimmy.soho@gmail.com> wrote:
> Take for example activesupport's usage of Time.zone. Under water this
> is set in a thread local var. If you set Time.zone in one request, but
> not in the next request, using unicorn the next request will use the
> time zone of the previous request. Using rack or mongrel (in
> multithreaded mode) you don't have this issue perse (though they have
> other issues then).
> 
> Same for the i18n gem and it's usage of the I18n.locale value, which
> is also set in a thread local var.
> 
> So yeah, unfortunately I have to take into account this "crappy idiom"
> and need to know exactly which thread local vars are set by all the
> components we use, and determine which of those must be reset before
> each request.

You can probably just write a trivial middleware to clear all
keys in Thread.current before every request.  Or play around with
Rainbows! with a single-threaded ThreadSpawn:

cat >> unicorn.conf.rb <<EOF
Rainbows! do
  use :ThreadSpawn
  worker_connections 1
  keepalive_timeout 0
end
EOF

And then just run "rainbows" instead of "unicorn".

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Thread.current
  2011-01-13  4:26           ` Thread.current Eric Wong
@ 2011-01-13 16:46             ` Jordan Ritter
  0 siblings, 0 replies; 10+ messages in thread
From: Jordan Ritter @ 2011-01-13 16:46 UTC (permalink / raw)
  To: unicorn list

For the record, the "clear the Thread.current storage before/after each request" is what I think is a crappy idiom.  YMMV I guess.

cheers,
--jordan

On Jan 12, 2011, at 8:26 PM, Eric Wong wrote:

> Jimmy Soho <jimmy.soho@gmail.com> wrote:
>> Take for example activesupport's usage of Time.zone. Under water this
>> is set in a thread local var. If you set Time.zone in one request, but
>> not in the next request, using unicorn the next request will use the
>> time zone of the previous request. Using rack or mongrel (in
>> multithreaded mode) you don't have this issue perse (though they have
>> other issues then).
>> 
>> Same for the i18n gem and it's usage of the I18n.locale value, which
>> is also set in a thread local var.
>> 
>> So yeah, unfortunately I have to take into account this "crappy idiom"
>> and need to know exactly which thread local vars are set by all the
>> components we use, and determine which of those must be reset before
>> each request.
> 
> You can probably just write a trivial middleware to clear all
> keys in Thread.current before every request.  Or play around with
> Rainbows! with a single-threaded ThreadSpawn:
> 
> cat >> unicorn.conf.rb <<EOF
> Rainbows! do
>  use :ThreadSpawn
>  worker_connections 1
>  keepalive_timeout 0
> end
> EOF
> 
> And then just run "rainbows" instead of "unicorn".
> 
> -- 
> Eric Wong
> _______________________________________________
> Unicorn mailing list - mongrel-unicorn@rubyforge.org
> http://rubyforge.org/mailman/listinfo/mongrel-unicorn
> Do not quote signatures (like this one) or top post when replying

_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-01-13 16:49 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-01-08  2:00 Thread.current Jimmy Soho
2011-01-08  2:57 ` Thread.current Eric Wong
2011-01-08  3:09 ` Thread.current Curtis j Schofield
2011-01-08  5:54   ` Thread.current Jimmy Soho
2011-01-11 22:52     ` Thread.current Jimmy Soho
2011-01-11 23:12       ` Thread.current Eric Wong
2011-01-11 23:12       ` Thread.current Jordan Ritter
2011-01-12  3:07         ` Thread.current Jimmy Soho
2011-01-13  4:26           ` Thread.current Eric Wong
2011-01-13 16:46             ` Thread.current Jordan Ritter

Code repositories for project(s) associated with this public inbox

	https://yhbt.net/unicorn.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).