Skip to content

Commit 298cd29

Browse files
authored
Merge pull request #168 from solarwinds/NH-96740
prepare oboe_metal change for fork change
2 parents 1eb3c59 + 14dde64 commit 298cd29

File tree

8 files changed

+74
-17
lines changed

8 files changed

+74
-17
lines changed

CONFIGURATION.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ or in your initialization step:
6969
ENV['OTEL_RUBY_INSTRUMENTATION_SINATRA_ENABLED'] = 'false'
7070
ENV['OTEL_RUBY_INSTRUMENTATION_MYSQL2_CONFIG_OPTS'] = 'db_statement=include;'
7171
```
72+
7273
## Programmatic Configuration
7374

7475
Many OpenTelemetry instrumentation library configurations can be set within the `SolarWindsAPM::OTelConfig.initialize_with_config ... end` block, please consult the individual [instrumentation](https://github.com/open-telemetry/opentelemetry-ruby-contrib/tree/main/instrumentation) README pages for the options available. Note this takes lower precedence than the [environment varable](#instrumentation-libraries) settings.
@@ -128,6 +129,7 @@ Environment Variable | Config File Key | Description | Default
128129
`SW_APM_TRUSTEDPATH` | N/A | The library uses the host system's default trusted CA certificates to verify the TLS connection to the collector. To override the default, define the trusted certificate path configuration option with an absolute path to a specific trusted certificate file in PEM format. | None
129130
`SW_APM_LAMBDA_PRELOAD_DEPS` | N/A | This option only takes effect in the AWS Lambda runtime. Set to `false` to disable the attempt to preload function dependencies and install instrumentations. | `true`
130131
`SW_APM_TRANSACTION_NAME` | N/A | Customize the transaction name for all traces, typically used to target specific instrumented lambda functions. _Precedence order_: custom SDK > `SW_APM_TRANSACTION_NAME` > automatic naming | None
132+
`SW_APM_ENABLE_AFTER_FORK` | N/A | For background job systems (e.g. resque) that fork child processes as jobs are received. This option delays the library initialization to the start of the child process, to avoid issues associated with gRPC's weak support of forking and cleanup. | false
131133
N/A | `:log_args` | Enable/disable the collection of URL query parameters, set to boolean false to disable. | true
132134
N/A | `:log_traceId` | Configure the insertion of trace context into application logs, setting `:traced` would include the available context fields such as trace_id, span_id into log messages. | `:never`
133135
N/A | `:tracing_mode` | Enable/disable the tracing mode for this service, setting `:disabled` would suppress all trace spans and metrics. | `:enabled`
@@ -181,3 +183,38 @@ end
181183
```
182184

183185
Note that with Rails >= 7.1 the comment format can be specified via the `config.active_record.query_log_tags_format` option. SolarWinds Observability functionality depends on the default `:sqlcommenter` format, it is not recommended to change this value.
186+
187+
### Background Jobs
188+
189+
#### Resque
190+
191+
[Resque](https://github.com/resque/resque) is a Redis-backed library for creating background jobs, queuing them on multiple queues, and processing them later.
192+
193+
When starting the Resque worker, it is necessary to set two options: `SW_APM_ENABLE_AFTER_FORK=true` and `RUN_AT_EXIT_HOOKS=1`.
194+
195+
For example:
196+
197+
```console
198+
SW_APM_ENABLE_AFTER_FORK=true RUN_AT_EXIT_HOOKS=1 QUEUE=${QUEUE_NAME} ${EXTRA_OPTIONS} bundle exec rake resque:work
199+
```
200+
201+
Explanation:
202+
203+
* `SW_APM_ENABLE_AFTER_FORK`: This option delays initialization of the library's C extension and gRPC-based reporter from the start of the parent worker process to the start of the child process, which is crucial to avoid issues associated with gRPC's weak support of forking and cleanup.
204+
* `RUN_AT_EXIT_HOOKS`: This option, provided by Resque, ensures that the forked processes shut down gracefully (i.e., no immediate `exit!`).
205+
206+
Additionally, you need to configure the Resque initializer in your Rails application by adding the following code to `config/initializers/resque.rb`. It's recommended to have a upper bound time (e.g. 8 seconds) to avoid infinited loop if something wrong with `solarwinds_apm` initialization.
207+
208+
```ruby
209+
require 'resque'
210+
211+
Resque.after_fork do
212+
unless SolarWindsAPM::API.solarwinds_ready?(8_000)
213+
puts "SolarWindsAPM is not ready, this job will not be traced."
214+
end
215+
end
216+
```
217+
218+
Explanation:
219+
220+
* Since Resque forks a new process for each task, `solarwinds_apm` needs a certain amount of time to complete initialization at the start of each child process [before it is ready to trace](https://github.com/solarwinds/apm-ruby/blob/main/README.md#check-if-solarwinds_apm-is-ready). This `solarwinds_ready?` check in the [`after_fork` hook](https://github.com/resque/resque/blob/master/docs/HOOKS.md) ensures the library is ready for instrumentation to collect metrics and sample for traces.

SECURITY.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ Our security strategy covers all aspects of our business, including:
1515
* Data protection
1616

1717
## Security Documents
18+
1819
[Security Statement](https://www.solarwinds.com/security/security-statement)
1920

2021
[Vendor Data Protection Requirements](https://www.solarwinds.com/security/vendor-data-protection-requirements)
2122

2223
## Vulnerability Disclosure Policy
24+
2325
[SolarWinds Vulnerability Disclosure Policy](https://www.solarwinds.com/information-security/vulnerability-disclosure-policy)
2426

2527
## Want to report a security concern?
28+
2629
SolarWinds reviews all reports of security vulnerabilities submitted to it affecting SolarWinds products and services. To report a vulnerability in one of our products or solutions or a vulnerability in one of our corporate websites, please contact our Product Security Incident Response Team (PSIRT) at [psirt@solarwinds.com](mailto:psirt@solarwinds.com).

lib/oboe_metal.rb

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ module SolarWindsAPM
1414
@loaded = false
1515
@reporter = nil
1616
@oboe_api = nil
17+
@init_sent = false
1718

1819
class << self
19-
attr_accessor :reporter, :loaded, :oboe_api
20+
attr_accessor :reporter, :loaded, :oboe_api, :init_sent
2021

2122
def sample_rate(rate)
2223
return unless SolarWindsAPM.loaded
@@ -38,7 +39,7 @@ def start
3839
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe # creates an array with the options in the right order
3940
SolarWindsAPM.reporter = Oboe_metal::Reporter.new(*options)
4041
SolarWindsAPM.loaded = true
41-
report_init
42+
report_init if (options[22]).zero? # report init at beginning if no after fork enabled
4243
rescue StandardError => e
4344
warn e.message
4445
SolarWindsAPM.loaded = false
@@ -70,12 +71,15 @@ def send_status(evt, context = nil, with_system_timestamp: true)
7071
# layer.
7172
#
7273
def report_init(layer = :rack) # :nodoc:
73-
# Don't send __Init in test or if SolarWindsAPM
74-
# isn't fully loaded (e.g. missing c-extension)
74+
# Don't send __Init in test or if SolarWindsAPM isn't fully loaded (e.g. missing c-extension)
75+
# or if already sent (e.g. SolarWindsAPM.init_sent = true)
76+
return if SolarWindsAPM.init_sent
7577
return unless SolarWindsAPM.loaded
7678

7779
platform_info = build_swo_init_report
7880
log_init(layer, platform_info)
81+
82+
SolarWindsAPM.init_sent = true
7983
SolarWindsAPM.logger.debug { "[#{self.class}/#{__method__}] Init message has been sent." }
8084
end
8185

lib/solarwinds_apm/oboe_init_options.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ def initialize
6767
@metric_format = determine_the_metric_model
6868
# log type (0 = stderr; 1 = stdout; 2 = file; 3 = null; 4 = disabled; default = 0)
6969
@log_type = determine_oboe_log_type
70+
# after fork enablement (0 = disable; 1 = enabled, parent process will not init oboe but fork child process will init oboe; default = 0)
71+
@after_fork = determine_oboe_after_fork
7072
end
7173

7274
# for testing with changed ENV vars
@@ -97,7 +99,8 @@ def array_for_oboe
9799
@grpc_proxy, # 18
98100
0, # 19 arg for lambda (no lambda for ruby yet)
99101
@metric_format, # 20
100-
@log_type # 21
102+
@log_type, # 21
103+
@after_fork # 22
101104
]
102105
end
103106

@@ -211,5 +214,9 @@ def determine_lambda
211214
true
212215
end
213216
end
217+
218+
def determine_oboe_after_fork
219+
ENV['SW_APM_ENABLE_AFTER_FORK'].to_s == 'true' ? 1 : 0
220+
end
214221
end
215222
end

lib/solarwinds_apm/opentelemetry/solarwinds_sampler.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ def should_sample?(trace_id:, parent_context:, links:, name:, kind:, attributes:
5555
attributes: #{attributes}"
5656
end
5757

58+
SolarWindsAPM::Reporter.send(:report_init) # This only happens if after_fork enabled
59+
5860
parent_span_context = ::OpenTelemetry::Trace.current_span(parent_context).context
5961
xtraceoptions = ::SolarWindsAPM::XTraceOptions.new(parent_context)
6062
SolarWindsAPM.logger.debug do

test/run_tests.sh

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ for gemfile in "${gemfiles[@]}" ; do
6666
continue
6767
fi
6868
# and here we are finally running the tests!!!
69-
BUNDLE_GEMFILE=$gemfile bundle exec rake test
7069
check_file_name=$gemfile
70+
BUNDLE_GEMFILE=$gemfile bundle exec rake test
7171
check_status
7272
done
7373

@@ -81,8 +81,8 @@ fi
8181
# for dbo patch test
8282
PATCH_TEST_FILE=$(find test/patch/*_test.rb -type f)
8383
for file in $PATCH_TEST_FILE; do
84-
BUNDLE_GEMFILE=gemfiles/test_gems.gemfile bundle exec ruby -I test $file
8584
check_file_name=$file
85+
BUNDLE_GEMFILE=gemfiles/test_gems.gemfile bundle exec ruby -I test $file
8686
check_status
8787
done
8888

@@ -95,8 +95,8 @@ echo "Fake libsolarwinds_apm.so created"
9595

9696
NUMBER_FILE=$(find test/solarwinds_apm/init_test/*_test.rb -type f | wc -l)
9797
for ((i = 1; i <= $NUMBER_FILE; i++)); do
98-
BUNDLE_GEMFILE=gemfiles/test_gems.gemfile bundle exec ruby -I test test/solarwinds_apm/init_test/init_${i}_test.rb
9998
check_file_name=init_${i}_test.rb
99+
BUNDLE_GEMFILE=gemfiles/test_gems.gemfile bundle exec ruby -I test test/solarwinds_apm/init_test/init_${i}_test.rb
100100
check_status
101101
done
102102

test/solarwinds_apm/oboe_init_options_test.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
SolarWindsAPM::OboeInitOptions.instance.re_init
5353
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
5454

55-
_(options.size).must_equal 22
55+
_(options.size).must_equal 23
5656
_(options[0]).must_equal 'string_4'
5757
_(options[1]).must_equal 2
5858
_(options[2]).must_equal 'string_5'
@@ -94,7 +94,7 @@
9494
SolarWindsAPM::OboeInitOptions.instance.re_init
9595
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
9696

97-
_(options.size).must_equal 22
97+
_(options.size).must_equal 23
9898

9999
_(options[0]).must_equal 'string_0'
100100
_(options[1]).must_equal 1
@@ -110,7 +110,7 @@
110110
SolarWindsAPM::OboeInitOptions.instance.re_init
111111
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
112112

113-
_(options.size).must_equal 22
113+
_(options.size).must_equal 23
114114
_(options[20]).must_equal 2
115115
end
116116

@@ -121,7 +121,7 @@
121121
SolarWindsAPM::OboeInitOptions.instance.re_init
122122
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
123123

124-
_(options.size).must_equal 22
124+
_(options.size).must_equal 23
125125
_(options[20]).must_equal 2
126126
end
127127

@@ -132,7 +132,7 @@
132132
SolarWindsAPM::OboeInitOptions.instance.re_init
133133
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
134134

135-
_(options.size).must_equal 22
135+
_(options.size).must_equal 23
136136
_(options[20]).must_equal 2
137137
end
138138

@@ -142,7 +142,7 @@
142142
SolarWindsAPM::OboeInitOptions.instance.re_init
143143
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
144144

145-
_(options.size).must_equal 22
145+
_(options.size).must_equal 23
146146
_(options[20]).must_equal 2
147147
end
148148

@@ -154,7 +154,7 @@
154154
SolarWindsAPM::OboeInitOptions.instance.re_init
155155
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
156156

157-
_(options.size).must_equal 22
157+
_(options.size).must_equal 23
158158
_(options[10]).must_equal "-----BEGIN CERTIFICATE-----\nMIID8TCCAtmgAwIBAgIJAMoDz7Npas2/MA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD\nVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5j\naXNjbzEVMBMGA1UECgwMTGlicmF0byBJbmMuMRUwEwYDVQQDDAxBcHBPcHRpY3Mg\nQ0ExJDAiBgkqhkiG9w0BCQEWFXN1cHBvcnRAYXBwb3B0aWNzLmNvbTAeFw0xNzA5\nMTUyMjAxMzlaFw0yNzA5MTMyMjAxMzlaMIGOMQswCQYDVQQGEwJVUzETMBEGA1UE\nCAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEVMBMGA1UECgwM\nTGlicmF0byBJbmMuMRUwEwYDVQQDDAxBcHBPcHRpY3MgQ0ExJDAiBgkqhkiG9w0B\nCQEWFXN1cHBvcnRAYXBwb3B0aWNzLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP\nADCCAQoCggEBAOxO0wsGba3iI4r3L5BMST0rAO/gGaUhpQre6nRwVTmPCnLw1bmn\nGdiFgYv/oRRwU+VieumHSQqoOmyFrg+ajGmvUDp2WqQ0It+XhcbaHFiAp2H7+mLf\ncUH6S43/em0WUxZHeRzRupRDyO1bX6Hh2jgxykivlFrn5HCIQD5Hx1/SaZoW9v2n\noATCbgFOiPW6kU/AVs4R0VBujon13HCehVelNKkazrAEBT1i6RvdOB6aQQ32seW+\ngLV5yVWSPEJvA9ZJqad/nQ8EQUMSSlVN191WOjp4bGpkJE1svs7NmM+Oja50W56l\nqOH5eWermr/8qWjdPlDJ+I0VkgN0UyHVuRECAwEAAaNQME4wHQYDVR0OBBYEFOuL\nKDTFhRQXwlBRxhPqhukrNYeRMB8GA1UdIwQYMBaAFOuLKDTFhRQXwlBRxhPqhukr\nNYeRMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAJQtH446NZhjusy6\niCyvmnD95ybfNPDpjHmNx5n9Y6w9n+9y1o3732HUJE+WjvbLS3h1o7wujGKMcRJn\n7I7eTDd26ZhLvnh5/AitYjdxrtUkQDgyxwLFJKhZu0ik2vXqj0fL961/quJL8Gyp\nhNj3Nf7WMohQMSohEmCCX2sHyZGVGYmQHs5omAtkH/NNySqmsWNcpgd3M0aPDRBZ\n5VFreOSGKBTJnoLNqods/S9RV0by84hm3j6aQ/tMDIVE9VCJtrE6evzC0MWyVFwR\nftgwcxyEq5SkiR+6BCwdzAMqADV37TzXDHLjwSrMIrgLV5xZM20Kk6chxI5QAr/f\n7tsqAxw=\n-----END CERTIFICATE-----"
159159
end
160160

@@ -166,7 +166,7 @@
166166
SolarWindsAPM::OboeInitOptions.instance.re_init
167167
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
168168

169-
_(options.size).must_equal 22
169+
_(options.size).must_equal 23
170170
_(options[10]).must_equal ''
171171
end
172172

@@ -177,7 +177,7 @@
177177
SolarWindsAPM::OboeInitOptions.instance.re_init
178178
options = SolarWindsAPM::OboeInitOptions.instance.array_for_oboe
179179

180-
_(options.size).must_equal 22
180+
_(options.size).must_equal 23
181181
_(options[10]).must_equal "-----BEGIN CERTIFICATE-----\nMIID8TCCAtmgAwIBAgIJAMoDz7Npas2/MA0GCSqGSIb3DQEBCwUAMIGOMQswCQYD\n-----END CERTIFICATE-----"
182182
end
183183

test/solarwinds_apm/oboe_metal_test.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@ def sendReport(*) # rubocop:disable Naming/MethodName
2929
log_output = StringIO.new
3030
SolarWindsAPM.logger = Logger.new(log_output)
3131
SolarWindsAPM.loaded = true
32+
SolarWindsAPM.init_sent = false
3233
SolarWindsAPM::Reporter.send(:report_init, :rack)
3334
assert_includes log_output.string, 'Init message has been sent.'
35+
_(SolarWindsAPM.init_sent).must_equal true
3436
end
3537

3638
it 'test_reporter_start' do
3739
log_output = StringIO.new
3840
SolarWindsAPM.logger = Logger.new(log_output)
41+
SolarWindsAPM.init_sent = false
3942
SolarWindsAPM::Reporter.start
4043
assert_includes log_output.string, 'Init message has been sent.'
44+
_(SolarWindsAPM.init_sent).must_equal true
4145
end
4246

4347
it 'test_build_swo_init_report_with_error' do

0 commit comments

Comments
 (0)