rack-healthz 0.1.0 → 0.2.0

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: 8f2db9eb6e4e77c61d09a7c24c09ed1054be13a9
4
- data.tar.gz: 47d097eda0ea43b797347f8c5c72be986a9adde3
3
+ metadata.gz: a57636e6570937379f557ffe6ae23130896cd754
4
+ data.tar.gz: 4d7f5099aad1b246100297615f972a1c7aa2a223
5
5
  SHA512:
6
- metadata.gz: 6f4c748f5b46b7128b12ad568c873d9dada21b438c885ff2738d591e7c949f48d79e236c8fc0e60635885a887e0b3381703eca359d7affda37158322baecba34
7
- data.tar.gz: ebbd397052a57252725f2edd24f094532d0871de79a476bd1d43d50aabe4c38d03f4d5c28dc422ac77374ca3d7191fbe4d5f351c7691181c8de157d5133c412b
6
+ metadata.gz: 44d8de20b2f9b21afbe0985b87c76cdeabd7b125a9efda9f0b5a3870c55f5326897fbdc2e0117ef467dc5dea36a1a0577d7fbb6bfe7c2da3fee4cf67a71cfe0a
7
+ data.tar.gz: 24f300760fbe30759db3151f8be56604d1800288adc6ddccbe2279c97429e6a69fe14b2f35a181be70c07be0f650a010834abe308ebde6969559182b224be33d
@@ -8,6 +8,11 @@ GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
10
  autoloaded (2.2.1)
11
+ coderay (1.1.2)
12
+ method_source (0.9.2)
13
+ pry (0.12.2)
14
+ coderay (~> 1.1.0)
15
+ method_source (~> 0.9.0)
11
16
  rack (2.0.7)
12
17
  rake (10.5.0)
13
18
 
@@ -16,6 +21,7 @@ PLATFORMS
16
21
 
17
22
  DEPENDENCIES
18
23
  bundler (~> 2.0)
24
+ pry
19
25
  rack
20
26
  rack-healthz!
21
27
  rake (~> 10.0)
@@ -1,86 +1,61 @@
1
- require "rack/healthz/version"
1
+ require 'rack/healthz/version'
2
+
2
3
  require 'autoloaded'
3
4
  require 'json'
5
+ require 'pry'
4
6
 
5
7
  module Rack
6
8
  class Healthz
7
9
  Autoloaded.module {}
8
- class Error < StandardError;
9
- end
10
10
 
11
- def initialize(app, stats_path: "/healthz", max_time_between_requests: 3600)
12
- @app = app
13
- @count = 0
14
- @my_stats = {}
15
- @up_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
16
- @last_success_time = nil
17
- @stats_path = stats_path
18
- @max_time_between_requests = max_time_between_requests
19
- end
11
+ DefaultPath = '/healthz'
20
12
 
21
- def call(env)
22
- if env.fetch("PATH_INFO") == @stats_path
23
- now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
24
- [return_status(now), {'Content-Type' => 'application/json'}, [body_out(now)]]
25
- else
26
- @count += 1
27
- start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
28
- result = @app.call env
29
- end_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
30
- elapsed_time = end_time - start_time
31
- response_code = result.first.to_i
32
- request_category = response_code / 100
33
- if !(@my_stats.key? request_category)
34
- @my_stats[request_category] = RequestAccumulator.new
35
- end
36
- @my_stats[request_category] << elapsed_time
37
- if response_code < 400
38
- @last_success_time = end_time
39
- end
40
- result
41
- end
42
- end
13
+ DefaultTimeBetweenRequests = 3600
14
+
15
+ HealthyStatus = 200
16
+ UnhealthyStatus = 503
43
17
 
44
- def return_status(now)
45
- if healthy?(now)
46
- 200
47
- else
48
- 503
18
+ ContentType = {'Content-Type' => 'application/json'}
19
+
20
+ class << self
21
+ def current_time
22
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
49
23
  end
50
- end
51
24
 
52
- def status(now)
53
- if healthy?(now)
54
- "healthy"
55
- else
56
- "unhealthy"
25
+ def measure(&block)
26
+ start_time = current_time
27
+
28
+ block.call
29
+
30
+ current_time - start_time
57
31
  end
58
32
  end
59
33
 
60
- def healthy?(now)
61
- !time_since_last_success(now).nil? and (time_since_last_success(now) < @max_time_between_requests)
34
+ class Error < StandardError;
62
35
  end
63
36
 
64
- def time_since_last_success(now)
65
- @last_success_time ? now - @last_success_time : nil
66
- end
37
+ attr_reader :accumulator
67
38
 
68
- def uptime(now)
69
- now - @up_at
39
+ def initialize(app, **args)
40
+ @accumulator = Accumulator.new(app, **args)
70
41
  end
71
42
 
72
- def body_out(now)
73
- result = {
74
- 'count' => @count,
75
- 'uptime' => uptime(now),
76
- 'time_since_last_success' => time_since_last_success(now),
77
- 'status' => status(now), # TODO
78
- 'stats' => {},
79
- }
80
- @my_stats.each_pair do |status_category, reqest_accumulator|
81
- result['stats'][status_category.to_s + 'xx'] = reqest_accumulator.to_h
43
+ def call(env)
44
+ with_now do
45
+ if accumulator.status_request?(env)
46
+ accumulator.response.to_a
47
+ else
48
+ accumulator.call(env).to_a
49
+ end
82
50
  end
83
- result.to_json
51
+ end
52
+
53
+ def with_now
54
+ old_now = Thread.current[:now]
55
+ Thread.current[:now] = Healthz.current_time
56
+ result = yield
57
+ Thread.current[:now] = old_now
58
+ result
84
59
  end
85
60
  end
86
61
  end
@@ -0,0 +1,108 @@
1
+ module Rack
2
+ class Healthz
3
+ class Accumulator
4
+ attr_reader :count, :path, :app, :stats, :max_time_between_requests, :up_at, :last_success_time
5
+
6
+ def initialize(app, max_time_between_requests: DefaultTimeBetweenRequests, path: DefaultPath)
7
+ @app = app
8
+ @count = 0
9
+ @stats = {}
10
+ @up_at = Healthz.current_time
11
+ @last_success_time = nil
12
+ @max_time_between_requests = max_time_between_requests
13
+ @path = path
14
+ end
15
+
16
+ def call(env)
17
+ count!
18
+
19
+ response = nil
20
+
21
+ elapsed_time = Healthz.measure do
22
+ response = app.call(env)
23
+ end
24
+
25
+ handle_response! elapsed_time, Rack::Response.new(response)
26
+
27
+ response
28
+ end
29
+
30
+ def status_request?(env)
31
+ env.fetch('PATH_INFO') == path
32
+ end
33
+
34
+ def healthy?
35
+ !time_since_last_success.nil? and (time_since_last_success < max_time_between_requests)
36
+ end
37
+
38
+ def response
39
+ Response.new self
40
+ end
41
+
42
+ def time_since_last_success
43
+ last_success_time ? current_time - last_success_time : nil
44
+ end
45
+
46
+ def uptime
47
+ current_time - up_at
48
+ end
49
+
50
+ def status
51
+ if healthy?
52
+ "healthy"
53
+ else
54
+ "unhealthy"
55
+ end
56
+ end
57
+
58
+ def to_h
59
+ {}.tap do |result|
60
+ result[:count] = count
61
+ result[:uptime] = uptime
62
+ result[:time_since_last_success] = time_since_last_success
63
+ result[:status] = status
64
+ result[:stats] = stats_hash
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def stats_hash
71
+ {}.tap do |result|
72
+ stats.values.each do |stat|
73
+ result[stat.category] = stat.to_h
74
+ end
75
+ end
76
+ end
77
+
78
+ #
79
+ # Internal Commands
80
+ #
81
+ def handle_response!(elapsed_time, response)
82
+ response_category = response_category_of response
83
+
84
+ stats[response_category] ||= RequestAccumulator.new(response_category)
85
+ stats[response_category] << elapsed_time
86
+
87
+ successful! if response.successful?
88
+ end
89
+
90
+ def successful!
91
+ @last_success_time = current_time
92
+ end
93
+
94
+ def count!
95
+ @count += 1
96
+ end
97
+
98
+ #Internal Queries
99
+ def current_time
100
+ Thread.current[:now]
101
+ end
102
+
103
+ def response_category_of response
104
+ response.status / 100
105
+ end
106
+ end
107
+ end
108
+ end
@@ -2,9 +2,10 @@ module Rack
2
2
  class Healthz
3
3
  class RequestAccumulator
4
4
 
5
- attr_reader :min_time, :max_time, :count, :std, :mean
5
+ attr_reader :min_time, :max_time, :count, :std, :mean, :category
6
6
 
7
- def initialize
7
+ def initialize(category)
8
+ @category = category
8
9
  @count = 0
9
10
  @sum_of_times = 0.0
10
11
  @sum_of_square_times = 0.0
@@ -19,6 +20,7 @@ module Rack
19
20
  @min_time = [@min_time, elapsed_time].min
20
21
  @max_time = [@max_time, elapsed_time].max
21
22
  @mean = @sum_of_times / @count
23
+
22
24
  @std = if @count > 1
23
25
  Math.sqrt((@sum_of_square_times / @count) - (@sum_of_times / @count) ** 2)
24
26
  elsif @count == 1
@@ -0,0 +1,39 @@
1
+ module Rack
2
+ class Healthz
3
+ class Response
4
+ attr_reader :accumulator
5
+
6
+ def initialize(accumulator)
7
+ @accumulator = accumulator
8
+ end
9
+
10
+ def to_a
11
+ [return_status, content_type, [to_json]]
12
+ end
13
+
14
+ def to_json
15
+ to_h.to_json
16
+ end
17
+
18
+ def to_h
19
+ accumulator.to_h
20
+ end
21
+
22
+ def content_type
23
+ ContentType
24
+ end
25
+
26
+ def healthy?
27
+ accumulator.healthy?
28
+ end
29
+
30
+ def return_status
31
+ if healthy?
32
+ HealthyStatus
33
+ else
34
+ UnhealthyStatus
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,5 +1,5 @@
1
1
  module Rack
2
2
  class Healthz
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency "bundler", "~> 2.0"
39
39
  spec.add_development_dependency "rake", "~> 10.0"
40
40
  spec.add_development_dependency "rack"
41
+ spec.add_development_dependency "pry"
41
42
 
42
43
  spec.add_runtime_dependency "autoloaded"
43
44
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-healthz
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Eberbach
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-07-12 00:00:00.000000000 Z
12
+ date: 2019-07-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: autoloaded
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -84,7 +98,9 @@ files:
84
98
  - bin/setup
85
99
  - config.ru
86
100
  - lib/rack/healthz.rb
101
+ - lib/rack/healthz/accumulator.rb
87
102
  - lib/rack/healthz/request_accumulator.rb
103
+ - lib/rack/healthz/response.rb
88
104
  - lib/rack/healthz/version.rb
89
105
  - rack-healthz.gemspec
90
106
  homepage: