Skip to content

Commit a9bcc4e

Browse files
committed
feat(metrics): add async support for metrics operations
- add AsyncMetrics class for async metrics retrieval - add async tests for metrics functionality - add async fixtures for testing async metrics operations
1 parent 02faf2a commit a9bcc4e

File tree

3 files changed

+145
-2
lines changed

3 files changed

+145
-2
lines changed

src/typesense/async_metrics.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
This module provides async functionality for retrieving metrics from the Typesense API.
3+
4+
It contains the AsyncMetrics class, which handles async API operations for retrieving
5+
system and Typesense metrics such as CPU, memory, disk, and network usage.
6+
7+
Classes:
8+
MetricsResponse: Type definition for metrics response (imported from typesense.metrics).
9+
AsyncMetrics: Manages async retrieval of metrics from the Typesense API.
10+
11+
Dependencies:
12+
- typesense.async_api_call: Provides the AsyncApiCall class for making async API requests.
13+
- typesense.metrics: Provides MetricsResponse type definitions.
14+
15+
Note: This module uses conditional imports to support both Python 3.11+ and earlier versions.
16+
"""
17+
18+
import sys
19+
20+
if sys.version_info >= (3, 11):
21+
import typing
22+
else:
23+
import typing_extensions as typing
24+
25+
from typesense.async_api_call import AsyncApiCall
26+
from typesense.metrics import MetricsResponse
27+
28+
29+
class AsyncMetrics:
30+
"""
31+
Manages async metrics retrieval from the Typesense API.
32+
33+
This class provides async methods to retrieve system and Typesense metrics
34+
such as CPU, memory, disk, and network usage.
35+
36+
Attributes:
37+
resource_path (str): The base path for metrics endpoint.
38+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
39+
"""
40+
41+
resource_path: typing.Final[str] = "/metrics.json"
42+
43+
def __init__(self, api_call: AsyncApiCall):
44+
"""
45+
Initialize the AsyncMetrics instance.
46+
47+
Args:
48+
api_call (AsyncApiCall): The AsyncApiCall instance for making async API requests.
49+
"""
50+
self.api_call = api_call
51+
52+
async def retrieve(self) -> MetricsResponse:
53+
"""
54+
Retrieve metrics from the Typesense API.
55+
56+
Returns:
57+
MetricsResponse: A dictionary containing system and Typesense metrics.
58+
59+
Example:
60+
>>> metrics = AsyncMetrics(async_api_call)
61+
>>> response = await metrics.retrieve()
62+
>>> print(response["system_cpu_active_percentage"])
63+
"""
64+
response: MetricsResponse = await self.api_call.get(
65+
AsyncMetrics.resource_path,
66+
as_json=True,
67+
entity_type=MetricsResponse,
68+
)
69+
return response

tests/fixtures/metrics_fixtures.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,24 @@
33
import pytest
44

55
from typesense.api_call import ApiCall
6+
from typesense.async_api_call import AsyncApiCall
7+
from typesense.async_metrics import AsyncMetrics
68
from typesense.metrics import Metrics
79

810

911
@pytest.fixture(scope="function", name="actual_metrics")
1012
def actual_debug_fixture(actual_api_call: ApiCall) -> Metrics:
1113
"""Return a Debug object using a real API."""
1214
return Metrics(actual_api_call)
15+
16+
17+
@pytest.fixture(scope="function", name="actual_async_metrics")
18+
def actual_async_metrics_fixture(actual_async_api_call: AsyncApiCall) -> AsyncMetrics:
19+
"""Return a AsyncMetrics object using a real API."""
20+
return AsyncMetrics(actual_async_api_call)
21+
22+
23+
@pytest.fixture(scope="function", name="fake_async_metrics")
24+
def fake_async_metrics_fixture(fake_async_api_call: AsyncApiCall) -> AsyncMetrics:
25+
"""Return a AsyncMetrics object with test values."""
26+
return AsyncMetrics(fake_async_api_call)

tests/metrics_test.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,51 @@
1-
"""Tests for the Debug class."""
1+
"""Tests for the Metrics class."""
22

33
from __future__ import annotations
44

5+
from tests.utils.object_assertions import (
6+
assert_match_object,
7+
assert_object_lists_match,
8+
)
9+
from typesense.api_call import ApiCall
10+
from typesense.async_api_call import AsyncApiCall
11+
from typesense.async_metrics import AsyncMetrics
512
from typesense.metrics import Metrics
613

714

15+
def test_init(fake_api_call: ApiCall) -> None:
16+
"""Test that the Metrics object is initialized correctly."""
17+
metrics = Metrics(fake_api_call)
18+
19+
assert_match_object(metrics.api_call, fake_api_call)
20+
assert_object_lists_match(
21+
metrics.api_call.node_manager.nodes,
22+
fake_api_call.node_manager.nodes,
23+
)
24+
assert_match_object(
25+
metrics.api_call.config.nearest_node,
26+
fake_api_call.config.nearest_node,
27+
)
28+
assert metrics.resource_path == "/metrics.json" # noqa: WPS437
29+
30+
31+
def test_init_async(fake_async_api_call: AsyncApiCall) -> None:
32+
"""Test that the AsyncMetrics object is initialized correctly."""
33+
metrics = AsyncMetrics(fake_async_api_call)
34+
35+
assert_match_object(metrics.api_call, fake_async_api_call)
36+
assert_object_lists_match(
37+
metrics.api_call.node_manager.nodes,
38+
fake_async_api_call.node_manager.nodes,
39+
)
40+
assert_match_object(
41+
metrics.api_call.config.nearest_node,
42+
fake_async_api_call.config.nearest_node,
43+
)
44+
assert metrics.resource_path == "/metrics.json" # noqa: WPS437
45+
46+
847
def test_actual_retrieve(actual_metrics: Metrics) -> None:
9-
"""Test that the Debug object can retrieve a debug on Typesense server and verify response structure."""
48+
"""Test that the Metrics object can retrieve metrics on Typesense server and verify response structure."""
1049
response = actual_metrics.retrieve()
1150

1251
assert "system_cpu_active_percentage" in response
@@ -24,3 +63,24 @@ def test_actual_retrieve(actual_metrics: Metrics) -> None:
2463
assert "typesense_memory_metadata_bytes" in response
2564
assert "typesense_memory_resident_bytes" in response
2665
assert "typesense_memory_retained_bytes" in response
66+
67+
68+
async def test_actual_retrieve_async(actual_async_metrics: AsyncMetrics) -> None:
69+
"""Test that the AsyncMetrics object can retrieve metrics on Typesense server and verify response structure."""
70+
response = await actual_async_metrics.retrieve()
71+
72+
assert "system_cpu_active_percentage" in response
73+
assert "system_disk_total_bytes" in response
74+
assert "system_disk_used_bytes" in response
75+
assert "system_memory_total_bytes" in response
76+
assert "system_memory_used_bytes" in response
77+
assert "system_network_received_bytes" in response
78+
assert "system_network_sent_bytes" in response
79+
assert "typesense_memory_active_bytes" in response
80+
assert "typesense_memory_allocated_bytes" in response
81+
assert "typesense_memory_fragmentation_ratio" in response
82+
83+
assert "typesense_memory_mapped_bytes" in response
84+
assert "typesense_memory_metadata_bytes" in response
85+
assert "typesense_memory_resident_bytes" in response
86+
assert "typesense_memory_retained_bytes" in response

0 commit comments

Comments
 (0)