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.
Files changed (57) hide show
  1. data/CHANGELOG +2 -0
  2. data/DEBUGGING +4 -0
  3. data/MIT-LICENSE +7 -0
  4. data/README +106 -0
  5. data/RUNNING_UNIT_TESTS +5 -0
  6. data/Rakefile +44 -0
  7. data/bin/boogaloo +159 -0
  8. data/doc/classes/Boogaloo.html +149 -0
  9. data/doc/classes/Boogaloo/Cache.html +121 -0
  10. data/doc/classes/Boogaloo/Cache/Base.html +664 -0
  11. data/doc/classes/Boogaloo/Cache/Persistent.html +122 -0
  12. data/doc/classes/Boogaloo/Cache/Temporary.html +439 -0
  13. data/doc/classes/Boogaloo/Client.html +111 -0
  14. data/doc/classes/Boogaloo/Client/Connection.html +228 -0
  15. data/doc/classes/Boogaloo/Config.html +540 -0
  16. data/doc/classes/Boogaloo/ServiceGateway.html +176 -0
  17. data/doc/classes/Boogaloo/ServiceRequest.html +210 -0
  18. data/doc/classes/Boogaloo/ThreadPool.html +205 -0
  19. data/doc/classes/Boogaloo/WorkerThread.html +239 -0
  20. data/doc/created.rid +1 -0
  21. data/doc/files/DEBUGGING.html +113 -0
  22. data/doc/files/MIT-LICENSE.html +129 -0
  23. data/doc/files/README.html +261 -0
  24. data/doc/files/examples/boogaloo_conf.html +130 -0
  25. data/doc/files/lib/boogaloo/cache/base_rb.html +101 -0
  26. data/doc/files/lib/boogaloo/cache/persistent_rb.html +108 -0
  27. data/doc/files/lib/boogaloo/cache/temporary_rb.html +101 -0
  28. data/doc/files/lib/boogaloo/client/connection_rb.html +101 -0
  29. data/doc/files/lib/boogaloo/config_rb.html +109 -0
  30. data/doc/files/lib/boogaloo/service_gateway_rb.html +110 -0
  31. data/doc/files/lib/boogaloo/service_request_rb.html +108 -0
  32. data/doc/files/lib/boogaloo/thread_pool_rb.html +101 -0
  33. data/doc/files/lib/boogaloo/util_rb.html +240 -0
  34. data/doc/files/lib/boogaloo/version_rb.html +114 -0
  35. data/doc/files/lib/boogaloo/worker_thread_rb.html +101 -0
  36. data/doc/fr_class_index.html +38 -0
  37. data/doc/fr_file_index.html +41 -0
  38. data/doc/fr_method_index.html +67 -0
  39. data/doc/index.html +24 -0
  40. data/doc/rdoc-style.css +208 -0
  41. data/examples/boogaloo.conf +17 -0
  42. data/lib/boogaloo/cache/base.rb +222 -0
  43. data/lib/boogaloo/cache/persistent.rb +15 -0
  44. data/lib/boogaloo/cache/temporary.rb +173 -0
  45. data/lib/boogaloo/client/connection.rb +55 -0
  46. data/lib/boogaloo/config.rb +206 -0
  47. data/lib/boogaloo/service_gateway.rb +37 -0
  48. data/lib/boogaloo/service_request.rb +39 -0
  49. data/lib/boogaloo/thread_pool.rb +40 -0
  50. data/lib/boogaloo/util.rb +29 -0
  51. data/lib/boogaloo/version.rb +1 -0
  52. data/lib/boogaloo/worker_thread.rb +46 -0
  53. data/test/test_persistent_cache.rb +99 -0
  54. data/test/test_service_gateway.rb +27 -0
  55. data/test/test_synchronicity.rb +29 -0
  56. data/test/test_temporary_cache.rb +42 -0
  57. metadata +113 -0
@@ -0,0 +1,206 @@
1
+ require 'yaml'
2
+ require 'boogaloo/util'
3
+
4
+ module Boogaloo
5
+
6
+ # See examples/boogalooo.conf[link:files/examples/boogaloo_conf.html] for an example configuration file.
7
+ #
8
+ # = Available Configuration Options
9
+ #
10
+ # == Section: Server
11
+ #
12
+ # +host+:: (required) The host name or IP address to bind the socket to.
13
+ # +port+:: (required) The port to bind the socket to.
14
+ # +worker_threads+:: (required) Boogaloo uses a pool of threads created at start-up that wait for requests to handle. 3 is a reasonable default.
15
+ # +debug+:: (optional) Enable/disable debugging. See DEBUGGING[link:files/DEBUGGING.html] for for information.
16
+ #
17
+ # == Section: PersistentCaches
18
+ #
19
+ # +instance_name+:: (required) The name to use for accessing the cache on the client.
20
+ #
21
+ # Example:
22
+ #
23
+ # PersistentCaches:
24
+ #
25
+ # MyPersistentCache:
26
+ # instance_name: main_cache
27
+ #
28
+ # == Section: TemporaryCaches
29
+ #
30
+ # +instance_name+:: (required) The name to use for accessing the cache on the client.
31
+ # +check_frequency+:: (required) The frequency in seconds at which the cache will check for expired objects to delete.
32
+ #
33
+ # Example:
34
+ #
35
+ # TemporaryCaches:
36
+ #
37
+ # MyTempCache:
38
+ # instance_name: temp_cache
39
+ # check_frequency: 10
40
+ #
41
+ # == Section: Services
42
+ #
43
+ # +instance_name+:: (required) The name to use for accessing the service on the client.
44
+ # +path+:: (required) The path to the directory containing your interface.rb file.
45
+ # +parameters+:: (optional) If your interface constructor accepts parameters you can specify them hear.
46
+ # +require_path+:: (optional) If your service needs to require files not in the standard $LOAD_PATH, you can specify directories to add to the $LOAD_PATH here either as a list or in singular form.
47
+ #
48
+ # Example:
49
+ #
50
+ # Services:
51
+ #
52
+ # MyService:
53
+ # instance_name: my_service
54
+ # path: /home/me/BoogalooServices/MyService
55
+ # parameters:
56
+ # - Hello
57
+ # - World
58
+ # require_path:
59
+ # - /some/dir
60
+ # - /some/other/dir
61
+ #
62
+ class Config
63
+
64
+ # Initialize a new cofig instance.
65
+ #
66
+ # Parameters:
67
+ #
68
+ # +file+:: The configuration file to read.
69
+ def initialize(file)
70
+
71
+ @config = YAML.load File.read(file)
72
+ error("Server configuration not found.") if not @config.key?('Server')
73
+ error("host attribute missing for Server configuration.") if not @config['Server'].key?('host')
74
+ error("port attribute missing for Server configuration.") if not @config['Server'].key?('port')
75
+ error("worker_threads attribute missing for Server configuration.") if not @config['Server'].key?('worker_threads')
76
+
77
+ @persistent_caches = {}
78
+ @temporary_caches = {}
79
+ @services = {}
80
+
81
+ if @config['PersistentCaches']
82
+
83
+ @config['PersistentCaches'].each do |name, config|
84
+
85
+ error "name attribute missing for persistent cache." if not name or not name.is_a?(String)
86
+ error "configration missing for persistent cache '#{name}'." if not config.is_a?(Hash)
87
+ error "instance_name attribute missing for persistent cache '#{name}'." if not config.key?('instance_name')
88
+
89
+ conflict = nil
90
+ @persistent_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
91
+ error "instance name '#{config['instance_name']}' for '#{name}' conflicts with persistent cache '#{conflict}'." if conflict
92
+
93
+ @persistent_caches[name] = config
94
+
95
+ end
96
+
97
+ end
98
+
99
+ if @config['TemporaryCaches']
100
+
101
+ @config['TemporaryCaches'].each do |name, config|
102
+
103
+ error "name attribute missing for temporary cache." if not name or not name.is_a?(String)
104
+ error "configration missing for temporary cache '#{name}'." if not config.is_a?(Hash)
105
+ error "instance_name attribute missing for temporary cache '#{name}'." if not config.key?('instance_name')
106
+ error "check_frequency attribute missing for temporary cache '#{name}'." if not config.key?('check_frequency')
107
+
108
+ conflict = nil
109
+ @temporary_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
110
+ error "instance name '#{config['instance_name']}' for '#{name}' conflicts with temporary cache '#{conflict}'." if conflict
111
+
112
+ @persistent_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
113
+ error "instance name '#{config['instance_name']}' for '#{name}' conflicts with persistent cache '#{conflict}'." if conflict
114
+
115
+ @temporary_caches[name] = config
116
+
117
+ end
118
+
119
+ end
120
+
121
+ if @config['Services']
122
+
123
+ @config['Services'].each do |name, config|
124
+
125
+ error "name attribute missing for service." if not name or not name.is_a?(String)
126
+ error "configration missing for service cache '#{name}'." if not config.is_a?(Hash)
127
+ error "path attribute missing for service '#{name}'." if not config.key?('path')
128
+ error "instance_name attribute missing for service '#{name}'." if not config.key?('instance_name')
129
+
130
+ conflict = nil
131
+ @services.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
132
+ error "instance name '#{config['instance_name']}' for '#{name}' conflicts with service '#{conflict}'." if conflict
133
+
134
+ @temporary_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
135
+ error "instance name '#{config['instance_name']}' for '#{name}' conflicts with temporary cache '#{conflict}'." if conflict
136
+
137
+ @persistent_caches.each { |key, value| conflict = key and break if value['instance_name'].eql?(config['instance_name'])}
138
+ error "instance name '#{config['instance_name']}' for '#{name}' conflicts with persistent cache '#{conflict}'." if conflict
139
+
140
+ @services[name] = config
141
+
142
+ end
143
+
144
+ end
145
+
146
+ if @config['Server']['debug'] == true
147
+
148
+ $debug = true
149
+ notice("Debugging enabled")
150
+
151
+ end
152
+
153
+ end
154
+
155
+ # The entire _Server_ configuration section.
156
+ def server
157
+
158
+ @config['Server']
159
+
160
+ end
161
+
162
+ # The server host.
163
+ def server_host
164
+
165
+ server['host']
166
+
167
+ end
168
+
169
+ # The server port.
170
+ def server_port
171
+
172
+ server['port']
173
+
174
+ end
175
+
176
+ # The number of worker threads.
177
+ def server_worker_threads
178
+
179
+ server['worker_threads']
180
+
181
+ end
182
+
183
+ # The entire _PersistentCaches_ configuration section.
184
+ def persistent_caches
185
+
186
+ @persistent_caches
187
+
188
+ end
189
+
190
+ # The entire _TemporaryCaches_ configuration section.
191
+ def temporary_caches
192
+
193
+ @temporary_caches
194
+
195
+ end
196
+
197
+ # The entire _Services_ configuration section.
198
+ def services
199
+
200
+ @services
201
+
202
+ end
203
+
204
+ end
205
+
206
+ end
@@ -0,0 +1,37 @@
1
+ require 'boogaloo/thread_pool'
2
+ require 'boogaloo/worker_thread'
3
+ require 'boogaloo/service_request'
4
+
5
+ module Boogaloo
6
+
7
+ class ServiceGateway
8
+
9
+ # Initialize a new service gateway.
10
+ #
11
+ # Parameters:
12
+ #
13
+ # +pool+:: A reference to a ThreadPool
14
+ # +services+:: A Hash of instance_name/instance pairs.
15
+ def initialize(pool, services)
16
+
17
+ services.each do |name, instance|
18
+
19
+ instance_variable_set("@"+name, instance)
20
+
21
+ self.class.instance_eval do
22
+
23
+ define_method(name) do
24
+
25
+ Boogaloo::ServiceRequest.new(instance_variable_get("@"+name), pool)
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,39 @@
1
+ require 'boogaloo/util'
2
+
3
+ module Boogaloo
4
+
5
+ # Handles server requests using threads from the thread pool.
6
+ class ServiceRequest
7
+
8
+ # Initialize a new ServiceRequest instance.
9
+ #
10
+ # Parameters:
11
+ #
12
+ # +obj+:: The service instance for which to handle the request.
13
+ # +pool+:: A reference to the thread pool.
14
+ def initialize(obj, pool)
15
+
16
+ @obj = obj
17
+ @pool = pool
18
+
19
+ end
20
+
21
+ # Forward method calls to the service instance via a WorkerThread.
22
+ def method_missing(meth, *a, &b)
23
+
24
+ (t_start = Time.now) if $debug
25
+ worker = @pool.pop
26
+ debug("Waited #{"%.5f" % (Time.now - t_start)} seconds for available WorkerThread") if $debug and not t_start.nil?
27
+ worker.use_instance(@obj)
28
+ ret = worker.perform(meth, *a)
29
+ @pool.push worker
30
+
31
+ raise ret if (ret.is_a?(NoMethodError) or ret.is_a?(TypeError))
32
+
33
+ ret
34
+
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,40 @@
1
+ module Boogaloo
2
+
3
+ class ThreadPool < Queue
4
+
5
+ # Initailize a new thread pool.
6
+ #
7
+ # Parameters:
8
+ #
9
+ # +threads+:: The number of threads to populate the pool with.
10
+ def initialize(threads)
11
+
12
+ super()
13
+
14
+ @threads = threads
15
+
16
+ for i in 1..@threads
17
+
18
+ push(Boogaloo::WorkerThread.new)
19
+
20
+ end
21
+
22
+ end
23
+
24
+ # Shutdown the pool.
25
+ def shutdown
26
+
27
+ for i in 1..@threads
28
+
29
+ worker = pop
30
+ worker.exit
31
+
32
+ end
33
+
34
+ size == 0 ? true : false
35
+
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,29 @@
1
+ APP_NAME = 'Boogaloo'
2
+
3
+ # Output an error message to stderr and perform an immediate ungraceful server shutdown.
4
+ def error(msg)
5
+
6
+ $stderr.puts "[#{APP_NAME}] ERROR: #{msg}"
7
+ exit 1
8
+
9
+ end
10
+
11
+ # Output an information message.
12
+ def notice(msg)
13
+
14
+ puts "[#{APP_NAME}] #{msg}"
15
+
16
+ end
17
+
18
+ # Output a warning message to stderr.
19
+ def warning(msg)
20
+
21
+ $stderr.puts "[#{APP_NAME}] WARNING: #{msg}"
22
+
23
+ end
24
+
25
+ def debug(msg)
26
+
27
+ puts "[#{APP_NAME}] DEBUG: #{msg}"
28
+
29
+ end
@@ -0,0 +1 @@
1
+ PKG_VERSION = "0.1"
@@ -0,0 +1,46 @@
1
+ module Boogaloo
2
+
3
+ # Handles method calls to a service instance in a thread.
4
+ class WorkerThread < Thread
5
+
6
+ # Initialize a new WorkerThread instance.
7
+ def initialize
8
+
9
+ super {}
10
+ @obj = nil
11
+
12
+ end
13
+
14
+ # Tells the WorkerThread to forward method calls to the given service instance.
15
+ #
16
+ # Parameters:
17
+ #
18
+ # +obj+:: The service instance to forward to.
19
+ def use_instance(obj)
20
+
21
+ @obj = obj
22
+
23
+ end
24
+
25
+ # Perform method calls on the service instance.
26
+ def perform(meth, *a, &b)
27
+
28
+ ret = nil
29
+
30
+ begin
31
+
32
+ ret = @obj.send(meth, *a)
33
+
34
+ rescue NoMethodError, TypeError => e
35
+
36
+ return e
37
+
38
+ end
39
+
40
+ ret
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,99 @@
1
+ require 'drb'
2
+ require 'lib/boogaloo/cache/persistent'
3
+ require 'lib/boogaloo/util'
4
+
5
+ class TestPersistentCache < Test::Unit::TestCase
6
+
7
+ def test_persistent_cache
8
+
9
+ @cache = Boogaloo::Cache::Persistent.new('TestCache', nil)
10
+ do_cache_stats_test
11
+
12
+ # Test cache add
13
+ @cache.set("test_namespace", "test_key_1", "Hello, World!")
14
+ assert_equal @cache.stat_namespaces, 2
15
+ assert_equal @cache.stat_objects, 1
16
+ assert @cache.get("test_namespace", "test_key_1")
17
+ assert_equal @cache.get("test_namespace", "test_key_1"), "Hello, World!"
18
+ assert_equal @cache.stat_hits, 2
19
+
20
+ # Test cache set
21
+ @cache.set("test_namespace", "test_key_1", "Hello again, World!")
22
+ assert_equal @cache.stat_namespaces, 2
23
+ assert_equal @cache.stat_objects, 1
24
+ # set should overwrite the previous add
25
+ assert @cache.get("test_namespace", "test_key_1")
26
+ assert_equal @cache.get("test_namespace", "test_key_1"), "Hello again, World!"
27
+ assert_equal @cache.stat_hits, 4
28
+
29
+ # Test global namespace
30
+ @cache.set(nil, "test_key_2", "Hey")
31
+ assert_equal @cache.stat_namespaces, 2
32
+ assert_equal @cache.stat_objects, 2
33
+ assert @cache.get(nil, "test_key_2")
34
+ assert_equal @cache.get(nil, "test_key_2"), "Hey"
35
+ assert_equal @cache.stat_hits, 6
36
+
37
+ # Test cache miss
38
+ @cache.get("non_existent", "not there!")
39
+ @cache.get(nil, "booo")
40
+ assert_equal @cache.stat_misses, 2
41
+
42
+ # Test reset
43
+ @cache.reset
44
+ do_cache_stats_test
45
+
46
+ # Test inspect_keys
47
+ @cache.set(nil, "test_key_3", "hello!")
48
+ @cache.set("test_namespace", "test_key_4", "umbongo")
49
+ keys = @cache.inspect_keys
50
+ assert keys
51
+ assert keys.is_a?(Array)
52
+ assert_equal keys.size, 2
53
+ assert keys[0].is_a?(Array)
54
+ assert keys[1].is_a?(Hash)
55
+ assert_equal keys[0][0], "test_key_3"
56
+ assert keys[1].key?("test_namespace")
57
+ assert_equal keys[1]['test_namespace'][0], "test_key_4"
58
+
59
+ @cache.reset
60
+
61
+ # Test cache pull
62
+ @cache.set(nil, "test_key_4", "hello!")
63
+ assert_equal @cache.pull(nil, "test_key_4"), "hello!"
64
+ assert_equal @cache.get(nil, "test_key_4"), nil
65
+ assert_equal @cache.pull(nil, "test_key_4"), nil
66
+
67
+ # Test cache pull with namespace cleanup
68
+ @cache.set("pull_ns", "test_key_5", "hello!")
69
+ assert_equal @cache.stat_namespaces, 2
70
+ assert_equal @cache.pull("pull_ns", "test_key_5"), "hello!"
71
+ assert_equal @cache.get("pull_ns", "test_key_5"), nil
72
+ assert_equal @cache.pull("pull_ns", "test_key_5"), nil
73
+ assert_equal @cache.stat_namespaces, 1
74
+
75
+ # Test nil error add/set
76
+ assert_kind_of TypeError, @cache.set(nil, "test_key_6", nil)
77
+ assert_kind_of TypeError, @cache.add(nil, "test_key_6", nil)
78
+
79
+ end
80
+
81
+ def do_cache_stats_test
82
+
83
+ stats = @cache.stat_all
84
+ assert @cache.stat_all.size == 5
85
+ assert stats[0].is_a?(Time)
86
+ assert stats[1] == 0
87
+ assert stats[2] == 0
88
+ assert stats[3] == 1
89
+ assert stats[4] == 0
90
+
91
+ assert @cache.stat_reset_at == stats[0]
92
+ assert @cache.stat_hits == stats[1]
93
+ assert @cache.stat_misses == stats[2]
94
+ assert @cache.stat_namespaces == stats[3]
95
+ assert @cache.stat_objects == stats[4]
96
+
97
+ end
98
+
99
+ end