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.
Files changed (43) hide show
  1. data/CHANGELOG +113 -0
  2. data/COPYING +348 -0
  3. data/README +75 -0
  4. data/Rakefile +123 -0
  5. data/doc/classes/EventMachine.html +146 -0
  6. data/doc/classes/EventMachine/Protocols.html +135 -0
  7. data/doc/classes/EventMachine/Protocols/Memcache.html +153 -0
  8. data/doc/classes/EventMachine/Protocols/Memcache/Client.html +121 -0
  9. data/doc/classes/EventMachine/Protocols/Memcache/Connectable.html +615 -0
  10. data/doc/classes/EventMachine/Protocols/Memcache/Connection.html +192 -0
  11. data/doc/classes/EventMachine/Protocols/Memcache/Sender.html +389 -0
  12. data/doc/classes/EventMachine/Protocols/Memcache/Server.html +463 -0
  13. data/doc/classes/EventMachine/Protocols/Memcache/TenaciousMC.html +277 -0
  14. data/doc/classes/MyHandler.html +197 -0
  15. data/doc/created.rid +1 -0
  16. data/doc/files/COPYING.html +473 -0
  17. data/doc/files/README.html +187 -0
  18. data/doc/files/extras/consumer_rb.html +110 -0
  19. data/doc/files/extras/producer_rb.html +110 -0
  20. data/doc/files/extras/server_rb.html +110 -0
  21. data/doc/files/lib/evented-memcache-client_rb.html +125 -0
  22. data/doc/files/lib/evented_memcache_client/client_rb.html +120 -0
  23. data/doc/files/lib/evented_memcache_client/connectable_rb.html +120 -0
  24. data/doc/files/lib/evented_memcache_client/connection_rb.html +120 -0
  25. data/doc/files/lib/evented_memcache_client/sender_rb.html +112 -0
  26. data/doc/files/lib/evented_memcache_client/server_rb.html +120 -0
  27. data/doc/files/lib/evented_memcache_client/tenacious_rb.html +120 -0
  28. data/doc/fr_class_index.html +36 -0
  29. data/doc/fr_file_index.html +38 -0
  30. data/doc/fr_method_index.html +66 -0
  31. data/doc/index.html +24 -0
  32. data/doc/rdoc-style.css +208 -0
  33. data/extras/consumer.rb +41 -0
  34. data/extras/producer.rb +22 -0
  35. data/extras/server.rb +33 -0
  36. data/lib/evented-memcache-client.rb +37 -0
  37. data/lib/evented_memcache_client/client.rb +36 -0
  38. data/lib/evented_memcache_client/connectable.rb +324 -0
  39. data/lib/evented_memcache_client/connection.rb +73 -0
  40. data/lib/evented_memcache_client/sender.rb +184 -0
  41. data/lib/evented_memcache_client/server.rb +118 -0
  42. data/lib/evented_memcache_client/tenacious.rb +106 -0
  43. 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