coverband-service-client 0.0.11 → 0.0.12.rc.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
  SHA256:
3
- metadata.gz: a0d326e9f18a89a72e6a251e92b1a2c4d99a44288c1e695570327675017e5328
4
- data.tar.gz: 83efe304d0dac5797b63ad4dea23516df89ab211a0aa5809554c9b3fce5dfa82
3
+ metadata.gz: f8bd1387ac9c907dceb9a215b957d77bd30021615658f95163e627cd81239564
4
+ data.tar.gz: 303f7fa7b317f8b97bbb50f8a4f526c6e6bbf6860ef934fd3f23966b6f0611e9
5
5
  SHA512:
6
- metadata.gz: 3588eb3a0fc1c70afa925b61feaf92ee95b7169476e0008abac2ee83413a7ad0a02726876fda490089fb2008d17861f71cebd297dc78744de049fc62a82c2022
7
- data.tar.gz: ee29788c3d877f9cd0079615c30da437490cc3a4c6fb74ed7a0454d44b405ef041965c648b4e3d5162f775c13d8661165c578fa57993de090d39be253f34a139
6
+ metadata.gz: 5be62bec964696cb38174cc21b1422335621c6745befe4b60d1ca62375ab558db60c161175bf1049f3cb824429fbeac256350c0781bc867c67b45941b644a4fa
7
+ data.tar.gz: 1c3dc9904513616b21bc86d051905663603ebc816683c11cad86d9a60cca187f38a3cf5bda5c3b15c49bd390688adecd7049f2f9fc9a637609d63f7861ab36b8
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- coverband-service-client (0.0.11)
4
+ coverband-service-client (0.0.12.rc.4)
5
5
  coverband (~> 4.2.4)
6
6
 
7
7
  GEM
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+
5
+ ENV['COVERBAND_ENABLE_DEV_MODE'] = 'true'
6
+ ENV['COVERBAND_ENABLE_TEST_MODE'] = 'true'
7
+ ENV['PROCESS_TYPE'] = 'debug'
8
+ ENV['COVERBAND_REPORT_PERIOD'] = '30'
9
+ ENV['COVERBAND_API_KEY'] ||= 'set this'
10
+ ENV['COVERBAND_STATS_KEY'] ||='set this'
11
+
12
+ require "pry-byebug";
13
+ require "dogapi";
14
+ require "net/http/persistent";
15
+ # require "httplog";
16
+ require 'benchmark'
17
+
18
+ require_relative "../lib/coverband-service-client"
19
+
20
+ # HttpLog.configure do |config|
21
+ # config.url_SAFElist_pattern = /coverband/
22
+ # end
23
+
24
+ data = {
25
+ 'app/helpers/posts_helper.rb' => [1, nil]
26
+ }
27
+
28
+ collector = Coverband::Collectors::Coverage.instance
29
+ store = Coverband.configuration.store
30
+
31
+ # What is the recommended timeout against the target, from the lib
32
+ # puts store.recommended_timeout
33
+
34
+ Benchmark.bmbm do |x|
35
+ x.report("connection") do
36
+ 30.times do
37
+ store.save_report(data)
38
+ end
39
+ end
40
+ end
41
+
42
+ puts "done"
@@ -28,6 +28,14 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_development_dependency "bundler", "~> 2.0"
30
30
  spec.add_development_dependency "rake", "~> 13.0"
31
+
32
+ # For benchmarking stats
33
+ # spec.add_development_dependency "pry-byebug"
34
+ # spec.add_development_dependency "dogapi"
35
+ # spec.add_development_dependency "httplog"
36
+ # # to benchmark persistent connections
37
+ # spec.add_development_dependency "net-http-persistent"
38
+
31
39
  spec.add_development_dependency "minitest", "~> 5.0"
32
40
  spec.add_runtime_dependency "coverband", "~> 4.2.4"
33
41
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'socket'
2
3
 
3
4
  COVERBAND_ORIGINAL_START = ENV['COVERBAND_DISABLE_AUTO_START']
4
5
  ENV['COVERBAND_DISABLE_AUTO_START'] = 'true'
@@ -9,11 +10,12 @@ require 'securerandom'
9
10
  module Coverband
10
11
  COVERBAND_ENV = ENV['RACK_ENV'] || ENV['RAILS_ENV'] || (defined?(Rails) ? Rails.env : 'unknown')
11
12
  COVERBAND_SERVICE_URL = ENV['COVERBAND_URL'] || 'https://coverband.io'
12
- COVERBAND_TIMEOUT = (COVERBAND_ENV == 'development') ? 5 : 1
13
+ COVERBAND_TIMEOUT = (COVERBAND_ENV == 'development') ? 5 : 2
13
14
  COVERBAND_ENABLE_DEV_MODE = ENV['COVERBAND_ENABLE_DEV_MODE'] || false
14
15
  COVERBAND_ENABLE_TEST_MODE = ENV['COVERBAND_ENABLE_TEST_MODE'] || false
15
16
  COVERBAND_PROCESS_TYPE = ENV['PROCESS_TYPE'] || 'unknown'
16
17
  COVERBAND_REPORT_PERIOD = (ENV['COVERBAND_REPORT_PERIOD'] || 600).to_i
18
+ COVERBAND_PERSISTENT_HTTP = ENV['COVERBAND_PERSISTENT_HTTP'] || false
17
19
 
18
20
  def self.service_disabled_dev_test_env?
19
21
  (COVERBAND_ENV == 'test' && !COVERBAND_ENABLE_TEST_MODE) ||
@@ -35,16 +37,39 @@ module Coverband
35
37
  #
36
38
  # NOTES:
37
39
  # * uses net/http to avoid any dependencies
38
- # * currently JSON, but likely better to move to something simpler / faster
40
+ # * currently JSON, but likely better to move to something faster
39
41
  ###
40
42
  class Service < Base
41
- attr_reader :coverband_url, :process_type, :runtime_env
43
+ attr_reader :coverband_url, :process_type, :runtime_env, :hostname, :pid, :stats
42
44
 
43
45
  def initialize(coverband_url, opts = {})
44
46
  super()
45
47
  @coverband_url = coverband_url
46
- @process_type = opts.fetch(:process_type) { COVERBAND_PROCESS_TYPE }
48
+ @process_type = opts.fetch(:process_type) { $PROGRAM_NAME&.split('/')&.last || COVERBAND_PROCESS_TYPE }
49
+ @hostname = opts.fetch(:hostname) { ENV["DYNO"] || Socket.gethostname.force_encoding('utf-8').encode }
50
+ @hostname = @hostname.gsub("'",'').gsub("’",'')
47
51
  @runtime_env = opts.fetch(:runtime_env) { COVERBAND_ENV }
52
+ @failed_coverage_reports = []
53
+ initialize_stats
54
+ end
55
+
56
+ def initialize_stats
57
+ return unless ENV['COVERBAND_STATS_KEY']
58
+ return unless defined?(Dogapi::Client)
59
+
60
+ @stats = Dogapi::Client.new(ENV['COVERBAND_STATS_KEY'])
61
+ @app_name = defined?(Rails) ? Rails.application.class.module_parent.to_s : "unknown"
62
+ end
63
+
64
+ def report_timing(timing)
65
+ return unless @stats
66
+
67
+ @stats.emit_point(
68
+ 'coverband.save.time',
69
+ timing,
70
+ host: hostname,
71
+ device: "coverband_#{self.class.name.split("::").last}",
72
+ options: {tags: [runtime_env]})
48
73
  end
49
74
 
50
75
  def logger
@@ -68,6 +93,9 @@ module Coverband
68
93
  ENV['COVERBAND_API_KEY'] || Coverband.configuration.api_key
69
94
  end
70
95
 
96
+ ###
97
+ # Fetch coverband coverage via the API
98
+ ###
71
99
  def coverage(local_type = nil, opts = {})
72
100
  local_type ||= opts.key?(:override_type) ? opts[:override_type] : type
73
101
  env_filter = opts.key?(:env_filter) ? opts[:env_filter] : 'production'
@@ -85,8 +113,11 @@ module Coverband
85
113
  def save_report(report)
86
114
  return if report.empty?
87
115
 
116
+ # We set here vs initialize to avoid setting on the primary process vs child processes
117
+ @pid ||= ::Process.pid
118
+
88
119
  # TODO: do we need dup
89
- # TODO: remove timestamps, server will track first_seen
120
+ # TODO: remove upstream timestamps, server will track first_seen
90
121
  Thread.new do
91
122
  data = expand_report(report.dup)
92
123
  full_package = {
@@ -95,12 +126,19 @@ module Coverband
95
126
  tags: {
96
127
  process_type: process_type,
97
128
  app_loading: type == Coverband::EAGER_TYPE,
98
- runtime_env: runtime_env
129
+ runtime_env: runtime_env,
130
+ pid: pid,
131
+ hostname: hostname,
99
132
  },
100
133
  file_coverage: data
101
134
  }
102
135
  }
136
+
137
+ starting = Process.clock_gettime(Process::CLOCK_MONOTONIC) if @stats
103
138
  save_coverage(full_package)
139
+ ending = Process.clock_gettime(Process::CLOCK_MONOTONIC) if @stats
140
+ report_timing((ending - starting)) if @stats
141
+ retry_failed_reports
104
142
  end&.join
105
143
  end
106
144
 
@@ -110,19 +148,48 @@ module Coverband
110
148
 
111
149
  private
112
150
 
151
+ def retry_failed_reports
152
+ retries = []
153
+ @failed_coverage_reports.any? do
154
+ report_body = arr.pop
155
+ send_report_body(report_body)
156
+ rescue StandardError
157
+ retries << report_body
158
+ end
159
+ retries.each do |report_body|
160
+ add_retry_message(report_body)
161
+ end
162
+ end
163
+
164
+ def add_retry_message(report_body)
165
+ if @failed_coverage_reports.length > 5
166
+ logger&.info "Coverband: The errored reporting queue has reached 5. Subsequent reports will not be transmitted"
167
+ else
168
+ @failed_coverage_reports << report_body
169
+ end
170
+ end
171
+
113
172
  def save_coverage(data)
114
173
  if api_key.nil?
115
174
  puts "Coverband: Error: no Coverband API key was found!"
175
+ return
116
176
  end
117
177
 
178
+ coverage_body = { remote_uuid: SecureRandom.uuid, data: data }.to_json
179
+ send_report_body(coverage_body)
180
+ rescue StandardError => e
181
+ add_retry_message(coverage_body)
182
+ logger&.info "Coverband: Error while saving coverage #{e}" if Coverband.configuration.verbose || COVERBAND_ENABLE_DEV_MODE
183
+ end
184
+
185
+ def send_report_body(coverage_body)
118
186
  uri = URI("#{coverband_url}/api/collector")
119
- req = Net::HTTP::Post.new(uri,
187
+ req = ::Net::HTTP::Post.new(uri,
120
188
  'Content-Type' => 'application/json',
121
189
  'Coverband-Token' => api_key)
122
- req.body = { remote_uuid: SecureRandom.uuid, data: data }.to_json
123
-
190
+ req.body = coverage_body
124
191
  logger&.info "Coverband: saving (#{uri}) #{req.body}" if Coverband.configuration.verbose
125
- res = Net::HTTP.start(
192
+ res = ::Net::HTTP.start(
126
193
  uri.hostname,
127
194
  uri.port,
128
195
  open_timeout: COVERBAND_TIMEOUT,
@@ -132,6 +199,61 @@ module Coverband
132
199
  ) do |http|
133
200
  http.request(req)
134
201
  end
202
+ if res.code.to_i >= 500
203
+ add_retry_message(coverage_body)
204
+ end
205
+ end
206
+ end
207
+
208
+ class PersistentService < Service
209
+ attr_reader :http, :stats
210
+
211
+ def initialize(coverband_url, opts = {})
212
+ super
213
+ initiate_http
214
+ end
215
+
216
+ def recommended_timeout
217
+ puts Net::HTTP::Persistent.detect_idle_timeout URI("#{coverband_url}/api/collector")
218
+ end
219
+
220
+ private
221
+
222
+ def initiate_http
223
+ @http = Net::HTTP::Persistent.new name: 'coverband_persistent'
224
+ @http.headers['Content-Type'] = 'application/json'
225
+ @http.headers['Coverband-Token'] = api_key
226
+ @http.open_timeout = COVERBAND_TIMEOUT
227
+ @http.read_timeout = COVERBAND_TIMEOUT
228
+ # the two below seem inconsistent in terms of how they are set
229
+ # leaving off for now
230
+ # @http.ssl_timeout = COVERBAND_TIMEOUT
231
+ # @http.write_timeout = COVERBAND_TIMEOUT
232
+ # default is 5-10 seconds but we report ever few min, heroku kills them
233
+ # before our reporting period... ;(
234
+ # @http.idle_timeout = 1000
235
+ end
236
+
237
+ def save_coverage(data)
238
+ persistent_attempts = 0
239
+ begin
240
+ if api_key.nil?
241
+ puts "Coverband: Error: no Coverband API key was found!"
242
+ return
243
+ end
244
+
245
+ post_uri = URI("#{coverband_url}/api/collector")
246
+ post = Net::HTTP::Post.new post_uri.path
247
+ body = { remote_uuid: SecureRandom.uuid, data: data }.to_json
248
+ post.body = body
249
+ logger&.info "Coverband: saving (#{post_uri}) #{body}" if Coverband.configuration.verbose
250
+ res = http.request post_uri, post
251
+ rescue Net::HTTP::Persistent::Error => e
252
+ persistent_attempts += 1
253
+ http.shutdown
254
+ initiate_http
255
+ retry if persistent_attempts < 2
256
+ end
135
257
  rescue StandardError => e
136
258
  logger&.info "Coverband: Error while saving coverage #{e}" if Coverband.configuration.verbose || COVERBAND_ENABLE_DEV_MODE
137
259
  end
@@ -213,8 +335,12 @@ end
213
335
 
214
336
  ENV['COVERBAND_DISABLE_AUTO_START'] = COVERBAND_ORIGINAL_START
215
337
  Coverband.configure do |config|
216
- # Use The Test Service Adapter
217
- config.store = Coverband::Adapters::Service.new(Coverband::COVERBAND_SERVICE_URL)
338
+ # Use the Service Adapter
339
+ if Coverband::COVERBAND_PERSISTENT_HTTP && defined?(Net::HTTP::Persistent)
340
+ config.store = Coverband::Adapters::PersistentService.new(Coverband::COVERBAND_SERVICE_URL)
341
+ else
342
+ config.store = Coverband::Adapters::Service.new(Coverband::COVERBAND_SERVICE_URL)
343
+ end
218
344
 
219
345
  # default to tracking views true
220
346
  config.track_views = if ENV['COVERBAND_DISABLE_VIEW_TRACKER']
@@ -1,7 +1,7 @@
1
1
  module Coverband
2
2
  module Service
3
3
  module Client
4
- VERSION = '0.0.11'
4
+ VERSION = '0.0.12.rc.4'
5
5
  end
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coverband-service-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12.rc.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Mayer
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2020-05-26 00:00:00.000000000 Z
12
+ date: 2020-06-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -84,6 +84,7 @@ files:
84
84
  - Rakefile
85
85
  - bin/console
86
86
  - bin/setup
87
+ - bin/stats
87
88
  - changelog.md
88
89
  - coverband-service-client.gemspec
89
90
  - lib/coverband-service-client.rb
@@ -106,9 +107,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
106
107
  version: '0'
107
108
  required_rubygems_version: !ruby/object:Gem::Requirement
108
109
  requirements:
109
- - - ">="
110
+ - - ">"
110
111
  - !ruby/object:Gem::Version
111
- version: '0'
112
+ version: 1.3.1
112
113
  requirements: []
113
114
  rubygems_version: 3.0.3
114
115
  signing_key: