From 741bec31ebc3592ba6c5a1e519ae994a81b854b1 Mon Sep 17 00:00:00 2001 From: Ivan <62664893+ivankalinovski@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:19:13 +0200 Subject: [PATCH] [Core] Handle and log client timeouts (#1195) What - Handle uncaught exceptions. Why - Missing logs in upsert entities caused by timeouts. How - Add try catch block to transport layer in ocean client to log unhandled client timeouts of non retryable methods. ## Type of change Please leave one option from the following and delete the rest: - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] New Integration (non-breaking change which adds a new integration) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Non-breaking change (fix of existing functionality that will not change current behavior) - [ ] Documentation (added/updated documentation)

All tests should be run against the port production environment(using a testing org).

### Core testing checklist - [ ] Integration able to create all default resources from scratch - [ ] Resync finishes successfully - [ ] Resync able to create entities - [ ] Resync able to update entities - [ ] Resync able to detect and delete entities - [ ] Scheduled resync able to abort existing resync and start a new one - [ ] Tested with at least 2 integrations from scratch - [ ] Tested with Kafka and Polling event listeners - [ ] Tested deletion of entities that don't pass the selector ### Integration testing checklist - [ ] Integration able to create all default resources from scratch - [ ] Resync able to create entities - [ ] Resync able to update entities - [ ] Resync able to detect and delete entities - [ ] Resync finishes successfully - [ ] If new resource kind is added or updated in the integration, add example raw data, mapping and expected result to the `examples` folder in the integration directory. - [ ] If resource kind is updated, run the integration with the example data and check if the expected result is achieved - [ ] If new resource kind is added or updated, validate that live-events for that resource are working as expected - [ ] Docs PR link [here](#) ### Preflight checklist - [ ] Handled rate limiting - [ ] Handled pagination - [ ] Implemented the code in async - [ ] Support Multi account ## Screenshots Include screenshots from your environment showing how the resources of the integration will look. ## API Documentation Provide links to the API documentation used for this integration. Co-authored-by: Ivan Kalinovski --- CHANGELOG.md | 8 ++++++++ port_ocean/helpers/retry.py | 39 ++++++++++++++++++++++++------------- pyproject.toml | 2 +- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ae489b7b0..d0f3b51cee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm +## 0.14.4 (2024-12-03) + + +### Bug Fixes + +- Add try/except block on httpx transport of ocean client to log timeouts and other exceptions (Client timeouts not recorded) + + ## 0.14.3 (2024-11-25) diff --git a/port_ocean/helpers/retry.py b/port_ocean/helpers/retry.py index 9585c9439c..0893235c4a 100644 --- a/port_ocean/helpers/retry.py +++ b/port_ocean/helpers/retry.py @@ -132,13 +132,18 @@ def handle_request(self, request: httpx.Request) -> httpx.Response: httpx.Response: The response received. """ - transport: httpx.BaseTransport = self._wrapped_transport # type: ignore - if request.method in self._retryable_methods: - send_method = partial(transport.handle_request) - response = self._retry_operation(request, send_method) - else: - response = transport.handle_request(request) - return response + try: + transport: httpx.BaseTransport = self._wrapped_transport # type: ignore + if request.method in self._retryable_methods: + send_method = partial(transport.handle_request) + response = self._retry_operation(request, send_method) + else: + response = transport.handle_request(request) + return response + except Exception as e: + if not self._is_retryable_method(request) and self._logger is not None: + self._logger.exception(f"{repr(e)} - {request.url}", exc_info=e) + raise e async def handle_async_request(self, request: httpx.Request) -> httpx.Response: """Sends an HTTP request, possibly with retries. @@ -150,13 +155,19 @@ async def handle_async_request(self, request: httpx.Request) -> httpx.Response: The response. """ - transport: httpx.AsyncBaseTransport = self._wrapped_transport # type: ignore - if self._is_retryable_method(request): - send_method = partial(transport.handle_async_request) - response = await self._retry_operation_async(request, send_method) - else: - response = await transport.handle_async_request(request) - return response + try: + transport: httpx.AsyncBaseTransport = self._wrapped_transport # type: ignore + if self._is_retryable_method(request): + send_method = partial(transport.handle_async_request) + response = await self._retry_operation_async(request, send_method) + else: + response = await transport.handle_async_request(request) + return response + except Exception as e: + # Retyable methods are logged via _log_error + if not self._is_retryable_method(request) and self._logger is not None: + self._logger.exception(f"{repr(e)} - {request.url}", exc_info=e) + raise e async def aclose(self) -> None: """ diff --git a/pyproject.toml b/pyproject.toml index aecf1ec569..7f0d43c13d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "port-ocean" -version = "0.14.3" +version = "0.14.4" description = "Port Ocean is a CLI tool for managing your Port projects." readme = "README.md" homepage = "https://app.getport.io"