tmm1-em-mysql 0.2.0 → 0.3.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.
Files changed (4) hide show
  1. data/em-mysql.gemspec +21 -0
  2. data/lib/em/mysql.rb +55 -29
  3. data/lib/sequel/async.rb +25 -21
  4. metadata +4 -3
@@ -0,0 +1,21 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'em-mysql'
3
+ s.version = '0.3.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.8')
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
@@ -1,27 +1,31 @@
1
1
  require 'rubygems'
2
2
  require 'eventmachine'
3
3
  require 'mysqlplus'
4
+ require 'fcntl'
5
+
6
+ class Mysql
7
+ def result
8
+ @cur_result
9
+ end
10
+ end
4
11
 
5
12
  class EventedMysql < EM::Connection
6
13
  def initialize mysql, opts
7
14
  @mysql = mysql
8
15
  @fd = mysql.socket
9
16
  @opts = opts
10
- @queue = []
11
- @pending = []
17
+ @current = nil
18
+ @@queue ||= []
12
19
  @processing = false
13
20
  @connected = true
14
21
 
15
22
  log 'mysql connected'
23
+ make_socket_blocking
24
+ EM.add_timer(0){ next_query }
16
25
  end
17
26
  attr_reader :processing, :connected, :opts
18
27
  alias :settings :opts
19
28
 
20
- def connection_completed
21
- @connected = true
22
- next_query
23
- end
24
-
25
29
  DisconnectErrors = [
26
30
  'query: not connected',
27
31
  'MySQL server has gone away',
@@ -30,11 +34,14 @@ class EventedMysql < EM::Connection
30
34
 
31
35
  def notify_readable
32
36
  log 'readable'
33
- if item = @queue.shift
37
+ if item = @current
38
+ @current = nil
34
39
  start, response, sql, cblk, eblk = item
35
40
  log 'mysql response', Time.now-start, sql
36
41
  arg = case response
37
42
  when :raw
43
+ result = @mysql.get_result
44
+ @mysql.instance_variable_set('@cur_result', result)
38
45
  @mysql
39
46
  when :select
40
47
  ret = []
@@ -64,8 +71,12 @@ class EventedMysql < EM::Connection
64
71
  end
65
72
  rescue Mysql::Error => e
66
73
  log 'mysql error', e.message
67
- if DisconnectErrors.include? e.message
68
- @pending << [response, sql, cblk, eblk]
74
+ if e.message =~ /Deadlock/
75
+ @@queue << [response, sql, cblk, eblk]
76
+ @processing = false
77
+ next_query
78
+ elsif DisconnectErrors.include? e.message
79
+ @@queue << [response, sql, cblk, eblk]
69
80
  return close
70
81
  elsif cb = (eblk || @opts[:on_error])
71
82
  cb.call(e)
@@ -101,6 +112,9 @@ class EventedMysql < EM::Connection
101
112
  @signature = EM.attach_fd @mysql.socket, true, false
102
113
  log 'mysql connected'
103
114
  EM.instance_variable_get('@conns')[@signature] = self
115
+ @connected = true
116
+ make_socket_blocking
117
+ next_query
104
118
  end
105
119
  end
106
120
 
@@ -115,7 +129,7 @@ class EventedMysql < EM::Connection
115
129
  # # log 'mysql errno', @mysql.errno
116
130
  # rescue
117
131
  # log 'mysql ping failed'
118
- # @pending << [response, sql, blk]
132
+ # @@queue << [response, sql, blk]
119
133
  # return close
120
134
  # end
121
135
 
@@ -124,13 +138,13 @@ class EventedMysql < EM::Connection
124
138
  log 'mysql sending', sql
125
139
  @mysql.send_query(sql)
126
140
  else
127
- @pending << [response, sql, cblk, eblk]
141
+ @@queue << [response, sql, cblk, eblk]
128
142
  return
129
143
  end
130
144
  rescue Mysql::Error => e
131
145
  log 'mysql error', e.message
132
146
  if DisconnectErrors.include? e.message
133
- @pending << [response, sql, cblk, eblk]
147
+ @@queue << [response, sql, cblk, eblk]
134
148
  return close
135
149
  else
136
150
  raise e
@@ -138,7 +152,7 @@ class EventedMysql < EM::Connection
138
152
  end
139
153
 
140
154
  log 'queuing', response, sql
141
- @queue << [Time.now, response, sql, cblk, eblk]
155
+ @current = [Time.now, response, sql, cblk, eblk]
142
156
  end
143
157
 
144
158
  def close
@@ -148,13 +162,23 @@ class EventedMysql < EM::Connection
148
162
  # EM.add_timer(0){ close_connection }
149
163
  # close_connection
150
164
  fd = detach
165
+ @io.close if @io
166
+ @io = nil
151
167
  log 'detached fd', fd
152
168
  end
153
169
 
154
170
  private
155
171
 
172
+ def make_socket_blocking
173
+ if defined?(Fcntl::F_GETFL)
174
+ @io = IO.for_fd(@mysql.socket)
175
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
176
+ @io.fcntl(Fcntl::F_SETFL, ~Fcntl::O_NONBLOCK & m)
177
+ end
178
+ end
179
+
156
180
  def next_query
157
- if @connected and !@processing and pending = @pending.shift
181
+ if @connected and !@processing and pending = @@queue.shift
158
182
  response, sql, cblk, eblk = pending
159
183
  execute(sql, response, cblk, eblk)
160
184
  end
@@ -205,9 +229,7 @@ class EventedMysql < EM::Connection
205
229
  # XXX multi results require multiple callbacks to parse
206
230
  # Mysql::CLIENT_MULTI_RESULTS +
207
231
  # Mysql::CLIENT_MULTI_STATEMENTS +
208
-
209
- # XXX this should check for opts[:compression]
210
- Mysql::CLIENT_COMPRESS
232
+ (opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
211
233
  )
212
234
 
213
235
  # increase timeout so mysql server doesn't disconnect us
@@ -227,16 +249,6 @@ class EventedMysql < EM::Connection
227
249
  # get results for queries
228
250
  conn.query_with_result = true
229
251
 
230
- # if encoding = opts[:encoding] || opts[:charset]
231
- # conn.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
232
- # conn.query("set names '#{encoding}'")
233
- # conn.query("set character_set_connection = '#{encoding}'")
234
- # conn.query("set character_set_client = '#{encoding}'")
235
- # conn.query("set character_set_database = '#{encoding}'")
236
- # conn.query("set character_set_server = '#{encoding}'")
237
- # conn.query("set character_set_results = '#{encoding}'")
238
- # end
239
-
240
252
  conn
241
253
  rescue Mysql::Error => e
242
254
  if cb = opts[:on_error]
@@ -362,7 +374,7 @@ if __FILE__ == $0 and require 'em/spec'
362
374
  should 'have raw mode which yields the mysql object' do
363
375
  @mysql.execute('select 1+2 as num', :raw){ |mysql|
364
376
  mysql.should.is_a? Mysql
365
- mysql.get_result.all_hashes.should == [{'num' => '3'}]
377
+ mysql.result.all_hashes.should == [{'num' => '3'}]
366
378
  done
367
379
  }
368
380
  end
@@ -446,6 +458,20 @@ if __FILE__ == $0 and require 'em/spec'
446
458
  }
447
459
  end
448
460
 
461
+ should 'allow access to insert_id in raw mode' do
462
+ SQL.raw('insert into evented_mysql_test (num) values (20), (21), (22)'){ |mysql|
463
+ mysql.insert_id.should == 4
464
+ done
465
+ }
466
+ end
467
+
468
+ should 'allow access to affected_rows in raw mode' do
469
+ SQL.raw('update evented_mysql_test set num = num + 10'){ |mysql|
470
+ mysql.affected_rows.should == 6
471
+ done
472
+ }
473
+ end
474
+
449
475
  should 'fire error callback with exceptions' do
450
476
  SQL.settings.update :on_error => proc{ |e|
451
477
  e.class.should == Mysql::Error
@@ -1,14 +1,14 @@
1
1
  # async sequel extensions, for use with em-mysql
2
2
  #
3
3
  # require 'em/mysql'
4
- # DB = Sequel.connect(...)
5
- # ADB = EventedMysql.connect(..., :on_error => proc{|e| log 'error', e })
4
+ # DB = Sequel.connect(:adapter => 'mysql', :user => 'root', :database => 'test', ...)
5
+ # EventedMysql.settings.update(..., :on_error => proc{|e| log 'error', e })
6
6
  #
7
7
  # def log *args
8
8
  # p [Time.now, *args]
9
9
  # end
10
10
  #
11
- # DB[:table].where(:id < 100).async_update do |num_updated|
11
+ # DB[:table].where(:id < 100).async_update(:field => 'new value') do |num_updated|
12
12
  # log "done updating #{num_updated} rows"
13
13
  # end
14
14
  #
@@ -48,49 +48,52 @@
48
48
  module Sequel
49
49
  class Dataset
50
50
  def async_insert *args, &cb
51
- ADB.insert insert_sql(*args), &cb
51
+ EventedMysql.insert insert_sql(*args), &cb
52
+ nil
53
+ end
54
+
55
+ def async_insert_ignore *args, &cb
56
+ EventedMysql.insert insert_sql(*args).sub(/insert/i, 'INSERT IGNORE'), &cb
52
57
  nil
53
58
  end
54
59
 
55
60
  def async_update *args, &cb
56
- ADB.update update_sql(*args), &cb
61
+ EventedMysql.update update_sql(*args), &cb
57
62
  nil
58
63
  end
59
64
 
60
65
  def async_delete &cb
61
- ADB.execute delete_sql, &cb
66
+ EventedMysql.execute delete_sql, &cb
62
67
  nil
63
68
  end
64
69
 
65
70
  def async_multi_insert *args, &cb
66
- ADB.execute multi_insert_sql(*args).first, &cb
71
+ EventedMysql.execute multi_insert_sql(*args).first, &cb
67
72
  nil
68
73
  end
69
74
 
70
75
  def async_multi_insert_ignore *args, &cb
71
- ADB.execute multi_insert_sql(*args).first.sub(/insert/i, "INSERT IGNORE"), &cb
76
+ EventedMysql.execute multi_insert_sql(*args).first.sub(/insert/i, "INSERT IGNORE"), &cb
72
77
  nil
73
78
  end
74
79
 
75
- def async_each *args
76
- ADB.select(select_sql(*args)) do |rows|
80
+ def async_each
81
+ EventedMysql.select(select_sql) do |rows|
77
82
  rows.each{|r|
78
- r = transform_load(r) if @transform
79
- r = row_proc[r] if row_proc
80
- yield r
83
+ if row_proc = @row_proc
84
+ yield row_proc.call(r)
85
+ else
86
+ yield r
87
+ end
81
88
  }
82
89
  end
83
90
  nil
84
91
  end
85
92
 
86
93
  def async_all
87
- ADB.select(sql) do |rows|
88
- if row_proc or transform
89
- yield(rows.map{|r|
90
- r = transform_load(r) if @transform
91
- r = row_proc[r] if row_proc
92
- r
93
- })
94
+ EventedMysql.select(sql) do |rows|
95
+ if row_proc = @row_proc
96
+ yield(rows.map{|r| row_proc.call(r) })
94
97
  else
95
98
  yield(rows)
96
99
  end
@@ -102,7 +105,7 @@ module Sequel
102
105
  if options_overlap(COUNT_FROM_SELF_OPTS)
103
106
  from_self.async_count(&cb)
104
107
  else
105
- naked.async_each(STOCK_COUNT_OPTS){|r|
108
+ clone(STOCK_COUNT_OPTS).async_each{|r|
106
109
  yield r.values.first.to_i
107
110
  }
108
111
  end
@@ -124,6 +127,7 @@ module Sequel
124
127
 
125
128
  class << self
126
129
  [ :async_insert,
130
+ :async_insert_ignore,
127
131
  :async_multi_insert,
128
132
  :async_multi_insert_ignore,
129
133
  :async_each,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmm1-em-mysql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-18 00:00:00 -08:00
12
+ date: 2009-06-23 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.12.4
23
+ version: 0.12.8
24
24
  version:
25
25
  description: Async MySQL client API for Ruby/EventMachine
26
26
  email: em-mysql@tmm1.net
@@ -32,6 +32,7 @@ extra_rdoc_files: []
32
32
 
33
33
  files:
34
34
  - README
35
+ - em-mysql.gemspec
35
36
  - lib/em/mysql.rb
36
37
  - lib/sequel/async.rb
37
38
  - test.rb