Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release HTTP connection back to the pool after making an API request #1231

Closed
wants to merge 1 commit into from
Closed

Release HTTP connection back to the pool after making an API request #1231

wants to merge 1 commit into from

Conversation

jdufresne
Copy link
Contributor

Fixes warnings in application code:

  ResourceWarning: unclosed <ssl.SSLSocket fd=X, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('...', ...), raddr=('52.95.145.10', 443)>

The warning normally only occurs for 4XX responses.

Fixes boto/boto3#454

@codecov-io
Copy link

codecov-io commented Jun 24, 2017

Codecov Report

Merging #1231 into develop will increase coverage by <.01%.
The diff coverage is 100%.

Impacted file tree graph

@@             Coverage Diff             @@
##           develop    #1231      +/-   ##
===========================================
+ Coverage    98.03%   98.03%   +<.01%     
===========================================
  Files           45       45              
  Lines         7342     7344       +2     
===========================================
+ Hits          7198     7200       +2     
  Misses         144      144
Impacted Files Coverage Δ
botocore/endpoint.py 97.24% <100%> (+0.03%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46ac74a...ecd9213. Read the comment docs.

Copy link
Contributor

@dstufft dstufft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is interesting, because this technically changes the semantics of the API to return a request object that no longer has an open connection instead of one that does. This object eventually gets passed into the event system which external parties can register events that do things.

That being said, I think that at this point closing the http connection should be fine, because I don't think there's anything left that can be done (unless someone is doing something crazy like pulling the connection off of the already consumed request and re-using it externally).

I'm inclined to accept this PR (with the changes I described in another comment), but let me double check on our API compatibility with this.

@@ -231,6 +231,7 @@ def _get_response(self, request, operation_model, attempts):
operation_model.metadata['protocol'])
parsed_response = parser.parse(
response_dict, operation_model.output_shape)
http_response.close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would be better written as:

with http_response:
    response_dict = ...
    parser = ...
    parsed_response = ...
    return (http_response, parsed_response), None

It has the same effect, but it ensures it gets closed even if there is a exception here.

@@ -231,6 +231,7 @@ def _get_response(self, request, operation_model, attempts):
operation_model.metadata['protocol'])
parsed_response = parser.parse(
response_dict, operation_model.output_shape)
http_response.close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this safe? I don't think this accounts for streaming responses does it?

@dstufft
Copy link
Contributor

dstufft commented Jul 3, 2017

Talking offline, this is probably going to require a lot more work to handle streaming responses. We pass the http_response as part of the object we return in some cases so we can't close it until after the user has consumed the response.

Fixes warnings in application code:

  ResourceWarning: unclosed <ssl.SSLSocket fd=X, family=AddressFamily.AF_INET, type=2049, proto=6, laddr=('...', ...), raddr=('52.95.145.10', 443)>

The warning normally only occurs for 4XX responses.

Fixes boto/boto3#454
@jdufresne
Copy link
Contributor Author

I've modified the code to use a context manager.

What do you think about this alternative implementation which moves the closing to BaseClient._make_api_call() so as to delay calling close until after the 'after-call.{endpoint_prefix}.{operation_name}' event is emitted.

diff --git a/botocore/client.py b/botocore/client.py
index e7a81c5..5ec6974 100644
--- a/botocore/client.py
+++ b/botocore/client.py
@@ -10,6 +10,7 @@
 # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 # ANY KIND, either express or implied. See the License for the specific
 # language governing permissions and limitations under the License.
+import contextlib
 import logging
 import functools
 
@@ -585,20 +586,21 @@ class BaseClient(object):
             http, parsed_response = self._endpoint.make_request(
                 operation_model, request_dict)
 
-        self.meta.events.emit(
-            'after-call.{endpoint_prefix}.{operation_name}'.format(
-                endpoint_prefix=self._service_model.endpoint_prefix,
-                operation_name=operation_name),
-            http_response=http, parsed=parsed_response,
-            model=operation_model, context=request_context
-        )
+        with contextlib.closing(http):
+            self.meta.events.emit(
+                'after-call.{endpoint_prefix}.{operation_name}'.format(
+                    endpoint_prefix=self._service_model.endpoint_prefix,
+                    operation_name=operation_name),
+                http_response=http, parsed=parsed_response,
+                model=operation_model, context=request_context
+            )
 
-        if http.status_code >= 300:
-            error_code = parsed_response.get("Error", {}).get("Code")
-            error_class = self.exceptions.from_code(error_code)
-            raise error_class(parsed_response, operation_name)
-        else:
-            return parsed_response
+            if http.status_code >= 300:
+                error_code = parsed_response.get("Error", {}).get("Code")
+                error_class = self.exceptions.from_code(error_code)
+                raise error_class(parsed_response, operation_name)
+            else:
+                return parsed_response
 
     def _convert_to_request_dict(self, api_params, operation_model,
                                  context=None):

@stealthycoin stealthycoin added pr/needs-review This PR needs a review from a member of the team. and removed incorporating-feedback labels Jul 18, 2017
@jdufresne
Copy link
Contributor Author

jdufresne commented Jul 28, 2017

I'm motivated to fix this runtime warning as it causes a lot of noise in my logs. If the original approach isn't workable, I'm very willing to investigate other solution or incorporate necessary feedback.

@stealthycoin stealthycoin added enhancement This issue requests an improvement to a current feature. small labels Sep 14, 2017
@kyleknap kyleknap removed the small label Feb 28, 2020
@kdaily kdaily added the needs-review This issue or pull request needs review from a core team member. label Aug 18, 2021
@kdaily
Copy link
Member

kdaily commented Apr 20, 2022

The solution presented here as a fix for boto/boto3#454 was discussed recently as to why we would not be able to accept this change:

boto/boto3#454 (comment)

Closing this out, please check on boto/boto3#454 for updates on proposed solutions. Thanks!

@kdaily kdaily closed this Apr 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement This issue requests an improvement to a current feature. needs-review This issue or pull request needs review from a core team member. pr/needs-review This PR needs a review from a member of the team.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ResourceWarning: unclosed ssl.SSLSocket
7 participants