ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Coro-Mysql/Mysql.pm
(Generate patch)

Comparing Coro-Mysql/Mysql.pm (file contents):
Revision 1.2 by root, Sat May 30 06:58:22 2009 UTC vs.
Revision 1.9 by root, Thu Feb 17 02:05:14 2011 UTC

20This can be used to make parallel sql requests using Coro, or to do other 20This can be used to make parallel sql requests using Coro, or to do other
21stuff while mysql is rumbling in the background. 21stuff while mysql is rumbling in the background.
22 22
23=head2 CAVEAT 23=head2 CAVEAT
24 24
25Note that this module must be linked against exactly the same 25Note that this module must be linked against exactly the same (shared,
26F<libmysqlclient> library as DBD::mysql, otherwise it will not work. 26possibly not working with all OSes) F<libmysqlclient> library as
27DBD::mysql, otherwise it will not work.
27 28
28Also, while this module makes database handles non-blocking, you still 29Also, while this module makes database handles non-blocking, you still
29cannot run multiple requests in parallel on the same database handle. If 30cannot run multiple requests in parallel on the same database handle. If
30you want to run multiple queries in parallel, you have to create multiple 31you want to run multiple queries in parallel, you have to create multiple
31database connections, one for each thread that runs queries. 32database connections, one for each thread that runs queries. Not doing so
33can corrupt your data - use a Coro::Semaphore when in doubt.
32 34
33If you make sure that you never run two or more requests in parallel, you 35If you make sure that you never run two or more requests in parallel, you
34cna freely share the database handles between threads, of course. 36can freely share the database handles between threads, of course.
35 37
36Also, this module uses a number of "unclean" techniques (patching an 38Also, this module uses a number of "unclean" techniques (patching an
37internal libmysql structure for one thing) and was hacked within a few 39internal libmysql structure for one thing) and was initially hacked within
38hours on a long flight to Malaysia. 40a few hours on a long flight to Malaysia.
39 41
40It does, however, check whether it indeed got the structure layout 42It does, however, check whether it indeed got the structure layout
41correct, so you should expect perl exceptions or early crashes as opposed 43correct, so you should expect perl exceptions or early crashes as opposed
42to data corruption when something goes wrong. 44to data corruption when something goes wrong during patching.
43 45
44=head2 SPEED 46=head2 SPEED
45 47
46This module is implemented in XS, and as long as mysqld replies quickly 48This module is implemented in XS, and as long as mysqld replies quickly
47enough, it adds no overhead to the standard libmysql communication 49enough, it adds no overhead to the standard libmysql communication
48routines (which are very badly written). 50routines (which are very badly written, btw.). In fact, since it has a
51more efficient buffering and allows requests to run in parallel, it often
52decreases the actual time to run many queries considerably.
49 53
50For very fast queries ("select 0"), this module can add noticable overhead 54For very fast queries ("select 0"), this module can add noticable overhead
51(around 15%) as it tries to switch to other coroutines when mysqld doesn't 55(around 15%, 7% when EV can be used) as it tries to switch to other
52deliver the data instantly. 56coroutines when mysqld doesn't deliver the data immediately, although,
57again, when running queries in parallel, they will usually execute faster.
53 58
54For most types of queries, there will be no overhead, especially on 59For most types of queries, there will be no extra latency, especially on
55multicore systems where your perl process can do other things while mysqld 60multicore systems where your perl process can do other things while mysqld
56does its stuff. 61does its stuff.
57 62
63=head2 LIMITATIONS
64
65This module only supports "standard" mysql connection handles - this
66means unix domain or TCP sockets, and excludes SSL/TLS connections, named
67pipes (windows) and shared memory (also windows). No support for these
68connection types is planned, either.
69
70=head1 CANCELLATION
71
72Cancelling a thread that is within a mysql query will likely make the
73handle unusable. As far as Coro::Mysql is concerned, the handle can be
74safely destroyed, but it's not clear how mysql itself will react to a
75cancellation.
76
77=head1 FUNCTIONS
78
79Coro::Mysql offers a single user-accessible function:
80
58=over 4 81=over 4
59 82
60=cut 83=cut
61 84
62package Coro::Mysql; 85package Coro::Mysql;
66 89
67use Scalar::Util (); 90use Scalar::Util ();
68use Carp qw(croak); 91use Carp qw(croak);
69 92
70use Guard; 93use Guard;
94use AnyEvent ();
71use Coro::Handle (); 95use Coro ();
96use Coro::AnyEvent (); # not necessary with newer Coro versions
72 97
73# we need this extra indirection, as Coro doesn't support 98# we need this extra indirection, as Coro doesn't support
74# calling SLF-like functions via call_sv. 99# calling SLF-like functions via call_sv.
75 100
76sub readable { &Coro::Handle::FH::readable } 101sub readable { &Coro::Handle::FH::readable }
77sub writable { &Coro::Handle::FH::writable } 102sub writable { &Coro::Handle::FH::writable }
78 103
79BEGIN { 104BEGIN {
80 our $VERSION = '0.1'; 105 our $VERSION = '1.02';
81 106
82 require XSLoader; 107 require XSLoader;
83 XSLoader::load Coro::Mysql::, $VERSION; 108 XSLoader::load Coro::Mysql::, $VERSION;
84} 109}
85 110
89so it becomes compatible to Coro threads. 114so it becomes compatible to Coro threads.
90 115
91After that, it returns the patched handle - you should always use the 116After that, it returns the patched handle - you should always use the
92newly returned database handle. 117newly returned database handle.
93 118
119It is safe to call this function on any database handle (or just about any
120value), but it will only do anything to L<DBD::mysql> handles, others are
121returned unchanged. That means it is harmless when applied to database
122handles of other databases.
123
94=cut 124=cut
95 125
96sub unblock { 126sub unblock {
97 my ($DBH) = @_; 127 my ($DBH) = @_;
128
129 if ($DBH->{Driver}{Name} eq "mysql") {
98 my $sock = $DBH->{sock}; 130 my $sock = $DBH->{sock};
99 131
100 open my $fh, "+>&" . $DBH->{sockfd} 132 open my $fh, "+>&" . $DBH->{sockfd}
101 or croak "Coro::Mysql unable to clone mysql fd"; 133 or croak "Coro::Mysql unable to clone mysql fd";
102 134
135 if (AnyEvent::detect ne "AnyEvent::Impl::EV" || !_use_ev) {
136 require Coro::Handle;
103 $fh = Coro::Handle::unblock $fh; 137 $fh = Coro::Handle::unblock ($fh);
138 }
104 139
105 _patch $sock, $DBH->{sockfd}, tied ${$fh}; 140 _patch $sock, $DBH->{sockfd}, $fh, tied ${$fh};
106 $DBH->{private_Coro_Mysql} = guard {
107 _unpatch $sock;
108 undef $fh;
109 }; 141 }
110 142
111 $DBH 143 $DBH
112} 144}
113 145
1141; 1461;
115 147
116=back 148=back
117 149
150=head1 USAGE EXAMPLE
151
152This example uses L<PApp::SQL> and L<Coro::on_enter> to implement a
153function C<with_db>, that connects to a database, uses C<unblock> on the
154resulting handle and then makes sure that C<$PApp::SQL::DBH> is set to the
155(per-thread) database handle when the given thread is running (it does not
156restore any previous value of $PApp::SQL::DBH, however):
157
158 use Coro;
159 use Coro::Mysql;
160 use PApp::SQL;
161
162 sub with_db($$$&) {
163 my ($database, $user, $pass, $cb) = @_;
164
165 my $dbh = DBI->connect ($database, $user, $pass)->Coro::Mysql::unblock
166 or die $DBI::errstr;
167
168 Coro::on_enter { $PApp::SQL::DBH = $dbh };
169
170 $cb->();
171 }
172
173This function makes it possible to easily use L<PApp::SQL> with
174L<Coro::Mysql>, without worrying about database handles.
175
176 # now start 10 threads doing stuff
177 async {
178
179 with_db "DBI:mysql:test", "", "", sub {
180 sql_exec "update table set col = 5 where id = 7";
181
182 my $st = sql_exec \my ($id, $name),
183 "select id, name from table where name like ?",
184 "a%";
185
186 while ($st->fetch) {
187 ...
188 }
189
190 my $id = sql_insertid sql_exec "insert into table values (1,2,3)";
191 # etc.
192 };
193
194 } for 1..10;
195
196=head1 SEE ALSO
197
198L<Coro>, L<PApp::SQL> (a user friendly but efficient wrapper around DBI).
199
118=head1 AUTHOR 200=head1 AUTHOR
119 201
120 Marc Lehmann <schmorp@schmorp.de> 202 Marc Lehmann <schmorp@schmorp.de>
121 http://home.schmorp.de/ 203 http://home.schmorp.de/
122 204

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines