sadie 0.1.8 → 0.1.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +2 -1
- data/README +10 -8
- data/Rakefile +10 -0
- data/TODO +25 -2
- data/lib/lock_manager.rb +52 -0
- data/lib/sadie/version.rb +1 -1
- data/lib/sadie_session.rb +92 -43
- data/lib/sadie_storage_manager.rb +17 -2
- data/lib/storage/file.rb +35 -2
- data/lib/storage/memory.rb +22 -1
- data/lib/timestamp_queue.rb +103 -0
- data/spec/lock_manager.rb +147 -0
- data/spec/sadie_session.rb +43 -7
- data/spec/storage_manager.rb +46 -36
- data/spec/storage_mechanisms/file.rb +37 -0
- data/spec/storage_mechanisms/memory.rb +31 -13
- data/spec/timestamp_queue.rb +39 -0
- data/test/v2/test_installation/primers/minimal_filestormech.rb +6 -0
- data/test/v2/test_installation/primers/test_refresh.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2491a41d00ecae062b1fc3b7c4d0ae719b6cfeb
|
4
|
+
data.tar.gz: e041a331e9cbaaa65927628590191188d913d906
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b884cc2dc571abb642daaf670dc264114d668b9799f108b32c1494e600f37f367fdb682872a48f999592ebc2a273f7cadefa789009dcfabc54e25eab7e42b919
|
7
|
+
data.tar.gz: 760fac48dbf4a93de8b9dc6d076c40e5219a1b5e68faa82ec59e05913adc5de888fdde220231b80397d9a33d0ed88234d51afdd46e6302bd0060201f4bf601fe
|
data/CHANGELOG
CHANGED
@@ -26,4 +26,5 @@
|
|
26
26
|
[0.0.51] eacher bugfix. now correctly handles specific keys.
|
27
27
|
[0.0.52] code cleanup
|
28
28
|
[0.1.01] ** ground-up rewrite, ignore everything before this line if you're not a historian **
|
29
|
-
[0.1.8] new version now at capability pairing with old version.
|
29
|
+
[0.1.8] new version now at capability pairing with old version.
|
30
|
+
[0.1.9] added metadata for keys, abstracted expiry and refresh data structs into timestamp queues, abstracted various mutexes in session into lock manager object in preparation for distributed behavior
|
data/README
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
==About Sadie
|
2
2
|
|
3
|
-
Sadie is a general-purpose data server written in Ruby for other Rubyists.
|
3
|
+
Sadie is a general-purpose data server written in Ruby for other Rubyists. It is designed to be fast, but also resource efficient, storing data in memory or, optionally and on a key-by-key basis, in disk-based storage.
|
4
4
|
|
5
5
|
==Purpose
|
6
6
|
Imagine you work in the IT department of a cell phone company and your boss asks you to design a system that will:
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
* build a status page that shows a map with the operational status of all the cell towers and, for each one, the distance to the nearest field technician
|
8
|
+
* generate a shapefile and a spreadsheet of the same data that will be available and up-to-date on the company's intranet, 24/7
|
9
|
+
* when there's an outtage, send an SMS alert to the nearest field tech to the tower with the problem
|
10
10
|
|
11
11
|
This is all inter-related data and, for a developer, it's not all that difficult to start thinking about how to approach these problems.
|
12
12
|
|
@@ -23,6 +23,8 @@ For data that changes often, it's possible to expire data after a set amount of
|
|
23
23
|
|
24
24
|
Primers can also reference other primers so it's easy to hide complexity.
|
25
25
|
|
26
|
+
For large, less frequently accessed data, it is possible to specfiy that the value be stored to disk instead of memory.
|
27
|
+
|
26
28
|
==How to use it
|
27
29
|
Create a directory for sadie to operate in, say /var/sadie.
|
28
30
|
|
@@ -36,8 +38,8 @@ In the primers directory, create files that end in .rb that look like:
|
|
36
38
|
prime ["test.expires.nsecs"] do
|
37
39
|
|
38
40
|
expire :never
|
39
|
-
refresh 300
|
40
|
-
|
41
|
+
refresh 300 # generate this again every 5 minutes
|
42
|
+
store_in :memory # optional, :memory is the default. :file is also supported
|
41
43
|
after "test.expires.nsecs" do |key, val|
|
42
44
|
cool_notify 'ward@thecleavers.com', val
|
43
45
|
end
|
@@ -75,8 +77,8 @@ Do it! The github url for Sadie is at: [https://github.com/FredAtLandMetrics/sa
|
|
75
77
|
|
76
78
|
==Future versions
|
77
79
|
Future version of Sadie will:
|
78
|
-
|
79
|
-
|
80
|
+
* index on key string patterns
|
81
|
+
* make use of a redis storage mechanism to become scalable and distributed
|
80
82
|
|
81
83
|
==Where to get it
|
82
84
|
Sadie can be downloaded via its rubygems page[https://rubygems.org/gems/sadie] or from github[https://github.com/FredAtLandMetrics/sadie].
|
data/Rakefile
CHANGED
@@ -41,6 +41,16 @@ namespace :spec do
|
|
41
41
|
system "rspec spec/primer.rb"
|
42
42
|
end
|
43
43
|
|
44
|
+
desc "test timestamp queue"
|
45
|
+
task :timestamp_queue do
|
46
|
+
system "rspec spec/timestamp_queue.rb"
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "test lock_manager"
|
50
|
+
task :lock_manager do
|
51
|
+
system "rspec spec/lock_manager.rb"
|
52
|
+
end
|
53
|
+
|
44
54
|
desc "test storage manager"
|
45
55
|
task :storage_manager do
|
46
56
|
system "rspec spec/storage_manager.rb"
|
data/TODO
CHANGED
@@ -1,2 +1,25 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
==Preparing for distributed-ness
|
2
|
+
|
3
|
+
The main idea behind distributed Sadie is a bunch of Sadie servers that share an nfs volume and which make use of a Redis cluster. So I need to be careful that no single sadie server need be "special".
|
4
|
+
|
5
|
+
As it stands, the session has these things to consider:
|
6
|
+
|
7
|
+
* mutexes
|
8
|
+
* expiry data structure
|
9
|
+
* refresh data structure
|
10
|
+
* registered keys
|
11
|
+
|
12
|
+
For mutexes, I'm thinking a locking abstraction will do. Something like:
|
13
|
+
|
14
|
+
lock_id = acquire_lock( params )
|
15
|
+
release_lock( lock_id )
|
16
|
+
|
17
|
+
Once abstracted, this should be relatively straightforward to extend as needed for distributed-ness.
|
18
|
+
|
19
|
+
[ NOTE: lock abstraction is now done ]
|
20
|
+
|
21
|
+
The expiry and refresh data structures are currently implemented as red-black trees, but it would be ideal if there were a way to share this information amongst the sadie instances. I'm thinking it might make sense to only a single sadie instance to be expiring at a time (and then only for a set amount of time). Further, I'm thinking refresh could be synchronized such that only a single sadie instance can be finding a refreshable key at a time, but, once it's selected one, it releases its lock so that another instance can begin a search for a another refreshable.
|
22
|
+
|
23
|
+
[ NOTE: the expiry and refresh data structures are now maintained by a 'timestamp_queue' class that will make it easier to swap in redis-based functionality ]
|
24
|
+
|
25
|
+
The registered keys data structure can actually be maintained separately in each instance in as much as the primers should really be the same across the instances.
|
data/lib/lock_manager.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
class LockManager
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@locks = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def create( params )
|
10
|
+
systype = params[:systype].to_s
|
11
|
+
locktype = params[:locktype].to_s
|
12
|
+
lock_id = "#{systype}:#{locktype}"
|
13
|
+
@locks[lock_id] = Mutex.new unless @locks.has_key?( lock_id )
|
14
|
+
lock_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def acquire( lock_id )
|
18
|
+
if @locks[lock_id].try_lock
|
19
|
+
lock_id
|
20
|
+
else
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def release( lock_id )
|
26
|
+
if @locks.has_key?( lock_id )
|
27
|
+
@locks[lock_id].unlock
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def critical_section_insist( lock_id )
|
32
|
+
if block_given?
|
33
|
+
if @locks.has_key?( lock_id )
|
34
|
+
@locks[lock_id].synchronize do
|
35
|
+
yield
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def critical_section_try( lock_id )
|
42
|
+
if block_given?
|
43
|
+
if @locks.has_key?( lock_id )
|
44
|
+
if acquire( lock_id )
|
45
|
+
yield
|
46
|
+
release( lock_id )
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
data/lib/sadie/version.rb
CHANGED
data/lib/sadie_session.rb
CHANGED
@@ -3,26 +3,33 @@ require 'storage/memory'
|
|
3
3
|
require 'storage/file'
|
4
4
|
require 'primer'
|
5
5
|
require 'thread'
|
6
|
-
require '
|
6
|
+
require 'lock_manager'
|
7
|
+
require 'timestamp_queue'
|
7
8
|
|
8
9
|
class SadieSession
|
9
10
|
attr_accessor :primers_dirpath
|
10
11
|
|
11
12
|
def initialize( params )
|
12
13
|
|
13
|
-
|
14
|
-
@
|
15
|
-
@expiry_thread = Thread.new do
|
16
|
-
_expiry_loop
|
17
|
-
end
|
14
|
+
# init lock manager
|
15
|
+
@lockmgr = LockManager.new
|
18
16
|
|
19
|
-
|
20
|
-
@
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
# init expiry and refresh threads
|
18
|
+
@expiry_lock = @lockmgr.create( :systype => :session,
|
19
|
+
:locktype => :expiry )
|
20
|
+
@refresh_lock = @lockmgr.create( :systype => :session,
|
21
|
+
:locktype => :refresh )
|
22
|
+
|
23
|
+
@expiry_queue,@refresh_queue = TimestampQueue.new,TimestampQueue.new
|
24
|
+
|
25
|
+
_initialize_expiry_thread
|
26
|
+
_initialize_refresh_thread
|
24
27
|
|
28
|
+
|
29
|
+
# init registered key hash
|
25
30
|
@registered_key = {}
|
31
|
+
|
32
|
+
# init session operating parameters
|
26
33
|
@default_storage_mechanism = :memory
|
27
34
|
@file_storage_mechanism_dirpath = nil
|
28
35
|
unless params.nil?
|
@@ -30,7 +37,6 @@ class SadieSession
|
|
30
37
|
|
31
38
|
if params.has_key?( :primers_dirpath )
|
32
39
|
self.primers_dirpath = params[:primers_dirpath]
|
33
|
-
puts "initializing session with primer dirpath: #{self.primers_dirpath}"
|
34
40
|
_register_primers
|
35
41
|
end
|
36
42
|
|
@@ -45,18 +51,30 @@ class SadieSession
|
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
48
|
-
|
54
|
+
# init storage manager
|
55
|
+
@storagemgr_lock = @lockmgr.create( :systype => :session,
|
56
|
+
:locktype => :expiry )
|
49
57
|
@storage_manager = SadieStorageManager.new
|
50
|
-
@
|
58
|
+
@lockmgr.critical_section_insist( @storagemgr_lock ) do
|
51
59
|
@storage_manager.register_storage_mechanism :memory, SadieStorageMechanismMemory.new
|
52
60
|
@storage_manager.register_storage_mechanism :file, SadieStorageMechanismFile.new(:key_storage_dirpath => @file_storage_mechanism_dirpath)
|
53
61
|
end
|
62
|
+
|
54
63
|
end
|
55
64
|
|
56
|
-
def has_key?( key )
|
57
|
-
@
|
58
|
-
|
65
|
+
def has_key?( key, params )
|
66
|
+
ret = @storage_manager.has_key?( key )
|
67
|
+
include_primers = true
|
68
|
+
|
69
|
+
if ( params.is_a?( Hash ) ) && ( params.has_key?( :include_primers ) )
|
70
|
+
include_primers = params[:include_primers]
|
71
|
+
end
|
72
|
+
|
73
|
+
if ( ! ret ) && ( include_primers )
|
74
|
+
ret = primer_registered?( key )
|
59
75
|
end
|
76
|
+
|
77
|
+
ret
|
60
78
|
end
|
61
79
|
|
62
80
|
def primer_registered?( key )
|
@@ -64,23 +82,33 @@ class SadieSession
|
|
64
82
|
end
|
65
83
|
|
66
84
|
def unset( key )
|
67
|
-
@
|
85
|
+
@lockmgr.critical_section_insist( @storagemgr_lock ) do
|
68
86
|
@storage_manager.unset( key )
|
69
87
|
end
|
70
88
|
end
|
71
89
|
|
90
|
+
def has_metadata?( key )
|
91
|
+
@storage_manager.has_metadata?( key )
|
92
|
+
end
|
93
|
+
|
94
|
+
def metadata( key )
|
95
|
+
@storage_manager.metadata( key )
|
96
|
+
end
|
97
|
+
|
72
98
|
def set( keys, value, params=nil )
|
73
|
-
expires, mechanism = :never, @default_storage_mechanism
|
99
|
+
expires, mechanism, metadata = :never, @default_storage_mechanism, nil
|
74
100
|
unless params.nil?
|
75
101
|
if params.is_a? Hash
|
76
102
|
expires = params[:expire] if params.has_key?( :expire )
|
77
103
|
mechanism = params[:mechanism] if params.has_key?( :mechanism )
|
104
|
+
metadata = params[:metadata] if params.has_key?( :metadata )
|
78
105
|
end
|
79
106
|
end
|
80
|
-
@
|
107
|
+
@lockmgr.critical_section_insist( @storagemgr_lock ) do
|
81
108
|
@storage_manager.set( :keys => Array( keys ),
|
82
109
|
:value => value,
|
83
|
-
:mechanism => mechanism
|
110
|
+
:mechanism => mechanism,
|
111
|
+
:metadata => metadata )
|
84
112
|
end
|
85
113
|
_manage_expiry( keys, expires ) unless expires == :never || expires == :on_get
|
86
114
|
end
|
@@ -106,13 +134,25 @@ class SadieSession
|
|
106
134
|
|
107
135
|
private
|
108
136
|
|
137
|
+
def _initialize_refresh_thread
|
138
|
+
@refresh_thread = Thread.new do
|
139
|
+
_refresh_loop
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def _initialize_expiry_thread
|
144
|
+
@expiry_thread = Thread.new do
|
145
|
+
_expiry_loop
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
109
149
|
def _manage_expiry( keys, expires_seconds )
|
110
150
|
if ! expires_seconds.is_a?( Symbol ) && expires_seconds.to_i > 0
|
111
151
|
expires = expires_seconds.to_i + _current_time
|
112
152
|
unless Array(keys).empty?
|
113
153
|
Array(keys).each do |key|
|
114
|
-
@
|
115
|
-
@
|
154
|
+
@lockmgr.critical_section_insist( @expiry_lock ) do
|
155
|
+
@expiry_queue.insert( key, :timestamp => expires )
|
116
156
|
end
|
117
157
|
end
|
118
158
|
end
|
@@ -124,8 +164,8 @@ class SadieSession
|
|
124
164
|
refreshes = refresh_seconds.to_i + _current_time
|
125
165
|
unless Array(keys).empty?
|
126
166
|
Array(keys).each do |key|
|
127
|
-
@
|
128
|
-
@
|
167
|
+
@lockmgr.critical_section_insist( @refresh_lock ) do
|
168
|
+
@refresh_queue.insert( key, :timestamp => refreshes )
|
129
169
|
end
|
130
170
|
end
|
131
171
|
end
|
@@ -159,18 +199,27 @@ class SadieSession
|
|
159
199
|
|
160
200
|
def _expiry_pass
|
161
201
|
time_now_in_seconds = _current_time
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
202
|
+
|
203
|
+
loop do
|
204
|
+
break if @expiry_queue.empty?
|
205
|
+
|
206
|
+
ts,key = nil
|
207
|
+
|
208
|
+
keys_to_unset = nil
|
209
|
+
@lockmgr.critical_section_insist( @expiry_lock ) do
|
210
|
+
|
211
|
+
keys_to_unset = @expiry_queue.find( :all, :before => time_now_in_seconds )
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
unless keys_to_unset.nil?
|
216
|
+
keys_to_unset.each do |key|
|
167
217
|
unset key
|
168
|
-
else
|
169
|
-
@expire_schedule[ts] = key
|
170
|
-
break
|
171
218
|
end
|
172
219
|
end
|
173
|
-
|
220
|
+
|
221
|
+
end
|
222
|
+
|
174
223
|
end
|
175
224
|
|
176
225
|
def _refresh_loop
|
@@ -182,18 +231,18 @@ class SadieSession
|
|
182
231
|
|
183
232
|
def _refresh_pass
|
184
233
|
time_now_in_seconds = _current_time
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
234
|
+
unless @refresh_queue.empty?
|
235
|
+
keys = nil
|
236
|
+
@lockmgr.critical_section_insist( @refresh_lock ) do
|
237
|
+
keys = @refresh_queue.find(:all, :before => time_now_in_seconds)
|
238
|
+
end
|
239
|
+
|
240
|
+
unless keys.nil?
|
241
|
+
keys.each do | key |
|
189
242
|
_refresh key
|
190
|
-
else
|
191
|
-
@refresh_mutex.synchronize do
|
192
|
-
@refresh_schedule[ts] = key
|
193
|
-
end
|
194
|
-
break
|
195
243
|
end
|
196
244
|
end
|
245
|
+
end
|
197
246
|
end
|
198
247
|
|
199
248
|
def _refresh( key )
|
@@ -41,6 +41,14 @@ class SadieStorageManager
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
+
def has_metadata?( key )
|
45
|
+
@mechanisms[where_key?( key )].has_metadata?( key ) if has_key?( key )
|
46
|
+
end
|
47
|
+
|
48
|
+
def metadata( key )
|
49
|
+
@mechanisms[where_key?( key )].metadata( key ) if has_key?( key )
|
50
|
+
end
|
51
|
+
|
44
52
|
def set( params )
|
45
53
|
unless params.nil?
|
46
54
|
|
@@ -52,9 +60,16 @@ class SadieStorageManager
|
|
52
60
|
|
53
61
|
if params.has_key?( :keys ) && params[:keys].is_a?( Array ) &&
|
54
62
|
params.has_key?( :value )
|
55
|
-
|
63
|
+
has_metadata = false
|
64
|
+
if params.has_key?(:metadata) && params[:metadata].is_a?( Hash )
|
65
|
+
has_metadata = true
|
66
|
+
end
|
56
67
|
params[:keys].each do |key|
|
57
|
-
|
68
|
+
if has_metadata
|
69
|
+
@mechanisms[params[:mechanism]].set( key, params[:value], :metadata => params[:metadata] )
|
70
|
+
else
|
71
|
+
@mechanisms[params[:mechanism]].set( key, params[:value] )
|
72
|
+
end
|
58
73
|
end
|
59
74
|
end
|
60
75
|
end
|
data/lib/storage/file.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'sadie_storage_mechanism'
|
2
|
+
# require 'marshal'
|
3
|
+
|
2
4
|
class SadieStorageMechanismFile < SadieStorageMechanism
|
3
5
|
attr_accessor :key_storage_dirpath
|
4
6
|
|
@@ -13,9 +15,24 @@ class SadieStorageMechanismFile < SadieStorageMechanism
|
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
16
|
-
def set( key, value )
|
18
|
+
def set( key, value, params=nil )
|
17
19
|
_validate_keystorage_directory
|
18
20
|
File.open(_keyvalue_filepath(key), 'wb') { |file| file.write(value) }
|
21
|
+
unless params.nil?
|
22
|
+
if params.has_key? :metadata
|
23
|
+
_write_metadata_file( key, params[:metadata] )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def metadata( key )
|
29
|
+
contents = File.open( _metadata_filepath( key ), 'rb') { |f| f.read }
|
30
|
+
# puts "contents: #{contents}"
|
31
|
+
Marshal.load( contents ) if has_metadata?( key )
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_metadata?( key )
|
35
|
+
File.exists? _metadata_filepath( key )
|
19
36
|
end
|
20
37
|
|
21
38
|
def get( key )
|
@@ -36,15 +53,31 @@ class SadieStorageMechanismFile < SadieStorageMechanism
|
|
36
53
|
|
37
54
|
private
|
38
55
|
|
56
|
+
def _metadata_filepath( key )
|
57
|
+
_ensure_metadata_dirpath_exists
|
58
|
+
File.join( self.key_storage_dirpath, '.meta', key )
|
59
|
+
end
|
60
|
+
|
61
|
+
def _write_metadata_file( key, metadata_hash )
|
62
|
+
raise 'metadata must be a hash' unless ( ! metadata_hash.nil? ) && ( metadata_hash.is_a?( Hash ) )
|
63
|
+
_ensure_metadata_dirpath_exists
|
64
|
+
File.open( _metadata_filepath( key ), 'wb' ) { |file| file.write( Marshal.dump( metadata_hash ) ) }
|
65
|
+
end
|
66
|
+
|
39
67
|
def _validate_keystorage_directory
|
40
68
|
raise "Key storage directory (#{self.key_storage_dirpath}) does not exist" unless _keystorage_directory_exists?
|
41
69
|
end
|
42
70
|
|
43
71
|
def _keyvalue_filepath(key)
|
44
|
-
File.join(self.key_storage_dirpath,key)
|
72
|
+
File.join( self.key_storage_dirpath, key )
|
45
73
|
end
|
46
74
|
|
47
75
|
def _keystorage_directory_exists?
|
48
76
|
Dir.exists?( self.key_storage_dirpath )
|
49
77
|
end
|
78
|
+
|
79
|
+
def _ensure_metadata_dirpath_exists
|
80
|
+
dirpath = File.join( self.key_storage_dirpath, '.meta' )
|
81
|
+
Dir.mkdir( dirpath ) unless Dir.exists?( dirpath )
|
82
|
+
end
|
50
83
|
end
|
data/lib/storage/memory.rb
CHANGED
@@ -3,10 +3,16 @@ class SadieStorageMechanismMemory < SadieStorageMechanism
|
|
3
3
|
|
4
4
|
def initialize
|
5
5
|
@storage_hash = {}
|
6
|
+
@metadata = {}
|
6
7
|
end
|
7
8
|
|
8
|
-
def set( key, value )
|
9
|
+
def set( key, value, params=nil )
|
9
10
|
@storage_hash[key] = value
|
11
|
+
unless params.nil?
|
12
|
+
if params.has_key? :metadata
|
13
|
+
_write_metadata( key, params[:metadata] )
|
14
|
+
end
|
15
|
+
end
|
10
16
|
end
|
11
17
|
|
12
18
|
def get( key )
|
@@ -21,4 +27,19 @@ class SadieStorageMechanismMemory < SadieStorageMechanism
|
|
21
27
|
@storage_hash.has_key?( key )
|
22
28
|
end
|
23
29
|
|
30
|
+
def metadata( key )
|
31
|
+
@metadata[key] if has_metadata?( key )
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_metadata?( key )
|
35
|
+
@metadata.has_key?( key )
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def _write_metadata( key, metadata_hash )
|
41
|
+
raise 'metadata must be a hash' unless ( ! metadata_hash.nil? ) && ( metadata_hash.is_a?( Hash ) )
|
42
|
+
@metadata[key] = metadata_hash
|
43
|
+
end
|
44
|
+
|
24
45
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'rbtree'
|
2
|
+
class TimestampQueue
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@queue = MultiRBTree.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def insert( key, params=nil )
|
9
|
+
ts = nil
|
10
|
+
if ( params.is_a?( Hash ) ) &&
|
11
|
+
( params.has_key?( :timestamp ) )
|
12
|
+
ts = params[:timestamp]
|
13
|
+
else
|
14
|
+
ts = _current_time
|
15
|
+
end
|
16
|
+
@queue[ts] = key
|
17
|
+
end
|
18
|
+
|
19
|
+
def find( which, params=nil )
|
20
|
+
if which == :first
|
21
|
+
_find_first( params )
|
22
|
+
elsif which == :all
|
23
|
+
_find_all( params )
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def empty?
|
28
|
+
@queue.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def _get_functions( params )
|
34
|
+
testfunc,getfunc = nil,nil,nil
|
35
|
+
if params.is_a? Hash
|
36
|
+
if params.has_key? :before
|
37
|
+
thresh = params[:before]
|
38
|
+
testfunc = Proc.new { |x| (x < thresh) }
|
39
|
+
getfunc = Proc.new { |q| q.shift }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
[testfunc,getfunc]
|
43
|
+
end
|
44
|
+
|
45
|
+
def _find_first( params )
|
46
|
+
|
47
|
+
ret = nil
|
48
|
+
testfunc,getfunc = _get_functions(params)
|
49
|
+
unless testfunc.nil?
|
50
|
+
ts,key = getfunc.call(@queue)
|
51
|
+
# puts "testing: #{ts}, #{key}"
|
52
|
+
if testfunc.call(ts)
|
53
|
+
ret = _package_rec(ts,key,params)
|
54
|
+
else
|
55
|
+
@queue[ts] = key
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
ret
|
60
|
+
end
|
61
|
+
|
62
|
+
def _find_all( params )
|
63
|
+
ret = nil
|
64
|
+
testfunc,getfunc = _get_functions(params)
|
65
|
+
unless testfunc.nil?
|
66
|
+
|
67
|
+
retarray = []
|
68
|
+
loop do
|
69
|
+
break if @queue.empty?
|
70
|
+
ts,key = getfunc.call(@queue)
|
71
|
+
if testfunc.call(ts)
|
72
|
+
retarray.push _package_rec(ts,key,params)
|
73
|
+
else
|
74
|
+
@queue[ts] = key
|
75
|
+
break
|
76
|
+
end
|
77
|
+
end
|
78
|
+
ret = retarray unless retarray.empty?
|
79
|
+
end
|
80
|
+
ret
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
def _package_rec(timestamp,key,params)
|
85
|
+
if params.nil? || ! params.is_a?( Hash )
|
86
|
+
key
|
87
|
+
elsif params.has_key?( :as )
|
88
|
+
if params[:as] == :hash
|
89
|
+
{ :timestamp => timestamp,
|
90
|
+
:key => key }
|
91
|
+
else
|
92
|
+
key
|
93
|
+
end
|
94
|
+
else
|
95
|
+
key
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def _current_time
|
100
|
+
Time.now.to_i
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'lock_manager'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
describe LockManager do
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@lockmgr = LockManager.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should properly protect critical sections with acquire and release" do
|
12
|
+
@total = 0
|
13
|
+
@max = 0
|
14
|
+
t1 = Thread.new do
|
15
|
+
@total += 1
|
16
|
+
@max = @total if @total > @max
|
17
|
+
sleep rand(3).to_i
|
18
|
+
@total -= 1
|
19
|
+
end
|
20
|
+
t2 = Thread.new do
|
21
|
+
@total += 1
|
22
|
+
@max = @total if @total > @max
|
23
|
+
sleep rand(3).to_i
|
24
|
+
@total -= 1
|
25
|
+
end
|
26
|
+
t3 = Thread.new do
|
27
|
+
@total += 1
|
28
|
+
@max = @total if @total > @max
|
29
|
+
sleep rand(3).to_i
|
30
|
+
@total -= 1
|
31
|
+
end
|
32
|
+
sleep 5
|
33
|
+
t1.join
|
34
|
+
t2.join
|
35
|
+
t3.join
|
36
|
+
( @max > 1 ).should be_true
|
37
|
+
|
38
|
+
lockid = @lockmgr.create( :systype => 'test', :locktype => 'test' )
|
39
|
+
@total = 0
|
40
|
+
@max = 0
|
41
|
+
t1 = Thread.new do
|
42
|
+
3.times do
|
43
|
+
unless @lockmgr.acquire( lockid ).nil?
|
44
|
+
@total += 1
|
45
|
+
@max = @total if @total > @max
|
46
|
+
sleep rand(3).to_i
|
47
|
+
@total -= 1
|
48
|
+
@lockmgr.release( lockid )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
t2 = Thread.new do
|
53
|
+
3.times do
|
54
|
+
unless @lockmgr.acquire( lockid ).nil?
|
55
|
+
@total += 1
|
56
|
+
@max = @total if @total > @max
|
57
|
+
sleep rand(3).to_i
|
58
|
+
@total -= 1
|
59
|
+
@lockmgr.release( lockid )
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
t3 = Thread.new do
|
64
|
+
3.times do
|
65
|
+
unless @lockmgr.acquire( lockid ).nil?
|
66
|
+
@total += 1
|
67
|
+
@max = @total if @total > @max
|
68
|
+
sleep rand(3).to_i
|
69
|
+
@total -= 1
|
70
|
+
@lockmgr.release( lockid )
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
sleep 5
|
75
|
+
t1.join
|
76
|
+
t2.join
|
77
|
+
t3.join
|
78
|
+
( @max > 1 ).should be_false
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should properly protect critical sections with critical section insist" do
|
82
|
+
@total = 0
|
83
|
+
@max = 0
|
84
|
+
t1 = Thread.new do
|
85
|
+
@total += 1
|
86
|
+
@max = @total if @total > @max
|
87
|
+
sleep rand(3).to_i
|
88
|
+
@total -= 1
|
89
|
+
end
|
90
|
+
t2 = Thread.new do
|
91
|
+
@total += 1
|
92
|
+
@max = @total if @total > @max
|
93
|
+
sleep rand(3).to_i
|
94
|
+
@total -= 1
|
95
|
+
end
|
96
|
+
t3 = Thread.new do
|
97
|
+
@total += 1
|
98
|
+
@max = @total if @total > @max
|
99
|
+
sleep rand(3).to_i
|
100
|
+
@total -= 1
|
101
|
+
end
|
102
|
+
sleep 5
|
103
|
+
t1.join
|
104
|
+
t2.join
|
105
|
+
t3.join
|
106
|
+
( @max > 1 ).should be_true
|
107
|
+
|
108
|
+
lockid = @lockmgr.create( :systype => 'test', :locktype => 'test' )
|
109
|
+
@total = 0
|
110
|
+
@max = 0
|
111
|
+
t1 = Thread.new do
|
112
|
+
3.times do
|
113
|
+
@lockmgr.critical_section_insist( lockid ) do
|
114
|
+
@total += 1
|
115
|
+
@max = @total if @total > @max
|
116
|
+
sleep rand(3).to_i
|
117
|
+
@total -= 1
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
t2 = Thread.new do
|
122
|
+
3.times do
|
123
|
+
@lockmgr.critical_section_insist( lockid ) do
|
124
|
+
@total += 1
|
125
|
+
@max = @total if @total > @max
|
126
|
+
sleep rand(3).to_i
|
127
|
+
@total -= 1
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
t3 = Thread.new do
|
132
|
+
3.times do
|
133
|
+
@lockmgr.critical_section_insist( lockid ) do
|
134
|
+
@total += 1
|
135
|
+
@max = @total if @total > @max
|
136
|
+
sleep rand(3).to_i
|
137
|
+
@total -= 1
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
sleep 5
|
142
|
+
t1.join
|
143
|
+
t2.join
|
144
|
+
t3.join
|
145
|
+
( @max > 1 ).should be_false
|
146
|
+
end
|
147
|
+
end
|
data/spec/sadie_session.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'timestamp_queue'
|
2
3
|
require 'sadie_session'
|
3
4
|
require 'pp'
|
4
5
|
describe SadieSession do
|
@@ -38,7 +39,7 @@ describe SadieSession do
|
|
38
39
|
end
|
39
40
|
|
40
41
|
it "should not execute primer assign directives" do
|
41
|
-
@session.has_key?("minimal.primer").should be_false
|
42
|
+
@session.has_key?("minimal.primer", :include_primers => false).should be_false
|
42
43
|
end
|
43
44
|
|
44
45
|
it "should be possible to _get_ registered keys" do
|
@@ -51,16 +52,23 @@ describe SadieSession do
|
|
51
52
|
|
52
53
|
it "should be possible to expire on get" do
|
53
54
|
@session.get("test.expires.onget").should == "testval"
|
54
|
-
@session.has_key?("test.expires.onget").should be_false
|
55
|
+
@session.has_key?("test.expires.onget", :include_primers => false).should be_false
|
55
56
|
end
|
56
57
|
|
57
58
|
it "should put keys in the expire schedule" do
|
58
|
-
def @session.
|
59
|
-
|
59
|
+
def @session.get_expiry_queue
|
60
|
+
@expiry_queue
|
60
61
|
end
|
62
|
+
q = @session.get_expiry_queue
|
63
|
+
def q.in_expire_schedule?( key )
|
64
|
+
! @queue.values.index(key).nil?
|
65
|
+
end
|
66
|
+
# def @session.in_expire_schedule?( key )
|
67
|
+
# ( ! @expire_schedule.values.index(key).nil? )
|
68
|
+
# end
|
61
69
|
|
62
70
|
@session.get("test.expires.nsecs").should == "testval"
|
63
|
-
|
71
|
+
q.in_expire_schedule?("test.expires.nsecs").should be true
|
64
72
|
end
|
65
73
|
|
66
74
|
it "should expire keys using _expire_pass" do
|
@@ -71,9 +79,9 @@ describe SadieSession do
|
|
71
79
|
@session.stub(:_current_time).and_return(2,5,8,11,14)
|
72
80
|
@session.stub(:_expiry_loop).and_return(false)
|
73
81
|
@session.get("test.expires.nsecs").should == "testval"
|
74
|
-
@session.has_key?("test.expires.nsecs").should be_true
|
82
|
+
@session.has_key?("test.expires.nsecs", :include_primers => false).should be_true
|
75
83
|
@session.run_expiry_pass
|
76
|
-
@session.has_key?("test.expires.nsecs").should be_false
|
84
|
+
@session.has_key?("test.expires.nsecs", :include_primers => false).should be_false
|
77
85
|
end
|
78
86
|
|
79
87
|
it "should refresh keys" do
|
@@ -124,6 +132,34 @@ describe SadieSession do
|
|
124
132
|
@session.detect_storage_mechanism('key2').should == :file
|
125
133
|
end
|
126
134
|
|
135
|
+
it "should be possible for primer files to choose the storage mechanism" do
|
136
|
+
def @session.detect_storage_mechanism(key)
|
137
|
+
@storage_manager.where_key?( key )
|
138
|
+
end
|
139
|
+
val_mem = @session.get("minimal.primer")
|
140
|
+
val_file = @session.get("minimal.primer.file")
|
141
|
+
val_mem.should == "testval"
|
142
|
+
val_file.should == "testval_file"
|
143
|
+
@session.detect_storage_mechanism("minimal.primer").should == :memory
|
144
|
+
@session.detect_storage_mechanism("minimal.primer.file").should == :file
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should be able to set and retrieve metadata" do
|
148
|
+
test_metadata_hash = { :type => :string,
|
149
|
+
:importance => :huge }
|
150
|
+
test_metadata_hash_right = { :type => :string,
|
151
|
+
:importance => :huge }
|
152
|
+
test_metadata_hash_wrong = { :type => :string,
|
153
|
+
:importance => :mild }
|
154
|
+
@session.set( "test.key1", "test.value1", :metadata => test_metadata_hash )
|
155
|
+
@session.set( "test.key2", "test.value2" )
|
156
|
+
@session.has_metadata?( "test.key1" ).should be_true
|
157
|
+
@session.has_metadata?( "test.key2" ).should be_false
|
158
|
+
( @session.metadata( "test.key1" ) == test_metadata_hash_right ).should be_true
|
159
|
+
( @session.metadata( "test.key1" ) == test_metadata_hash_wrong ).should be_false
|
160
|
+
|
161
|
+
end
|
162
|
+
|
127
163
|
# --- SLOW!
|
128
164
|
if ENV.has_key?('SADIE_SESSION_TEST_TIMERS') && ENV['SADIE_SESSION_TEST_TIMERS'].to_i == 1
|
129
165
|
|
data/spec/storage_manager.rb
CHANGED
@@ -4,55 +4,65 @@ require 'storage_mechanisms/memory'
|
|
4
4
|
|
5
5
|
describe SadieStorageManager do
|
6
6
|
|
7
|
+
before :each do
|
8
|
+
@storage = SadieStorageManager.new
|
9
|
+
@mech = SadieStorageMechanismMemory.new
|
10
|
+
@storage.register_storage_mechanism :memory, @mech
|
11
|
+
end
|
12
|
+
|
7
13
|
it "should be able to get and set using the memory storage mechanism" do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
storage.set( :mechanism => :memory,
|
13
|
-
:keys => [ "simple.test" ],
|
14
|
-
:value => "test.value" )
|
15
|
-
mech.get( "simple.test" ).should == "test.value"
|
14
|
+
@storage.set( :mechanism => :memory,
|
15
|
+
:keys => [ "simple.test" ],
|
16
|
+
:value => "test.value" )
|
17
|
+
@mech.get( "simple.test" ).should == "test.value"
|
16
18
|
end
|
17
19
|
|
18
20
|
it "should report a mechanism is registered after it has been registered" do
|
19
|
-
storage
|
20
|
-
mech = SadieStorageMechanismMemory.new
|
21
|
-
storage.register_storage_mechanism :memory, mech
|
22
|
-
storage.mechanism_is_registered?( :memory ).should be_true
|
21
|
+
@storage.mechanism_is_registered?( :memory ).should be_true
|
23
22
|
end
|
24
23
|
|
25
24
|
it "should have a functional has_key? method" do
|
26
|
-
storage
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
storage.
|
31
|
-
:value => "test.value",
|
32
|
-
:mechanism => :memory )
|
33
|
-
storage.has_key?( "test.key" ).should be_true
|
25
|
+
@storage.has_key?( "test.key" ).should be_false
|
26
|
+
@storage.set( :keys => ["test.key"],
|
27
|
+
:value => "test.value",
|
28
|
+
:mechanism => :memory )
|
29
|
+
@storage.has_key?( "test.key" ).should be_true
|
34
30
|
end
|
35
31
|
|
36
32
|
it "should have a functional get method" do
|
37
|
-
storage
|
38
|
-
|
39
|
-
|
40
|
-
storage.
|
41
|
-
:value => "test.value",
|
42
|
-
:mechanism => :memory )
|
43
|
-
storage.get( "test.key" ).should == "test.value"
|
33
|
+
@storage.set( :keys => ["test.key"],
|
34
|
+
:value => "test.value",
|
35
|
+
:mechanism => :memory )
|
36
|
+
@storage.get( "test.key" ).should == "test.value"
|
44
37
|
end
|
45
38
|
|
46
39
|
it "should have a functional unset method" do
|
47
|
-
storage
|
48
|
-
|
49
|
-
|
50
|
-
storage.
|
51
|
-
|
52
|
-
|
53
|
-
storage.has_key?( "test.key" ).should be_true
|
54
|
-
storage.unset "test.key"
|
55
|
-
storage.has_key?( "test.key" ).should be_false
|
40
|
+
@storage.set( :keys => ["test.key"],
|
41
|
+
:value => "test.value",
|
42
|
+
:mechanism => :memory )
|
43
|
+
@storage.has_key?( "test.key" ).should be_true
|
44
|
+
@storage.unset "test.key"
|
45
|
+
@storage.has_key?( "test.key" ).should be_false
|
56
46
|
end
|
57
47
|
|
48
|
+
it "should be able to set metadata for a key" do
|
49
|
+
@storage.set( :keys => ["test.key1"],
|
50
|
+
:value => "test.value1",
|
51
|
+
:mechanism => :memory )
|
52
|
+
test_metadata_hash = { :type => :string,
|
53
|
+
:importance => :huge }
|
54
|
+
test_metadata_hash_right = { :type => :string,
|
55
|
+
:importance => :huge }
|
56
|
+
test_metadata_hash_wrong = { :type => :string,
|
57
|
+
:importance => :mild }
|
58
|
+
@storage.set( :keys => ["test.key2"],
|
59
|
+
:value => "test.value2",
|
60
|
+
:mechanism => :memory,
|
61
|
+
:metadata => test_metadata_hash )
|
62
|
+
@storage.has_metadata?( "test.key1" ).should be_false
|
63
|
+
@storage.has_metadata?( "test.key2" ).should be_true
|
64
|
+
( @storage.metadata( "test.key2" ) == test_metadata_hash_right ).should be_true
|
65
|
+
( @storage.metadata( "test.key2" ) == test_metadata_hash_wrong ).should be_false
|
66
|
+
end
|
67
|
+
|
58
68
|
end
|
@@ -28,4 +28,41 @@ describe SadieStorageMechanismFile do
|
|
28
28
|
@mech.has_key?( "somekey.test" ).should be_false
|
29
29
|
end
|
30
30
|
|
31
|
+
it "should create a file when set is called" do
|
32
|
+
@mech.set 'somekey.test','some_value'
|
33
|
+
File.exists?( File.join( '/tmp/stormech-file-test','somekey.test' ) ).should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should delete a file when unset is called" do
|
37
|
+
@mech.set 'somekey.test','some_value'
|
38
|
+
File.exists?( File.join( '/tmp/stormech-file-test','somekey.test' ) ).should be_true
|
39
|
+
@mech.unset 'somekey.test'
|
40
|
+
File.exists?( File.join( '/tmp/stormech-file-test','somekey.test' ) ).should be_false
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should write metadata to a file" do
|
44
|
+
@mech.set 'somekey.test','some_value', :metadata => { :type => :string }
|
45
|
+
File.exists?( File.join( '/tmp/stormech-file-test','somekey.test' ) ).should be_true
|
46
|
+
File.exists?( File.join( '/tmp/stormech-file-test/.meta','somekey.test' ) ).should be_true
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should return the same metadata that was given to it" do
|
50
|
+
metadata_to_give = {
|
51
|
+
:type => :string,
|
52
|
+
:awesomeness_level => :excrutiatingly
|
53
|
+
}
|
54
|
+
metadata_to_test = {
|
55
|
+
:type => :string,
|
56
|
+
:awesomeness_level => :excrutiatingly
|
57
|
+
}
|
58
|
+
wrong_metadata_to_test = {
|
59
|
+
:type => :integer,
|
60
|
+
:awesomeness_level => :excrutiatingly
|
61
|
+
}
|
62
|
+
@mech.set 'somekey.test','some_value', :metadata => metadata_to_give
|
63
|
+
fetched_meta = @mech.metadata( 'somekey.test' )
|
64
|
+
( fetched_meta == metadata_to_test ).should be_true
|
65
|
+
( fetched_meta == wrong_metadata_to_test ).should be_false
|
66
|
+
end
|
67
|
+
|
31
68
|
end
|
@@ -1,28 +1,46 @@
|
|
1
1
|
require 'storage/memory'
|
2
2
|
describe SadieStorageMechanismMemory do
|
3
3
|
|
4
|
+
before :each do
|
5
|
+
@mech = SadieStorageMechanismMemory.new
|
6
|
+
end
|
4
7
|
it "should successfully return a set value" do
|
5
|
-
|
6
|
-
mech
|
7
|
-
mech.set 'somekey.test','some_value'
|
8
|
-
mech.get( 'somekey.test' ).should == 'some_value'
|
8
|
+
@mech.set 'somekey.test','some_value'
|
9
|
+
@mech.get( 'somekey.test' ).should == 'some_value'
|
9
10
|
|
10
11
|
end
|
11
12
|
|
12
13
|
it "should have a functional has_key? method" do
|
13
|
-
mech
|
14
|
-
mech.
|
15
|
-
mech.
|
16
|
-
mech.has_key?( "somekey.test" ).should be_true
|
14
|
+
@mech.has_key?( "somekey.test" ).should be_false
|
15
|
+
@mech.set 'somekey.test','some_value'
|
16
|
+
@mech.has_key?( "somekey.test" ).should be_true
|
17
17
|
end
|
18
18
|
|
19
19
|
it "should have a functional unset method" do
|
20
|
-
mech
|
21
|
-
mech.
|
22
|
-
mech.
|
23
|
-
mech.
|
24
|
-
mech.has_key?( "somekey.test" ).should be_false
|
20
|
+
@mech.set 'somekey.test','some_value'
|
21
|
+
@mech.has_key?( "somekey.test" ).should be_true
|
22
|
+
@mech.unset 'somekey.test'
|
23
|
+
@mech.has_key?( "somekey.test" ).should be_false
|
25
24
|
|
26
25
|
end
|
27
26
|
|
27
|
+
it "should return the same metadata that was given to it" do
|
28
|
+
metadata_to_give = {
|
29
|
+
:type => :string,
|
30
|
+
:awesomeness_level => :excrutiatingly
|
31
|
+
}
|
32
|
+
metadata_to_test = {
|
33
|
+
:type => :string,
|
34
|
+
:awesomeness_level => :excrutiatingly
|
35
|
+
}
|
36
|
+
wrong_metadata_to_test = {
|
37
|
+
:type => :integer,
|
38
|
+
:awesomeness_level => :excrutiatingly
|
39
|
+
}
|
40
|
+
@mech.set 'somekey.test','some_value', :metadata => metadata_to_give
|
41
|
+
fetched_meta = @mech.metadata( 'somekey.test' )
|
42
|
+
( fetched_meta == metadata_to_test ).should be_true
|
43
|
+
( fetched_meta == wrong_metadata_to_test ).should be_false
|
44
|
+
end
|
45
|
+
|
28
46
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'timestamp_queue'
|
3
|
+
|
4
|
+
describe TimestampQueue do
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@tsq = TimestampQueue.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should store a key using the current time if one is not provided" do
|
11
|
+
@tsq.stub(:_current_time).and_return(99)
|
12
|
+
@tsq.insert( 'testkey' )
|
13
|
+
rec = @tsq.find :first, :before => 100, :as => :hash
|
14
|
+
rec[:key].should == 'testkey'
|
15
|
+
rec[:timestamp].should == 99
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should return a key if the as parameter is not provided" do
|
19
|
+
@tsq.stub(:_current_time).and_return(99)
|
20
|
+
@tsq.insert( 'testkey' )
|
21
|
+
key = @tsq.find :first, :before => 100
|
22
|
+
key.should == 'testkey'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should return an array of records using :all" do
|
26
|
+
@tsq.stub(:_current_time).and_return(99,100,101,104)
|
27
|
+
@tsq.insert( 'testkey1' )
|
28
|
+
@tsq.insert( 'testkey2' )
|
29
|
+
@tsq.insert( 'testkey3' )
|
30
|
+
@tsq.insert( 'testkey4' )
|
31
|
+
keys = @tsq.find :all, :before => 102
|
32
|
+
keys.is_a?( Array ).should be_true
|
33
|
+
keys.empty?.should be_false
|
34
|
+
keys.length.should == 3
|
35
|
+
keys[0].should == 'testkey1'
|
36
|
+
keys[1].should == 'testkey2'
|
37
|
+
keys[2].should == 'testkey3'
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sadie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred McDavid
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sinatra
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- doc/planning/brainstorm.txt
|
97
97
|
- doc/planning/todo.examples.txt
|
98
98
|
- lib/.gitignore
|
99
|
+
- lib/lock_manager.rb
|
99
100
|
- lib/primer.rb
|
100
101
|
- lib/sadie/version.rb
|
101
102
|
- lib/sadie_server.rb
|
@@ -104,6 +105,7 @@ files:
|
|
104
105
|
- lib/sadie_storage_mechanism.rb
|
105
106
|
- lib/storage/file.rb
|
106
107
|
- lib/storage/memory.rb
|
108
|
+
- lib/timestamp_queue.rb
|
107
109
|
- rdoc/classes/Sadie.html
|
108
110
|
- rdoc/created.rid
|
109
111
|
- rdoc/files/README.html
|
@@ -116,6 +118,7 @@ files:
|
|
116
118
|
- rdoc/index.html
|
117
119
|
- rdoc/rdoc-style.css
|
118
120
|
- sadie.gemspec
|
121
|
+
- spec/lock_manager.rb
|
119
122
|
- spec/primer.rb
|
120
123
|
- spec/sadie_server.rb
|
121
124
|
- spec/sadie_server_lib.rb
|
@@ -123,10 +126,12 @@ files:
|
|
123
126
|
- spec/storage_manager.rb
|
124
127
|
- spec/storage_mechanisms/file.rb
|
125
128
|
- spec/storage_mechanisms/memory.rb
|
129
|
+
- spec/timestamp_queue.rb
|
126
130
|
- test/v2/another_test_installation/config/sadie.yml
|
127
131
|
- test/v2/another_test_installation/primers/minimal.rb
|
128
132
|
- test/v2/test_installation/config/sadie.yml
|
129
133
|
- test/v2/test_installation/primers/minimal.rb
|
134
|
+
- test/v2/test_installation/primers/minimal_filestormech.rb
|
130
135
|
- test/v2/test_installation/primers/onelevel/twolevel/test_subdir.rb
|
131
136
|
- test/v2/test_installation/primers/test_after_each.rb
|
132
137
|
- test/v2/test_installation/primers/test_after_key.rb
|