33from collections .abc import Mapping
44from typing import Any
55
6+ from ai .backend .common .exception import BackendAIError
7+ from ai .backend .common .metrics .metric import DomainType , LayerType
8+ from ai .backend .common .resilience .policies .metrics import MetricArgs , MetricPolicy
9+ from ai .backend .common .resilience .policies .retry import BackoffStrategy , RetryArgs , RetryPolicy
10+ from ai .backend .common .resilience .resilience import Resilience
611from ai .backend .manager .data .app_config .types import AppConfigSearchResult
712from ai .backend .manager .data .app_config_fragment .types import (
813 AppConfigFragmentData ,
2328from ai .backend .manager .repositories .base .creator import Creator
2429from ai .backend .manager .repositories .base .querier import BatchQuerier
2530
31+ app_config_fragment_admin_repository_resilience = Resilience (
32+ policies = [
33+ MetricPolicy (
34+ MetricArgs (
35+ domain = DomainType .REPOSITORY ,
36+ layer = LayerType .APP_CONFIG_FRAGMENT_ADMIN_REPOSITORY ,
37+ )
38+ ),
39+ RetryPolicy (
40+ RetryArgs (
41+ max_retries = 5 ,
42+ retry_delay = 0.1 ,
43+ backoff_strategy = BackoffStrategy .FIXED ,
44+ non_retryable_exceptions = (BackendAIError ,),
45+ )
46+ ),
47+ ]
48+ )
49+
2650
2751class AppConfigFragmentAdminRepository :
2852 """Admin-only operations on AppConfigFragment.
@@ -33,12 +57,10 @@ class AppConfigFragmentAdminRepository:
3357 `AppConfigFragmentRepository`. Authorization is enforced at the
3458 service layer before reaching either repository.
3559
36- The required-policy invariant from BEP-1052 §1 is enforced by the
37- service layer; the FK on `app_config_fragments.name` is the
38- defense-in-depth backstop, translated by the creator spec into
60+ The required-policy invariant is enforced by the service layer;
61+ the FK on `app_config_fragments.name` is the defense-in-depth
62+ backstop, translated by the creator spec into
3963 :class:`AppConfigFragmentPolicyMissing`.
40-
41- Retry + metric policies are applied at the DB-source layer.
4264 """
4365
4466 _db_source : AppConfigFragmentDBSource
@@ -48,6 +70,7 @@ def __init__(self, db: ExtendedAsyncSAEngine) -> None:
4870
4971 # ── Mutations ─────────────────────────────────────────────────
5072
73+ @app_config_fragment_admin_repository_resilience .apply ()
5174 async def create (
5275 self ,
5376 key : AppConfigFragmentKey ,
@@ -63,6 +86,7 @@ async def create(
6386 )
6487 return await self ._db_source .create (creator )
6588
89+ @app_config_fragment_admin_repository_resilience .apply ()
6690 async def update (
6791 self ,
6892 key : AppConfigFragmentKey ,
@@ -73,17 +97,20 @@ async def update(
7397 spec = AppConfigFragmentUpdaterSpec (extra_config = extra_config )
7498 return await self ._db_source .update (key , spec )
7599
100+ @app_config_fragment_admin_repository_resilience .apply ()
76101 async def purge (self , key : AppConfigFragmentKey ) -> bool :
77102 return await self ._db_source .purge (key )
78103
79104 # ── Cross-scope reads ────────────────────────────────────────
80105
106+ @app_config_fragment_admin_repository_resilience .apply ()
81107 async def admin_search (
82108 self ,
83109 querier : BatchQuerier ,
84110 ) -> AppConfigFragmentSearchResult :
85111 return await self ._db_source .admin_search (querier )
86112
113+ @app_config_fragment_admin_repository_resilience .apply ()
87114 async def admin_search_app_configs (
88115 self ,
89116 querier : BatchQuerier ,
0 commit comments