forked from robusta-dev/robusta
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request robusta-dev#43 from robusta-dev/service_resolution…
…_cache_miss Fix service resolution cache miss
- Loading branch information
Showing
18 changed files
with
292 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,64 @@ | ||
import logging | ||
import threading | ||
import time | ||
from collections import defaultdict | ||
from typing import List | ||
|
||
from ..model.services import ServiceInfo, get_service_key | ||
from pydantic.main import BaseModel | ||
|
||
from ..model.env_vars import SERVICE_UPDATES_CACHE_TTL_SEC | ||
from ..model.services import ServiceInfo | ||
|
||
|
||
class CachedServiceInfo(BaseModel): | ||
service_info: ServiceInfo | ||
event_time: float | ||
|
||
|
||
class TopServiceResolver: | ||
__recent_service_updates = {} | ||
__namespace_to_service = defaultdict(list) | ||
__cached_updates_lock = threading.Lock() | ||
|
||
cached_services: List[ServiceInfo] = [] | ||
@classmethod | ||
def store_cached_services(cls, services: List[ServiceInfo]): | ||
new_store = defaultdict(list) | ||
for service in services: | ||
new_store[service.namespace].append(service) | ||
|
||
# The services are stored periodically, after reading it from the API server. If, between reads | ||
# new services are added, they will be missing from the cache. So, in addition to the periodic read, we | ||
# update the cache from listening to add/update API server events. | ||
# append recent updates, to avoid race conditions between events and api server read | ||
with cls.__cached_updates_lock: | ||
recent_updates_keys = list(cls.__recent_service_updates.keys()) | ||
for service_key in recent_updates_keys: | ||
recent_update = cls.__recent_service_updates[service_key] | ||
if ( | ||
time.time() - recent_update.event_time | ||
> SERVICE_UPDATES_CACHE_TTL_SEC | ||
): | ||
del cls.__recent_service_updates[service_key] | ||
else: | ||
new_store[recent_update.service_info.namespace].append( | ||
recent_update.service_info | ||
) | ||
|
||
cls.__namespace_to_service = new_store | ||
|
||
# TODO remove this guess function | ||
# temporary try to guess who the owner service is. | ||
@staticmethod | ||
def guess_service_key(name: str, namespace: str) -> str: | ||
for cached_service in TopServiceResolver.cached_services: | ||
if cached_service.namespace == namespace and name.startswith( | ||
cached_service.name | ||
): | ||
return get_service_key( | ||
cached_service.name, | ||
cached_service.service_type, | ||
cached_service.namespace, | ||
) | ||
@classmethod | ||
def guess_service_key(cls, name: str, namespace: str) -> str: | ||
for cached_service in cls.__namespace_to_service[namespace]: | ||
if name.startswith(cached_service.name): | ||
return cached_service.get_service_key() | ||
return "" | ||
|
||
@classmethod | ||
def add_cached_service(cls, service: ServiceInfo): | ||
cls.__namespace_to_service[service.namespace].append(service) | ||
with cls.__cached_updates_lock: | ||
cls.__recent_service_updates[service.get_service_key()] = CachedServiceInfo( | ||
service_info=service, event_time=time.time() | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
from robusta.api import * | ||
from robusta.core.model.services import ServiceInfo | ||
from robusta.core.discovery.top_service_resolver import TopServiceResolver | ||
|
||
|
||
@on_kubernetes_any_resource_all_changes | ||
def cluster_discovery_updates(event: KubernetesAnyEvent): | ||
if ( | ||
event.operation in [K8sOperationType.CREATE, K8sOperationType.UPDATE] | ||
and event.obj.kind in ["Deployment", "ReplicaSet", "DaemonSet", "StatefulSet"] | ||
and not event.obj.metadata.ownerReferences | ||
): | ||
TopServiceResolver.add_cached_service( | ||
ServiceInfo( | ||
name=event.obj.metadata.name, | ||
service_type=event.obj.kind, | ||
namespace=event.obj.metadata.namespace, | ||
) | ||
) |
Oops, something went wrong.