tupelo 0.21 → 0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +171 -45
- data/bin/tup +51 -0
- data/example/counters/merge.rb +23 -3
- data/example/multi-tier/multi-sinatras.rb +5 -0
- data/example/riemann/event-subspace.rb +1 -4
- data/example/riemann/expiration-dbg.rb +2 -0
- data/example/riemann/producer.rb +4 -3
- data/example/riemann/v1/riemann.rb +2 -2
- data/example/riemann/v2/event-template.rb +71 -0
- data/example/riemann/v2/expirer.rb +1 -1
- data/example/riemann/v2/hash-store.rb +1 -0
- data/example/riemann/v2/ordered-event-store.rb +4 -1
- data/example/riemann/v2/riemann.rb +15 -8
- data/example/riemann/v2/sqlite-event-store.rb +117 -72
- data/example/sqlite/poi-store.rb +1 -1
- data/example/sqlite/poi-template.rb +2 -2
- data/example/sqlite/poi-v2.rb +2 -2
- data/example/subspaces/ramp.rb +9 -2
- data/example/tcp.rb +5 -0
- data/example/tiny-tcp-client.rb +15 -0
- data/example/tiny-tcp-service.rb +32 -0
- data/lib/tupelo/app.rb +4 -4
- data/lib/tupelo/app/builder.rb +2 -2
- data/lib/tupelo/app/irb-shell.rb +3 -3
- data/lib/tupelo/archiver.rb +0 -2
- data/lib/tupelo/archiver/tuplestore.rb +1 -1
- data/lib/tupelo/archiver/worker.rb +6 -6
- data/lib/tupelo/client.rb +2 -2
- data/lib/tupelo/client/reader.rb +3 -3
- data/lib/tupelo/client/scheduler.rb +1 -1
- data/lib/tupelo/client/subspace.rb +2 -2
- data/lib/tupelo/client/transaction.rb +28 -28
- data/lib/tupelo/client/tuplestore.rb +2 -2
- data/lib/tupelo/client/worker.rb +11 -10
- data/lib/tupelo/util/bin-circle.rb +8 -8
- data/lib/tupelo/util/boolean.rb +1 -1
- data/lib/tupelo/version.rb +1 -1
- data/test/lib/mock-client.rb +10 -10
- data/test/system/test-archiver.rb +2 -2
- data/test/unit/test-ops.rb +21 -21
- metadata +10 -20
- data/example/bingo/bingo-v2.rb +0 -20
- data/example/broker-queue.rb +0 -35
- data/example/child-of-child.rb +0 -34
- data/example/dataflow.rb +0 -21
- data/example/pregel/dist-opt.rb +0 -15
- data/example/riemann/v2/event-sql.rb +0 -56
- data/example/sqlite/tmp/poi-sqlite.rb +0 -35
- data/example/subspaces/addr-book-v1.rb +0 -104
- data/example/subspaces/addr-book-v2.rb +0 -16
- data/example/subspaces/sorted-set-space-OLD.rb +0 -130
- data/lib/tupelo/tuplets/persistent-archiver.rb +0 -86
- data/lib/tupelo/tuplets/persistent-archiver/tuplespace.rb +0 -91
- data/lib/tupelo/tuplets/persistent-archiver/worker.rb +0 -114
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 79341ae98464d4246f83672dc84801f0ec5c9979
|
4
|
+
data.tar.gz: 128e129fa77dfc86ddd3097a89d1563e9f20b8b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d88280189aed380cc55f30182fd58a3e66daa58094bc80ca19c444df4719706b0a0ef427910b6b67fe276c5d73eb677159b02c61fab1a6f99af5f10561aa28e8
|
7
|
+
data.tar.gz: 1bb0816c7e482a3b6bd15ff42d6542c5a8fad88ce22b51cb36098a0b954501292f8fd8fdce7e59dd955b5d6f705169e67dbe8f607fd12b95bcf6ffb7afd777db
|
data/README.md
CHANGED
@@ -1,12 +1,42 @@
|
|
1
1
|
Tupelo
|
2
2
|
==
|
3
3
|
|
4
|
-
Tupelo is a language-agnostic tuplespace for coordination of distributed programs. It is designed for distribution of both computation and storage, on disk and in memory, with pluggable storage adapters. Its programming model is small and semantically transparent: there are tuples (built from arrays, hashes, and scalars), a few operations on tuples (read, write, take), and transactions composed of these operations. This data-centric model, unlike RPC and most forms of messaging, decouples application endpoints from each other, not only in space and time, but also in referential structure: processes refer to data rather than to other processes.
|
4
|
+
Tupelo is a language-agnostic tuplespace for coordination of distributed programs. It is designed for distribution of both computation and storage, on disk and in memory, with pluggable storage adapters. Its programming model is small and semantically transparent: there are tuples (built from arrays, hashes, and scalars), a few operations on tuples (read, write, take), and transactions composed of these operations. This data-centric model, unlike RPC and most forms of messaging, decouples application endpoints from each other, not only in space and time, but also in referential structure: processes refer to data rather than to other processes or to channels.
|
5
5
|
|
6
6
|
Tupelo is inspired by Masatoshi Seki's Rinda in the Ruby standard library, which in turn is based on David Gelernter's Linda. The programming models of Tupelo and Rinda are similar, except for the lack of transactions in Rinda. However, the implementations of the two are nearly opposite in architectural approach.
|
7
7
|
|
8
8
|
This repository contains the reference implementation in Ruby, with documentation, tests, benchmarks, and examples. Implementations in other languages must communicate with this one.
|
9
9
|
|
10
|
+
News
|
11
|
+
====
|
12
|
+
|
13
|
+
* 2014-June-26: Slides and videos from my talk on Calvin, which has some ideas in common with tupelo (plus a lot more):
|
14
|
+
* http://www.meetup.com/papers-we-love-too/events/171291972
|
15
|
+
* https://speakerdeck.com/paperswelove/pwlsf-number-4-equals-joel-vanderwerf-on-calvin
|
16
|
+
* https://plus.google.com/events/cprsf9edesa0ghm3b8c416ppa7o
|
17
|
+
|
18
|
+
* 2014-April-7: Want to use these fancy stores inside a client that you run from the command line? See [here](example/sqlite/README.md) and [here](example/riemann/README.md).
|
19
|
+
|
20
|
+
* 2014-April-1: [Riemann example](example/riemann) uses more stores in various roles:
|
21
|
+
|
22
|
+
* sqlite store for the consumer replicas, with indexes for
|
23
|
+
* service/host/time
|
24
|
+
* tags
|
25
|
+
* custom data
|
26
|
+
* rbtree store for the expirer
|
27
|
+
* hash store for the alerter
|
28
|
+
|
29
|
+
* 2014-March-19: release 0.21
|
30
|
+
|
31
|
+
* correct use of `store` and `space` terms everywhere
|
32
|
+
* sqlite example improvements
|
33
|
+
* API additions
|
34
|
+
* tuplestores can define #find_all_matches_for
|
35
|
+
* for efficient search in #read_all
|
36
|
+
* msgpack and json options to deserialize keys as symbols
|
37
|
+
* invoke via Client option and tup switch
|
38
|
+
|
39
|
+
See also https://github.com/vjoel/tupelo/releases.
|
10
40
|
|
11
41
|
Documentation
|
12
42
|
============
|
@@ -22,6 +52,7 @@ In Depth
|
|
22
52
|
* [Transactions](doc/transactions.md)
|
23
53
|
* [Replication](doc/replication.md)
|
24
54
|
* [Subspaces](doc/subspace.md)
|
55
|
+
* [Tuple stores](doc/tuplestores.md)
|
25
56
|
* [Causality](doc/causality.md)
|
26
57
|
* [Concurrency](doc/concurrency.md)
|
27
58
|
|
@@ -68,46 +99,7 @@ Tupelo is a flexible base layer for various distributed programming patterns and
|
|
68
99
|
|
69
100
|
Tupelo can be used to impose a unified transactional structure and distributed access model on a mixture of programs and languages (polyglot computation) and a mixture of data stores (polyglot persistence), with consistent replication.
|
70
101
|
|
71
|
-
|
72
|
-
Example
|
73
|
-
-------
|
74
|
-
|
75
|
-
This program counts prime numbers in an interval by distributing the problem to a set of hosts:
|
76
|
-
|
77
|
-
require 'tupelo/app/remote'
|
78
|
-
|
79
|
-
hosts = %w{itchy scratchy lisa bart} # ssh hosts with key-based auth
|
80
|
-
|
81
|
-
Tupelo.tcp_application do
|
82
|
-
hosts.each do |host|
|
83
|
-
remote host: host, passive: true, eval: %{
|
84
|
-
require 'prime' # ruby stdlib for prime factorization
|
85
|
-
loop do
|
86
|
-
_, input = take(["input", Integer])
|
87
|
-
write ["output", input, input.prime_division]
|
88
|
-
end
|
89
|
-
}
|
90
|
-
end
|
91
|
-
|
92
|
-
local do
|
93
|
-
inputs = 1_000_000_000_000 .. 1_000_000_000_200
|
94
|
-
|
95
|
-
inputs.each do |input|
|
96
|
-
write ["input", input]
|
97
|
-
end
|
98
|
-
|
99
|
-
count = 0
|
100
|
-
inputs.size.times do |i|
|
101
|
-
_, input, factors = take ["output", Integer, nil]
|
102
|
-
count += 1 if factors.size == 1 and factors[0][1] == 1
|
103
|
-
print "\rChecked #{i}"
|
104
|
-
end
|
105
|
-
|
106
|
-
puts "\nThere are #{count} primes in #{inputs}"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
Ssh is used to set up the remote processes. Additionally, with the `--tunnel` command line argument, all tuple communication is tunneled over ssh. More examples like this are in [example/map-reduce](example/map-reduce).
|
102
|
+
See the [example section](#examples) below and the [examples](example) directory.
|
111
103
|
|
112
104
|
|
113
105
|
Limitations
|
@@ -180,14 +172,148 @@ Additional benefits (not related to message sequencing) include:
|
|
180
172
|
Process control and tunneling are available independently of tupelo using the easy-serve gem.
|
181
173
|
|
182
174
|
|
175
|
+
Examples
|
176
|
+
========
|
177
|
+
|
178
|
+
Distributed processing
|
179
|
+
----------------------
|
180
|
+
|
181
|
+
This program counts prime numbers in an interval by distributing the problem to a set of hosts:
|
182
|
+
|
183
|
+
require 'tupelo/app/remote'
|
184
|
+
|
185
|
+
hosts = %w{itchy scratchy lisa bart} # ssh hosts with key-based auth
|
186
|
+
|
187
|
+
Tupelo.tcp_application do
|
188
|
+
hosts.each do |host|
|
189
|
+
remote host: host, passive: true, eval: %{
|
190
|
+
require 'prime' # ruby stdlib for prime factorization
|
191
|
+
loop do
|
192
|
+
_, input = take(["input", Integer])
|
193
|
+
write ["output", input, input.prime_division]
|
194
|
+
end
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
local do
|
199
|
+
inputs = 1_000_000_000_000 .. 1_000_000_000_200
|
200
|
+
|
201
|
+
inputs.each do |input|
|
202
|
+
write ["input", input]
|
203
|
+
end
|
204
|
+
|
205
|
+
count = 0
|
206
|
+
inputs.size.times do |i|
|
207
|
+
_, input, factors = take ["output", Integer, nil]
|
208
|
+
count += 1 if factors.size == 1 and factors[0][1] == 1
|
209
|
+
print "\rChecked #{i}"
|
210
|
+
end
|
211
|
+
|
212
|
+
puts "\nThere are #{count} primes in #{inputs}"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
Ssh is used to set up the remote processes. Additionally, with the `--tunnel` command line argument, all tuple communication is tunneled over ssh. More examples like this are in [example/map-reduce](example/map-reduce), [example/pregel](example/pregel), and [example/parallel.rb](example/parallel.rb).
|
217
|
+
|
218
|
+
Distributed storage
|
219
|
+
-------------------
|
220
|
+
|
221
|
+
Here's an example that creates an in-memory sqlite in one client with a table for Points of Interest (POI). A second client populates that table by writing POI tuples and then executes a SQL delete by writing a tuple with the deletion parameters.
|
222
|
+
|
223
|
+
require 'tupelo/app'
|
224
|
+
require_relative 'poi-client' # run this in example/sqlite
|
225
|
+
|
226
|
+
Tupelo.application do
|
227
|
+
local do
|
228
|
+
POISPACE = PoiStore.define_poispace(self)
|
229
|
+
define_subspace("cmd", {id: nil, cmd: String, arg: nil})
|
230
|
+
define_subspace("rsp", {id: nil, result: nil})
|
231
|
+
end
|
232
|
+
|
233
|
+
child PoiClient, poispace: POISPACE, subscribe: "cmd", passive: true do
|
234
|
+
loop do
|
235
|
+
req = take subspace("cmd")
|
236
|
+
case req[:cmd]
|
237
|
+
when "delete box"
|
238
|
+
lat = req[:arg][:lat]; lng = req[:arg][:lng]
|
239
|
+
template = PoiTemplate.new(poi_template: subspace("poi"),
|
240
|
+
lat: lat[0]..lat[1], lng: lng[0]..lng[1])
|
241
|
+
deleted = []
|
242
|
+
transaction do
|
243
|
+
while poi = take_nowait(template)
|
244
|
+
deleted << poi
|
245
|
+
end
|
246
|
+
end
|
247
|
+
write id: req[:id], result: deleted
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
child subscribe: "rsp" do
|
253
|
+
write lat: 1.2, lng: 3.4, desc: "foo"
|
254
|
+
write lat: 5.6, lng: 7.8, desc: "bar"
|
255
|
+
write lat: 1.3, lng: 3.5, desc: "baz"
|
256
|
+
|
257
|
+
write id: 1, cmd: "delete box", arg: {lat: [1.0, 1.4], lng: [3.0, 4.0]}
|
258
|
+
rsp = take id: 1, result: nil
|
259
|
+
log "deleted: #{rsp["result"]}"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
The output should be something like this:
|
264
|
+
|
265
|
+
A: client 3: deleted: [{"lat"=>1.2, "lng"=>3.4, "desc"=>"foo"}, {"lat"=>1.3, "lng"=>3.5, "desc"=>"baz"}]
|
266
|
+
|
267
|
+
See [example/sqlite](example/sqlite) for the complete example. More advanced versions of this example have remote, replicated sqlites for redundancy and load distribution.
|
268
|
+
|
269
|
+
Web app coordination
|
270
|
+
--------------------
|
271
|
+
|
272
|
+
This example runs several sinatra web apps and uses tupelo to set up a chat network between their users.
|
273
|
+
|
274
|
+
require 'tupelo/app'
|
275
|
+
require 'sinatra/base'
|
276
|
+
|
277
|
+
Tupelo.application do
|
278
|
+
[9001, 9002, 9003].each do |port|
|
279
|
+
child do |client|
|
280
|
+
Class.new(Sinatra::Base).class_eval do
|
281
|
+
post '/send' do
|
282
|
+
client.write ["message", params["dest"], params["text"]]
|
283
|
+
end
|
284
|
+
|
285
|
+
get '/recv' do
|
286
|
+
"%s for %s: %s\n" %
|
287
|
+
(client.take ["message", params["dest"], String])
|
288
|
+
end
|
289
|
+
|
290
|
+
set :port, port
|
291
|
+
run!
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
You can use curl to chat:
|
298
|
+
|
299
|
+
$ curl 'localhost:9001/send?text=hello&dest=fred' -d ''
|
300
|
+
|
301
|
+
and
|
302
|
+
|
303
|
+
$ curl 'localhost:9003/recv?dest=fred'
|
304
|
+
message for fred: hello
|
305
|
+
|
306
|
+
Note that the `recv` call waits for a message if none is available.
|
307
|
+
|
308
|
+
See also [example/multi-tier](example/multi-tier) and the chat server in [example/chat](example/chat).
|
309
|
+
|
310
|
+
|
183
311
|
Development
|
184
312
|
===========
|
185
313
|
|
186
314
|
Patches and bug reports are most welcome.
|
187
315
|
|
188
|
-
This project is hosted at
|
189
|
-
|
190
|
-
https://github.com/vjoel/tupelo
|
316
|
+
This project is hosted at https://github.com/vjoel/tupelo
|
191
317
|
|
192
318
|
Dependencies
|
193
319
|
------------
|
@@ -219,7 +345,7 @@ Optional gems for some of the examples:
|
|
219
345
|
Contact
|
220
346
|
=======
|
221
347
|
|
222
|
-
Joel VanderWerf, vjoel@users.sourceforge.net, @JoelVanderWerf.
|
348
|
+
Joel VanderWerf, vjoel@users.sourceforge.net, [@JoelVanderWerf](https://twitter.com/JoelVanderWerf).
|
223
349
|
|
224
350
|
License and Copyright
|
225
351
|
========
|
data/bin/tup
CHANGED
@@ -75,6 +75,17 @@ if ARGV.delete("-h") or ARGV.delete("--help")
|
|
75
75
|
subscribe to specified subspaces; use "" for none
|
76
76
|
by default, tup client subscribes to everything
|
77
77
|
|
78
|
+
--store class
|
79
|
+
--store class,subspace
|
80
|
+
Use a store of the specified class for the client's
|
81
|
+
local tuplestore. If subspace is given, pass the subspace's
|
82
|
+
spec as the first argument to <class>.new.
|
83
|
+
(The -I and -r options are useful.)
|
84
|
+
|
85
|
+
-I path append dir to $LOAD_PATH (the space char is necessary)
|
86
|
+
|
87
|
+
-r file require file (the space char is necessary)
|
88
|
+
|
78
89
|
END
|
79
90
|
exit
|
80
91
|
end
|
@@ -93,6 +104,24 @@ if i=argv.index("--subscribe") # default is to subscribe to all
|
|
93
104
|
subscribed_tags = argv.delete_at(i).split(",")
|
94
105
|
end
|
95
106
|
|
107
|
+
__store = nil
|
108
|
+
if i=argv.index("--store")
|
109
|
+
argv.delete("--store")
|
110
|
+
__store = argv.delete_at(i).split(",")
|
111
|
+
end
|
112
|
+
|
113
|
+
_r_files = []
|
114
|
+
if i=argv.index("-r")
|
115
|
+
argv.delete("-r")
|
116
|
+
_r_files << argv.delete_at(i)
|
117
|
+
end
|
118
|
+
|
119
|
+
_I_dirs = []
|
120
|
+
if i=argv.index("-I")
|
121
|
+
argv.delete("-I")
|
122
|
+
_I_dirs << argv.delete_at(i)
|
123
|
+
end
|
124
|
+
|
96
125
|
services_file = argv.shift
|
97
126
|
proto = (argv.shift || :unix).to_sym
|
98
127
|
addr = {proto: proto}
|
@@ -113,6 +142,24 @@ Tupelo.application(
|
|
113
142
|
cseqd_addr: addr, # using same addr causes autoincrement of port/filename
|
114
143
|
arcd_addr: addr) do
|
115
144
|
|
145
|
+
$LOAD_PATH.unshift(*_I_dirs)
|
146
|
+
_r_files.each do |f|
|
147
|
+
require f
|
148
|
+
end
|
149
|
+
|
150
|
+
if __store
|
151
|
+
store_args = [Object.const_get(__store[0])]
|
152
|
+
store_subspace = __store[1]
|
153
|
+
if store_subspace
|
154
|
+
local subscribe: nil do
|
155
|
+
log.debug "getting spec for subspace #{store_subspace.inspect}"
|
156
|
+
store_args << subspace(store_subspace, wait: true).spec
|
157
|
+
end
|
158
|
+
end
|
159
|
+
else
|
160
|
+
store_args = nil
|
161
|
+
end
|
162
|
+
|
116
163
|
class TupClient < Tupelo::Client
|
117
164
|
alias w write_wait
|
118
165
|
alias pl pulse_wait
|
@@ -168,6 +215,10 @@ Tupelo.application(
|
|
168
215
|
client_opts[:subscribe] = subscribed_tags
|
169
216
|
end
|
170
217
|
|
218
|
+
if store_args
|
219
|
+
client_opts[:tuplestore] = store_args
|
220
|
+
end
|
221
|
+
|
171
222
|
local TupClient, **client_opts do
|
172
223
|
log.info {"cpu time: %.2fs" % Process.times.inject {|s,x|s+x}}
|
173
224
|
log.info {"starting shell."}
|
data/example/counters/merge.rb
CHANGED
@@ -17,18 +17,38 @@ Tupelo.application do
|
|
17
17
|
# note: no need to init counter
|
18
18
|
pids.each {|pid| Process.waitpid pid}
|
19
19
|
|
20
|
-
#
|
21
|
-
#
|
22
|
-
|
20
|
+
# The next block of code reads the "current value" of the counter.
|
21
|
+
#
|
22
|
+
# "Current" means globally correct as of the last global
|
23
|
+
# tick received at the local client.
|
24
|
+
#
|
25
|
+
# The "value" of the counter is defined to be the sum of all n
|
26
|
+
# occurring in some tuple matching {count: nil}.
|
27
|
+
#
|
28
|
+
# The difficulty is that we cannot read the counter without
|
29
|
+
# first merging all the matching count tuples. In a sense, this
|
30
|
+
# is like CRDTs in an eventually consistent DB.
|
31
|
+
#
|
32
|
+
# See also example/dedup.rb.
|
33
|
+
log "merging..." while transaction do
|
23
34
|
c1 = take_nowait count: nil
|
24
35
|
c2 = take_nowait count: nil
|
25
36
|
if c1 and c2
|
26
37
|
write count: c1["count"] + c2["count"]
|
27
38
|
true
|
39
|
+
# We had to merge, so try again.
|
28
40
|
elsif c1
|
29
41
|
log c1
|
42
|
+
# At the tick of the second take_nowait, exactly one count
|
43
|
+
# tuple exists, so we can safely report that value as the
|
44
|
+
# global count, with the understanding that the count
|
45
|
+
# may have changed at a later tick (even by the time the
|
46
|
+
# transaction commits).
|
47
|
+
false
|
30
48
|
else
|
31
49
|
log count: 0
|
50
|
+
# At the time of this txn, no count tuple exists.
|
51
|
+
false
|
32
52
|
end
|
33
53
|
end
|
34
54
|
end
|
@@ -3,6 +3,10 @@
|
|
3
3
|
# http.rb example and shows how to use tupelo to coordinate multiple sinatra
|
4
4
|
# instances.
|
5
5
|
#
|
6
|
+
# This could easily be adapted to run the servers on remote hosts (using
|
7
|
+
# #remote instead of #child) and to restrict the network traffic to just
|
8
|
+
# the subspaces each client needs (using subscribe).
|
9
|
+
#
|
6
10
|
# Depends on the sinatra and http gems.
|
7
11
|
|
8
12
|
PORTS = [9001, 9002, 9003]
|
@@ -29,6 +33,7 @@ fork do
|
|
29
33
|
end
|
30
34
|
|
31
35
|
get '/recv' do
|
36
|
+
## actually this should nt be a 'get' but a 'post' or 'delete'
|
32
37
|
dest = params["dest"]
|
33
38
|
_, _, text = client.take ["message", dest, String]
|
34
39
|
text
|
@@ -25,10 +25,7 @@ class Tupelo::Client
|
|
25
25
|
# event is considered valid for. Expired states
|
26
26
|
# may be removed from the index.
|
27
27
|
|
28
|
-
custom:
|
29
|
-
# (not quite same as riemann's custom event attrs,
|
30
|
-
# which are just arbitrary key-value pairs;
|
31
|
-
# tupelo does not permit wildcards in keys)
|
28
|
+
custom: Hash # Arbitrary key-value pairs (not just strings).
|
32
29
|
})
|
33
30
|
end
|
34
31
|
|
data/example/riemann/producer.rb
CHANGED
@@ -9,7 +9,7 @@ class Tupelo::Client
|
|
9
9
|
tags: [],
|
10
10
|
metric: 0,
|
11
11
|
ttl: 0,
|
12
|
-
custom:
|
12
|
+
custom: {}
|
13
13
|
}.freeze
|
14
14
|
end
|
15
15
|
|
@@ -36,11 +36,12 @@ class Tupelo::Client
|
|
36
36
|
30.times do |ei|
|
37
37
|
e_cpu = base_event.merge(
|
38
38
|
service: "service #{i}",
|
39
|
-
state: ei==
|
39
|
+
state: (ei%10==0) ? "critical" : "ok",
|
40
40
|
time: Time.now.to_f,
|
41
41
|
ttl: 0.2,
|
42
42
|
tags: ["cpu", "cumulative"],
|
43
|
-
metric: Process.times.utime + Process.times.stime
|
43
|
+
metric: Process.times.utime + Process.times.stime,
|
44
|
+
custom: {foo: ["bar", 42*i + ei]}
|
44
45
|
)
|
45
46
|
write_event e_cpu
|
46
47
|
sleep 0.2
|