statsd-instrument 2.3.5 → 2.4.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 +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/workflows/ci.yml +31 -0
- data/.gitignore +1 -0
- data/.rubocop-https---shopify-github-io-ruby-style-guide-rubocop-yml +1027 -0
- data/.rubocop.yml +21 -0
- data/CHANGELOG.md +9 -0
- data/CONTRIBUTING.md +25 -5
- data/Gemfile +2 -0
- data/Rakefile +3 -1
- data/lib/statsd-instrument.rb +2 -0
- data/lib/statsd/instrument.rb +51 -18
- data/lib/statsd/instrument/assertions.rb +24 -18
- data/lib/statsd/instrument/backend.rb +3 -2
- data/lib/statsd/instrument/backends/capture_backend.rb +2 -1
- data/lib/statsd/instrument/backends/logger_backend.rb +3 -3
- data/lib/statsd/instrument/backends/null_backend.rb +2 -0
- data/lib/statsd/instrument/backends/udp_backend.rb +18 -15
- data/lib/statsd/instrument/environment.rb +2 -0
- data/lib/statsd/instrument/helpers.rb +6 -2
- data/lib/statsd/instrument/matchers.rb +14 -11
- data/lib/statsd/instrument/metric.rb +34 -21
- data/lib/statsd/instrument/metric_expectation.rb +32 -18
- data/lib/statsd/instrument/railtie.rb +2 -1
- data/lib/statsd/instrument/version.rb +3 -1
- data/statsd-instrument.gemspec +13 -10
- data/test/assertions_test.rb +15 -4
- data/test/benchmark/default_tags.rb +47 -0
- data/test/benchmark/metrics.rb +9 -8
- data/test/benchmark/tags.rb +5 -3
- data/test/capture_backend_test.rb +4 -2
- data/test/environment_test.rb +2 -1
- data/test/helpers_test.rb +2 -1
- data/test/integration_test.rb +27 -7
- data/test/logger_backend_test.rb +10 -8
- data/test/matchers_test.rb +34 -20
- data/test/metric_test.rb +15 -4
- data/test/statsd_instrumentation_test.rb +7 -7
- data/test/statsd_test.rb +24 -15
- data/test/test_helper.rb +2 -0
- data/test/udp_backend_test.rb +3 -26
- metadata +22 -3
- data/.travis.yml +0 -12
data/.rubocop.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
inherit_from:
|
2
|
+
- https://shopify.github.io/ruby-style-guide/rubocop.yml
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
TargetRubyVersion: 2.3
|
6
|
+
UseCache: true
|
7
|
+
CacheRootDirectory: tmp/rubocop
|
8
|
+
Exclude:
|
9
|
+
- statsd-instrument.gemspec
|
10
|
+
|
11
|
+
Naming/FileName:
|
12
|
+
Enabled: true
|
13
|
+
Exclude:
|
14
|
+
- lib/statsd-instrument.rb
|
15
|
+
|
16
|
+
Style/ClassAndModuleChildren:
|
17
|
+
Enabled: false # TODO: enable later
|
18
|
+
|
19
|
+
|
20
|
+
Style/MethodCallWithArgsParentheses:
|
21
|
+
Enabled: false # TODO: enable later
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,15 @@ please at an entry to the "unreleased changes" section below.
|
|
5
5
|
|
6
6
|
### Unreleased changes
|
7
7
|
|
8
|
+
## Version 2.4.0
|
9
|
+
|
10
|
+
- Add `StatsD.default_tags` to specify tags that should be included in all metrics. (#159)
|
11
|
+
- Improve assertion message when assertying metrics whose tags do not match. (#100)
|
12
|
+
- Enforce the Shopify Ruby style guide. (#164)
|
13
|
+
- Migrate CI to Github actions. (#158)
|
14
|
+
- Make the library frozen string literal-compatible. (#161, #163)
|
15
|
+
- Fix all Ruby warnings. (#162)
|
16
|
+
|
8
17
|
## Version 2.3.5
|
9
18
|
|
10
19
|
- Re-add `StatsD::Instrument.duration`, which was accidentally removed since verison 2.5.3 (#157)
|
data/CONTRIBUTING.md
CHANGED
@@ -17,21 +17,41 @@ When reporting issues, please include the following information:
|
|
17
17
|
- The statsd-instrument version. **Note:** only the latest version is supported.
|
18
18
|
- The StatsD backend you are using.
|
19
19
|
|
20
|
-
##
|
20
|
+
## Opening pull requests
|
21
21
|
|
22
22
|
1. Fork the repository, and create a branch.
|
23
23
|
2. Implement the feature or bugfix, and add tests that cover the changed functionality.
|
24
|
-
3. Create a pull request. Make sure that you get
|
24
|
+
3. Create a pull request. Make sure that you get a green CI status on your commit.
|
25
25
|
|
26
26
|
Some notes:
|
27
27
|
|
28
|
-
- Make sure to follow to coding style.
|
28
|
+
- Make sure to follow to coding style. This is enforced by Rubocop
|
29
29
|
- Make sure your changes are properly documented using [yardoc syntax](http://www.rubydoc.info/gems/yard/file/docs/GettingStarted.md).
|
30
30
|
- Add an entry to the "unreleased changes" section of [CHANGELOG.md](./CHANGELOG.md).
|
31
31
|
- **Do not** update `StatsD::Instrument::VERSION`. This will be done during the release prodecure.
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
### On perfomance & benchmarking
|
34
|
+
|
35
|
+
This gem is used in production at Shopify, and is used to instrument some of
|
36
|
+
our hottest code paths. This means that we are very careful about not
|
37
|
+
introducing performance regressions in this library.
|
38
|
+
|
39
|
+
**Important:** Whenever you make changes to the metric emission code path in this library,
|
40
|
+
you **must** include benchmark results to show the impact of your changes.
|
41
|
+
|
42
|
+
The `test/benchmark/` folder contains some example benchmark script that you
|
43
|
+
can use, or can serve as a starting point. Please run your benchmark on your
|
44
|
+
pull request revision, as well as the latest revision on `master`.
|
45
|
+
|
46
|
+
### On backwards compatibility
|
47
|
+
|
48
|
+
Shopify's codebases are heavily instrumented using this library. As a result, we cannot
|
49
|
+
accept changes that are backwards incompatible:
|
50
|
+
|
51
|
+
- Changes that will require us to update our codebases.
|
52
|
+
- Changes that will cause metrics emitted by this library to change in form or shape.
|
53
|
+
|
54
|
+
This means that we may not be able to accept fixes for what you consider a bug.
|
35
55
|
|
36
56
|
## Release procedure
|
37
57
|
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
data/lib/statsd-instrument.rb
CHANGED
data/lib/statsd/instrument.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'socket'
|
2
4
|
require 'logger'
|
3
5
|
|
@@ -28,6 +30,10 @@ require 'logger'
|
|
28
30
|
# The logger to use in case of any errors. The logger is also used as default logger
|
29
31
|
# for the LoggerBackend (although this can be overwritten).
|
30
32
|
#
|
33
|
+
# @!attribute default_tags
|
34
|
+
# The tags to apply to all metrics.
|
35
|
+
# @return [Array<String>, Hash<String, String>, nil] The default tags, or <tt>nil</tt> when no default tags is used
|
36
|
+
#
|
31
37
|
# @see StatsD::Instrument::Backends::LoggerBackend
|
32
38
|
# @return [Logger]
|
33
39
|
#
|
@@ -88,7 +94,8 @@ module StatsD
|
|
88
94
|
def statsd_measure(method, name, *metric_options)
|
89
95
|
add_to_method(method, name, :measure) do
|
90
96
|
define_method(method) do |*args, &block|
|
91
|
-
StatsD
|
97
|
+
metric_name = StatsD::Instrument.generate_metric_name(name, self, *args)
|
98
|
+
StatsD.measure(metric_name, *metric_options) { super(*args, &block) }
|
92
99
|
end
|
93
100
|
end
|
94
101
|
end
|
@@ -104,7 +111,8 @@ module StatsD
|
|
104
111
|
def statsd_distribution(method, name, *metric_options)
|
105
112
|
add_to_method(method, name, :distribution) do
|
106
113
|
define_method(method) do |*args, &block|
|
107
|
-
StatsD
|
114
|
+
metric_name = StatsD::Instrument.generate_metric_name(name, self, *args)
|
115
|
+
StatsD.distribution(metric_name, *metric_options) { super(*args, &block) }
|
108
116
|
end
|
109
117
|
end
|
110
118
|
end
|
@@ -133,20 +141,27 @@ module StatsD
|
|
133
141
|
truthiness = false
|
134
142
|
raise
|
135
143
|
else
|
136
|
-
|
144
|
+
if block_given?
|
145
|
+
begin
|
146
|
+
truthiness = yield(result)
|
147
|
+
rescue
|
148
|
+
truthiness = false
|
149
|
+
end
|
150
|
+
end
|
137
151
|
result
|
138
152
|
ensure
|
139
153
|
suffix = truthiness == false ? 'failure' : 'success'
|
140
|
-
|
154
|
+
metric_name = "#{StatsD::Instrument.generate_metric_name(name, self, *args)}.#{suffix}"
|
155
|
+
StatsD.increment(metric_name, 1, *metric_options)
|
141
156
|
end
|
142
157
|
end
|
143
158
|
end
|
144
159
|
end
|
145
160
|
|
146
|
-
# Adds success
|
161
|
+
# Adds success counter instrumentation to a method.
|
147
162
|
#
|
148
163
|
# A method call will be considered successful if it does not raise an exception, and the result is true-y.
|
149
|
-
# Only for successful calls, the metric will be
|
164
|
+
# Only for successful calls, the metric will be incremented.
|
150
165
|
#
|
151
166
|
# @param method (see #statsd_measure)
|
152
167
|
# @param name (see #statsd_measure)
|
@@ -165,10 +180,19 @@ module StatsD
|
|
165
180
|
truthiness = false
|
166
181
|
raise
|
167
182
|
else
|
168
|
-
|
183
|
+
if block_given?
|
184
|
+
begin
|
185
|
+
truthiness = yield(result)
|
186
|
+
rescue
|
187
|
+
truthiness = false
|
188
|
+
end
|
189
|
+
end
|
169
190
|
result
|
170
191
|
ensure
|
171
|
-
|
192
|
+
if truthiness
|
193
|
+
metric_name = StatsD::Instrument.generate_metric_name(name, self, *args)
|
194
|
+
StatsD.increment(metric_name, *metric_options)
|
195
|
+
end
|
172
196
|
end
|
173
197
|
end
|
174
198
|
end
|
@@ -186,7 +210,8 @@ module StatsD
|
|
186
210
|
def statsd_count(method, name, *metric_options)
|
187
211
|
add_to_method(method, name, :count) do
|
188
212
|
define_method(method) do |*args, &block|
|
189
|
-
StatsD
|
213
|
+
metric_name = StatsD::Instrument.generate_metric_name(name, self, *args)
|
214
|
+
StatsD.increment(metric_name, 1, *metric_options)
|
190
215
|
super(*args, &block)
|
191
216
|
end
|
192
217
|
end
|
@@ -254,8 +279,13 @@ module StatsD
|
|
254
279
|
def add_to_method(method, name, action, &block)
|
255
280
|
instrumentation_module = statsd_instrumentation_for(method, name, action)
|
256
281
|
|
257
|
-
|
258
|
-
|
282
|
+
if instrumentation_module.method_defined?(method)
|
283
|
+
raise ArgumentError, "Already instrumented #{method} for #{self.name}"
|
284
|
+
end
|
285
|
+
|
286
|
+
unless method_defined?(method) || private_method_defined?(method)
|
287
|
+
raise ArgumentError, "could not find method #{method} for #{self.name}"
|
288
|
+
end
|
259
289
|
|
260
290
|
method_scope = method_visibility(method)
|
261
291
|
|
@@ -269,10 +299,9 @@ module StatsD
|
|
269
299
|
end
|
270
300
|
|
271
301
|
def method_visibility(method)
|
272
|
-
|
273
|
-
when private_method_defined?(method)
|
302
|
+
if private_method_defined?(method)
|
274
303
|
:private
|
275
|
-
|
304
|
+
elsif protected_method_defined?(method)
|
276
305
|
:protected
|
277
306
|
else
|
278
307
|
:public
|
@@ -282,6 +311,11 @@ module StatsD
|
|
282
311
|
|
283
312
|
attr_accessor :logger, :default_sample_rate, :prefix
|
284
313
|
attr_writer :backend
|
314
|
+
attr_reader :default_tags
|
315
|
+
|
316
|
+
def default_tags=(tags)
|
317
|
+
@default_tags = StatsD::Instrument::Metric.normalize_tags(tags)
|
318
|
+
end
|
285
319
|
|
286
320
|
def backend
|
287
321
|
@backend ||= StatsD::Instrument::Environment.default_backend
|
@@ -382,7 +416,7 @@ module StatsD
|
|
382
416
|
# http_response = StatsD.distribution('HTTP.call.duration') do
|
383
417
|
# HTTP.get(url)
|
384
418
|
# end
|
385
|
-
def distribution(key, value=nil, *metric_options, &block)
|
419
|
+
def distribution(key, value = nil, *metric_options, &block)
|
386
420
|
value, metric_options = parse_options(value, metric_options)
|
387
421
|
|
388
422
|
return collect_metric(:d, key, value, metric_options) unless block_given?
|
@@ -442,7 +476,7 @@ module StatsD
|
|
442
476
|
# @param args [Array] The list of non-required arguments.
|
443
477
|
# @return [Hash] The hash of optional arguments.
|
444
478
|
def hash_argument(args)
|
445
|
-
return {} if args.
|
479
|
+
return {} if args.empty?
|
446
480
|
return args.first if args.length == 1 && args.first.is_a?(Hash)
|
447
481
|
|
448
482
|
order = [:sample_rate, :tags]
|
@@ -450,8 +484,7 @@ module StatsD
|
|
450
484
|
args.each_with_index do |value, index|
|
451
485
|
hash[order[index]] = value
|
452
486
|
end
|
453
|
-
|
454
|
-
return hash
|
487
|
+
hash
|
455
488
|
end
|
456
489
|
|
457
490
|
def parse_options(value, metric_options)
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module StatsD::Instrument::Assertions
|
2
4
|
include StatsD::Instrument::Helpers
|
3
5
|
|
4
6
|
def assert_no_statsd_calls(metric_name = nil, &block)
|
5
7
|
metrics = capture_statsd_calls(&block)
|
6
8
|
metrics.select! { |m| m.name == metric_name } if metric_name
|
7
|
-
assert
|
9
|
+
assert(metrics.empty?, "No StatsD calls for metric #{metrics.map(&:name).join(', ')} expected.")
|
8
10
|
end
|
9
11
|
|
10
12
|
def assert_statsd_increment(metric_name, options = {}, &block)
|
@@ -48,32 +50,36 @@ module StatsD::Instrument::Assertions
|
|
48
50
|
expected_metric_times = expected_metric.times
|
49
51
|
expected_metric_times_remaining = expected_metric.times
|
50
52
|
filtered_metrics = metrics.select { |m| m.type == expected_metric.type && m.name == expected_metric.name }
|
51
|
-
|
52
|
-
"No StatsD calls for metric #{expected_metric.name} of type #{expected_metric.type} were made."
|
53
|
+
refute(filtered_metrics.empty?,
|
54
|
+
"No StatsD calls for metric #{expected_metric.name} of type #{expected_metric.type} were made.")
|
53
55
|
|
54
56
|
filtered_metrics.each do |metric|
|
57
|
+
next unless expected_metric.matches(metric)
|
58
|
+
|
55
59
|
assert within_numeric_range?(metric.sample_rate),
|
56
60
|
"Unexpected sample rate type for metric #{metric.name}, must be numeric"
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
|
62
|
+
assert(expected_metric_times_remaining > 0,
|
63
|
+
"Unexpected StatsD call; number of times this metric was expected exceeded: #{expected_metric.inspect}")
|
64
|
+
|
65
|
+
expected_metric_times_remaining -= 1
|
66
|
+
metrics.delete(metric)
|
67
|
+
if expected_metric_times_remaining == 0
|
68
|
+
matched_expected_metrics << expected_metric
|
65
69
|
end
|
66
70
|
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
msg = +"Metric expected #{expected_metric_times} times but seen " \
|
73
|
+
"#{expected_metric_times - expected_metric_times_remaining} " \
|
74
|
+
"times: #{expected_metric.inspect}."
|
75
|
+
msg << "\nCaptured metrics with the same key: #{filtered_metrics}" if filtered_metrics.any?
|
76
|
+
|
77
|
+
assert(expected_metric_times_remaining == 0, msg)
|
72
78
|
end
|
73
79
|
expected_metrics -= matched_expected_metrics
|
74
80
|
|
75
|
-
assert
|
76
|
-
"Unexpected StatsD calls; the following metric expectations were not satisfied: #{expected_metrics.inspect}"
|
81
|
+
assert(expected_metrics.empty?,
|
82
|
+
"Unexpected StatsD calls; the following metric expectations were not satisfied: #{expected_metrics.inspect}")
|
77
83
|
end
|
78
84
|
|
79
85
|
private
|
@@ -87,6 +93,6 @@ module StatsD::Instrument::Assertions
|
|
87
93
|
end
|
88
94
|
|
89
95
|
def within_numeric_range?(object)
|
90
|
-
object.
|
96
|
+
object.is_a?(Numeric) && (0.0..1.0).cover?(object)
|
91
97
|
end
|
92
98
|
end
|
@@ -1,12 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This abstract class specifies the interface a backend implementation should conform to.
|
2
4
|
# @abstract
|
3
5
|
class StatsD::Instrument::Backend
|
4
|
-
|
5
6
|
# Collects a metric.
|
6
7
|
#
|
7
8
|
# @param metric [StatsD::Instrument::Metric] The metric to collect
|
8
9
|
# @return [void]
|
9
|
-
def collect_metric(
|
10
|
+
def collect_metric(_metric)
|
10
11
|
raise NotImplementedError, "Use a concerete backend implementation"
|
11
12
|
end
|
12
13
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module StatsD::Instrument::Backends
|
3
4
|
# The logger backend simply logs every metric to a logger
|
4
5
|
# @!attribute logger
|
5
6
|
# @return [Logger]
|
6
7
|
class LoggerBackend < StatsD::Instrument::Backend
|
7
|
-
|
8
8
|
attr_accessor :logger
|
9
9
|
|
10
10
|
def initialize(logger)
|
@@ -14,7 +14,7 @@ module StatsD::Instrument::Backends
|
|
14
14
|
# @param metric [StatsD::Instrument::Metric]
|
15
15
|
# @return [void]
|
16
16
|
def collect_metric(metric)
|
17
|
-
logger.info
|
17
|
+
logger.info("[StatsD] #{metric}")
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'monitor'
|
2
4
|
|
3
5
|
module StatsD::Instrument::Backends
|
4
6
|
class UDPBackend < StatsD::Instrument::Backend
|
5
|
-
|
6
7
|
BASE_SUPPORTED_METRIC_TYPES = { c: true, ms: true, g: true, s: true }
|
7
8
|
|
8
9
|
class DogStatsDProtocol
|
@@ -24,7 +25,7 @@ module StatsD::Instrument::Backends
|
|
24
25
|
SUPPORTED_METRIC_TYPES = BASE_SUPPORTED_METRIC_TYPES.merge(h: true, _e: true, _sc: true, d: true)
|
25
26
|
|
26
27
|
def generate_packet(metric)
|
27
|
-
packet = ""
|
28
|
+
packet = +""
|
28
29
|
|
29
30
|
if metric.type == :_e
|
30
31
|
escaped_title = metric.name.gsub("\n", "\\n")
|
@@ -41,6 +42,7 @@ module StatsD::Instrument::Backends
|
|
41
42
|
|
42
43
|
packet << "|@#{metric.sample_rate}" if metric.sample_rate < 1
|
43
44
|
packet << "|##{metric.tags.join(',')}" if metric.tags
|
45
|
+
|
44
46
|
packet
|
45
47
|
end
|
46
48
|
|
@@ -57,7 +59,7 @@ module StatsD::Instrument::Backends
|
|
57
59
|
SUPPORTED_METRIC_TYPES = BASE_SUPPORTED_METRIC_TYPES.merge(kv: true)
|
58
60
|
|
59
61
|
def generate_packet(metric)
|
60
|
-
packet = "#{metric.name}:#{metric.value}|#{metric.type}"
|
62
|
+
packet = +"#{metric.name}:#{metric.value}|#{metric.type}"
|
61
63
|
packet << "|@#{metric.sample_rate}" unless metric.sample_rate == 1
|
62
64
|
packet << "\n"
|
63
65
|
packet
|
@@ -68,7 +70,7 @@ module StatsD::Instrument::Backends
|
|
68
70
|
SUPPORTED_METRIC_TYPES = BASE_SUPPORTED_METRIC_TYPES
|
69
71
|
|
70
72
|
def generate_packet(metric)
|
71
|
-
packet = "#{metric.name}:#{metric.value}|#{metric.type}"
|
73
|
+
packet = +"#{metric.name}:#{metric.value}|#{metric.type}"
|
72
74
|
packet << "|@#{metric.sample_rate}" if metric.sample_rate < 1
|
73
75
|
packet
|
74
76
|
end
|
@@ -88,19 +90,20 @@ module StatsD::Instrument::Backends
|
|
88
90
|
|
89
91
|
def implementation=(value)
|
90
92
|
@packet_factory = case value
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
93
|
+
when :datadog
|
94
|
+
DogStatsDProtocol.new
|
95
|
+
when :statsite
|
96
|
+
StatsiteStatsDProtocol.new
|
97
|
+
else
|
98
|
+
StatsDProtocol.new
|
99
|
+
end
|
98
100
|
@implementation = value
|
99
101
|
end
|
100
102
|
|
101
103
|
def collect_metric(metric)
|
102
104
|
unless @packet_factory.class::SUPPORTED_METRIC_TYPES[metric.type]
|
103
|
-
StatsD.logger.warn("[StatsD] Metric type #{metric.type.inspect} not supported
|
105
|
+
StatsD.logger.warn("[StatsD] Metric type #{metric.type.inspect} is not supported " \
|
106
|
+
"on #{implementation} implementation.")
|
104
107
|
return false
|
105
108
|
end
|
106
109
|
|
@@ -139,13 +142,13 @@ module StatsD::Instrument::Backends
|
|
139
142
|
synchronize do
|
140
143
|
socket.send(command, 0) > 0
|
141
144
|
end
|
142
|
-
rescue ThreadError
|
145
|
+
rescue ThreadError
|
143
146
|
# In cases where a TERM or KILL signal has been sent, and we send stats as
|
144
147
|
# part of a signal handler, locks cannot be acquired, so we do our best
|
145
148
|
# to try and send the command without a lock.
|
146
149
|
socket.send(command, 0) > 0
|
147
|
-
rescue SocketError, IOError, SystemCallError
|
148
|
-
StatsD.logger.error
|
150
|
+
rescue SocketError, IOError, SystemCallError => e
|
151
|
+
StatsD.logger.error("[StatsD] #{e.class.name}: #{e.message}")
|
149
152
|
invalidate_socket
|
150
153
|
end
|
151
154
|
|