memcache-client 1.8.1 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,7 @@
1
+ = 1.8.2 (2010-04-03)
2
+
3
+ * Fix concurrency issues with eventmachine support.
4
+
1
5
  = 1.8.1 (2010-03-20)
2
6
 
3
7
  * Only require SystemTimer if the Ruby VM looks like MRI.
@@ -110,7 +110,7 @@ class MemCache
110
110
  # Please note this feature only works in memcached 1.2.5 and later. Earlier
111
111
  # versions will reply with "ERROR".
112
112
  attr_reader :no_reply
113
-
113
+
114
114
  ##
115
115
  # Accepts a list of +servers+ and a list of +opts+. +servers+ may be
116
116
  # omitted. See +servers=+ for acceptable server list arguments.
@@ -154,10 +154,11 @@ class MemCache
154
154
  raise ArgumentError, "wrong number of arguments (#{args.length} for 2)"
155
155
  end
156
156
 
157
+ @evented = defined?(EM) && EM.reactor_running?
157
158
  opts = DEFAULT_OPTIONS.merge opts
158
159
  @namespace = opts[:namespace]
159
160
  @readonly = opts[:readonly]
160
- @multithread = opts[:multithread]
161
+ @multithread = opts[:multithread] && !@evented
161
162
  @autofix_keys = opts[:autofix_keys]
162
163
  @timeout = opts[:timeout]
163
164
  @failover = opts[:failover]
@@ -170,6 +171,7 @@ class MemCache
170
171
  logger.info { "memcache-client #{VERSION} #{Array(servers).inspect}" } if logger
171
172
 
172
173
  Thread.current[:memcache_client] = self.object_id if !@multithread
174
+
173
175
 
174
176
  self.servers = servers
175
177
  end
@@ -946,6 +948,7 @@ class MemCache
946
948
 
947
949
  def check_multithread_status!
948
950
  return if @multithread
951
+ return if @evented
949
952
 
950
953
  if Thread.current[:memcache_client] != self.object_id
951
954
  raise MemCacheError, <<-EOM
@@ -1011,6 +1014,8 @@ class MemCache
1011
1014
  @status = 'NOT CONNECTED'
1012
1015
  @timeout = memcache.timeout
1013
1016
  @logger = memcache.logger
1017
+
1018
+ self.extend(MemCache::EventedServer) if defined?(EM) and EM.reactor_running?
1014
1019
  end
1015
1020
 
1016
1021
  ##
@@ -1101,8 +1106,7 @@ class MemCache
1101
1106
  # Mark the server as dead and close its socket.
1102
1107
 
1103
1108
  def mark_dead(error)
1104
- @sock.close if @sock && !@sock.closed?
1105
- @sock = nil
1109
+ close
1106
1110
  @retry = Time.now + RETRY_DELAY
1107
1111
 
1108
1112
  reason = "#{error.class.name}: #{error.message}"
@@ -2,42 +2,62 @@
2
2
 
3
3
  raise "memcache/event_machine requires Ruby 1.9" if RUBY_VERSION < '1.9'
4
4
 
5
+ require 'memcache'
6
+ require 'eventmachine'
5
7
  require 'fiber'
6
8
 
7
- class MemCache::Server
9
+ class MemCache
8
10
 
9
- alias :blocking_socket :socket
10
-
11
- def socket
12
- # Support plain old TCP socket connections if the user
13
- # has not setup EM. Much easier to deal with in irb,
14
- # script/console, etc.
15
- return blocking_socket if !EM.reactor_running?
11
+ # Since we are working in a single Thread, multiple Fiber environment,
12
+ # disable the multithread Mutex as it will not work.
13
+ # DEFAULT_OPTIONS[:multithread] = false
16
14
 
17
- return @sock if @sock and not @sock.closed?
15
+ module EventedServer
18
16
 
19
- @sock = nil
17
+ def fiber_key
18
+ @fiber_key ||= "memcached-#{@host}-#{@port}"
19
+ end
20
+
21
+ def socket
22
+ sock = Thread.current[fiber_key]
23
+ return sock if sock and not sock.closed?
20
24
 
21
- # If the host was dead, don't retry for a while.
22
- return if @retry and @retry > Time.now
25
+ Thread.current[fiber_key] = nil
26
+
27
+ # If the host was dead, don't retry for a while.
28
+ return if @retry and @retry > Time.now
23
29
 
24
- fiber = Fiber.current
25
- @sock = EM::SocketConnection.connect(@host, @port, @timeout)
26
- yielding = true
27
- @sock.callback do
28
- @status = 'CONNECTED'
29
- @retry = nil
30
- yielding = false
31
- fiber.resume if Fiber.current != fiber
30
+ Thread.current[fiber_key] ||= begin
31
+ sock = EM::SocketConnection.connect(@host, @port, @timeout)
32
+ yielding = true
33
+ fiber = Fiber.current
34
+ sock.callback do
35
+ @status = 'CONNECTED'
36
+ @retry = nil
37
+ yielding = false
38
+ fiber.resume if Fiber.current != fiber
39
+ end
40
+ sock.errback do
41
+ sock = nil
42
+ yielding = false
43
+ fiber.resume if Fiber.current != fiber
44
+ end
45
+ Fiber.yield if yielding
46
+ sock
47
+ end
32
48
  end
33
- @sock.errback do
34
- yielding = false
35
- fiber.resume if Fiber.current != fiber
49
+
50
+ def close
51
+ sock = Thread.current[fiber_key]
52
+ if sock
53
+ sock.close if !sock.closed?
54
+ Thread.current[fiber_key] = nil
55
+ end
56
+ @retry = nil
57
+ @status = "NOT CONNECTED"
36
58
  end
37
- Fiber.yield if yielding
38
- @sock
59
+
39
60
  end
40
-
41
61
  end
42
62
 
43
63
  module EM
@@ -127,7 +147,11 @@ module EM
127
147
  end
128
148
 
129
149
  def unbind
130
- @connected = false
150
+ if @connected
151
+ @connected = false
152
+ else
153
+ fail
154
+ end
131
155
  end
132
156
 
133
157
  private
@@ -2,5 +2,5 @@ class MemCache
2
2
  ##
3
3
  # The version of MemCache you are using.
4
4
 
5
- VERSION = "1.8.1"
5
+ VERSION = "1.8.2"
6
6
  end
@@ -4,6 +4,52 @@ require 'memcache'
4
4
 
5
5
  class TestEventMachine < Test::Unit::TestCase
6
6
 
7
+ def test_concurrent_fibers
8
+ return puts("Skipping EventMachine test, not Ruby 1.9") if RUBY_VERSION < '1.9'
9
+ return puts("Skipping EventMachine test, no live server") if !live_server?
10
+
11
+ require 'eventmachine'
12
+ require 'memcache/event_machine'
13
+ ex = nil
14
+ m = MemCache.new(['127.0.0.1:11211', 'localhost:11211'])
15
+ within_em(3) do
16
+ begin
17
+ key1 = 'foo'
18
+ key2 = 'bar'*50
19
+ key3 = '£∞'*45
20
+ value1 = 'abc'
21
+ value2 = 'xyz'*1000
22
+ value3 = '∞§¶•ª'*1000
23
+
24
+ 100.times do
25
+ assert_equal "STORED\r\n", m.set(key1, value1)
26
+ assert_equal "STORED\r\n", m.set(key2, value2)
27
+ assert_equal "STORED\r\n", m.set(key3, value3)
28
+ m.get(key1)
29
+ m.get(key2)
30
+ m.get(key3)
31
+ assert m.delete(key1)
32
+ assert_equal "STORED\r\n", m.set(key1, value2)
33
+ m.get(key1)
34
+ assert_equal "STORED\r\n", m.set(key2, value3)
35
+ m.get(key2)
36
+ assert_equal "STORED\r\n", m.set(key3, value1)
37
+ m.get(key3)
38
+ h = m.get_multi(key1, key2, key3)
39
+ assert h
40
+ assert_equal Hash, h.class
41
+ assert h.size > 0
42
+ end
43
+ rescue Exception => exp
44
+ puts exp.message
45
+ ex = exp
46
+ ensure
47
+ EM.stop
48
+ end
49
+ end
50
+ raise ex if ex
51
+ end
52
+
7
53
  def test_live_server
8
54
  return puts("Skipping EventMachine test, not Ruby 1.9") if RUBY_VERSION < '1.9'
9
55
  return puts("Skipping EventMachine test, no live server") if !live_server?
@@ -50,9 +96,11 @@ class TestEventMachine < Test::Unit::TestCase
50
96
 
51
97
  private
52
98
 
53
- def within_em(&block)
99
+ def within_em(count=1, &block)
54
100
  EM.run do
55
- Fiber.new(&block).resume
101
+ count.times do
102
+ Fiber.new(&block).resume
103
+ end
56
104
  end
57
105
  end
58
106
 
@@ -1201,6 +1201,7 @@ class TestMemCache < Test::Unit::TestCase
1201
1201
  # Use a null logger to verify logging doesn't blow up at runtime
1202
1202
  cache = MemCache.new(['localhost:11211', '127.0.0.1:11211'], :logger => Logger.new('/dev/null'))
1203
1203
  cache.flush_all
1204
+ assert_equal true, cache.multithread
1204
1205
  workers = []
1205
1206
 
1206
1207
  cache.set('f', 'zzz')
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 8
8
- - 1
9
- version: 1.8.1
8
+ - 2
9
+ version: 1.8.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Eric Hodel
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2010-03-20 00:00:00 -05:00
19
+ date: 2010-04-03 00:00:00 -05:00
20
20
  default_executable: memcached_top
21
21
  dependencies: []
22
22