archipelago 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/TODO +12 -2
- data/lib/{current.rb → archipelago/current.rb} +0 -0
- data/lib/{disco.rb → archipelago/disco.rb} +33 -8
- data/lib/{hashish.rb → archipelago/hashish.rb} +1 -1
- data/lib/{pirate.rb → archipelago/pirate.rb} +99 -6
- data/lib/{tranny.rb → archipelago/tranny.rb} +20 -6
- data/lib/{treasure.rb → archipelago/treasure.rb} +109 -19
- data/lib/archipelago.rb +5 -5
- data/scripts/chest.rb +1 -1
- data/scripts/pirate.rb +14 -1
- data/scripts/tranny.rb +1 -1
- data/tests/current_test.rb +0 -1
- data/tests/disco_test.rb +32 -25
- data/tests/pirate_test.rb +27 -5
- data/tests/test_helper.rb +7 -2
- data/tests/tranny_test.rb +0 -2
- data/tests/treasure_test.rb +12 -4
- metadata +8 -8
data/TODO
CHANGED
@@ -1,3 +1,13 @@
|
|
1
1
|
|
2
|
-
* Create a new HashishProvider with built in redundancy,
|
3
|
-
using the Chord project: http://pdos.csail.mit.edu/chord/
|
2
|
+
* Create a new HashishProvider with built in redundancy,
|
3
|
+
for example using the Chord project: http://pdos.csail.mit.edu/chord/
|
4
|
+
|
5
|
+
* Make Chest aware about whether transactions have affected it 'for real' ie
|
6
|
+
check whether the instance before the call differs from the instance after
|
7
|
+
the call. Preferably without incurring performance lossage.
|
8
|
+
|
9
|
+
* Test the transaction recovery mechanism of Chest.
|
10
|
+
|
11
|
+
* Make Archipelago::Treasure::Dubloons work after an Archipelago::Treasure::Chest
|
12
|
+
has rebooted. For example: demand that the chest always run the same host+port
|
13
|
+
or make Dubloons able to lookup their home Chest by service_id.
|
File without changes
|
@@ -19,7 +19,7 @@ require 'socket'
|
|
19
19
|
require 'thread'
|
20
20
|
require 'ipaddr'
|
21
21
|
require 'pp'
|
22
|
-
require 'current'
|
22
|
+
require 'archipelago/current'
|
23
23
|
require 'drb'
|
24
24
|
require 'set'
|
25
25
|
|
@@ -83,6 +83,30 @@ module Archipelago
|
|
83
83
|
#
|
84
84
|
module Publishable
|
85
85
|
|
86
|
+
#
|
87
|
+
# Also add the ClassMethods to +base+.
|
88
|
+
#
|
89
|
+
def self.append_features(base)
|
90
|
+
super
|
91
|
+
base.extend(ClassMethods)
|
92
|
+
end
|
93
|
+
|
94
|
+
module ClassMethods
|
95
|
+
#
|
96
|
+
# Just load whatever we have in +s+.
|
97
|
+
#
|
98
|
+
def _load(s)
|
99
|
+
DRbObject._load(s)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Dump a DRbObject refering to us.
|
105
|
+
#
|
106
|
+
def _dump(dummy_param)
|
107
|
+
DRbObject.new(self)._dump(dummy_param)
|
108
|
+
end
|
109
|
+
|
86
110
|
#
|
87
111
|
# Will initialize this instance with @service_description and @jockey_options
|
88
112
|
# and merge these with the optionally given <i>:service_description</i> and
|
@@ -91,8 +115,8 @@ module Archipelago
|
|
91
115
|
def initialize_publishable(options = {})
|
92
116
|
@service_description = {
|
93
117
|
:service_id => service_id,
|
94
|
-
:validator =>
|
95
|
-
:service =>
|
118
|
+
:validator => self,
|
119
|
+
:service => self,
|
96
120
|
:class => self.class.name
|
97
121
|
}.merge(options[:service_description] || {})
|
98
122
|
@jockey_options = options[:jockey_options] || {}
|
@@ -106,7 +130,7 @@ module Archipelago
|
|
106
130
|
# <i>:service_description</i>.
|
107
131
|
#
|
108
132
|
def publish!(options = {})
|
109
|
-
@jockey ||= Archipelago::Disco::Jockey.new(@jockey_options.merge(options[:jockey_options] || {}))
|
133
|
+
@jockey ||= defined?(Archipelago::Disco::MC) ? Archipelago::Disco::MC : Archipelago::Disco::Jockey.new(@jockey_options.merge(options[:jockey_options] || {}))
|
110
134
|
@jockey.publish(Archipelago::Disco::Record.new(@service_description.merge(options[:service_description] || {})))
|
111
135
|
end
|
112
136
|
|
@@ -203,7 +227,7 @@ module Archipelago
|
|
203
227
|
# Initialize this Record with a hash that must contain an <i>:service_id</i> and a <i>:validator</i>.
|
204
228
|
#
|
205
229
|
def initialize(hash)
|
206
|
-
raise "Record must have
|
230
|
+
raise "Record must have a :service_id" unless hash.include?(:service_id)
|
207
231
|
raise "Record must have a :validator" unless hash.include?(:validator)
|
208
232
|
super(hash)
|
209
233
|
end
|
@@ -278,8 +302,6 @@ module Archipelago
|
|
278
302
|
#
|
279
303
|
class Jockey
|
280
304
|
|
281
|
-
attr_reader :new_service_semaphore
|
282
|
-
|
283
305
|
#
|
284
306
|
# Will create a Jockey service running on <i>:address</i> and <i>:port</i> or
|
285
307
|
# ADDRESS and PORT if none are given.
|
@@ -366,7 +388,7 @@ module Archipelago
|
|
366
388
|
#
|
367
389
|
# Stops all the threads in this instance.
|
368
390
|
#
|
369
|
-
def stop
|
391
|
+
def stop!
|
370
392
|
@listener_thread.kill
|
371
393
|
@unilistener_thread.kill
|
372
394
|
@shouter_thread.kill
|
@@ -412,6 +434,7 @@ module Archipelago
|
|
412
434
|
#
|
413
435
|
def publish(service)
|
414
436
|
if service.valid?
|
437
|
+
service[:published_at] = Time.now
|
415
438
|
@local_services[service[:service_id]] = service
|
416
439
|
@new_service_semaphore.broadcast
|
417
440
|
unless @thrifty_publishing
|
@@ -543,6 +566,8 @@ module Archipelago
|
|
543
566
|
|
544
567
|
end
|
545
568
|
|
569
|
+
MC = Jockey.new(defined?(MC_OPTIONS) ? MC_OPTIONS : {}) unless defined?(MC_DISABLED) && MC_DISABLED
|
570
|
+
|
546
571
|
end
|
547
572
|
|
548
573
|
end
|
@@ -15,9 +15,9 @@
|
|
15
15
|
# along with this program; if not, write to the Free Software
|
16
16
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
17
|
|
18
|
-
require 'disco'
|
19
|
-
require 'tranny'
|
20
|
-
require 'treasure'
|
18
|
+
require 'archipelago/disco'
|
19
|
+
require 'archipelago/tranny'
|
20
|
+
require 'archipelago/treasure'
|
21
21
|
require 'pp'
|
22
22
|
require 'drb'
|
23
23
|
require 'digest/sha1'
|
@@ -65,7 +65,7 @@ module Archipelago
|
|
65
65
|
# Archipelago::Treasure:Dubloons in them.
|
66
66
|
#
|
67
67
|
class Captain
|
68
|
-
attr_reader :chests, :treasure_map, :trannies
|
68
|
+
attr_reader :chests, :treasure_map, :trannies
|
69
69
|
#
|
70
70
|
# Initialize an instance using an Archipelago::Disco::Jockey with <i>:jockey_options</i>
|
71
71
|
# if given, that looks for new services <i>:initial_service_update_interval</i> or INITIAL_SERVICE_UPDATE_INTERVAL,
|
@@ -74,13 +74,20 @@ module Archipelago
|
|
74
74
|
# Will look for Archipelago::Treasure::Chests matching <i>:chest_description</i> or CHEST_DESCRIPTION and
|
75
75
|
# Archipelago::Tranny::Managers matching <i>:tranny_description</i> or TRANNY_DESCRIPTION.
|
76
76
|
#
|
77
|
+
# Will send off all <i>:chest_eval_files</i> to any chest found for possible evaluation to ensure existence
|
78
|
+
# of required classes and modules at the chest.
|
79
|
+
#
|
77
80
|
def initialize(options = {})
|
78
|
-
@treasure_map = Archipelago::Disco::Jockey.new(options[:jockey_options] || {})
|
81
|
+
@treasure_map = defined?(Archipelago::Disco::MC) ? Archipelago::Disco::MC : Archipelago::Disco::Jockey.new(options[:jockey_options] || {})
|
79
82
|
|
80
83
|
@chest_description = CHEST_DESCRIPTION.merge(options[:chest_description] || {})
|
81
84
|
@tranny_description = TRANNY_DESCRIPTION.merge(options[:tranny_description] || {})
|
82
85
|
@jockey_options = options[:jockey_options] || {}
|
83
86
|
|
87
|
+
@chest_eval_files = options[:chest_eval_files] || []
|
88
|
+
|
89
|
+
@chests_having_evaluated = {}
|
90
|
+
|
84
91
|
start_service_updater(options[:initial_service_update_interval] || INITIAL_SERVICE_UPDATE_INTERVAL,
|
85
92
|
options[:maximum_service_update_interval] || MAXIMUM_SERVICE_UPDATE_INTERVAL)
|
86
93
|
|
@@ -135,6 +142,38 @@ module Archipelago
|
|
135
142
|
return rval
|
136
143
|
end
|
137
144
|
|
145
|
+
#
|
146
|
+
# Execute +block+ within a transaction.
|
147
|
+
#
|
148
|
+
# Will commit! transaction after the block is finished unless
|
149
|
+
# the transaction is aborted or commited already.
|
150
|
+
#
|
151
|
+
# Will abort! the transaction if any exception is raised.
|
152
|
+
#
|
153
|
+
def transaction(&block) #:yields: transaction
|
154
|
+
raise NoTransactionManagerAvailableException.new(self) if @trannies.empty?
|
155
|
+
|
156
|
+
@transaction = @trannies.values.first[:service].begin
|
157
|
+
begin
|
158
|
+
yield(@transaction)
|
159
|
+
@transaction.commit! if @transaction.state == :active
|
160
|
+
rescue Exception => e
|
161
|
+
@transaction.abort! unless @transaction.state == :aborted
|
162
|
+
puts e
|
163
|
+
pp e.backtrace
|
164
|
+
ensure
|
165
|
+
@transaction = nil
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
#
|
170
|
+
# Evaluate this file in all known chests.
|
171
|
+
#
|
172
|
+
def evaluate!(filename)
|
173
|
+
@chest_eval_files << filename
|
174
|
+
evaluate_in_chests
|
175
|
+
end
|
176
|
+
|
138
177
|
#
|
139
178
|
# Commit the transaction we are a member of and forget about it.
|
140
179
|
#
|
@@ -159,6 +198,13 @@ module Archipelago
|
|
159
198
|
'yar!'
|
160
199
|
end
|
161
200
|
|
201
|
+
#
|
202
|
+
# Stops the service update thread for this Pirate.
|
203
|
+
#
|
204
|
+
def stop!
|
205
|
+
@service_update_thread.kill
|
206
|
+
end
|
207
|
+
|
162
208
|
private
|
163
209
|
|
164
210
|
#
|
@@ -175,14 +221,60 @@ module Archipelago
|
|
175
221
|
return @chests[sorted_chest_ids.first]
|
176
222
|
end
|
177
223
|
|
224
|
+
#
|
225
|
+
# Make sure all our known chests have evaluated
|
226
|
+
# all files we need them to.
|
227
|
+
#
|
228
|
+
def evaluate_in_chests
|
229
|
+
#
|
230
|
+
# For all chests
|
231
|
+
#
|
232
|
+
@chests.values.each do |chest|
|
233
|
+
#
|
234
|
+
# Ensure that this chest has a Set of evaluated files
|
235
|
+
#
|
236
|
+
@chests_having_evaluated[
|
237
|
+
[
|
238
|
+
chest[:service_id],
|
239
|
+
chest[:published_at]
|
240
|
+
]
|
241
|
+
] ||= Set.new
|
242
|
+
@chest_eval_files.each do |filename|
|
243
|
+
unless @chests_having_evaluated[
|
244
|
+
[
|
245
|
+
chest[:service_id],
|
246
|
+
chest[:published_at]
|
247
|
+
]
|
248
|
+
].include?(filename)
|
249
|
+
begin
|
250
|
+
chest[:service].evaluate!(filename,
|
251
|
+
File.ctime(filename),
|
252
|
+
open(filename).read)
|
253
|
+
rescue Exception => e
|
254
|
+
puts e
|
255
|
+
pp e.backtrace
|
256
|
+
ensure
|
257
|
+
@chests_having_evaluated[
|
258
|
+
[
|
259
|
+
chest[:service_id],
|
260
|
+
chest[:published_at]
|
261
|
+
]
|
262
|
+
] << filename
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
178
269
|
#
|
179
270
|
# Start a thread looking up existing chests between every
|
180
271
|
# +initial+ and +maximum+ seconds.
|
181
272
|
#
|
182
273
|
def start_service_updater(initial, maximum)
|
183
274
|
@chests = @treasure_map.lookup(Archipelago::Disco::Query.new(@chest_description), 0)
|
275
|
+
evaluate_in_chests
|
184
276
|
@trannies = @treasure_map.lookup(Archipelago::Disco::Query.new(@tranny_description), 0)
|
185
|
-
Thread.start do
|
277
|
+
@service_update_thread = Thread.start do
|
186
278
|
standoff = initial
|
187
279
|
loop do
|
188
280
|
begin
|
@@ -190,6 +282,7 @@ module Archipelago
|
|
190
282
|
standoff *= 2
|
191
283
|
standoff = maximum if standoff > maximum
|
192
284
|
@chests = @treasure_map.lookup(Archipelago::Disco::Query.new(@chest_description), 0)
|
285
|
+
evaluate_in_chests
|
193
286
|
@trannies = @treasure_map.lookup(Archipelago::Disco::Query.new(@tranny_description), 0)
|
194
287
|
rescue Exception => e
|
195
288
|
puts e
|
@@ -18,9 +18,10 @@
|
|
18
18
|
require 'bdb'
|
19
19
|
require 'pathname'
|
20
20
|
require 'drb'
|
21
|
-
require 'current'
|
21
|
+
require 'archipelago/current'
|
22
22
|
require 'digest/sha1'
|
23
|
-
require 'disco'
|
23
|
+
require 'archipelago/disco'
|
24
|
+
require 'archipelago/hashish'
|
24
25
|
|
25
26
|
module Archipelago
|
26
27
|
|
@@ -91,7 +92,6 @@ module Archipelago
|
|
91
92
|
#
|
92
93
|
class Manager
|
93
94
|
|
94
|
-
include DRb::DRbUndumped
|
95
95
|
include Archipelago::Disco::Publishable
|
96
96
|
|
97
97
|
attr_accessor :error_logger
|
@@ -113,7 +113,6 @@ module Archipelago
|
|
113
113
|
|
114
114
|
@transaction_timeout = options[:transaction_timeout] || TRANSACTION_TIMEOUT
|
115
115
|
|
116
|
-
@metadata = @persistence_provider.get_hashish("metadata")
|
117
116
|
@db = @persistence_provider.get_cached_hashish("db")
|
118
117
|
end
|
119
118
|
|
@@ -185,6 +184,7 @@ module Archipelago
|
|
185
184
|
attr_accessor :transaction_id
|
186
185
|
def initialize(transaction)
|
187
186
|
@manager = transaction.manager
|
187
|
+
@manager_id = transaction.manager.service_id
|
188
188
|
@transaction_id = transaction.transaction_id
|
189
189
|
@state = :unknown
|
190
190
|
end
|
@@ -222,7 +222,21 @@ module Archipelago
|
|
222
222
|
# returnvalue if necessary.
|
223
223
|
#
|
224
224
|
def method_missing(meth, *args) #:nodoc:
|
225
|
-
rval =
|
225
|
+
rval = nil
|
226
|
+
begin
|
227
|
+
rval = @manager.call_instance_method(@transaction_id, meth, *args)
|
228
|
+
rescue DRb::DRbConnError => e
|
229
|
+
if defined?(Archipelago::Disco::MC)
|
230
|
+
possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @manager_id}))
|
231
|
+
raise e if possible_replacements.empty?
|
232
|
+
|
233
|
+
@manager = possible_replacements[@manager_id][:service]
|
234
|
+
|
235
|
+
retry
|
236
|
+
else
|
237
|
+
raise e
|
238
|
+
end
|
239
|
+
end
|
226
240
|
case meth
|
227
241
|
when :abort!
|
228
242
|
@state = :aborted
|
@@ -346,7 +360,7 @@ module Archipelago
|
|
346
360
|
#
|
347
361
|
# We have a manager!
|
348
362
|
#
|
349
|
-
@manager =
|
363
|
+
@manager = manager
|
350
364
|
#
|
351
365
|
# We have a proxy to send forth into the world!
|
352
366
|
#
|
@@ -16,14 +16,14 @@
|
|
16
16
|
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
17
|
|
18
18
|
require 'drb'
|
19
|
-
require 'disco'
|
20
19
|
require 'bdb'
|
21
20
|
require 'pathname'
|
22
21
|
require 'digest/sha1'
|
23
22
|
require 'pp'
|
24
23
|
require 'set'
|
25
|
-
require 'hashish'
|
26
|
-
require 'tranny'
|
24
|
+
require 'archipelago/hashish'
|
25
|
+
require 'archipelago/tranny'
|
26
|
+
require 'archipelago/disco'
|
27
27
|
|
28
28
|
module Archipelago
|
29
29
|
|
@@ -35,6 +35,12 @@ module Archipelago
|
|
35
35
|
#
|
36
36
|
TRANSACTION_RECOVERY_INTERVAL = 30
|
37
37
|
|
38
|
+
#
|
39
|
+
# The known chests by service id to speed up
|
40
|
+
# recovery by Dubloons having lost their Chest.
|
41
|
+
#
|
42
|
+
CHESTS_BY_SERVICE_ID = {}
|
43
|
+
|
38
44
|
#
|
39
45
|
# Raised whenever the optimistic locking of the serializable transaction isolation level
|
40
46
|
# was proved wrong.
|
@@ -104,6 +110,36 @@ module Archipelago
|
|
104
110
|
@public_methods = public_methods
|
105
111
|
end
|
106
112
|
#
|
113
|
+
# A more or less normal dump of all our instance variables.
|
114
|
+
#
|
115
|
+
def _dump(dummy_levels)
|
116
|
+
Marshal.dump([
|
117
|
+
@key,
|
118
|
+
@chest,
|
119
|
+
@transaction,
|
120
|
+
@chest_id,
|
121
|
+
@public_methods
|
122
|
+
])
|
123
|
+
end
|
124
|
+
#
|
125
|
+
# Load some instance variables and replace @chest if we know that
|
126
|
+
# it actually is not correct.
|
127
|
+
#
|
128
|
+
def self._load(s)
|
129
|
+
key, chest, transaction, chest_id, public_methods = Marshal.load(s)
|
130
|
+
instance = self.allocate
|
131
|
+
instance.instance_variable_set(:@key, key)
|
132
|
+
instance.instance_variable_set(:@transaction, transaction)
|
133
|
+
instance.instance_variable_set(:@chest_id, chest_id)
|
134
|
+
instance.instance_variable_set(:@public_methods, public_methods)
|
135
|
+
if CHEST_BY_SERVICE_ID.include?(chest_id)
|
136
|
+
instance.instance_variable_set(:@chest, CHEST_BY_SERVICE_ID[chest_id])
|
137
|
+
else
|
138
|
+
instance.instance_variable_set(:@chest, chest)
|
139
|
+
end
|
140
|
+
return instance
|
141
|
+
end
|
142
|
+
#
|
107
143
|
# The public_methods of our target.
|
108
144
|
#
|
109
145
|
def public_methods
|
@@ -144,7 +180,21 @@ module Archipelago
|
|
144
180
|
#
|
145
181
|
def method_missing(meth, *args, &block)
|
146
182
|
if respond_to?(meth)
|
147
|
-
|
183
|
+
begin
|
184
|
+
return @chest.call_instance_method(@key, meth, @transaction, *args, &block)
|
185
|
+
rescue DRb::DRbConnError => e
|
186
|
+
if defined?(Archipelago::Disco::MC)
|
187
|
+
possible_replacements = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({:service_id => @chest_id}))
|
188
|
+
raise e if possible_replacements.empty?
|
189
|
+
|
190
|
+
@chest = possible_replacements[@chest_id][:service]
|
191
|
+
CHESTS_BY_SERVICE_ID[@chest_id] = @chest
|
192
|
+
|
193
|
+
retry
|
194
|
+
else
|
195
|
+
raise e
|
196
|
+
end
|
197
|
+
end
|
148
198
|
else
|
149
199
|
return super(meth, *args)
|
150
200
|
end
|
@@ -158,15 +208,8 @@ module Archipelago
|
|
158
208
|
#
|
159
209
|
# Has support for optimistically locked distributed serializable transactions.
|
160
210
|
#
|
161
|
-
# TODO: Test the transaction recovery mechanism.
|
162
|
-
#
|
163
211
|
class Chest
|
164
212
|
|
165
|
-
#
|
166
|
-
# The Chest never leaves its host.
|
167
|
-
#
|
168
|
-
include DRb::DRbUndumped
|
169
|
-
|
170
213
|
#
|
171
214
|
# The Chest can be published.
|
172
215
|
#
|
@@ -215,6 +258,8 @@ module Archipelago
|
|
215
258
|
#
|
216
259
|
@crashed = Set.new
|
217
260
|
|
261
|
+
initialize_seen_data
|
262
|
+
|
218
263
|
#
|
219
264
|
# The magical persistent map that defines how we actually
|
220
265
|
# store our data.
|
@@ -223,6 +268,7 @@ module Archipelago
|
|
223
268
|
|
224
269
|
initialize_prepared(options[:transaction_recovery_interval] || TRANSACTION_RECOVERY_INTERVAL)
|
225
270
|
|
271
|
+
CHESTS_BY_SERVICE_ID[self.service_id] = self
|
226
272
|
end
|
227
273
|
|
228
274
|
#
|
@@ -232,6 +278,32 @@ module Archipelago
|
|
232
278
|
@snapshot_by_transaction.keys.clone
|
233
279
|
end
|
234
280
|
|
281
|
+
#
|
282
|
+
# Evaluate +data+ if we have not already seen +label+ or if we have an earlier +timestamp+ than the one given.
|
283
|
+
#
|
284
|
+
def evaluate!(label, timestamp, data)
|
285
|
+
serialized_label = Marshal.dump(label)
|
286
|
+
go = false
|
287
|
+
|
288
|
+
if @seen_data.include?(serialized_label)
|
289
|
+
last_timestamp, last_data = Marshal.load(@seen_data[serialized_label])
|
290
|
+
go = true if timestamp > last_timestamp
|
291
|
+
else
|
292
|
+
go = true
|
293
|
+
end
|
294
|
+
|
295
|
+
if go
|
296
|
+
begin
|
297
|
+
Object.class_eval(data)
|
298
|
+
@seen_data[serialized_label] = Marshal.dump([timestamp, data])
|
299
|
+
rescue Exception => e
|
300
|
+
@seen_data[serialized_label] = Marshal.load([timestamp, nil])
|
301
|
+
puts e
|
302
|
+
pp e.backtrace
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
235
307
|
#
|
236
308
|
# Return the contents of this chest using a given +key+ and +transaction+.
|
237
309
|
#
|
@@ -244,7 +316,7 @@ module Archipelago
|
|
244
316
|
if Dubloon === instance
|
245
317
|
return instance.join(transaction)
|
246
318
|
else
|
247
|
-
return Dubloon.new(key,
|
319
|
+
return Dubloon.new(key, self, transaction, self.service_id, instance.public_methods)
|
248
320
|
end
|
249
321
|
end
|
250
322
|
|
@@ -365,11 +437,6 @@ module Archipelago
|
|
365
437
|
# this transaction to block until it is either aborted
|
366
438
|
# or commited!
|
367
439
|
#
|
368
|
-
# TODO: Make us aware about whether transactions have affected us
|
369
|
-
# 'for real' ie check whether the instance before the call
|
370
|
-
# differs from the instance after the call. Preferably
|
371
|
-
# without incurring performance lossage.
|
372
|
-
#
|
373
440
|
def prepare!(transaction)
|
374
441
|
assert_transaction(transaction)
|
375
442
|
|
@@ -457,6 +524,29 @@ module Archipelago
|
|
457
524
|
|
458
525
|
private
|
459
526
|
|
527
|
+
#
|
528
|
+
# Evaluates all data we have been told to evaluate in earlier runs.
|
529
|
+
#
|
530
|
+
def initialize_seen_data
|
531
|
+
#
|
532
|
+
# [label => [timestamp, data]]
|
533
|
+
# To remember what and when we have evaluated and be able to evaluate it again.
|
534
|
+
#
|
535
|
+
@seen_data = @persistence_provider.get_hashish("seen_data")
|
536
|
+
@seen_data.each do |serialized_label, serialized_pair|
|
537
|
+
timestamp, data = Marshal.load(serialized_pair)
|
538
|
+
|
539
|
+
if data
|
540
|
+
begin
|
541
|
+
Object.class_eval(data)
|
542
|
+
rescue Exception => e
|
543
|
+
puts e
|
544
|
+
pp e.backtrace
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
460
550
|
#
|
461
551
|
# Allocates space for this +transaction+.
|
462
552
|
#
|
@@ -471,7 +561,7 @@ module Archipelago
|
|
471
561
|
@snapshot_by_transaction[transaction] = {}
|
472
562
|
@snapshot_by_transaction[transaction].extend(Archipelago::Current::Synchronized)
|
473
563
|
@timestamp_by_key_by_transaction[transaction] = {}
|
474
|
-
transaction.join(
|
564
|
+
transaction.join(self)
|
475
565
|
end
|
476
566
|
end
|
477
567
|
else
|
@@ -631,7 +721,7 @@ module Archipelago
|
|
631
721
|
|
632
722
|
return value if Dubloon === value
|
633
723
|
|
634
|
-
return Dubloon.new(key,
|
724
|
+
return Dubloon.new(key, self, transaction, service_id, value.public_methods)
|
635
725
|
end
|
636
726
|
|
637
727
|
#
|
data/lib/archipelago.rb
CHANGED
@@ -17,8 +17,8 @@
|
|
17
17
|
|
18
18
|
$: << File.dirname(File.expand_path(__FILE__))
|
19
19
|
|
20
|
-
require 'disco'
|
21
|
-
require 'current'
|
22
|
-
require 'tranny'
|
23
|
-
require 'treasure'
|
24
|
-
require 'pirate'
|
20
|
+
require 'archipelago/disco'
|
21
|
+
require 'archipelago/current'
|
22
|
+
require 'archipelago/tranny'
|
23
|
+
require 'archipelago/treasure'
|
24
|
+
require 'archipelago/pirate'
|
data/scripts/chest.rb
CHANGED
data/scripts/pirate.rb
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'pirate'
|
3
|
+
require 'archipelago/pirate'
|
4
|
+
|
5
|
+
#
|
6
|
+
# An overload of Archipelago::Hashish::BerkeleyHashish
|
7
|
+
# just to get a nice debugging method.
|
8
|
+
#
|
9
|
+
class Archipelago::Hashish::BerkeleyHashish
|
10
|
+
#
|
11
|
+
# Get the serialized value for +key+.
|
12
|
+
#
|
13
|
+
def get_serialized_value(key)
|
14
|
+
@content_db[Marshal.dump(key)]
|
15
|
+
end
|
16
|
+
end
|
4
17
|
|
5
18
|
DRb.start_service("druby://localhost:#{rand(1000) + 5000}")
|
6
19
|
@p = Archipelago::Pirate::Captain.new
|
data/scripts/tranny.rb
CHANGED
data/tests/current_test.rb
CHANGED
data/tests/disco_test.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
|
2
2
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
3
|
-
require 'disco'
|
4
|
-
require 'drb'
|
5
|
-
require 'socket'
|
6
|
-
require 'ipaddr'
|
7
|
-
require 'thread'
|
8
3
|
|
9
4
|
class RemoteValidator
|
10
5
|
include DRb::DRbUndumped
|
@@ -27,8 +22,8 @@ class DiscoTest < Test::Unit::TestCase
|
|
27
22
|
@d2 = TestJockey.new(:thrifty_publishing => false)
|
28
23
|
@v1 = RemoteValidator.new(true)
|
29
24
|
@p1 = Archipelago::Disco::Record.new(:service_id => 1,
|
30
|
-
|
31
|
-
|
25
|
+
:validator => DRbObject.new(@v1),
|
26
|
+
:epa => "blar")
|
32
27
|
@d1.publish(@p1)
|
33
28
|
assert(!@d2.lookup(Archipelago::Disco::Query.new(:epa => "blar")).empty?)
|
34
29
|
|
@@ -62,8 +57,8 @@ class DiscoTest < Test::Unit::TestCase
|
|
62
57
|
end
|
63
58
|
|
64
59
|
def teardown
|
65
|
-
@d1.stop
|
66
|
-
@d2.stop
|
60
|
+
@d1.stop!
|
61
|
+
@d2.stop!
|
67
62
|
DRb.stop_service
|
68
63
|
@lt.kill
|
69
64
|
@listener.close
|
@@ -78,8 +73,8 @@ class DiscoTest < Test::Unit::TestCase
|
|
78
73
|
assert(empty)
|
79
74
|
|
80
75
|
@d1.publish(Archipelago::Disco::Record.new(:service_id => 1,
|
81
|
-
|
82
|
-
|
76
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
77
|
+
:epa => "blar2"))
|
83
78
|
|
84
79
|
assert_within(0.5) do
|
85
80
|
!empty
|
@@ -94,7 +89,9 @@ class DiscoTest < Test::Unit::TestCase
|
|
94
89
|
def test_thrifty_publishing
|
95
90
|
@ltq.clear
|
96
91
|
|
97
|
-
@d1.publish(Archipelago::Disco::Record.new(:glada => "jaa",
|
92
|
+
@d1.publish(Archipelago::Disco::Record.new(:glada => "jaa",
|
93
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
94
|
+
:service_id => 344))
|
98
95
|
sleep 0.1
|
99
96
|
assert(@ltq.any? do |d|
|
100
97
|
o = Marshal.load(d)
|
@@ -106,24 +103,28 @@ class DiscoTest < Test::Unit::TestCase
|
|
106
103
|
end
|
107
104
|
end)
|
108
105
|
|
109
|
-
@d1.stop
|
110
|
-
@d2.stop
|
106
|
+
@d1.stop!
|
107
|
+
@d2.stop!
|
111
108
|
|
112
109
|
@ltq.clear
|
113
110
|
c3 = Archipelago::Disco::Jockey.new(:thrifty_publishing => true)
|
114
|
-
c3.publish(Archipelago::Disco::Record.new(:glad => "ja",
|
111
|
+
c3.publish(Archipelago::Disco::Record.new(:glad => "ja",
|
112
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
113
|
+
:service_id => 33))
|
115
114
|
sleep 0.1
|
116
115
|
assert(@ltq.empty?)
|
117
116
|
|
118
117
|
c1 = Archipelago::Disco::Jockey.new(:thrifty_publishing => true)
|
119
118
|
assert(!c1.lookup(Archipelago::Disco::Query.new(:glad => "ja")).empty?)
|
120
119
|
|
121
|
-
c1.stop
|
122
|
-
c3.stop
|
120
|
+
c1.stop!
|
121
|
+
c3.stop!
|
123
122
|
end
|
124
123
|
|
125
124
|
def test_thrifty_replying
|
126
|
-
@d1.publish(Archipelago::Disco::Record.new(:gladaa => "jaaa",
|
125
|
+
@d1.publish(Archipelago::Disco::Record.new(:gladaa => "jaaa",
|
126
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
127
|
+
:service_id => 3444))
|
127
128
|
|
128
129
|
@ltq.clear
|
129
130
|
assert(!@d2.lookup(Archipelago::Disco::Query.new(:gladaa => "jaaa")).empty?)
|
@@ -138,10 +139,12 @@ class DiscoTest < Test::Unit::TestCase
|
|
138
139
|
end)
|
139
140
|
|
140
141
|
|
141
|
-
@d1.stop
|
142
|
-
@d2.stop
|
142
|
+
@d1.stop!
|
143
|
+
@d2.stop!
|
143
144
|
c3 = Archipelago::Disco::Jockey.new(:thrifty_replying => true, :thrifty_publishing => true)
|
144
|
-
c3.publish(Archipelago::Disco::Record.new(:glad2 => "ja2",
|
145
|
+
c3.publish(Archipelago::Disco::Record.new(:glad2 => "ja2",
|
146
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
147
|
+
:service_id => 34))
|
145
148
|
|
146
149
|
@ltq.clear
|
147
150
|
|
@@ -157,19 +160,23 @@ class DiscoTest < Test::Unit::TestCase
|
|
157
160
|
end
|
158
161
|
end)
|
159
162
|
|
160
|
-
c1.stop
|
161
|
-
c3.stop
|
163
|
+
c1.stop!
|
164
|
+
c3.stop!
|
162
165
|
end
|
163
166
|
|
164
167
|
def test_thrifty_caching
|
165
|
-
@d2.publish(Archipelago::Disco::Record.new(:bojkotta => "jag",
|
168
|
+
@d2.publish(Archipelago::Disco::Record.new(:bojkotta => "jag",
|
169
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
170
|
+
:service_id => 411))
|
166
171
|
sleep 0.1
|
167
172
|
assert(@d1.remote_services.include?(411))
|
168
173
|
|
169
174
|
c1 = TestJockey.new(:thrifty_caching => true)
|
170
175
|
assert(!c1.local_services.include?(41))
|
171
176
|
assert(!c1.remote_services.include?(41))
|
172
|
-
@d1.publish(Archipelago::Disco::Record.new(:bojkott => "ja",
|
177
|
+
@d1.publish(Archipelago::Disco::Record.new(:bojkott => "ja",
|
178
|
+
:validator => Archipelago::Disco::MockValidator.new,
|
179
|
+
:service_id => 41))
|
173
180
|
sleep 0.1
|
174
181
|
assert(!c1.local_services.include?(41))
|
175
182
|
assert(!c1.remote_services.include?(41))
|
data/tests/pirate_test.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
|
2
2
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
3
|
-
require 'treasure'
|
4
|
-
require 'drb'
|
5
|
-
require 'tranny'
|
6
|
-
require 'hashish'
|
7
|
-
require 'pirate'
|
8
3
|
|
9
4
|
class PirateTest < Test::Unit::TestCase
|
10
5
|
|
@@ -27,12 +22,39 @@ class PirateTest < Test::Unit::TestCase
|
|
27
22
|
end
|
28
23
|
|
29
24
|
def teardown
|
25
|
+
@p.stop!
|
30
26
|
@c.persistence_provider.unlink
|
31
27
|
@c2.persistence_provider.unlink
|
32
28
|
@tm.persistence_provider.unlink
|
33
29
|
DRb.stop_service
|
34
30
|
end
|
35
31
|
|
32
|
+
def test_evaluate
|
33
|
+
assert_raise(NameError) do
|
34
|
+
e = Evaltest.new
|
35
|
+
end
|
36
|
+
|
37
|
+
p2 = Archipelago::Pirate::Captain.new(:chest_description => {:class => "TestChest"},
|
38
|
+
:tranny_description => {:class => "TestManager"},
|
39
|
+
:chest_eval_files => [File.join(File.dirname(__FILE__), 'evaltest')])
|
40
|
+
|
41
|
+
assert_within(10) do
|
42
|
+
!p2.chests.empty?
|
43
|
+
end
|
44
|
+
|
45
|
+
e = Evaltest.new
|
46
|
+
|
47
|
+
assert_raise(NameError) do
|
48
|
+
e = Evaltestmore.new
|
49
|
+
end
|
50
|
+
|
51
|
+
p2.evaluate!(File.join(File.dirname(__FILE__), "evaltestmore"))
|
52
|
+
|
53
|
+
e = Evaltestmore.new
|
54
|
+
|
55
|
+
p2.stop!
|
56
|
+
end
|
57
|
+
|
36
58
|
def test_write_read
|
37
59
|
0.upto(100) do |n|
|
38
60
|
@p["#{n}"] = "#{n}"
|
data/tests/test_helper.rb
CHANGED
@@ -2,11 +2,16 @@
|
|
2
2
|
home = File.expand_path(File.dirname(__FILE__))
|
3
3
|
$: << File.join(home, "..", "lib")
|
4
4
|
|
5
|
+
MC_DISABLED = true
|
6
|
+
|
5
7
|
require 'pp'
|
8
|
+
require 'drb'
|
6
9
|
require 'test/unit'
|
7
|
-
require '
|
8
|
-
require 'treasure'
|
10
|
+
require 'archipelago'
|
9
11
|
require 'benchmark'
|
12
|
+
require 'socket'
|
13
|
+
require 'ipaddr'
|
14
|
+
require 'thread'
|
10
15
|
|
11
16
|
class TestTransaction
|
12
17
|
def join(o)
|
data/tests/tranny_test.rb
CHANGED
data/tests/treasure_test.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
|
2
2
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
3
|
-
require 'treasure'
|
4
|
-
require 'drb'
|
5
|
-
require 'tranny'
|
6
|
-
require 'hashish'
|
7
3
|
|
8
4
|
class TreasureTest < Test::Unit::TestCase
|
9
5
|
|
@@ -21,6 +17,18 @@ class TreasureTest < Test::Unit::TestCase
|
|
21
17
|
DRb.stop_service
|
22
18
|
end
|
23
19
|
|
20
|
+
def test_eval
|
21
|
+
t = Time.now
|
22
|
+
@c.evaluate!("burk", t, "class Burk; end")
|
23
|
+
b = Burk.new
|
24
|
+
@c.evaluate!("burk", t, "class Bong; end")
|
25
|
+
assert_raise(NameError) do
|
26
|
+
b = Bong.new
|
27
|
+
end
|
28
|
+
@c.evaluate!("burk", Time.now + 10, "class Bong; end")
|
29
|
+
b = Bong.new
|
30
|
+
end
|
31
|
+
|
24
32
|
def test_store_load_update
|
25
33
|
s = "hehu"
|
26
34
|
@c["oj"] = "hehu"
|
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.1.
|
7
|
-
date: 2006-11-
|
6
|
+
version: 0.1.1
|
7
|
+
date: 2006-11-14 00:00:00 +01:00
|
8
8
|
summary: A set of tools for distributed computing in ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -29,12 +29,12 @@ authors:
|
|
29
29
|
- Martin Kihlgren
|
30
30
|
files:
|
31
31
|
- lib/archipelago.rb
|
32
|
-
- lib/current.rb
|
33
|
-
- lib/disco.rb
|
34
|
-
- lib/hashish.rb
|
35
|
-
- lib/pirate.rb
|
36
|
-
- lib/tranny.rb
|
37
|
-
- lib/treasure.rb
|
32
|
+
- lib/archipelago/current.rb
|
33
|
+
- lib/archipelago/disco.rb
|
34
|
+
- lib/archipelago/hashish.rb
|
35
|
+
- lib/archipelago/pirate.rb
|
36
|
+
- lib/archipelago/tranny.rb
|
37
|
+
- lib/archipelago/treasure.rb
|
38
38
|
- scripts/chest.rb
|
39
39
|
- scripts/console
|
40
40
|
- scripts/pirate.rb
|