Skip to content

Commit 6956636

Browse files
RUBY-3680 Close pipe fds (#2936)
1 parent e741df4 commit 6956636

File tree

11 files changed

+115
-86
lines changed

11 files changed

+115
-86
lines changed

.evergreen/config.yml

Lines changed: 14 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -377,38 +377,9 @@ functions:
377377
script: |
378378
${PREPARE_SHELL}
379379
TEST_CMD="bundle exec rake driver_bench" PERFORMANCE_RESULTS_FILE="$PROJECT_DIRECTORY/perf.json" .evergreen/run-tests.sh
380-
- command: shell.exec
380+
- command: perf.send
381381
params:
382-
script: |
383-
# We use the requester expansion to determine whether the data is from a mainline evergreen run or not
384-
if [ "${requester}" == "commit" ]; then
385-
is_mainline=true
386-
else
387-
is_mainline=false
388-
fi
389-
390-
# We parse the username out of the order_id as patches append that in and SPS does not need that information
391-
parsed_order_id=$(echo "${revision_order_id}" | awk -F'_' '{print $NF}')
392-
393-
# Submit the performance data to the SPS endpoint
394-
response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X 'POST' \
395-
"https://performance-monitoring-api.corp.mongodb.com/raw_perf_results/cedar_report?project=${project_id}&version=${version_id}&variant=${build_variant}&order=$parsed_order_id&task_name=${task_name}&task_id=${task_id}&execution=${execution}&mainline=$is_mainline" \
396-
-H 'accept: application/json' \
397-
-H 'Content-Type: application/json' \
398-
-d @${PROJECT_DIRECTORY}/perf.json)
399-
400-
http_status=$(echo "$response" | grep "HTTP_STATUS" | awk -F':' '{print $2}')
401-
response_body=$(echo "$response" | sed '/HTTP_STATUS/d')
402-
403-
# We want to throw an error if the data was not successfully submitted
404-
if [ "$http_status" -ne 200 ]; then
405-
echo "Error: Received HTTP status $http_status"
406-
echo "Response Body: $response_body"
407-
exit 1
408-
fi
409-
410-
echo "Response Body: $response_body"
411-
echo "HTTP Status: $http_status"
382+
file: "${PROJECT_DIRECTORY}/perf.json"
412383

413384
"run tests":
414385
- command: shell.exec
@@ -1947,17 +1918,18 @@ buildvariants:
19471918
# - name: testgcpkms_task_group
19481919
# batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
19491920

1950-
- matrix_name: testazurekms-variant
1951-
matrix_spec:
1952-
ruby: ruby-3.0
1953-
fle: helper
1954-
topology: standalone
1955-
os: debian11 # could eventually look at updating this to rhel80
1956-
mongodb-version: 6.0
1957-
display_name: "AZURE KMS"
1958-
tasks:
1959-
- name: testazurekms_task_group
1960-
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
1921+
# https://jira.mongodb.org/browse/RUBY-3672
1922+
#- matrix_name: testazurekms-variant
1923+
# matrix_spec:
1924+
# ruby: ruby-3.0
1925+
# fle: helper
1926+
# topology: standalone
1927+
# os: debian11 # could eventually look at updating this to rhel80
1928+
# mongodb-version: 6.0
1929+
# display_name: "AZURE KMS"
1930+
# tasks:
1931+
# - name: testazurekms_task_group
1932+
# batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
19611933

19621934
- matrix_name: atlas-full
19631935
matrix_spec:
@@ -1975,15 +1947,6 @@ buildvariants:
19751947
tasks:
19761948
- name: testatlas_task_group
19771949

1978-
- matrix_name: "serverless"
1979-
matrix_spec:
1980-
ruby: "ruby-3.3"
1981-
fle: path
1982-
os: ubuntu2204
1983-
display_name: "Atlas serverless ${ruby}"
1984-
tasks:
1985-
- name: serverless_task_group
1986-
19871950
- matrix_name: "aws-lambda"
19881951
matrix_spec:
19891952
ruby: 'ruby-3.2'

.evergreen/config/standard.yml.erb

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -494,17 +494,18 @@ buildvariants:
494494
# - name: testgcpkms_task_group
495495
# batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
496496

497-
- matrix_name: testazurekms-variant
498-
matrix_spec:
499-
ruby: ruby-3.0
500-
fle: helper
501-
topology: standalone
502-
os: debian11 # could eventually look at updating this to rhel80
503-
mongodb-version: 6.0
504-
display_name: "AZURE KMS"
505-
tasks:
506-
- name: testazurekms_task_group
507-
batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
497+
# https://jira.mongodb.org/browse/RUBY-3672
498+
#- matrix_name: testazurekms-variant
499+
# matrix_spec:
500+
# ruby: ruby-3.0
501+
# fle: helper
502+
# topology: standalone
503+
# os: debian11 # could eventually look at updating this to rhel80
504+
# mongodb-version: 6.0
505+
# display_name: "AZURE KMS"
506+
# tasks:
507+
# - name: testazurekms_task_group
508+
# batchtime: 20160 # Use a batchtime of 14 days as suggested by the CSFLE test README
508509

509510
- matrix_name: atlas-full
510511
matrix_spec:
@@ -522,15 +523,6 @@ buildvariants:
522523
tasks:
523524
- name: testatlas_task_group
524525

525-
- matrix_name: "serverless"
526-
matrix_spec:
527-
ruby: <%= latest_ruby %>
528-
fle: path
529-
os: ubuntu2204
530-
display_name: "Atlas serverless ${ruby}"
531-
tasks:
532-
- name: serverless_task_group
533-
534526
- matrix_name: "aws-lambda"
535527
matrix_spec:
536528
ruby: 'ruby-3.2'

.rubocop.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,6 @@ Style/TrailingCommaInHashLiteral:
113113

114114
RSpec/ExampleLength:
115115
Max: 10
116+
117+
RSpec/MessageSpies:
118+
EnforcedStyle: receive

lib/mongo/server/connection_pool.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ def close(options = nil)
671671

672672
@max_connecting_cv.broadcast
673673
@size_cv.broadcast
674+
@generation_manager.close_all_pipes
674675
end
675676

676677
publish_cmap_event(

lib/mongo/server/connection_pool/generation_manager.rb

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ def generation_unlocked(service_id: nil)
4747
end
4848

4949
def pipe_fds(service_id: nil)
50-
@pipe_fds[service_id][@map[service_id]]
50+
@pipe_fds.dig(service_id, @map[service_id])
5151
end
5252

5353
def remove_pipe_fds(generation, service_id: nil)
5454
validate_service_id!(service_id)
5555

5656
r, w = @pipe_fds[service_id].delete(generation)
57+
return unless r && w
58+
5759
w.close
5860
# Schedule the read end of the pipe to be closed. We cannot close it
5961
# immediately since we need to wait for any Kernel#select calls to
@@ -89,8 +91,31 @@ def bump(service_id: nil)
8991
end
9092
end
9193

94+
# Close all pipes in the generation manager.
95+
#
96+
# This method should be called only when the +ConnectionPool+ that
97+
# owns this +GenerationManager+ is closed, to ensure that all
98+
# pipes are closed properly.
99+
def close_all_pipes
100+
@lock.synchronize do
101+
close_all_scheduled
102+
@pipe_fds.keys.each do |service_id|
103+
generations = @pipe_fds.delete(service_id)
104+
generations.values.each do |(r, w)|
105+
r.close
106+
w.close
107+
rescue IOError
108+
# Ignore any IOError that occurs when closing the
109+
# pipe, as there is nothing we can do about it.
110+
end
111+
end
112+
end
113+
end
114+
115+
92116
private
93117

118+
94119
def validate_service_id!(service_id)
95120
if service_id
96121
unless server.load_balancer?

lib/mongo/socket.rb

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class Socket
6464
# connection (for non-monitoring connections) that created this socket.
6565
# @option options [ true | false ] :monitor Whether this socket was
6666
# created by a monitoring connection.
67+
# @option options :pipe [ IO ] The file descriptor for the read end of the
68+
# pipe to listen on during the select system call when reading from the
69+
# socket.
6770
#
6871
# @api private
6972
def initialize(timeout, options)
@@ -106,6 +109,13 @@ def monitor?
106109
!!options[:monitor]
107110
end
108111

112+
# @return [ IO ] The file descriptor for the read end of the pipe to
113+
# listen on during the select system call when reading from the
114+
# socket.
115+
def pipe
116+
options[:pipe]
117+
end
118+
109119
# @return [ String ] Human-readable summary of the socket for debugging.
110120
#
111121
# @api private
@@ -161,7 +171,7 @@ def close
161171
begin
162172
# Sometimes it seems the close call can hang for a long time
163173
::Timeout.timeout(5) do
164-
@socket.close
174+
@socket&.close
165175
end
166176
rescue
167177
# Silence all errors
@@ -390,7 +400,6 @@ def read_from_socket(length, socket_timeout: nil, csot: false)
390400
raise_timeout_error!("Took more than #{_timeout} seconds to receive data", csot)
391401
end
392402
end
393-
pipe = options[:pipe]
394403
if exc.is_a?(IO::WaitReadable)
395404
if pipe
396405
select_args = [[@socket, pipe], nil, [@socket, pipe], select_timeout]

spec/mongo/client_construction_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# possible future work: re-enable these one at a time and do the hard work of
1111
# making them right.
1212
#
13-
# rubocop:disable RSpec/ExpectInHook, RSpec/MessageSpies, RSpec/ExampleLength
13+
# rubocop:disable RSpec/ExpectInHook, RSpec/ExampleLength
1414
# rubocop:disable RSpec/ContextWording, RSpec/RepeatedExampleGroupDescription
1515
# rubocop:disable RSpec/ExampleWording, Style/BlockComments, RSpec/AnyInstance
1616
# rubocop:disable RSpec/VerifiedDoubles
@@ -2717,7 +2717,7 @@
27172717
it_behaves_like 'duplicated client with reused monitoring'
27182718
end
27192719
end
2720-
# rubocop:enable RSpec/ExpectInHook, RSpec/MessageSpies, RSpec/ExampleLength
2720+
# rubocop:enable RSpec/ExpectInHook, RSpec/ExampleLength
27212721
# rubocop:enable RSpec/ContextWording, RSpec/RepeatedExampleGroupDescription
27222722
# rubocop:enable RSpec/ExampleWording, Style/BlockComments, RSpec/AnyInstance
27232723
# rubocop:enable RSpec/VerifiedDoubles
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe Mongo::Server::ConnectionPool::GenerationManager do
6+
describe '#close_all_pipes' do
7+
let(:service_id) { 'test_service_id' }
8+
9+
let(:server) { instance_double(Mongo::Server) }
10+
11+
let(:manager) { described_class.new(server: server) }
12+
13+
before do
14+
manager.pipe_fds(service_id: service_id)
15+
end
16+
17+
it 'closes all pipes and removes them from the map' do
18+
expect(manager.pipe_fds(service_id: service_id).size).to eq(2)
19+
20+
manager.instance_variable_get(:@pipe_fds)[service_id].each do |_gen, (r, w)|
21+
expect(r).to receive(:close).and_call_original
22+
expect(w).to receive(:close).and_call_original
23+
end
24+
25+
manager.close_all_pipes
26+
27+
expect(manager.instance_variable_get(:@pipe_fds)).to be_empty
28+
end
29+
end
30+
end

spec/mongo/server/connection_pool_spec.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,11 @@ def create_pool(min_pool_size)
12151215
expect(pool).to be_closed
12161216
end
12171217
end
1218+
1219+
it 'closes all pipes' do
1220+
expect(pool.generation_manager).to receive(:close_all_pipes).and_call_original
1221+
pool.close
1222+
end
12181223
end
12191224

12201225
describe '#inspect' do

spec/shared

0 commit comments

Comments
 (0)