ustate-client 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ module UState
2
2
  grammar QueryString
3
3
  # (state =~ "foo" or state == "bar") and service == "foo"
4
4
  # Binding order proceeds from loosest to tightest.
5
-
5
+
6
6
  rule or
7
7
  first:and rest:(space 'or' space and)* {
8
8
  def query
@@ -21,15 +21,15 @@ module UState
21
21
  end
22
22
 
23
23
  rule and
24
- first:primary rest:(space 'and' space primary)* {
24
+ first:(not / primary) rest:(space 'and' space p:(not / primary))* {
25
25
  def query
26
- rest.elements.map { |x| x.primary }.inject(first.query) do |a, sub|
26
+ rest.elements.map { |x| x.p }.inject(first.query) do |a, sub|
27
27
  Query::And.new a, sub.query
28
28
  end
29
29
  end
30
30
 
31
31
  def sql
32
- rest.elements.map { |x| x.primary }.
32
+ rest.elements.map { |x| x.p }.
33
33
  inject(first.sql) do |a, sub|
34
34
  a & sub.sql
35
35
  end
@@ -37,6 +37,18 @@ module UState
37
37
  }
38
38
  end
39
39
 
40
+ rule not
41
+ 'not' space p:(not / primary) {
42
+ def query
43
+ Query::Not.new p.query
44
+ end
45
+
46
+ def sql
47
+ ~ p.sql
48
+ end
49
+ }
50
+ end
51
+
40
52
  rule primary
41
53
  '(' space? x:or space? ')' {
42
54
  def query
@@ -52,7 +64,7 @@ module UState
52
64
  end
53
65
 
54
66
  rule predicate
55
- equals / not_equals / approximately
67
+ true / false / less_equal / less / greater_equal / greater / equals / not_equals / approximately
56
68
  end
57
69
 
58
70
  rule approximately
@@ -66,7 +78,55 @@ module UState
66
78
  end
67
79
  }
68
80
  end
81
+
82
+ rule less_equal
83
+ field space? '<=' space? value {
84
+ def query
85
+ Query::LessEqual.new field.sql, value.sql
86
+ end
69
87
 
88
+ def sql
89
+ Sequel::SQL::BooleanExpression.new(:<=, field.sql, value.sql)
90
+ end
91
+ }
92
+ end
93
+
94
+ rule less
95
+ field space? '<' space? value {
96
+ def query
97
+ Query::Less.new field.sql, value.sql
98
+ end
99
+
100
+ def sql
101
+ Sequel::SQL::BooleanExpression.new(:<, field.sql, value.sql)
102
+ end
103
+ }
104
+ end
105
+
106
+ rule greater_equal
107
+ field space? '>=' space? value {
108
+ def query
109
+ Query::GreaterEqual.new field.sql, value.sql
110
+ end
111
+
112
+ def sql
113
+ Sequel::SQL::BooleanExpression.new(:>=, field.sql, value.sql)
114
+ end
115
+ }
116
+ end
117
+
118
+ rule greater
119
+ field space? '>' space? value {
120
+ def query
121
+ Query::Greater.new field.sql, value.sql
122
+ end
123
+
124
+ def sql
125
+ Sequel::SQL::BooleanExpression.new(:>, field.sql, value.sql)
126
+ end
127
+ }
128
+ end
129
+
70
130
  rule not_equals
71
131
  field space? '!=' space? value {
72
132
  def query
@@ -92,9 +152,41 @@ module UState
92
152
  end
93
153
 
94
154
  rule value
95
- double_quoted_string / float / integer / null
155
+ true / false / double_quoted_string / float / integer / null
96
156
  end
97
157
 
158
+ rule true
159
+ 'true' {
160
+ def query
161
+ Query::True.new
162
+ end
163
+
164
+ def ruby_value
165
+ true
166
+ end
167
+
168
+ def sql
169
+ Sequel::TRUE
170
+ end
171
+ }
172
+ end
173
+
174
+ rule false
175
+ 'false' {
176
+ def query
177
+ Query::False.new
178
+ end
179
+
180
+ def ruby_value
181
+ false
182
+ end
183
+
184
+ def sql
185
+ Sequel::FALSE
186
+ end
187
+ }
188
+ end
189
+
98
190
  rule integer
99
191
  '-'? [0-9]+ {
100
192
  def ruby_value
@@ -116,7 +208,7 @@ module UState
116
208
 
117
209
  rule field
118
210
  # [a-zA-Z] [a-zA-Z_0-9]*
119
- ("state" / "host" / "service" / "description" / "metric_f") {
211
+ ("state" / "host" / "service" / "description" / "metric_f" / "time") {
120
212
  def sql
121
213
  text_value.to_sym
122
214
  end
@@ -0,0 +1,126 @@
1
+ module UState
2
+ class Reaper
3
+ # The reaper deletes old states from an index.
4
+
5
+ # By default, states die after
6
+ DEFAULT = 1000
7
+ # By default, scans for old states every
8
+ INTERVAL = 10
9
+
10
+ attr_reader :default
11
+ attr_accessor :index
12
+ attr_accessor :interval
13
+ attr_accessor :targets
14
+
15
+ def initialize(index, opts = {})
16
+ @index = index
17
+ @interval = opts[:interval] || INTERVAL
18
+ @server = opts[:server]
19
+ @targets = {}
20
+ @lock = Mutex.new
21
+ @compiled_targets = {}
22
+ self.default = opts[:default] || DEFAULT
23
+
24
+ start
25
+ end
26
+
27
+ # Transform targets into disjoint queries, ordered by age.
28
+ # This is kinda ugly; should do write an AST optimizer with boolean
29
+ # minimization if anyone starts using this feature heavily.
30
+ def compile
31
+ @lock.synchronize do
32
+ @compiled_targets = {}
33
+
34
+ ordered = @targets.sort do |a,b|
35
+ # The states with the longest lifetimes must be excluded from
36
+ # all the younger checks. Nil = +inf.
37
+ if b[1].nil?
38
+ 1
39
+ elsif a[1].nil?
40
+ -1
41
+ else
42
+ b[1] <=> a[1]
43
+ end
44
+ end
45
+
46
+ excluded = ordered.inject(nil) do |exclude, pair|
47
+ # Build up a set of all queries which should last *longer* than us.
48
+ query, age = pair
49
+ if exclude
50
+ if age
51
+ @compiled_targets["(#{query}) and not (#{exclude})"] = age
52
+ end
53
+ "(#{query}) or (#{exclude})"
54
+ else
55
+ if age
56
+ @compiled_targets[query] = age
57
+ end
58
+ query
59
+ end
60
+ end
61
+
62
+ # Add default
63
+ if @default
64
+ q = if excluded
65
+ "not (#{excluded})"
66
+ else
67
+ "true"
68
+ end
69
+ @compiled_targets[q] = @default
70
+ end
71
+ end
72
+ end
73
+
74
+ def default=(d)
75
+ @default = d
76
+ compile
77
+ end
78
+
79
+ # Delete states matching query
80
+ def delete(query)
81
+ @index.query(Query.new(string: query)).each do |state|
82
+ @index.on_state_change(
83
+ state,
84
+ State.new(
85
+ host: state.host,
86
+ service: state.service,
87
+ state: 'unknown',
88
+ description: "ustate has not heard from this service since #{Time.at(state.time)}",
89
+ time: Time.now.to_i
90
+ )
91
+ )
92
+ @index.delete state
93
+ end
94
+ end
95
+
96
+ # Reap states matching query after age seconds.
97
+ #
98
+ # The reaper is gentle; it will give any state specified to reap() the
99
+ # maximum possible time to live.
100
+ def reap(query, age)
101
+ reap! query, age
102
+ compile
103
+ end
104
+
105
+ def reap!(query, age)
106
+ @targets[query] = age
107
+ end
108
+
109
+ def start
110
+ @runner = Thread.new do
111
+ loop do
112
+ begin
113
+ @lock.synchronize do
114
+ @compiled_targets.each do |query, age|
115
+ delete "(#{query}) and time < #{(Time.now - age).to_i}"
116
+ end
117
+ end
118
+ rescue Exception => e
119
+ @server.log.warn e
120
+ end
121
+ sleep @interval
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -15,11 +15,13 @@ module UState
15
15
  require 'ustate/query_string'
16
16
  require 'ustate/query/ast'
17
17
  require 'ustate/metric_thread'
18
+ require 'ustate/reaper'
18
19
  require 'logger'
19
20
  require 'mtrc'
20
21
 
21
22
  attr_accessor :backends
22
23
  attr_accessor :index
24
+ attr_accessor :reaper
23
25
  attr_writer :aggregator
24
26
  attr_writer :emailer
25
27
  attr_writer :graphite
@@ -35,6 +37,8 @@ module UState
35
37
 
36
38
  @index = Index.new :server => self
37
39
 
40
+ @reaper = UState::Reaper.new(@index, server: self)
41
+
38
42
  @log = Logger.new('ustate.log', 4, 134217728)
39
43
  @log.level = Logger::INFO
40
44
 
@@ -10,15 +10,12 @@ module UState
10
10
 
11
11
  THREADS = 1000
12
12
  BUFFER_SIZE = 10
13
- # Forget about states after
14
- EXPIRY = 1000
15
13
 
16
14
  # Update metrics every
17
15
  INSERT_RATE_INTERVAL = 5
18
16
  INSERT_TIMES_INTERVAL = 5
19
17
 
20
18
  attr_reader :db, :queue
21
- attr_accessor :expiry
22
19
  attr_accessor :insert_rate_interval
23
20
  attr_accessor :insert_times_interval
24
21
 
@@ -33,8 +30,6 @@ module UState
33
30
  @on_state_once = []
34
31
  @on_state = []
35
32
 
36
- @expiry = opts[:expiry] || EXPIRY
37
-
38
33
  @insert_rate_interval = opts[:insert_rate_interval] || INSERT_RATE_INTERVAL
39
34
  @insert_times_interval = opts[:insert_times_interval] || INSERT_TIMES_INTERVAL
40
35
  setup_db
@@ -52,6 +47,15 @@ module UState
52
47
  @insert_rate << 1
53
48
  end
54
49
 
50
+ # Removes a state from the index.
51
+ #
52
+ # Right now state is anything which responds to #host and #service.
53
+ # I'll probably evolve the index to support arbitrary operations on all
54
+ # states matching a query, but haven't thought out the API.
55
+ def delete(state)
56
+ @db[:states].filter(host: state.host, service: state.service).delete
57
+ end
58
+
55
59
  def thread(s)
56
60
  Thread.new do
57
61
  process s
@@ -146,36 +150,6 @@ module UState
146
150
  end
147
151
  end
148
152
 
149
- # Remove states older than @expiry
150
- def reap
151
- @db[:states].filter { |s|
152
- s.time < (Time.now - @expiry).to_i
153
- }.each do |row|
154
- on_state_change(
155
- row_to_state(row),
156
- row_to_state(
157
- row.merge(
158
- state: 'unknown',
159
- description: "ustate has not heard from this service since #{Time.at(row[:time])}",
160
- metric_f: nil,
161
- time: Time.now.to_i
162
- )
163
- )
164
- )
165
- @db[:states].filter(host: row[:host], state: row[:state]).delete
166
- end
167
- end
168
-
169
- # Periodically expire old states.
170
- def reaper
171
- Thread.new do
172
- loop do
173
- sleep 1
174
- reap
175
- end
176
- end
177
- end
178
-
179
153
  # Converts a row to a State
180
154
  def row_to_state(row)
181
155
  State.new(row)
@@ -197,7 +171,6 @@ module UState
197
171
  def start
198
172
  stop!
199
173
  @pool = []
200
- @reaper = reaper
201
174
 
202
175
  @insert_rate = MetricThread.new(Mtrc::Rate) do |r|
203
176
  self << State.new(
@@ -1,3 +1,3 @@
1
1
  module UState
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ustate-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-20 00:00:00.000000000Z
12
+ date: 2011-10-05 00:00:00.000000000 -07:00
13
+ default_executable:
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: beefcake
16
- requirement: &76033910 !ruby/object:Gem::Requirement
17
+ requirement: &17908120 !ruby/object:Gem::Requirement
17
18
  none: false
18
19
  requirements:
19
20
  - - ! '>='
@@ -21,10 +22,10 @@ dependencies:
21
22
  version: 0.3.5
22
23
  type: :runtime
23
24
  prerelease: false
24
- version_requirements: *76033910
25
+ version_requirements: *17908120
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: trollop
27
- requirement: &76033510 !ruby/object:Gem::Requirement
28
+ requirement: &17907540 !ruby/object:Gem::Requirement
28
29
  none: false
29
30
  requirements:
30
31
  - - ! '>='
@@ -32,10 +33,10 @@ dependencies:
32
33
  version: 1.16.2
33
34
  type: :runtime
34
35
  prerelease: false
35
- version_requirements: *76033510
36
+ version_requirements: *17907540
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: mtrc
38
- requirement: &76033170 !ruby/object:Gem::Requirement
39
+ requirement: &17907040 !ruby/object:Gem::Requirement
39
40
  none: false
40
41
  requirements:
41
42
  - - ! '>='
@@ -43,7 +44,7 @@ dependencies:
43
44
  version: 0.0.4
44
45
  type: :runtime
45
46
  prerelease: false
46
- version_requirements: *76033170
47
+ version_requirements: *17907040
47
48
  description:
48
49
  email: aphyr@aphyr.com
49
50
  executables: []
@@ -51,44 +52,53 @@ extensions: []
51
52
  extra_rdoc_files: []
52
53
  files:
53
54
  - lib/ustate.rb
54
- - lib/ustate/graphite.rb
55
+ - lib/ustate/query/not_equals.rb
56
+ - lib/ustate/query/equals.rb
57
+ - lib/ustate/query/false.rb
58
+ - lib/ustate/query/node.rb
59
+ - lib/ustate/query/true.rb
60
+ - lib/ustate/query/or.rb
61
+ - lib/ustate/query/approximately.rb
62
+ - lib/ustate/query/greater.rb
63
+ - lib/ustate/query/and.rb
64
+ - lib/ustate/query/less.rb
65
+ - lib/ustate/query/ast.rb
66
+ - lib/ustate/query/less_equal.rb
67
+ - lib/ustate/query/greater_equal.rb
68
+ - lib/ustate/query/not.rb
55
69
  - lib/ustate/query_string.treetop
56
- - lib/ustate/message.rb
70
+ - lib/ustate/server.rb
57
71
  - lib/ustate/auto_state.rb
58
- - lib/ustate/metric_thread.rb
59
- - lib/ustate/aggregator.rb
72
+ - lib/ustate/emailer.rb
73
+ - lib/ustate/query.rb
60
74
  - lib/ustate/version.rb
61
- - lib/ustate/server/graphite.rb
75
+ - lib/ustate/aggregator.rb
76
+ - lib/ustate/state.rb
77
+ - lib/ustate/reaper.rb
78
+ - lib/ustate/graphite.rb
79
+ - lib/ustate/client/query.rb
80
+ - lib/ustate/metric_thread.rb
81
+ - lib/ustate/query_string.rb
82
+ - lib/ustate/server/index.rb
62
83
  - lib/ustate/server/backends.rb
63
- - lib/ustate/server/backends/base.rb
64
84
  - lib/ustate/server/backends/tcp.rb
65
- - lib/ustate/server/index.rb
85
+ - lib/ustate/server/backends/base.rb
86
+ - lib/ustate/server/graphite.rb
66
87
  - lib/ustate/server/connection.rb
67
- - lib/ustate/server.rb
68
- - lib/ustate/dash.rb
69
- - lib/ustate/state.rb
70
- - lib/ustate/query.rb
71
- - lib/ustate/client/query.rb
72
88
  - lib/ustate/client.rb
73
- - lib/ustate/dash/rack/static.rb
89
+ - lib/ustate/message.rb
90
+ - lib/ustate/dash/helper/renderer.rb
91
+ - lib/ustate/dash/views/css.scss
74
92
  - lib/ustate/dash/views/index.erubis
75
93
  - lib/ustate/dash/views/layout.erubis
76
- - lib/ustate/dash/views/css.scss
94
+ - lib/ustate/dash/rack/static.rb
77
95
  - lib/ustate/dash/controller/index.rb
78
96
  - lib/ustate/dash/controller/css.rb
79
97
  - lib/ustate/dash/config.rb
80
- - lib/ustate/dash/helper/renderer.rb
81
- - lib/ustate/query/or.rb
82
- - lib/ustate/query/not_equals.rb
83
- - lib/ustate/query/ast.rb
84
- - lib/ustate/query/equals.rb
85
- - lib/ustate/query/approximately.rb
86
- - lib/ustate/query/not.rb
87
- - lib/ustate/query/and.rb
88
- - lib/ustate/query_string.rb
89
- - lib/ustate/emailer.rb
98
+ - lib/ustate/dash.rb
90
99
  - LICENSE
91
100
  - README.markdown
101
+ has_rdoc: true
92
102
  homepage: https://github.com/aphyr/ustate
93
103
  licenses: []
94
104
  post_install_message:
@@ -109,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
109
119
  version: '0'
110
120
  requirements: []
111
121
  rubyforge_project: ustate-client
112
- rubygems_version: 1.8.10
122
+ rubygems_version: 1.6.2
113
123
  signing_key:
114
124
  specification_version: 3
115
125
  summary: Client for the distributed state server ustate.