boogaloo 0.1
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 +2 -0
- data/DEBUGGING +4 -0
- data/MIT-LICENSE +7 -0
- data/README +106 -0
- data/RUNNING_UNIT_TESTS +5 -0
- data/Rakefile +44 -0
- data/bin/boogaloo +159 -0
- data/doc/classes/Boogaloo.html +149 -0
- data/doc/classes/Boogaloo/Cache.html +121 -0
- data/doc/classes/Boogaloo/Cache/Base.html +664 -0
- data/doc/classes/Boogaloo/Cache/Persistent.html +122 -0
- data/doc/classes/Boogaloo/Cache/Temporary.html +439 -0
- data/doc/classes/Boogaloo/Client.html +111 -0
- data/doc/classes/Boogaloo/Client/Connection.html +228 -0
- data/doc/classes/Boogaloo/Config.html +540 -0
- data/doc/classes/Boogaloo/ServiceGateway.html +176 -0
- data/doc/classes/Boogaloo/ServiceRequest.html +210 -0
- data/doc/classes/Boogaloo/ThreadPool.html +205 -0
- data/doc/classes/Boogaloo/WorkerThread.html +239 -0
- data/doc/created.rid +1 -0
- data/doc/files/DEBUGGING.html +113 -0
- data/doc/files/MIT-LICENSE.html +129 -0
- data/doc/files/README.html +261 -0
- data/doc/files/examples/boogaloo_conf.html +130 -0
- data/doc/files/lib/boogaloo/cache/base_rb.html +101 -0
- data/doc/files/lib/boogaloo/cache/persistent_rb.html +108 -0
- data/doc/files/lib/boogaloo/cache/temporary_rb.html +101 -0
- data/doc/files/lib/boogaloo/client/connection_rb.html +101 -0
- data/doc/files/lib/boogaloo/config_rb.html +109 -0
- data/doc/files/lib/boogaloo/service_gateway_rb.html +110 -0
- data/doc/files/lib/boogaloo/service_request_rb.html +108 -0
- data/doc/files/lib/boogaloo/thread_pool_rb.html +101 -0
- data/doc/files/lib/boogaloo/util_rb.html +240 -0
- data/doc/files/lib/boogaloo/version_rb.html +114 -0
- data/doc/files/lib/boogaloo/worker_thread_rb.html +101 -0
- data/doc/fr_class_index.html +38 -0
- data/doc/fr_file_index.html +41 -0
- data/doc/fr_method_index.html +67 -0
- data/doc/index.html +24 -0
- data/doc/rdoc-style.css +208 -0
- data/examples/boogaloo.conf +17 -0
- data/lib/boogaloo/cache/base.rb +222 -0
- data/lib/boogaloo/cache/persistent.rb +15 -0
- data/lib/boogaloo/cache/temporary.rb +173 -0
- data/lib/boogaloo/client/connection.rb +55 -0
- data/lib/boogaloo/config.rb +206 -0
- data/lib/boogaloo/service_gateway.rb +37 -0
- data/lib/boogaloo/service_request.rb +39 -0
- data/lib/boogaloo/thread_pool.rb +40 -0
- data/lib/boogaloo/util.rb +29 -0
- data/lib/boogaloo/version.rb +1 -0
- data/lib/boogaloo/worker_thread.rb +46 -0
- data/test/test_persistent_cache.rb +99 -0
- data/test/test_service_gateway.rb +27 -0
- data/test/test_synchronicity.rb +29 -0
- data/test/test_temporary_cache.rb +42 -0
- metadata +113 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# Example Boogaloo configuration.
|
2
|
+
|
3
|
+
Server:
|
4
|
+
host: localhost
|
5
|
+
port: 7777
|
6
|
+
worker_threads: 3
|
7
|
+
|
8
|
+
PersistentCaches:
|
9
|
+
|
10
|
+
MainCache:
|
11
|
+
instance_name: main_cache
|
12
|
+
|
13
|
+
TemporaryCaches:
|
14
|
+
|
15
|
+
TempCache:
|
16
|
+
instance_name: temp_cache
|
17
|
+
check_frequency: 10
|
@@ -0,0 +1,222 @@
|
|
1
|
+
module Boogaloo
|
2
|
+
|
3
|
+
module Cache
|
4
|
+
|
5
|
+
# Provides fundemental cache operations such as add, set, get and various statistical operations.
|
6
|
+
class Base
|
7
|
+
|
8
|
+
include DRbUndumped
|
9
|
+
|
10
|
+
# Initialize a new base cache instance.
|
11
|
+
#
|
12
|
+
# Parameters:
|
13
|
+
# name: The name of the cache
|
14
|
+
# config: A Hash containing configuration for the cache.
|
15
|
+
def initialize(name, config)
|
16
|
+
|
17
|
+
@name = name
|
18
|
+
@config = config
|
19
|
+
reset
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get an object from the cache.
|
24
|
+
#
|
25
|
+
# Parameters:
|
26
|
+
# +namespace+:: The namespace to get the object from. Use <tt>nil</tt> if global namespace should be used.
|
27
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
28
|
+
def get(namespace, key)
|
29
|
+
|
30
|
+
debug("[#{@name}] GET: #{namespace or 'nil'}, #{key}") if $debug
|
31
|
+
|
32
|
+
if namespace.nil?
|
33
|
+
|
34
|
+
@global_store.key?(key) ? @hit += 1 : @miss += 1
|
35
|
+
@global_store[key]
|
36
|
+
|
37
|
+
else
|
38
|
+
|
39
|
+
@miss += 1 and return nil if not @namespace_store.key?(namespace)
|
40
|
+
@namespace_store[namespace].key?(key) ? @hit += 1 : @miss += 1
|
41
|
+
@namespace_store[namespace][key]
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
# Pull and object from the cache (get and delete).
|
48
|
+
#
|
49
|
+
# Parameters:
|
50
|
+
# +namespace+:: The namespace to get the object from. Use <tt>nil</tt> if global namespace should be used.
|
51
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
52
|
+
def pull(namespace, key)
|
53
|
+
|
54
|
+
debug("[#{@name}] PULL: #{namespace or 'nil'}, #{key}") if $debug
|
55
|
+
|
56
|
+
if namespace.nil?
|
57
|
+
|
58
|
+
@global_store.key?(key) ? @hit += 1 : @miss += 1
|
59
|
+
@global_store.delete(key)
|
60
|
+
|
61
|
+
else
|
62
|
+
|
63
|
+
@miss += 1 and return nil if not @namespace_store.key?(namespace)
|
64
|
+
@namespace_store[namespace].key?(key) ? @hit += 1 : @miss += 1
|
65
|
+
ret = @namespace_store[namespace].delete(key)
|
66
|
+
@namespace_store.delete(namespace) if @namespace_store[namespace].size == 0
|
67
|
+
ret
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add an object to the cache only if it doesn't already exist.
|
74
|
+
#
|
75
|
+
# Parameters:
|
76
|
+
# +namespace+:: The namespace in which the object will exist. Use <tt>nil</tt> if global namespace should be used.
|
77
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
78
|
+
# +obj+:: The object to add to the cache.
|
79
|
+
def add(namespace, key, obj)
|
80
|
+
|
81
|
+
return nil_error if obj.nil?
|
82
|
+
debug("[#{@name}] ADD: #{namespace or 'nil'}, #{key}") if $debug
|
83
|
+
|
84
|
+
if namespace.nil?
|
85
|
+
|
86
|
+
@global_store[key] = obj if not @global_store.key?(key)
|
87
|
+
|
88
|
+
else
|
89
|
+
|
90
|
+
create_namespace(namespace)
|
91
|
+
@namespace_store[namespace][key] = obj if not @namespace_store[namespace].key?(key)
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
# Add an object to the cache, overwriting if one already exists.
|
98
|
+
#
|
99
|
+
# Parameters:
|
100
|
+
# +namespace+:: The namespace in which the object will exist. Use <tt>nil</tt> if global namespace should be used.
|
101
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
102
|
+
# +obj+:: The object to add to the cache.
|
103
|
+
def set(namespace, key, obj)
|
104
|
+
|
105
|
+
return nil_error if obj.nil?
|
106
|
+
debug("[#{@name}] SET: #{namespace or 'nil'}, #{key}") if $debug
|
107
|
+
|
108
|
+
if namespace.nil?
|
109
|
+
|
110
|
+
@global_store[key] = obj
|
111
|
+
|
112
|
+
else
|
113
|
+
|
114
|
+
create_namespace(namespace)
|
115
|
+
@namespace_store[namespace][key] = obj
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns all available statistics in an Array.
|
122
|
+
#
|
123
|
+
# Array elements:
|
124
|
+
#
|
125
|
+
# <tt>0</tt>:: stat_reset_at
|
126
|
+
# <tt>1</tt>:: stat_hits
|
127
|
+
# <tt>2</tt>:: stat_misses
|
128
|
+
# <tt>3</tt>:: stat_namespaces
|
129
|
+
# <tt>4</tt>:: stat_objects
|
130
|
+
def stat_all
|
131
|
+
|
132
|
+
return [stat_reset_at, stat_hits, stat_misses, stat_namespaces, stat_objects]
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
# The time the cache was created or last reset.
|
137
|
+
def stat_reset_at
|
138
|
+
|
139
|
+
@reset_at
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
# The number of successful cache gets.
|
144
|
+
def stat_hits
|
145
|
+
|
146
|
+
@hit
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
# The number of unsuccessful cache gets.
|
151
|
+
def stat_misses
|
152
|
+
|
153
|
+
@miss
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
# The number of namespaces (plus the global namespace).
|
158
|
+
def stat_namespaces
|
159
|
+
|
160
|
+
@namespace_store.size + 1
|
161
|
+
|
162
|
+
end
|
163
|
+
|
164
|
+
# The number of objects stored in the cache.
|
165
|
+
def stat_objects
|
166
|
+
|
167
|
+
get_obj_count
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
# Returns an Array of all object keys currently contained in the cache.
|
172
|
+
#
|
173
|
+
# Array elements:
|
174
|
+
#
|
175
|
+
# <tt>0</tt>:: Keys in global scope (Array).
|
176
|
+
# <tt>1</tt>:: Namespaces and their respective keys (Hash).
|
177
|
+
def inspect_keys
|
178
|
+
|
179
|
+
tmp = {}
|
180
|
+
@namespace_store.keys.each { |key| tmp[key] = @namespace_store[key].keys }
|
181
|
+
[@global_store.keys, tmp]
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
# Reset the cache to an empty state.
|
186
|
+
def reset
|
187
|
+
|
188
|
+
@hit = 0
|
189
|
+
@miss = 0
|
190
|
+
@global_store = {}
|
191
|
+
@namespace_store = {}
|
192
|
+
@reset_at = Time.now
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
protected
|
197
|
+
|
198
|
+
def get_obj_count #:nodoc:
|
199
|
+
|
200
|
+
i = 0
|
201
|
+
@namespace_store.each { |key, value| i += value.size}
|
202
|
+
i + @global_store.size
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
def create_namespace(namespace) #:nodoc:
|
207
|
+
|
208
|
+
@namespace_store[namespace] = {} if not @namespace_store.key?(namespace)
|
209
|
+
|
210
|
+
end
|
211
|
+
|
212
|
+
def nil_error #:nodoc:
|
213
|
+
|
214
|
+
return TypeError.new("nil objects cannot be stored in the cache.")
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'boogaloo/cache/base'
|
2
|
+
|
3
|
+
module Boogaloo
|
4
|
+
|
5
|
+
module Cache
|
6
|
+
|
7
|
+
# A persistent cache in which objects persist for the lifetime of the server.
|
8
|
+
#
|
9
|
+
# See Boogaloo::Cache::Base[link:classes/Boogaloo/Cache/Base.html] for common cache methods.
|
10
|
+
class Persistent < Boogaloo::Cache::Base
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
module Boogaloo
|
2
|
+
|
3
|
+
module Cache
|
4
|
+
|
5
|
+
# A temporary cache in which objects can expire and are subsequently deleted when they do.
|
6
|
+
# The frequency at which the cache is checked for objects to delete is determined by the <tt>check_frequency</tt> parameter.
|
7
|
+
#
|
8
|
+
# See Boogaloo::Cache::Base[link:classes/Boogaloo/Cache/Base.html] for common cache methods.
|
9
|
+
class Temporary < Boogaloo::Cache::Base
|
10
|
+
|
11
|
+
# Initialize a new temporary cache instance.
|
12
|
+
#
|
13
|
+
# Parameters:
|
14
|
+
# +name+:: The name of the cache
|
15
|
+
# +config+:: A Hash containing configuration for the cache.
|
16
|
+
# Configuration options:
|
17
|
+
# +check_frequency+:: The frequency in seconds at which the cache will check for objects to delete.
|
18
|
+
def initialize(name, config)
|
19
|
+
|
20
|
+
super
|
21
|
+
|
22
|
+
Thread.new {
|
23
|
+
|
24
|
+
while true
|
25
|
+
|
26
|
+
sleep @config['check_frequency'].to_f
|
27
|
+
|
28
|
+
@global_store.keys.each do |key|
|
29
|
+
|
30
|
+
times_key = "#{key}_#{@global_store[key].object_id}"
|
31
|
+
|
32
|
+
if @times[times_key][:time] <= Time.now
|
33
|
+
|
34
|
+
@global_store.delete(key)
|
35
|
+
@times.delete(key)
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
@namespace_store.keys.each do |nskey|
|
42
|
+
|
43
|
+
@namespace_store[nskey].keys.each do |key|
|
44
|
+
|
45
|
+
times_key = "#{nskey}_#{key}_#{@namespace_store[nskey][key].object_id}"
|
46
|
+
|
47
|
+
if @times[times_key][:time] <= Time.now
|
48
|
+
|
49
|
+
@namespace_store[nskey].delete(key)
|
50
|
+
@times.delete(times_key)
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
}
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
# Add an object to the cache only if it doesn't already exist.
|
65
|
+
#
|
66
|
+
# Parameters:
|
67
|
+
# +namespace+:: The namespace in which the object will exist. Use <tt>nil</tt> if global namespace is desired.
|
68
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
69
|
+
# +obj+:: The object to add to the cache.
|
70
|
+
# +expires+:: The time in seconds which the object should exist in the cache without being touched before it is deleted.
|
71
|
+
def add(namespace, key, obj, expires)
|
72
|
+
|
73
|
+
check_expires(expires)
|
74
|
+
super(namespace, key, obj)
|
75
|
+
set_expiration(namespace, key, obj, expires)
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add an object to the cache, overwriting if one already exists.
|
80
|
+
#
|
81
|
+
# Parameters:
|
82
|
+
# +namespace+:: The namespace in which the object will exist. Use <tt>nil</tt> if global namespace should be used.
|
83
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
84
|
+
# +obj+:: The object to add to the cache.
|
85
|
+
# +expires+:: The time in seconds which the object should exist in the cache without being touched before it is deleted.
|
86
|
+
def set(namespace, key, obj, expires)
|
87
|
+
|
88
|
+
check_expires(expires)
|
89
|
+
super(namespace, key, obj)
|
90
|
+
set_expiration(namespace, key, obj, expires)
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get an object from the cache.
|
95
|
+
#
|
96
|
+
# Parameters:
|
97
|
+
# +namespace+:: The namespace to get the object from. Use <tt>nil</tt> if global namespace should be used.
|
98
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
99
|
+
def get(namespace, key)
|
100
|
+
|
101
|
+
ret = super
|
102
|
+
reset_expiration(namespace, key, ret) if not ret.nil?
|
103
|
+
ret
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
# Pull and object from the cache (get and delete).
|
108
|
+
#
|
109
|
+
# Parameters:
|
110
|
+
# +namespace+:: The namespace to get the object from. Use <tt>nil</tt> if global namespace should be used.
|
111
|
+
# +key+:: A uniqeue identifier for the object relative to the given namespace.
|
112
|
+
def pull(namespace, key)
|
113
|
+
|
114
|
+
ret = super
|
115
|
+
@times.delete(format_times_key(namespace, key, ret)) if not ret.nil?
|
116
|
+
ret
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
def __times_size #:nodoc: Only used by unit test.
|
121
|
+
|
122
|
+
@times.size
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
def reset
|
127
|
+
|
128
|
+
@times = {}
|
129
|
+
super
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
def check_expires(expires) #:nodoc:
|
136
|
+
|
137
|
+
raise TypeError.new("Expected Fixnum or Float as expiration.") if not expires.is_a?(Fixnum) and not expires.is_a?(Float)
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_expiration(namespace, key, obj, expires) #:nodoc:
|
142
|
+
|
143
|
+
warning("Expiration time (#{expires}) less than check frequency (#{@config['check_frequency'].to_f}) for key '#{key}'#{" in namespace '#{namespace}'" if namespace}.") if expires.to_f < @config['check_frequency'].to_f
|
144
|
+
@times[format_times_key(namespace, key, obj)] = { :time => Time.now + expires, :duration => expires }
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
def reset_expiration(namespace, key, obj) #:nodoc:
|
149
|
+
|
150
|
+
times_key = format_times_key(namespace, key, obj)
|
151
|
+
@times[times_key][:time] = Time.now + @times[times_key][:duration]
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
def format_times_key(namespace, key, obj) #:nodoc:
|
156
|
+
|
157
|
+
if namespace.nil?
|
158
|
+
|
159
|
+
times_key = "#{key}_#{obj.object_id}"
|
160
|
+
|
161
|
+
else
|
162
|
+
|
163
|
+
times_key = "#{namespace}_#{key}_#{obj.object_id}"
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Boogaloo
|
2
|
+
|
3
|
+
module Client
|
4
|
+
|
5
|
+
# A wrapper around DRb for connecting to the boogaloo server.
|
6
|
+
#
|
7
|
+
# Example usage:
|
8
|
+
#
|
9
|
+
# boogaloo_cache = Boogaloo::Client::Connection.new('localhost', 7777, true)
|
10
|
+
class Connection
|
11
|
+
|
12
|
+
# Create a new connection to the boogaloo server.
|
13
|
+
#
|
14
|
+
# Parameters:
|
15
|
+
#
|
16
|
+
# +host+:: The host name or IP address to bind the socket to.
|
17
|
+
# +port+:: The port to bind the socket to.
|
18
|
+
# +wrap+:: If true, attempts to access the server when it is offline will result in a _nil_ being returned instead of an exception being raised.
|
19
|
+
def initialize(host, port, wrap=false)
|
20
|
+
|
21
|
+
@wrap = wrap
|
22
|
+
druby_str = "druby://#{host}:#{port}"
|
23
|
+
DRb.start_service
|
24
|
+
@conn = DRbObject.new(nil, druby_str)
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# Forwards all method calls to the server.
|
29
|
+
def method_missing(msg_id, *a, &b)
|
30
|
+
|
31
|
+
if @wrap
|
32
|
+
|
33
|
+
begin
|
34
|
+
|
35
|
+
return @conn.method_missing(msg_id, a, b)
|
36
|
+
|
37
|
+
rescue DRb::DRbConnError
|
38
|
+
|
39
|
+
return nil
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
else
|
44
|
+
|
45
|
+
return @conn.method_missing(msg_id, a, b)
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|