Skip to content

Commit

Permalink
feat(server): caching LRU (#1073)
Browse files Browse the repository at this point in the history
  • Loading branch information
alambare authored Jun 14, 2024
1 parent e76f4a8 commit 60338b6
Show file tree
Hide file tree
Showing 22 changed files with 688 additions and 477 deletions.
3 changes: 2 additions & 1 deletion NOTICE
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ https://github.com/annotated-types/annotated-types
https://github.com/pypa/setuptools
https://github.com/pydantic/pydantic
https://github.com/pydantic/pydantic-core
https://github.com/pydantic/pydantic-settings
https://github.com/geopython/pygeofilter

https://github.com/tkem/cachetools

================================================================
The BSD-2-Clause Licence
Expand Down
6 changes: 3 additions & 3 deletions charts/eodag-server/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dependencies:
- name: common
repository: oci://registry-1.docker.io/bitnamicharts
version: 2.4.0
digest: sha256:b371e6f7f1449fa3abdcb97a04b0bbb2b5d36a4facb8e79041ac36a455b02bb0
generated: "2023-06-19T12:39:44.271254606+02:00"
version: 2.19.3
digest: sha256:de997835d9ce9a9deefc2d70d8c62b11aa1d1a76ece9e86a83736ab9f930bf4d
generated: "2024-06-03T18:43:30.71045378+02:00"
2 changes: 1 addition & 1 deletion charts/eodag-server/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ dependencies:
repository: oci://registry-1.docker.io/bitnamicharts
tags:
- bitnami-common
version: 2.x
version: 2.x.x
description: EODAG (Earth Observation Data Access Gateway) is a tool for searching,
aggregating results and downloading remote sensed images offering a unified API
for data access regardless of the data provider.
Expand Down
7 changes: 5 additions & 2 deletions charts/eodag-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ The command removes all the Kubernetes components associated with the chart and
| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` |
| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` |


### Common parameters

| Name | Description | Value |
Expand All @@ -60,7 +59,6 @@ The command removes all the Kubernetes components associated with the chart and
| `clusterDomain` | Kubernetes cluster domain name | `cluster.local` |
| `extraDeploy` | Array of extra objects to deploy with the release | `[]` |


### EODAG Server parameters

| Name | Description | Value |
Expand Down Expand Up @@ -179,6 +177,11 @@ The command removes all the Kubernetes components associated with the chart and
| `serviceAccount.name` | The name of the ServiceAccount to use. | `""` |
| `serviceAccount.annotations` | Additional custom annotations for the ServiceAccount | `{}` |
| `serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` |
| `autoscaling.enabled` | Enable autoscaling | `false` |
| `autoscaling.minReplicas` | Minimum number of replicas | `1` |
| `autoscaling.maxReplicas` | Maximum number of replicas | `10` |
| `autoscaling.targetCPU` | Target CPU utilization percentage | `""` |
| `autoscaling.targetMemory` | Target Memory utilization percentage | `""` |


```console
Expand Down
48 changes: 48 additions & 0 deletions charts/eodag-server/templates/hpa.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{{- /*
Copyright Broadcom, Inc. All Rights Reserved.
SPDX-License-Identifier: APACHE-2.0
*/}}

{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "common.names.fullname" . }}
namespace: {{ include "common.names.namespace" . | quote }}
labels: {{- include "common.labels.standard" ( dict "customLabels" .Values.commonLabels "context" $ ) | nindent 4 }}
{{- if .Values.commonAnnotations }}
annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }}
{{- end }}
spec:
scaleTargetRef:
apiVersion: {{ include "common.capabilities.deployment.apiVersion" . }}
kind: Deployment
name: {{ include "common.names.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetMemory }}
- type: Resource
resource:
name: memory
{{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }}
targetAverageUtilization: {{ .Values.autoscaling.targetMemory }}
{{- else }}
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemory }}
{{- end }}
{{- end }}
{{- if .Values.autoscaling.targetCPU }}
- type: Resource
resource:
name: cpu
{{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .) }}
targetAverageUtilization: {{ .Values.autoscaling.targetCPU }}
{{- else }}
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPU }}
{{- end }}
{{- end }}
{{- end }}
14 changes: 14 additions & 0 deletions charts/eodag-server/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -690,3 +690,17 @@ serviceAccount:
## @param serviceAccount.automountServiceAccountToken Automount service account token for the server service account
##
automountServiceAccountToken: true

## Autoscaling configuration
## @param autoscaling.enabled Enable autoscaling
## @param autoscaling.minReplicas Minimum number of replicas
## @param autoscaling.maxReplicas Maximum number of replicas
## @param autoscaling.targetCPU Target CPU utilization percentage
## @param autoscaling.targetMemory Target Memory utilization percentage
##
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 10
targetCPU: ""
targetMemory: ""
37 changes: 6 additions & 31 deletions eodag/api/product/_product.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import requests
from requests import RequestException
from requests.auth import AuthBase
from shapely import geometry, wkb, wkt
from shapely import geometry
from shapely.errors import ShapelyError

try:
Expand Down Expand Up @@ -149,36 +149,11 @@ def __init__(
product_geometry = properties.pop("defaultGeometry", DEFAULT_GEOMETRY)
else:
product_geometry = properties["geometry"]
# Let's try 'latmin lonmin latmax lonmax'
if isinstance(product_geometry, str):
bbox_pattern = re.compile(
r"^(-?\d+\.?\d*) (-?\d+\.?\d*) (-?\d+\.?\d*) (-?\d+\.?\d*)$"
)
found_bbox = bbox_pattern.match(product_geometry)
if found_bbox:
coords = found_bbox.groups()
if len(coords) == 4:
product_geometry = geometry.box(
float(coords[1]),
float(coords[0]),
float(coords[3]),
float(coords[2]),
)
# Best effort to understand provider specific geometry (the default is to
# assume an object implementing the Geo Interface: see
# https://gist.github.com/2217756)
if isinstance(product_geometry, str):
try:
product_geometry = wkt.loads(product_geometry)
except (ShapelyError, GEOSException):
try:
product_geometry = wkb.loads(product_geometry)
# Also catching TypeError because product_geometry can be a
# string and not a bytes string
except (ShapelyError, GEOSException, TypeError):
# Giv up!
raise
self.geometry = self.search_intersection = geometry.shape(product_geometry)

self.geometry = self.search_intersection = get_geometry_from_various(
geometry=product_geometry
)

self.search_kwargs = kwargs
if self.search_kwargs.get("geometry") is not None:
searched_geom = get_geometry_from_various(
Expand Down
70 changes: 70 additions & 0 deletions eodag/rest/cache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
# Copyright 2024, CS GROUP - France, https://www.csgroup.eu/
#
# This file is part of EODAG project
# https://www.github.com/CS-SI/EODAG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
from typing import Any, Callable, Coroutine, Dict, TypeVar, cast

import orjson
from cachetools import LRUCache
from fastapi import FastAPI, Request

from eodag.rest.config import Settings
from eodag.utils import urlsplit

logger = logging.getLogger("eodag.rest.utils")

T = TypeVar("T")


def init_cache(app: FastAPI) -> None:
"""Connect to local cache"""
settings = Settings.from_environment()

app.state.cache = LRUCache(maxsize=settings.cache_maxsize)


async def cached(
fn: Callable[[], Coroutine[Any, Any, T]], cache_key: str, request: Request
) -> T:
"""Either get the result from local cache or run the function and cache the result."""
settings = Settings.from_environment()

host = urlsplit(cast(str, request.state.url_root)).netloc

host_cache_key = f"{cache_key}:{host}"

try:
c: Dict[str, Any] = request.app.state.cache

if cached := c.get(host_cache_key):
logger.debug("Cache result hit")
return orjson.loads(cached) # type: ignore
except Exception as e:
logger.error(f"Error in cache: {e}")
if settings.debug:
raise

result = await fn()

try:
c[host_cache_key] = orjson.dumps(result) # type: ignore
except Exception as e:
logger.error(f"Error in cache: {e}")
if settings.debug:
raise

return result
45 changes: 45 additions & 0 deletions eodag/rest/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
# Copyright 2024, CS GROUP - France, https://www.csgroup.eu/
#
# This file is part of EODAG project
# https://www.github.com/CS-SI/EODAG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations

from functools import lru_cache

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict

from eodag.rest.constants import DEFAULT_MAXSIZE, DEFAULT_TTL


class Settings(BaseSettings):
"""EODAG Server config"""

# local cache config
cache_ttl: int = Field(default=DEFAULT_TTL)
cache_maxsize: int = Field(default=DEFAULT_MAXSIZE)

debug: bool = False

model_config = SettingsConfigDict(
env_prefix="EODAG_", extra="ignore", env_nested_delimiter="__"
)

@classmethod
@lru_cache(maxsize=1)
def from_environment(cls) -> Settings:
"""Get settings"""
return Settings()
27 changes: 27 additions & 0 deletions eodag/rest/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright 2024, CS GROUP - France, https://www.csgroup.eu/
#
# This file is part of EODAG project
# https://www.github.com/CS-SI/EODAG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# default cache ttl
DEFAULT_TTL = 600 # 10 min

DEFAULT_MAXSIZE = 2048 # local cache maxsize

CACHE_KEY_COLLECTIONS = "collections"
CACHE_KEY_COLLECTION = "collection"
CACHE_KEY_SEARCH = "search"
CACHE_KEY_QUERYABLES = "queryables"
CACHE_KEY_CATALOGS = "catalogs"
Loading

0 comments on commit 60338b6

Please sign in to comment.