archipelago 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|