In the game server we access the database. This IO blocking makes it
more optimal to use threads instead of one thread.
Of course threads have a cpu performance penalty.
We also are cpu bound in certain sections of the process. This makes
it more performant to place the cpu bound sections in critical
sections to avoid context switching.
It seems like using a lightweight concurrency approach would be more efficient.
Im thinking that we could have the main Eventmachine thread and a game
processing thread. The game processing thread would have a Queue
object that we could push commands to.
So in our IO bound sections (querying the database), we would use a
nonblocking socket that enqueues the rest of the operations once the
database server responds to the query.
This would mean we would need to restructure our program to use more
closure style commands or use contituations.
For example:
http://pastie.org/211184
Does this seem worth it, or are there easier approaches?
Thanks,
Brian
_______________________________________________
Eventmachine-talk mailing list
Eventmac...@rubyforge.org
http://rubyforge.org/mailman/listinfo/eventmachine-talk
Yes that would be nice to have evented versions for the DBMS libraries.
On Sun, Jun 8, 2008 at 5:27 PM, Brian Takita <brian....@gmail.com> wrote:On Sun, Jun 8, 2008 at 2:14 PM, Francis Cianfrocca
<garbag...@gmail.com> wrote:
> There are so many moving parts to this problem that it's hard to give a
> useful answer in the abstract. If you have compute-bound code that is so
> sensitive that you can actually see a difference by pinning it to a CPU,
> that's an effect that's orders of magnitude finer than what you might gain
> by making the database accesses more concurrent.
> If you're looking to improve raw per-request speed, then you need to ask
> whether there are capturable latencies in the architecture, where you can
> better use the time you're waiting for something else to happen. If you're
> looking to improve scalability, then reducing threading can be critically
> important.
Yes, the Q will be large with lots of concurrent client requests, sothere is more work that can be done while being I/O blocked.
Threading (especially Ruby's) is less cpu efficient than a more
lightweight approach.
>Ideally we would still have the same I/O concurrency because we would
> If you haven't done so already, you might start by doing a careful profile
> of your application. The approach of using a Queue to stack up the DBMS
> accesses is actually very interesting, but my intuition is that, unless you
> run the DBMS engine on the same server as the application, your losses from
> the lower I/O concurrency will swamp your gains from the reduced thread
> switching.
use something like IO.select and move on the the next item in the Q.
When there is a response, then the next command would enqueued, which
could possibly be another I/O blocking operation.
Assuming I understood you correctly, you're thinking about single-threading your blocking DB calls by stacking them up on a queue. That does avoid the high performance impact of spinning a thread for each request. But you may find that the database server is underutilized, which means you're still not getting optimal performance, so you have to find a balance.
If there were an evented mysql library (which someone really needs to write), the problem would be easy
It shouldn't matter that there's a response loop --- most
client-server protocols work this way. As long as the "wait" state
isn't blocking, other state machines can progress, and everything is
happy.
I spent some time reading ruby-mysql after Francis posted yesterday.
This doesn't appear to be a hard project. There, see? I've committed
myself to it. =)
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
>> The inherent problem with certainly SQL as a DBMS paradigm is that
>> it's a
>> request-response busy waiting loop, by logic. Even with an evented
>> driver,
>> you're really considering using futures and deferrables (or
>> threads) to get
>
> It shouldn't matter that there's a response loop --- most
> client-server protocols work this way. As long as the "wait" state
> isn't blocking, other state machines can progress, and everything is
> happy.
No, indeed it shouldn't, but you have to step away from traditional
branch logic styles in order to deal with that:
def DB.query(str)
DB.send_raw(str)
until buffer.has_response?
rebuffer
end
buffer.pop
end
Now even in an initial evented implementation, all this really changes
is that fetch doesn't really do anything (remove the loop, assuming it
happens automatically on receive_data), potentially return maybe a
future (from buffer.pop), or nil, or just block, in which case the
block happens only marginally later in the stack. You can head for a
callback approach, but that's just what I'm talking about... The api
has to change significantly for that, as you can't "pause" code in ruby.
No matter how deep you get into the application code you end up in a
situation where the application logic itself is your blocker. Under
rails for example, the request response loop for HTTP will hard block
on application logic that's waiting for a database response. This is
just an api problem, but that's also An API Problem, if you catch my
drift. Especially worse when it's a big project like rails, that would
maybe like to support both styles of processing.
The way around this is a deferrable or other lightweight state machine
construct in order to use a callback or a continuation style
enclosure, with which you can still 'return' on the originating
request/method/action whatever (returning on the active receive_data /
timer callback / etc). In order to support this however, there are
greater demands on a change of API, and I am trying to come up with
something that won't kill developer productivity by code overhead.
The same thing is apparent in a Rack & Thin setup, where one can't
effectively service long lived XHR at this time, due to the fact that
the API demands a real http response array at the 'end' of every
request (#call). In a non-threaded server this means waiting for
whatever may block the application logic, not just IO blocking.
The short of what I'm trying to get at is, this isn't going to be a
drop-in solution, sadly, and moreover, I really don't think it even
can be.
> I spent some time reading ruby-mysql after Francis posted yesterday.
> This doesn't appear to be a hard project. There, see? I've committed
> myself to it. =)
Going postgres-pr here, all input more than welcome ;)
Yes, that is the nature of evented programs.
The problem EM has now is that there's no elegant scalable persistence
layer. From what I can see, the best you can do now is write a
synchronous web app as a sidecar, and hand your database operations
off to it; at least then your EM engine can queue requests in memory
and drain them out over HTTP.
All an evented DB driver does is get rid of that middleman, allowing
you to queue requests directly to the database.
It sounds like you're exploring some kind of
continuation-passing-style database interface. From what I can tell,
attempts to design scalable continuation-driven network apps are like
land wars in Asia. I'd be happy just to be able to have a screaming
fast application that didn't serialize on its SQL connection.
Perhaps some day I might try to work on the following:
ruby 1.9
rails multi-thread safe :) -- one DB connection per thread
hack the mysql adapter so that it sets rb_thread_non_blocking_region
around any sql calls # allow for non blocking threads. Dangerous, but
hey, this is theoretical land, right?
I'd still have to have a thread per process, though. But if it worked
it would be nice, and potentially use less RAM.
But that's still multi-threaded.
I guess for non multi-threaded you'd have to adopt a staged
architecture [pretty popular for Event driven stuff--I noticed it
looking at some SEDA code the other day].
I guess it works with stages, something like
stage 1
do some code
setup a db call which will return its result to stage 2 for you
end
def stage 2
same
end
def stage 3
render a page
end
AND you'd have to rewrite the mysql binary to hook in to EM's threaded
loop for its socket handling.
I guess the revactor package does something along these lines. Maybe
that would work more elegantly.
Take care.
-R
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
>> The short of what I'm trying to get at is, this isn't going to be a
>> drop-in solution, sadly, and moreover, I really don't think it even
>> can be.
>
> Perhaps some day I might try to work on the following:
> ruby 1.9
> rails multi-thread safe :) -- one DB connection per thread
> hack the mysql adapter so that it sets rb_thread_non_blocking_region
> around any sql calls # allow for non blocking threads. Dangerous,
> but hey, this is theoretical land, right?
> I'd still have to have a thread per process, though. But if it
> worked it would be nice, and potentially use less RAM.
> But that's still multi-threaded.
In this regard, I'm getting quite excited to see if a nice stop gap
can be made with the JRuby reactor, given that it supports real
threads, and has mature db interfaces available.
I needed to use Selenium to upon a browser which ran the spec suite,
and have the server wait for a response from the client that said what
the response was.
I had to monkey patch Thin's process method to not close the
connection unless the Content-Length header == the body's length.
You can also send a closing empty response by setting the Content-Length to 0.
I wouldn't say its exactly easy, because its an "unusual" concern for
your average web app, but for Comet style servers, it makes sense.
Nice. Now if we could just make rails thread safe... :) [actually I
did see a google summer of code project on it. Maybe once that's out
[if it helps] then this would work]. But perhaps a 'quick' web server
would be merb running in Jruby or something, with a lot of threads.
Dunno.
-R
I've got a bottom-edge driver for the MySQL client-server protocol,
barely tested, supporting only the COMMAND_QUERY commands (meaning:
almost every command you'd execute from the mysql command line). It
supports only 5.x MySQLd. It solves no real-world problem at this
point, and is sure to be an embarassment to my children's children's
children, but is a starting point.
It clocks in at 600 lines of Ruby --- less, if you get rid of my hexdump code.
EventMachine::run {
c = Asymy::Connection.new(:target => "localhost",
:port => 13306,
:username => "user",
:password => "pass",
:database => "mysql")
c.exec("show databases") do |fields, rows|
pp fields
pp rows
end
}
Since I have no idea how this code would even fit in with the
eventmachine tree, here's a tarball:
http://www.matasano.com/asymy.tgz
Again, I'm literally posting the very first thing I managed to make do
anything, because I thought it might be interesting. I make no claims
that any of this actually works.
http://www.okito.net/post/37659446/pipelined-easy-event-driven-programming-for-ruby
-Charles
On Jun 9, 2008, at 3:45 PM, Brian Takita wrote:
>> You can head for a callback approach, but
>> that's just what I'm talking about... The api has to change
>> significantly
>> for that, as you can't "pause" code in ruby.
_______________________________________________
>> In this regard, I'm getting quite excited to see if a nice stop gap
>> can be made with the JRuby reactor, given that it supports real
>> threads, and has mature db interfaces available.
>
> Nice. Now if we could just make rails thread safe... :) [actually I
> did see a google summer of code project on it. Maybe once that's
> out [if it helps] then this would work]. But perhaps a 'quick' web
> server would be merb running in Jruby or something, with a lot of
> threads. Dunno.
If you keep the separation on the other side of the db interface
boundary, it won't need to be. Run your entire app in one thread, and
only share a single thread safe request queue, you can avoid all of
the rails issues, and the db thread will also remain quiet when it's
not talking to the db. Of course, again, we're coming into the app
logic problem with that, and you somewhat loose the advantage of
threads if you continue to use AR and block on db responses.
It's worth noting also that AR does have some concurrency
capabilities: ActiveRecord::Base.allow_concurrency = true
Of course, the observations that have been made about this is that
thread pooling under MRI makes no gains, with AR, Sequel and
DataMapper. I haven't run those up under JRuby to see if the story is
any different there. For more information, see Kevin Williams article
on the topic.
well done :)
> Sorry, that took me longer to expected. Have you ever read MySQLd? Ew.
Right...
> I've got a bottom-edge driver for the MySQL client-server protocol,
> barely tested, supporting only the COMMAND_QUERY commands (meaning:
> almost every command you'd execute from the mysql command line). It
> supports only 5.x MySQLd. It solves no real-world problem at this
> point, and is sure to be an embarassment to my children's children's
> children, but is a starting point.
>
> It clocks in at 600 lines of Ruby --- less, if you get rid of my
> hexdump code.
>
> EventMachine::run {
> c = Asymy::Connection.new(:target => "localhost",
> :port => 13306,
> :username => "user",
> :password => "pass",
> :database => "mysql")
> c.exec("show databases") do |fields, rows|
> pp fields
> pp rows
> end
> }
Very cool, well done :)
> Since I have no idea how this code would even fit in with the
> eventmachine tree, here's a tarball:
>
> http://www.matasano.com/asymy.tgz
Under Protocols, I've been thinking that we're going to need to start
modularising that tree however, or at least not pre-loading
everything, otherwise we'll end up like facets < 2.0.
> Again, I'm literally posting the very first thing I managed to make do
> anything, because I thought it might be interesting. I make no claims
> that any of this actually works.
:)
It has memory leaks because continuations in ruby (MRI ruby, at least)
leak memory. And they are really slow, too. Cool idea, though. :)
Kirk Haines
If the memory leaks can be eliminated I think this approach could be
useful anyway because the perf gain from writing internally Evented
libraries that still use a non-event driven api far outweighs the
added cost of continuations. That was the point of my post -
continuation based Evented code outran the nonevented version by 20x
-Charles
Nice! An evented mysql. I wonder how you could combine this with
existing apps to get a single threaded server :)
-R
I just posted a rough patch of code that helps to "unwind" event driven programming using continuations in Ruby. It currently has some memory leaks which I need to figure out but the code is quite simple and it would make it possible for you to put a layer on top of an event-driven/callback-style DB API to make it "normal" again.
http://www.okito.net/post/37659446/pipelined-easy-event-driven-programming-for-ruby
Tony, do you have a code sample on how this would work using Revactor?
medioh.com _______________________________________________
> You might check out Revactor... this is exactly what it does, except using
> Ruby 1.9 fibers (and thus free of the memory leaks): http://revactor.org
>
> That said I posted something similar to this mailing list a few years ago
> (using continuations) and as far as I could tell the memory leaks were
> intractable.
as in very hard to track down?
is there any benefit to using revactor versus just a normal threaded approach?
> That said I posted something similar to this mailing list a few years agoas in very hard to track down?
> (using continuations) and as far as I could tell the memory leaks were
> intractable.
J
-----Original Message-----
From: "Tony Arcieri" <to...@medioh.com>
Date: Tue, 10 Jun 2008 16:28:12
To:eventmac...@rubyforge.org
Subject: Re: [Eventmachine-talk] Evented database access
--
Tony Arcieri
medioh.com <http://medioh.com> _______________________________________________
Yeah you'd think that JRuby + a thread safe server [merb/ramaze] + a
thread safe DB [sequel] + multiple threads would be good enough.
Maybe it is :)
Another thought would be [as discussed previously with Tony], using
fibers instead of threads and using an evented DB connector with a DB
connection pool. Then when it does DB requests, it pauses the fiber
until the request returns [without blocking the other fibers].
Tony's done some of the base work for this--revactor comes with a
fibered version of mongrel.
I'm wondering if sequel could be patched to use an evented DB driver.
If so then this + the fibered mongrel might yield similar benefits to
the jruby solution mentioned above. You'd be single threaded without
blocking. Of course, that means you'd be single threaded [so
single-cored, basically]--but hopefully that one thread could do a lot
of background processing while waiting for DB connections to return.
Do you think has potential to yield a nice, single threaded web
server?
Having worked with EM a little in the past, it seems that if you can
move to single threaded, it's a lot faster. Like a lot. And seems to
use less RAM.
I guess a drawback would be that if you have something that's CPU
bound, like...a page that takes FOREVER to render [has that ever
happened to anyone?] then it would block the others, but not for too
long. Evented mongrel I suppose would suffer from the same thing.
Even the threaded solutions would, if you got too many of long
requests piled up against them.
NB that Rails isn't thread safe so this wouldn't help that until they
get their act together. Until there isn't a huge mutex lock over the
entire thing, no continuation or fiber will help them, I'd doubt. But
other frameworks, quite possibly. :)
Take care!
-R
Evented MySQL was really easy; clearly, a single-threaded end-to-end
web stack is doable.
I'm not even sure continuations are that much of a win. The web stack
can abstract away the HTTP callbacks, so that's going to look just
like Rails controllers. Saved block arguments give you blocks instead
of callbacks. If you render Javascript and mostly field JSON or XML
from the database to build the page, which is what plenty of Rails
apps like like today, there's not much pain to using straight EM.
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
Then again, maybe your version is faster, as it is :) Quite possible.
I have heard, however, that the pure ruby mysql is way slower than
the normal.
Speaking of which, I wonder if one could just use revactor on it
straight, as it is now :)
Take care, and thanks for your work on this.
-R
On Tue, Jun 10, 2008 at 12:18 AM, Thomas Ptacek <tq...@matasano.com> wrote:
> Sorry, that took me longer to expected. Have you ever read MySQLd? Ew.
>
> I've got a bottom-edge driver for the MySQL client-server protocol,
> barely tested, supporting only the COMMAND_QUERY commands (meaning:
> almost every command you'd execute from the mysql command line). It
> supports only 5.x MySQLd. It solves no real-world problem at this
> point, and is sure to be an embarassment to my children's children's
> children, but is a starting point.
Speaking of which, I wonder if one could just use revactor on it
straight, as it is now :)
You wouldn't want to do this in a low level library like a db adaptor
but rather for high level code. This way you can have a few event-
driven nuts write you low-level code that usually blocks - such as db
adaptors and rest-client code - and then "mere mortals" can write
their application logic like normal.
You don't gain the full benefits of using 100% evdriven code but you
get 80% of the win and the code can be usedby everyone.
Incidentially you do not need to use multiple threads at all of your
db lib is evented. Much faster.
-Charles
I don't think extending ruby-mysql is the way to go.
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
I see. So you're saying that using fibers or continuations is a 'half
way point' between being pure event driven and using pure threads.
Wait come to think of it, pure threads aren't even a concurrency
option in 1.9, since it only runs one thread at a time. That makes
fibers even more tempting. Apparently they're only like a 10K
overhead per fiber, which isn't too much.
-R
> You wouldn't want to do this in a low level library like a db adaptor but
> rather for high level code. This way you can have a few event-driven nuts
> write you low-level code that usually blocks - such as db adaptors and
> rest-client code - and then "mere mortals" can write their application logic
> like normal.
>
> You don't gain the full benefits of using 100% evdriven code but you get 80%
> of the win and the code can be usedby everyone.
>
> Incidentially you do not need to use multiple threads at all of your db lib
> is evented. Much faster.
So it seems that the test we 'wish' to optimize would be:
trying to do 10 'long running' and some number 'very short' mysql
queries immediately after them [while they're still running].
the very short mysql queries should return very quickly.
Which is not the case with today's Ruby :)
So one way to do a proof of concept with this would be using fibers
and revactor and ruby-mysql. Maybe :) Or using the evented mysql
posted here + fibers + EM.
Should it prove successfull, then it is possible that
fibered mongrel + something that ends up using the evented mysql would
perform better than it currently does.
One can hope :)
-R
0.upto(10) do |i|
conns[i].exec("select * from huge_table") {|cols, rows| pp [i, rows.size]}
end
0.upto(10) do |i|
conns[10+i].exec("select COUNT(*) from tiny_table") {|cols, rows|
pp [i, rows.size]}
end
I'm not sure I'm seeing why you need fibers for this. The short
queries should return first. The queries should all run concurrently.
The snozzberries should, in fact, taste like snozzberries.
There's no situation in which fibers are going to make the "fastest"
queries on a single connection return first, because the MySQL
protocol itself is synchronous.
On 6/11/08, Roger Pack <roger...@leadmediapartners.com> wrote:
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
Wow. After having tried this out it TOTALLY ROCKS!
Especially compared to 1.8.6 and its threading model, which basically
reverts to 100% blocking IO.
I took the liberty of posting an EM version based on the previous email
http://code.google.com/p/ruby-roger-useful-functions/source/browse/trunk/asymy/big_em.rb
[also has some added code for a connection pool--kind of a hack, but works]
and its competitor--a 'normal' threaded version:
http://code.google.com/p/ruby-roger-useful-functions/source/browse/trunk/asymy/big_thread.rb
And, as you said, it works and tastes like shnozzleberries.
Some observations:
you can run 1000's of shorter queries while the longer ones are running.
MySql itself uses 160% CPU when run against EM and 130% when run
against threaded. This means that using the EM version we are able to
more easily take advantage of DB CPU cycles. When it was running at
160% that was 40% idle, meaning that overall the EM one was able to
complete faster than its 70% idle threaded neighbor. Well, it's my
hypothesis that 'more CPU for MySql means faster overall queries'
It's impossible to actually compare the running times of it, because
the Ruby thread version takes SO MUCH LONGER since it's single
threaded.
Also, if you do a lot of short SQL queries before starting some longer
ones, with threads, the short ones tend to get done rather quickly.
Not sure why that is. I suspect that ruby doesn't swap out threads
very quickly, so the first created threads tend to run to completion
their first time [which is before the longer queries' threads run even
once]. But it does work quickly in that instance, with threads.
Quicker, anyway.
~/dev/ruby-roger-useful-functions/asymy time ruby big_em.rb
real 0m4.731s
user 0m0.121s
sys 0m0.037s
~/dev/ruby-roger-useful-functions/asymy time ruby big_thread.rb
real 0m14.033s
user 0m0.089s
sys 0m0.039s
About fibers--you were correct--they aren't absolutely necessary.
This code is all 1.8.6 code, as an example.
For practical purposes, they'll be nice to use because [as noted by
authors previously] you can use them to generate 'non threaded, but
interleaved code' [i.e. normal looking code] instead of using staged
and/or partitioned code, as EM typically requires you to use. Take a
look at revactor for an example. It basically sleeps threads until
their blocking IO calls stops blocking. They can then continue from
there. So it just uses sleep instead of continuations, but that's
another story.
As a note the asym library works really well in 1.8 but doesn't work
[as yet] in 1.9, despite a feeble attempt to get it to work.
It also doesn't work with [AFAIK] blank passwords, but it does work
with non-blank passwords.
I did also note that the ruby mysql adapter hasn't been updated since
2005, so might be a little outdated :)
Thanks for your work.
At Aman Gupta's suggestion, I copied this up to GitHub:
http://www.github.com/tqbf/asymy
I'm an svn person, but you git people are apparently meant to know
what to do with it there.
My totally unscientific guess is that the nature of the MySQL protocol
and server architecture is going to dominate performance. You could
play games with query scheduling on a single connection, for instance
by having Connection#exec store queries on a heap instead of a queue.
I'm debating taking another few hours to do the prepared statement
binary protocol.
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
It supports blank passwords now. Thanks for noticing.
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
How do we contribute back to this?
>
>
> I'm an svn person, but you git people are apparently meant to know
> what to do with it there.
>
> My totally unscientific guess is that the nature of the MySQL protocol
> and server architecture is going to dominate performance. You could
> play games with query scheduling on a single connection, for instance
> by having Connection#exec store queries on a heap instead of a queue.
Yeah some type of 'more fair scheduling than a queue' might be nice.
Either that or maybe a really large pool :)
>> My totally unscientific guess is that the nature of the MySQL protocol
>> and server architecture is going to dominate performance. You could
>> play games with query scheduling on a single connection, for instance
>> by having Connection#exec store queries on a heap instead of a queue.
>
> Yeah some type of 'more fair scheduling than a queue' might be nice. Either
> that or maybe a really large pool :)
This is damn interesting to me. One of the reasons I have never added
the ability for Analogger to log directly to a database is because the
synchronous nature of that would kill performance. An asynchronous,
event based sytem, though, would be perfect. (*goes to hack on the
code*)
Kirk Haines
One thing I want to hack together once I get the chance is something
up your alley--
hack evented mongrel to spawn fibers instead of Threads.
Then when you do async DB calls you just do something like.
var = nil
main_fiber = Fiber.current
conn.exec("select * from table") { |i,c| var = i,c
main_fiber.resume
}
Fiber.yield # yields it back to the original caller
return var
Well I haven't quite figured it out yet. anyway with such as this
then all of your DB calls are simple calls to yield.
Oh wait, for your logger you probably wanted to do it asynchronously.
Yeah you wouldn't need this then :)
-R
> One thing I want to hack together once I get the chance is something up your
> alley--
> hack evented mongrel to spawn fibers instead of Threads.
> Then when you do async DB calls you just do something like.
evented_mongrel doesn't use threads. But yeah, the idea is a sound one.
Kirk Haines
Well, I have been following this discussion somewhat closely, but I
wonder, how is it possible with whatever sockets you use, especially
with mysql. With mysql, especially threre is no way to make async
query because protocol doesn't support multiplexing. There is no way
to identify, who made the request and for whom this response has come.
twisted simulates this behaviour with thread_pool, but that sort of
defeats the purpose, doesn't it?
On the other hand, post gres, fully supports async queries and hence
prolly we should have a pgsql driver first.
Just because you can't drive more than one query on the connection at
a time doesn't mean you can't make the API asynchronous. All you need
to accomplish is not blocking the whole program while a query runs.
> On the other hand, post gres, fully supports async queries and hence
> prolly we should have a pgsql driver first.
Have at it.
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
An example of something doint this [a mongrel handler that makes some
async DB calls and returns 'hello'] is
Basically the only added things were
http://code.google.com/p/ruby-roger-useful-functions/source/browse/trunk/asymy/fibers/mongrel_test/fibered_mongrel.rb
# builds off evented mongrel and just wraps its requests in Fibers.
and an added function in
http://code.google.com/p/ruby-roger-useful-functions/source/browse/trunk/asymy/source/asymy/connection.rb
# added 'exec_and_fiber_yield' [and a small 1.9 patch and 'em_connection']
Also note that in ...handlers.rb of mongrel you have to replace a few
:'s with 'then's [it yields a syntax error when you use it--replace
them there] for 1.9
And, snozzleberries it is. I'm able to pound it with lots of requests
and those that do "very little" IO wait time finish [as hoped for]
very quickly [like 20ms], and those with "lots" of IO finish...after
their IO finishes, Much later.
I am able to hammer it with no ill results. Wow.
The good news for this is that it basically means that with small
tweaks to mongrel and to the DB layer, any [multi-thread safe] 1.9 app
can use this as a drop in replacement and immediately reap the
benefits. I'd imagine this yields some good results, though I haven't
benchmarked it versus threaded or "evented only" yet, and, of course,
my benchmarks are all synthetic.
This has some good potential. Especially with the use of epoll for EM.
Thanks for doing that stuff!
-R
On Wed, Jun 11, 2008 at 11:32 PM, Thomas Ptacek <tq...@matasano.com> wrote:
> Whoah, you benchmarked it! I'll be honest and say I've been afraid
> even to touch it. Thank you!
>
> At Aman Gupta's suggestion, I copied this up to GitHub:
>
> http://www.github.com/tqbf/asymy
>
As for how to maintain this, I thought this was the problem that
GitHub and DVCS magically solved. Isn't it supposed to read our minds
and stuff and automatically merge everything together? Maybe someone
who has used github for awhile can tell me how this particular piece
of pixie dust is supposed to work. =)
--
---
Thomas H. Ptacek // matasano security
read us on the web: http://www.matasano.com/log
Two descriptions I found useful:
http://b.lesseverything.com/2008/3/25/got-git-howto-git-and-github
http://b.lesseverything.com/2008/4/4/git-howto-start-a-new-project-based-on-an-other-project
HTH
In further new, got mongrel with a pooled db to work:
non-pooled:
~ time ab -n 50 -c 50 http://127.0.0.1:3000/
real 0m0.116s
pooled:
real 0m0.076s
And also it seems that if your time is dominated by very long running
mySQL queries that it doesn't matter very much if you use pooled or
not, though I could be wrong [it showed like a .5s difference, like
10.3 to 9.8, but it wasn't very strong].
Not sure what/if an ideal setup would be.
Rock on. Here's the pooled fiber :)
So I'd say it works just fine. Only thing left is to get Sequel or AR
to use it as a back end and, coupled with EM you've got a swell,
fibered server [well, if you use ramaze or merb which are thread
safe].
Also as a warning, if you run it with many open connection at the same
time and use ulimit -n [to increase number of file descriptors] and
don't use kqueue/epoll, then some of your connections just hang and
never come back.
Also, for some reason with kqueue I was getting these:
~/19/asymy/fibers/mongrel_test ruby19 test_mongrel_with_db.rb
Assertion failed: (nbytes > 0), function _WriteOutboundData, file
ed.cpp, line 607.
Abort trap
If anybody has seen them before. It appears to have subsequently
stopped doing it, but it's still odd.
Thanks!
-R
Yeah EM itself has a mechanism for conveniently increasing the file
descriptors available:
EventMachine.set_descriptor_table_size number
and then set_effective_user username
so that you can more conveniently run the program as sudo, set the
descriptors, then change it so you're no longer sudo.[1]
I was noting that if you ever do that, and you don't use epoll or
kqueue, you quite possibly run a danger, as select only handles up to
64 connection [windows] and only file descriptors up to the number
1024 [linux], so need to be careful. Fixing that bug would be nice,
though, too, though unfortunately I can't reliably recreate it. It
does occur, however, with the latest gem release version.
Thanks!
-R
1: http://wilkboardonline.com/roger/rdoc2/classes/EventMachine.html#M000058
One possible explanation for this is that, using kqueue, all the
sockets are set thus on creation:
em.cpp: EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD |
EV_ONESHOT, 0, 0, ed);
which [I think] means that they will be selected as writable [either
immediately, or when the socket connects--I'd imagine the latter].
This is unanticipated by EM [which thinks it means we had marked it to
write something], so it raises the error since it didn't write
anything.
Now my latest weirdness is trying to figure out why, when I add 1000
ports or so 'in one run', it corrupts the Descriptors table:
(gdb) p Descriptors
Cannot access memory at address 0x3
$8 = {
<std::_Vector_base<EventableDescriptor*,std::allocator<EventableDescriptor*>
>> = {
_M_impl = {
<std::allocator<EventableDescriptor*>> = {
<__gnu_cxx::new_allocator<EventableDescriptor*>> = {<No data
fields>}, <No data fields>},
members of
std::_Vector_base<EventableDescriptor*,std::allocator<EventableDescriptor*>
>::_Vector_impl:
_M_start = 0x58001fc0,
_M_finish = 0x1feb,
_M_end_of_storage = 0x2000
}
}, <No data fields>}
you'll notice that _M_finish looks very small--it should typically be
greater than _M_start
Any ideas?
And, of course, yet another wish--that if you called kqueue or epoll
within an EM::run it would raise, since it might mess up existing
descriptors, and it also won't have been set for the current run block
you're in, so it confuses you as to whether it's in use or not.
Thanks!
-R
>> Also, for some reason with kqueue I was getting these:
>> ~/19/asymy/fibers/mongrel_test ruby19 test_mongrel_with_db.rb
>> Assertion failed: (nbytes > 0), function _WriteOutboundData, file
>> ed.cpp, line 607.
>> Abort trap
>
> One possible explanation for this is that, using kqueue, all the
> sockets are set thus on creation:
> em.cpp: EV_SET (&k, ed->GetSocket(), EVFILT_WRITE, EV_ADD |
> EV_ONESHOT, 0, 0, ed);
Mmm, this is nasty, and I was lead to believe kqueue turns itself on
under EM. Anywhoo, after talking to Kirk last night, we decided it
would be a good idea to add a compile time configuration for the
results of three new functions:
EM::kqueue?
EM::epoll?
EM::ssl?
Which respectively tell you if this part of the extension was
successfully compiled. This should aid in clear intent for apps that
want to be in a generic configuration, and should also solve problems
on some systems where calling epoll or kqueue can cause other problems.
> And, of course, yet another wish--that if you called kqueue or epoll
> within an EM::run it would raise, since it might mess up existing
> descriptors, and it also won't have been set for the current run block
> you're in, so it confuses you as to whether it's in use or not.
Some kind of EM::mode tuple to report this would be useful?
Or having EM::kqueue return a useful value would do about the same,
though not as flexible [since...once you turn it on, you can never
turn it off--though I can't imagine anybody ever wanting to turn it
off, anyway]. I think Kirk suggested that like a year ago or so :)
>> And, of course, yet another wish--that if you called kqueue or epoll
>> within an EM::run it would raise, since it might mess up existing
>> descriptors, and it also won't have been set for the current run block
>> you're in, so it confuses you as to whether it's in use or not.
>
> Some kind of EM::mode tuple to report this would be useful?
The functions mentioned above would probably be good enough to be able
to know which mode you're running in [i.e. don't set it to kqueue
unless kqueue is available, save that fact away somewhere].
I was just pointing out that if you do
EM::run {
EM::kqueue
# do some stuff
}
That kqueue doesn't actually get run within that block. So it confuses
developers. It has [now that I think about it] the potential of some
nasty side effects, since I think kqueue is indeed run, but it
"misses" the original kqueue initialization stuff, so it might cause
bugs if you allow this to happen. So to avoid confusion it might be
nice to raise on these functions if they're within a running block. I
could add a patch if you'd like.
With regard to the weird nasty error messages, I believe those occur
when using kqueue.
With select [on mac], I get something a little different.
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x0000005f
0x001c678f in EventMachine_t::_RunSelectOnce (this=0xffffffff) at em.cpp:741
741 SelectData.tv = Quantum;
(gdb) p selectData
Cannot access memory at address 0x3
(gdb) p Quantum
Cannot access memory at address 0x3
Odd that things that should be on the stack [SelectData] or in global
space [Quantum] are declared suddenly as 0x3. I wonder if this is
some type of memory corruption. Hopefully sometime soon I can run
this on linux and see if the problem also exists there [and maybe
valgrind will help].
It appears that the mysql gem causes an interpreter lock.
http://blog.hungrymachine.com/2008/8/6/ruby-and-multi-threaded-mysql-mri-vs-jruby-jdbc-vs-dataobjects-mysql
Yeah the mysql gem does. You'd have to use an asynchronous mysql
adapter to overcome that [asymy -- or jruby], as discussed a little
above.
Good luck!
Fascinatingly, the pure ruby mysql driver is actually ruby thread
friendly [albeit very slow]. Just learned that--thought I'd mention
it. Guess you could drive it evented-ly that being the case. LOL.
-=R