port-ocean 0.23.2__py3-none-any.whl → 0.23.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of port-ocean might be problematic. Click here for more details.
- integrations/_infra/Dockerfile.Deb +5 -1
- integrations/_infra/Dockerfile.local +5 -0
- integrations/_infra/entry_local.sh +0 -2
- port_ocean/clients/port/mixins/integrations.py +3 -2
- port_ocean/context/metric_resource.py +63 -0
- port_ocean/core/handlers/resync_state_updater/updater.py +3 -3
- port_ocean/core/integrations/mixins/sync_raw.py +110 -63
- port_ocean/core/integrations/mixins/utils.py +19 -0
- port_ocean/helpers/metric/metric.py +49 -12
- port_ocean/helpers/metric/utils.py +25 -1
- port_ocean/ocean.py +0 -1
- port_ocean/tests/core/handlers/mixins/test_sync_raw.py +29 -5
- port_ocean/utils/ipc.py +1 -1
- {port_ocean-0.23.2.dist-info → port_ocean-0.23.4.dist-info}/METADATA +1 -1
- {port_ocean-0.23.2.dist-info → port_ocean-0.23.4.dist-info}/RECORD +18 -17
- {port_ocean-0.23.2.dist-info → port_ocean-0.23.4.dist-info}/LICENSE.md +0 -0
- {port_ocean-0.23.2.dist-info → port_ocean-0.23.4.dist-info}/WHEEL +0 -0
- {port_ocean-0.23.2.dist-info → port_ocean-0.23.4.dist-info}/entry_points.txt +0 -0
|
@@ -5,11 +5,15 @@ FROM ${BASE_BUILDER_PYTHON_IMAGE} AS base
|
|
|
5
5
|
|
|
6
6
|
ARG BUILD_CONTEXT
|
|
7
7
|
ARG BUILDPLATFORM
|
|
8
|
+
ARG PROMETHEUS_MULTIPROC_DIR=/tmp/ocean/prometheus/metrics
|
|
8
9
|
|
|
9
10
|
ENV LIBRDKAFKA_VERSION=2.8.2 \
|
|
10
11
|
PYTHONUNBUFFERED=1 \
|
|
11
12
|
POETRY_VIRTUALENVS_IN_PROJECT=1 \
|
|
12
|
-
PIP_ROOT_USER_ACTION=ignore
|
|
13
|
+
PIP_ROOT_USER_ACTION=ignore \
|
|
14
|
+
PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
|
|
15
|
+
|
|
16
|
+
RUN mkdir -p ${PROMETHEUS_MULTIPROC_DIR}
|
|
13
17
|
|
|
14
18
|
WORKDIR /app
|
|
15
19
|
|
|
@@ -25,6 +25,11 @@ RUN apt-get update \
|
|
|
25
25
|
&& apt-get clean
|
|
26
26
|
|
|
27
27
|
ARG BUILD_CONTEXT
|
|
28
|
+
ARG PROMETHEUS_MULTIPROC_DIR=/tmp/ocean/prometheus/metrics
|
|
29
|
+
|
|
30
|
+
ENV PROMETHEUS_MULTIPROC_DIR=${PROMETHEUS_MULTIPROC_DIR}
|
|
31
|
+
|
|
32
|
+
RUN mkdir -p ${PROMETHEUS_MULTIPROC_DIR}
|
|
28
33
|
|
|
29
34
|
WORKDIR /app
|
|
30
35
|
|
|
@@ -214,8 +214,9 @@ class IntegrationClientMixin:
|
|
|
214
214
|
logger.debug("starting POST metrics request", metrics=metrics)
|
|
215
215
|
metrics_attributes = await self.get_metrics_attributes()
|
|
216
216
|
headers = await self.auth.headers()
|
|
217
|
+
url = metrics_attributes["ingestUrl"] + "/syncMetrics"
|
|
217
218
|
response = await self.client.post(
|
|
218
|
-
|
|
219
|
+
url,
|
|
219
220
|
headers=headers,
|
|
220
221
|
json={
|
|
221
222
|
"syncKindsMetrics": metrics,
|
|
@@ -229,7 +230,7 @@ class IntegrationClientMixin:
|
|
|
229
230
|
metrics_attributes = await self.get_metrics_attributes()
|
|
230
231
|
url = (
|
|
231
232
|
metrics_attributes["ingestUrl"]
|
|
232
|
-
+ f"/resync/{kind_metrics['eventId']}/kind/{kind_metrics['kindIdentifier']}"
|
|
233
|
+
+ f"/syncMetrics/resync/{kind_metrics['eventId']}/kind/{kind_metrics['kindIdentifier']}"
|
|
233
234
|
)
|
|
234
235
|
headers = await self.auth.headers()
|
|
235
236
|
response = await self.client.put(
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from contextlib import asynccontextmanager
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import AsyncIterator, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from loguru import logger
|
|
6
|
+
from werkzeug.local import LocalStack, LocalProxy
|
|
7
|
+
|
|
8
|
+
from port_ocean.exceptions.context import (
|
|
9
|
+
ResourceContextNotFoundError,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class MetricResourceContext:
|
|
18
|
+
"""
|
|
19
|
+
The metric resource context is a context manager that allows you to access the current metric resource if there is one.
|
|
20
|
+
This is useful for getting the metric resource kind
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
metric_resource_kind: str
|
|
24
|
+
index: int
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def kind(self) -> str:
|
|
28
|
+
return self.metric_resource_kind
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_resource_context_stack: LocalStack[MetricResourceContext] = LocalStack()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _get_metric_resource_context() -> MetricResourceContext:
|
|
35
|
+
"""
|
|
36
|
+
Get the context from the current thread.
|
|
37
|
+
"""
|
|
38
|
+
top_resource_context = _resource_context_stack.top
|
|
39
|
+
if top_resource_context is None:
|
|
40
|
+
raise ResourceContextNotFoundError(
|
|
41
|
+
"You must be inside an metric resource context in order to use it"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
return top_resource_context
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
metric_resource: MetricResourceContext = LocalProxy(lambda: _get_metric_resource_context()) # type: ignore
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@asynccontextmanager
|
|
51
|
+
async def metric_resource_context(
|
|
52
|
+
metric_resource_kind: str, index: int = 0
|
|
53
|
+
) -> AsyncIterator[MetricResourceContext]:
|
|
54
|
+
_resource_context_stack.push(
|
|
55
|
+
MetricResourceContext(metric_resource_kind=metric_resource_kind, index=index)
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
with logger.contextualize(
|
|
59
|
+
metric_resource_kind=metric_resource.metric_resource_kind
|
|
60
|
+
):
|
|
61
|
+
yield metric_resource
|
|
62
|
+
|
|
63
|
+
_resource_context_stack.pop()
|
|
@@ -94,6 +94,6 @@ class ResyncStateUpdater:
|
|
|
94
94
|
await ocean.metrics.send_metrics_to_webhook(
|
|
95
95
|
kind=ocean.metrics.current_resource_kind()
|
|
96
96
|
)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
await ocean.metrics.report_sync_metrics(
|
|
98
|
+
kinds=[ocean.metrics.current_resource_kind()]
|
|
99
|
+
)
|
|
@@ -9,6 +9,7 @@ import httpx
|
|
|
9
9
|
from loguru import logger
|
|
10
10
|
from port_ocean.clients.port.types import UserAgentType
|
|
11
11
|
from port_ocean.context.event import TriggerType, event_context, EventType, event
|
|
12
|
+
from port_ocean.context.metric_resource import metric_resource_context
|
|
12
13
|
from port_ocean.context.ocean import ocean
|
|
13
14
|
from port_ocean.context.resource import resource_context
|
|
14
15
|
from port_ocean.context import resource
|
|
@@ -16,6 +17,7 @@ from port_ocean.core.handlers.port_app_config.models import ResourceConfig
|
|
|
16
17
|
from port_ocean.core.integrations.mixins import HandlerMixin, EventsMixin
|
|
17
18
|
from port_ocean.core.integrations.mixins.utils import (
|
|
18
19
|
ProcessWrapper,
|
|
20
|
+
clear_http_client_context,
|
|
19
21
|
is_resource_supported,
|
|
20
22
|
unsupported_kind_response,
|
|
21
23
|
resync_generator_wrapper,
|
|
@@ -32,8 +34,8 @@ from port_ocean.core.ocean_types import (
|
|
|
32
34
|
)
|
|
33
35
|
from port_ocean.core.utils.utils import resolve_entities_diff, zip_and_sum, gather_and_split_errors_from_results
|
|
34
36
|
from port_ocean.exceptions.core import OceanAbortException
|
|
35
|
-
from port_ocean.helpers.metric.metric import SyncState, MetricType, MetricPhase
|
|
36
|
-
from port_ocean.helpers.metric.utils import TimeMetric
|
|
37
|
+
from port_ocean.helpers.metric.metric import MetricResourceKind, SyncState, MetricType, MetricPhase
|
|
38
|
+
from port_ocean.helpers.metric.utils import TimeMetric, TimeMetricWithResourceKind
|
|
37
39
|
from port_ocean.utils.ipc import FileIPC
|
|
38
40
|
|
|
39
41
|
SEND_RAW_DATA_EXAMPLES_AMOUNT = 5
|
|
@@ -248,9 +250,16 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
248
250
|
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.SKIPPED],
|
|
249
251
|
value=len(objects_diff[0].entity_selector_diff.passed) - len(changed_entities)
|
|
250
252
|
)
|
|
251
|
-
await self.entities_state_applier.upsert(
|
|
253
|
+
upserted_entities = await self.entities_state_applier.upsert(
|
|
252
254
|
changed_entities, user_agent_type
|
|
253
255
|
)
|
|
256
|
+
|
|
257
|
+
ocean.metrics.set_metric(
|
|
258
|
+
name=MetricType.OBJECT_COUNT_NAME,
|
|
259
|
+
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.LOADED],
|
|
260
|
+
value=len(upserted_entities)
|
|
261
|
+
)
|
|
262
|
+
|
|
254
263
|
else:
|
|
255
264
|
logger.info("Entities in batch didn't changed since last sync, skipping", total_entities=len(objects_diff[0].entity_selector_diff.passed))
|
|
256
265
|
ocean.metrics.inc_metric(
|
|
@@ -264,6 +273,11 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
264
273
|
modified_objects = await self.entities_state_applier.upsert(
|
|
265
274
|
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
266
275
|
)
|
|
276
|
+
ocean.metrics.set_metric(
|
|
277
|
+
name=MetricType.OBJECT_COUNT_NAME,
|
|
278
|
+
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.LOADED],
|
|
279
|
+
value=len(upserted_entities)
|
|
280
|
+
)
|
|
267
281
|
else:
|
|
268
282
|
modified_objects = await self.entities_state_applier.upsert(
|
|
269
283
|
objects_diff[0].entity_selector_diff.passed, user_agent_type
|
|
@@ -594,6 +608,7 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
594
608
|
) -> None:
|
|
595
609
|
logger.info(f"process started successfully for {resource.kind} with index {index}")
|
|
596
610
|
|
|
611
|
+
clear_http_client_context()
|
|
597
612
|
async def process_resource_task() -> None:
|
|
598
613
|
result = await self._process_resource(
|
|
599
614
|
resource, index, user_agent_type
|
|
@@ -631,16 +646,12 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
631
646
|
async with resource_context(resource,index):
|
|
632
647
|
resource_kind_id = f"{resource.kind}-{index}"
|
|
633
648
|
ocean.metrics.sync_state = SyncState.SYNCING
|
|
649
|
+
|
|
634
650
|
task = asyncio.create_task(
|
|
635
651
|
self._register_in_batches(resource, user_agent_type)
|
|
636
652
|
)
|
|
637
653
|
event.on_abort(lambda: task.cancel())
|
|
638
654
|
kind_results: tuple[list[Entity], list[Exception]] = await task
|
|
639
|
-
ocean.metrics.set_metric(
|
|
640
|
-
name=MetricType.OBJECT_COUNT_NAME,
|
|
641
|
-
labels=[ocean.metrics.current_resource_kind(), MetricPhase.LOAD, MetricPhase.LoadResult.LOADED],
|
|
642
|
-
value=len(kind_results[0])
|
|
643
|
-
)
|
|
644
655
|
|
|
645
656
|
if ocean.metrics.sync_state != SyncState.FAILED:
|
|
646
657
|
ocean.metrics.sync_state = SyncState.COMPLETED
|
|
@@ -648,10 +659,88 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
648
659
|
await ocean.metrics.send_metrics_to_webhook(
|
|
649
660
|
kind=resource_kind_id
|
|
650
661
|
)
|
|
651
|
-
|
|
662
|
+
await ocean.metrics.report_kind_sync_metrics(kind=resource_kind_id, blueprint=resource.port.entity.mappings.blueprint)
|
|
652
663
|
|
|
653
664
|
return kind_results
|
|
654
665
|
|
|
666
|
+
@TimeMetricWithResourceKind(MetricPhase.RESYNC)
|
|
667
|
+
async def resync_reconciliation(
|
|
668
|
+
self,
|
|
669
|
+
creation_results: list[tuple[list[Entity], list[Exception]]],
|
|
670
|
+
did_fetched_current_state: bool,
|
|
671
|
+
user_agent_type: UserAgentType,
|
|
672
|
+
app_config: Any,
|
|
673
|
+
silent: bool = True,
|
|
674
|
+
) -> None:
|
|
675
|
+
"""Handle the reconciliation phase of the resync process.
|
|
676
|
+
|
|
677
|
+
This method handles:
|
|
678
|
+
1. Sorting and upserting failed entities
|
|
679
|
+
2. Checking if current state was fetched
|
|
680
|
+
3. Calculating resync diff
|
|
681
|
+
4. Handling errors
|
|
682
|
+
5. Deleting entities that are no longer needed
|
|
683
|
+
6. Executing resync complete hooks
|
|
684
|
+
|
|
685
|
+
Args:
|
|
686
|
+
creation_results (list[tuple[list[Entity], list[Exception]]]): Results from entity creation
|
|
687
|
+
did_fetched_current_state (bool): Whether the current state was successfully fetched
|
|
688
|
+
user_agent_type (UserAgentType): The type of user agent
|
|
689
|
+
app_config (Any): The application configuration
|
|
690
|
+
silent (bool): Whether to raise exceptions or handle them silently
|
|
691
|
+
|
|
692
|
+
"""
|
|
693
|
+
await self.sort_and_upsert_failed_entities(user_agent_type)
|
|
694
|
+
|
|
695
|
+
if not did_fetched_current_state:
|
|
696
|
+
logger.warning(
|
|
697
|
+
"Due to an error before the resync, the previous state of entities at Port is unknown."
|
|
698
|
+
" Skipping delete phase due to unknown initial state."
|
|
699
|
+
)
|
|
700
|
+
return False
|
|
701
|
+
|
|
702
|
+
logger.info("Starting resync diff calculation")
|
|
703
|
+
generated_entities, errors = zip_and_sum(creation_results) or [
|
|
704
|
+
[],
|
|
705
|
+
[],
|
|
706
|
+
]
|
|
707
|
+
|
|
708
|
+
if errors:
|
|
709
|
+
message = f"Resync failed with {len(errors)} errors, skipping delete phase due to incomplete state"
|
|
710
|
+
error_group = ExceptionGroup(
|
|
711
|
+
message,
|
|
712
|
+
errors,
|
|
713
|
+
)
|
|
714
|
+
if not silent:
|
|
715
|
+
raise error_group
|
|
716
|
+
|
|
717
|
+
logger.error(message, exc_info=error_group)
|
|
718
|
+
return False
|
|
719
|
+
|
|
720
|
+
logger.info(
|
|
721
|
+
f"Running resync diff calculation, number of entities created during sync: {len(generated_entities)}"
|
|
722
|
+
)
|
|
723
|
+
entities_at_port = await ocean.port_client.search_entities(
|
|
724
|
+
user_agent_type
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
await self.entities_state_applier.delete_diff(
|
|
728
|
+
{"before": entities_at_port, "after": generated_entities},
|
|
729
|
+
user_agent_type, app_config.get_entity_deletion_threshold()
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
logger.info("Resync finished successfully")
|
|
733
|
+
|
|
734
|
+
# Execute resync_complete hooks
|
|
735
|
+
if "resync_complete" in self.event_strategy:
|
|
736
|
+
logger.info("Executing resync_complete hooks")
|
|
737
|
+
|
|
738
|
+
for resync_complete_fn in self.event_strategy["resync_complete"]:
|
|
739
|
+
await resync_complete_fn()
|
|
740
|
+
|
|
741
|
+
logger.info("Finished executing resync_complete hooks")
|
|
742
|
+
|
|
743
|
+
|
|
655
744
|
@TimeMetric(MetricPhase.RESYNC)
|
|
656
745
|
async def sync_raw_all(
|
|
657
746
|
self,
|
|
@@ -687,8 +776,9 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
687
776
|
logger.info(f"Resync will use the following mappings: {app_config.dict()}")
|
|
688
777
|
|
|
689
778
|
kinds = [f"{resource.kind}-{index}" for index, resource in enumerate(app_config.resources)]
|
|
779
|
+
blueprints = [resource.port.entity.mappings.blueprint for resource in app_config.resources]
|
|
690
780
|
ocean.metrics.initialize_metrics(kinds)
|
|
691
|
-
|
|
781
|
+
await ocean.metrics.report_sync_metrics(kinds=kinds, blueprints=blueprints)
|
|
692
782
|
|
|
693
783
|
# Clear cache
|
|
694
784
|
await ocean.app.cache_provider.clear()
|
|
@@ -714,64 +804,21 @@ class SyncRawMixin(HandlerMixin, EventsMixin):
|
|
|
714
804
|
multiprocessing.set_start_method('fork', True)
|
|
715
805
|
try:
|
|
716
806
|
for index,resource in enumerate(app_config.resources):
|
|
717
|
-
|
|
718
807
|
logger.info(f"Starting processing resource {resource.kind} with index {index}")
|
|
719
|
-
|
|
720
808
|
creation_results.append(await self.process_resource(resource,index,user_agent_type))
|
|
721
|
-
|
|
722
|
-
await self.sort_and_upsert_failed_entities(user_agent_type)
|
|
723
|
-
|
|
724
809
|
except asyncio.CancelledError as e:
|
|
725
810
|
logger.warning("Resync aborted successfully, skipping delete phase. This leads to an incomplete state")
|
|
726
811
|
raise
|
|
727
812
|
else:
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
generated_entities, errors = zip_and_sum(creation_results) or [
|
|
737
|
-
[],
|
|
738
|
-
[],
|
|
739
|
-
]
|
|
740
|
-
|
|
741
|
-
if errors:
|
|
742
|
-
message = f"Resync failed with {len(errors)} errors, skipping delete phase due to incomplete state"
|
|
743
|
-
error_group = ExceptionGroup(
|
|
744
|
-
message,
|
|
745
|
-
errors,
|
|
746
|
-
)
|
|
747
|
-
if not silent:
|
|
748
|
-
raise error_group
|
|
749
|
-
|
|
750
|
-
logger.error(message, exc_info=error_group)
|
|
751
|
-
return False
|
|
752
|
-
else:
|
|
753
|
-
logger.info(
|
|
754
|
-
f"Running resync diff calculation, number of entities created during sync: {len(generated_entities)}"
|
|
755
|
-
)
|
|
756
|
-
entities_at_port = await ocean.port_client.search_entities(
|
|
757
|
-
user_agent_type
|
|
758
|
-
)
|
|
759
|
-
await self.entities_state_applier.delete_diff(
|
|
760
|
-
{"before": entities_at_port, "after": generated_entities},
|
|
761
|
-
user_agent_type, app_config.get_entity_deletion_threshold()
|
|
762
|
-
)
|
|
763
|
-
|
|
764
|
-
logger.info("Resync finished successfully")
|
|
765
|
-
|
|
766
|
-
# Execute resync_complete hooks
|
|
767
|
-
if "resync_complete" in self.event_strategy:
|
|
768
|
-
logger.info("Executing resync_complete hooks")
|
|
769
|
-
|
|
770
|
-
for resync_complete_fn in self.event_strategy["resync_complete"]:
|
|
771
|
-
await resync_complete_fn()
|
|
772
|
-
|
|
773
|
-
logger.info("Finished executing resync_complete hooks")
|
|
774
|
-
|
|
775
|
-
return True
|
|
813
|
+
await self.resync_reconciliation(
|
|
814
|
+
creation_results,
|
|
815
|
+
did_fetched_current_state,
|
|
816
|
+
user_agent_type,
|
|
817
|
+
app_config,
|
|
818
|
+
silent
|
|
819
|
+
)
|
|
820
|
+
await ocean.metrics.report_sync_metrics(kinds=[MetricResourceKind.RECONCILIATION])
|
|
776
821
|
finally:
|
|
777
822
|
await ocean.app.cache_provider.clear()
|
|
823
|
+
if ocean.app.process_execution_mode == ProcessExecutionMode.multi_process:
|
|
824
|
+
ocean.metrics.cleanup_prometheus_metrics()
|
|
@@ -19,6 +19,10 @@ from port_ocean.exceptions.core import (
|
|
|
19
19
|
KindNotImplementedException,
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
+
from port_ocean.utils.async_http import _http_client
|
|
23
|
+
from port_ocean.clients.port.utils import _http_client as _port_http_client
|
|
24
|
+
|
|
25
|
+
from port_ocean.context.ocean import ocean
|
|
22
26
|
|
|
23
27
|
@contextmanager
|
|
24
28
|
def resync_error_handling() -> Generator[None, None, None]:
|
|
@@ -76,6 +80,7 @@ def unsupported_kind_response(
|
|
|
76
80
|
logger.error(f"Kind {kind} is not supported in this integration")
|
|
77
81
|
return [], [KindNotImplementedException(kind, available_resync_kinds)]
|
|
78
82
|
|
|
83
|
+
|
|
79
84
|
class ProcessWrapper(multiprocessing.Process):
|
|
80
85
|
def __init__(self, *args, **kwargs):
|
|
81
86
|
super().__init__(*args, **kwargs)
|
|
@@ -87,4 +92,18 @@ class ProcessWrapper(multiprocessing.Process):
|
|
|
87
92
|
logger.error(f"Process {self.pid} failed with exit code {self.exitcode}")
|
|
88
93
|
else:
|
|
89
94
|
logger.info(f"Process {self.pid} finished with exit code {self.exitcode}")
|
|
95
|
+
ocean.metrics.cleanup_prometheus_metrics(self.pid)
|
|
90
96
|
return super().join()
|
|
97
|
+
|
|
98
|
+
def clear_http_client_context() -> None:
|
|
99
|
+
try:
|
|
100
|
+
while _http_client.top is not None:
|
|
101
|
+
_http_client.pop()
|
|
102
|
+
except (RuntimeError, AttributeError):
|
|
103
|
+
pass
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
while _port_http_client.top is not None:
|
|
107
|
+
_port_http_client.pop()
|
|
108
|
+
except (RuntimeError, AttributeError):
|
|
109
|
+
pass
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import os
|
|
1
2
|
from typing import Any, TYPE_CHECKING, Optional, Dict, List, Tuple
|
|
2
3
|
from fastapi import APIRouter
|
|
3
4
|
from port_ocean.exceptions.context import ResourceContextNotFoundError
|
|
4
5
|
import prometheus_client
|
|
5
6
|
from httpx import AsyncClient
|
|
6
|
-
|
|
7
|
+
from fastapi.responses import PlainTextResponse
|
|
7
8
|
from loguru import logger
|
|
8
|
-
from port_ocean.context import resource
|
|
9
|
+
from port_ocean.context import metric_resource, resource
|
|
9
10
|
from prometheus_client import Gauge
|
|
10
11
|
import prometheus_client.openmetrics
|
|
11
12
|
import prometheus_client.openmetrics.exposition
|
|
@@ -56,6 +57,11 @@ class SyncState:
|
|
|
56
57
|
FAILED = "failed"
|
|
57
58
|
|
|
58
59
|
|
|
60
|
+
class MetricResourceKind:
|
|
61
|
+
RECONCILIATION = "__reconciliation__"
|
|
62
|
+
RESYNC = "__resync__"
|
|
63
|
+
|
|
64
|
+
|
|
59
65
|
# Registry for core and custom metrics
|
|
60
66
|
_metrics_registry: Dict[str, Tuple[str, str, List[str]]] = {
|
|
61
67
|
MetricType.DURATION_NAME: (
|
|
@@ -193,7 +199,21 @@ class Metrics:
|
|
|
193
199
|
"""
|
|
194
200
|
self.get_metric(name, labels).set(value)
|
|
195
201
|
|
|
202
|
+
@staticmethod
|
|
203
|
+
def cleanup_prometheus_metrics(pid: int | None = None) -> None:
|
|
204
|
+
try:
|
|
205
|
+
prometheus_multiproc_dir = os.environ.get("PROMETHEUS_MULTIPROC_DIR")
|
|
206
|
+
for file in os.listdir(prometheus_multiproc_dir):
|
|
207
|
+
if pid:
|
|
208
|
+
if file.endswith(".db") and file[0:-3].split("_")[-1] == str(pid):
|
|
209
|
+
os.remove(f"{prometheus_multiproc_dir}/{file}")
|
|
210
|
+
else:
|
|
211
|
+
os.remove(f"{prometheus_multiproc_dir}/{file}")
|
|
212
|
+
except Exception as e:
|
|
213
|
+
logger.error(f"Failed to cleanup prometheus metrics: {e}")
|
|
214
|
+
|
|
196
215
|
def initialize_metrics(self, kind_blockes: list[str]) -> None:
|
|
216
|
+
self.cleanup_prometheus_metrics()
|
|
197
217
|
for kind in kind_blockes:
|
|
198
218
|
self.set_metric(MetricType.SUCCESS_NAME, [kind, MetricPhase.RESYNC], 0)
|
|
199
219
|
self.set_metric(MetricType.DURATION_NAME, [kind, MetricPhase.RESYNC], 0)
|
|
@@ -237,11 +257,9 @@ class Metrics:
|
|
|
237
257
|
)
|
|
238
258
|
|
|
239
259
|
def create_mertic_router(self) -> APIRouter:
|
|
240
|
-
if not self.enabled:
|
|
241
|
-
return APIRouter()
|
|
242
260
|
router = APIRouter()
|
|
243
261
|
|
|
244
|
-
@router.get("/")
|
|
262
|
+
@router.get("/", response_class=PlainTextResponse)
|
|
245
263
|
async def prom_metrics() -> str:
|
|
246
264
|
return self.generate_latest()
|
|
247
265
|
|
|
@@ -250,6 +268,12 @@ class Metrics:
|
|
|
250
268
|
def current_resource_kind(self) -> str:
|
|
251
269
|
try:
|
|
252
270
|
return f"{resource.resource.kind}-{resource.resource.index}"
|
|
271
|
+
except ResourceContextNotFoundError:
|
|
272
|
+
return self.current_metric_resource_kind()
|
|
273
|
+
|
|
274
|
+
def current_metric_resource_kind(self) -> str:
|
|
275
|
+
try:
|
|
276
|
+
return metric_resource.metric_resource.metric_resource_kind
|
|
253
277
|
except ResourceContextNotFoundError:
|
|
254
278
|
return "__runtime__"
|
|
255
279
|
|
|
@@ -259,15 +283,21 @@ class Metrics:
|
|
|
259
283
|
).decode()
|
|
260
284
|
|
|
261
285
|
async def report_sync_metrics(
|
|
262
|
-
self,
|
|
286
|
+
self,
|
|
287
|
+
metric_name: Optional[str] = None,
|
|
288
|
+
kinds: Optional[list[str]] = None,
|
|
289
|
+
blueprints: Optional[list[Optional[str]]] = None,
|
|
263
290
|
) -> None:
|
|
264
291
|
if kinds is None:
|
|
265
292
|
return None
|
|
266
293
|
|
|
267
294
|
metrics = []
|
|
268
295
|
|
|
269
|
-
|
|
270
|
-
|
|
296
|
+
if blueprints is None:
|
|
297
|
+
blueprints = [None] * len(kinds)
|
|
298
|
+
|
|
299
|
+
for kind, blueprint in zip(kinds, blueprints):
|
|
300
|
+
metric = self.generate_metrics(metric_name, kind, blueprint)
|
|
271
301
|
metrics.extend(metric)
|
|
272
302
|
|
|
273
303
|
try:
|
|
@@ -276,9 +306,12 @@ class Metrics:
|
|
|
276
306
|
logger.error(f"Error posting metrics: {e}", metrics=metrics)
|
|
277
307
|
|
|
278
308
|
async def report_kind_sync_metrics(
|
|
279
|
-
self,
|
|
309
|
+
self,
|
|
310
|
+
metric_name: Optional[str] = None,
|
|
311
|
+
kind: Optional[str] = None,
|
|
312
|
+
blueprint: Optional[str] = None,
|
|
280
313
|
) -> None:
|
|
281
|
-
metrics = self.generate_metrics(metric_name, kind)
|
|
314
|
+
metrics = self.generate_metrics(metric_name, kind, blueprint)
|
|
282
315
|
if not metrics:
|
|
283
316
|
return None
|
|
284
317
|
|
|
@@ -289,7 +322,10 @@ class Metrics:
|
|
|
289
322
|
logger.error(f"Error putting metrics: {e}", metrics=metrics)
|
|
290
323
|
|
|
291
324
|
def generate_metrics(
|
|
292
|
-
self,
|
|
325
|
+
self,
|
|
326
|
+
metric_name: Optional[str] = None,
|
|
327
|
+
kind: Optional[str] = None,
|
|
328
|
+
blueprint: Optional[str] = None,
|
|
293
329
|
) -> list[dict[str, Any]]:
|
|
294
330
|
try:
|
|
295
331
|
latest_raw = self.generate_latest()
|
|
@@ -348,9 +384,10 @@ class Metrics:
|
|
|
348
384
|
if "-" in kind_key
|
|
349
385
|
else kind_key
|
|
350
386
|
),
|
|
351
|
-
"kindIndex":
|
|
387
|
+
"kindIndex": int(kind_key[-1]) if kind_key[-1].isdigit() else 0,
|
|
352
388
|
"eventId": self.event_id,
|
|
353
389
|
"syncState": self.sync_state,
|
|
390
|
+
"blueprint": blueprint if blueprint else "",
|
|
354
391
|
"metrics": metrics,
|
|
355
392
|
}
|
|
356
393
|
events.append(event)
|
|
@@ -2,8 +2,9 @@ from functools import wraps
|
|
|
2
2
|
import time
|
|
3
3
|
from typing import Any, Callable
|
|
4
4
|
|
|
5
|
+
from port_ocean.context.metric_resource import metric_resource_context
|
|
5
6
|
from port_ocean.context.ocean import ocean
|
|
6
|
-
from port_ocean.helpers.metric.metric import MetricType
|
|
7
|
+
from port_ocean.helpers.metric.metric import MetricResourceKind, MetricType
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def TimeMetric(phase: str) -> Any:
|
|
@@ -26,3 +27,26 @@ def TimeMetric(phase: str) -> Any:
|
|
|
26
27
|
return wrapper
|
|
27
28
|
|
|
28
29
|
return decorator
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def TimeMetricWithResourceKind(phase: str) -> Any:
|
|
33
|
+
def decorator(func: Callable[..., Any]) -> Any:
|
|
34
|
+
|
|
35
|
+
@wraps(func)
|
|
36
|
+
async def wrapper(*args: Any, **kwargs: dict[Any, Any]) -> Any:
|
|
37
|
+
async with metric_resource_context(MetricResourceKind.RECONCILIATION):
|
|
38
|
+
start = time.monotonic()
|
|
39
|
+
res = await func(*args, **kwargs)
|
|
40
|
+
end = time.monotonic()
|
|
41
|
+
duration = end - start
|
|
42
|
+
ocean.metrics.inc_metric(
|
|
43
|
+
name=MetricType.DURATION_NAME,
|
|
44
|
+
labels=[ocean.metrics.current_resource_kind(), phase],
|
|
45
|
+
value=duration,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return res
|
|
49
|
+
|
|
50
|
+
return wrapper
|
|
51
|
+
|
|
52
|
+
return decorator
|
port_ocean/ocean.py
CHANGED
|
@@ -154,7 +154,7 @@ async def test_sync_raw_mixin_self_dependency(
|
|
|
154
154
|
|
|
155
155
|
# Add assertions for actual metrics
|
|
156
156
|
metrics = mock_ocean.metrics.generate_metrics()
|
|
157
|
-
assert len(metrics) ==
|
|
157
|
+
assert len(metrics) == 3
|
|
158
158
|
|
|
159
159
|
# Verify object counts
|
|
160
160
|
for metric in metrics:
|
|
@@ -187,7 +187,7 @@ async def test_sync_raw_mixin_self_dependency(
|
|
|
187
187
|
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
188
188
|
"loaded"
|
|
189
189
|
]["object_count"]
|
|
190
|
-
==
|
|
190
|
+
== 1
|
|
191
191
|
)
|
|
192
192
|
|
|
193
193
|
# Verify success
|
|
@@ -196,6 +196,14 @@ async def test_sync_raw_mixin_self_dependency(
|
|
|
196
196
|
# Verify sync state
|
|
197
197
|
assert metric["syncState"] == "completed"
|
|
198
198
|
|
|
199
|
+
if metric["kind"] == "reconciliation":
|
|
200
|
+
assert (
|
|
201
|
+
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
202
|
+
"failed"
|
|
203
|
+
]["object_count"]
|
|
204
|
+
== 1
|
|
205
|
+
)
|
|
206
|
+
|
|
199
207
|
|
|
200
208
|
@pytest.mark.asyncio
|
|
201
209
|
async def test_sync_raw_mixin_circular_dependency(
|
|
@@ -282,7 +290,7 @@ async def test_sync_raw_mixin_circular_dependency(
|
|
|
282
290
|
|
|
283
291
|
# Add assertions for actual metrics
|
|
284
292
|
metrics = mock_ocean.metrics.generate_metrics()
|
|
285
|
-
assert len(metrics) ==
|
|
293
|
+
assert len(metrics) == 3
|
|
286
294
|
|
|
287
295
|
# Verify object counts
|
|
288
296
|
for metric in metrics:
|
|
@@ -315,7 +323,7 @@ async def test_sync_raw_mixin_circular_dependency(
|
|
|
315
323
|
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
316
324
|
"loaded"
|
|
317
325
|
]["object_count"]
|
|
318
|
-
==
|
|
326
|
+
== 0
|
|
319
327
|
)
|
|
320
328
|
|
|
321
329
|
# Verify success
|
|
@@ -324,6 +332,14 @@ async def test_sync_raw_mixin_circular_dependency(
|
|
|
324
332
|
# Verify sync state
|
|
325
333
|
assert metric["syncState"] == "completed"
|
|
326
334
|
|
|
335
|
+
if metric["kind"] == "reconciliation":
|
|
336
|
+
assert (
|
|
337
|
+
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
338
|
+
"loaded"
|
|
339
|
+
]["object_count"]
|
|
340
|
+
== 2
|
|
341
|
+
)
|
|
342
|
+
|
|
327
343
|
|
|
328
344
|
@pytest.mark.asyncio
|
|
329
345
|
async def test_sync_raw_mixin_dependency(
|
|
@@ -422,7 +438,7 @@ async def test_sync_raw_mixin_dependency(
|
|
|
422
438
|
|
|
423
439
|
# Add assertions for actual metrics
|
|
424
440
|
metrics = mock_ocean.metrics.generate_metrics()
|
|
425
|
-
assert len(metrics) ==
|
|
441
|
+
assert len(metrics) == 3
|
|
426
442
|
|
|
427
443
|
# Verify object counts
|
|
428
444
|
for metric in metrics:
|
|
@@ -458,6 +474,14 @@ async def test_sync_raw_mixin_dependency(
|
|
|
458
474
|
# Verify sync state
|
|
459
475
|
assert metric["syncState"] == "completed"
|
|
460
476
|
|
|
477
|
+
if metric["kind"] == "reconciliation":
|
|
478
|
+
assert (
|
|
479
|
+
metric["metrics"]["phase"]["load"]["object_count_type"][
|
|
480
|
+
"loaded"
|
|
481
|
+
]["object_count"]
|
|
482
|
+
== 5
|
|
483
|
+
)
|
|
484
|
+
|
|
461
485
|
|
|
462
486
|
@pytest.mark.asyncio
|
|
463
487
|
async def test_register_raw(
|
port_ocean/utils/ipc.py
CHANGED
|
@@ -7,7 +7,7 @@ class FileIPC:
|
|
|
7
7
|
def __init__(self, process_id: str, name: str, default_return: Any = None):
|
|
8
8
|
self.process_id = process_id
|
|
9
9
|
self.name = name
|
|
10
|
-
self.dir_path = f"/tmp/p_{self.process_id}"
|
|
10
|
+
self.dir_path = f"/tmp/ocean/processes/p_{self.process_id}"
|
|
11
11
|
self.file_path = f"{self.dir_path}/{self.name}.pkl"
|
|
12
12
|
self.default_return = default_return
|
|
13
13
|
os.makedirs(self.dir_path, exist_ok=True)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
integrations/_infra/Dockerfile.Deb,sha256=
|
|
1
|
+
integrations/_infra/Dockerfile.Deb,sha256=JMyxgvJxLeo3Vcys-w6m_F6fxfu6JyAcjAMPuXsNQBM,1679
|
|
2
2
|
integrations/_infra/Dockerfile.alpine,sha256=7E4Sb-8supsCcseerHwTkuzjHZoYcaHIyxiBZ-wewo0,3482
|
|
3
3
|
integrations/_infra/Dockerfile.base.builder,sha256=ESe1PKC6itp_AuXawbLI75k1Kruny6NTANaTinxOgVs,743
|
|
4
4
|
integrations/_infra/Dockerfile.base.runner,sha256=uAcs2IsxrAAUHGXt_qULA5INr-HFguf5a5fCKiqEzbY,384
|
|
5
5
|
integrations/_infra/Dockerfile.dockerignore,sha256=CM1Fxt3I2AvSvObuUZRmy5BNLSGC7ylnbpWzFgD4cso,1163
|
|
6
|
-
integrations/_infra/Dockerfile.local,sha256=
|
|
6
|
+
integrations/_infra/Dockerfile.local,sha256=4M5wyksRcGQ0WSXfDcJsHshE8uUSQw5yf6O3JK_DyK4,1035
|
|
7
7
|
integrations/_infra/Makefile,sha256=YgLKvuF_Dw4IA7X98Nus6zIW_3cJ60M1QFGs3imj5c4,2430
|
|
8
|
-
integrations/_infra/entry_local.sh,sha256=
|
|
8
|
+
integrations/_infra/entry_local.sh,sha256=GIuAXACcYv4DDxH6ubDidNJboqmAJYW4OanUK_VW4nQ,547
|
|
9
9
|
integrations/_infra/grpcio.sh,sha256=m924poYznoRZ6Tt7Ct8Cs5AV_cmmOx598yIZ3z4DvZE,616
|
|
10
10
|
integrations/_infra/init.sh,sha256=nN8lTrOhB286UfFvD6sJ9YJ-9asT9zVSddQB-RAb7Z4,99
|
|
11
11
|
port_ocean/__init__.py,sha256=uMpjg5d_cXgnyCxA_LmICR8zqBmC6Fe9Ivu9hcvJ7EY,313
|
|
@@ -60,7 +60,7 @@ port_ocean/clients/port/client.py,sha256=dv0mxIOde6J-wFi1FXXZkoNPVHrZzY7RSMhNkDD
|
|
|
60
60
|
port_ocean/clients/port/mixins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
61
61
|
port_ocean/clients/port/mixins/blueprints.py,sha256=aMCG4zePsMSMjMLiGrU37h5z5_ElfMzTcTvqvOI5wXY,4683
|
|
62
62
|
port_ocean/clients/port/mixins/entities.py,sha256=-1Gs74z_8eviWItHIpveQhKdA7gnjbqZ3STS4jgGONs,11668
|
|
63
|
-
port_ocean/clients/port/mixins/integrations.py,sha256=
|
|
63
|
+
port_ocean/clients/port/mixins/integrations.py,sha256=s6paomK9bYWW-Tu3y2OIaEGSxsXCHyhapVi4JIhhO64,11162
|
|
64
64
|
port_ocean/clients/port/mixins/migrations.py,sha256=vdL_A_NNUogvzujyaRLIoZEu5vmKDY2BxTjoGP94YzI,1467
|
|
65
65
|
port_ocean/clients/port/mixins/organization.py,sha256=A2cP5V49KnjoAXxjmnm_XGth4ftPSU0qURNfnyUyS_Y,1041
|
|
66
66
|
port_ocean/clients/port/retry_transport.py,sha256=PtIZOAZ6V-ncpVysRUsPOgt8Sf01QLnTKB5YeKBxkJk,1861
|
|
@@ -74,6 +74,7 @@ port_ocean/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
74
74
|
port_ocean/consumers/kafka_consumer.py,sha256=N8KocjBi9aR0BOPG8hgKovg-ns_ggpEjrSxqSqF_BSo,4710
|
|
75
75
|
port_ocean/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
76
76
|
port_ocean/context/event.py,sha256=z7DBNOPOL9P3s-SR8jpgwoyaQ6IL9vZxLaAxIjv1Faw,6493
|
|
77
|
+
port_ocean/context/metric_resource.py,sha256=_EbLRjv3r1y6E3B9mU10wuGg_oepD8zo3VueaJMeH6A,1689
|
|
77
78
|
port_ocean/context/ocean.py,sha256=xWD30AbStQK44QFW9ad5pO4i5dQG7uouvjuHScKnJOM,7884
|
|
78
79
|
port_ocean/context/resource.py,sha256=WQlPzplxWic0hvvMxWKu8xPfQQC0VjrsJVeA6yZytw0,1712
|
|
79
80
|
port_ocean/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -108,7 +109,7 @@ port_ocean/core/handlers/queue/__init__.py,sha256=1fICM0ZLATmmj6f7cdq_eV2kmw0_jy
|
|
|
108
109
|
port_ocean/core/handlers/queue/abstract_queue.py,sha256=q_gpaWFFZHxM3XovEbgsDn8jEOLM45iAZWVC81Paxto,620
|
|
109
110
|
port_ocean/core/handlers/queue/local_queue.py,sha256=EzqsGIX43xbVAcePwTcCg5QDrXATQpy-VzWxxN_OyAM,574
|
|
110
111
|
port_ocean/core/handlers/resync_state_updater/__init__.py,sha256=kG6y-JQGpPfuTHh912L_bctIDCzAK4DN-d00S7rguWU,81
|
|
111
|
-
port_ocean/core/handlers/resync_state_updater/updater.py,sha256=
|
|
112
|
+
port_ocean/core/handlers/resync_state_updater/updater.py,sha256=TRYq6QnTtPlJg6MvgZPtQdZPvkAhkvpcmWjtkxCnkg4,3764
|
|
112
113
|
port_ocean/core/handlers/webhook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
113
114
|
port_ocean/core/handlers/webhook/abstract_webhook_processor.py,sha256=5KwZkdkDd5HdVkXPzKiqabodZKl-hOtMypkTKd8Hq3M,3891
|
|
114
115
|
port_ocean/core/handlers/webhook/processor_manager.py,sha256=ipyAXoFtF84EGczyzRcZCzQG4Ippjo4eMsnVxMVz12A,12072
|
|
@@ -120,8 +121,8 @@ port_ocean/core/integrations/mixins/events.py,sha256=2L7P3Jhp8XBqddh2_o9Cn4N261n
|
|
|
120
121
|
port_ocean/core/integrations/mixins/handler.py,sha256=mZ7-0UlG3LcrwJttFbMe-R4xcOU2H_g33tZar7PwTv8,3771
|
|
121
122
|
port_ocean/core/integrations/mixins/live_events.py,sha256=8HklZmlyffYY_LeDe8xbt3Tb08rlLkqVhFF-2NQeJP4,4126
|
|
122
123
|
port_ocean/core/integrations/mixins/sync.py,sha256=Vm_898pLKBwfVewtwouDWsXoxcOLicnAy6pzyqqk6U8,4053
|
|
123
|
-
port_ocean/core/integrations/mixins/sync_raw.py,sha256=
|
|
124
|
-
port_ocean/core/integrations/mixins/utils.py,sha256=
|
|
124
|
+
port_ocean/core/integrations/mixins/sync_raw.py,sha256=wZrn0Bz05HmVKDgAJ5LPvP54nSUDpE8uHvjHAYqXVD4,33987
|
|
125
|
+
port_ocean/core/integrations/mixins/utils.py,sha256=g1XbC12dswefQ-NpcLSCqFtd_WRp2bTL98jyZ5rRbGk,3444
|
|
125
126
|
port_ocean/core/models.py,sha256=MKfq69zGbFRzo0I2HRDUvSbz_pjrtcFVsD5B4Qwa3fw,2538
|
|
126
127
|
port_ocean/core/ocean_types.py,sha256=4VipWFOHEh_d9LmWewQccwx1p2dtrRYW0YURVgNsAjo,1398
|
|
127
128
|
port_ocean/core/utils/entity_topological_sorter.py,sha256=MDUjM6OuDy4Xj68o-7InNN0w1jqjxeDfeY8U02vySNI,3081
|
|
@@ -138,15 +139,15 @@ port_ocean/exceptions/utils.py,sha256=gjOqpi-HpY1l4WlMFsGA9yzhxDhajhoGGdDDyGbLnq
|
|
|
138
139
|
port_ocean/exceptions/webhook_processor.py,sha256=yQYazg53Y-ohb7HfViwq1opH_ZUuUdhHSRxcUNveFpI,114
|
|
139
140
|
port_ocean/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
140
141
|
port_ocean/helpers/async_client.py,sha256=SRlP6o7_FCSY3UHnRlZdezppePVxxOzZ0z861vE3K40,1783
|
|
141
|
-
port_ocean/helpers/metric/metric.py,sha256=
|
|
142
|
-
port_ocean/helpers/metric/utils.py,sha256=
|
|
142
|
+
port_ocean/helpers/metric/metric.py,sha256=mlXH4T_ehShZ0XjGkng3h4MDv7Myw0upwTlpAoC84tA,14395
|
|
143
|
+
port_ocean/helpers/metric/utils.py,sha256=1lAgrxnZLuR_wUNDyPOPzLrm32b8cDdioob2lvnPQ1A,1619
|
|
143
144
|
port_ocean/helpers/retry.py,sha256=gmS4YxM6N4fboFp7GSgtOzyBJemxs46bnrz4L4rDS6Y,16136
|
|
144
145
|
port_ocean/log/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
145
146
|
port_ocean/log/handlers.py,sha256=ncVjgqrZRh6BhyRrA6DQG86Wsbxph1yWYuEC0cWfe-Q,3631
|
|
146
147
|
port_ocean/log/logger_setup.py,sha256=0K3zVG0YYrYOWEV8-rCGks1o-bMRxgHXlqawu9w_tSw,2656
|
|
147
148
|
port_ocean/log/sensetive.py,sha256=lVKiZH6b7TkrZAMmhEJRhcl67HNM94e56x12DwFgCQk,2920
|
|
148
149
|
port_ocean/middlewares.py,sha256=9wYCdyzRZGK1vjEJ28FY_DkfwDNENmXp504UKPf5NaQ,2727
|
|
149
|
-
port_ocean/ocean.py,sha256=
|
|
150
|
+
port_ocean/ocean.py,sha256=83zgTEI5o2wfl8mq-iIC9DzPOZbWyCqNIy1BEjv9TOk,8824
|
|
150
151
|
port_ocean/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
151
152
|
port_ocean/run.py,sha256=CmKz14bxfdOooNbQ5QqH1MwX-XLYVG4NgT4KbrzFaqI,2216
|
|
152
153
|
port_ocean/sonar-project.properties,sha256=X_wLzDOkEVmpGLRMb2fg9Rb0DxWwUFSvESId8qpvrPI,73
|
|
@@ -165,7 +166,7 @@ port_ocean/tests/core/defaults/test_common.py,sha256=sR7RqB3ZYV6Xn6NIg-c8k5K6JcG
|
|
|
165
166
|
port_ocean/tests/core/handlers/entities_state_applier/test_applier.py,sha256=WNg1fWZsXu0MDnz9-ahRiPb_OPofWx7E8wxBx0cyZKs,8946
|
|
166
167
|
port_ocean/tests/core/handlers/entity_processor/test_jq_entity_processor.py,sha256=8WpMn559Mf0TFWmloRpZrVgr6yWwyA0C4n2lVHCtyq4,13596
|
|
167
168
|
port_ocean/tests/core/handlers/mixins/test_live_events.py,sha256=iAwVpr3n3PIkXQLw7hxd-iB_SR_vyfletVXJLOmyz28,12480
|
|
168
|
-
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=
|
|
169
|
+
port_ocean/tests/core/handlers/mixins/test_sync_raw.py,sha256=iK-lfu6vk-DBScYm-nLvRzbj0ImbKUN4LfNLXOb8Z3Q,43413
|
|
169
170
|
port_ocean/tests/core/handlers/port_app_config/test_api.py,sha256=eJZ6SuFBLz71y4ca3DNqKag6d6HUjNJS0aqQPwiLMTI,1999
|
|
170
171
|
port_ocean/tests/core/handlers/port_app_config/test_base.py,sha256=hSh556bJM9zuELwhwnyKSfd9z06WqWXIfe-6hCl5iKI,9799
|
|
171
172
|
port_ocean/tests/core/handlers/queue/test_local_queue.py,sha256=9Ly0HzZXbs6Rbl_bstsIdInC3h2bgABU3roP9S_PnJM,2582
|
|
@@ -192,15 +193,15 @@ port_ocean/utils/__init__.py,sha256=KMGnCPXZJbNwtgxtyMycapkDz8tpSyw23MSYT3iVeHs,
|
|
|
192
193
|
port_ocean/utils/async_http.py,sha256=aDsw3gQIMwt6qLegbZtkHqD8em48tKvbITnblsrTY3g,1260
|
|
193
194
|
port_ocean/utils/async_iterators.py,sha256=CPXskYWkhkZtAG-ducEwM8537t3z5usPEqXR9vcivzw,3715
|
|
194
195
|
port_ocean/utils/cache.py,sha256=tRwPomG2VIxx8ZNi4QYH6Yc47d9yYV1A7Hx-L_fX4Dg,4494
|
|
195
|
-
port_ocean/utils/ipc.py,sha256=
|
|
196
|
+
port_ocean/utils/ipc.py,sha256=eTjTTvsKl6IXYeOkIjP5iyrw-8gLQ9rf15WeyxCqXog,912
|
|
196
197
|
port_ocean/utils/misc.py,sha256=0q2cJ5psqxn_5u_56pT7vOVQ3shDM02iC1lzyWQ_zl0,2098
|
|
197
198
|
port_ocean/utils/queue_utils.py,sha256=KWWl8YVnG-glcfIHhM6nefY-2sou_C6DVP1VynQwzB4,2762
|
|
198
199
|
port_ocean/utils/repeat.py,sha256=U2OeCkHPWXmRTVoPV-VcJRlQhcYqPWI5NfmPlb1JIbc,3229
|
|
199
200
|
port_ocean/utils/signal.py,sha256=mMVq-1Ab5YpNiqN4PkiyTGlV_G0wkUDMMjTZp5z3pb0,1514
|
|
200
201
|
port_ocean/utils/time.py,sha256=pufAOH5ZQI7gXvOvJoQXZXZJV-Dqktoj9Qp9eiRwmJ4,1939
|
|
201
202
|
port_ocean/version.py,sha256=UsuJdvdQlazzKGD3Hd5-U7N69STh8Dq9ggJzQFnu9fU,177
|
|
202
|
-
port_ocean-0.23.
|
|
203
|
-
port_ocean-0.23.
|
|
204
|
-
port_ocean-0.23.
|
|
205
|
-
port_ocean-0.23.
|
|
206
|
-
port_ocean-0.23.
|
|
203
|
+
port_ocean-0.23.4.dist-info/LICENSE.md,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
|
|
204
|
+
port_ocean-0.23.4.dist-info/METADATA,sha256=6drOs6277IC86mO9ZhmdCjVUKy6QL2IqVTi_6x7vyI4,6764
|
|
205
|
+
port_ocean-0.23.4.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
206
|
+
port_ocean-0.23.4.dist-info/entry_points.txt,sha256=F_DNUmGZU2Kme-8NsWM5LLE8piGMafYZygRYhOVtcjA,54
|
|
207
|
+
port_ocean-0.23.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|