memcache-client 1.8.1 → 1.8.2

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.
@@ -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