gremlin 0.0.3 → 0.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: 96d4a7c502789e7253b18116cb7f0ec78f723d87
4
- data.tar.gz: 79abfbd5b0389284cea77f479c8251bc150f2808
3
+ metadata.gz: 76f9c1702a54c108247ab988504f59efb41a0736
4
+ data.tar.gz: 739424d848af9ff260de7ba34d78addb6a77f89d
5
5
  SHA512:
6
- metadata.gz: ac3351e8bc6f3a26ca5e56682774e9d89460c26624cc6601f32ff1bed0d75c8735ae321aa5b571b6c38276f9b122f3977eef099d4e6ddf61030cab380e11f103
7
- data.tar.gz: 74382b4281f6665f900639bc89480cdba8b88c09ed54eef140af923302ace24b69cb699018f90b14c9b289185120670b7e8149d9b66ff4cc9898c9a53baa33c2
6
+ metadata.gz: cacbcc4da37f36e33167541c6958005e31f8dee7b7bf8f7c21d464deb7fe566313092c7b0fbdf93a2809967b65b523f333235cea42155db5252d0cc6dbe696b6
7
+ data.tar.gz: 0562dee3d4491516df49a0f4bd6f17d59d6a4ab59207c259663cdeae21901edae05da79431d1957591cb923481fe3eaca5ee8b5724d2e38f133723baa9b98d34
data/.gitignore CHANGED
@@ -1,4 +1,12 @@
1
+ .bundle/
2
+ log/*.log
3
+ pkg/
4
+ spec/dummy/db/*.sqlite3
5
+ spec/dummy/db/*.sqlite3-journal
6
+ spec/dummy/log/*.log
7
+ spec/dummy/tmp/
1
8
  *.sw*
2
9
  spec/examples.txt
3
10
  *.gem
4
11
  *.rdb
12
+ .idea
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1 @@
1
+ gremlin
@@ -0,0 +1 @@
1
+ 2.3.0
@@ -1,11 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gremlin (0.0.3)
4
+ gremlin (0.0.4)
5
5
  quantile
6
6
  rails
7
7
  redis (~> 3.2)
8
- redis-native_hash
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
@@ -96,10 +95,8 @@ GEM
96
95
  rake (>= 0.8.7)
97
96
  thor (>= 0.18.1, < 2.0)
98
97
  rake (10.5.0)
99
- rdoc (5.0.0)
98
+ rdoc (5.1.0)
100
99
  redis (3.3.3)
101
- redis-native_hash (0.2.1)
102
- redis (>= 2.0.0)
103
100
  rspec (3.5.0)
104
101
  rspec-core (~> 3.5.0)
105
102
  rspec-expectations (~> 3.5.0)
@@ -112,6 +109,14 @@ GEM
112
109
  rspec-mocks (3.5.0)
113
110
  diff-lcs (>= 1.2.0, < 2.0)
114
111
  rspec-support (~> 3.5.0)
112
+ rspec-rails (3.5.2)
113
+ actionpack (>= 3.0)
114
+ activesupport (>= 3.0)
115
+ railties (>= 3.0)
116
+ rspec-core (~> 3.5.0)
117
+ rspec-expectations (~> 3.5.0)
118
+ rspec-mocks (~> 3.5.0)
119
+ rspec-support (~> 3.5.0)
115
120
  rspec-support (3.5.0)
116
121
  sprockets (3.7.1)
117
122
  concurrent-ruby (~> 1.0)
@@ -120,6 +125,7 @@ GEM
120
125
  actionpack (>= 4.0)
121
126
  activesupport (>= 4.0)
122
127
  sprockets (>= 3.0.0)
128
+ sqlite3 (1.3.13)
123
129
  thor (0.19.4)
124
130
  thread_safe (0.3.6)
125
131
  tzinfo (1.2.2)
@@ -137,6 +143,8 @@ DEPENDENCIES
137
143
  rake (~> 10.0)
138
144
  rdoc
139
145
  rspec (~> 3.3, >= 3.3.0)
146
+ rspec-rails
147
+ sqlite3
140
148
 
141
149
  BUNDLED WITH
142
- 1.12.4
150
+ 1.14.5
data/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/gremlin`. To experiment with that code, run `bin/console` for an interactive prompt.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
5
  ## Installation
8
6
 
9
7
  Add this line to your application's Gemfile:
@@ -32,10 +30,13 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
30
 
33
31
  ## Contributing
34
32
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/gremlin.
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/omadahealth/gremlin.
36
34
 
37
35
 
38
36
  ## License
39
37
 
40
38
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
39
 
40
+ ## Resources
41
+
42
+ https://speakerdeck.com/nextmat/digging-deep-with-activesupportnotifications
@@ -8,7 +8,7 @@ module Gremlin
8
8
  registry = Gremlin.registry
9
9
 
10
10
  metrics = registry.metrics.map do |metric|
11
- metric.repr
11
+ metric.repr_and_delete
12
12
  end
13
13
 
14
14
  plaintext(metrics)
@@ -1,5 +1,6 @@
1
1
  require 'benchmark'
2
2
  require 'gremlin'
3
+ require 'securerandom'
3
4
 
4
5
 
5
6
  include Benchmark
@@ -8,15 +9,16 @@ print "Starting benchmarks..."
8
9
 
9
10
  orig_std_out = STDOUT.clone
10
11
  STDOUT.reopen(File::open('/dev/null', 'w'))
12
+ iterations = ARGV[-1].to_i
11
13
 
12
- counter_init_benchmarks = (1..100).map do |n|
14
+ counter_init_benchmarks = (1..iterations).map do |n|
13
15
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
14
16
  counter_name = "test_#{SecureRandom.hex(5)}_counter".to_sym
15
17
  x.report("Counter initialize") { Gremlin::Instruments::Counter.new counter_name, "placeholder", {} }
16
18
  end
17
19
  end
18
20
 
19
- counter_init_and_fetch_benchmarks = (1..100).map do |n|
21
+ counter_init_and_fetch_benchmarks = (1..iterations).map do |n|
20
22
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
21
23
  counter_name = "test_#{SecureRandom.hex(5)}_counter".to_sym
22
24
  x.report("Counter initialize then fetch") do
@@ -26,7 +28,7 @@ counter_init_and_fetch_benchmarks = (1..100).map do |n|
26
28
  end
27
29
  end
28
30
 
29
- counter_init_and_incr_benchmarks = (1..100).map do |n|
31
+ counter_init_and_incr_benchmarks = (1..iterations).map do |n|
30
32
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
31
33
  counter_name = "test_#{SecureRandom.hex(5)}_counter".to_sym
32
34
  x.report("Counter increment") do
@@ -36,7 +38,7 @@ counter_init_and_incr_benchmarks = (1..100).map do |n|
36
38
  end
37
39
  end
38
40
 
39
- gauge_init_benchmarks = (1..100).map do |n|
41
+ gauge_init_benchmarks = (1..iterations).map do |n|
40
42
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
41
43
  gauge_name = "test_#{SecureRandom.hex(5)}_gauge".to_sym
42
44
  gauge_set_val = rand(1..25000000)
@@ -44,7 +46,7 @@ gauge_init_benchmarks = (1..100).map do |n|
44
46
  end
45
47
  end
46
48
 
47
- gauge_init_and_fetch_benchmarks = (1..100).map do |n|
49
+ gauge_init_and_fetch_benchmarks = (1..iterations).map do |n|
48
50
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
49
51
  gauge_name = "test_#{SecureRandom.hex(5)}_gauge".to_sym
50
52
  gauge_set_val = rand(1..25000000)
@@ -55,7 +57,7 @@ gauge_init_and_fetch_benchmarks = (1..100).map do |n|
55
57
  end
56
58
  end
57
59
 
58
- gauge_init_and_set_benchmarks = (1..100).map do |n|
60
+ gauge_init_and_set_benchmarks = (1..iterations).map do |n|
59
61
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
60
62
  gauge_name = "test_#{SecureRandom.hex(5)}_gauge".to_sym
61
63
  gauge_set_val = rand(1..25000000)
@@ -66,7 +68,7 @@ gauge_init_and_set_benchmarks = (1..100).map do |n|
66
68
  end
67
69
  end
68
70
 
69
- summary_init_benchmarks = (1..100).map do |n|
71
+ summary_init_benchmarks = (1..iterations).map do |n|
70
72
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
71
73
  summary_name = "test_#{SecureRandom.hex(5)}_summary".to_sym
72
74
  summary_observe_val = rand(1..25000000)
@@ -74,7 +76,7 @@ summary_init_benchmarks = (1..100).map do |n|
74
76
  end
75
77
  end
76
78
 
77
- summary_init_and_fetch_benchmarks = (1..100).map do |n|
79
+ summary_init_and_fetch_benchmarks = (1..iterations).map do |n|
78
80
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
79
81
  summary_name = "test_#{SecureRandom.hex(5)}_summary".to_sym
80
82
  summary_observe_val = rand(1..25000000)
@@ -85,13 +87,15 @@ summary_init_and_fetch_benchmarks = (1..100).map do |n|
85
87
  end
86
88
  end
87
89
 
88
- summary_init_and_observe_benchmarks = (1..100).map do |n|
90
+ summary_init_and_observe_benchmarks = (1..iterations).map do |n|
89
91
  Benchmark.benchmark(CAPTION, 35, FORMAT) do |x|
90
92
  summary_name = "test_#{SecureRandom.hex(5)}_summary".to_sym
91
93
  summary_observe_val = rand(1..25000000)
92
94
  x.report("Summary observe") do
93
95
  summary = Gremlin::Instruments::Summary.new summary_name.to_sym, "placeholder", {}
94
- summary.observe({ :bar => "foo" }, summary_observe_val)
96
+ (1..iterations).each do
97
+ summary.observe({ :bar => "foo" }, summary_observe_val)
98
+ end
95
99
  end
96
100
  end
97
101
  end
@@ -106,11 +110,11 @@ counter_init_and_fetch_agg = counter_init_and_fetch_benchmarks.flatten.inject(0)
106
110
  puts "--------------------------------------------------------"
107
111
  puts ""
108
112
  puts "Counter Initialize Total: #{counter_init_agg*1000} ms"
109
- puts "Counter Initialize Avg: #{counter_init_agg/100*1000} ms over 100 iterations"
113
+ puts "Counter Initialize Avg: #{counter_init_agg/iterations*1000} ms over #{iterations} iterations"
110
114
  puts "Counter Initialize and Fetch Total: #{counter_init_and_fetch_agg*1000} ms"
111
- puts "Counter Initialize and Fetch Avg: #{counter_init_and_fetch_agg/100*1000} ms over 100 iterations"
115
+ puts "Counter Initialize and Fetch Avg: #{counter_init_and_fetch_agg/iterations*1000} ms over #{iterations} iterations"
112
116
  puts "Counter Initialize and Fetch Total: #{counter_init_and_incr_agg*1000} ms"
113
- puts "Counter Initialize and Fetch Avg: #{counter_init_and_incr_agg/100*1000} ms over 100 iterations"
117
+ puts "Counter Initialize and Fetch Avg: #{counter_init_and_incr_agg/iterations*1000} ms over #{iterations} iterations"
114
118
  puts ""
115
119
 
116
120
  gauge_init_agg = gauge_init_benchmarks.flatten.inject(0) { |sum, x| sum + x.real }
@@ -120,11 +124,11 @@ gauge_init_and_set_agg = gauge_init_and_set_benchmarks.flatten.inject(0) { |su
120
124
  puts "--------------------------------------------------------"
121
125
  puts ""
122
126
  puts "Gauge Initialize Total: #{gauge_init_agg*1000} ms"
123
- puts "Gauge Initialize Avg: #{gauge_init_agg/100*1000} ms over 100 iterations"
127
+ puts "Gauge Initialize Avg: #{gauge_init_agg/iterations*1000} ms over #{iterations} iterations"
124
128
  puts "Gauge Initialize and Fetch Total: #{gauge_init_and_fetch_agg*1000} ms"
125
- puts "Gauge Initialize and Fetch Avg: #{gauge_init_and_fetch_agg/100*1000} ms over 100 iterations"
129
+ puts "Gauge Initialize and Fetch Avg: #{gauge_init_and_fetch_agg/iterations*1000} ms over #{iterations} iterations"
126
130
  puts "Gauge Initialize and Set Total: #{gauge_init_and_set_agg*1000} ms"
127
- puts "Gauge Initialize and Set Avg: #{gauge_init_and_set_agg/100*1000} ms over 100 iterations"
131
+ puts "Gauge Initialize and Set Avg: #{gauge_init_and_set_agg/iterations*1000} ms over #{iterations} iterations"
128
132
  puts ""
129
133
  puts "--------------------------------------------------------"
130
134
 
@@ -134,10 +138,10 @@ summary_init_and_observe_agg = summary_init_and_observe_benchmarks.flatten.inj
134
138
 
135
139
  puts ""
136
140
  puts "Summary Initialize Total: #{summary_init_agg*1000} ms"
137
- puts "Summary Initialize Avg: #{summary_init_agg/100*1000} ms over 100 iterations"
141
+ puts "Summary Initialize Avg: #{summary_init_agg/iterations*1000} ms over #{iterations} iterations"
138
142
  puts "Summary Initialize and Fetch Total: #{summary_init_and_fetch_agg*1000} ms"
139
- puts "Summary Initialize and Fetch Avg: #{summary_init_and_fetch_agg/100*1000} ms over 100 iterations"
143
+ puts "Summary Initialize and Fetch Avg: #{summary_init_and_fetch_agg/iterations*1000} ms over #{iterations} iterations"
140
144
  puts "Summary Initialize and Observe Total: #{summary_init_and_observe_agg*1000} ms"
141
- puts "Summary Initialize and Observe Avg: #{summary_init_and_observe_agg/100*1000} ms over 100 iterations"
145
+ puts "Summary Initialize and Observe Avg: #{summary_init_and_observe_agg/(iterations*iterations)*1000} ms over #{iterations} observations with #{iterations} iterations"
142
146
  puts ""
143
147
  puts "--------------------------------------------------------"
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails gems
3
+ # installed from the root of your application.
4
+
5
+ ENGINE_ROOT = File.expand_path('../..', __FILE__)
6
+ ENGINE_PATH = File.expand_path('../../lib/gremlin/engine', __FILE__)
7
+
8
+ # Set up gems listed in the Gemfile.
9
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
10
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
11
+
12
+ require 'rails/all'
13
+ require 'rails/engine/commands'
@@ -6,7 +6,7 @@ require 'gremlin/version'
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "gremlin"
8
8
  spec.version = Gremlin::VERSION
9
- spec.authors = ["Kevin G. Phillips"]
9
+ spec.authors = ["Kevin G. Phillips", "Chris Constantine"]
10
10
  spec.email = ["platform@omadahealth.com"]
11
11
 
12
12
  spec.summary = %q{Gem to sanely emit metrics from Omadahealth rails applications}
@@ -31,8 +31,9 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "rake", "~> 10.0"
32
32
  spec.add_development_dependency 'rspec', '~> 3.3', '>= 3.3.0'
33
33
  spec.add_development_dependency 'rdoc'
34
+ spec.add_development_dependency "rspec-rails"
35
+ spec.add_development_dependency "sqlite3"
34
36
 
35
- spec.add_runtime_dependency 'redis-native_hash'
36
37
  spec.add_runtime_dependency 'quantile'
37
38
  spec.add_runtime_dependency 'rails'
38
39
  spec.add_runtime_dependency 'redis', '~> 3.2'
@@ -1,10 +1,11 @@
1
1
  # Supporting librarires
2
2
  require "active_support/cache"
3
3
  require "active_support/notifications" if defined?(::Rails)
4
+ require "active_support/subscriber" if defined?(::Rails)
4
5
  require "erb"
5
6
  require "json"
6
7
  require "quantile"
7
- require "redis_hash"
8
+ require "redis"
8
9
  require "yaml"
9
10
 
10
11
  # Gem internals
@@ -7,14 +7,39 @@ module Gremlin
7
7
  @name = name
8
8
  @docstring = docstring
9
9
  @base_labels = base_labels
10
- @mutex = Mutex.new
11
10
 
12
11
  @r = Redis.new(**Gremlin.configuration.redis)
13
- Redis::NativeHash.redis = @r
14
- @values = Redis::NativeHash.find(retention_key) || Redis::NativeHash.new
15
- #@values.redis = Redis::NativeHash.redis
16
- @values.key = retention_key
17
- @values.save
12
+ end
13
+
14
+ def delete
15
+ @r.del retention_key
16
+ end
17
+
18
+ def retention_get
19
+ @r.hgetall retention_key
20
+ end
21
+
22
+ def parse(value)
23
+ {
24
+ name: @name,
25
+ type: type_string,
26
+ help: help_string,
27
+ values: value.each_with_object({}) { |(l,v), m| m[JSON.parse(l).merge(@base_labels)] = cast(v) }
28
+ }
29
+ end
30
+
31
+ def repr
32
+ parse(retention_get)
33
+ end
34
+
35
+ def repr_and_delete
36
+ val = nil
37
+ @r.pipelined do
38
+ val = retention_get
39
+ @r.del retention_key
40
+ end
41
+ v = parse(val.value)
42
+ return v
18
43
  end
19
44
 
20
45
  def node
@@ -33,10 +58,6 @@ module Gremlin
33
58
  "# TYPE #{@name} #{type}"
34
59
  end
35
60
 
36
- def default
37
- nil
38
- end
39
-
40
61
  def help
41
62
  @docstring
42
63
  end
@@ -45,27 +66,20 @@ module Gremlin
45
66
  "# HELP #{@name} #{help}"
46
67
  end
47
68
 
48
- def repr
49
- {
50
- name: @name,
51
- type: type_string,
52
- help: help_string,
53
- values: values.each_with_object({}) { |(l,v), m| m[l.merge(@base_labels)] = v }
54
- }
69
+ def cast(v)
70
+ case type
71
+ when :counter
72
+ v.to_i
73
+ when :gauge
74
+ v.to_f
75
+ else
76
+ v
77
+ end
55
78
  end
56
79
 
57
80
  def values
58
- @mutex.synchronize do
59
- @values.each_with_object({}) do |(labels, value), memo|
60
- case type
61
- when :counter
62
- memo[JSON.parse(labels)] = value.to_i
63
- when :gauge
64
- memo[JSON.parse(labels)] = value.to_f
65
- else
66
- memo[JSON.parse(labels)] = value
67
- end
68
- end
81
+ retention_get.each_with_object({}) do |(labels, value), memo|
82
+ memo[JSON.parse(labels)] = cast(value)
69
83
  end
70
84
  end
71
85
  end
@@ -4,9 +4,7 @@ module Gremlin
4
4
  module Instruments
5
5
  class Counter < Base
6
6
  def increment(labels={}, by=1)
7
- @mutex.synchronize do
8
- @r.hincrby retention_key, labels.to_json, by
9
- end
7
+ @r.hincrby retention_key, labels.to_json, by
10
8
  end
11
9
 
12
10
  def default
@@ -22,9 +20,8 @@ module Gremlin
22
20
  end
23
21
 
24
22
  def get(labels={})
25
- @values.reload!
26
- v = @values[labels.to_json]
27
- v.to_i
23
+ v = retention_get[labels.to_json]
24
+ cast(v)
28
25
  end
29
26
  end
30
27
  end
@@ -4,10 +4,8 @@ module Gremlin
4
4
  module Instruments
5
5
  class Gauge < Base
6
6
  def set(labels={}, value)
7
- @mutex.synchronize do
8
- @r.hset retention_key, labels.to_json, value
9
- get(labels)
10
- end
7
+ @r.hset retention_key, labels.to_json, value
8
+ get(labels)
11
9
  end
12
10
 
13
11
  def type
@@ -19,9 +17,8 @@ module Gremlin
19
17
  end
20
18
 
21
19
  def get(labels={})
22
- @values.reload!
23
- v = @values[labels.to_json]
24
- v.to_f
20
+ v = retention_get[labels.to_json]
21
+ cast(v)
25
22
  end
26
23
  end
27
24
  end
@@ -6,68 +6,68 @@ module Gremlin
6
6
  class Value < Hash
7
7
  def initialize(estimator)
8
8
  @sum = estimator.sum
9
- @total = estimator.observations
10
9
 
11
- estimator.invariants.each do |invariant|
12
- self[invariant.quantile] = estimator.query(invariant.quantile)
10
+ estimator.values.each do |percent, value|
11
+ self[percent] = value
13
12
  end
14
13
  end
15
14
  end
16
15
 
17
16
  def observe(labels={}, value)
18
- @mutex.synchronize do
19
- e = @values[labels.to_json]
20
-
21
- if e.nil?
22
- estimator = default
23
- else
24
- deserialized = JSON.parse(e)
25
- estimator = unpack(deserialized)
26
- end
27
- sum = estimator.observe(value)
28
-
29
- @values[labels.to_json] = estimator.serialize
30
- @values.save
31
-
32
- sum
33
- end
17
+ # grabbing estimator from redis
18
+ # deserializing and converting back to objects
19
+ # adding a new observation into the values array
20
+ # set the updated estimator into redis
21
+ # return sum
22
+ # if retention_get == {}
23
+ # e = nil
24
+ # else
25
+ # e = parse(retention_get)[labels.to_json]
26
+ # end
27
+ #
28
+ # if e.nil?
29
+ # estimator = default
30
+ # else
31
+ # deserialized = JSON.parse(e)
32
+ # estimator = unpack(deserialized)
33
+ # end
34
+ # sum = estimator.observe(value)
35
+ #
36
+ # @r.hset retention_key, labels.to_json, p(estimator.serialize)
37
+ #
38
+ # sum
39
+
40
+ estimator = Gremlin::Quantile::Estimator.deserialize(retention_get[labels.to_json])
41
+ sum = estimator.observe(value)
42
+ @r.hset retention_key, labels.to_json, estimator.serialize
43
+ sum
34
44
  end
35
45
 
36
46
  def values
37
- @mutex.synchronize do
38
- @values.each_with_object({}) do |(labels, value), memo|
39
- deserialized = JSON.parse(value)
40
- e = unpack(deserialized)
41
- memo[JSON.parse(labels)] = Value.new(e)
42
- end
47
+ retention_get.each_with_object({}) do |(labels, value), memo|
48
+ deserialized = JSON.parse(value)
49
+ e = unpack(deserialized)
50
+ memo[JSON.parse(labels)] = Value.new(e)
43
51
  end
44
52
  end
45
53
 
46
54
  def get(labels={})
47
- @mutex.synchronize do
48
- val = @values[labels.to_json]
49
- return val if val.nil?
55
+ val = retention_get[labels.to_json]
56
+ return val if val.nil?
50
57
 
51
- deserialized = JSON.parse(val)
52
- Value.new(unpack(deserialized))
53
- end
58
+ deserialized = JSON.parse(val)
59
+ Value.new(unpack(deserialized))
54
60
  end
55
61
 
56
- def unpack(metric_hash)
57
- Gremlin::Quantile::Estimator.new(metric_hash["buffer"],
58
- metric_hash["head"],
59
- metric_hash["observations"].to_i,
60
- metric_hash["sum"].to_f,
61
- *metric_hash["invariants"].map { |i| Gremlin::Quantile::Quantile.new i["quantile"], i["inaccuracy"] })
62
- end
63
-
64
- def repr
65
- vals = values.each_with_object({}) { |(l,v), m| m[l.merge(@base_labels)] = v }
62
+ def parse(value)
63
+ vals = value.each_with_object({}) { |(l,v), m| m[JSON.parse(l).merge(@base_labels)] = cast(v) }
66
64
 
67
- values = vals.each_with_object({}) do |(labels, value), memo|
68
- if value.is_a? Hash
69
- value.each do |b, i|
70
- memo[labels.merge({quantile: b})] = i
65
+ values = vals.each_with_object({}) do |(l, v), memo|
66
+ v = JSON.parse(v)
67
+ if v.is_a? Hash
68
+ v = Value.new(unpack(v))
69
+ v.each do |b, i|
70
+ memo[l.merge({percent: b})] = i
71
71
  end
72
72
  end
73
73
  end
@@ -80,6 +80,11 @@ module Gremlin
80
80
  }
81
81
  end
82
82
 
83
+ def unpack(metric_hash)
84
+ Gremlin::Quantile::Estimator.new(values: metric_hash["values"],
85
+ invariants: metric_hash["invariants"].map { |i| Gremlin::Quantile::Percentile.new(i["percent"]) })
86
+ end
87
+
83
88
  def default
84
89
  Gremlin::Quantile::Estimator.new
85
90
  end
@@ -1,5 +1,5 @@
1
1
  require 'gremlin/quantile/estimator'
2
- require 'gremlin/quantile/quantile'
2
+ require 'gremlin/quantile/percentile'
3
3
 
4
4
  module Gremlin
5
5
  module Quantile
@@ -1,27 +1,46 @@
1
1
  module Gremlin
2
2
  module Quantile
3
- class Estimator < ::Quantile::Estimator
4
- def initialize(buffer=[], head=nil, observations=0, sum=0, *invariants)
5
- if invariants.empty?
6
- invariants = [Gremlin::Quantile::Quantile.new(0.5, 0.05), Gremlin::Quantile::Quantile.new(0.9, 0.01), Gremlin::Quantile::Quantile.new(0.99, 0.001)]
3
+ class Estimator
4
+
5
+ attr_accessor :invariants
6
+
7
+ def initialize(values: nil, invariants: nil)
8
+ if invariants.nil?
9
+ invariants = [Gremlin::Quantile::Percentile.new(0.5), Gremlin::Quantile::Percentile.new(0.9), Gremlin::Quantile::Percentile.new(0.99)]
7
10
  end
8
11
 
9
12
  @invariants = invariants
10
- @buffer = buffer
11
- @head = head
12
- @observations = observations
13
- @sum = sum
13
+ @values = values || []
14
+ end
15
+
16
+ def observe(value)
17
+ @values << value
18
+ sum
19
+ end
20
+
21
+ def sum
22
+ @values.reduce { |sum, x| sum += x }
23
+ end
24
+
25
+ def values
26
+ @values.sort!
27
+ invariants.inject(Hash.new) do |doc, invariant|
28
+ doc[invariant.percent] = invariant.calculate(@values)
29
+ doc
30
+ end
14
31
  end
15
32
 
16
33
  def serialize
17
34
  JSON.dump({
18
- invariants: @invariants.map { |i| i.to_h },
19
- buffer: @buffer,
20
- head: @head,
21
- sum: @sum,
22
- observations: @observations
35
+ invariants: @invariants.map { |i| i.to_h },
36
+ values: @values
23
37
  })
24
38
  end
39
+
40
+ def self.deserialize(message)
41
+ doc = JSON.parse(message || "{}")
42
+ new(values: doc["values"], invariants: doc["invariants"])
43
+ end
25
44
  end
26
45
  end
27
46
  end
@@ -0,0 +1,26 @@
1
+ module Gremlin
2
+ module Quantile
3
+ class Percentile
4
+ attr_accessor :percent
5
+
6
+ def initialize(percent)
7
+ @percent = percent
8
+ raise unless @percent
9
+ end
10
+
11
+ def to_h
12
+ {
13
+ percent: @percent,
14
+ }
15
+ end
16
+
17
+ def calculate(sorted_values)
18
+ sorted_values[(@percent * sorted_values.count).to_i]
19
+ end
20
+
21
+ def serialize
22
+ to_h.to_json
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,39 +1,4 @@
1
- module Gremlin
2
- module ActionController
3
- class StartProcessingCounterReceiver < Gremlin::NotificationObserver::CounterReceiver
4
- def call(name, start, finish, id, payload)
5
- begin
6
- instrument.increment({ controller: payload[:controller].to_s,
7
- format: payload[:format].to_s,
8
- action: payload[:action] })
9
- rescue; end
10
- end
11
- end
12
-
13
- class ProcessActionCounterReceiver < Gremlin::NotificationObserver::CounterReceiver
14
- def call(name, start, finish, id, payload)
15
- begin
16
- instrument.increment({ controller: payload[:controller].to_s,
17
- format: payload[:format].to_s,
18
- action: payload[:action],
19
- status: payload[:status] })
20
- rescue; end
21
- end
22
- end
23
-
24
- class ProcessActionSummaryReceiver < Gremlin::NotificationObserver::SummaryReceiver
25
- def call(name, start, finish, id, payload)
26
- begin
27
- instrument.observe({ controller: payload[:controller],
28
- method: payload[:method],
29
- status: payload[:status],
30
- action: payload[:action] },
31
- payload[@field_to_observe.to_sym])
32
- rescue; end
33
- end
34
- end
35
- end
36
-
1
+ module Gremlin
37
2
  class Railtie < ::Rails::Railtie
38
3
  config.before_initialize do
39
4
  Gremlin.configure do |c|
@@ -50,44 +15,101 @@ module Gremlin
50
15
  end
51
16
  end
52
17
  end
18
+ end
19
+ end
53
20
 
54
- initializer "gremlin.base_instrumentation" do |app|
21
+ module ActionController
22
+ class StatsSubscriber < ActiveSupport::Subscriber
23
+ attach_to :action_controller
24
+
25
+ def basic_labels
55
26
  environment = ::Rails.env.to_s
56
27
  application_name = ::Rails.application.class.parent_name.downcase
57
28
  node = `hostname`.strip
58
- basic_labels = { node: node, app: application_name, env: environment }
59
-
60
- next if Gremlin.disabled?
61
-
62
- #
63
- # ActionController instrumentation
64
- #
65
-
66
- # Generic instrumentation for /(start_processing|process_action|redirect_to)\.action_controller/
67
- occurance_counter = Gremlin::NotificationObserver::CounterReceiver.new("notification_count",
68
- "Count of notifications handled by Gremlin.",
69
- basic_labels)
70
- ActiveSupport::Notifications.subscribe /(start_processing|process_action|redirect_to)\.action_controller/, occurance_counter
71
-
72
- # Instrumentation for start_processing.action_controller
73
- start_processing_occurance_counter = Gremlin::ActionController::StartProcessingCounterReceiver.new("action_controller_start_processing_count",
74
- "Count of start_processing.action_controller notifications handled by Gremlin.",
75
- basic_labels)
76
- ActiveSupport::Notifications.subscribe "start_processing.action_controller", start_processing_occurance_counter
77
-
78
- # Instrumentation for process_action.action_controller
79
- view_summary = Gremlin::ActionController::ProcessActionSummaryReceiver.new("action_controller_process_action_view_runtime_ms",
80
- "ActionController process action view runtime in milliseconds.",
81
- basic_labels, "view_runtime")
82
- db_summary = Gremlin::ActionController::ProcessActionSummaryReceiver.new("action_controller_process_action_db_runtime_ms",
83
- "ActionController process action db runtime in milliseconds.",
84
- basic_labels, "db_runtime")
85
- process_action_occurance_counter = Gremlin::ActionController::ProcessActionCounterReceiver.new("action_controller_process_action_count",
86
- "Count of process_action.action_controller notifications handled by Gremlin.",
87
- basic_labels)
88
- ActiveSupport::Notifications.subscribe "process_action.action_controller", process_action_occurance_counter
89
- ActiveSupport::Notifications.subscribe "process_action.action_controller", view_summary
90
- ActiveSupport::Notifications.subscribe "process_action.action_controller", db_summary
29
+
30
+ { node: node, app: application_name, env: environment }
31
+ end
32
+
33
+ def notification_count
34
+ @notification_count ||= Gremlin.registry.register Gremlin::Instruments::Counter.new("notification_count",
35
+ "Count of notifications handled by Gremlin.",
36
+ basic_labels)
37
+ end
38
+
39
+ def action_controller_start_processing_count
40
+ @start_processing_count ||= Gremlin.registry.register Gremlin::Instruments::Counter.new("action_controller_start_processing_count",
41
+ "Count of start_processing.action_controller notifications handled by Gremlin.",
42
+ basic_labels)
43
+ end
44
+
45
+ def action_controller_process_action_count
46
+ @process_action_count ||= Gremlin.registry.register Gremlin::Instruments::Counter.new("action_controller_process_action_count",
47
+ "Count of process_action.action_controller notifications handled by Gremlin.",
48
+ basic_labels)
49
+ end
50
+
51
+ def action_controller_process_action_view_runtime_ms
52
+ @process_action_view_runtime ||= Gremlin.registry.register Gremlin::Instruments::Summary.new("action_controller_process_action_view_runtime_ms",
53
+ "ActionController process action view runtime in milliseconds.",
54
+ basic_labels)
55
+ end
56
+
57
+ def action_controller_process_action_db_runtime_ms
58
+ @process_action_db_runtime ||= Gremlin.registry.register Gremlin::Instruments::Summary.new("action_controller_process_action_db_runtime_ms",
59
+ "ActionController process action db runtime in milliseconds.",
60
+ basic_labels)
61
+ end
62
+
63
+ def action_controller_process_action_total_runtime_ms
64
+ @process_action_total_runtime ||= Gremlin.registry.register Gremlin::Instruments::Summary.new("action_controller_process_action_total_runtime_ms",
65
+ "ActionController process action total runtime in milliseconds.",
66
+ basic_labels)
67
+ end
68
+
69
+ def process_action(event)
70
+ return if Gremlin.disabled?
71
+ notification_count.increment
72
+
73
+ action_controller_process_action_count.increment({ controller: event.payload[:controller].to_s,
74
+ format: event.payload[:format].to_s,
75
+ action: event.payload[:action],
76
+ status: event.payload[:status] })
77
+
78
+ begin
79
+ action_controller_process_action_view_runtime_ms.observe({ controller: event.payload[:controller],
80
+ method: event.payload[:method],
81
+ status: event.payload[:status],
82
+ action: event.payload[:action] },
83
+ event.payload[:view_runtime]
84
+ )
85
+ rescue; end
86
+
87
+ begin
88
+ action_controller_process_action_db_runtime_ms.observe({ controller: event.payload[:controller],
89
+ method: event.payload[:method],
90
+ status: event.payload[:status],
91
+ action: event.payload[:action] },
92
+ event.payload[:db_runtime]
93
+ )
94
+ rescue; end
95
+
96
+ begin
97
+ action_controller_process_action_total_runtime_ms.observe({ controller: event.payload[:controller],
98
+ method: event.payload[:method],
99
+ status: event.payload[:status],
100
+ action: event.payload[:action] },
101
+ event.duration
102
+ )
103
+ rescue; end
104
+ end
105
+
106
+ def start_processing(event)
107
+ return if Gremlin.disabled?
108
+ notification_count.increment
109
+
110
+ action_controller_start_processing_count.increment({ controller: event.payload[:controller].to_s,
111
+ format: event.payload[:format].to_s,
112
+ action: event.payload[:action] })
91
113
  end
92
114
  end
93
115
  end
@@ -5,13 +5,7 @@ module Gremlin
5
5
  NotAMetricError = Class.new StandardError
6
6
 
7
7
  def initialize
8
- Redis::NativeHash.redis = Redis.new(**Gremlin.configuration.redis)
9
- @metrics = Redis::NativeHash.find(retention_key) || Redis::NativeHash.new()
10
- #@metrics.redis = Redis::NativeHash.redis
11
- @metrics.key = retention_key
12
- @metrics.save
13
-
14
- @mutex = Mutex.new
8
+ @r = Redis.new(**Gremlin.configuration.redis)
15
9
  end
16
10
 
17
11
  def node
@@ -22,44 +16,40 @@ module Gremlin
22
16
  "gremlin_prometheus_#{node}_metrics_registry"
23
17
  end
24
18
 
19
+ def retention_get
20
+ @r.hgetall retention_key
21
+ end
22
+
25
23
  def metrics
26
- @metrics.values.map do |m|
24
+ retention_get.values.map do |m|
27
25
  deserialize(m)
28
26
  end
29
27
  end
30
28
 
31
29
  def dump_metrics
32
- @metrics
30
+ retention_get
33
31
  end
34
32
 
35
33
  def get(name)
36
- m = @metrics[name.to_sym]
34
+ m = retention_get[name.to_s]
37
35
  deserialize(m) unless m.nil?
38
36
  end
39
37
 
40
38
  def exist?(name)
41
- @metrics.key? name
39
+ @r.hexists retention_key, name.to_s
42
40
  end
43
41
 
44
42
  def register(instrument)
45
43
  name = instrument.name
46
- raise AlreadyRegisteredError.new("#{name} has already been registered") if exist? name
47
- @mutex.synchronize do
48
- @metrics[name.to_sym] = serialize(instrument)
49
- @metrics.save
50
- end
44
+ set = @r.hset retention_key, name, serialize(instrument)
51
45
 
52
46
  instrument
53
47
  end
54
48
 
55
49
  def deregister(instrument)
56
50
  name = instrument.name
57
- raise NotRegisteredError.new("#{name} has not been registered.") unless exist? name
58
- @mutex.synchronize do
59
- @metrics.delete(instrument.name.to_sym)
60
- @metrics.save
61
- metrics
62
- end
51
+ raise NotRegisteredError.new() if @r.hdel(retention_key, name) == 0
52
+ metrics
63
53
  end
64
54
 
65
55
  def deserialize(metric)
@@ -1,3 +1,3 @@
1
1
  module Gremlin
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gremlin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin G. Phillips
8
+ - Chris Constantine
8
9
  autorequire:
9
10
  bindir: exe
10
11
  cert_chain: []
11
- date: 2017-02-28 00:00:00.000000000 Z
12
+ date: 2017-03-03 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
@@ -73,13 +74,27 @@ dependencies:
73
74
  - !ruby/object:Gem::Version
74
75
  version: '0'
75
76
  - !ruby/object:Gem::Dependency
76
- name: redis-native_hash
77
+ name: rspec-rails
77
78
  requirement: !ruby/object:Gem::Requirement
78
79
  requirements:
79
80
  - - ">="
80
81
  - !ruby/object:Gem::Version
81
82
  version: '0'
82
- type: :runtime
83
+ type: :development
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: sqlite3
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ type: :development
83
98
  prerelease: false
84
99
  version_requirements: !ruby/object:Gem::Requirement
85
100
  requirements:
@@ -136,6 +151,9 @@ extensions: []
136
151
  extra_rdoc_files: []
137
152
  files:
138
153
  - ".gitignore"
154
+ - ".rspec"
155
+ - ".ruby-gemset"
156
+ - ".ruby-version"
139
157
  - Gemfile
140
158
  - Gemfile.lock
141
159
  - LICENSE.txt
@@ -144,6 +162,7 @@ files:
144
162
  - app/controllers/gremlin/metrics_controller.rb
145
163
  - benchmark.rb
146
164
  - bin/console
165
+ - bin/rails
147
166
  - bin/setup
148
167
  - config/routes.rb
149
168
  - gremlin.gemspec
@@ -156,7 +175,7 @@ files:
156
175
  - lib/gremlin/notification_observer.rb
157
176
  - lib/gremlin/quantile.rb
158
177
  - lib/gremlin/quantile/estimator.rb
159
- - lib/gremlin/quantile/quantile.rb
178
+ - lib/gremlin/quantile/percentile.rb
160
179
  - lib/gremlin/railtie.rb
161
180
  - lib/gremlin/registry.rb
162
181
  - lib/gremlin/version.rb
@@ -1,16 +0,0 @@
1
- module Gremlin
2
- module Quantile
3
- class Quantile < ::Quantile::Quantile
4
- def to_h
5
- {
6
- quantile: @quantile,
7
- inaccuracy: @inaccuracy
8
- }
9
- end
10
-
11
- def serialize
12
- to_h.to_json
13
- end
14
- end
15
- end
16
- end