tupelo 0.9 → 0.10
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/README.md +31 -1
- data/bugs/read-take.rb +19 -0
- data/bugs/take-write.rb +7 -7
- data/example/app-and-tup.rb +11 -8
- data/example/async-transaction.rb +7 -7
- data/example/balance-xfer-locking.rb +13 -13
- data/example/balance-xfer-retry.rb +16 -16
- data/example/balance-xfer.rb +9 -9
- data/example/boolean-match.rb +5 -5
- data/example/bounded-retry.rb +9 -9
- data/example/broker-locking.rb +14 -14
- data/example/broker-optimistic.rb +7 -7
- data/example/cancel.rb +7 -7
- data/example/concurrent-transactions.rb +17 -17
- data/example/custom-class.rb +9 -9
- data/example/custom-search.rb +8 -8
- data/example/fail-and-retry.rb +11 -11
- data/example/hash-tuples.rb +12 -10
- data/example/increment.rb +8 -8
- data/example/load-balancer.rb +2 -1
- data/example/lock-mgr-with-queue.rb +18 -18
- data/example/lock-mgr.rb +18 -18
- data/example/map-reduce-v2.rb +11 -11
- data/example/map-reduce.rb +11 -11
- data/example/matching.rb +5 -5
- data/example/message-bus.rb +6 -3
- data/example/notify.rb +17 -17
- data/example/optimist.rb +9 -9
- data/example/parallel.rb +16 -8
- data/example/pregel/distributed.rb +129 -0
- data/example/pregel/pagerank.rb +72 -0
- data/example/pregel/pregel.rb +102 -0
- data/example/pregel/remote.rb +165 -0
- data/example/pulse.rb +10 -10
- data/example/read-in-trans.rb +15 -15
- data/example/subspace.rb +34 -0
- data/example/take-nowait.rb +1 -0
- data/example/tcp.rb +3 -3
- data/example/timeout-trans.rb +5 -5
- data/example/timeout.rb +10 -9
- data/example/tiny-client.rb +5 -5
- data/example/tiny-server.rb +2 -2
- data/example/transaction-logic.rb +16 -16
- data/example/wait-interrupt.rb +38 -0
- data/example/write-wait.rb +11 -11
- data/lib/tupelo/archiver/tuplespace.rb +5 -1
- data/lib/tupelo/archiver/worker.rb +25 -18
- data/lib/tupelo/client/reader.rb +2 -2
- data/lib/tupelo/client/transaction.rb +79 -36
- data/lib/tupelo/client/tuplespace.rb +1 -0
- data/lib/tupelo/client/worker.rb +107 -13
- data/lib/tupelo/client.rb +36 -2
- data/lib/tupelo/version.rb +1 -1
- data/test/lib/mock-client.rb +4 -0
- data/test/lib/testable-worker.rb +1 -1
- data/test/stress/concurrent-transactions.rb +15 -15
- data/test/system/test-archiver.rb +8 -8
- data/test/unit/test-ops.rb +56 -0
- metadata +72 -68
- data/bugs/write-read.rb +0 -15
- data/example/broker-queue.rb +0 -35
- data/example/child-of-child.rb +0 -34
data/example/fail-and-retry.rb
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
3
|
+
Tupelo.application do
|
4
4
|
2.times do
|
5
|
-
|
5
|
+
child do
|
6
6
|
begin
|
7
7
|
# the block is re-executed for the client that fails to take [1]
|
8
8
|
# this is also true in the transaction do...end construct.
|
9
|
-
t =
|
9
|
+
t = transaction
|
10
10
|
r = t.take [Integer]
|
11
|
-
|
11
|
+
log "trying to take #{r.inspect}"
|
12
12
|
t.commit.wait
|
13
|
-
|
13
|
+
log "took #{r.inspect}"
|
14
14
|
rescue Tupelo::Client::TransactionFailure => ex
|
15
|
-
|
15
|
+
log "#{ex} -- retrying"
|
16
16
|
retry
|
17
17
|
# manually emulate the effect of transaction do...end
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
child do
|
23
|
+
write [1]
|
24
|
+
log "wrote #{[1]}"
|
25
25
|
sleep 0.1
|
26
|
-
|
27
|
-
|
26
|
+
write [2]
|
27
|
+
log "wrote #{[2]}"
|
28
28
|
end
|
29
29
|
end
|
data/example/hash-tuples.rb
CHANGED
@@ -35,19 +35,21 @@
|
|
35
35
|
|
36
36
|
require 'tupelo/app'
|
37
37
|
|
38
|
-
Tupelo.application do
|
39
|
-
|
40
|
-
|
38
|
+
Tupelo.application do
|
39
|
+
child do
|
40
|
+
write x: 1, y: 2
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
t =
|
45
|
-
|
46
|
-
|
43
|
+
child do
|
44
|
+
t = take x: Numeric, y: Numeric
|
45
|
+
write x: t["x"], y: t["y"], sum: t["x"] + t["y"]
|
46
|
+
log "sum result: #{read x: nil, y: nil, sum: nil}"
|
47
47
|
|
48
48
|
# N.B.: these are all empty, for the reason given above.
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
log read_all x: nil
|
50
|
+
log read_all y: nil
|
51
|
+
log read_all x: nil, y: nil, z: nil
|
52
|
+
|
53
|
+
log read_all
|
52
54
|
end
|
53
55
|
end
|
data/example/increment.rb
CHANGED
@@ -2,20 +2,20 @@ require 'tupelo/app'
|
|
2
2
|
|
3
3
|
N = 5
|
4
4
|
|
5
|
-
Tupelo.application do
|
5
|
+
Tupelo.application do
|
6
6
|
N.times do |i|
|
7
|
-
|
8
|
-
|
9
|
-
n, s =
|
7
|
+
child do
|
8
|
+
transaction do
|
9
|
+
n, s = take [Numeric, String]
|
10
10
|
#sleep rand # No race conditions here!
|
11
|
-
|
11
|
+
write [n + 1, s + "\n incremented by client #{i}"]
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
n, s =
|
16
|
+
child do
|
17
|
+
write [0, "started with 0"]
|
18
|
+
n, s = take [N, String]
|
19
19
|
puts s, "result is #{n}"
|
20
20
|
end
|
21
21
|
end
|
data/example/load-balancer.rb
CHANGED
@@ -33,7 +33,8 @@ Tupelo.application do
|
|
33
33
|
req_id = nil
|
34
34
|
transaction do
|
35
35
|
# grouping the following ops in a transaction is not necessary for
|
36
|
-
# correctness, but it does reduce latency
|
36
|
+
# correctness, but it does reduce latency. Also, it's more robust
|
37
|
+
# in that a crash or network problem cannot cause a lost tuple.
|
37
38
|
_, req_id = take ["next_req_id", Integer]
|
38
39
|
write ["next_req_id", req_id + 1]
|
39
40
|
write ["request", req_id, req_dat]
|
@@ -9,36 +9,36 @@ require 'tupelo/app'
|
|
9
9
|
|
10
10
|
N = 3
|
11
11
|
|
12
|
-
Tupelo.application do
|
13
|
-
|
14
|
-
|
12
|
+
Tupelo.application do
|
13
|
+
child passive: true do # the lock manager
|
14
|
+
log.progname << " (lock mgr)"
|
15
15
|
waiters = Queue.new
|
16
16
|
|
17
17
|
Thread.new do
|
18
18
|
loop do
|
19
19
|
_, _, client_id, duration =
|
20
|
-
|
20
|
+
take ["request", "resource", nil, nil]
|
21
21
|
waiters << [client_id, duration]
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
25
|
loop do
|
26
26
|
client_id, duration = waiters.pop
|
27
|
-
|
27
|
+
write ["resource", client_id, duration]
|
28
28
|
begin
|
29
|
-
|
29
|
+
take ["done", "resource", client_id], timeout: duration
|
30
30
|
rescue TimeoutError
|
31
|
-
|
31
|
+
log "forcing client #{client_id} to stop using resource."
|
32
32
|
end
|
33
|
-
|
33
|
+
take ["resource", client_id, duration]
|
34
34
|
end
|
35
35
|
# exercise for reader: make this work with 2 or more resources
|
36
36
|
# exercise: rewrite this with hash tuples instead of array tuples
|
37
37
|
end
|
38
38
|
|
39
39
|
N.times do |i|
|
40
|
-
|
41
|
-
|
40
|
+
child do
|
41
|
+
write ["request", "resource", client_id, 0.5]
|
42
42
|
|
43
43
|
2.times do |j|
|
44
44
|
# Now we are ouside of transaction, but still no other client may use
|
@@ -46,20 +46,20 @@ Tupelo.application do |app|
|
|
46
46
|
# as all clients follow the read protocol below.
|
47
47
|
sleep 0.2
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
transaction do
|
50
|
+
read ["resource", client_id, nil]
|
51
|
+
write ["c#{client_id}##{j}"]
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
|
57
|
+
child passive: true do
|
58
58
|
# This client never even tries to lock the resource, so it cannot write.
|
59
|
-
|
60
|
-
|
61
|
-
|
59
|
+
transaction do
|
60
|
+
read ["resource", client_id, nil]
|
61
|
+
write ["c#{client_id}##{j}"]
|
62
62
|
end
|
63
|
-
|
63
|
+
log.error "should never get here"
|
64
64
|
end
|
65
65
|
end
|
data/example/lock-mgr.rb
CHANGED
@@ -4,14 +4,14 @@ require 'tupelo/app'
|
|
4
4
|
|
5
5
|
N = 3
|
6
6
|
|
7
|
-
Tupelo.application do
|
8
|
-
|
9
|
-
|
7
|
+
Tupelo.application do
|
8
|
+
child passive: true do # the lock manager
|
9
|
+
log.progname << " (lock mgr)"
|
10
10
|
loop do
|
11
|
-
|
12
|
-
lock_id, client_id, duration =
|
11
|
+
write ["resource", "none"]
|
12
|
+
lock_id, client_id, duration = read ["resource", nil, nil]
|
13
13
|
sleep duration
|
14
|
-
|
14
|
+
take [lock_id, client_id, duration]
|
15
15
|
# optimization: combine take and write in a transaction, just to
|
16
16
|
# reduce delay
|
17
17
|
end
|
@@ -20,10 +20,10 @@ Tupelo.application do |app|
|
|
20
20
|
end
|
21
21
|
|
22
22
|
N.times do |i|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
child do
|
24
|
+
transaction do
|
25
|
+
take ["resource", "none"]
|
26
|
+
write ["resource", client_id, 0.5]
|
27
27
|
end
|
28
28
|
# Thundering herd -- all N clients can respond at the same time.
|
29
29
|
# Can be avoided with a queue -- see lock-mgr-with-queue.rb.
|
@@ -34,20 +34,20 @@ Tupelo.application do |app|
|
|
34
34
|
# as all clients follow the read protocol below.
|
35
35
|
sleep 0.2
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
37
|
+
transaction do
|
38
|
+
read ["resource", client_id, nil]
|
39
|
+
write ["c#{client_id}##{j}"]
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
|
45
|
+
child passive: true do
|
46
46
|
# This client never even tries to lock the resource, so it cannot write.
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
transaction do
|
48
|
+
read ["resource", client_id, nil]
|
49
|
+
write ["c#{client_id}##{j}"]
|
50
50
|
end
|
51
|
-
|
51
|
+
log.error "should never get here"
|
52
52
|
end
|
53
53
|
end
|
data/example/map-reduce-v2.rb
CHANGED
@@ -3,13 +3,13 @@ require 'tupelo/util/boolean'
|
|
3
3
|
|
4
4
|
N = 2 # how many cpus do you want to use for mappers?
|
5
5
|
|
6
|
-
Tupelo.application do
|
7
|
-
|
6
|
+
Tupelo.application do
|
7
|
+
child do
|
8
8
|
document = "I will not map reduce in class\n" * 10
|
9
9
|
lineno = 0
|
10
10
|
document.each_line do |line|
|
11
11
|
lineno += 1
|
12
|
-
|
12
|
+
write line: line, lineno: lineno
|
13
13
|
# Note that tuples should be small, so if the data is large, the line
|
14
14
|
# should be a reference, not the actual data.
|
15
15
|
# Also, in a complex application you might want to add another
|
@@ -19,13 +19,13 @@ Tupelo.application do |app|
|
|
19
19
|
results = Hash.new(0)
|
20
20
|
lines_remaining = lineno
|
21
21
|
results_remaining = 0
|
22
|
-
result_template =
|
22
|
+
result_template = match_any(
|
23
23
|
{word: String, count: Integer},
|
24
24
|
{lineno: Integer, result_count: Integer}
|
25
25
|
)
|
26
26
|
|
27
27
|
until lines_remaining == 0 and results_remaining == 0 do
|
28
|
-
result =
|
28
|
+
result = take result_template
|
29
29
|
|
30
30
|
if result["word"]
|
31
31
|
results[result["word"]] += result["count"]
|
@@ -38,15 +38,15 @@ Tupelo.application do |app|
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
41
|
+
log "results = #{results}"
|
42
42
|
end
|
43
43
|
|
44
44
|
N.times do |i|
|
45
|
-
|
46
|
-
|
45
|
+
child passive: true do
|
46
|
+
log.progname = "mapper #{i}"
|
47
47
|
|
48
48
|
loop do
|
49
|
-
input =
|
49
|
+
input = take line: String, lineno: Integer
|
50
50
|
|
51
51
|
h = Hash.new(0)
|
52
52
|
input["line"].split.each do |word|
|
@@ -54,9 +54,9 @@ Tupelo.application do |app|
|
|
54
54
|
end
|
55
55
|
|
56
56
|
h.each do |word, count|
|
57
|
-
|
57
|
+
write word: word, count: count
|
58
58
|
end
|
59
|
-
|
59
|
+
write lineno: input["lineno"], result_count: h.size
|
60
60
|
end
|
61
61
|
end
|
62
62
|
end
|
data/example/map-reduce.rb
CHANGED
@@ -2,13 +2,13 @@ require 'tupelo/app'
|
|
2
2
|
|
3
3
|
N = 2 # how many cpus do you want to use for mappers?
|
4
4
|
|
5
|
-
Tupelo.application do
|
6
|
-
|
5
|
+
Tupelo.application do
|
6
|
+
child do
|
7
7
|
document = "I will not map reduce in class\n" * 10
|
8
8
|
lineno = 0
|
9
9
|
document.each_line do |line|
|
10
10
|
lineno += 1
|
11
|
-
|
11
|
+
write ["wc input", lineno, line]
|
12
12
|
# Note that tuples should be small, so if the data is large, the line
|
13
13
|
# should be a reference, not the actual data.
|
14
14
|
end
|
@@ -17,9 +17,9 @@ Tupelo.application do |app|
|
|
17
17
|
lines_remaining = lineno
|
18
18
|
results_remaining = 0
|
19
19
|
until lines_remaining == 0 and results_remaining == 0 do
|
20
|
-
event, *a =
|
20
|
+
event, *a = take [/wc (?:output|done)/, nil, nil]
|
21
21
|
# Using a regex is hacky. Better to use an "or" template. See
|
22
|
-
# boolean-match.rb.
|
22
|
+
# boolean-match.rb.
|
23
23
|
# Also, in real use it might be better to use hash tuples rather than
|
24
24
|
# arrays.
|
25
25
|
# See map-reduce-v2.rb.
|
@@ -36,15 +36,15 @@ Tupelo.application do |app|
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
log "results = #{results}"
|
40
40
|
end
|
41
41
|
|
42
42
|
N.times do |i|
|
43
|
-
|
44
|
-
|
43
|
+
child passive: true do
|
44
|
+
log.progname = "mapper #{i}"
|
45
45
|
|
46
46
|
loop do
|
47
|
-
_, lineno, line =
|
47
|
+
_, lineno, line = take ["wc input", Integer, String]
|
48
48
|
|
49
49
|
h = Hash.new(0)
|
50
50
|
line.split.each do |word|
|
@@ -52,9 +52,9 @@ Tupelo.application do |app|
|
|
52
52
|
end
|
53
53
|
|
54
54
|
h.each do |word, count|
|
55
|
-
|
55
|
+
write ["wc output", word, count]
|
56
56
|
end
|
57
|
-
|
57
|
+
write ["wc done", lineno, h.size]
|
58
58
|
end
|
59
59
|
end
|
60
60
|
end
|
data/example/matching.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
5
|
-
|
6
|
-
p
|
7
|
-
p
|
3
|
+
Tupelo.application do
|
4
|
+
local do
|
5
|
+
write_wait ["foo", 42.5]
|
6
|
+
p read_all [/oo/, nil]
|
7
|
+
p read_all [nil, 5..95]
|
8
8
|
end
|
9
9
|
end
|
data/example/message-bus.rb
CHANGED
@@ -24,7 +24,7 @@ Tupelo.application do
|
|
24
24
|
sleep delay
|
25
25
|
ch = channels[ pi % channels.size ]
|
26
26
|
transaction do
|
27
|
-
|
27
|
+
take [ch, nil]
|
28
28
|
write [ch, "pub #{pi} slept for #{delay} sec"]
|
29
29
|
end
|
30
30
|
end
|
@@ -36,8 +36,7 @@ Tupelo.application do
|
|
36
36
|
ch = channels[ si % channels.size ]
|
37
37
|
loop do
|
38
38
|
sleep 1
|
39
|
-
|
40
|
-
log t if t
|
39
|
+
log read [ch, nil] # note asynchronous reads
|
41
40
|
end
|
42
41
|
end
|
43
42
|
end
|
@@ -54,6 +53,10 @@ Tupelo.application do
|
|
54
53
|
end
|
55
54
|
|
56
55
|
local do
|
56
|
+
channels.each do |ch|
|
57
|
+
write [ch, nil]
|
58
|
+
end
|
59
|
+
|
57
60
|
write ['start']
|
58
61
|
end
|
59
62
|
end
|
data/example/notify.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
-
# See also bin/tspy
|
1
|
+
# See also bin/tspy and the --trace switch on all tupelo apps and examples.
|
2
2
|
|
3
3
|
require 'tupelo/app'
|
4
4
|
|
5
|
-
Tupelo.application do
|
6
|
-
|
5
|
+
Tupelo.application do
|
6
|
+
child do
|
7
7
|
Thread.new do
|
8
|
-
note =
|
9
|
-
|
8
|
+
note = notifier
|
9
|
+
write ["start"]
|
10
10
|
|
11
|
-
|
11
|
+
log "%10s %10s %10s %s" % %w{ status tick client operation }
|
12
12
|
loop do
|
13
13
|
status, global_tick, client_id, op = note.wait
|
14
|
-
|
14
|
+
log "%10s %10d %10d %p" % [status, global_tick, client_id, op]
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
take ["finish"]
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
child do
|
22
|
+
take ["start"]
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
write [1, 2]
|
25
|
+
write [3, 4]
|
26
|
+
write foo: "bar", baz: ["zap"]
|
27
27
|
|
28
|
-
|
29
|
-
x, y =
|
30
|
-
|
28
|
+
transaction do
|
29
|
+
x, y = take [Numeric, Numeric]
|
30
|
+
write [x, y, x + y]
|
31
31
|
end
|
32
32
|
|
33
|
-
|
33
|
+
write ["finish"]
|
34
34
|
end
|
35
35
|
end
|
data/example/optimist.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
5
|
-
|
3
|
+
Tupelo.application do
|
4
|
+
child do
|
5
|
+
write [1]
|
6
6
|
sleep 0.1
|
7
|
-
|
8
|
-
|
7
|
+
take [1]
|
8
|
+
write [2]
|
9
9
|
end
|
10
10
|
|
11
|
-
|
11
|
+
child do
|
12
12
|
final_i =
|
13
|
-
|
14
|
-
|
13
|
+
take([Integer]) do |optimistic_i|
|
14
|
+
log "optimistic_i = #{optimistic_i}"
|
15
15
|
sleep 0.2
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
log "final_i = #{final_i}"
|
19
19
|
end
|
20
20
|
end
|
data/example/parallel.rb
CHANGED
@@ -9,14 +9,18 @@ hosts = ARGV.shift
|
|
9
9
|
map = ARGV.slice!(0,3)
|
10
10
|
reduce = ARGV.slice!(0,4)
|
11
11
|
|
12
|
-
|
13
|
-
|
12
|
+
args_ok = hosts && map[0] == "map" && reduce[0] == "reduce" && reduce[3]
|
13
|
+
|
14
|
+
abort <<END unless args_ok
|
14
15
|
|
15
|
-
usage: #$0 <ssh-host>,... map <var> <expr> reduce <var> <var> <expr>
|
16
|
+
usage: #$0 <ssh-host>,... map <var> <expr> reduce <var> <var> <expr>
|
17
|
+
|
18
|
+
Input is provided on standard input
|
19
|
+
|
20
|
+
Writes the result of the last reduction to standard output.
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
reduction to standard output.
|
22
|
+
If <ssh-host> is of the form host:<number> than <number> processes are
|
23
|
+
started on host.
|
20
24
|
|
21
25
|
If --show-steps is set then intermediate reductions are printed as they
|
22
26
|
are computed. If input is stdin at the terminal, then you can see these
|
@@ -28,12 +32,16 @@ abort <<END unless hosts and
|
|
28
32
|
Example:
|
29
33
|
|
30
34
|
ruby #$0 localhost,localhost map s s.length reduce l1 l2 l1+l2
|
31
|
-
|
35
|
+
|
32
36
|
Use `s.split.length` to get word count instead of char count.
|
33
37
|
|
34
38
|
END
|
35
39
|
|
36
|
-
hosts = hosts.split(",")
|
40
|
+
hosts = hosts.split(",").map do |s|
|
41
|
+
s.slice!(/:(\d+)\z/)
|
42
|
+
$1 ? [s] * Integer($1) : s
|
43
|
+
end
|
44
|
+
hosts.flatten!
|
37
45
|
|
38
46
|
map_str = <<END
|
39
47
|
proc do |#{map[1]}|
|