gremlin 0.0.3 → 0.0.4

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.
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