evented-memcache-client 1.0.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/CHANGELOG +113 -0
- data/COPYING +348 -0
- data/README +75 -0
- data/Rakefile +123 -0
- data/doc/classes/EventMachine.html +146 -0
- data/doc/classes/EventMachine/Protocols.html +135 -0
- data/doc/classes/EventMachine/Protocols/Memcache.html +153 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Client.html +121 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Connectable.html +615 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Connection.html +192 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Sender.html +389 -0
- data/doc/classes/EventMachine/Protocols/Memcache/Server.html +463 -0
- data/doc/classes/EventMachine/Protocols/Memcache/TenaciousMC.html +277 -0
- data/doc/classes/MyHandler.html +197 -0
- data/doc/created.rid +1 -0
- data/doc/files/COPYING.html +473 -0
- data/doc/files/README.html +187 -0
- data/doc/files/extras/consumer_rb.html +110 -0
- data/doc/files/extras/producer_rb.html +110 -0
- data/doc/files/extras/server_rb.html +110 -0
- data/doc/files/lib/evented-memcache-client_rb.html +125 -0
- data/doc/files/lib/evented_memcache_client/client_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/connectable_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/connection_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/sender_rb.html +112 -0
- data/doc/files/lib/evented_memcache_client/server_rb.html +120 -0
- data/doc/files/lib/evented_memcache_client/tenacious_rb.html +120 -0
- data/doc/fr_class_index.html +36 -0
- data/doc/fr_file_index.html +38 -0
- data/doc/fr_method_index.html +66 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/extras/consumer.rb +41 -0
- data/extras/producer.rb +22 -0
- data/extras/server.rb +33 -0
- data/lib/evented-memcache-client.rb +37 -0
- data/lib/evented_memcache_client/client.rb +36 -0
- data/lib/evented_memcache_client/connectable.rb +324 -0
- data/lib/evented_memcache_client/connection.rb +73 -0
- data/lib/evented_memcache_client/sender.rb +184 -0
- data/lib/evented_memcache_client/server.rb +118 -0
- data/lib/evented_memcache_client/tenacious.rb +106 -0
- metadata +115 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'eventmachine'
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
module Protocols
|
28
|
+
module Memcache
|
29
|
+
|
30
|
+
# Implements a memcache protocol connection.
|
31
|
+
|
32
|
+
class Connection < EventMachine::Connection
|
33
|
+
|
34
|
+
include Connectable
|
35
|
+
|
36
|
+
######################################################################
|
37
|
+
# EventMachine::Connection entry points
|
38
|
+
######################################################################
|
39
|
+
|
40
|
+
# Create a new connection. Args are a hash of callbacks (or a
|
41
|
+
# Module -- see Connectable#set_handler).
|
42
|
+
#
|
43
|
+
# Note that when you're using EventMachine, you won't be
|
44
|
+
# calling Connection::new yourself - EM does it for you when a
|
45
|
+
# connection is established.
|
46
|
+
#
|
47
|
+
# Eg:
|
48
|
+
# callbacks = {
|
49
|
+
# :open => Proc.new { |conn|
|
50
|
+
# conn.set(:key => 'x', :data => Time.now.to_s)
|
51
|
+
# },
|
52
|
+
# :stored => Proc.new { |conn, data, args|
|
53
|
+
# puts "Stored a value"
|
54
|
+
# EventMachine::stop
|
55
|
+
# },
|
56
|
+
# }
|
57
|
+
# EventMachine::run {
|
58
|
+
# EventMachine::connect('127.0.0.1',
|
59
|
+
# 12345,
|
60
|
+
# EventMachine::Protocols::Memcache::Connection,
|
61
|
+
# callbacks)
|
62
|
+
# }
|
63
|
+
#
|
64
|
+
def initialize(*args)
|
65
|
+
super(*args)
|
66
|
+
return if !args || args.empty?
|
67
|
+
set_handler(args[0])
|
68
|
+
end
|
69
|
+
|
70
|
+
end # Connection
|
71
|
+
end # Memcache
|
72
|
+
end # module Protocols
|
73
|
+
end # module EventMachine
|
@@ -0,0 +1,184 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
module EventMachine
|
24
|
+
module Protocols
|
25
|
+
module Memcache
|
26
|
+
|
27
|
+
# Interface for sending memcache protocol messages like STORE,
|
28
|
+
# END, etc. See
|
29
|
+
# http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt
|
30
|
+
# for more details.
|
31
|
+
#
|
32
|
+
# NB: since several methods are defined dynamically here using
|
33
|
+
# define_method, they don't show up in the RDoc-generated
|
34
|
+
# documentation. Sorry.
|
35
|
+
|
36
|
+
module Sender
|
37
|
+
|
38
|
+
# Keeps stats on message sends.
|
39
|
+
def book_it(msg_type)
|
40
|
+
@snd_stats[msg_type] ||= 0
|
41
|
+
@snd_stats[msg_type] += 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# Wish these could be properly rdoc'd...
|
45
|
+
%w(set add replace append prepend).each { |cmd|
|
46
|
+
class_eval do
|
47
|
+
define_method "#{cmd}" do |args|
|
48
|
+
book_it(cmd.to_sym)
|
49
|
+
key = args[:key]
|
50
|
+
flags = args[:flags] || 0
|
51
|
+
exptime = args[:exptime] || 0
|
52
|
+
noreply = args[:noreply] || false
|
53
|
+
data = args[:data]
|
54
|
+
raise ArgumentError.new(':data cannot be nil') unless data
|
55
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
56
|
+
cmd_str = "#{cmd} #{key} #{flags} #{exptime} #{data.length}"
|
57
|
+
cmd_str << " noreply" if noreply
|
58
|
+
send_to_peer "#{cmd_str}\r\n#{data}\r\n"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
}
|
62
|
+
|
63
|
+
# Send a cas request to the memcached server.
|
64
|
+
def cas(*args)
|
65
|
+
book_it(:cas)
|
66
|
+
key = args[:key]
|
67
|
+
flags = args[:flags] || 0
|
68
|
+
exptime = args[:exptime] || 0
|
69
|
+
cas_uniq = args[:cas_uniq]
|
70
|
+
noreply = args[:noreply] || false
|
71
|
+
data = args[:data]
|
72
|
+
raise ArgumentError.new(':data cannot be nil') unless data
|
73
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
74
|
+
raise ArgumentError.new(':cas_uniq cannot be nil') unless cas_uniq
|
75
|
+
cmd_str = "cas #{key} #{flags} #{exptime} #{data.length} #{cas_uniq}"
|
76
|
+
cmd_str << " noreply" if noreply
|
77
|
+
send_to_peer "#{cmd_str}\r\n#{data}\r\n"
|
78
|
+
end
|
79
|
+
|
80
|
+
# Send a get request to the memcached server. +args+ must
|
81
|
+
# have a :keys key if it is a hash, otherwise it's assumed
|
82
|
+
# that it is a single key. Eg., get('queue') or
|
83
|
+
# get(:keys=>['queue_a', 'queue_b']) or get(:key=>'queue').
|
84
|
+
def get(args)
|
85
|
+
if args.respond_to?(:keys)
|
86
|
+
keys = args[:keys] || [args[:key]]
|
87
|
+
else
|
88
|
+
keys = [args]
|
89
|
+
end
|
90
|
+
raise ArgumentError.new('No keys specified.') if keys.empty?
|
91
|
+
send_to_peer "get #{keys.join(' ')}\r\n"
|
92
|
+
end
|
93
|
+
alias_method :gets, :get
|
94
|
+
|
95
|
+
# Send a delete request to the memcached server. +args+ is a
|
96
|
+
# Hash with a required :key member, and optional :time and
|
97
|
+
# :noreply members.
|
98
|
+
def delete(*args)
|
99
|
+
book_it(:delete)
|
100
|
+
key = args[:key]
|
101
|
+
time = args[:time] || 0
|
102
|
+
noreply = args[:noreply] || false
|
103
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
104
|
+
cmd_str = "delete #{key}"
|
105
|
+
cmd_str << " #{time}" if time > 0
|
106
|
+
cmd_str << " noreply" if noreply
|
107
|
+
send_to_peer "#{cmd_str}\r\n"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Wish these could be properly rdoc'd...
|
111
|
+
%w(incr decr).each { |cmd|
|
112
|
+
class_eval do
|
113
|
+
define_method "#{cmd}" do |args|
|
114
|
+
book_it(cmd.to_sym)
|
115
|
+
key = args[:key]
|
116
|
+
value = args[:value] || 0
|
117
|
+
noreply = args[:noreply] || false
|
118
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
119
|
+
raise ArgumentError.new(':value cannot be nil') unless value
|
120
|
+
cmd_str = "#{cmd} #{key} #{value}"
|
121
|
+
cmd_str << " noreply" if noreply
|
122
|
+
send_to_peer "#{cmd_str}\r\n"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
}
|
126
|
+
|
127
|
+
# Send a stats request to the memcached server. +args+ is a
|
128
|
+
# Array that specifies the specific stats command as defined
|
129
|
+
# in the memcache protocol.
|
130
|
+
def stats(*args)
|
131
|
+
book_it(:stats)
|
132
|
+
cmd_str = "stats #{args.join(' ')}"
|
133
|
+
send_to_peer "#{cmd_str}\r\n"
|
134
|
+
end
|
135
|
+
|
136
|
+
# Wish these could be properly rdoc'd...
|
137
|
+
%w(flush_all version quit).each { |cmd|
|
138
|
+
class_eval do
|
139
|
+
define_method "#{cmd}" do
|
140
|
+
book_it(cmd.to_sym)
|
141
|
+
send_to_peer "#{cmd}\r\n"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
}
|
145
|
+
|
146
|
+
# Wish these could be properly rdoc'd...
|
147
|
+
%w(stored end).each { |cmd|
|
148
|
+
class_eval do
|
149
|
+
define_method "#{cmd}" do
|
150
|
+
book_it(cmd.to_sym)
|
151
|
+
send_to_peer "#{cmd.upcase}\r\n"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
}
|
155
|
+
|
156
|
+
# Send a value message to the remote endpoint (client); this
|
157
|
+
# is a server-sent message. +args+ is a
|
158
|
+
# Hash with a required :key member, and optional :flash and
|
159
|
+
# :cas_uniq members. Hash must also include a :data
|
160
|
+
# member (the payload to be sent).
|
161
|
+
def value(args)
|
162
|
+
book_it(:value)
|
163
|
+
key = args[:key]
|
164
|
+
flags = args[:flags] || 0
|
165
|
+
cas_uniq = args[:cas_uniq]
|
166
|
+
data = args[:data]
|
167
|
+
raise ArgumentError.new(':data cannot be nil') unless data
|
168
|
+
raise ArgumentError.new(':key cannot be nil') unless key
|
169
|
+
cmd_str = "VALUE #{key} #{flags} #{data.length}"
|
170
|
+
cmd_str << " #{cas_uniq}" if cas_uniq
|
171
|
+
send_to_peer "#{cmd_str}\r\n#{data}\r\n"
|
172
|
+
end
|
173
|
+
|
174
|
+
# Send a memcache protocol message contained in the :data
|
175
|
+
# argument to the remote endpoint.
|
176
|
+
def send_to_peer(data)
|
177
|
+
@msgs_out ||= 0
|
178
|
+
@msgs_out += 1
|
179
|
+
send_data(data)
|
180
|
+
end
|
181
|
+
end # Sender
|
182
|
+
end # Memcache
|
183
|
+
end # Protocols
|
184
|
+
end # EventMachine
|
@@ -0,0 +1,118 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'eventmachine'
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
module Protocols
|
28
|
+
module Memcache
|
29
|
+
class Server
|
30
|
+
|
31
|
+
DEFAULT_PORT = 22122
|
32
|
+
DEFAULT_HOST = 'localhost'
|
33
|
+
|
34
|
+
attr_accessor :connections
|
35
|
+
attr :host, true
|
36
|
+
attr :port, true
|
37
|
+
|
38
|
+
# Create a new memcache server listener, on the specified host
|
39
|
+
# and port.
|
40
|
+
def initialize(host=nil, port=nil)
|
41
|
+
@host = host || DEFAULT_HOST
|
42
|
+
@port = port || DEFAULT_PORT
|
43
|
+
@connections = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Start listening for client connections.
|
47
|
+
def start
|
48
|
+
begin
|
49
|
+
@signature = EventMachine.start_server(@host,
|
50
|
+
@port.to_i,
|
51
|
+
Connectable,
|
52
|
+
self) { |conn|
|
53
|
+
conn.set_handler(self)
|
54
|
+
|
55
|
+
# Usually called from EM; we fake it out.
|
56
|
+
conn.connection_completed
|
57
|
+
}
|
58
|
+
rescue => e
|
59
|
+
return false
|
60
|
+
else
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Stop listening for client connections, and close any open
|
66
|
+
# clients.
|
67
|
+
def stop
|
68
|
+
EventMachine.stop_server(@signature)
|
69
|
+
unless wait_for_connections
|
70
|
+
# Still some running
|
71
|
+
EventMachine.add_periodic_timer(1) {
|
72
|
+
wait_for_connections
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
######################################################################
|
79
|
+
# Callbacks from the connection
|
80
|
+
######################################################################
|
81
|
+
|
82
|
+
def open(conn)
|
83
|
+
@connections << conn
|
84
|
+
end
|
85
|
+
|
86
|
+
def close(conn)
|
87
|
+
@connections.delete(conn)
|
88
|
+
end
|
89
|
+
|
90
|
+
def set(conn, data, args)
|
91
|
+
# we don't do nuffing
|
92
|
+
end
|
93
|
+
alias_method :replace, :set
|
94
|
+
alias_method :prepend, :set
|
95
|
+
alias_method :append, :set
|
96
|
+
alias_method :add, :set
|
97
|
+
|
98
|
+
def get(conn, data, args)
|
99
|
+
# we don't do nuffing
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def wait_for_connections
|
105
|
+
if @connections.empty?
|
106
|
+
true
|
107
|
+
else
|
108
|
+
@connections.each { |c|
|
109
|
+
c.close_connection_after_writing
|
110
|
+
}
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end # Server
|
116
|
+
end # Memcache
|
117
|
+
end # module Protocols
|
118
|
+
end # module EventMachine
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#############################################################################
|
2
|
+
#
|
3
|
+
# Author:: Colin Steele (colin@colinsteele.org)
|
4
|
+
# Homepage::
|
5
|
+
#
|
6
|
+
#----------------------------------------------------------------------------
|
7
|
+
#
|
8
|
+
# Copyright (C) 2008 by Colin Steele. All Rights Reserved.
|
9
|
+
# colin@colinsteele.org
|
10
|
+
#
|
11
|
+
# This program is free software; you can redistribute it and/or modify
|
12
|
+
# it under the terms of either: 1) the GNU General Public License
|
13
|
+
# as published by the Free Software Foundation; either version 2 of the
|
14
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
15
|
+
#
|
16
|
+
# See the file COPYING for complete licensing information.
|
17
|
+
#
|
18
|
+
#---------------------------------------------------------------------------
|
19
|
+
#
|
20
|
+
#
|
21
|
+
#############################################################################
|
22
|
+
|
23
|
+
require 'rubygems'
|
24
|
+
require 'eventmachine'
|
25
|
+
|
26
|
+
module EventMachine
|
27
|
+
module Protocols
|
28
|
+
module Memcache
|
29
|
+
|
30
|
+
# TenaciousMC is a "tenacious" memcache client connection. When
|
31
|
+
# the underlying connection is lost, it attempts to reacquire it
|
32
|
+
# automatically.
|
33
|
+
|
34
|
+
class TenaciousMC < Client
|
35
|
+
|
36
|
+
# Create a new "tenacious" connection, which will attempt to
|
37
|
+
# reacquire a connection to the server if it is lost.
|
38
|
+
#
|
39
|
+
# Note that when you're using EventMachine, you won't be
|
40
|
+
# calling TenaciousMC::new yourself - EM does it for you when a
|
41
|
+
# connection is established.
|
42
|
+
#
|
43
|
+
# First argument as per EventMachine::Protocols::Memcache::Client.
|
44
|
+
# Ie., callback hash, module or handler object.
|
45
|
+
#
|
46
|
+
# Second argument is hash of options governing TenaciousMC's
|
47
|
+
# behavior:
|
48
|
+
#
|
49
|
+
# { :host => 'somewhere.com', :port=>12345, :interval => 5 }
|
50
|
+
#
|
51
|
+
# If the options hash includes a :eager key, and its value is
|
52
|
+
# true, then when the connection opens, TenaciousMC will
|
53
|
+
# immediately send a 'get' to the server to fetch data, using
|
54
|
+
# the value of the :key option as the key fetched.
|
55
|
+
#
|
56
|
+
# So a typical invocation:
|
57
|
+
#
|
58
|
+
# EventMachine::run {
|
59
|
+
# EventMachine::connect('over.there',
|
60
|
+
# 12345,
|
61
|
+
# EventMachine::Protocols::Memcache::TenaciousMC,
|
62
|
+
# MyHandlerModule,
|
63
|
+
# {:host => 'over.there', :port => 12345})
|
64
|
+
# }
|
65
|
+
def initialize(*args)
|
66
|
+
@handle_arg = args[0]
|
67
|
+
options = args[1]
|
68
|
+
@host = options[:host] || 'localhost'
|
69
|
+
@port = options[:port]
|
70
|
+
raise "No port option is set" unless @port
|
71
|
+
@interval = options[:interval] || 5
|
72
|
+
@eager = options[:eager] || false
|
73
|
+
@key = options[:key] || nil
|
74
|
+
super(*args)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Called from EM::Connection after the connection is established.
|
78
|
+
def connection_completed
|
79
|
+
super
|
80
|
+
if @eager && @key
|
81
|
+
self.get(:key => @key)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Called from EM::Connection when the connection is dead.
|
86
|
+
def unbind
|
87
|
+
super
|
88
|
+
if @interval > 0
|
89
|
+
EventMachine::add_timer(@interval) do
|
90
|
+
EventMachine::connect(@host,
|
91
|
+
@port,
|
92
|
+
self.class,
|
93
|
+
@handle_arg,
|
94
|
+
{
|
95
|
+
:host => @host,
|
96
|
+
:port => @port,
|
97
|
+
:interval => @interval
|
98
|
+
})
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end # TenaciousMC
|
104
|
+
end # Memcache
|
105
|
+
end # module Protocols
|
106
|
+
end # module EventMachine
|