sadie 0.1.8 → 0.1.9
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.
- 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
|