We have a custom EventMachine server that is used to host a multiplayer game. 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
Out of curiosity, what database are you using? For a long, long time I''ve wanted to see evented versions of the standard DBMS libraries. On Sun, Jun 8, 2008 at 4:21 PM, Brian Takita <brian.takita at gmail.com> wrote:> We have a custom EventMachine server that is used to host a multiplayer > game. > > 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 > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080608/146a0d5b/attachment.html>
On Sun, Jun 8, 2008 at 1:23 PM, Francis Cianfrocca <garbagecat10 at gmail.com> wrote:> Out of curiosity, what database are you using? For a long, long time I''ve > wanted to see evented versions of the standard DBMS libraries.Mysql. Yes that would be nice to have evented versions for the DBMS libraries.> > On Sun, Jun 8, 2008 at 4:21 PM, Brian Takita <brian.takita at gmail.com> wrote: >> >> We have a custom EventMachine server that is used to host a multiplayer >> game. >> >> 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 >> Eventmachine-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
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. 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. On Sun, Jun 8, 2008 at 4:34 PM, Brian Takita <brian.takita at gmail.com> wrote:> On Sun, Jun 8, 2008 at 1:23 PM, Francis Cianfrocca > <garbagecat10 at gmail.com> wrote: > > Out of curiosity, what database are you using? For a long, long time I''ve > > wanted to see evented versions of the standard DBMS libraries. > Mysql. > > Yes that would be nice to have evented versions for the DBMS libraries. > > > > On Sun, Jun 8, 2008 at 4:21 PM, Brian Takita <brian.takita at gmail.com> > wrote: > >> > >> We have a custom EventMachine server that is used to host a multiplayer > >> game. > >> > >> 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 > >> Eventmachine-talk at rubyforge.org > >> http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > > > > _______________________________________________ > > Eventmachine-talk mailing list > > Eventmachine-talk at rubyforge.org > > http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080608/ff8d343f/attachment-0001.html>
On Sun, Jun 8, 2008 at 2:14 PM, Francis Cianfrocca <garbagecat10 at 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, so there 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.> > 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.Ideally we would still have the same I/O concurrency because we would 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.> > > On Sun, Jun 8, 2008 at 4:34 PM, Brian Takita <brian.takita at gmail.com> wrote: >> >> On Sun, Jun 8, 2008 at 1:23 PM, Francis Cianfrocca >> <garbagecat10 at gmail.com> wrote: >> > Out of curiosity, what database are you using? For a long, long time >> > I''ve >> > wanted to see evented versions of the standard DBMS libraries. >> Mysql. >> >> Yes that would be nice to have evented versions for the DBMS libraries. >> > >> > On Sun, Jun 8, 2008 at 4:21 PM, Brian Takita <brian.takita at gmail.com> >> > wrote: >> >> >> >> We have a custom EventMachine server that is used to host a multiplayer >> >> game. >> >> >> >> 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 >> >> Eventmachine-talk at rubyforge.org >> >> http://rubyforge.org/mailman/listinfo/eventmachine-talk >> > >> > >> > _______________________________________________ >> > Eventmachine-talk mailing list >> > Eventmachine-talk at rubyforge.org >> > http://rubyforge.org/mailman/listinfo/eventmachine-talk >> > >> _______________________________________________ >> Eventmachine-talk mailing list >> Eventmachine-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
On Sun, Jun 8, 2008 at 5:27 PM, Brian Takita <brian.takita at gmail.com> wrote:> On Sun, Jun 8, 2008 at 2:14 PM, Francis Cianfrocca > <garbagecat10 at 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, so > there 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. > > > > 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. > Ideally we would still have the same I/O concurrency because we would > 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 -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080608/df4b8387/attachment.html>
On 8 Jun 2008, at 22:38, Francis Cianfrocca wrote:> On Sun, Jun 8, 2008 at 5:27 PM, Brian Takita > <brian.takita at gmail.com> wrote: > On Sun, Jun 8, 2008 at 2:14 PM, Francis Cianfrocca > <garbagecat10 at 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.I totally agree, I have been toying with the idea of testing out an intermediate evented pool to handle a higher latency IO work, that is, leaving the Q deliberately on the DB server, but having an effective proxy which can make gains only really by it''s ability to coalesce requests. Clearly this doesn''t work for all scenarios, but can dramatically speed up total bandwidth over small requests.> > 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.I''m still sitting on the fence about threads vs. processes for the really big background tasks. Under the ruby paradigm, processes seem to clearly be the way (on MRI at least), but I''m dying to get the JRuby stuff up and running for this reason. They have real threads, and for something like a DB pool, that could really help, while we get our evented drivers sorted.> Yes, the Q will be large with lots of concurrent client requests, so > there 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.Quite right, and especially when we have to work the IO around it. Have you considered also simply splitting into two processes in order to achieve "a non-blocking thread"? Under MRI, and certain architectures, this again can do it, even with the overhead of local IPC over sockets.> > > > > 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. > Ideally we would still have the same I/O concurrency because we would > 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.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 around the issue that you really do just have to wait for the response. Now there are some potential solutions, one of which has worked for us in the past using PostgreSQLs NOTIFY clause and heavily abusing stored procedures and queuing tables. All of the DB business logic lives inside the DB, and requests are largely fire and forget (the app treats them as async, effectively). The problem with this approach is that your devs will eventually learn to hate you after the first few thousand lines of plpgsql. Code generation can help.> 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.And as with the suggestions I''ve made above, there is a balance, I''ve mostly suggested methods which add further indirection or overhead, and that is never free. The real impact depends on the rest of your architecture and specific message sizes, latencies and processing requirements.> If there were an evented mysql library (which someone really needs > to write), the problem would be easyI''ve started a postgres one, but I''ve put a lot on my plate, so there is no promise of a speedy delivery, if anyone wants to help however, please get in contact. The logic bound issue can still cause problems too, and so there''s a lot of experimental work to do to try and really start pushing against the capabilities of the technology, especially if coming from a ruby app. There''s also a slightly special case of synchrony problems under certain conditions, which I will want to study, although a plpgsql architecture can overcome that conveniently.> _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080609/79c4c8cd/attachment-0001.html>
> 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 getIt 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
All right! By the way, Eventmachine is now on github, so feel free to add a fork for your work on this.. On Mon, Jun 9, 2008 at 11:06 AM, Thomas Ptacek <tqbf at matasano.com> wrote:> > 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. > > 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 > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080609/f8efaf6f/attachment.html>
On 9 Jun 2008, at 16:06, Thomas Ptacek wrote:>> 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 ;)> > > -- > --- > Thomas H. Ptacek // matasano security > read us on the web: http://www.matasano.com/log > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk
> No, indeed it shouldn''t, but you have to step away from traditional branch > logic styles in order to deal with that: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. -- --- 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. 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
This is pretty confusing, but just to be clear: MySQL is a client-server protocol, and if you can speak it, you can drive an arbitrary number of queries to an arbitrary number of databases through a single thread via the event loop. No binary hacking required. The MySQL protocol is less complicated than it seems. On 6/9/08, Roger Pack <rogerpack2005 at gmail.com> wrote:> > 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. > > 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 > > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
On 9 Jun 2008, at 20:25, Roger Pack wrote:>> 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.
On Mon, Jun 9, 2008 at 8:59 AM, James Tucker <jftucker at gmail.com> wrote:> > On 9 Jun 2008, at 16:06, Thomas Ptacek wrote: > >>> 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.I was thinking of either continuations or a command queue. The branching logic would be different from your typical app. I suppose ruby blocks & lambdas could help out with making it more palatable.> > 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 already implemented a solution for Comet style requests. http://github.com/pivotal/screw-unit-server http://github.com/pivotal/js-spec-server 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.> > >> 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 ;) > >> >> >> -- >> --- >> Thomas H. Ptacek // matasano security >> read us on the web: http://www.matasano.com/log >> _______________________________________________ >> Eventmachine-talk mailing list >> Eventmachine-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/eventmachine-talk > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
> 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. -R
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. 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. On 6/9/08, Francis Cianfrocca <garbagecat10 at gmail.com> wrote:> All right! > > By the way, Eventmachine is now on github, so feel free to add a fork for > your work on this.. > > > On Mon, Jun 9, 2008 at 11:06 AM, Thomas Ptacek <tqbf at matasano.com> wrote: > > > > > > > > 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. > > > > 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 > > > > > > > > _______________________________________________ > > Eventmachine-talk mailing list > > Eventmachine-talk at rubyforge.org > > http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
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 -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.
On 10 Jun 2008, at 01:03, Roger Pack wrote:>> 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.> -R > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk
That''s extremely interesting, I had been wondering if that could be done... well done :) On 10 Jun 2008, at 08:52, Charles Jolley wrote:> 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 > > -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. > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk
On 10 Jun 2008, at 07:18, Thomas Ptacek wrote:> 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.tgzUnder 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.:)
On Tue, Jun 10, 2008 at 1:52 AM, Charles Jolley <charles at okito.net> wrote:> 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-rubyIt has memory leaks because continuations in ruby (MRI ruby, at least) leak memory. And they are really slow, too. Cool idea, though. :) Kirk Haines
Well I''ve experimented a bit and continuations do not leak memory all of the time...only under certain conditions which I am still trying to identify. (i suspect it has to do with the interaction between continuations and closures) I may not be able to make this code work without leaking memory but we shall see. 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 On Jun 10, 2008, at 5:11 AM, "Kirk Haines" <wyhaines at gmail.com> wrote:> On Tue, Jun 10, 2008 at 1:52 AM, Charles Jolley <charles at okito.net> > wrote: >> 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 > > 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 > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk
> 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 > }Nice! An evented mysql. I wonder how you could combine this with existing apps to get a single threaded server :) -R
On Tue, Jun 10, 2008 at 1:52 AM, Charles Jolley <charles at okito.net> wrote:> 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 >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. -- Tony Arcieri medioh.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080610/7be72600/attachment-0001.html>
On Tue, Jun 10, 2008 at 10:18 AM, Tony Arcieri <tony at medioh.com> wrote:> On Tue, Jun 10, 2008 at 1:52 AM, Charles Jolley <charles at okito.net> wrote: >> >> 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 > > 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.Too bad fibers aren''t backported to 1.8.> > -- > Tony Arcieri > medioh.com > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
On Tue, Jun 10, 2008 at 10:43 AM, Brian Takita <brian.takita at gmail.com> wrote:> On Tue, Jun 10, 2008 at 10:18 AM, Tony Arcieri <tony at medioh.com> wrote: >> On Tue, Jun 10, 2008 at 1:52 AM, Charles Jolley <charles at okito.net> wrote: >>> >>> 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 >> >> 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. > Too bad fibers aren''t backported to 1.8.Tony, do you have a code sample on how this would work using Revactor?>> >> -- >> Tony Arcieri >> medioh.com >> _______________________________________________ >> Eventmachine-talk mailing list >> Eventmachine-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/eventmachine-talk >> >
On Tue, Jun 10, 2008 at 11:46 AM, Brian Takita <brian.takita at gmail.com> wrote:> Tony, do you have a code sample on how this would work using Revactor? >For all intents and purposes it''d be the same as the MySQL (or ActiveRecord / DataMapper / DBI / whatever) API, as it facilitates "blocking" calls that wait for the request to complete (but suspend the fiber making the request until it completes) -- Tony Arcieri medioh.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080610/1f78754e/attachment.html>
Revactor looks really interesting Tony. Does it work in any special way with EM by any chance? The pipelined code actually pauses execution while waiting on a deferrable. This way it can interact with any evented-library that uses deferrables to communicate callback state (ala the HTTP library in EM). -Charles On Jun 10, 2008, at 10:50 AM, Tony Arcieri wrote:> On Tue, Jun 10, 2008 at 11:46 AM, Brian Takita > <brian.takita at gmail.com> wrote: > Tony, do you have a code sample on how this would work using Revactor? > > For all intents and purposes it''d be the same as the MySQL (or > ActiveRecord / DataMapper / DBI / whatever) API, as it facilitates > "blocking" calls that wait for the request to complete (but suspend > the fiber making the request until it completes) > > -- > Tony Arcieri > medioh.com _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080610/51f9a4c8/attachment.html>
is there any benefit to using revactor versus just a normal threaded approach?> 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?
On Tue, Jun 10, 2008 at 4:16 PM, Roger Pack < roger.pack at leadmediapartners.com> wrote:> is there any benefit to using revactor versus just a normal threaded > approach? >It depends on what you''re writing. Revactor is fundamentally evented and thus eliminates some of the headaches of threads. That said Rubinius exposes an Actor API which is mostly compatible with Revactor''s, so you can prototype applications on Revactor then move them over to Rubinius as soon as it''s ready.> 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? >As in I see no solution. My original approach required continuing within a continuation, and if you do that you just keep piling more and more on the stack without ever reclaiming what''s underneath. I haven''t looked specifically at the continuation-based approaches others have posted but if they''re encountering endless memory growth it''s likely the same problem. That''s why you need a coroutine mechanism like Fibers to make this approach workable... -- Tony Arcieri medioh.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080610/acac1743/attachment.html>
I''m about to do a revactor prototype port of my own threaded TCPServer. My stuff is on 1.86 and so it''s threads not fibres. I''ve had some fun with race conditions and having to force context switching so i''m pretty ramped to play with revactor. J -----Original Message----- From: "Tony Arcieri" <tony at medioh.com> Date: Tue, 10 Jun 2008 16:28:12 To:eventmachine-talk at rubyforge.org Subject: Re: [Eventmachine-talk] Evented database access On Tue, Jun 10, 2008 at 4:16 PM, Roger Pack <roger.pack at leadmediapartners.com <mailto:roger.pack at leadmediapartners.com> > wrote: is there any benefit to using revactor versus just a normal threaded approach? It depends on what you''re writing.? Revactor is fundamentally evented and thus eliminates some of the headaches of threads. That said Rubinius exposes an Actor API which is mostly compatible with Revactor''s, so you can prototype applications on Revactor then move them over to Rubinius as soon as it''s ready.> 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? As in I see no solution.? My original approach required continuing within a continuation, and if you do that you just keep piling more and more on the stack without ever reclaiming what''s underneath.? I haven''t looked specifically at the continuation-based approaches others have posted but if they''re encountering endless memory growth it''s likely the same problem. That''s why you need a coroutine mechanism like Fibers to make this approach workable... -- Tony Arcieri medioh.com <http://medioh.com> _______________________________________________ Eventmachine-talk mailing list Eventmachine-talk at rubyforge.org http://rubyforge.org/mailman/listinfo/eventmachine-talk
> 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.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
> 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.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
I wonder if it would be possible to re-tool the pure ruby mysql, as another option: http://www.tmtm.org/en/ruby/mysql/ 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 <tqbf at 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.
On Wed, Jun 11, 2008 at 5:20 PM, Roger Pack < roger.pack at leadmediapartners.com> wrote:> Speaking of which, I wonder if one could just use revactor on it > straight, as it is now :)It should be possible to use the pure Ruby MySQL in an evented manner by (monkey)patching it to use Revactor''s sockets rather than Ruby TCP sockets. -- Tony Arcieri medioh.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080611/f565783a/attachment.html>
The benefit of using continuations or fibers in 1.9 is that it allows you to "unwind" event driven programming so that it looks like a normal stack based programming model. 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 On Jun 11, 2008, at 4:15 PM, "Thomas Ptacek" <tqbf at matasano.com> wrote:>> 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. > > 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 > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk
The pure ruby MySQL does basically nothing but feed text SQL to the server and read the responses, and it doesn''t support the modern MySQL login protocol (then again, I don''t support the archaic login). Neither my code nor ruby-mysql supports the binary prepared statement protocol --- which, if you look at it on the wire, might not be much of a win anyways. I don''t think extending ruby-mysql is the way to go. On 6/11/08, Roger Pack <roger.pack at leadmediapartners.com> wrote:> I wonder if it would be possible to re-tool the pure ruby mysql, as > another option: > http://www.tmtm.org/en/ruby/mysql/ > > 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 <tqbf at 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. > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
On Wed, Jun 11, 2008 at 5:28 PM, Charles Jolley <charles at okito.net> wrote:> The benefit of using continuations or fibers in 1.9 is that it allows you to > "unwind" event driven programming so that it looks like a normal stack based > programming model.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.
> The pure ruby MySQL does basically nothing but feed text SQL to the > server and read the responses, and it doesn''t support the modern MySQL > login protocol (then again, I don''t support the archaic login). > Neither my code nor ruby-mysql supports the binary prepared statement > protocol --- which, if you look at it on the wire, might not be much > of a win anyways. > > I don''t think extending ruby-mysql is the way to go.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
conns = 0.upto(20).map {|i| Asymy::Connection.new(opts)} 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.pack at leadmediapartners.com> wrote:> > The pure ruby MySQL does basically nothing but feed text SQL to the > > server and read the responses, and it doesn''t support the modern MySQL > > login protocol (then again, I don''t support the archaic login). > > Neither my code nor ruby-mysql supports the binary prepared statement > > protocol --- which, if you look at it on the wire, might not be much > > of a win anyways. > > > > I don''t think extending ruby-mysql is the way to go. > > > 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 > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
On Wed, Jun 11, 2008 at 6:28 PM, Thomas Ptacek <tqbf at matasano.com> wrote:> conns = 0.upto(20).map {|i| Asymy::Connection.new(opts)} > > 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.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. -R
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 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. On 6/12/08, Roger Pack <roger.pack at leadmediapartners.com> wrote:> On Wed, Jun 11, 2008 at 6:28 PM, Thomas Ptacek <tqbf at matasano.com> wrote: > > conns = 0.upto(20).map {|i| Asymy::Connection.new(opts)} > > > > 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. > > > 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. > > -R > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
> 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 :)It supports blank passwords now. Thanks for noticing. -- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
> At Aman Gupta''s suggestion, I copied this up to GitHub: > > http://www.github.com/tqbf/asymyHow 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 :) -R
On Thu, Jun 12, 2008 at 8:16 AM, Roger Pack <rogerpack2005 at gmail.com> wrote:>> 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
> 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*)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
On Thu, Jun 12, 2008 at 11:32 AM, Roger Pack <rogerpack2005 at gmail.com> wrote:> 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
And Mongrel on Revactor already provides a way to spawn fibers instead of threads with very minor modifications to Mongrel On Thu, Jun 12, 2008 at 12:34 PM, Kirk Haines <wyhaines at gmail.com> wrote:> On Thu, Jun 12, 2008 at 11:32 AM, Roger Pack <rogerpack2005 at gmail.com> > wrote: > > > 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 > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- Tony Arcieri medioh.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080612/f54b800f/attachment.html>
On 6/12/08, Tony Arcieri <tony at medioh.com> wrote:> On Wed, Jun 11, 2008 at 5:20 PM, Roger Pack > <roger.pack at leadmediapartners.com> wrote: > > > Speaking of which, I wonder if one could just use revactor on it > > straight, as it is now :) > > It should be possible to use the pure Ruby MySQL in an evented manner by > (monkey)patching it to use Revactor''s sockets rather than Ruby TCP sockets. >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.
> 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.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
In latest news, it turned out to be nicely easy to add fibers to DB access and to mongrel. An example of something doint this [a mongrel handler that makes some async DB calls and returns ''hello''] is http://code.google.com/p/ruby-roger-useful-functions/source/browse/trunk/asymy/fibers/mongrel_test/test_mongrel_with_db.rb 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
Nice. Were you going to maintain this or just kind of put it out there for others to use? [ex: was thinking of changing it slightly to be @conn.exec("sql") {|columns, rows, error_message_if_it_fails_half_way| } Or something or other to denote failure. Thanks! -R On Wed, Jun 11, 2008 at 11:32 PM, Thomas Ptacek <tqbf at 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 >
The "columns" and "rows" should probably be merged into an array of hashes, with the keys merged from the "columns" def and a :value key from the row data. 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. =) On 6/16/08, Roger Pack <roger.pack at leadmediapartners.com> wrote:> Nice. Were you going to maintain this or just kind of put it out > there for others to use? > [ex: was thinking of changing it slightly to be > @conn.exec("sql") {|columns, rows, error_message_if_it_fails_half_way| > } > Or something or other to denote failure. > Thanks! > > -R > > > > On Wed, Jun 11, 2008 at 11:32 PM, Thomas Ptacek <tqbf at 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 > > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
On Tue, Jun 17, 2008 at 7:02 AM, Thomas Ptacek <tqbf at matasano.com> wrote:> The "columns" and "rows" should probably be merged into an array of > hashes, with the keys merged from the "columns" def and a :value key > from the row data. > > 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. =) >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> On 6/16/08, Roger Pack <roger.pack at leadmediapartners.com> wrote: >> Nice. Were you going to maintain this or just kind of put it out >> there for others to use? >> [ex: was thinking of changing it slightly to be >> @conn.exec("sql") {|columns, rows, error_message_if_it_fails_half_way| >> } >> Or something or other to denote failure. >> Thanks! >> >> -R >> >> >> >> On Wed, Jun 11, 2008 at 11:32 PM, Thomas Ptacek <tqbf at 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 >> > >> >> _______________________________________________ >> Eventmachine-talk mailing list >> Eventmachine-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/eventmachine-talk >> > > > -- > --- > Thomas H. Ptacek // matasano security > read us on the web: http://www.matasano.com/log > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
I''d say try to pass them back in whatever is the fastest to create, and then let them worry about how to make it work :) Oh that''s me, though. 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]. http://code.google.com/p/ruby-roger-useful-functions/source/browse/trunk/asymy/fibers/mongrel_test/connection_pool_fibered.rb 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 On Mon, Jun 16, 2008 at 3:02 PM, Thomas Ptacek <tqbf at matasano.com> wrote:> The "columns" and "rows" should probably be merged into an array of > hashes, with the keys merged from the "columns" def and a :value key > from the row data. > > 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. =) > > On 6/16/08, Roger Pack <roger.pack at leadmediapartners.com> wrote: >> Nice. Were you going to maintain this or just kind of put it out >> there for others to use? >> [ex: was thinking of changing it slightly to be >> @conn.exec("sql") {|columns, rows, error_message_if_it_fails_half_way| >> } >> Or something or other to denote failure. >> Thanks! >> >> -R >> >> >> >> On Wed, Jun 11, 2008 at 11:32 PM, Thomas Ptacek <tqbf at 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 >> > >> >> _______________________________________________ >> Eventmachine-talk mailing list >> Eventmachine-talk at rubyforge.org >> http://rubyforge.org/mailman/listinfo/eventmachine-talk >> > > > -- > --- > Thomas H. Ptacek // matasano security > read us on the web: http://www.matasano.com/log > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
The error handling in this code is really appalling, so I may work on that before I do anything else. On most Unix systems you can bump up the ulimit programatically if you have the privs to run "ulimit".> 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 > > > On Mon, Jun 16, 2008 at 3:02 PM, Thomas Ptacek <tqbf at matasano.com> wrote: > > The "columns" and "rows" should probably be merged into an array of > > hashes, with the keys merged from the "columns" def and a :value key > > from the row data. > > > > 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. =) > > > > On 6/16/08, Roger Pack <roger.pack at leadmediapartners.com> wrote: > >> Nice. Were you going to maintain this or just kind of put it out > >> there for others to use? > >> [ex: was thinking of changing it slightly to be > >> @conn.exec("sql") {|columns, rows, error_message_if_it_fails_half_way| > >> } > >> Or something or other to denote failure. > >> Thanks! > >> > >> -R > >> > >> > >> > >> On Wed, Jun 11, 2008 at 11:32 PM, Thomas Ptacek <tqbf at 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 > >> > > >> > >> _______________________________________________ > >> Eventmachine-talk mailing list > >> Eventmachine-talk at rubyforge.org > >> http://rubyforge.org/mailman/listinfo/eventmachine-talk > >> > > > > > > -- > > --- > > Thomas H. Ptacek // matasano security > > read us on the web: http://www.matasano.com/log > > _______________________________________________ > > Eventmachine-talk mailing list > > Eventmachine-talk at rubyforge.org > > http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- --- Thomas H. Ptacek // matasano security read us on the web: http://www.matasano.com/log
> On most Unix systems you can bump up > the ulimit programatically if you have the privs to run "ulimit".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
> 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 trapOne 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
On 19 Jun 2008, at 05:54, Roger Pack wrote:>> 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?> > Thanks! > -R > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk
> EM::kqueue? > EM::epoll? > EM::ssl?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].> >> >> Thanks! >> -R
K I created a small write up on "how to make a single threaded, fast ruby web server that uses asynchronous IO" at http://betterlogic.com/roger/?p=339 if anyone''s interested :) Seems to have potential. Also noted that the asymy lib now accomodates for 1.9. Thanks! -R
On Wed, Jun 11, 2008 at 10:09 PM, Roger Pack <roger.pack at leadmediapartners.com> wrote:> On Wed, Jun 11, 2008 at 6:28 PM, Thomas Ptacek <tqbf at matasano.com> wrote: >> conns = 0.upto(20).map {|i| Asymy::Connection.new(opts)} >> >> 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. > > 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.039sIt 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> > > 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. > -R > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >
Yeah, it''s been a major problem, especially considering the MySQL gem doesn''t seem to use TRAP_BEG / TRAP_END properly... On Wed, Aug 6, 2008 at 8:04 PM, Brian Takita <brian.takita at gmail.com> wrote:> On Wed, Jun 11, 2008 at 10:09 PM, Roger Pack > <roger.pack at leadmediapartners.com> wrote: > > On Wed, Jun 11, 2008 at 6:28 PM, Thomas Ptacek <tqbf at matasano.com> > wrote: > >> conns = 0.upto(20).map {|i| Asymy::Connection.new(opts)} > >> > >> 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. > > > > 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 > > 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 > > > > > > 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. > > -R > > _______________________________________________ > > Eventmachine-talk mailing list > > Eventmachine-talk at rubyforge.org > > http://rubyforge.org/mailman/listinfo/eventmachine-talk > > > _______________________________________________ > Eventmachine-talk mailing list > Eventmachine-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/eventmachine-talk >-- Tony Arcieri medioh.com -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://rubyforge.org/pipermail/eventmachine-talk/attachments/20080806/ccfa590b/attachment-0001.html>
>> ~/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 > > 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-mysqlYeah 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! -R
>> Speaking of which, I wonder if one could just use revactor on it >> straight, as it is now :) > > It should be possible to use the pure Ruby MySQL in an evented manner by > (monkey)patching it to use Revactor''s sockets rather than Ruby TCP sockets.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