tupelo 0.6 → 0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -9
- data/example/remote-map-reduce.rb +27 -0
- data/example/remote.rb +7 -2
- data/example/small.rb +2 -2
- data/lib/tupelo/app/remote.rb +1 -2
- data/lib/tupelo/app.rb +1 -1
- data/lib/tupelo/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 74456cde00385b5340494724ee0affc7cf6e2534
|
4
|
+
data.tar.gz: 331f3970d1fd41a702ada650f5701616de24b184
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcfe41b674e798ba15761f6740bc74a11963a793538a725ec393fefd4328eb9053e019d95c0f4701cb2ca680dc52c2d2e8dce48921be7cd68532c639b4c61d90
|
7
|
+
data.tar.gz: 82b13b6aa0675069946428033e5d032ca4418409c5901da00cac76cf139d7cc34afa428c4e666e42370b69241c797cabb55b375cfd34e5d8bd5bc83f2e6bf9a9
|
data/README.md
CHANGED
@@ -105,7 +105,8 @@ Getting started
|
|
105
105
|
...
|
106
106
|
end
|
107
107
|
|
108
|
-
Note that a local match is still not a guarantee of `x_final == x_optimistic`. Another process may take `x_optimistic` first, and the take will be re-executed.
|
108
|
+
Note that a local match is still not a guarantee of `x_final == x_optimistic`. Another process may take `x_optimistic` first, and the take will be re-executed. (Think of #take_nowait as a way of saying "take a match, but don't bother trying if there is no match known at this time.")
|
109
|
+
|
109
110
|
Perform a general transaction:
|
110
111
|
|
111
112
|
result =
|
@@ -133,7 +134,7 @@ Getting started
|
|
133
134
|
|
134
135
|
Note that all bin and example programs accept blob type (e.g., --msgpack, --json) on command line (it only needs to be specified for server -- the clients discover it). Also, all these programs accept log level on command line. The default is --warn. The --info level is a good way to get an idea of what is happening, without the verbosity of --debug.
|
135
136
|
|
136
|
-
6. Debugging: in addition to the --info switch on all bin and example programs, bin/tspy is also really useful. There is also the similar --trace switch that is available to all bin and example programs
|
137
|
+
6. Debugging: in addition to the --info switch on all bin and example programs, bin/tspy is also really useful. There is also the similar --trace switch that is available to all bin and example programs. This switch diagnostic output for each transaction. For example:
|
137
138
|
|
138
139
|
```
|
139
140
|
tick cid status operation
|
@@ -142,7 +143,7 @@ Getting started
|
|
142
143
|
3 3 atomic take ["x", 1], ["y", 2]
|
143
144
|
```
|
144
145
|
|
145
|
-
The `Tupelo.application` command, provided by `tupelo/app`, is the source of all these options and is available to your programs.
|
146
|
+
The `Tupelo.application` command, provided by `tupelo/app`, is the source of all these options and is available to your programs. It's a kind of lightweight process deployment and control framework; however it is not necessary to use tupelo.
|
146
147
|
|
147
148
|
|
148
149
|
What is a tuplespace?
|
@@ -180,7 +181,7 @@ In other words, a tuple is a fairly general object, though this depends on the s
|
|
180
181
|
|
181
182
|
* hashes
|
182
183
|
|
183
|
-
It's kind of like a "JSON object", except that
|
184
|
+
It's kind of like a "JSON object", except that, when using the json serializer, the hash keys can only be strings. In the msgpack case, keys have no special limitations. In the case of the marshal and yaml modes, tuples can contain many other kinds of objects.
|
184
185
|
|
185
186
|
What is a template?
|
186
187
|
-------------------
|
@@ -244,7 +245,7 @@ Transactions and optimistic concurrency
|
|
244
245
|
|
245
246
|
Transactions combine operations into a group that take effect at the same instant in (logical) time, isolated from other transactions.
|
246
247
|
|
247
|
-
However, it may take some time to prepare the transaction. This is true in terms of both real time (clock and process) and logical
|
248
|
+
However, it may take some time to prepare the transaction. This is true in terms of both real time (clock and process) and logical time (global sequence of operations). Preparing a transaction means finding tuples that match the criteria of the read and take operations. Finding tuples may require searching (locally) for tuples, or waiting for new tuples to be written by others. Also, the transaction may fail even after matching tuples are found (when another process takes tuples of interest). Then the transaction needs to be prepared again. Once prepared, transaction is sent to all clients, where it may either succeed (in all clients) or fail (for the same reason as before--someone else grabbed one of our tuples). If it fails, then the preparation begins again. A transaction guarantees that, when it completes, all the operations were performed on the tuples at the same logical time. It does not guarantee that the world stands still while one process is inside the `transaction {...}` block.
|
248
249
|
|
249
250
|
Transactions are not just about batching up operations into a more efficient package (though you can do that with the #batch api). A transaction makes the combined operations execute atomically: the transaction finishes only when all of its operations can be successfully performed. Writes and pulses can always succeed, but takes and reads only succeed if the tuples exist.
|
250
251
|
|
@@ -378,6 +379,8 @@ To compare
|
|
378
379
|
|
379
380
|
* pubsubs: kafka
|
380
381
|
|
382
|
+
* spark, storm
|
383
|
+
|
381
384
|
Architecture
|
382
385
|
============
|
383
386
|
|
@@ -410,9 +413,9 @@ Protocol
|
|
410
413
|
|
411
414
|
Nothing in the protocol specifies local searching or storage, or matching, or notification, or templating. That's all up to each client. The protocol only contains tuples and operations on them (take, write, pulse, read), combined into transactions.
|
412
415
|
|
413
|
-
The protocol has two layers. The outer (message) layer is 6 fields, managed by the funl gem, using msgpack for serialization. All socket reads are non-blocking, so a slow sender will not block other activity in the system.
|
416
|
+
The protocol has two layers. The outer (message) layer is 6 fields, managed by the funl gem, using msgpack for serialization. All socket reads are non-blocking (using msgpack's stream mode), so a slow sender will not block other activity in the system.
|
414
417
|
|
415
|
-
The inner (blob) layer manages
|
418
|
+
One of those 6 fields is a data blob, containing the actual transaction and tuple information. The inner (blob) layer manages that field using msgpack (by default), marshal, json, or yaml. This layer contains the transaction operations. The blob is not unpacked by the server, only by clients.
|
416
419
|
|
417
420
|
Each inner serialization method ("blobber") has its own advantages and drawbacks:
|
418
421
|
|
@@ -422,8 +425,6 @@ Each inner serialization method ("blobber") has its own advantages and drawbacks
|
|
422
425
|
|
423
426
|
* msgpack and json (yajl) are both relatively efficient (in terms of packet size, as well as parse/emit time)
|
424
427
|
|
425
|
-
* msgpack and json both support non-blocking (buffered) reads, which can avoid bottlenecks due to slow senders or bad networks.
|
426
|
-
|
427
428
|
* msgpack and json support the least diversity of objects (just "JSON objects"), but msgpack also supports hash keys that are objects rather than just strings.
|
428
429
|
|
429
430
|
For most purposes, msgpack is a good choice, so it is the default.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'tupelo/app/remote'
|
2
|
+
|
3
|
+
hosts = ARGV.shift or abort "usage: #$0 <ssh-hostname>,<ssh-hostname>,..."
|
4
|
+
hosts = hosts.split(",")
|
5
|
+
|
6
|
+
Tupelo.tcp_application do
|
7
|
+
hosts.each do |host|
|
8
|
+
remote host: host, passive: true, eval: %{
|
9
|
+
loop do
|
10
|
+
len = take([String])[0].size
|
11
|
+
write [len]
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
local do
|
17
|
+
input = [ ["We are going to"], ["calcula"], ["te the len"],
|
18
|
+
["gth of this "], ["sentence."] ]
|
19
|
+
write *input
|
20
|
+
sum = 0
|
21
|
+
input.size.times do
|
22
|
+
sum += take([Numeric])[0]
|
23
|
+
end
|
24
|
+
log "sum = #{sum}, correct sum = #{input.flatten.join.size}"
|
25
|
+
sleep 2
|
26
|
+
end
|
27
|
+
end
|
data/example/remote.rb
CHANGED
@@ -13,15 +13,20 @@ Tupelo.tcp_application do
|
|
13
13
|
# only for examples and tests, not production.
|
14
14
|
end
|
15
15
|
|
16
|
-
remote host: host, eval: %{
|
16
|
+
remote host: host, log: true, eval: %{
|
17
17
|
write host: `hostname`.chomp, mode: "eval", client: client_id
|
18
18
|
}
|
19
19
|
# rather than embed large chunks of code in the string, it's better to
|
20
20
|
# load or require a file and pass self (which is a Client instance) to
|
21
21
|
# a method in that file.
|
22
22
|
|
23
|
+
remote host: host, log: true, passive: true, eval: %{
|
24
|
+
write host: `hostname`.chomp, mode: "eval", client: client_id
|
25
|
+
sleep # since passive, app can stop this process
|
26
|
+
}
|
27
|
+
|
23
28
|
local do
|
24
|
-
|
29
|
+
3.times do
|
25
30
|
log take host: nil, mode: nil, client: nil
|
26
31
|
end
|
27
32
|
end
|
data/example/small.rb
CHANGED
@@ -58,7 +58,7 @@ EasyServe.start(servers_file: "small-servers.yaml") do |ez|
|
|
58
58
|
client.stop if client # gracefully exit the tuplespace management thread
|
59
59
|
end
|
60
60
|
|
61
|
-
ez.
|
61
|
+
ez.child :seqd, :cseqd, :arcd do |seqd, cseqd, arcd|
|
62
62
|
run_client seq: seqd, cseq: cseqd, arc: arcd, log: log do |client|
|
63
63
|
client.write [2, 3, "frogs"]
|
64
64
|
_, s = client.take ["animals", nil]
|
@@ -66,7 +66,7 @@ EasyServe.start(servers_file: "small-servers.yaml") do |ez|
|
|
66
66
|
end
|
67
67
|
end
|
68
68
|
|
69
|
-
ez.
|
69
|
+
ez.child :seqd, :cseqd, :arcd do |seqd, cseqd, arcd|
|
70
70
|
run_client seq: seqd, cseq: cseqd, arc: arcd, log: log do |client|
|
71
71
|
x, y, s = client.take [Numeric, Numeric, String]
|
72
72
|
s2 = ([s] * (x + y)).join(" ")
|
data/lib/tupelo/app/remote.rb
CHANGED
@@ -23,14 +23,13 @@ module Tupelo
|
|
23
23
|
def remote client_class = Client,
|
24
24
|
client_lib: 'tupelo/client', host: nil, **opts
|
25
25
|
require 'easy-serve/remote'
|
26
|
-
raise if opts[:passive] ## not supported yet
|
27
26
|
## detach option so that remote process doesn't keep ssh connection
|
28
27
|
snames = :seqd, :cseqd, :arcd
|
29
28
|
|
30
29
|
if opts[:eval]
|
31
30
|
ez.remote *snames, host: host, **opts, eval: %{
|
32
31
|
require #{client_lib.inspect}
|
33
|
-
|
32
|
+
|
34
33
|
seqd, cseqd, arcd = *conns
|
35
34
|
client_class = Object.const_get(#{client_class.name.inspect})
|
36
35
|
|
data/lib/tupelo/app.rb
CHANGED
@@ -46,7 +46,7 @@ module Tupelo
|
|
46
46
|
# way. Then you do not have to manually interrupt the whole application when
|
47
47
|
# the active processes are done. See examples.
|
48
48
|
def child client_class = Client, passive: false, &block
|
49
|
-
ez.
|
49
|
+
ez.child :seqd, :cseqd, :arcd, passive: passive do |seqd, cseqd, arcd|
|
50
50
|
run_client client_class,
|
51
51
|
seq: seqd, cseq: cseqd, arc: arcd, log: log do |client|
|
52
52
|
if block
|
data/lib/tupelo/version.rb
CHANGED
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.7'
|
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-08-
|
11
|
+
date: 2013-08-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: atdo
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- example/hash-tuples.rb
|
119
119
|
- example/pulse.rb
|
120
120
|
- example/transaction-logic.rb
|
121
|
+
- example/remote-map-reduce.rb
|
121
122
|
- example/map-reduce.rb
|
122
123
|
- example/balance-xfer.rb
|
123
124
|
- example/add-dsl.rb
|