jjp-memcache-client 1.8.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,173 @@
1
+ # Extensions for using memcache-client with EventMachine
2
+
3
+ raise "memcache/event_machine requires Ruby 1.9" if RUBY_VERSION < '1.9'
4
+
5
+ require 'memcache'
6
+ require 'eventmachine'
7
+ require 'fiber'
8
+
9
+ class MemCache
10
+
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
14
+
15
+ module EventedServer
16
+
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?
24
+
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
29
+
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
48
+ end
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"
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ module EM
64
+ module SocketConnection
65
+ include EM::Deferrable
66
+
67
+ def self.connect(host, port, timeout)
68
+ EM.connect(host, port, self) do |conn|
69
+ conn.pending_connect_timeout = timeout
70
+ end
71
+ end
72
+
73
+ def initialize
74
+ @connected = false
75
+ @index = 0
76
+ @buf = ''
77
+ end
78
+
79
+ def closed?
80
+ !@connected
81
+ end
82
+
83
+ def close
84
+ @connected = false
85
+ close_connection(true)
86
+ end
87
+
88
+ def write(buf)
89
+ send_data(buf)
90
+ end
91
+
92
+ def read(size)
93
+ if can_read?(size)
94
+ yank(size)
95
+ else
96
+ fiber = Fiber.current
97
+ @size = size
98
+ @callback = proc { |data|
99
+ fiber.resume(data)
100
+ }
101
+ # TODO Can leak fiber if the connection dies while
102
+ # this fiber is yielded, waiting for data
103
+ Fiber.yield
104
+ end
105
+ end
106
+
107
+ SEP = "\r\n"
108
+
109
+ def gets
110
+ while true
111
+ # Read to ensure we have some data in the buffer
112
+ line = read(2)
113
+ # Reset the buffer index to zero
114
+ @buf = @buf.slice(@index..-1)
115
+ @index = 0
116
+ if eol = @buf.index(SEP)
117
+ line << yank(eol + SEP.size)
118
+ break
119
+ else
120
+ # EOL not in the current buffer
121
+ line << yank(@buf.size)
122
+ end
123
+ end
124
+ line
125
+ end
126
+
127
+ def can_read?(size)
128
+ @buf.size >= @index + size
129
+ end
130
+
131
+ # EM callbacks
132
+
133
+ def receive_data(data)
134
+ @buf << data
135
+
136
+ if @callback and can_read?(@size)
137
+ callback = @callback
138
+ data = yank(@size)
139
+ @callback = @size = nil
140
+ callback.call(data)
141
+ end
142
+ end
143
+
144
+ def post_init
145
+ @connected = true
146
+ succeed
147
+ end
148
+
149
+ def unbind
150
+ if @connected
151
+ @connected = false
152
+ else
153
+ fail
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ BUFFER_SIZE = 4096
160
+
161
+ def yank(len)
162
+ data = @buf.slice(@index, len)
163
+ @index += len
164
+ @index = @buf.size if @index > @buf.size
165
+ if @index >= BUFFER_SIZE
166
+ @buf = @buf.slice(@index..-1)
167
+ @index = 0
168
+ end
169
+ data
170
+ end
171
+
172
+ end
173
+ end
@@ -0,0 +1,6 @@
1
+ class MemCache
2
+ ##
3
+ # The version of MemCache you are using.
4
+
5
+ VERSION = "1.8.5"
6
+ end
@@ -0,0 +1,105 @@
1
+ ##
2
+ # A utility wrapper around the MemCache client to simplify cache access. All
3
+ # methods silently ignore MemCache errors.
4
+ #
5
+ # This API is deprecated, please use the Rails.cache API or your own wrapper API
6
+ # around MemCache.
7
+
8
+ module Cache
9
+
10
+ ##
11
+ # Try to return a logger object that does not rely
12
+ # on ActiveRecord for logging.
13
+ def self.logger
14
+ @logger ||= if defined? Rails.logger # Rails 2.1 +
15
+ Rails.logger
16
+ elsif defined? RAILS_DEFAULT_LOGGER # Rails 1.2.2 +
17
+ RAILS_DEFAULT_LOGGER
18
+ else
19
+ ActiveRecord::Base.logger # ... very old Rails.
20
+ end
21
+ end
22
+ ##
23
+ # Returns the object at +key+ from the cache if successful, or nil if either
24
+ # the object is not in the cache or if there was an error attermpting to
25
+ # access the cache.
26
+ #
27
+ # If there is a cache miss and a block is given the result of the block will
28
+ # be stored in the cache with optional +expiry+, using the +add+ method rather
29
+ # than +set+.
30
+
31
+ def self.get(key, expiry = 0)
32
+ start_time = Time.now
33
+ value = CACHE.get key
34
+ elapsed = Time.now - start_time
35
+ logger.debug('MemCache Get (%0.6f) %s' % [elapsed, key])
36
+ if value.nil? and block_given? then
37
+ value = yield
38
+ add key, value, expiry
39
+ end
40
+ value
41
+ rescue MemCache::MemCacheError => err
42
+ logger.debug "MemCache Error: #{err.message}"
43
+ if block_given? then
44
+ value = yield
45
+ put key, value, expiry
46
+ end
47
+ value
48
+ end
49
+
50
+ ##
51
+ # Sets +value+ in the cache at +key+, with an optional +expiry+ time in
52
+ # seconds.
53
+
54
+ def self.put(key, value, expiry = 0)
55
+ start_time = Time.now
56
+ CACHE.set key, value, expiry
57
+ elapsed = Time.now - start_time
58
+ logger.debug('MemCache Set (%0.6f) %s' % [elapsed, key])
59
+ value
60
+ rescue MemCache::MemCacheError => err
61
+ ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
62
+ nil
63
+ end
64
+
65
+ ##
66
+ # Sets +value+ in the cache at +key+, with an optional +expiry+ time in
67
+ # seconds. If +key+ already exists in cache, returns nil.
68
+
69
+ def self.add(key, value, expiry = 0)
70
+ start_time = Time.now
71
+ response = CACHE.add key, value, expiry
72
+ elapsed = Time.now - start_time
73
+ logger.debug('MemCache Add (%0.6f) %s' % [elapsed, key])
74
+ (response == "STORED\r\n") ? value : nil
75
+ rescue MemCache::MemCacheError => err
76
+ ActiveRecord::Base.logger.debug "MemCache Error: #{err.message}"
77
+ nil
78
+ end
79
+
80
+ ##
81
+ # Deletes +key+ from the cache in +delay+ seconds.
82
+
83
+ def self.delete(key, delay = nil)
84
+ start_time = Time.now
85
+ CACHE.delete key, delay
86
+ elapsed = Time.now - start_time
87
+ logger.debug('MemCache Delete (%0.6f) %s' %
88
+ [elapsed, key])
89
+ nil
90
+ rescue MemCache::MemCacheError => err
91
+ logger.debug "MemCache Error: #{err.message}"
92
+ nil
93
+ end
94
+
95
+ ##
96
+ # Resets all connections to MemCache servers.
97
+
98
+ def self.reset
99
+ CACHE.reset
100
+ logger.debug 'MemCache Connections Reset'
101
+ nil
102
+ end
103
+
104
+ end
105
+
data/performance.txt ADDED
@@ -0,0 +1,143 @@
1
+ == 1.5.0, 1.8.6 (default in Rails 2.2 and lower)
2
+
3
+ user system total real
4
+ set:plain:memcache-client 41.550000 0.590000 42.140000 ( 43.740685)
5
+ set:ruby:memcache-client 41.540000 0.590000 42.130000 ( 43.733796)
6
+ get:plain:memcache-client 41.920000 0.610000 42.530000 ( 44.031005)
7
+ get:ruby:memcache-client 41.940000 0.600000 42.540000 ( 44.082447)
8
+ multiget:ruby:memcache-client 46.120000 0.440000 46.560000 ( 47.354041)
9
+ missing:ruby:memcache-client 41.490000 0.580000 42.070000 ( 43.610837)
10
+ mixed:ruby:memcache-client 83.820000 1.190000 85.010000 ( 88.117077)
11
+
12
+
13
+ == 1.7.0, timeout, 1.8.6 (closest to default in Rails 2.3)
14
+ user system total real
15
+ set:plain:memcache-client 4.320000 2.280000 6.600000 ( 7.102900)
16
+ set:ruby:memcache-client 4.400000 2.300000 6.700000 ( 6.856992)
17
+ get:plain:memcache-client 9.890000 6.830000 16.720000 ( 16.984208)
18
+ get:ruby:memcache-client 10.040000 6.890000 16.930000 ( 17.141128)
19
+ multiget:ruby:memcache-client 5.350000 4.110000 9.460000 ( 9.542898)
20
+ missing:ruby:memcache-client 4.710000 3.180000 7.890000 ( 8.030969)
21
+ mixed:ruby:memcache-client 14.540000 9.200000 23.740000 ( 24.121824)
22
+
23
+ == 1.7.0, timeout, system_timer, 1.8.6
24
+ user system total real
25
+ set:plain:memcache-client 3.840000 0.640000 4.480000 ( 4.643790)
26
+ set:ruby:memcache-client 3.930000 0.650000 4.580000 ( 4.731868)
27
+ get:plain:memcache-client 8.320000 1.290000 9.610000 ( 9.903877)
28
+ get:ruby:memcache-client 8.460000 1.310000 9.770000 ( 9.986694)
29
+ multiget:ruby:memcache-client 4.250000 0.560000 4.810000 ( 4.935326)
30
+ missing:ruby:memcache-client 3.840000 0.640000 4.480000 ( 4.569696)
31
+ mixed:ruby:memcache-client 12.400000 1.960000 14.360000 ( 14.857924)
32
+
33
+ == 1.7.0, timeout, 1.9.1
34
+ user system total real
35
+ set:plain:memcache-client 2.130000 2.150000 4.280000 ( 3.774238)
36
+ set:ruby:memcache-client 2.230000 2.230000 4.460000 ( 3.883686)
37
+ get:plain:memcache-client 4.030000 4.250000 8.280000 ( 6.702740)
38
+ get:ruby:memcache-client 4.090000 4.220000 8.310000 ( 6.749134)
39
+ multiget:ruby:memcache-client 1.960000 1.840000 3.800000 ( 3.089448)
40
+ missing:ruby:memcache-client 2.110000 2.210000 4.320000 ( 3.659019)
41
+ mixed:ruby:memcache-client 6.400000 6.560000 12.960000 ( 11.116317)
42
+
43
+ == 1.7.0, no timeout, 1.9.1
44
+ user system total real
45
+ set:plain:memcache-client 0.560000 0.320000 0.880000 ( 1.849380)
46
+ set:ruby:memcache-client 0.630000 0.320000 0.950000 ( 1.968208)
47
+ get:plain:memcache-client 0.640000 0.330000 0.970000 ( 1.962473)
48
+ get:ruby:memcache-client 0.690000 0.320000 1.010000 ( 2.002295)
49
+ multiget:ruby:memcache-client 0.460000 0.110000 0.570000 ( 0.885827)
50
+ missing:ruby:memcache-client 0.530000 0.320000 0.850000 ( 1.721371)
51
+ mixed:ruby:memcache-client 1.340000 0.660000 2.000000 ( 3.973213)
52
+
53
+ == 1.7.0, no timeout, 1.8.6
54
+ user system total real
55
+ set:plain:memcache-client 1.220000 0.310000 1.530000 ( 2.763310)
56
+ set:ruby:memcache-client 1.270000 0.300000 1.570000 ( 2.806251)
57
+ get:plain:memcache-client 1.400000 0.300000 1.700000 ( 2.944343)
58
+ get:ruby:memcache-client 1.450000 0.310000 1.760000 ( 2.997234)
59
+ multiget:ruby:memcache-client 1.120000 0.110000 1.230000 ( 1.665716)
60
+ missing:ruby:memcache-client 1.160000 0.300000 1.460000 ( 2.683376)
61
+ mixed:ruby:memcache-client 2.760000 0.610000 3.370000 ( 5.851047)
62
+
63
+ == 1.7.1, timeout, 1.8.6, raw + gets SystemTimer
64
+ user system total real
65
+ set:plain:memcache-client 2.670000 0.510000 3.180000 ( 3.489509)
66
+ set:ruby:memcache-client 2.810000 0.530000 3.340000 ( 3.675955)
67
+ get:plain:memcache-client 4.380000 0.720000 5.100000 ( 5.400587)
68
+ get:ruby:memcache-client 4.490000 0.730000 5.220000 ( 5.477543)
69
+ multiget:ruby:memcache-client 2.570000 0.310000 2.880000 ( 3.034944)
70
+ missing:ruby:memcache-client 2.800000 0.530000 3.330000 ( 3.547073)
71
+ mixed:ruby:memcache-client 7.460000 1.250000 8.710000 ( 9.272177)
72
+
73
+ == 1.7.1, timeout, 1.9.1, raw + gets Timeout
74
+ user system total real
75
+ set:plain:memcache-client 1.370000 1.300000 2.670000 ( 2.708669)
76
+ set:ruby:memcache-client 1.400000 1.240000 2.640000 ( 2.713737)
77
+ get:plain:memcache-client 2.070000 2.020000 4.090000 ( 3.950879)
78
+ get:ruby:memcache-client 2.160000 2.090000 4.250000 ( 3.924613)
79
+ multiget:ruby:memcache-client 1.080000 0.820000 1.900000 ( 1.744107)
80
+ missing:ruby:memcache-client 1.330000 1.270000 2.600000 ( 2.547597)
81
+ mixed:ruby:memcache-client 3.540000 3.270000 6.810000 ( 6.735349)
82
+
83
+ == 1.7.1, timeout, 1.8.6, raw + gets SystemTimer, native binary search
84
+ user system total real
85
+ set:plain:memcache-client 1.840000 0.450000 2.290000 ( 2.651285)
86
+ set:ruby:memcache-client 1.960000 0.460000 2.420000 ( 2.712650)
87
+ get:plain:memcache-client 3.180000 0.630000 3.810000 ( 4.079930)
88
+ get:ruby:memcache-client 3.290000 0.640000 3.930000 ( 4.242648)
89
+ multiget:ruby:memcache-client 1.640000 0.250000 1.890000 ( 2.003687)
90
+ missing:ruby:memcache-client 1.940000 0.450000 2.390000 ( 2.619675)
91
+ mixed:ruby:memcache-client 5.360000 1.100000 6.460000 ( 7.040998)
92
+
93
+ == 1.7.2, timeout, 1.8.6, SystemTimer, native binary search
94
+ user system total real
95
+ set:plain:memcache-client 3.260000 0.590000 3.850000 ( 4.067382)
96
+ set:ruby:memcache-client 3.370000 0.590000 3.960000 ( 4.364004)
97
+ get:plain:memcache-client 6.740000 1.240000 7.980000 ( 8.586676)
98
+ get:ruby:memcache-client 6.780000 1.210000 7.990000 ( 8.423400)
99
+ multiget:ruby:memcache-client 3.480000 0.540000 4.020000 ( 4.288633)
100
+ missing:ruby:memcache-client 3.250000 0.590000 3.840000 ( 4.043602)
101
+ mixed:ruby:memcache-client 10.150000 1.810000 11.960000 ( 12.372054)
102
+
103
+ == 1.7.4, 1.8.6, buffered and non-blocking IO
104
+ user system total real
105
+ set:plain:memcache-client 2.450000 0.790000 3.240000 ( 3.397091)
106
+ set:ruby:memcache-client 2.490000 0.790000 3.280000 ( 3.555436)
107
+ get:plain:memcache-client 2.840000 0.810000 3.650000 ( 3.759695)
108
+ get:ruby:memcache-client 2.890000 0.790000 3.680000 ( 3.778011)
109
+ multiget:ruby:memcache-client 1.380000 0.280000 1.660000 ( 1.695290)
110
+ missing:ruby:memcache-client 2.380000 0.780000 3.160000 ( 3.251136)
111
+ mixed:ruby:memcache-client 5.360000 1.600000 6.960000 ( 7.189314)
112
+
113
+ == memcached 0.13 + libmemcached 0.25.4 versus memcache-client 1.7.4
114
+
115
+ user system total real
116
+ set:plain:noblock:memcached 0.090000 0.030000 0.120000 ( 0.277929)
117
+ set:plain:memcached 0.220000 0.270000 0.490000 ( 1.251547)
118
+ set:plain:memcache-client 0.610000 0.270000 0.880000 ( 1.670718)
119
+ set:ruby:noblock:memcached 0.150000 0.020000 0.170000 ( 0.309201)
120
+ set:ruby:memcached 0.300000 0.290000 0.590000 ( 1.390354)
121
+ set:ruby:memcache-client 0.670000 0.270000 0.940000 ( 1.713558)
122
+ get:plain:memcached 0.240000 0.270000 0.510000 ( 1.169909)
123
+ get:plain:memcache-client 0.850000 0.270000 1.120000 ( 1.885270)
124
+ get:ruby:memcached 0.270000 0.280000 0.550000 ( 1.229705)
125
+ get:ruby:memcache-client 0.890000 0.260000 1.150000 ( 1.861660)
126
+ multiget:ruby:memcached 0.190000 0.090000 0.280000 ( 0.396264)
127
+ multiget:ruby:memcache-client 0.530000 0.100000 0.630000 ( 0.901016)
128
+ missing:ruby:memcached 0.280000 0.290000 0.570000 ( 1.254400)
129
+ missing:ruby:memcached:inline 0.300000 0.290000 0.590000 ( 1.235122)
130
+ missing:ruby:memcache-client 0.570000 0.250000 0.820000 ( 1.461293)
131
+ mixed:ruby:noblock:memcached 0.540000 0.620000 1.160000 ( 2.429200)
132
+ mixed:ruby:memcached 0.580000 0.570000 1.150000 ( 2.610819)
133
+ mixed:ruby:memcache-client 1.580000 0.540000 2.120000 ( 3.632775)
134
+
135
+ == 1.7.6, 1.8.7 64-bit (Snow Leopard), SystemTimer
136
+ user system total real
137
+ set:plain:memcache-client 3.070000 0.380000 3.450000 ( 3.643275)
138
+ set:ruby:memcache-client 3.140000 0.370000 3.510000 ( 3.698602)
139
+ get:plain:memcache-client 3.480000 0.360000 3.840000 ( 3.983941)
140
+ get:ruby:memcache-client 3.540000 0.360000 3.900000 ( 4.034308)
141
+ multiget:ruby:memcache-client 1.690000 0.140000 1.830000 ( 1.889290)
142
+ missing:ruby:memcache-client 3.070000 0.360000 3.430000 ( 3.571754)
143
+ mixed:ruby:memcache-client 6.720000 0.750000 7.470000 ( 7.838771)
@@ -0,0 +1,142 @@
1
+ require 'rubygems'
2
+ require 'benchmark'
3
+ require 'test/unit'
4
+
5
+ $TESTING = true
6
+ require 'memcache'
7
+
8
+ class TestBenchmark < Test::Unit::TestCase
9
+
10
+ def setup
11
+ puts "Testing #{MemCache::VERSION}"
12
+ # We'll use a simple @value to try to avoid spending time in Marshal,
13
+ # which is a constant penalty that both clients have to pay
14
+ @value = []
15
+ @marshalled = Marshal.dump(@value)
16
+
17
+ @opts = [
18
+ ['127.0.0.1:11211', 'localhost:11211'],
19
+ {
20
+ :namespace => "namespace",
21
+ # :no_reply => true,
22
+ # :timeout => nil,
23
+ }
24
+ ]
25
+ @key1 = "Short"
26
+ @key2 = "Sym1-2-3::45"*8
27
+ @key3 = "Long"*40
28
+ @key4 = "Medium"*8
29
+ # 5 and 6 are only used for multiget miss test
30
+ @key5 = "Medium2"*8
31
+ @key6 = "Long3"*40
32
+ end
33
+
34
+ def test_em
35
+ return if RUBY_VERSION < '1.9'
36
+ require 'eventmachine'
37
+ require 'memcache/event_machine'
38
+ puts "with EventMachine"
39
+ EM.run do
40
+ Fiber.new do
41
+ test_benchmark
42
+ EM.stop
43
+ end.resume
44
+ end
45
+ end
46
+
47
+ def test_benchmark
48
+ Benchmark.bm(31) do |x|
49
+
50
+ n = 2500
51
+
52
+ @m = MemCache.new(*@opts)
53
+ x.report("set:plain:memcache-client") do
54
+ n.times do
55
+ @m.set @key1, @marshalled, 0, true
56
+ @m.set @key2, @marshalled, 0, true
57
+ @m.set @key3, @marshalled, 0, true
58
+ @m.set @key1, @marshalled, 0, true
59
+ @m.set @key2, @marshalled, 0, true
60
+ @m.set @key3, @marshalled, 0, true
61
+ end
62
+ end
63
+
64
+ @m = MemCache.new(*@opts)
65
+ x.report("set:ruby:memcache-client") do
66
+ n.times do
67
+ @m.set @key1, @value
68
+ @m.set @key2, @value
69
+ @m.set @key3, @value
70
+ @m.set @key1, @value
71
+ @m.set @key2, @value
72
+ @m.set @key3, @value
73
+ end
74
+ end
75
+
76
+ @m = MemCache.new(*@opts)
77
+ x.report("get:plain:memcache-client") do
78
+ n.times do
79
+ @m.get @key1, true
80
+ @m.get @key2, true
81
+ @m.get @key3, true
82
+ @m.get @key1, true
83
+ @m.get @key2, true
84
+ @m.get @key3, true
85
+ end
86
+ end
87
+
88
+ @m = MemCache.new(*@opts)
89
+ x.report("get:ruby:memcache-client") do
90
+ n.times do
91
+ @m.get @key1
92
+ @m.get @key2
93
+ @m.get @key3
94
+ @m.get @key1
95
+ @m.get @key2
96
+ @m.get @key3
97
+ end
98
+ end
99
+
100
+ @m = MemCache.new(*@opts)
101
+ x.report("multiget:ruby:memcache-client") do
102
+ n.times do
103
+ # We don't use the keys array because splat is slow
104
+ @m.get_multi @key1, @key2, @key3, @key4, @key5, @key6
105
+ end
106
+ end
107
+
108
+ @m = MemCache.new(*@opts)
109
+ x.report("missing:ruby:memcache-client") do
110
+ n.times do
111
+ begin @m.delete @key1; rescue; end
112
+ begin @m.get @key1; rescue; end
113
+ begin @m.delete @key2; rescue; end
114
+ begin @m.get @key2; rescue; end
115
+ begin @m.delete @key3; rescue; end
116
+ begin @m.get @key3; rescue; end
117
+ end
118
+ end
119
+
120
+ @m = MemCache.new(*@opts)
121
+ x.report("mixed:ruby:memcache-client") do
122
+ n.times do
123
+ @m.set @key1, @value
124
+ @m.set @key2, @value
125
+ @m.set @key3, @value
126
+ @m.get @key1
127
+ @m.get @key2
128
+ @m.get @key3
129
+ @m.set @key1, @value
130
+ @m.get @key1
131
+ @m.set @key2, @value
132
+ @m.get @key2
133
+ @m.set @key3, @value
134
+ @m.get @key3
135
+ end
136
+ end
137
+
138
+ assert true
139
+ end
140
+
141
+ end
142
+ end