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"