Skip to content

Commit

Permalink
Merge branch 'fix/improve_performance_by_request_caching' into 'main'
Browse files Browse the repository at this point in the history
Improve performance by request caching

See merge request espressif/idf-component-manager!466
  • Loading branch information
hfudev committed Nov 25, 2024
2 parents b256811 + 857d18b commit d9b40aa
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 2 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# IDF Component Manager

The IDF Component Manager is a tool that ensures the correct versions of all components required for a successful build are present in your [ESP-IDF](https://www.espressif.com/en/products/sdks/esp-idf) project.

- The Component Manager downloads the dependencies for your project automatically during a `CMake` run.
- The components can be sourced either from [the ESP Component Registry](https://components.espressif.com/) or from a Git repository.
- A list of components can be found at https://components.espressif.com/

## Contributing to the project

See [CONTRIBUTING.md](CONTRIBUTING.md)

## Resources

- [Offical Documentation at docs.espressif.com](https://docs.espressif.com/projects/idf-component-manager/en/latest/)
- The Python Package Index project page https://pypi.org/project/idf-component-manager/
- The Component Manager section in the [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html)
Expand Down Expand Up @@ -43,7 +46,6 @@ python -m pip uninstall -y idf-component-manager
python -m pip install git+https://github.com/espressif/idf-component-manager.git@main
```


## Disabling the Component Manager

The Component Manager can be explicitly disabled by setting `IDF_COMPONENT_MANAGER` environment variable to `0`.
Expand Down Expand Up @@ -164,3 +166,4 @@ examples:
| IDF_COMPONENT_SUPPRESS_UNKNOWN_FILE_WARNINGS | 0 | Ignore unknown files in managed_components directory |
| IDF_COMPONENT_CHECK_NEW_VERSION | 1 | Check for new versions of components |
| IDF_COMPONENT_VERIFY_SSL | 1 | Verify SSL certificates when making requests to the registry, set it 0 to disable or provide a CA bundle path |
| IDF_COMPONENT_CACHE_HTTP_REQUESTS | 1 | Cache HTTP requests to the registry during runtime, set it 0 to disable |
1 change: 1 addition & 0 deletions idf_component_tools/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class ComponentManagerSettings(BaseSettings):
)
API_TOKEN: t.Optional[str] = None
VERIFY_SSL: t.Union[bool, str] = True
CACHE_HTTP_REQUESTS: bool = True
PROFILE: t.Optional[str] = Field(
default=None,
validation_alias=AliasChoices(
Expand Down
2 changes: 2 additions & 0 deletions idf_component_tools/registry/base_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
import platform
import typing as t
from functools import lru_cache

import requests
from requests.adapters import HTTPAdapter
Expand All @@ -24,6 +25,7 @@
MAX_RETRIES = 3


@lru_cache(maxsize=None)
def create_session(
token: t.Optional[str] = None,
) -> requests.Session:
Expand Down
24 changes: 23 additions & 1 deletion idf_component_tools/registry/request_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from pydantic import ValidationError
from requests import Response

from idf_component_tools import ComponentManagerSettings
from idf_component_tools import ComponentManagerSettings, debug

from .api_models import ApiBaseModel, ErrorResponse
from .client_errors import (
Expand All @@ -25,6 +25,9 @@
60.1, # Read timeout
)

# Storage for caching requests
_request_cache: t.Dict[t.Tuple[t.Any], Response] = {}


def join_url(*args) -> str:
"""
Expand All @@ -34,6 +37,23 @@ def join_url(*args) -> str:
return '/'.join(parts)


def cache_request(func):
"""Decorator to conditionally cache function output based on CACHE_HTTP_REQUESTS"""

def wrapper(*args, **kwargs):
if ComponentManagerSettings().CACHE_HTTP_REQUESTS:
cache_key = (args, frozenset(kwargs.items()))
if cache_key in _request_cache:
return _request_cache[cache_key]
result = func(*args, **kwargs)
_request_cache[cache_key] = result
return result
return func(*args, **kwargs)

return wrapper


@cache_request
def make_request(
method: str,
session: requests.Session,
Expand All @@ -44,6 +64,7 @@ def make_request(
timeout: t.Union[float, t.Tuple[float, float]],
) -> Response:
try:
debug(f'HTTP request: {method.upper()} {endpoint}')
response = session.request(
method,
endpoint,
Expand All @@ -59,6 +80,7 @@ def make_request(
except requests.exceptions.RequestException as e:
raise APIClientError(f'HTTP request error {e}', endpoint=endpoint)

debug(f'HTTP response: {response.status_code} total: {response.elapsed.total_seconds()}s')
return response


Expand Down
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ def monkeypatch_idf_version_and_tools_path(monkeypatch, tmp_path):
monkeypatch.setenv('IDF_TOOLS_PATH', str(tmp_path))


@pytest.fixture(autouse=True)
def monkeypatch_disable_request_cache(monkeypatch):
monkeypatch.setenv('IDF_COMPONENT_CACHE_HTTP_REQUESTS', '0')


@pytest.fixture()
def valid_manifest():
return {
Expand Down

0 comments on commit d9b40aa

Please sign in to comment.