tupelo 0.3 → 0.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: caba2e1b8c79aa057dc89f71e8317efd1ac5e5a7
4
- data.tar.gz: 174ab307f70d73e96808ddf75658b7e248a5554c
3
+ metadata.gz: 526ed0a8bc267debbd03d9306433256bc225ff41
4
+ data.tar.gz: 1be5ebae3fff130e0d0dde261490604f4936d78e
5
5
  SHA512:
6
- metadata.gz: 9b2ce6df5ed232e82e1a6583111581046ebf169ce5f35381321137b67b70f0759756c1cd4091b6759fe9e46b16c2b274a01d915d43e42bf074b49bb65cbcdef0
7
- data.tar.gz: e805c61b256c080209e983fa207b7a262ecb313fa8c13166a4002b41f2a7fa6ffc4267def274561aea8eef67078daf9ac38aa188ba4170dc6bdecf043f35cc9a
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). Briefly:
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
- t <tuple>
88
- take <tuple>
100
+ Take a tuple matching a template, but only if a local match exist (otherwise return nil):
89
101
 
90
- Take a tuple and optimistically use the local value before the transaction is
91
- complete:
102
+ take_nowait <template>
92
103
 
93
- x_final = take <tuple> do |x_optimistic|
104
+ x_final = take_nowait <template> do |x_optimistic|
94
105
  ...
95
106
  end
96
107
 
97
- It's possible that the block will be called with a value different from the eventual return value. It's also possible for the block to be called more than once.
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 get executed more than once, if there is competition for the tuples that you are trying to #take. When the block exits, however, the transaction is final and universally accepted by all clients.
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/2.0.0/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. However, it may take some time (both real and logical) to prepare the transaction: to find 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 (globally) or fail (for the same reason as before--someone else grabbed 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.
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 / write lock tuples. See the examples. If you have a lot of contention and want to avoid the thundering herd, see example/lock-mgr-with-queue.rb.
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 -- Atomic and Isolated are enforced by the transactions; Consistency is enforced by the 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.
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
- These 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 imposes a limitation on transactions over subspaces...
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 systems (redundant array of in-memory sqlite, for example). Clients managing different subspaces may benefit by using different stores and algorithms.
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
- No central bottleneck.
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 right default choice.
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
@@ -155,7 +155,7 @@ EasyServe.start ez_opts do |ez|
155
155
  "starting shell. Commands: #{TupClient::CMD_ALIASES.join(", ")}"
156
156
  }
157
157
 
158
- require 'irb-shell'
158
+ require 'tupelo/irb-shell'
159
159
  IRB.start_session(client)
160
160
 
161
161
  client.stop
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Tupelo
2
- VERSION = "0.3"
2
+ VERSION = "0.4"
3
3
  end
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.3'
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-23 00:00:00.000000000 Z
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