archipelago 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +19 -13
- data/TODO +3 -0
- data/lib/archipelago/current.rb +39 -12
- data/lib/archipelago/disco.rb +2 -10
- data/lib/archipelago/hashish.rb +36 -6
- data/lib/archipelago/pirate.rb +26 -4
- data/lib/archipelago/tranny.rb +6 -0
- data/lib/archipelago/treasure.rb +45 -14
- data/tests/current_test.rb +44 -0
- data/tests/pirate_test.rb +59 -3
- data/tests/tranny_test.rb +1 -1
- data/tests/treasure_benchmark.rb +1 -1
- data/tests/treasure_test.rb +61 -3
- metadata +2 -2
data/README
CHANGED
@@ -13,27 +13,33 @@ Archipelago::Hashish:: A hash-like tool that provides transparent persistence.
|
|
13
13
|
Archipelago::Treasure:: A distributed object database where the objects never leave the database, instead you do your operations upon references to the objects. It has support for serializably isolated transactions with optimistic concurrency control using Archipelago::Tranny or any transaction manager with similar semantics.
|
14
14
|
Archipelago::Pirate:: A client tool to allocate Archipelago::Treasure::Chests for different keys and act like an almost normal local Hash for providing distributed and scaleable object database facilities to any ruby application.
|
15
15
|
|
16
|
-
==
|
16
|
+
== Usage:
|
17
|
+
|
18
|
+
To use archipelago in the simplest and most obvious way, just run the script/services.rb script.
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
You can run this on any number of machines in your local network, and they will all communicate through their own
|
21
|
+
discovery services.
|
20
22
|
|
21
|
-
|
23
|
+
Then you instantiate an Archipelago::Pirate::Captain anywhere in the network. This instance can be used basically as
|
24
|
+
a normal Hash - most everything will be transparently hidden from you.
|
22
25
|
|
23
|
-
|
26
|
+
Everything you put in this instance will be persistently stored in the network.
|
24
27
|
|
25
|
-
|
28
|
+
== Examples:
|
26
29
|
|
27
|
-
|
30
|
+
To run a transaction manager and a database server, just do the following (from script/services.rb):
|
28
31
|
|
29
|
-
|
32
|
+
:include:script/services.rb
|
33
|
+
|
34
|
+
To set up an Archipelago::Pirate::Captain do the following (from script/pirate.rb):
|
30
35
|
|
31
36
|
:include:script/pirate.rb
|
32
37
|
|
33
|
-
|
38
|
+
Or you can run script/console, which starts an irb and loads script/pirate.rb.
|
39
|
+
|
40
|
+
So, to set up a test environment to play around with in a few simple steps, run the following
|
34
41
|
commands in a few terminals:
|
35
42
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
* script/console
|
43
|
+
script/services.rb /tmp/services1
|
44
|
+
script/services.rb /tmp/services2
|
45
|
+
script/console
|
data/TODO
CHANGED
@@ -6,6 +6,9 @@
|
|
6
6
|
check whether the instance before the call differs from the instance after
|
7
7
|
the call. Preferably without incurring performance lossage.
|
8
8
|
|
9
|
+
* Make Dubloon proxy targets able to provide a dirty_state method that decides
|
10
|
+
whether they are to be considered dirty, clean or auto-select.
|
11
|
+
|
9
12
|
* Test the transaction recovery mechanism of Chest.
|
10
13
|
|
11
14
|
* Make Archipelago::Treasure::Dubloons work after an Archipelago::Treasure::Chest
|
data/lib/archipelago/current.rb
CHANGED
@@ -36,47 +36,58 @@ module Archipelago
|
|
36
36
|
#
|
37
37
|
# Adds a few threaded methods to the normal ruby collections.
|
38
38
|
#
|
39
|
+
# The only method your class has to implement to use this module is <b>each(&block)</b>.
|
40
|
+
#
|
39
41
|
# NB: Will work slightly different than the unthreaded ones in certain circumstances.
|
40
42
|
#
|
41
43
|
module ThreadedCollection
|
42
44
|
|
43
45
|
#
|
44
|
-
# Like each, except calls +block+ within a new thread.
|
46
|
+
# Like each, except calls +block+ or +callable+ within a new thread.
|
45
47
|
#
|
46
|
-
def t_each(&block)
|
48
|
+
def t_each(callable = nil, &block)
|
49
|
+
raise "You have to provide either callable or block" if callable.nil? && block.nil?
|
50
|
+
|
47
51
|
threads = []
|
52
|
+
|
48
53
|
self.each do |args|
|
49
54
|
threads << Thread.new do
|
50
|
-
|
55
|
+
call_helper(callable, args, &block)
|
51
56
|
end
|
52
57
|
end
|
58
|
+
|
53
59
|
threads.each do |thread|
|
54
60
|
thread.join
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
58
64
|
#
|
59
|
-
# Like collect, except calls +block+ within a new thread.
|
65
|
+
# Like collect, except calls +block+ or +callable+ within a new thread.
|
60
66
|
#
|
61
|
-
def t_collect(&block)
|
67
|
+
def t_collect(callable = nil, &block)
|
68
|
+
raise "You have to provide either callable or block" if callable.nil? && block.nil?
|
69
|
+
|
62
70
|
result = []
|
63
71
|
result.extend(Synchronized)
|
64
72
|
self.t_each do |args|
|
73
|
+
new_value = call_helper(callable, args, &block)
|
65
74
|
result.synchronize do
|
66
|
-
result <<
|
75
|
+
result << new_value
|
67
76
|
end
|
68
77
|
end
|
69
78
|
return result
|
70
79
|
end
|
71
80
|
|
72
81
|
#
|
73
|
-
# Like select, except calls +block+ within a new thread.
|
82
|
+
# Like select, except calls +block+ or +callable+ within a new thread.
|
74
83
|
#
|
75
|
-
def t_select(&block)
|
84
|
+
def t_select(callable = nil, &block)
|
85
|
+
raise "You have to provide either callable or block" if callable.nil? && block.nil?
|
86
|
+
|
76
87
|
result = []
|
77
88
|
result.extend(Synchronized)
|
78
89
|
self.t_each do |args|
|
79
|
-
matches =
|
90
|
+
matches = call_helper(callable, args, &block)
|
80
91
|
result.synchronize do
|
81
92
|
result << args
|
82
93
|
end if matches
|
@@ -85,13 +96,15 @@ module Archipelago
|
|
85
96
|
end
|
86
97
|
|
87
98
|
#
|
88
|
-
# Like reject, except calls +block+ within a new thread.
|
99
|
+
# Like reject, except calls +block+ or +callable+ within a new thread.
|
89
100
|
#
|
90
|
-
def t_reject(&block)
|
101
|
+
def t_reject(callable = nil, &block)
|
102
|
+
raise "You have to provide either callable or block" if callable.nil? && block.nil?
|
103
|
+
|
91
104
|
result = []
|
92
105
|
result.extend(Synchronized)
|
93
106
|
self.t_each do |args|
|
94
|
-
matches =
|
107
|
+
matches = call_helper(callable, args, &block)
|
95
108
|
result.synchronize do
|
96
109
|
result << args
|
97
110
|
end unless matches
|
@@ -99,6 +112,20 @@ module Archipelago
|
|
99
112
|
return result
|
100
113
|
end
|
101
114
|
|
115
|
+
private
|
116
|
+
|
117
|
+
def call_helper(o = nil, args = nil, &block)
|
118
|
+
if o
|
119
|
+
if Array === args
|
120
|
+
return o.call(*args)
|
121
|
+
else
|
122
|
+
return o.call(args)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
return yield(args)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
102
129
|
end
|
103
130
|
|
104
131
|
|
data/lib/archipelago/disco.rb
CHANGED
@@ -209,11 +209,7 @@ module Archipelago
|
|
209
209
|
#
|
210
210
|
def method_missing(meth, *args, &block)
|
211
211
|
if @attributes.respond_to?(meth)
|
212
|
-
|
213
|
-
@attributes.send(meth, *args, &block)
|
214
|
-
else
|
215
|
-
@attributes.send(meth, *args)
|
216
|
-
end
|
212
|
+
@attributes.send(meth, *args, &block)
|
217
213
|
else
|
218
214
|
super(*args)
|
219
215
|
end
|
@@ -296,11 +292,7 @@ module Archipelago
|
|
296
292
|
def method_missing(meth, *args, &block)
|
297
293
|
if @hash.respond_to?(meth)
|
298
294
|
synchronize do
|
299
|
-
|
300
|
-
@hash.send(meth, *args, &block)
|
301
|
-
else
|
302
|
-
@hash.send(meth, *args)
|
303
|
-
end
|
295
|
+
@hash.send(meth, *args, &block)
|
304
296
|
end
|
305
297
|
else
|
306
298
|
super(meth, *args, &block)
|
data/lib/archipelago/hashish.rb
CHANGED
@@ -62,8 +62,19 @@ module Archipelago
|
|
62
62
|
return Marshal.load(@content_db[Marshal.dump(key)])
|
63
63
|
end
|
64
64
|
#
|
65
|
+
# Returns true if this BerkeleyHashish include +key+.
|
66
|
+
#
|
67
|
+
def include?(key)
|
68
|
+
@content.include?(key) || !@content_db[Marshal.dump(key)].nil?
|
69
|
+
end
|
70
|
+
#
|
65
71
|
# Simply get the value for the +key+.
|
66
72
|
#
|
73
|
+
# Will call <b>value.load_hook</b> and send it
|
74
|
+
# a block that does the actuall insertion of the value
|
75
|
+
# into the live hash if <b>value.respond_to?(:load_hook)</b>
|
76
|
+
# if the value didnt exist in the live hash yet.
|
77
|
+
#
|
67
78
|
def [](key)
|
68
79
|
@lock.synchronize_on(key) do
|
69
80
|
|
@@ -94,7 +105,8 @@ module Archipelago
|
|
94
105
|
end
|
95
106
|
#
|
96
107
|
# Stores whatever is under +key+ if it is not the same as
|
97
|
-
# whats in the persistent db
|
108
|
+
# whats in the persistent db - if the +key+ actually has
|
109
|
+
# a representation in the db.
|
98
110
|
#
|
99
111
|
# Will call <b>value.save_hook(old_value)</b> and send
|
100
112
|
# it a block that does the actual saving if
|
@@ -105,11 +117,13 @@ module Archipelago
|
|
105
117
|
@lock.synchronize_on(key) do
|
106
118
|
|
107
119
|
serialized_key = Marshal.dump(key)
|
120
|
+
|
108
121
|
value = @content[key]
|
109
122
|
serialized_value = Marshal.dump(value)
|
110
|
-
|
111
|
-
write_to_db(key, serialized_key, serialized_value, value) if @content_db[serialized_key] != serialized_value
|
123
|
+
old_serialized_value = @content_db[serialized_key]
|
112
124
|
|
125
|
+
write_to_db(key, serialized_key, serialized_value, value) if old_serialized_value && old_serialized_value != serialized_value
|
126
|
+
|
113
127
|
end
|
114
128
|
end
|
115
129
|
#
|
@@ -198,13 +212,23 @@ module Archipelago
|
|
198
212
|
# Read +key+ from db and if it is found
|
199
213
|
# put it in the cache Hash.
|
200
214
|
#
|
215
|
+
# Will call <b>value.load_hook</b> and send it
|
216
|
+
# a block that does the actuall insertion of the value
|
217
|
+
# into the live hash if <b>value.respond_to?(:load_hook)</b>.
|
218
|
+
#
|
201
219
|
def get_from_db(key)
|
202
220
|
serialized_key = Marshal.dump(key)
|
203
221
|
serialized_value = @content_db[serialized_key]
|
204
222
|
return nil unless serialized_value
|
205
223
|
|
206
224
|
value = Marshal.load(serialized_value)
|
207
|
-
|
225
|
+
if value.respond_to?(:load_hook)
|
226
|
+
value.load_hook do
|
227
|
+
@content[key] = value
|
228
|
+
end
|
229
|
+
else
|
230
|
+
@content[key] = value
|
231
|
+
end
|
208
232
|
return value
|
209
233
|
end
|
210
234
|
end
|
@@ -242,15 +266,21 @@ module Archipelago
|
|
242
266
|
return db
|
243
267
|
end
|
244
268
|
#
|
245
|
-
# Closes databases opened by this instance
|
269
|
+
# Closes databases opened by this instance.
|
246
270
|
#
|
247
|
-
def
|
271
|
+
def close!
|
248
272
|
@berkeley_hashishes.each do |h|
|
249
273
|
h.close!
|
250
274
|
end
|
251
275
|
@bdb_dbs.each do |d|
|
252
276
|
d.close
|
253
277
|
end
|
278
|
+
end
|
279
|
+
#
|
280
|
+
# Closes databases opened by this instance and removes the persistent files.
|
281
|
+
#
|
282
|
+
def unlink!
|
283
|
+
close!
|
254
284
|
home = Pathname.new(@env.home)
|
255
285
|
@env.close
|
256
286
|
home.rmtree if home.exist?
|
data/lib/archipelago/pirate.rb
CHANGED
@@ -51,6 +51,15 @@ module Archipelago
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
#
|
55
|
+
# Raised when the transaction block failed to commit the transaction in the end.
|
56
|
+
#
|
57
|
+
class CommitFailedException < RuntimeError
|
58
|
+
def initialize(pirate, transaction)
|
59
|
+
super("#{pirate} failed to commit #{transaction}")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
54
63
|
INITIAL_SERVICE_UPDATE_INTERVAL = 1
|
55
64
|
MAXIMUM_SERVICE_UPDATE_INTERVAL = 60
|
56
65
|
CHEST_DESCRIPTION = {
|
@@ -111,6 +120,13 @@ module Archipelago
|
|
111
120
|
@yar_counter = 0
|
112
121
|
end
|
113
122
|
|
123
|
+
#
|
124
|
+
# Returns true if this Captain includes the given +key+, optionally within a +transaction+.
|
125
|
+
#
|
126
|
+
def include?(key, transaction = nil)
|
127
|
+
responsible_chest(key)[:service].include?(key, transaction || @transaction)
|
128
|
+
end
|
129
|
+
|
114
130
|
#
|
115
131
|
# Get a value from the distributed database network using a +key+,
|
116
132
|
# optionally within a +transaction+.
|
@@ -157,6 +173,13 @@ module Archipelago
|
|
157
173
|
return rval
|
158
174
|
end
|
159
175
|
|
176
|
+
#
|
177
|
+
# Returns our active transaction, if any.
|
178
|
+
#
|
179
|
+
def active_transaction
|
180
|
+
@transaction
|
181
|
+
end
|
182
|
+
|
160
183
|
#
|
161
184
|
# Execute +block+ within a transaction.
|
162
185
|
#
|
@@ -171,11 +194,10 @@ module Archipelago
|
|
171
194
|
@transaction = @trannies.values.first[:service].begin
|
172
195
|
begin
|
173
196
|
yield(@transaction)
|
174
|
-
@transaction
|
197
|
+
raise CommitFailedException.new(self, @transaction) unless @transaction.commit! == :commited
|
175
198
|
rescue Exception => e
|
176
199
|
@transaction.abort! unless @transaction.state == :aborted
|
177
|
-
|
178
|
-
pp e.backtrace
|
200
|
+
raise e
|
179
201
|
ensure
|
180
202
|
@transaction = nil
|
181
203
|
end
|
@@ -254,7 +276,7 @@ module Archipelago
|
|
254
276
|
def responsible_chest(key)
|
255
277
|
raise NoRemoteDatabaseAvailableException.new(self) if @chests.empty?
|
256
278
|
|
257
|
-
key_id = Digest::SHA1.
|
279
|
+
key_id = Digest::SHA1.hexdigest(Marshal.dump(key))
|
258
280
|
sorted_chest_ids = @chests.keys.sort
|
259
281
|
sorted_chest_ids.each do |id|
|
260
282
|
return @chests[id] if id > key_id
|
data/lib/archipelago/tranny.rb
CHANGED
data/lib/archipelago/treasure.rb
CHANGED
@@ -94,6 +94,19 @@ module Archipelago
|
|
94
94
|
#
|
95
95
|
# A proxy to something in the chest.
|
96
96
|
#
|
97
|
+
# This will do a very efficient masquerade as the object it proxies. When asked
|
98
|
+
# for its class or any other attribute it will forward the query to the proxied object.
|
99
|
+
#
|
100
|
+
# It can also be a part of a transaction, either because it was fetched from the chest
|
101
|
+
# within a transaction, or because it is the return value of a Dubloon#join call.
|
102
|
+
#
|
103
|
+
# In this case all forwarded methods will also be within the same transaction, and
|
104
|
+
# any change to the proxied object will be inside that transaction.
|
105
|
+
#
|
106
|
+
# If the proxied object itself needs to handle transaction semantics it can implement
|
107
|
+
# the <b>with_transaction(transaction, &block)</b> method, which will wrap the
|
108
|
+
# method call itself within the home Chest of the proxied object.
|
109
|
+
#
|
97
110
|
class Dubloon
|
98
111
|
#
|
99
112
|
# Remove all methods so that we look like our target.
|
@@ -328,6 +341,20 @@ module Archipelago
|
|
328
341
|
end
|
329
342
|
end
|
330
343
|
|
344
|
+
#
|
345
|
+
# Returns true if this Chest includes the given +key+, optionally within a +transaction+.
|
346
|
+
#
|
347
|
+
def include?(key, transaction = nil)
|
348
|
+
join!(transaction)
|
349
|
+
|
350
|
+
if transaction
|
351
|
+
return @snapshot_by_transaction[transaction].include?(key)
|
352
|
+
else
|
353
|
+
return @db.include?(key)
|
354
|
+
end
|
355
|
+
|
356
|
+
end
|
357
|
+
|
331
358
|
#
|
332
359
|
# Return the contents of this chest using a given +key+ and +transaction+.
|
333
360
|
#
|
@@ -402,6 +429,10 @@ module Archipelago
|
|
402
429
|
# Call an instance +method+ on whatever this chest holds at +key+
|
403
430
|
# with any +transaction+ and +args+.
|
404
431
|
#
|
432
|
+
# If a +transaction+ is provided <b>and</b> the value for the +key+
|
433
|
+
# <b>respond_to?(:with_transaction)</b> then the actual method call
|
434
|
+
# will be wrapped within the block sent to <b>with_transaction(transaction, &block)</b>.
|
435
|
+
#
|
405
436
|
def call_instance_method(key, method, transaction, *arguments, &block)
|
406
437
|
if transaction
|
407
438
|
return call_with_transaction(key, method, transaction, *arguments, &block)
|
@@ -602,7 +633,10 @@ module Archipelago
|
|
602
633
|
end
|
603
634
|
|
604
635
|
#
|
605
|
-
# Call a method within a transaction
|
636
|
+
# Call a +method+ within a +transaction+.
|
637
|
+
#
|
638
|
+
# If the object we want to run the +method+ on <b>respond_to?(:with_transaction)</b>
|
639
|
+
# then we will execute the actual +method+ within a block sent to the <b>with_transaction</b> method.
|
606
640
|
#
|
607
641
|
def call_with_transaction(key, method, transaction, *arguments, &block)
|
608
642
|
assert_transaction(transaction)
|
@@ -617,7 +651,15 @@ module Archipelago
|
|
617
651
|
raise UnknownObjectException.new(self, key, transaction) unless instance
|
618
652
|
|
619
653
|
begin
|
620
|
-
|
654
|
+
if instance.respond_to?(:with_transaction)
|
655
|
+
return_value = nil
|
656
|
+
instance.with_transaction(transaction) do
|
657
|
+
return_value = instance.send(method, *arguments, &block)
|
658
|
+
end
|
659
|
+
return return_value
|
660
|
+
else
|
661
|
+
return instance.send(method, *arguments, &block)
|
662
|
+
end
|
621
663
|
ensure
|
622
664
|
#
|
623
665
|
# Make sure we remember when this object was last changed according
|
@@ -630,17 +672,6 @@ module Archipelago
|
|
630
672
|
end
|
631
673
|
end
|
632
674
|
|
633
|
-
#
|
634
|
-
# Execute +m+ with arguments +a+ and block +b+ on +o+.
|
635
|
-
#
|
636
|
-
def execute(o, m, *a, &b)
|
637
|
-
if b
|
638
|
-
return o.send(m, *a, &b)
|
639
|
-
else
|
640
|
-
return o.send(m, *a)
|
641
|
-
end
|
642
|
-
end
|
643
|
-
|
644
675
|
#
|
645
676
|
# Call a method outside any transaction (ie inside a transaction of its own).
|
646
677
|
#
|
@@ -650,7 +681,7 @@ module Archipelago
|
|
650
681
|
raise UnknownObjectException(self, key) unless instance
|
651
682
|
|
652
683
|
begin
|
653
|
-
return
|
684
|
+
return instance.send(method, *arguments, &block)
|
654
685
|
ensure
|
655
686
|
@db.store_if_changed(key)
|
656
687
|
end
|
data/tests/current_test.rb
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
|
2
2
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
3
3
|
|
4
|
+
class HashCollector
|
5
|
+
attr_accessor :epa
|
6
|
+
def call(k,v)
|
7
|
+
@epa ||= {}
|
8
|
+
@epa[k] = v
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ArrayCollector
|
13
|
+
attr_accessor :epa
|
14
|
+
def call(e)
|
15
|
+
@epa ||= []
|
16
|
+
@epa << e
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
4
20
|
class CurrentTest < Test::Unit::TestCase
|
5
21
|
|
6
22
|
def test_synchronized
|
@@ -24,6 +40,24 @@ class CurrentTest < Test::Unit::TestCase
|
|
24
40
|
end
|
25
41
|
end
|
26
42
|
|
43
|
+
def test_lock_on
|
44
|
+
t = true
|
45
|
+
|
46
|
+
a = "hej"
|
47
|
+
a.extend(Archipelago::Current::Synchronized)
|
48
|
+
a.lock_on("epa")
|
49
|
+
Thread.new do
|
50
|
+
a.lock_on("epa")
|
51
|
+
t = false
|
52
|
+
end
|
53
|
+
Thread.pass
|
54
|
+
assert(t)
|
55
|
+
a.unlock_on("epa")
|
56
|
+
assert_within(0.5) do
|
57
|
+
!t
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
27
61
|
def test_threaded_collection
|
28
62
|
a = Array(10)
|
29
63
|
a.extend(Archipelago::Current::ThreadedCollection)
|
@@ -51,6 +85,16 @@ class CurrentTest < Test::Unit::TestCase
|
|
51
85
|
b = {}
|
52
86
|
a.t_each do |k,v| b[k] = v end
|
53
87
|
assert_equal(b,a)
|
88
|
+
|
89
|
+
c = HashCollector.new
|
90
|
+
a.t_each(c)
|
91
|
+
assert_equal(a, c.epa)
|
92
|
+
|
93
|
+
a = [1,2,3,4,5,6,7]
|
94
|
+
a.extend(Archipelago::Current::ThreadedCollection)
|
95
|
+
c = ArrayCollector.new
|
96
|
+
a.t_each(c)
|
97
|
+
assert_equal(a.sort, c.epa.sort)
|
54
98
|
end
|
55
99
|
|
56
100
|
end
|
data/tests/pirate_test.rb
CHANGED
@@ -25,14 +25,36 @@ class PirateTest < Test::Unit::TestCase
|
|
25
25
|
def teardown
|
26
26
|
@p.stop!
|
27
27
|
@c.stop!
|
28
|
-
@c.persistence_provider.unlink
|
28
|
+
@c.persistence_provider.unlink!
|
29
29
|
@c2.stop!
|
30
|
-
@c2.persistence_provider.unlink
|
30
|
+
@c2.persistence_provider.unlink!
|
31
31
|
@tm.stop!
|
32
|
-
@tm.persistence_provider.unlink
|
32
|
+
@tm.persistence_provider.unlink!
|
33
33
|
DRb.stop_service
|
34
34
|
end
|
35
35
|
|
36
|
+
def test_include
|
37
|
+
assert(!@p.include?("blabla"))
|
38
|
+
@p["blabla"] = "kissobjas"
|
39
|
+
assert(@p.include?("blabla"))
|
40
|
+
t = @tm.begin
|
41
|
+
assert(!@p.include?("hehu"))
|
42
|
+
assert(!@p.include?("hehu", t))
|
43
|
+
@p["hehu", t] = "brunte"
|
44
|
+
assert(!@p.include?("hehu"))
|
45
|
+
assert(@p.include?("hehu", t))
|
46
|
+
t.commit!
|
47
|
+
assert(@p.include?("hehu"))
|
48
|
+
t = @tm.begin
|
49
|
+
assert(!@p.include?("hehu2"))
|
50
|
+
assert(!@p.include?("hehu2", t))
|
51
|
+
@p["hehu2", t] = "brunte"
|
52
|
+
assert(!@p.include?("hehu2"))
|
53
|
+
assert(@p.include?("hehu2", t))
|
54
|
+
t.abort!
|
55
|
+
assert(!@p.include?("hehu2"))
|
56
|
+
end
|
57
|
+
|
36
58
|
def test_each
|
37
59
|
@p["oj"] = "bla"
|
38
60
|
@p["brunt"] = "ja"
|
@@ -89,6 +111,40 @@ class PirateTest < Test::Unit::TestCase
|
|
89
111
|
assert_equal(s1, s2)
|
90
112
|
end
|
91
113
|
|
114
|
+
def test_transaction
|
115
|
+
p2 = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
|
116
|
+
:tranny_description => {:class => "TestManager"})
|
117
|
+
assert_within(10) do
|
118
|
+
p2.chests.keys.sort == [@c.service_id, @c2.service_id].sort
|
119
|
+
end
|
120
|
+
assert_within(10) do
|
121
|
+
p2.trannies.keys == [@tm.service_id]
|
122
|
+
end
|
123
|
+
trans = nil
|
124
|
+
assert_raise(Archipelago::Pirate::CommitFailedException) do
|
125
|
+
@p.transaction do |trans|
|
126
|
+
assert_equal(trans.transaction_id, @p.active_transaction.transaction_id)
|
127
|
+
@p["hehu"] = "haha"
|
128
|
+
assert(!p2.include?("hehu"))
|
129
|
+
assert(!p2.include?("hehu", nil))
|
130
|
+
assert_equal(nil, p2.active_transaction)
|
131
|
+
trans2 = nil
|
132
|
+
p2.transaction do |trans2|
|
133
|
+
assert_equal(trans2.transaction_id, p2.active_transaction.transaction_id)
|
134
|
+
p2["hehu"] = "hoj"
|
135
|
+
assert_equal("haha", @p["hehu"])
|
136
|
+
assert_equal("hoj", @p["hehu", trans2])
|
137
|
+
end
|
138
|
+
assert_equal(:commited, trans2.state)
|
139
|
+
assert_equal(:active, trans.state)
|
140
|
+
assert_equal("hoj", p2["hehu"])
|
141
|
+
assert_equal("haha", p2["hehu", trans])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
assert_equal(:aborted, trans.state)
|
145
|
+
assert_equal("hoj", @p["hehu"])
|
146
|
+
end
|
147
|
+
|
92
148
|
def test_write_read_transaction
|
93
149
|
$T = true
|
94
150
|
@p["hej"] = "haha"
|
data/tests/tranny_test.rb
CHANGED
data/tests/treasure_benchmark.rb
CHANGED
data/tests/treasure_test.rb
CHANGED
@@ -7,6 +7,17 @@ class A < String
|
|
7
7
|
yield
|
8
8
|
$BURKMAT2 += 1
|
9
9
|
end
|
10
|
+
def load_hook(&block)
|
11
|
+
$BURKMAT3 += 1
|
12
|
+
yield
|
13
|
+
$BURKMAT4 += 1
|
14
|
+
end
|
15
|
+
def with_transaction(transaction, &block)
|
16
|
+
$BURKMAT7 = transaction
|
17
|
+
$BURKMAT5 += 1
|
18
|
+
yield
|
19
|
+
$BURKMAT6 += 1
|
20
|
+
end
|
10
21
|
end
|
11
22
|
|
12
23
|
class TreasureTest < Test::Unit::TestCase
|
@@ -18,15 +29,29 @@ class TreasureTest < Test::Unit::TestCase
|
|
18
29
|
@tm = TestManager.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("tranny1.db")))
|
19
30
|
$BURKMAT = 0
|
20
31
|
$BURKMAT2 = 0
|
32
|
+
$BURKMAT3 = 0
|
33
|
+
$BURKMAT4 = 0
|
34
|
+
$BURKMAT5 = 0
|
35
|
+
$BURKMAT6 = 0
|
36
|
+
$BURKMAT7 = nil
|
21
37
|
end
|
22
38
|
|
23
39
|
def teardown
|
24
|
-
@c.persistence_provider.unlink
|
25
|
-
@c2.persistence_provider.unlink
|
26
|
-
@tm.persistence_provider.unlink
|
40
|
+
@c.persistence_provider.unlink!
|
41
|
+
@c2.persistence_provider.unlink!
|
42
|
+
@tm.persistence_provider.unlink!
|
27
43
|
DRb.stop_service
|
28
44
|
end
|
29
45
|
|
46
|
+
def test_with_transaction
|
47
|
+
t = @tm.begin
|
48
|
+
@c["hehu"] = A.new("kiss")
|
49
|
+
@c["hehu", t].upcase!
|
50
|
+
assert_equal(1, $BURKMAT5)
|
51
|
+
assert_equal(1, $BURKMAT6)
|
52
|
+
assert(!$BURKMAT7.nil?)
|
53
|
+
end
|
54
|
+
|
30
55
|
def test_each
|
31
56
|
@c["oj"] = "bla"
|
32
57
|
@c["brunt"] = "ja"
|
@@ -37,6 +62,37 @@ class TreasureTest < Test::Unit::TestCase
|
|
37
62
|
assert_equal({"oj" => "bla", "brunt" => "ja"}, h)
|
38
63
|
end
|
39
64
|
|
65
|
+
def test_include
|
66
|
+
assert(!@c.include?("blabla"))
|
67
|
+
@c["blabla"] = "kissobjas"
|
68
|
+
assert(@c.include?("blabla"))
|
69
|
+
t = @tm.begin
|
70
|
+
assert(!@c.include?("hehu"))
|
71
|
+
assert(!@c.include?("hehu", t))
|
72
|
+
@c["hehu", t] = "brunte"
|
73
|
+
assert(!@c.include?("hehu"))
|
74
|
+
assert(@c.include?("hehu", t))
|
75
|
+
t.commit!
|
76
|
+
assert(@c.include?("hehu"))
|
77
|
+
t = @tm.begin
|
78
|
+
assert(!@c.include?("hehu2"))
|
79
|
+
assert(!@c.include?("hehu2", t))
|
80
|
+
@c["hehu2", t] = "brunte"
|
81
|
+
assert(!@c.include?("hehu2"))
|
82
|
+
assert(@c.include?("hehu2", t))
|
83
|
+
t.abort!
|
84
|
+
assert(!@c.include?("hehu2"))
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_around_load
|
88
|
+
@c["brunis"] = A.new("hirr")
|
89
|
+
@c.persistence_provider.close!
|
90
|
+
@c = TestChest.new(:persistence_provider => Archipelago::Hashish::BerkeleyHashishProvider.new(Pathname.new(__FILE__).parent.join("chest.db")))
|
91
|
+
a = @c["brunis"]
|
92
|
+
assert_equal(1, $BURKMAT3)
|
93
|
+
assert_equal(1, $BURKMAT4)
|
94
|
+
end
|
95
|
+
|
40
96
|
def test_around_save
|
41
97
|
s = A.new("hehu")
|
42
98
|
@c["oj"] = s
|
@@ -61,7 +117,9 @@ class TreasureTest < Test::Unit::TestCase
|
|
61
117
|
|
62
118
|
def test_store_load_update
|
63
119
|
s = "hehu"
|
120
|
+
assert(!@c.include?("oj"))
|
64
121
|
@c["oj"] = "hehu"
|
122
|
+
assert(@c.include?("oj"))
|
65
123
|
oj1 = @c["oj"]
|
66
124
|
assert_equal(oj1, @c["oj"])
|
67
125
|
assert_equal("hehu", oj1)
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: archipelago
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.2.
|
7
|
-
date: 2006-11-
|
6
|
+
version: 0.2.3
|
7
|
+
date: 2006-11-29 00:00:00 +01:00
|
8
8
|
summary: A set of tools for distributed computing in ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|