superfeedr-em-mysql 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +40 -0
- data/em-mysql.gemspec +21 -0
- data/lib/em/mysql.rb +306 -0
- data/lib/sequel/async.rb +184 -0
- data/test.rb +59 -0
- metadata +68 -0
data/README
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
Async MySQL driver for Ruby/EventMachine
|
2
|
+
(c) 2008 Aman Gupta (tmm1)
|
3
|
+
|
4
|
+
|
5
|
+
Requires mysqlplus.
|
6
|
+
|
7
|
+
require 'em/mysql'
|
8
|
+
|
9
|
+
# alias SQL for simpler syntax
|
10
|
+
|
11
|
+
SQL = EventedMysql
|
12
|
+
def SQL(query, &blk) SQL.select(query, &blk) end
|
13
|
+
|
14
|
+
|
15
|
+
# setup connection details and allow 4 connections to the server
|
16
|
+
|
17
|
+
SQL.settings.update :host => 'localhost',
|
18
|
+
:port => 3306,
|
19
|
+
:database => 'test',
|
20
|
+
:connections => 4
|
21
|
+
|
22
|
+
|
23
|
+
# use 4 connections to execute queries in parallel
|
24
|
+
|
25
|
+
SQL('select sleep(0.25)'){ p 'done' }
|
26
|
+
SQL('select sleep(0.25)'){ p 'done' }
|
27
|
+
SQL('select sleep(0.25)'){ p 'done' }
|
28
|
+
SQL('select sleep(0.25)'){ p 'done' }
|
29
|
+
|
30
|
+
Also includes a sequel async wrapper
|
31
|
+
|
32
|
+
require 'sequel'
|
33
|
+
require 'sequel/async'
|
34
|
+
|
35
|
+
DB = Sequel.connect(:adapter => 'mysql', :user => 'root', :database => 'test', ...)
|
36
|
+
EventedMysql.settings.update(..., :on_error => proc{|e| log 'error', e })
|
37
|
+
|
38
|
+
DB[:table].where(:field => 'value').async_update(:field => 'new value')
|
39
|
+
|
40
|
+
For more info, see the comments in lib/sequel/async.rb
|
data/em-mysql.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
spec = Gem::Specification.new do |s|
|
2
|
+
s.name = 'superfeedr-em-mysql'
|
3
|
+
s.version = '0.4.0'
|
4
|
+
s.date = '2009-06-23'
|
5
|
+
s.summary = 'Async MySQL client API for Ruby/EventMachine'
|
6
|
+
s.email = "em-mysql@tmm1.net"
|
7
|
+
s.homepage = "http://github.com/tmm1/em-mysql"
|
8
|
+
s.description = 'Async MySQL client API for Ruby/EventMachine'
|
9
|
+
s.has_rdoc = false
|
10
|
+
s.authors = ["Aman Gupta"]
|
11
|
+
s.add_dependency('eventmachine', '>= 0.12.9')
|
12
|
+
|
13
|
+
# git ls-files
|
14
|
+
s.files = %w[
|
15
|
+
README
|
16
|
+
em-mysql.gemspec
|
17
|
+
lib/em/mysql.rb
|
18
|
+
lib/sequel/async.rb
|
19
|
+
test.rb
|
20
|
+
]
|
21
|
+
end
|
data/lib/em/mysql.rb
ADDED
@@ -0,0 +1,306 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'mysqlplus'
|
4
|
+
require 'fcntl'
|
5
|
+
|
6
|
+
MAX_RETRIES_ON_DEADLOCKS = 10
|
7
|
+
|
8
|
+
class Mysql
|
9
|
+
def result
|
10
|
+
@cur_result
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class EventedMysql < EM::Connection
|
15
|
+
def initialize mysql, opts
|
16
|
+
@mysql = mysql
|
17
|
+
@fd = mysql.socket
|
18
|
+
@opts = opts
|
19
|
+
@current = nil
|
20
|
+
@@queue ||= []
|
21
|
+
@processing = false
|
22
|
+
@connected = true
|
23
|
+
|
24
|
+
log 'mysql connected'
|
25
|
+
|
26
|
+
self.notify_readable = true
|
27
|
+
EM.add_timer(0){ next_query }
|
28
|
+
end
|
29
|
+
attr_reader :processing, :connected, :opts
|
30
|
+
alias :settings :opts
|
31
|
+
|
32
|
+
DisconnectErrors = [
|
33
|
+
'query: not connected',
|
34
|
+
'MySQL server has gone away',
|
35
|
+
'Lost connection to MySQL server during query'
|
36
|
+
] unless defined? DisconnectErrors
|
37
|
+
|
38
|
+
def notify_readable
|
39
|
+
log 'readable'
|
40
|
+
if item = @current
|
41
|
+
@current = nil
|
42
|
+
start, response, sql, cblk, eblk, retries = item
|
43
|
+
log 'mysql response', Time.now-start, sql
|
44
|
+
arg = case response
|
45
|
+
when :raw
|
46
|
+
result = @mysql.get_result
|
47
|
+
@mysql.instance_variable_set('@cur_result', result)
|
48
|
+
@mysql
|
49
|
+
when :select
|
50
|
+
ret = []
|
51
|
+
result = @mysql.get_result
|
52
|
+
result.each_hash{|h| ret << h }
|
53
|
+
log 'mysql result', ret
|
54
|
+
ret
|
55
|
+
when :update
|
56
|
+
result = @mysql.get_result
|
57
|
+
@mysql.affected_rows
|
58
|
+
when :insert
|
59
|
+
result = @mysql.get_result
|
60
|
+
@mysql.insert_id
|
61
|
+
else
|
62
|
+
result = @mysql.get_result
|
63
|
+
log 'got a result??', result if result
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
@processing = false
|
68
|
+
# result.free if result.is_a? Mysql::Result
|
69
|
+
next_query
|
70
|
+
cblk.call(arg) if cblk
|
71
|
+
else
|
72
|
+
log 'readable, but nothing queued?! probably an ERROR state'
|
73
|
+
return close
|
74
|
+
end
|
75
|
+
rescue Mysql::Error => e
|
76
|
+
log 'mysql error', e.message
|
77
|
+
if e.message =~ /Deadlock/ and retries < MAX_RETRIES_ON_DEADLOCKS
|
78
|
+
@@queue << [response, sql, cblk, eblk, retries + 1]
|
79
|
+
@processing = false
|
80
|
+
next_query
|
81
|
+
elsif DisconnectErrors.include? e.message
|
82
|
+
@@queue << [response, sql, cblk, eblk, retries + 1]
|
83
|
+
return close
|
84
|
+
elsif cb = (eblk || @opts[:on_error])
|
85
|
+
cb.call(e)
|
86
|
+
@processing = false
|
87
|
+
next_query
|
88
|
+
else
|
89
|
+
raise e
|
90
|
+
end
|
91
|
+
# ensure
|
92
|
+
# res.free if res.is_a? Mysql::Result
|
93
|
+
# @processing = false
|
94
|
+
# next_query
|
95
|
+
end
|
96
|
+
|
97
|
+
def unbind
|
98
|
+
log 'mysql disconnect', $!, *($! ? $!.backtrace[0..5] : [])
|
99
|
+
# cp = EventedMysql.instance_variable_get('@connection_pool') and cp.delete(self)
|
100
|
+
@connected = false
|
101
|
+
|
102
|
+
# XXX wait for the next tick until the current fd is removed completely from the reactor
|
103
|
+
#
|
104
|
+
# XXX in certain cases the new FD# (@mysql.socket) is the same as the old, since FDs are re-used
|
105
|
+
# XXX without next_tick in these cases, unbind will get fired on the newly attached signature as well
|
106
|
+
#
|
107
|
+
# XXX do _NOT_ use EM.next_tick here. if a bunch of sockets disconnect at the same time, we want
|
108
|
+
# XXX reconnects to happen after all the unbinds have been processed
|
109
|
+
EM.add_timer(0) do
|
110
|
+
log 'mysql reconnecting'
|
111
|
+
@processing = false
|
112
|
+
@mysql = EventedMysql._connect @opts
|
113
|
+
@fd = @mysql.socket
|
114
|
+
|
115
|
+
@signature = EM.attach_fd @mysql.socket, true
|
116
|
+
EM.set_notify_readable @signature, true
|
117
|
+
log 'mysql connected'
|
118
|
+
EM.instance_variable_get('@conns')[@signature] = self
|
119
|
+
@connected = true
|
120
|
+
make_socket_blocking
|
121
|
+
next_query
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def execute sql, response = nil, cblk = nil, eblk = nil, retries = 0, &blk
|
126
|
+
cblk ||= blk
|
127
|
+
|
128
|
+
begin
|
129
|
+
unless @processing or !@connected
|
130
|
+
# begin
|
131
|
+
# log 'mysql ping', @mysql.ping
|
132
|
+
# # log 'mysql stat', @mysql.stat
|
133
|
+
# # log 'mysql errno', @mysql.errno
|
134
|
+
# rescue
|
135
|
+
# log 'mysql ping failed'
|
136
|
+
# @@queue << [response, sql, blk]
|
137
|
+
# return close
|
138
|
+
# end
|
139
|
+
|
140
|
+
@processing = true
|
141
|
+
|
142
|
+
log 'mysql sending', sql
|
143
|
+
@mysql.send_query(sql)
|
144
|
+
else
|
145
|
+
@@queue << [response, sql, cblk, eblk, retries]
|
146
|
+
return
|
147
|
+
end
|
148
|
+
rescue Mysql::Error => e
|
149
|
+
log 'mysql error', e.message
|
150
|
+
if DisconnectErrors.include? e.message
|
151
|
+
@@queue << [response, sql, cblk, eblk, retries]
|
152
|
+
return close
|
153
|
+
else
|
154
|
+
raise e
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
log 'queuing', response, sql
|
159
|
+
@current = [Time.now, response, sql, cblk, eblk, retries]
|
160
|
+
end
|
161
|
+
|
162
|
+
def close
|
163
|
+
@connected = false
|
164
|
+
fd = detach
|
165
|
+
log 'detached fd', fd
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def next_query
|
171
|
+
if @connected and !@processing and pending = @@queue.shift
|
172
|
+
response, sql, cblk, eblk = pending
|
173
|
+
execute(sql, response, cblk, eblk)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def log *args
|
178
|
+
return unless @opts[:logging]
|
179
|
+
p [Time.now, @fd, (@signature[-4..-1] if @signature), *args]
|
180
|
+
end
|
181
|
+
|
182
|
+
public
|
183
|
+
|
184
|
+
def self.connect opts
|
185
|
+
unless EM.respond_to?(:watch) and Mysql.method_defined?(:socket)
|
186
|
+
raise RuntimeError, 'mysqlplus and EM.watch are required for EventedMysql'
|
187
|
+
end
|
188
|
+
|
189
|
+
opts = servers_opts.pop
|
190
|
+
opts[:failover] = servers_opts
|
191
|
+
if conn = _connect(opts)
|
192
|
+
EM.watch conn.socket, self, conn, opts
|
193
|
+
else
|
194
|
+
EM.add_timer(5){ connect opts }
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
self::Mysql = ::Mysql unless defined? self::Mysql
|
199
|
+
|
200
|
+
# stolen from sequel
|
201
|
+
def self._connect opts
|
202
|
+
opts = settings.merge(opts)
|
203
|
+
|
204
|
+
conn = Mysql.init
|
205
|
+
|
206
|
+
# set encoding _before_ connecting
|
207
|
+
if charset = opts[:charset] || opts[:encoding]
|
208
|
+
conn.options(Mysql::SET_CHARSET_NAME, charset)
|
209
|
+
end
|
210
|
+
|
211
|
+
conn.options(Mysql::OPT_LOCAL_INFILE, 'client')
|
212
|
+
|
213
|
+
conn.real_connect(
|
214
|
+
opts[:host] || 'localhost',
|
215
|
+
opts[:user] || 'root',
|
216
|
+
opts[:password],
|
217
|
+
opts[:database],
|
218
|
+
opts[:port],
|
219
|
+
opts[:socket],
|
220
|
+
0 +
|
221
|
+
# XXX multi results require multiple callbacks to parse
|
222
|
+
# Mysql::CLIENT_MULTI_RESULTS +
|
223
|
+
# Mysql::CLIENT_MULTI_STATEMENTS +
|
224
|
+
(opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
|
225
|
+
)
|
226
|
+
|
227
|
+
# increase timeout so mysql server doesn't disconnect us
|
228
|
+
# this is especially bad if we're disconnected while EM.attach is
|
229
|
+
# still in progress, because by the time it gets to EM, the FD is
|
230
|
+
# no longer valid, and it throws a c++ 'bad file descriptor' error
|
231
|
+
# (do not use a timeout of -1 for unlimited, it does not work on mysqld > 5.0.60)
|
232
|
+
conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
|
233
|
+
|
234
|
+
# we handle reconnecting (and reattaching the new fd to EM)
|
235
|
+
conn.reconnect = false
|
236
|
+
|
237
|
+
# By default, MySQL 'where id is null' selects the last inserted id
|
238
|
+
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
239
|
+
conn.query("set SQL_AUTO_IS_NULL=0")
|
240
|
+
|
241
|
+
# get results for queries
|
242
|
+
conn.query_with_result = true
|
243
|
+
|
244
|
+
conn
|
245
|
+
rescue Mysql::Error => e
|
246
|
+
if cb = opts[:on_error]
|
247
|
+
cb.call(e)
|
248
|
+
nil
|
249
|
+
elsif opts[:failover]
|
250
|
+
connect(opts[:failover])
|
251
|
+
else
|
252
|
+
raise e
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class EventedMysql
|
258
|
+
def self.settings
|
259
|
+
@settings ||= { :connections => 4, :logging => false }
|
260
|
+
end
|
261
|
+
|
262
|
+
def self.execute query, type = nil, cblk = nil, eblk = nil, &blk
|
263
|
+
unless nil#connection = connection_pool.find{|c| not c.processing and c.connected }
|
264
|
+
@n ||= 0
|
265
|
+
connection = connection_pool[@n]
|
266
|
+
@n = 0 if (@n+=1) >= connection_pool.size
|
267
|
+
end
|
268
|
+
|
269
|
+
connection.execute(query, type, cblk, eblk, &blk)
|
270
|
+
end
|
271
|
+
|
272
|
+
%w[ select insert update raw ].each do |type| class_eval %[
|
273
|
+
|
274
|
+
def self.#{type} query, cblk = nil, eblk = nil, &blk
|
275
|
+
execute query, :#{type}, cblk, eblk, &blk
|
276
|
+
end
|
277
|
+
|
278
|
+
] end
|
279
|
+
|
280
|
+
def self.all query, type = nil, &blk
|
281
|
+
responses = 0
|
282
|
+
connection_pool.each do |c|
|
283
|
+
c.execute(query, type) do
|
284
|
+
responses += 1
|
285
|
+
blk.call if blk and responses == @connection_pool.size
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def self.connection_pool
|
291
|
+
@connection_pool ||= (1..settings[:connections]).map{ EventedMysql.connect(settings) }
|
292
|
+
# p ['connpool', settings[:connections], @connection_pool.size]
|
293
|
+
# (1..(settings[:connections]-@connection_pool.size)).each do
|
294
|
+
# @connection_pool << EventedMysql.connect(settings)
|
295
|
+
# end unless settings[:connections] == @connection_pool.size
|
296
|
+
# @connection_pool
|
297
|
+
end
|
298
|
+
|
299
|
+
def self.reset!
|
300
|
+
@connection_pool.each do |c|
|
301
|
+
c.close
|
302
|
+
end
|
303
|
+
@connection_pool = nil
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
data/lib/sequel/async.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
# async sequel extensions, for use with em-mysql
|
2
|
+
#
|
3
|
+
# require 'em/mysql'
|
4
|
+
# DB = Sequel.connect(:adapter => 'mysql', :user => 'root', :database => 'test', ...)
|
5
|
+
# EventedMysql.settings.update(..., :on_error => proc{|e| log 'error', e })
|
6
|
+
#
|
7
|
+
# def log *args
|
8
|
+
# p [Time.now, *args]
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# DB[:table].where(:id < 100).async_update(:field => 'new value') do |num_updated|
|
12
|
+
# log "done updating #{num_updated} rows"
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# DB[:table].async_insert(:field => 'value') do |insert_id|
|
16
|
+
# log "inserted row #{insert_id}"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# DB[:table].async_multi_insert([:field], [ ['one'], ['two'], ['three'] ]) do
|
20
|
+
# log "done inserting 3 rows"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# DB[:table].limit(10).async_each do |row|
|
24
|
+
# log "got a row", row
|
25
|
+
# end; log "this will be printed before the query returns"
|
26
|
+
#
|
27
|
+
# DB[:table].async_all do |rows|
|
28
|
+
# DB[:table].async_multi_insert([:field], rows.map{|r| "new_#{r[:field]}" })
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# DB[:table].async_all do |rows|
|
32
|
+
# num = rows.size
|
33
|
+
#
|
34
|
+
# rows.each{ |r|
|
35
|
+
# DB[:table].where(:id => r[:id]).async_update(:field => rand(10000).to_s) do
|
36
|
+
# num = num-1
|
37
|
+
# if num == 0
|
38
|
+
# log "last update completed"
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
# }
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# DB[:table].async_count do |num_rows|
|
45
|
+
# log "table has #{num_rows} rows"
|
46
|
+
# end
|
47
|
+
|
48
|
+
require 'sequel'
|
49
|
+
raise 'need Sequel >= 3.2.0' unless Sequel::MAJOR == 3 and Sequel::MINOR >= 2
|
50
|
+
|
51
|
+
module Sequel
|
52
|
+
class Dataset
|
53
|
+
def async_insert *args, &cb
|
54
|
+
EventedMysql.insert insert_sql(*args), &cb
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def async_insert_ignore *args, &cb
|
59
|
+
EventedMysql.insert insert_ignore.insert_sql(*args), &cb
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def async_update *args, &cb
|
64
|
+
EventedMysql.update update_sql(*args), &cb
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def async_delete &cb
|
69
|
+
EventedMysql.execute delete_sql, &cb
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def async_multi_insert *args, &cb
|
74
|
+
EventedMysql.execute multi_insert_sql(*args).first, &cb
|
75
|
+
nil
|
76
|
+
end
|
77
|
+
|
78
|
+
def async_multi_insert_ignore *args, &cb
|
79
|
+
EventedMysql.execute insert_ignore.multi_insert_sql(*args).first, &cb
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def async_fetch_rows sql, iter = :each
|
84
|
+
EventedMysql.raw(sql) do |m|
|
85
|
+
r = m.result
|
86
|
+
|
87
|
+
i = -1
|
88
|
+
cols = r.fetch_fields.map{|f| [output_identifier(f.name), Sequel::MySQL::MYSQL_TYPES[f.type], i+=1]}
|
89
|
+
@columns = cols.map{|c| c.first}
|
90
|
+
rows = []
|
91
|
+
while row = r.fetch_row
|
92
|
+
h = {}
|
93
|
+
cols.each{|n, p, i| v = row[i]; h[n] = (v && p) ? p.call(v) : v}
|
94
|
+
if iter == :each
|
95
|
+
yield h
|
96
|
+
else
|
97
|
+
rows << h
|
98
|
+
end
|
99
|
+
end
|
100
|
+
yield rows if iter == :all
|
101
|
+
end
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
def async_each
|
106
|
+
async_fetch_rows(select_sql, :each) do |r|
|
107
|
+
if row_proc = @row_proc
|
108
|
+
yield row_proc.call(r)
|
109
|
+
else
|
110
|
+
yield r
|
111
|
+
end
|
112
|
+
end
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
|
116
|
+
def async_all
|
117
|
+
async_fetch_rows(sql, :all) do |rows|
|
118
|
+
if row_proc = @row_proc
|
119
|
+
yield(rows.map{|r| row_proc.call(r) })
|
120
|
+
else
|
121
|
+
yield(rows)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
def async_count &cb
|
128
|
+
if options_overlap(COUNT_FROM_SELF_OPTS)
|
129
|
+
from_self.async_count(&cb)
|
130
|
+
else
|
131
|
+
clone(STOCK_COUNT_OPTS).async_each{|r|
|
132
|
+
yield r.is_a?(Hash) ? r.values.first.to_i : r.values.values.first.to_i
|
133
|
+
}
|
134
|
+
end
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
class Model
|
140
|
+
def async_update *args, &cb
|
141
|
+
this.async_update(*args, &cb)
|
142
|
+
set(*args)
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def async_delete &cb
|
147
|
+
this.async_delete(&cb)
|
148
|
+
nil
|
149
|
+
end
|
150
|
+
|
151
|
+
class << self
|
152
|
+
[ :async_insert,
|
153
|
+
:async_insert_ignore,
|
154
|
+
:async_multi_insert,
|
155
|
+
:async_multi_insert_ignore,
|
156
|
+
:async_each,
|
157
|
+
:async_all,
|
158
|
+
:async_update,
|
159
|
+
:async_count ].each do |method|
|
160
|
+
class_eval %[
|
161
|
+
def #{method} *args, &cb
|
162
|
+
dataset.#{method}(*args, &cb)
|
163
|
+
end
|
164
|
+
]
|
165
|
+
end
|
166
|
+
|
167
|
+
# async version of Model#[]
|
168
|
+
def async_lookup args
|
169
|
+
unless Hash === args
|
170
|
+
args = primary_key_hash(args)
|
171
|
+
end
|
172
|
+
|
173
|
+
dataset.where(args).limit(1).async_all{ |rows|
|
174
|
+
if rows.any?
|
175
|
+
yield rows.first
|
176
|
+
else
|
177
|
+
yield nil
|
178
|
+
end
|
179
|
+
}
|
180
|
+
nil
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
data/test.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'lib/em/mysql'
|
2
|
+
|
3
|
+
# EM.kqueue
|
4
|
+
# EM.epoll
|
5
|
+
EM.run{
|
6
|
+
EM.start_server '127.0.0.1', 12345 do |c|
|
7
|
+
def c.receive_data data
|
8
|
+
p 'sending http response'
|
9
|
+
send_data "hello"
|
10
|
+
close_connection_after_writing
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
SQL = EventedMysql
|
15
|
+
def SQL(query, &blk) SQL.select(query, &blk) end
|
16
|
+
|
17
|
+
if false
|
18
|
+
|
19
|
+
SQL.settings.update :logging => true,
|
20
|
+
:database => 'test',
|
21
|
+
:connections => 1
|
22
|
+
|
23
|
+
SQL.execute('select 1+2')
|
24
|
+
|
25
|
+
EM.add_timer(1){
|
26
|
+
3.times do SQL.select('select sleep(0.5)+1'){|r| p(r) } end
|
27
|
+
}
|
28
|
+
|
29
|
+
elsif false
|
30
|
+
|
31
|
+
SQL.settings.update :logging => true,
|
32
|
+
:database => 'test',
|
33
|
+
:connections => 10
|
34
|
+
|
35
|
+
EM.add_timer(2.5){ SQL.all('use test') }
|
36
|
+
|
37
|
+
else
|
38
|
+
|
39
|
+
SQL.settings.update :logging => true,
|
40
|
+
:database => 'test',
|
41
|
+
:connections => 10,
|
42
|
+
:timeout => 1
|
43
|
+
|
44
|
+
n = 0
|
45
|
+
|
46
|
+
SQL.execute('drop table if exists testingabc'){
|
47
|
+
SQL.execute('create table testingabc (a int, b int, c int)'){
|
48
|
+
EM.add_periodic_timer(0.2) do
|
49
|
+
cur_num = n+=1
|
50
|
+
SQL.execute("insert into testingabc values (1,2,#{cur_num})"){
|
51
|
+
SQL("select * from testingabc where c = #{cur_num} limit 1"){ |res| puts;puts }
|
52
|
+
}
|
53
|
+
end
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
}
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: superfeedr-em-mysql
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aman Gupta
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-06-23 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: eventmachine
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.12.9
|
24
|
+
version:
|
25
|
+
description: Async MySQL client API for Ruby/EventMachine
|
26
|
+
email: em-mysql@tmm1.net
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- README
|
35
|
+
- em-mysql.gemspec
|
36
|
+
- lib/em/mysql.rb
|
37
|
+
- lib/sequel/async.rb
|
38
|
+
- test.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://github.com/tmm1/em-mysql
|
41
|
+
licenses: []
|
42
|
+
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: "0"
|
53
|
+
version:
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: "0"
|
59
|
+
version:
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.5
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Async MySQL client API for Ruby/EventMachine
|
67
|
+
test_files: []
|
68
|
+
|