hoodoo 1.13.0 → 1.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/hoodoo.rb +1 -0
- data/lib/hoodoo/services/services/session.rb +8 -104
- data/lib/hoodoo/transient_store.rb +19 -0
- data/lib/hoodoo/transient_store/mocks/dalli_client.rb +148 -0
- data/lib/hoodoo/transient_store/mocks/redis.rb +138 -0
- data/lib/hoodoo/transient_store/transient_store.rb +344 -0
- data/lib/hoodoo/transient_store/transient_store/base.rb +81 -0
- data/lib/hoodoo/transient_store/transient_store/memcached.rb +116 -0
- data/lib/hoodoo/transient_store/transient_store/memcached_redis_mirror.rb +181 -0
- data/lib/hoodoo/transient_store/transient_store/redis.rb +126 -0
- data/lib/hoodoo/version.rb +1 -1
- data/spec/active/active_record/support_spec.rb +3 -9
- data/spec/active/active_record/translated_spec.rb +2 -5
- data/spec/logger/writers/file_writer_spec.rb +1 -4
- data/spec/logger/writers/stream_writer_spec.rb +2 -9
- data/spec/services/middleware/middleware_logging_spec.rb +1 -4
- data/spec/services/middleware/middleware_permissions_spec.rb +2 -2
- data/spec/services/services/interface_spec.rb +2 -2
- data/spec/services/services/session_spec.rb +26 -19
- data/spec/transient_store/transient_store/base_spec.rb +52 -0
- data/spec/transient_store/transient_store/memcached_redis_mirror_spec.rb +380 -0
- data/spec/transient_store/transient_store/memcached_spec.rb +244 -0
- data/spec/transient_store/transient_store/mocks/dalli_client_spec.rb +44 -0
- data/spec/transient_store/transient_store/mocks/redis_spec.rb +28 -0
- data/spec/transient_store/transient_store/redis_spec.rb +242 -0
- data/spec/transient_store/transient_store_spec.rb +448 -0
- metadata +31 -9
@@ -0,0 +1,344 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: transient_store.rb
|
3
|
+
# (C):: Loyalty New Zealand 2017
|
4
|
+
#
|
5
|
+
# Purpose:: Provide a simple abstraction over transient storage engines
|
6
|
+
# such as Memcached or Redis, making it easier for client code
|
7
|
+
# to switch engines with very few changes.
|
8
|
+
# ----------------------------------------------------------------------
|
9
|
+
# 01-Feb-2017 (ADH): Created.
|
10
|
+
########################################################################
|
11
|
+
|
12
|
+
module Hoodoo
|
13
|
+
|
14
|
+
# A simple abstraction over transient storage engines such as
|
15
|
+
# {Memcached}[https://memcached.org] or {Redis}[https://redis.io], making it
|
16
|
+
# it easier for client code to switch engines with very few changes. If the
|
17
|
+
# storage engine chosen when creating instances of this object is defined in
|
18
|
+
# application-wide configuration data, all you would need to do is change the
|
19
|
+
# configuration for all new TransientStore instances to use the new engine.
|
20
|
+
#
|
21
|
+
class TransientStore
|
22
|
+
|
23
|
+
###########################################################################
|
24
|
+
# Class methods
|
25
|
+
###########################################################################
|
26
|
+
|
27
|
+
# Register a new storage engine plugin class. It _MUST_ inherit from and
|
28
|
+
# thus follow the template laid out in Hoodoo::TransientStore::Base.
|
29
|
+
#
|
30
|
+
# _Named_ parameters are:
|
31
|
+
#
|
32
|
+
# +as+:: The name, as a Symbol, for the ::new +storage_engine+
|
33
|
+
# input parameter, to select this plugin.
|
34
|
+
#
|
35
|
+
# +using+:: The class reference of the Hoodoo::TransientStore::Base
|
36
|
+
# subclass to be associated with the name given in +as+.
|
37
|
+
#
|
38
|
+
# Example:
|
39
|
+
#
|
40
|
+
# Hoodoo::TransientStore.register(
|
41
|
+
# as: :memcached,
|
42
|
+
# using: Hoodoo::TransientStore::Memcached
|
43
|
+
# )
|
44
|
+
#
|
45
|
+
def self.register( as:, using: )
|
46
|
+
as = as.to_sym
|
47
|
+
|
48
|
+
@@supported_storage_engines = {} unless defined?( @@supported_storage_engines )
|
49
|
+
|
50
|
+
unless using < Hoodoo::TransientStore::Base
|
51
|
+
raise "Hoodoo::TransientStore.register requires a Hoodoo::TransientStore::Base subclass - got '#{ using.to_s }'"
|
52
|
+
end
|
53
|
+
|
54
|
+
if @@supported_storage_engines.has_key?( as )
|
55
|
+
raise "Hoodoo::TransientStore.register: A storage engine called '#{ as }' is already registered"
|
56
|
+
end
|
57
|
+
|
58
|
+
@@supported_storage_engines[ as ] = using
|
59
|
+
end
|
60
|
+
|
61
|
+
# Remove a storage engine plugin class from the supported collection. Any
|
62
|
+
# existing Hoodoo::TransientStore instances using the removed class will
|
63
|
+
# not be affected, but new instances cannot be made.
|
64
|
+
#
|
65
|
+
# _Named_ parameters are:
|
66
|
+
#
|
67
|
+
# +as+:: The value given to #register in the corresponding +as+ parameter.
|
68
|
+
#
|
69
|
+
def self.deregister( as: )
|
70
|
+
@@supported_storage_engines.delete( as )
|
71
|
+
end
|
72
|
+
|
73
|
+
# Return an array of the names of all supported storage engine names known
|
74
|
+
# to the Hoodoo::TransientStore class. Any one of those names can be used
|
75
|
+
# with the ::new +storage_engine+ parameter.
|
76
|
+
#
|
77
|
+
def self.supported_storage_engines
|
78
|
+
@@supported_storage_engines.keys()
|
79
|
+
end
|
80
|
+
|
81
|
+
###########################################################################
|
82
|
+
# Instance methods
|
83
|
+
###########################################################################
|
84
|
+
|
85
|
+
# Read this instance's storage engine; see ::supported_storage_engines and
|
86
|
+
# ::new.
|
87
|
+
#
|
88
|
+
attr_reader :storage_engine
|
89
|
+
|
90
|
+
# Read the storage engine insteance for the #storage_engine - this allows
|
91
|
+
# engine-specific configuration to be set where available, though this is
|
92
|
+
# strongly discouraged as it couples client code to the engine in use,
|
93
|
+
# defeating the main rationale behind the TransientStore abstraction.
|
94
|
+
#
|
95
|
+
attr_reader :storage_engine_instance
|
96
|
+
|
97
|
+
# Read this instance's default item maximum lifespan, in seconds. See
|
98
|
+
# also ::new.
|
99
|
+
#
|
100
|
+
attr_reader :default_maximum_lifespan
|
101
|
+
|
102
|
+
# Read this instance's default storage namespace, as a String. See also
|
103
|
+
# ::new.
|
104
|
+
#
|
105
|
+
attr_reader :default_namespace
|
106
|
+
|
107
|
+
# Instantiate a new Transient storage object through which temporary data
|
108
|
+
# can be stored or retrieved.
|
109
|
+
#
|
110
|
+
# The TransientStore abstraction is a high level and simple abstraction over
|
111
|
+
# heterogenous data storage engines. It does not expose the many subtle
|
112
|
+
# configuration settings usually available in such. If you need to take
|
113
|
+
# advantage of those at an item storage level, you'll need to use a lower
|
114
|
+
# level interface and thus lock your code to the engine of choice.
|
115
|
+
#
|
116
|
+
# Engine plug-ins are recommended to attempt to gain and test a connection
|
117
|
+
# to the storage engine when this object is constructed, so if building a
|
118
|
+
# TransientStore instance, ensure your chosen storage engine is running
|
119
|
+
# first. Exceptions may be raised by storage engines, so you will probably
|
120
|
+
# want to catch those with more civilised error handling code.
|
121
|
+
#
|
122
|
+
# _Named_ parameters are:
|
123
|
+
#
|
124
|
+
# +storage_engine+:: An entry from ::supported_storage_engines.
|
125
|
+
#
|
126
|
+
# +storage_host_uri+:: The engine-dependent connection URI. Consult
|
127
|
+
# documentation for your chosen engine to find
|
128
|
+
# out its connection URI requirements, along
|
129
|
+
# with the documentation for the constructor
|
130
|
+
# method of the plug-in in use, since in some
|
131
|
+
# cases requirements may be unusual (e.g. in
|
132
|
+
# Hoodoo::TransientStore::MemcachedRedisMirror).
|
133
|
+
#
|
134
|
+
# +default_maximum_lifespan+:: The default time-to-live for data items, in,
|
135
|
+
# seconds; can be overridden per item; default
|
136
|
+
# is 604800 seconds or 7 days.
|
137
|
+
#
|
138
|
+
# +default_namespace+:: Storage engine keys are namespaced with
|
139
|
+
# +nz_co_loyalty_hoodoo_transient_store_+ by
|
140
|
+
# default, though this can be overridden here.
|
141
|
+
# Pass a String or Symbol.
|
142
|
+
#
|
143
|
+
def initialize(
|
144
|
+
storage_engine:,
|
145
|
+
storage_host_uri:,
|
146
|
+
default_maximum_lifespan: 604800,
|
147
|
+
default_namespace: 'nz_co_loyalty_hoodoo_transient_store_'
|
148
|
+
)
|
149
|
+
|
150
|
+
unless self.class.supported_storage_engines().include?( storage_engine )
|
151
|
+
|
152
|
+
# Be kind and use 'inspect' to indicate that we expect Symbols here
|
153
|
+
# in the exception, because of the arising leading ':' in the output.
|
154
|
+
#
|
155
|
+
engines = self.class.supported_storage_engines().map { | symbol | "'#{ symbol.inspect }'" }
|
156
|
+
allowed = engines.join( ', ' )
|
157
|
+
|
158
|
+
raise "Hoodoo::TransientStore: Unrecognised storage engine '#{ storage_engine.inspect }' requested; allowed values: #{ allowed }"
|
159
|
+
end
|
160
|
+
|
161
|
+
@default_maximum_lifespan = default_maximum_lifespan
|
162
|
+
@default_namespace = ( default_namespace || '' ).to_s()
|
163
|
+
@storage_engine = storage_engine
|
164
|
+
@storage_engine_instance = @@supported_storage_engines[ storage_engine ].new(
|
165
|
+
storage_host_uri: storage_host_uri,
|
166
|
+
namespace: @default_namespace
|
167
|
+
)
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
# Set (write) a given payload into the storage engine with the given
|
172
|
+
# payload and maximum lifespan.
|
173
|
+
#
|
174
|
+
# Payloads must only contain simple types such as Hash, Array, String and
|
175
|
+
# Integer. Complex types like Symbol, Date, Float, BigDecimal or custom
|
176
|
+
# objects are unlikely to serialise properly but since this depends upon
|
177
|
+
# the storage engine in use, errors may or may not be raised for misuse.
|
178
|
+
#
|
179
|
+
# Storage engines usually have a maximum payload size limit; consult your
|
180
|
+
# engine administrator for information. For example, the default - but
|
181
|
+
# reconfigurable - maximum payload size for Memcached is 1MB.
|
182
|
+
#
|
183
|
+
# For maximum possible compatibility:
|
184
|
+
#
|
185
|
+
# * Use only Hash payloads with String key/value paids and no nesting. You
|
186
|
+
# may choose to marshal the data into a String manually for unusual data
|
187
|
+
# requirements, manually converting back when reading stored data.
|
188
|
+
#
|
189
|
+
# * Keep the payload size as small as possible - large objects belong in
|
190
|
+
# bulk storage engines such as Amazon S3.
|
191
|
+
#
|
192
|
+
# These are only guidelines though - heterogenous storage engine support
|
193
|
+
# and the ability of system administrators to arbitrarily configure those
|
194
|
+
# storage engines makes it impossible to be more precise.
|
195
|
+
#
|
196
|
+
# Returns:
|
197
|
+
#
|
198
|
+
# * +true+ if storage was successful
|
199
|
+
# * +false+ if storage failed but the reason is unknown
|
200
|
+
# * An +Exception+ instance if storage failed and the storage engine
|
201
|
+
# raised an exception describing the problem.
|
202
|
+
#
|
203
|
+
# _Named_ parameters are:
|
204
|
+
#
|
205
|
+
# +key+:: Storage key to use in the engine, which is then used
|
206
|
+
# in subsequent calls to #get and possibly eventually
|
207
|
+
# to #delete. Only non-empty Strings or Symbols are
|
208
|
+
# permitted, else an exception will be raised.
|
209
|
+
#
|
210
|
+
# +payload+:: Payload data to store under the given +key+. A flat
|
211
|
+
# Hash is recommended rather than simple types such as
|
212
|
+
# String (unless marshalling a complex type into such)
|
213
|
+
# in order to make potential additions to stored data
|
214
|
+
# easier to implement. Note that +nil+ is prohibited.
|
215
|
+
#
|
216
|
+
# +maximum_lifespan+:: Optional maximum lifespan, seconds. Storage engines
|
217
|
+
# may chooset to evict payloads sooner than this; it
|
218
|
+
# is a maximum time, not a guarantee. Omit to use this
|
219
|
+
# TransientStore instance's default value - see ::new.
|
220
|
+
# If you know you no longer need a piece of data at a
|
221
|
+
# particular point in the execution flow of your code,
|
222
|
+
# explicitly delete it via #delete rather than leaving
|
223
|
+
# it to expire. This maximises the storage engine's
|
224
|
+
# pool free space and so minimises the chance of early
|
225
|
+
# item eviction.
|
226
|
+
#
|
227
|
+
def set( key:, payload:, maximum_lifespan: nil )
|
228
|
+
key = normalise_key( key, 'set' )
|
229
|
+
|
230
|
+
if payload.nil?
|
231
|
+
raise "Hoodoo::TransientStore\#set: Payloads of 'nil' are prohibited"
|
232
|
+
end
|
233
|
+
|
234
|
+
maximum_lifespan ||= @default_maximum_lifespan
|
235
|
+
|
236
|
+
begin
|
237
|
+
result = @storage_engine_instance.set(
|
238
|
+
key: key,
|
239
|
+
payload: payload,
|
240
|
+
maximum_lifespan: maximum_lifespan
|
241
|
+
)
|
242
|
+
|
243
|
+
if result != true && result != false
|
244
|
+
raise "Hoodoo::TransientStore\#set: Engine '#{ @storage_engine }' returned an invalid response"
|
245
|
+
end
|
246
|
+
|
247
|
+
rescue => e
|
248
|
+
result = e
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
return result
|
253
|
+
end
|
254
|
+
|
255
|
+
# Retrieve data previously stored with #set.
|
256
|
+
#
|
257
|
+
# _Named_ parameters are:
|
258
|
+
#
|
259
|
+
# +key+:: Key previously given to #set.
|
260
|
+
#
|
261
|
+
# Returns +nil+ if the item is not found - either the key is wrong, the
|
262
|
+
# stored data has expired or the stored data has been evicted early from
|
263
|
+
# the storage engine's pool.
|
264
|
+
#
|
265
|
+
# Only non-empty String or Symbol keys are permitted, else an exception
|
266
|
+
# will be raised.
|
267
|
+
#
|
268
|
+
def get( key: )
|
269
|
+
key = normalise_key( key, 'get' )
|
270
|
+
@storage_engine_instance.get( key: key ) rescue nil
|
271
|
+
end
|
272
|
+
|
273
|
+
# Delete data previously stored with #set.
|
274
|
+
#
|
275
|
+
# _Named_ parameters are:
|
276
|
+
#
|
277
|
+
# +key+:: Key previously given to #set.
|
278
|
+
#
|
279
|
+
# Returns:
|
280
|
+
#
|
281
|
+
# * +true+ if deletion was successful, if the item has already expired or
|
282
|
+
# if the key is simply not recognised so there is no more work to do.
|
283
|
+
# * +false+ if deletion failed but the reason is unknown.
|
284
|
+
# * An +Exception+ instance if deletion failed and the storage engine
|
285
|
+
# raised an exception describing the problem.
|
286
|
+
#
|
287
|
+
# Only non-empty String or Symbol keys are permitted, else an exception
|
288
|
+
# will be raised.
|
289
|
+
#
|
290
|
+
def delete( key: )
|
291
|
+
key = normalise_key( key, 'delete' )
|
292
|
+
|
293
|
+
begin
|
294
|
+
result = @storage_engine_instance.delete( key: key )
|
295
|
+
|
296
|
+
if result != true && result != false
|
297
|
+
raise "Hoodoo::TransientStore\#delete: Engine '#{ @storage_engine }' returned an invalid response"
|
298
|
+
end
|
299
|
+
|
300
|
+
rescue => e
|
301
|
+
result = e
|
302
|
+
|
303
|
+
end
|
304
|
+
|
305
|
+
return result
|
306
|
+
end
|
307
|
+
|
308
|
+
# If you aren't going to use this instance again, it is good manners to
|
309
|
+
# immediately close its connection(s) to any storage engines by calling
|
310
|
+
# here.
|
311
|
+
#
|
312
|
+
# No useful return value is generated and exceptions are ignored.
|
313
|
+
#
|
314
|
+
def close
|
315
|
+
@storage_engine_instance.close() rescue nil
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
|
320
|
+
# Given a storage key, make sure it's a String or Symbol, coerce to a
|
321
|
+
# String and ensure it isn't empty. Returns the non-empty String version.
|
322
|
+
# Raises exceptions for bad input classes or empty keys.
|
323
|
+
#
|
324
|
+
# +key+:: Key to normalise.
|
325
|
+
#
|
326
|
+
# +calling_method_name+:: Name of calling method to declare in exception
|
327
|
+
# messages, to aid callers in debugging.
|
328
|
+
#
|
329
|
+
def normalise_key( key, calling_method_name )
|
330
|
+
unless key.is_a?( String ) || key.is_a?( Symbol )
|
331
|
+
raise "Hoodoo::TransientStore\##{ calling_method_name }: Keys must be of String or Symbol class; you provided '#{ key.class }'"
|
332
|
+
end
|
333
|
+
|
334
|
+
key = key.to_s
|
335
|
+
|
336
|
+
if key.empty?
|
337
|
+
raise "Hoodoo::TransientStore\##{ calling_method_name }: Empty String or Symbol keys are prohibited"
|
338
|
+
end
|
339
|
+
|
340
|
+
return key
|
341
|
+
end
|
342
|
+
|
343
|
+
end
|
344
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: base.rb
|
3
|
+
# (C):: Loyalty New Zealand 2017
|
4
|
+
#
|
5
|
+
# Purpose:: Base class for Base class for Hoodoo::TransientStore plugins.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 01-Feb-2017 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
module Hoodoo
|
11
|
+
class TransientStore
|
12
|
+
|
13
|
+
# Base class for Hoodoo::TransientStore plugins. This is in effect just a
|
14
|
+
# template / abstract class, providing a source-level guideline for plug-in
|
15
|
+
# authors. See also out-of-the-box existing plug-ins as worked examples.
|
16
|
+
#
|
17
|
+
class Base
|
18
|
+
|
19
|
+
# Base class template for a constructor. Subclasses should try to
|
20
|
+
# establish a connection with their storage engine(s) here and raise
|
21
|
+
# exceptions if things go wrong.
|
22
|
+
#
|
23
|
+
# +storage_host_uri+:: The engine-dependent connection URI. See
|
24
|
+
# Hoodoo::TransientStore::new for details.
|
25
|
+
#
|
26
|
+
# +namespace+:: The storage key namespace to use, as a String.
|
27
|
+
# See Hoodoo::TransientStore::new for details.
|
28
|
+
#
|
29
|
+
def initialize( storage_host_uri:, namespace: )
|
30
|
+
@storage_host_uri = storage_host_uri
|
31
|
+
@namespace = namespace
|
32
|
+
end
|
33
|
+
|
34
|
+
# Base class template for the plug-in's back-end implementation of
|
35
|
+
# Hoodoo::TransientStore#set - see that for details.
|
36
|
+
#
|
37
|
+
# The implementation is free to raise an exception if an error is
|
38
|
+
# encountered while trying to set the data - this will be caught and
|
39
|
+
# returned by Hoodoo::TransientStore#set. Otherwise return +true+ on
|
40
|
+
# success or +false+ for failures of unknown origin.
|
41
|
+
#
|
42
|
+
def set( key:, payload:, maximum_lifespan: )
|
43
|
+
raise 'Subclasses must implement Hoodoo::TransientStore::Base#set'
|
44
|
+
end
|
45
|
+
|
46
|
+
# Base class template for the plug-in's back-end implementation of
|
47
|
+
# Hoodoo::TransientStore#get - see that for details. Returns +nil+ if
|
48
|
+
# no data is found for the given key, or if data is explicitly +nil+.
|
49
|
+
#
|
50
|
+
# The implementation is free to raise an exception if an error is
|
51
|
+
# encountered while trying to get the data - this will be caught and
|
52
|
+
# +nil+ returned by Hoodoo::TransientStore#get.
|
53
|
+
#
|
54
|
+
def get( key: )
|
55
|
+
raise 'Subclasses must implement Hoodoo::TransientStore::Base#get'
|
56
|
+
end
|
57
|
+
|
58
|
+
# Base class template for the plug-in's back-end implementation of
|
59
|
+
# Hoodoo::TransientStore#delete - see that for details.
|
60
|
+
#
|
61
|
+
# The implementation is free to raise an exception if an error is
|
62
|
+
# encountered while trying to get the data - this will be caught and
|
63
|
+
# ignored by Hoodoo::TransientStore#delete. Otherwise return +true+ on
|
64
|
+
# success or +false+ for failures of unknown origin.
|
65
|
+
#
|
66
|
+
def delete( key: )
|
67
|
+
raise 'Subclasses must implement Hoodoo::TransientStore::Base#delete'
|
68
|
+
end
|
69
|
+
|
70
|
+
# Base class template for the plug-in's back-end implementation of
|
71
|
+
# Hoodoo::TransientStore#close - see that for details.
|
72
|
+
#
|
73
|
+
# Any exception raised will be ignored by Hoodoo::TransientStore#close.
|
74
|
+
#
|
75
|
+
def close
|
76
|
+
raise 'Subclasses must implement Hoodoo::TransientStore::Base#close'
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
########################################################################
|
2
|
+
# File:: memcached.rb
|
3
|
+
# (C):: Loyalty New Zealand 2017
|
4
|
+
#
|
5
|
+
# Purpose:: Hoodoo::TransientStore plugin supporting Memcached.
|
6
|
+
# ----------------------------------------------------------------------
|
7
|
+
# 01-Feb-2017 (ADH): Created.
|
8
|
+
########################################################################
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'dalli'
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
|
15
|
+
module Hoodoo
|
16
|
+
class TransientStore
|
17
|
+
|
18
|
+
# Hoodoo::TransientStore plugin for {Memcached}[https://memcached.org]. The
|
19
|
+
# {Dalli gem}[https://github.com/petergoldstein/dalli] is used for server
|
20
|
+
# communication.
|
21
|
+
#
|
22
|
+
class Memcached < Hoodoo::TransientStore::Base
|
23
|
+
|
24
|
+
# See Hoodoo::TransientStore::Base::new for details.
|
25
|
+
#
|
26
|
+
# Do not instantiate this class directly. Use
|
27
|
+
# Hoodoo::TransientStore::new.
|
28
|
+
#
|
29
|
+
# The {Dalli gem}[https://github.com/petergoldstein/dalli] is used to
|
30
|
+
# talk to {Memcached}[https://memcached.org] and accepts connection UIRs
|
31
|
+
# of simple, terse forms such as <tt>'localhost:11211'</tt>. Connections
|
32
|
+
# are configured with JSON serialisation, compression off and a forced
|
33
|
+
# namespace of +nz_co_loyalty_hoodoo_transient_store_+ to avoid collision
|
34
|
+
# of data stored with this object and other data that may be in the
|
35
|
+
# Memcached instance identified by +storage_host_uri+.
|
36
|
+
#
|
37
|
+
def initialize( storage_host_uri:, namespace: )
|
38
|
+
super # Pass all arguments through -> *not* 'super()'
|
39
|
+
@client = connect_to_memcached( storage_host_uri, namespace )
|
40
|
+
end
|
41
|
+
|
42
|
+
# See Hoodoo::TransientStore::Base#set for details.
|
43
|
+
#
|
44
|
+
def set( key:, payload:, maximum_lifespan: )
|
45
|
+
@client.set( key, payload, maximum_lifespan )
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
# See Hoodoo::TransientStore::Base#get for details.
|
50
|
+
#
|
51
|
+
def get( key: )
|
52
|
+
@client.get( key )
|
53
|
+
end
|
54
|
+
|
55
|
+
# See Hoodoo::TransientStore::Base#delete for details.
|
56
|
+
#
|
57
|
+
def delete( key: )
|
58
|
+
@client.delete( key )
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
# See Hoodoo::TransientStore::Base#close for details.
|
63
|
+
#
|
64
|
+
def close
|
65
|
+
@client.close()
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# Connect to Memcached if possible and return the connected Dalli client
|
71
|
+
# instance, else raise an exception.
|
72
|
+
#
|
73
|
+
# +host+:: Connection URI, e.g. <tt>localhost:11211</tt>.
|
74
|
+
#
|
75
|
+
def connect_to_memcached( host, namespace )
|
76
|
+
exception = nil
|
77
|
+
stats = nil
|
78
|
+
client = nil
|
79
|
+
|
80
|
+
begin
|
81
|
+
client = ::Dalli::Client.new(
|
82
|
+
host,
|
83
|
+
{
|
84
|
+
:compress => false,
|
85
|
+
:serializer => JSON,
|
86
|
+
:namespace => namespace
|
87
|
+
}
|
88
|
+
)
|
89
|
+
|
90
|
+
stats = client.stats()
|
91
|
+
|
92
|
+
rescue => e
|
93
|
+
exception = e
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
if stats.nil?
|
98
|
+
if exception.nil?
|
99
|
+
raise "Hoodoo::TransientStore::Memcached: Did not get back meaningful data from Memcached at '#{ host }'"
|
100
|
+
else
|
101
|
+
raise "Hoodoo::TransientStore::Memcached: Cannot connect to Memcached at '#{ host }': #{ exception.to_s }"
|
102
|
+
end
|
103
|
+
else
|
104
|
+
return client
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
Hoodoo::TransientStore.register(
|
111
|
+
as: :memcached,
|
112
|
+
using: Hoodoo::TransientStore::Memcached
|
113
|
+
) if defined?( ::Dalli )
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|