tupelo 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -30
- data/bin/tup +1 -1
- data/lib/tupelo/irb-shell.rb +60 -0
- 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: 526ed0a8bc267debbd03d9306433256bc225ff41
|
4
|
+
data.tar.gz: 1be5ebae3fff130e0d0dde261490604f4936d78e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a98abb4bf4465e3e166c5295065984120f62a06021e369aa53db12f8ad83308253cb170a2b63fca017c8b83a0abfc1d15c29268af1b22ac5bca89b0d3119ffb4
|
7
|
+
data.tar.gz: 24a8d32264737f932a124abccaf9c9512febd28473930b762baaada6c159d8a5a7afb805b8ee55ac251764c9c19162b9185e29efd2508d06cbc1186c4a5ae066
|
data/README.md
CHANGED
@@ -9,10 +9,12 @@ Tupelo differs from other spaces in several ways:
|
|
9
9
|
|
10
10
|
* minimal central storage: the only state in the server is a counter and socket connections
|
11
11
|
|
12
|
-
* minimal central computation: just counter increment, message dispatch, and connection management
|
12
|
+
* minimal central computation: just counter increment, message dispatch, and connection management (and it never unpacks serialized tuples)
|
13
13
|
|
14
14
|
* clients do all the tuple work: registering and checking waiters, matching, searching, notifying, storing, inserting, deleting, persisting, etc. Each client is free to to decide how to do these things (application code is insulated from this, however). Special-purpose clients may use specialized algorithms and stores for the subspaces they manage.
|
15
15
|
|
16
|
+
* transactions, in addition to the classic operators.
|
17
|
+
|
16
18
|
* replication is inherent in the design (in fact it is unavoidable), for better or worse.
|
17
19
|
|
18
20
|
|
@@ -21,7 +23,7 @@ Getting started
|
|
21
23
|
|
22
24
|
1. Install ruby 2 (not 1.9) from http://ruby-lang.org. Examples and tests will not work on windows (they use fork and unix sockets), though probably the underying libs will (using tcp sockets).
|
23
25
|
|
24
|
-
2. Install the gem and its dependencies:
|
26
|
+
2. Install the gem and its dependencies (you may need to `sudo` this):
|
25
27
|
|
26
28
|
gem install tupelo
|
27
29
|
|
@@ -34,9 +36,9 @@ Getting started
|
|
34
36
|
>> t [nil, nil]
|
35
37
|
=> ["hello", "world"]
|
36
38
|
|
37
|
-
If you run tup with the --info switch it will tell you the aliases to the tuple API (and also tell you much about what is happening in your transactions).
|
39
|
+
If you run tup with the --info switch it will tell you the aliases to the tuple API (and also tell you much about what is happening in your transactions). Here's an overview of the API, including the short aliases avilable in tup:
|
38
40
|
|
39
|
-
Write one or more tuples (and wait for the transaction to be recorded):
|
41
|
+
Write one or more tuples (and wait for the transaction to be recorded in the local space):
|
40
42
|
|
41
43
|
w <tuple>,...
|
42
44
|
write_wait <tuple>,...
|
@@ -63,7 +65,7 @@ Getting started
|
|
63
65
|
r <template>
|
64
66
|
read_wait <template>
|
65
67
|
|
66
|
-
Read tuple matching a template, without waiting for a match to exist:
|
68
|
+
Read tuple matching a template and return it, without waiting for a match to exist (returning nil in that case):
|
67
69
|
|
68
70
|
read_nowait <template>
|
69
71
|
|
@@ -82,19 +84,28 @@ Getting started
|
|
82
84
|
|
83
85
|
reads all 2-tuples.
|
84
86
|
|
85
|
-
Take a tuple
|
87
|
+
Take a tuple matching a template:
|
88
|
+
|
89
|
+
t <template>
|
90
|
+
take <template>
|
91
|
+
|
92
|
+
Take a tuple matching a template and optimistically use the local value before the transaction is complete:
|
93
|
+
|
94
|
+
x_final = take <template> do |x_optimistic|
|
95
|
+
...
|
96
|
+
end
|
97
|
+
|
98
|
+
There is no guarantee that `x_final == x_optimistic`. The block may execute more than once.
|
86
99
|
|
87
|
-
|
88
|
-
take <tuple>
|
100
|
+
Take a tuple matching a template, but only if a local match exist (otherwise return nil):
|
89
101
|
|
90
|
-
|
91
|
-
complete:
|
102
|
+
take_nowait <template>
|
92
103
|
|
93
|
-
x_final =
|
104
|
+
x_final = take_nowait <template> do |x_optimistic|
|
94
105
|
...
|
95
106
|
end
|
96
107
|
|
97
|
-
|
108
|
+
Note that a local match is still not a guarantee of returning that tuple immediately -- another process may take it first, blocking this process.
|
98
109
|
|
99
110
|
Perform a general transaction:
|
100
111
|
|
@@ -107,21 +118,23 @@ complete:
|
|
107
118
|
[rval, tval] # pass out result
|
108
119
|
end
|
109
120
|
|
110
|
-
Note that the block may
|
121
|
+
Note that the block may execute more than once, if there is competition for the tuples that you are trying to #take or #read. When the block exits, however, the transaction is final and universally accepted by all clients.
|
111
122
|
|
112
123
|
4. Run tup with a server file so that two sessions can interact. Do this in two terminals in the same dir:
|
113
124
|
|
114
125
|
$ tup svr
|
115
126
|
|
116
127
|
(The 'svr' argument names a file that the first instance of tup uses to store information like socket addresses and the second instance uses to connect. The first instance starts the servers as child processes. However, both instances appear in the terminal as interactive shells.)
|
128
|
+
|
129
|
+
To do this on two hosts, copy the svr file and edit its hostname params as needed.
|
117
130
|
|
118
131
|
5. Look at the examples. You may need to dig a bit to find the gem installation. For example:
|
119
132
|
|
120
|
-
ls /usr/local/lib/ruby/gems/
|
133
|
+
ls -d /usr/local/lib/ruby/gems/*/gems/tupelo*
|
121
134
|
|
122
|
-
Note that all bin and example programs accept blob type (e.g., --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
|
+
Note that all bin and example programs accept blob type (e.g., --msgpaack, --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.
|
123
136
|
|
124
|
-
6. Deugging: in addition to --info, bin/tspy is also really useful, and see the debugger client in example/lock-mgr.rb.
|
137
|
+
6. Deugging: in addition to the --info switch on all bin and example programs, bin/tspy is also really useful, and see the debugger client in example/lock-mgr.rb.
|
125
138
|
|
126
139
|
|
127
140
|
What is a tuplespace?
|
@@ -159,7 +172,7 @@ In other words, a tuple is a fairly general object, though this depends on the s
|
|
159
172
|
|
160
173
|
* hashes
|
161
174
|
|
162
|
-
It's kind of like a "JSON object", except that in the json blob case, the hash keys can only be strings. In the case of the marshal and yaml modes, tuples can contain many other kinds of objects.
|
175
|
+
It's kind of like a "JSON object", except that in the json blob case, 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.
|
163
176
|
|
164
177
|
What is a template?
|
165
178
|
-------------------
|
@@ -178,7 +191,7 @@ but not these tuples:
|
|
178
191
|
[3, 7.2, "foobar", "xyz"]
|
179
192
|
[3, 7, "fobar", "xyz"]
|
180
193
|
|
181
|
-
The nil wildcard matches anything.
|
194
|
+
The nil wildcard matches anything. The Range, Regexp, and Class entries function as wildcards because of the way they define the #=== (match) method. See ruby docs for general information on "threequals" matching.
|
182
195
|
|
183
196
|
Here's a template for matching some hash tuples:
|
184
197
|
|
@@ -186,7 +199,7 @@ Here's a template for matching some hash tuples:
|
|
186
199
|
|
187
200
|
This would match all tuples whose keys are "name" and "location" and whose values for those keys are any string and the string "home", respectively.
|
188
201
|
|
189
|
-
A template doesn't have to be a pattern, though. It can be anything with a #=== method. For example:
|
202
|
+
A template doesn't have to be a tuple pattern with wildcards, though. It can be anything with a #=== method. For example:
|
190
203
|
|
191
204
|
read_all proc {|t| some_predicate(t)}
|
192
205
|
read_all Hash
|
@@ -204,28 +217,30 @@ What are the operations on tuples?
|
|
204
217
|
|
205
218
|
* take - search the space for matching tuples, waiting if none found, removing the tuple if found
|
206
219
|
|
207
|
-
* pulse - write and take the tuple; readers see it, but it cannot be taken
|
220
|
+
* pulse - write and take the tuple; readers see it, but it cannot be taken (nto a classical tuplespace operation)
|
208
221
|
|
209
222
|
These operations have a few variations (wait vs nowait) and options (timeouts).
|
210
223
|
|
211
224
|
Transactions and optimistic concurrency
|
212
225
|
--------------------
|
213
226
|
|
214
|
-
Transactions combine operations into a group that take effect at the same instant in (logical) time, isolated from other transactions.
|
227
|
+
Transactions combine operations into a group that take effect at the same instant in (logical) time, isolated from other transactions.
|
228
|
+
|
229
|
+
However, it may take some time to prepare the transaction. This is true in terms of both real time (clock and process) and logical tiem (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 can begin 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.
|
215
230
|
|
216
231
|
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.
|
217
232
|
|
218
233
|
Transactions give you a means of optimistic locking: the transaction proceeds in a way that depends on preconditions. See example/increment.rb for a very simple example. Not only can you make a transaction depend on the existence of a tuple, you can make the effect of the transaction a function of existing tuples (see example/transaction-logic.rb and example/broker-optimistic.rb).
|
219
234
|
|
220
|
-
If you prefer classical tuplespace locking, you can simply take
|
235
|
+
If you prefer classical tuplespace locking, you can simply use certain tuples as locks, using take/write to lock/unlock them. See the examples. If you have a lot of contention and want to avoid the thundering herd, see example/lock-mgr-with-queue.rb.
|
221
236
|
|
222
237
|
If an optimistic transaction fails (for example, it is trying to take a tuple, but the tuple has just been taken by another transaction), then the transaction block is re-executed, possibly waiting for new matches to the templates. Application code must be aware of the possible re-execution of the block. This is better explained in the examples...
|
223
238
|
|
224
|
-
ACID
|
239
|
+
Tupelo transactions are ACID in the following sense. They are Atomic and Isolated -- this is enforced by the transaction processing in each client. Consistency is enforced by the underlying message sequencer (each client's copy of the space is the deterministic result of the same sequence of operations). Durability is optional, but can be provided by the archiver (to be implemented) or other clients.
|
225
240
|
|
226
241
|
On the CAP spectrum, tupelo tends towards consistency.
|
227
242
|
|
228
|
-
|
243
|
+
Tupelo transactions do not require two-phase commit, because they are less powerful than general transactions. Each client has enough information to decide (in the same way as all other clients) whether the transaction succeeds or fails. This has performance advantages, but imposes some limitations on transactions over subspaces that are known to one client but not another.
|
229
244
|
|
230
245
|
|
231
246
|
Advantages
|
@@ -264,22 +279,21 @@ What other potential problems and how does tupelo solve them?
|
|
264
279
|
Future
|
265
280
|
======
|
266
281
|
|
267
|
-
- Subspaces. Redundancy, for read-heavy
|
282
|
+
- Subspaces. Redundancy, for read-heavy data stores (redundant array of in-memory sqlite, for example). Clients managing different subspaces may benefit by using different stores and algorithms.
|
268
283
|
|
269
284
|
- More persistence options.
|
270
285
|
|
271
286
|
- Fail-over. Robustness.
|
272
287
|
|
273
|
-
- Investigate nio4r for faster networking.
|
288
|
+
- Investigate nio4r for faster networking, especially with many clients.
|
274
289
|
|
275
290
|
- Interoperable client and server implementations in C, Python, Go, ....
|
276
291
|
|
277
|
-
- UDP multicast.
|
292
|
+
- UDP multicast to further reduce the bottleneck in the message sequencer.
|
278
293
|
|
279
294
|
- Tupelo as a service; specialized and replicated subspace managers as services.
|
280
295
|
|
281
296
|
|
282
|
-
|
283
297
|
Comparisons
|
284
298
|
===========
|
285
299
|
|
@@ -290,6 +304,8 @@ Unlike redis, computations are not a centralized bottleneck. Set intersection, f
|
|
290
304
|
|
291
305
|
Pushing data to client eliminates need for polling, makes reads faster.
|
292
306
|
|
307
|
+
Tupelo's pulse/read ops are like pubsub in redis.
|
308
|
+
|
293
309
|
However, tupelo is not a substitute for the caching functionality of redis and memcache.
|
294
310
|
|
295
311
|
|
@@ -298,7 +314,7 @@ Rinda
|
|
298
314
|
|
299
315
|
Very similar api.
|
300
316
|
|
301
|
-
|
317
|
+
Rinda has a severe bottleneck, though: all matching, waiting, etc. are performed in one process.
|
302
318
|
|
303
319
|
Rinda is rpc-based, which is slower and also more vulnerable due to the extra client-server state; tupelo is imlemented on a message layer, rather than rpc. This also helps with pipelined writes.
|
304
320
|
|
@@ -375,7 +391,7 @@ Each inner serialization method ("blobber") has its own advantages and drawbacks
|
|
375
391
|
|
376
392
|
* 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.
|
377
393
|
|
378
|
-
For most purposes, msgpack is the
|
394
|
+
For most purposes, msgpack is a good choice, so it is the default.
|
379
395
|
|
380
396
|
|
381
397
|
Development
|
data/bin/tup
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'irb'
|
4
|
+
require 'irb/completion'
|
5
|
+
|
6
|
+
module IRB
|
7
|
+
def IRB.parse_opts
|
8
|
+
# Don't touch ARGV, which belongs to the app which called this module.
|
9
|
+
end
|
10
|
+
|
11
|
+
def IRB.start_session(*args)
|
12
|
+
unless $irb
|
13
|
+
IRB.setup nil
|
14
|
+
## maybe set some opts here, as in parse_opts in irb/init.rb?
|
15
|
+
end
|
16
|
+
|
17
|
+
workspace = WorkSpace.new(*args)
|
18
|
+
|
19
|
+
@CONF[:PROMPT_MODE] = :SIMPLE
|
20
|
+
|
21
|
+
# Enables _ as last value
|
22
|
+
@CONF[:EVAL_HISTORY] = 1000
|
23
|
+
@CONF[:SAVE_HISTORY] = 100
|
24
|
+
|
25
|
+
if @CONF[:SCRIPT] ## normally, set by parse_opts
|
26
|
+
$irb = Irb.new(workspace, @CONF[:SCRIPT])
|
27
|
+
else
|
28
|
+
$irb = Irb.new(workspace)
|
29
|
+
end
|
30
|
+
|
31
|
+
@CONF[:IRB_RC].call($irb.context) if @CONF[:IRB_RC]
|
32
|
+
@CONF[:MAIN_CONTEXT] = $irb.context
|
33
|
+
|
34
|
+
trap 'INT' do
|
35
|
+
$irb.signal_handle
|
36
|
+
end
|
37
|
+
|
38
|
+
custom_configuration if defined?(IRB.custom_configuration)
|
39
|
+
|
40
|
+
begin
|
41
|
+
catch :IRB_EXIT do
|
42
|
+
$irb.eval_input
|
43
|
+
end
|
44
|
+
ensure
|
45
|
+
IRB.irb_at_exit
|
46
|
+
end
|
47
|
+
|
48
|
+
## might want to reset your app's interrupt handler here
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Object
|
53
|
+
include IRB::ExtendCommandBundle # so that Marshal.dump works
|
54
|
+
end
|
55
|
+
|
56
|
+
if __FILE__ == $0
|
57
|
+
x = Object.new
|
58
|
+
puts "Started irb shell for x with current binding"
|
59
|
+
IRB.start_session(binding, x)
|
60
|
+
end
|
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.4'
|
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-07-
|
11
|
+
date: 2013-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: atdo
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/tupelo/client/tuplespace.rb
|
88
88
|
- lib/tupelo/client/transaction.rb
|
89
89
|
- lib/tupelo/client/reader.rb
|
90
|
+
- lib/tupelo/irb-shell.rb
|
90
91
|
- lib/tupelo/archiver/worker.rb
|
91
92
|
- lib/tupelo/archiver/tuplespace.rb
|
92
93
|
- lib/tupelo/version.rb
|