tupelo 0.12 → 0.13
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 +6 -1
- data/example/broker-queue.rb +35 -0
- data/example/chat/chat-nohistory.rb +33 -0
- data/example/chat/chat.rb +4 -9
- data/example/child-of-child.rb +34 -0
- data/example/fish01.rb +48 -0
- data/example/pregel/dist-opt.rb +15 -0
- data/example/subspaces/addr-book-v1.rb +106 -0
- data/example/subspaces/addr-book-v2.rb +18 -0
- data/example/subspaces/sorted-set-space.rb +130 -0
- data/lib/tupelo/client/reader.rb +13 -6
- data/lib/tupelo/client/worker.rb +6 -4
- data/lib/tupelo/version.rb +1 -1
- data/test/unit/test-ops.rb +24 -0
- metadata +91 -83
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a657d2c56962432faa339d693f6856f409249f6
|
4
|
+
data.tar.gz: 64a58b3ab627151aeb2fc8965de21a39c1ebf3f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ea7042705c178a855d4d9ad621c2b65433d0adb4ff7dd75ee855f75d4b53d2bc4301ea91f3491bfaf2847294076a9edffa176290192d004488bdf87caa0b016
|
7
|
+
data.tar.gz: 7e371d515db7f86bd95c353fcd5153efd5d1f34ba6edb0c75b7602994309509615f602ba7463729326f3c8d3aa55ba290e67c0e24fac07e917d61e72d668b886
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
**NEWS**: Come hear a talk on Tupelo on December 11 in San Francisco at the [SF Distributed Computing meetup](http://www.meetup.com/San-Francisco-Distributed-Computing).
|
1
|
+
**NEWS**: Come hear a talk on Tupelo on December 11 in San Francisco at the [SF Distributed Computing meetup](http://www.meetup.com/San-Francisco-Distributed-Computing/events/153886592/). Abstract: [doc/sfdc.txt](doc/sfdc.md).
|
2
2
|
|
3
3
|
tupelo
|
4
4
|
==
|
@@ -93,6 +93,11 @@ Getting started
|
|
93
93
|
|
94
94
|
reads all 2-tuples.
|
95
95
|
|
96
|
+
Read tuples in a stream, both existing and as they arrive:
|
97
|
+
|
98
|
+
read <template> do |tuple| ... end
|
99
|
+
read do |tuple| ... end # match any tuple
|
100
|
+
|
96
101
|
Take a tuple matching a template:
|
97
102
|
|
98
103
|
t <template>
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# more like how you would do it in redis, except that the queue is not stored in
|
2
|
+
# the central server, so operations on it are not a bottleneck, FWIW
|
3
|
+
|
4
|
+
require 'tupelo/app'
|
5
|
+
|
6
|
+
N_PLAYERS = 10
|
7
|
+
|
8
|
+
Tupelo.application do
|
9
|
+
N_PLAYERS.times do
|
10
|
+
# sleep rand / 10 # reduce contention -- could also randomize inserts
|
11
|
+
child do
|
12
|
+
me = client_id
|
13
|
+
write name: me
|
14
|
+
|
15
|
+
you = transaction do
|
16
|
+
game = read_nowait(
|
17
|
+
player1: nil,
|
18
|
+
player2: me)
|
19
|
+
break game["player1"] if game
|
20
|
+
|
21
|
+
unless take_nowait name: me
|
22
|
+
raise Tupelo::Client::TransactionFailure
|
23
|
+
end
|
24
|
+
|
25
|
+
you = take(name: nil)["name"]
|
26
|
+
write(
|
27
|
+
player1: me,
|
28
|
+
player2: you)
|
29
|
+
you
|
30
|
+
end
|
31
|
+
|
32
|
+
log "now playing with #{you}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Same as chat.rb, but no messages are stored.
|
2
|
+
|
3
|
+
require 'tupelo/app'
|
4
|
+
|
5
|
+
svr = "chat-nohistory.yaml"
|
6
|
+
|
7
|
+
Thread.abort_on_exception = true
|
8
|
+
|
9
|
+
def display_message msg
|
10
|
+
from, time, line = msg.values_at(*%w{from time line})
|
11
|
+
time_str = Time.at(time).strftime("%I:%M:%S")
|
12
|
+
print "\r\033[2K" # Esc[2K is "Clear entire line"
|
13
|
+
puts "#{from}@#{time_str}> #{line}"
|
14
|
+
end
|
15
|
+
|
16
|
+
Tupelo.tcp_application servers_file: svr do
|
17
|
+
me = argv.shift
|
18
|
+
|
19
|
+
local do
|
20
|
+
require 'readline'
|
21
|
+
|
22
|
+
Thread.new do
|
23
|
+
read from: nil, line: nil, time: nil do |msg|
|
24
|
+
display_message msg
|
25
|
+
Readline.refresh_line
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
while line = Readline.readline("#{me}> ", true)
|
30
|
+
pulse from: me, line: line, time: Time.now.to_f
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/example/chat/chat.rb
CHANGED
@@ -22,7 +22,8 @@ Thread.abort_on_exception = true
|
|
22
22
|
|
23
23
|
def display_message msg
|
24
24
|
from, time, line = msg.values_at(*%w{from time line})
|
25
|
-
time_str = Time.at(time).strftime("%I:%M
|
25
|
+
time_str = Time.at(time).strftime("%I:%M:%S")
|
26
|
+
print "\r\033[2K" # Esc[2K is "Clear entire line"
|
26
27
|
puts "#{from}@#{time_str}> #{line}"
|
27
28
|
end
|
28
29
|
|
@@ -33,15 +34,9 @@ Tupelo.tcp_application servers_file: svr do
|
|
33
34
|
require 'readline'
|
34
35
|
|
35
36
|
Thread.new do
|
36
|
-
seen_at_start = {}
|
37
|
-
read_all(from: nil, line: nil, time: nil).
|
38
|
-
sort_by {|msg| msg["time"]}.
|
39
|
-
each {|msg| display_message msg; seen_at_start[msg] = true}
|
40
|
-
|
41
37
|
read from: nil, line: nil, time: nil do |msg|
|
42
|
-
|
43
|
-
|
44
|
-
Readline.redisplay ### why not u work?
|
38
|
+
display_message msg
|
39
|
+
Readline.refresh_line
|
45
40
|
end
|
46
41
|
end
|
47
42
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'tupelo/app'
|
2
|
+
|
3
|
+
### need a programmatic way to start up clients
|
4
|
+
|
5
|
+
Tupelo.application do |app|
|
6
|
+
|
7
|
+
app.child do ## local still hangs
|
8
|
+
3.times do |i|
|
9
|
+
app.child do
|
10
|
+
write [i]
|
11
|
+
log "wrote #{i}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
3.times do
|
16
|
+
log take [nil]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
__END__
|
22
|
+
|
23
|
+
this hangs sometimes but not always:
|
24
|
+
|
25
|
+
tick cid status operation
|
26
|
+
A: client 3: wrote 0
|
27
|
+
A: client 4: wrote 1
|
28
|
+
1 3 batch write [0]
|
29
|
+
2 4 batch write [1]
|
30
|
+
A: client 2: [0]
|
31
|
+
3 2 atomic take [0]
|
32
|
+
4 2 atomic take [1]
|
33
|
+
A: client 2: [1]
|
34
|
+
A: client 5: wrote 2
|
data/example/fish01.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# This works, but requires a fix-up step.
|
2
|
+
|
3
|
+
require 'tupelo/app'
|
4
|
+
|
5
|
+
Tupelo.application do
|
6
|
+
2.times do
|
7
|
+
child passive: true do
|
8
|
+
loop do
|
9
|
+
fish = nil
|
10
|
+
|
11
|
+
transaction do
|
12
|
+
fish, _ = take([String])
|
13
|
+
n, _ = take_nowait([Integer, fish])
|
14
|
+
if n
|
15
|
+
write [n + 1, fish]
|
16
|
+
else
|
17
|
+
write [1, fish] # another process might also write this, so ...
|
18
|
+
end
|
19
|
+
end
|
20
|
+
### what if both processes die here?
|
21
|
+
transaction do # ... fix up the two tuples.
|
22
|
+
n1, _ = take_nowait [Integer, fish]; abort unless n1
|
23
|
+
n2, _ = take_nowait [Integer, fish]; abort unless n2
|
24
|
+
#log "fixing: #{[n1 + n2, fish]}"
|
25
|
+
write [n1 + n2, fish]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
local do
|
32
|
+
seed = 3
|
33
|
+
srand seed
|
34
|
+
log "seed = #{seed}"
|
35
|
+
|
36
|
+
fishes = %w{ trout marlin char salmon }
|
37
|
+
|
38
|
+
a = fishes * 10
|
39
|
+
a.shuffle!
|
40
|
+
a.each do |fish|
|
41
|
+
write [fish]
|
42
|
+
end
|
43
|
+
|
44
|
+
fishes.each do |fish|
|
45
|
+
log take [10, fish]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
## TODO
|
2
|
+
##
|
3
|
+
## scaling params
|
4
|
+
|
5
|
+
require 'tupelo/app'
|
6
|
+
|
7
|
+
ab_tag = "my address book"
|
8
|
+
ab_sort_field = 1
|
9
|
+
ab_val_field = 2
|
10
|
+
cmd_tag = "#{ab_tag} commands"
|
11
|
+
resp_tag = "#{ab_tag} responses"
|
12
|
+
|
13
|
+
Tupelo.application do
|
14
|
+
local do
|
15
|
+
use_subspaces!
|
16
|
+
|
17
|
+
# Subspace for tuples belonging to the addr book.
|
18
|
+
define_subspace(
|
19
|
+
tag: ab_tag,
|
20
|
+
template: [
|
21
|
+
{value: ab_tag},
|
22
|
+
{type: "string"}, # name <-- ab_sort_field references this field
|
23
|
+
nil # address; can be any object <-- ab_val_field
|
24
|
+
]
|
25
|
+
)
|
26
|
+
|
27
|
+
# Subspace for commands for fetch and delete.
|
28
|
+
# We can't use #read and #take because then the requesting client
|
29
|
+
# would have to subscribe to the ab_tag subspace.
|
30
|
+
define_subspace(
|
31
|
+
tag: cmd_tag,
|
32
|
+
template: [
|
33
|
+
{value: cmd_tag},
|
34
|
+
{type: "string"}, # cmd name
|
35
|
+
{type: "list"} # arguments
|
36
|
+
]
|
37
|
+
)
|
38
|
+
|
39
|
+
# Subspace for responses to commands. Identify the command this is in
|
40
|
+
# response to by copying it (alternately, could use ids).
|
41
|
+
define_subspace(
|
42
|
+
tag: resp_tag,
|
43
|
+
template: [
|
44
|
+
{value: resp_tag},
|
45
|
+
{type: "string"}, # cmd name
|
46
|
+
{type: "list"}, # arguments
|
47
|
+
nil # result of query -- type depends on command
|
48
|
+
]
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
## Could set N_SORTED_SET_SPACE > 1, but lookups are so fast it would
|
53
|
+
## just lead to contention and redundant computation. Redundancy is useful
|
54
|
+
## though.
|
55
|
+
|
56
|
+
# Inserts are just writes, which are handled by Worker and SortedSetSpace,
|
57
|
+
# so this child's app loop only needs to handle special commands: fetch and
|
58
|
+
# delete, which are delegated to the SortedSetSpace.
|
59
|
+
child tuplespace: [SortedSetSpace, ab_tag, ab_sort_field, ab_val_field],
|
60
|
+
subscribe: [ab_tag, cmd_tag], passive: true do
|
61
|
+
loop do
|
62
|
+
transaction do
|
63
|
+
_, cmd, args = take(subspace cmd_tag)
|
64
|
+
|
65
|
+
case cmd
|
66
|
+
when "delete"
|
67
|
+
args.each do |name|
|
68
|
+
take [ab_tag, name, nil]
|
69
|
+
end
|
70
|
+
|
71
|
+
when "fetch"
|
72
|
+
name = args[0]
|
73
|
+
_, _, addr = read [ab_tag, name, nil]
|
74
|
+
write [resp_tag, name, args, addr]
|
75
|
+
|
76
|
+
when "next", "prev"
|
77
|
+
name = args[0]
|
78
|
+
_, name2, addr = read SortedSetTemplate[ab_tag, cmd, name]
|
79
|
+
write [resp_tag, name, args, name2, addr]
|
80
|
+
|
81
|
+
when "first", "last"
|
82
|
+
_, name, addr = read SortedSetTemplate[ab_tag, cmd]
|
83
|
+
write [resp_tag, name, args, name, addr]
|
84
|
+
|
85
|
+
else # maybe write an error message in a tuple
|
86
|
+
log.error "bad command: #{cmd}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
child subscribe: resp_tag do
|
93
|
+
# write some ab entries
|
94
|
+
write [ab_tag, "McFirst, Firsty", "123 W. Crescent Terrace"]
|
95
|
+
write [ab_tag, "Secondismus, Deuce", "456 S. West Way"]
|
96
|
+
|
97
|
+
# make some queries
|
98
|
+
write [cmd_tag, "first", []]
|
99
|
+
*, name, addr = take [resp_tag, "first", [], nil, nil]
|
100
|
+
log "first entry: #{name} => #{addr}"
|
101
|
+
|
102
|
+
write [cmd_tag, "next", [name]]
|
103
|
+
*, name, addr = take [resp_tag, "next", [name], nil, nil]
|
104
|
+
log "next entry: #{name} => #{addr}"
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'tupelo/app'
|
2
|
+
|
3
|
+
Tupelo.application do
|
4
|
+
local do
|
5
|
+
use_subspaces!
|
6
|
+
|
7
|
+
define_subspace(
|
8
|
+
tag: "address books",
|
9
|
+
template: {
|
10
|
+
book: {type: "string"},
|
11
|
+
name: {type: "string"},
|
12
|
+
address: nil
|
13
|
+
}
|
14
|
+
)
|
15
|
+
|
16
|
+
define_subspace
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require 'rbtree'
|
2
|
+
|
3
|
+
class SortedSetTemplate
|
4
|
+
class << self
|
5
|
+
alias [] new
|
6
|
+
end
|
7
|
+
|
8
|
+
# cmd can be "next", "prev", "first", "last"
|
9
|
+
# for next/prev, args is ["name"]
|
10
|
+
# for first/last, args is empty
|
11
|
+
def initialize tag, cmd, *args
|
12
|
+
@tag = tag
|
13
|
+
@cmd = cmd
|
14
|
+
@args = args
|
15
|
+
end
|
16
|
+
|
17
|
+
def === other
|
18
|
+
raise ### should not need this?
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_in rbtree
|
22
|
+
case @cmd
|
23
|
+
when "first"
|
24
|
+
rbtree.first
|
25
|
+
###
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# A tuple store (in-memory) that is optimized for (key_string, object) pairs.
|
30
|
+
# The object may be any serializable object (built up from numbers, booleans,
|
31
|
+
# nil, strings, hashes and arrays).
|
32
|
+
#
|
33
|
+
# Unlike in a key-value store, a given key_string may occur more than once.
|
34
|
+
# It is up to the application to decide whether to enforce key uniqueness or
|
35
|
+
# not (for example, by taking (k,...) before writing (k,v).
|
36
|
+
#
|
37
|
+
# This store should be used only by clients that subscribe to a subspace
|
38
|
+
# that can be represented as pairs. (See memo2.rb.)
|
39
|
+
#
|
40
|
+
# This store also manages meta tuples, which it keeps in an array, just like
|
41
|
+
# the default Tuplespace class does.
|
42
|
+
class SortedSetSpace
|
43
|
+
include Enumerable
|
44
|
+
|
45
|
+
attr_reader :tag, :hash, :metas
|
46
|
+
|
47
|
+
def initialize tag
|
48
|
+
@tag = tag
|
49
|
+
clear
|
50
|
+
end
|
51
|
+
|
52
|
+
def clear
|
53
|
+
@hash = Hash.new {|h,k| h[k] = []}
|
54
|
+
# It's up to the application to enforce that these arrays have size <=1.
|
55
|
+
@metas = []
|
56
|
+
# We are automatically subscribed to tupelo metadata (subspace defs), so
|
57
|
+
# we need to keep them somewhere.
|
58
|
+
end
|
59
|
+
|
60
|
+
def each
|
61
|
+
hash.each do |k, vs|
|
62
|
+
vs.each do |v|
|
63
|
+
yield tag, k, v
|
64
|
+
end
|
65
|
+
end
|
66
|
+
metas.each do |tuple|
|
67
|
+
yield tuple
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def insert tuple
|
72
|
+
if tuple.kind_of? Array
|
73
|
+
# and tuple.size == 3 and tuple[0] == tag and tuple[1].kind_of? String
|
74
|
+
# This is redundant, because of subscribe.
|
75
|
+
t, k, v = tuple
|
76
|
+
hash[k] << v
|
77
|
+
|
78
|
+
else
|
79
|
+
metas << tuple
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def delete_once tuple
|
84
|
+
if tuple.kind_of? Array
|
85
|
+
# and tuple.size == 3 and tuple[0] == tag and tuple[1].kind_of? String
|
86
|
+
# This is redundant, because of subscribe.
|
87
|
+
t, k, v = tuple
|
88
|
+
if hash.key?(k) and hash[k].include? v
|
89
|
+
hash[k].delete v
|
90
|
+
hash.delete k if hash[k].empty?
|
91
|
+
true
|
92
|
+
else
|
93
|
+
false
|
94
|
+
end
|
95
|
+
|
96
|
+
else
|
97
|
+
if i=metas.index(tuple)
|
98
|
+
delete_at i
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def transaction inserts: [], deletes: [], tick: nil
|
104
|
+
deletes.each do |tuple|
|
105
|
+
delete_once tuple or raise "bug"
|
106
|
+
end
|
107
|
+
|
108
|
+
inserts.each do |tuple|
|
109
|
+
insert tuple.freeze ## should be deep_freeze
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def find_distinct_matches_for templates
|
114
|
+
templates.inject([]) do |tuples, template|
|
115
|
+
tuples << find_match_for(template, distinct_from: tuples)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def find_match_for template, distinct_from: []
|
120
|
+
case template
|
121
|
+
when SortedSetTemplate
|
122
|
+
template.find_in rbtree, distinct_from: distinct_from ###
|
123
|
+
else
|
124
|
+
# fall back to linear search
|
125
|
+
find do |tuple|
|
126
|
+
template === tuple and not distinct_from.any? {|t| t.equal? tuple}
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
data/lib/tupelo/client/reader.rb
CHANGED
@@ -3,10 +3,14 @@ require 'tupelo/client/common'
|
|
3
3
|
class Tupelo::Client
|
4
4
|
# Include into class that defines #worker and #log.
|
5
5
|
module Api
|
6
|
-
# If block given,
|
7
|
-
#
|
8
|
-
#
|
9
|
-
|
6
|
+
# If no block given, return one matching tuple, blocking if necessary.
|
7
|
+
# If block given, yield each matching tuple that is found
|
8
|
+
# locally and then yield each new match as it is written to the space.
|
9
|
+
# Guaranteed not to miss tuples, even if they arrive and are immediately
|
10
|
+
# taken. (Note that simply doing read(template) in a loop would not
|
11
|
+
# have this guarantee.)
|
12
|
+
# The template defaults to Object, which matches any tuple.
|
13
|
+
def read_wait template = Object
|
10
14
|
waiter = Waiter.new(worker.make_template(template), self, !block_given?)
|
11
15
|
worker << waiter
|
12
16
|
if block_given?
|
@@ -23,13 +27,16 @@ class Tupelo::Client
|
|
23
27
|
end
|
24
28
|
alias read read_wait
|
25
29
|
|
26
|
-
|
30
|
+
# The template defaults to Object, which matches any tuple.
|
31
|
+
def read_nowait template = Object
|
27
32
|
matcher = Matcher.new(worker.make_template(template), self)
|
28
33
|
worker << matcher
|
29
34
|
matcher.wait
|
30
35
|
end
|
31
36
|
|
32
|
-
#
|
37
|
+
# Returns all matching tuples currently in the space. The template defaults
|
38
|
+
# to Object, which matches any tuple. Does not wait for more tuples to
|
39
|
+
# arrive.
|
33
40
|
def read_all template = Object
|
34
41
|
matcher = Matcher.new(worker.make_template(template), self, :all => true)
|
35
42
|
worker << matcher
|
data/lib/tupelo/client/worker.rb
CHANGED
@@ -507,13 +507,15 @@ class Tupelo::Client
|
|
507
507
|
end
|
508
508
|
|
509
509
|
def handle_waiter waiter
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
510
|
+
if waiter.once
|
511
|
+
tuple = tuplespace.find_match_for waiter.template
|
512
|
+
if tuple
|
513
|
+
waiter.peek tuple
|
514
|
+
else
|
514
515
|
read_waiters << waiter
|
515
516
|
end
|
516
517
|
else
|
518
|
+
tuplespace.each {|tuple| waiter.gloms tuple}
|
517
519
|
read_waiters << waiter
|
518
520
|
end
|
519
521
|
end
|
data/lib/tupelo/version.rb
CHANGED
data/test/unit/test-ops.rb
CHANGED
@@ -86,6 +86,30 @@ class TestOps < Minitest::Test
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
+
def test_read_stream
|
90
|
+
writer, reader = make_clients(2)
|
91
|
+
a = []
|
92
|
+
n = 10; k = 3
|
93
|
+
|
94
|
+
(0...k).each do |i|
|
95
|
+
writer.now {write [i]}
|
96
|
+
end
|
97
|
+
|
98
|
+
reader.will {read([nil]){|t| a << t}}
|
99
|
+
|
100
|
+
(0...k).each do |i|
|
101
|
+
reader.run_until_blocked
|
102
|
+
assert_equal [i], a[i]
|
103
|
+
end
|
104
|
+
|
105
|
+
assert_equal [0], a[0]
|
106
|
+
(k...n).each do |i|
|
107
|
+
writer.now {write [i]}
|
108
|
+
reader.run_until_blocked
|
109
|
+
assert_equal [i], a[i]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
89
113
|
def test_take_existing
|
90
114
|
t = ["foo"]
|
91
115
|
cl = make_clients(2)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tupelo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.13'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joel VanderWerf
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-12-
|
11
|
+
date: 2013-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: atdo
|
@@ -84,111 +84,119 @@ files:
|
|
84
84
|
- lib/tupelo/app/trace.rb
|
85
85
|
- lib/tupelo/app/builder.rb
|
86
86
|
- lib/tupelo/client.rb
|
87
|
-
- lib/tupelo/tuplets/persistent-archiver.rb
|
88
|
-
- lib/tupelo/tuplets/persistent-archiver/worker.rb
|
89
|
-
- lib/tupelo/tuplets/persistent-archiver/tuplespace.rb
|
90
|
-
- lib/tupelo/util/boolean.rb
|
91
87
|
- lib/tupelo/app.rb
|
92
88
|
- lib/tupelo/archiver.rb
|
89
|
+
- lib/tupelo/client/worker.rb
|
90
|
+
- lib/tupelo/client/common.rb
|
91
|
+
- lib/tupelo/client/tuplespace.rb
|
92
|
+
- lib/tupelo/client/transaction.rb
|
93
|
+
- lib/tupelo/client/atdo.rb
|
94
|
+
- lib/tupelo/client/reader.rb
|
95
|
+
- lib/tupelo/tuplets/persistent-archiver/worker.rb
|
96
|
+
- lib/tupelo/tuplets/persistent-archiver/tuplespace.rb
|
97
|
+
- lib/tupelo/tuplets/persistent-archiver.rb
|
93
98
|
- lib/tupelo/archiver/persister.rb
|
94
99
|
- lib/tupelo/archiver/worker.rb
|
95
100
|
- lib/tupelo/archiver/tuplespace.rb
|
96
101
|
- lib/tupelo/archiver/persistent-tuplespace.rb
|
97
|
-
- lib/tupelo/
|
98
|
-
- lib/tupelo/client/worker.rb
|
99
|
-
- lib/tupelo/client/reader.rb
|
100
|
-
- lib/tupelo/client/tuplespace.rb
|
101
|
-
- lib/tupelo/client/atdo.rb
|
102
|
-
- lib/tupelo/client/common.rb
|
102
|
+
- lib/tupelo/util/boolean.rb
|
103
103
|
- lib/tupelo/version.rb
|
104
104
|
- bench/pipeline.rb
|
105
105
|
- bugs/read-take.rb
|
106
106
|
- bugs/take-write.rb
|
107
|
+
- example/pubsub.rb
|
108
|
+
- example/timeout-trans.rb
|
109
|
+
- example/fish01.rb
|
110
|
+
- example/tiny-client.rb
|
111
|
+
- example/add.rb
|
112
|
+
- example/parallel.rb
|
113
|
+
- example/socket-broker.rb
|
114
|
+
- example/multi-tier/memo2.rb
|
115
|
+
- example/multi-tier/drb.rb
|
116
|
+
- example/multi-tier/memo.rb
|
117
|
+
- example/multi-tier/kvspace.rb
|
118
|
+
- example/multi-tier/http.rb
|
119
|
+
- example/multi-tier/multi-sinatras.rb
|
120
|
+
- example/app-and-tup.rb
|
121
|
+
- example/small.rb
|
122
|
+
- example/bounded-retry.rb
|
123
|
+
- example/fish.rb
|
124
|
+
- example/zk/lock.rb
|
125
|
+
- example/concurrent-transactions.rb
|
126
|
+
- example/cancel.rb
|
127
|
+
- example/map-reduce/map-reduce-v2.rb
|
128
|
+
- example/map-reduce/remote-map-reduce.rb
|
129
|
+
- example/map-reduce/map-reduce.rb
|
130
|
+
- example/tiny-server.rb
|
131
|
+
- example/write-wait.rb
|
132
|
+
- example/tcp.rb
|
107
133
|
- example/timeout.rb
|
134
|
+
- example/read-in-trans.rb
|
135
|
+
- example/subspaces/simple.rb
|
136
|
+
- example/subspaces/pubsub.rb
|
137
|
+
- example/subspaces/addr-book-v1.rb
|
138
|
+
- example/subspaces/addr-book-v2.rb
|
139
|
+
- example/subspaces/shop/shop-v2.rb
|
140
|
+
- example/subspaces/shop/shop-v1.rb
|
141
|
+
- example/subspaces/sorted-set-space.rb
|
142
|
+
- example/balance-xfer-retry.rb
|
143
|
+
- example/take-nowait-caution.rb
|
144
|
+
- example/lock-mgr-with-queue.rb
|
145
|
+
- example/hash-tuples.rb
|
146
|
+
- example/pulse.rb
|
147
|
+
- example/transaction-logic.rb
|
148
|
+
- example/lease.rb
|
149
|
+
- example/chat/chat.rb
|
150
|
+
- example/chat/chat-nohistory.rb
|
151
|
+
- example/balance-xfer.rb
|
108
152
|
- example/add-dsl.rb
|
109
|
-
- example/
|
110
|
-
- example/
|
111
|
-
- example/
|
112
|
-
- example/dphil.rb
|
113
|
-
- example/broker-optimistic.rb
|
153
|
+
- example/lock-mgr.rb
|
154
|
+
- example/broker-locking.rb
|
155
|
+
- example/dphil-optimistic.rb
|
114
156
|
- example/fail-and-retry.rb
|
115
|
-
- example/
|
116
|
-
- example/read-in-trans.rb
|
117
|
-
- example/bounded-retry.rb
|
118
|
-
- example/pregel/remote.rb
|
119
|
-
- example/pregel/pagerank.rb
|
157
|
+
- example/fish0.rb
|
120
158
|
- example/pregel/pregel.rb
|
121
159
|
- example/pregel/distributed.rb
|
160
|
+
- example/pregel/pagerank.rb
|
122
161
|
- example/pregel/update.rb
|
162
|
+
- example/pregel/remote.rb
|
163
|
+
- example/pregel/dist-opt.rb
|
164
|
+
- example/dphil-optimistic-v2.rb
|
165
|
+
- example/broker-optimistic-v2.rb
|
166
|
+
- example/remote.rb
|
123
167
|
- example/take-nowait.rb
|
124
|
-
- example/
|
125
|
-
- example/
|
126
|
-
- example/broker-locking.rb
|
127
|
-
- example/transaction-logic.rb
|
168
|
+
- example/wait-interrupt.rb
|
169
|
+
- example/optimist.rb
|
128
170
|
- example/message-bus.rb
|
129
|
-
- example/small-simplified.rb
|
130
|
-
- example/small.rb
|
131
|
-
- example/lock-mgr.rb
|
132
|
-
- example/take-nowait-caution.rb
|
133
|
-
- example/concurrent-transactions.rb
|
134
|
-
- example/tcp.rb
|
135
|
-
- example/notify.rb
|
136
|
-
- example/pulse.rb
|
137
|
-
- example/chat/chat.rb
|
138
|
-
- example/hash-tuples.rb
|
139
171
|
- example/balance-xfer-locking.rb
|
140
|
-
- example/
|
172
|
+
- example/increment.rb
|
173
|
+
- example/child-of-child.rb
|
174
|
+
- example/custom-class.rb
|
175
|
+
- example/matching.rb
|
141
176
|
- example/custom-search.rb
|
142
|
-
- example/
|
143
|
-
- example/
|
144
|
-
- example/
|
145
|
-
- example/
|
146
|
-
- example/multi-tier/kvspace.rb
|
147
|
-
- example/multi-tier/memo.rb
|
148
|
-
- example/multi-tier/drb.rb
|
149
|
-
- example/take-many.rb
|
150
|
-
- example/subspaces/simple.rb
|
151
|
-
- example/subspaces/shop/shop-v2.rb
|
152
|
-
- example/subspaces/shop/shop-v1.rb
|
153
|
-
- example/subspaces/pubsub.rb
|
154
|
-
- example/dphil-optimistic.rb
|
177
|
+
- example/broker-optimistic.rb
|
178
|
+
- example/notify.rb
|
179
|
+
- example/small-simplified.rb
|
180
|
+
- example/broker-queue.rb
|
155
181
|
- example/async-transaction.rb
|
156
|
-
- example/
|
157
|
-
- example/
|
158
|
-
- example/
|
182
|
+
- example/boolean-match.rb
|
183
|
+
- example/load-balancer.rb
|
184
|
+
- example/take-many.rb
|
159
185
|
- example/deadlock.rb
|
160
|
-
- example/
|
161
|
-
- example/add.rb
|
162
|
-
- example/dphil-optimistic-v2.rb
|
163
|
-
- example/parallel.rb
|
164
|
-
- example/tiny-client.rb
|
165
|
-
- example/map-reduce/map-reduce.rb
|
166
|
-
- example/map-reduce/remote-map-reduce.rb
|
167
|
-
- example/map-reduce/map-reduce-v2.rb
|
168
|
-
- example/lock-mgr-with-queue.rb
|
169
|
-
- example/balance-xfer.rb
|
170
|
-
- example/cancel.rb
|
171
|
-
- example/socket-broker.rb
|
172
|
-
- example/timeout-trans.rb
|
173
|
-
- example/optimist.rb
|
174
|
-
- example/tiny-server.rb
|
175
|
-
- example/pubsub.rb
|
176
|
-
- example/broker-optimistic-v2.rb
|
177
|
-
- example/write-wait.rb
|
178
|
-
- example/custom-class.rb
|
179
|
-
- test/stress/archiver-load.rb
|
180
|
-
- test/stress/concurrent-transactions.rb
|
181
|
-
- test/system/test-archiver.rb
|
182
|
-
- test/lib/mock-client.rb
|
183
|
-
- test/lib/time-fuzz.rb
|
184
|
-
- test/lib/mock-queue.rb
|
185
|
-
- test/lib/mock-seq.rb
|
186
|
+
- example/dphil.rb
|
186
187
|
- test/lib/testable-worker.rb
|
188
|
+
- test/lib/mock-seq.rb
|
187
189
|
- test/lib/mock-msg.rb
|
188
|
-
- test/
|
189
|
-
- test/
|
190
|
+
- test/lib/mock-queue.rb
|
191
|
+
- test/lib/time-fuzz.rb
|
192
|
+
- test/lib/mock-client.rb
|
193
|
+
- test/system/test-archiver.rb
|
190
194
|
- test/unit/test-ops.rb
|
191
195
|
- test/unit/test-mock-client.rb
|
196
|
+
- test/unit/test-mock-seq.rb
|
197
|
+
- test/unit/test-mock-queue.rb
|
198
|
+
- test/stress/concurrent-transactions.rb
|
199
|
+
- test/stress/archiver-load.rb
|
192
200
|
- bin/tup
|
193
201
|
- bin/tspy
|
194
202
|
homepage: https://github.com/vjoel/tupelo
|
@@ -223,8 +231,8 @@ signing_key:
|
|
223
231
|
specification_version: 4
|
224
232
|
summary: Distributed tuplespace
|
225
233
|
test_files:
|
226
|
-
- test/unit/test-mock-seq.rb
|
227
|
-
- test/unit/test-mock-queue.rb
|
228
234
|
- test/unit/test-ops.rb
|
229
235
|
- test/unit/test-mock-client.rb
|
236
|
+
- test/unit/test-mock-seq.rb
|
237
|
+
- test/unit/test-mock-queue.rb
|
230
238
|
has_rdoc:
|