tupelo 0.18 → 0.19

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: 60516b564c0329a1544a913f4ec1680bd8308de8
4
- data.tar.gz: 6aadbcc4c96e57d762d072e266ff921972f58578
3
+ metadata.gz: a8a67711763af58aa46979622519450ba198e603
4
+ data.tar.gz: fc399bd00b0b69ec5938a0d3ffae68155c186ae7
5
5
  SHA512:
6
- metadata.gz: 75b24e537a78c831be55b389457a41fd527bf3fc88cb7e4dd358d03ecc492d439157ce28a3191ae86946c2df12a5ee5f74b5b388aebd8a9da57e0e1cc0af8386
7
- data.tar.gz: 53a0848f5b3c30e091fc0bd0d58fced4703b77561099866b032e1d9f7c312a85da01d2a4066c2979f2632caa31a6c97466b21cf4319acca99308f4beddaa25d5
6
+ metadata.gz: 0f3570788a47744267fc361e25bd50b56bf327177764fb428317cb51ed1870c7e86acc8df53ef28d60278f9c902057b2a5275f86eba29189ea80f28259ddfc9f
7
+ data.tar.gz: 85a3be5a56f49d9518c2ed1409d992654e3f36f80847b441d7f4d84bf29afba541c11164c4052f97e208916e9a2a4cbf57aeef04736207160d4c6b4cd2d69e25
data/example/lease.rb CHANGED
@@ -52,9 +52,8 @@ Tupelo.application do
52
52
  # one worker lives. This demonstrates how to recover from worker failure
53
53
  # and prevent "lost tuples".
54
54
  child passive: true do
55
- require 'tupelo/client/atdo'
56
-
57
55
  scheduler = make_scheduler
56
+
58
57
  alive_until = Hash.new(0)
59
58
 
60
59
  loop do
data/example/observer.rb CHANGED
@@ -32,6 +32,9 @@ Tupelo.application do
32
32
  counter = t.read count: Numeric
33
33
  log "entering new state: #{counter}"
34
34
  t.wait
35
+ # If you prefer to check for failure periodically, rather than
36
+ # blocking with #wait, then simply call t.failed? You can call
37
+ # t.missing to see which tuples caused the failure.
35
38
  rescue Tupelo::Client::TransactionFailure => ex
36
39
  log "leaving old state: #{counter}"
37
40
  end
@@ -0,0 +1,60 @@
1
+ class Tupelo::Client
2
+ def define_event_subspace
3
+ define_subspace("event", {
4
+ # field type description
5
+ # (from http://riemann.io/concepts.html)
6
+
7
+ host: String, # A hostname, e.g. "api1", "foo.com"
8
+
9
+ service: String, # e.g. "API port 8000 reqs/sec"
10
+
11
+ state: String, # Any string less than 255 bytes, e.g. "ok",
12
+ # "warning", "critical"
13
+
14
+ time: Numeric, # The time of the event, in unix epoch seconds
15
+
16
+ description: String, # Freeform text
17
+
18
+ tags: Array, # Freeform list of strings,
19
+ # e.g. ["rate", "fooproduct", "transient"]
20
+
21
+ metric: Numeric, # A number associated with this event,
22
+ # e.g. the number of reqs/sec.
23
+
24
+ ttl: Numeric, # A floating-point time, in seconds, that this
25
+ # event is considered valid for. Expired states
26
+ # may be removed from the index.
27
+
28
+ custom: nil # Any data
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)
32
+ })
33
+ end
34
+
35
+ # This could be a subspace of the event subspace, but for now, we can just
36
+ # use it as a template to select critical events out of the event subspace.
37
+ CRITICAL_EVENT = {
38
+ host: nil,
39
+ service: nil,
40
+ state: /\A(?:critical|fatal)\z/i,
41
+ time: nil,
42
+ description: nil,
43
+ tags: nil,
44
+ metric: nil,
45
+ ttl: nil,
46
+ custom: nil
47
+ }.freeze
48
+
49
+ EXPIRED_EVENT = {
50
+ host: nil,
51
+ service: nil,
52
+ state: /\Aexpired\z/i,
53
+ time: nil,
54
+ description: nil,
55
+ tags: nil,
56
+ metric: nil,
57
+ ttl: nil,
58
+ custom: nil
59
+ }.freeze
60
+ end
@@ -0,0 +1,25 @@
1
+ class Tupelo::Client
2
+ # Expire old events.
3
+ def run_expirer_v1
4
+ scheduler = make_scheduler
5
+
6
+ read subspace("event") do |event|
7
+ next if event["state"] == "expired"
8
+
9
+ event_exp = event["time"] + event["ttl"]
10
+ scheduler.at event_exp do
11
+ transaction do
12
+ take event
13
+ pulse event.merge("state" => "expired")
14
+ # Not sure if this is riemann semantics. Using #pulse rather
15
+ # that #write means that the expired event exists in the
16
+ # space only while the transaction is executing, but that is
17
+ # enough to trigger any client that is waiting on a template
18
+ # that matches the event. Use the --debug-expiration switch
19
+ # to see this happening (and use -v to make log messages
20
+ # verbose, showing timestamps).
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,32 @@
1
+ class Tupelo::Client
2
+ # Generate some events.
3
+ def run_producer i
4
+ event = {
5
+ host: `hostname`.chomp,
6
+ service: "service #{i}",
7
+ state: "",
8
+ time: 0,
9
+ description: "",
10
+ tags: [],
11
+ metric: 0,
12
+ ttl: 0,
13
+ custom: nil
14
+ }.freeze
15
+
16
+ e_ok = event.merge(
17
+ state: "ok",
18
+ time: Time.now.to_f,
19
+ ttl: 0.2
20
+ )
21
+
22
+ if e_ok[:ttl] == 0.0
23
+ pulse e_ok # no need to bother with expiration
24
+ else
25
+ write e_ok
26
+ end
27
+
28
+ log "created event #{e_ok}"
29
+
30
+ sleep 0.5 # Let it expire
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ # A toy implementation of Riemann (http://riemann.io).
2
+ #
3
+ # Version 1 uses the default tuplespace for all subspaces, which is inefficient
4
+ # for searching.
5
+
6
+ require 'tupelo/app'
7
+ require_relative 'event-subspace'
8
+ require_relative 'producer'
9
+ require_relative 'expirer-v1'
10
+
11
+ N_PRODUCERS = 3
12
+ N_CONSUMERS = 2
13
+
14
+ Tupelo.application do
15
+
16
+ local do
17
+ use_subspaces!
18
+ define_event_subspace
19
+ end
20
+
21
+ N_PRODUCERS.times do |i|
22
+ child subscribe: [] do # N.b., no subscriptions
23
+ log.progname = "producer #{i}"
24
+ run_producer i
25
+ end
26
+ end
27
+
28
+ N_CONSUMERS.times do |i|
29
+ # stores events
30
+ child subscribe: "event", passive: true do
31
+ log.progname = "consumer #{i}"
32
+ read subspace("event") do |event|
33
+ log event ### need filtering, actions, etc.
34
+ end
35
+ end
36
+ end
37
+
38
+ # critical event alerter
39
+ child subscribe: "event", passive: true do
40
+ log.progname = "alerter"
41
+ read Tupelo::Client::CRITICAL_EVENT do |event|
42
+ log.error event
43
+ end
44
+ end
45
+
46
+ if argv.include?("--debug-expiration")
47
+ # expired event debugger
48
+ child subscribe: "event", passive: true do
49
+ log.progname = "expiration debugger"
50
+ read Tupelo::Client::EXPIRED_EVENT do |event|
51
+ log event
52
+ end
53
+ end
54
+ end
55
+
56
+ # expirer: stores current events and looks for events that can be expired.
57
+ child subscribe: "event", passive: true do
58
+ log.progname = "expirer"
59
+ run_expirer_v1
60
+ end
61
+ end
@@ -0,0 +1,72 @@
1
+ # A toy implementation of Riemann (http://riemann.io).
2
+ #
3
+ # Version 2 stores the event subspace using different data
4
+ # structures in different clients, depending on needs:
5
+ #
6
+ # * generic consumers need to index by host and service
7
+ #
8
+ # * the expiration manager need to sort by expiration time
9
+ #
10
+ # * the critical event alerter doesn't need to sort at all.
11
+
12
+ abort "work in progress"
13
+
14
+ require 'tupelo/app'
15
+ require_relative 'event-subspace'
16
+ require_relative 'producer'
17
+ require_relative 'expirer-v2'
18
+
19
+ N_PRODUCERS = 3
20
+ N_CONSUMERS = 2
21
+
22
+ Tupelo.application do
23
+
24
+ local do
25
+ use_subspaces!
26
+ define_event_subspace
27
+ end
28
+
29
+ N_PRODUCERS.times do |i|
30
+ child subscribe: [] do # N.b., no subscriptions
31
+ log.progname = "producer #{i}"
32
+ run_producer i ### V2: manual tagging
33
+ end
34
+ end
35
+
36
+ N_CONSUMERS.times do |i|
37
+ # stores events indexed by host, service
38
+ child subscribe: "event", passive: true do ### tuplespace: sqlite
39
+ log.progname = "consumer #{i}"
40
+ read subspace("event") do |event|
41
+ log event ### need filtering, actions, etc.
42
+ end
43
+ end
44
+ end
45
+
46
+ # critical event alerter
47
+ child subscribe: "event", passive: true do ### tuplespace: bag?
48
+ log.progname = "alerter"
49
+ read Tupelo::Client::CRITICAL_EVENT do |event|
50
+ log.error event
51
+ end
52
+ end
53
+
54
+ if argv.include?("--debug-expiration")
55
+ # expired event debugger
56
+ child subscribe: "event", passive: true do
57
+ log.progname = "expiration debugger"
58
+ read Tupelo::Client::EXPIRED_EVENT do |event|
59
+ log event
60
+ end
61
+ end
62
+ end
63
+
64
+ # expirer: stores current events and looks for events that can be expired.
65
+ child subscribe: "event", passive: true do
66
+ log.progname = "expirer"
67
+ run_expirer_v2
68
+ ### use rbtree
69
+ end
70
+
71
+ ### Add sinatra app.
72
+ end
@@ -0,0 +1,56 @@
1
+ require 'atdo'
2
+
3
+ module Tupelo
4
+ class Client
5
+ class Scheduler < AtDo
6
+ DEFAULT_STORAGE =
7
+ begin
8
+ require 'rbtree'
9
+ MultiRBTree
10
+ rescue LoadError
11
+ Array
12
+ end
13
+
14
+ # Instead of calling this method, call Client#make_scheduler.
15
+ def initialize client, **opts
16
+ @client = client
17
+ super **opts
18
+ end
19
+
20
+ # Accepts numeric +time+ or Time instance. Logs errors that occur
21
+ # in +action+. Otherwise, same as AtDo#at from the atdo gem.
22
+ def at time, &action
23
+ time = Time.at(time) if time.kind_of? Numeric
24
+ super time do
25
+ begin
26
+ action.call
27
+ rescue => ex
28
+ @client.log.error "error in action scheduled for #{time}:" +
29
+ " #{ex.class}: #{ex}\n #{ex.backtrace.join("\n ")}"
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ # Returns a scheduler, which has a method that you can call to schedule
36
+ # an action at a time:
37
+ #
38
+ # s = make_scheduler # or client.make_scheduler
39
+ # s.at t do ... end
40
+ #
41
+ # where t can be either a Time or Numeric seconds.
42
+ #
43
+ # The scheduler runs in its own thread. It uses a red-black tree to store
44
+ # the actions, if the rbtree gem is installed and you do not specify
45
+ # 'storage: Array'. Otherwise it uses a sorted Array, which is fine for
46
+ # small schedules.
47
+ #
48
+ # Scheduler is used internally by Worker to manage transaction timeouts,
49
+ # but client code may create its own scheduler -- see example/lease.rb.
50
+ #
51
+ def make_scheduler **opts
52
+ opts[:storage] ||= Scheduler::DEFAULT_STORAGE
53
+ Scheduler.new self, **opts
54
+ end
55
+ end
56
+ end
@@ -1,8 +1,8 @@
1
1
  require 'thread'
2
2
  require 'tupelo/client/reader'
3
3
  require 'tupelo/client/transaction'
4
+ require 'tupelo/client/scheduler'
4
5
  require 'object-template'
5
- require 'atdo'
6
6
 
7
7
  class Tupelo::Client
8
8
  class Worker
@@ -70,6 +70,7 @@ class Tupelo::Client
70
70
  @seq = nil
71
71
  @arc = nil
72
72
  @log = client.log
73
+ @scheduler = nil
73
74
 
74
75
  @client_id = nil
75
76
  @global_tick = nil
@@ -142,19 +143,19 @@ class Tupelo::Client
142
143
  cmd_queue << :stop
143
144
  worker_thread.join if worker_thread ## join(limit)?
144
145
  msg_reader_thread.kill if msg_reader_thread
145
- @atdo.stop if @atdo
146
+ @scheduler.stop if @scheduler
146
147
  end
147
148
 
148
149
  # stop without any remote handshaking
149
150
  def stop!
150
151
  @msg_reader_thread.kill if msg_reader_thread
151
152
  @worker_thread.kill if worker_thread
152
- @atdo.stop if @atdo
153
+ @scheduler.stop if @scheduler
153
154
  end
154
155
 
155
156
  def at time, &action
156
- @atdo ||= AtDo.new
157
- @atdo.at time do
157
+ @scheduler ||= client.make_scheduler
158
+ @scheduler.at time do
158
159
  cmd_queue << action
159
160
  end
160
161
  end
@@ -1,3 +1,3 @@
1
1
  module Tupelo
2
- VERSION = "0.18"
2
+ VERSION = "0.19"
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.18'
4
+ version: '0.19'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel VanderWerf
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-25 00:00:00.000000000 Z
11
+ date: 2014-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: atdo
@@ -145,6 +145,11 @@ files:
145
145
  - example/pulse.rb
146
146
  - example/read-in-trans.rb
147
147
  - example/remote.rb
148
+ - example/riemann/event-subspace.rb
149
+ - example/riemann/expirer-v1.rb
150
+ - example/riemann/producer.rb
151
+ - example/riemann/riemann-v1.rb
152
+ - example/riemann/riemann-v2.rb
148
153
  - example/small-simplified.rb
149
154
  - example/small.rb
150
155
  - example/socket-broker.rb
@@ -153,7 +158,6 @@ files:
153
158
  - example/subspaces/addr-book.rb
154
159
  - example/subspaces/pubsub.rb
155
160
  - example/subspaces/ramp.rb
156
- - example/subspaces/riemann.rb
157
161
  - example/subspaces/shop/shop-v1.rb
158
162
  - example/subspaces/shop/shop-v2.rb
159
163
  - example/subspaces/simple.rb
@@ -186,9 +190,9 @@ files:
186
190
  - lib/tupelo/archiver/tuplespace.rb
187
191
  - lib/tupelo/archiver/worker.rb
188
192
  - lib/tupelo/client.rb
189
- - lib/tupelo/client/atdo.rb
190
193
  - lib/tupelo/client/common.rb
191
194
  - lib/tupelo/client/reader.rb
195
+ - lib/tupelo/client/scheduler.rb
192
196
  - lib/tupelo/client/subspace.rb
193
197
  - lib/tupelo/client/transaction.rb
194
198
  - lib/tupelo/client/tuplespace.rb
@@ -1,103 +0,0 @@
1
- # A toy implementation of Riemann (http://riemann.io)
2
- #
3
- # Also, this is an example of storing a subspace using different data
4
- # structures in different clients, depending on needs: some clients (generic
5
- # consumers) need to index by host and service, and others (expiration manager)
6
- # need to sort by expiration time.
7
-
8
- require 'tupelo/app'
9
-
10
- N_PRODUCERS = 3
11
- N_CONSUMERS = 2
12
-
13
- Tupelo.application do
14
-
15
- local do
16
- use_subspaces!
17
-
18
- define_subspace("event", {
19
- # field type description
20
- # (from http://riemann.io/concepts.html)
21
-
22
- host: String, # A hostname, e.g. "api1", "foo.com"
23
-
24
- service: String, # e.g. "API port 8000 reqs/sec"
25
-
26
- state: String, # Any string less than 255 bytes, e.g. "ok",
27
- # "warning", "critical"
28
-
29
- time: Numeric, # The time of the event, in unix epoch seconds
30
-
31
- description: String, # Freeform text
32
-
33
- tags: Array, # Freeform list of strings,
34
- # e.g. ["rate", "fooproduct", "transient"]
35
-
36
- metric: Numeric, # A number associated with this event,
37
- # e.g. the number of reqs/sec.
38
-
39
- ttl: Numeric # A floating-point time, in seconds, that this
40
- # event is considered valid for. Expired states
41
- # may be removed from the index.
42
- })
43
- end
44
-
45
- N_PRODUCERS.times do
46
- child subscribe: [] do # N.b., no subscriptions
47
- event = {
48
- host: `hostname`.chomp,
49
- service: "service #{client_id}", # placeholder
50
- state: "",
51
- time: 0,
52
- description: "",
53
- tags: [],
54
- metric: 0,
55
- ttl: 0
56
- }.freeze
57
-
58
- e_ok = event.merge(
59
- state: "ok",
60
- time: Time.now.to_f,
61
- ttl: 1.0
62
- )
63
-
64
- if e_ok[:ttl] == 0.0
65
- pulse e_ok # no need to bother with expiration
66
- else
67
- write e_ok
68
- end
69
- end
70
- end
71
-
72
- N_CONSUMERS.times do
73
- # stores events indexed by host, service
74
- child subscribe: "event", passive: true do ### tuplespace: sqlite
75
- read subspace("event") do |event|
76
- log event ### need filtering, actions, etc.
77
- end
78
- end
79
- end
80
-
81
- # This could be a subspace of the event subspace.
82
- critical_event = {
83
- host: nil,
84
- service: nil,
85
- state: /critical|fatal/i,
86
- time: nil,
87
- description: nil,
88
- tags: nil,
89
- metric: nil,
90
- ttl: nil
91
- }
92
-
93
- child subscribe: "event", passive: true do
94
- read critical_event do |event|
95
- log.error event
96
- end
97
- end
98
-
99
- # expirer: stores current events in expiration order
100
- child subscribe: "event", passive: true do
101
- ### use rbtree
102
- end
103
- end
@@ -1,31 +0,0 @@
1
- require 'tupelo/client'
2
- require 'atdo'
3
-
4
- module Tupelo
5
- class Client
6
- class AtDo < ::AtDo
7
- def initialize client, **opts
8
- @client = client
9
- super **opts
10
- end
11
-
12
- # Accepts numeric +time+. Logs errors in +action+. Otherwise, same
13
- # as ::AtDo.
14
- def at time, &action
15
- time = Time.at(time) if time.kind_of? Numeric
16
- super time do
17
- begin
18
- action.call
19
- rescue => ex
20
- @client.log.error "error in action scheduled for #{time}:" +
21
- " #{ex.class}: #{ex}\n #{ex.backtrace.join("\n ")}"
22
- end
23
- end
24
- end
25
- end
26
-
27
- def make_scheduler **opts
28
- AtDo.new self, **opts
29
- end
30
- end
31
- end