Steg 9 (R4): unified migrate-corpus.mjs --write over engineering/governance/ infrastructure/security. 327 filer mutert, verified=null, prosa byte-identisk (fra første ## seksjon), advisor urørt (0 endringer). To applier-fixes oppdaget under kjøring (TDD, RED→GREEN): - insertHeaderFields: anker faller nå tilbake når en meta-linje selv passerer 500B (2 filer pakket et avsnitt i **Status:** → Type/Source landet utenfor scan-vinduet, applierens post-write-assertion fanget + restaurerte). - normalizeStaleVerified: fjerner nå ALLE stale non-date **Verified:** i 500B-vinduet, inkl. stray body-dup rett under --- (9 mlops-genaiops-filer var ellers falskt "verified"/fresh, droppet fra worklist). Operatør-godkjent utvidelse av carve-out; kun stray metadata-linjer, aldri prosa. test-transform-criterion: precondition oppdatert til post-migrasjons-sannhet (fila bærer nå Source). Suite 728/728 grønn.
524 lines
19 KiB
Markdown
524 lines
19 KiB
Markdown
# Circuit Breaker Patterns for AI Models
|
|
|
|
**Last updated:** 2026-02
|
|
**Status:** GA
|
|
**Category:** API Management & AI Gateway
|
|
**Type:** reference
|
|
|
|
---
|
|
|
|
## Innhold
|
|
|
|
- [Introduksjon](#introduksjon)
|
|
- [Circuit Breaker State Machine](#circuit-breaker-state-machine)
|
|
- [Konfigurasjon](#konfigurasjon)
|
|
- [Failure Threshold Tuning](#failure-threshold-tuning)
|
|
- [Fallback-policies](#fallback-policies)
|
|
- [Recovery-mekanismer](#recovery-mekanismer)
|
|
- [Timeout-konfigurasjon](#timeout-konfigurasjon)
|
|
- [Avanserte mønstre](#avanserte-mønstre)
|
|
- [Anti-mønstre](#anti-mønstre)
|
|
- [Komplett resiliens-policy](#komplett-resiliens-policy)
|
|
- [For Cosmo](#for-cosmo)
|
|
|
|
## Introduksjon
|
|
|
|
Circuit breaker-mønsteret er en grunnleggende resiliensmekanisme for AI-applikasjoner som kommuniserer med Azure OpenAI og andre LLM-backends. Når en backend-tjeneste blir overbelastet eller utilgjengelig, forhindrer circuit breaker at applikasjonen fortsetter å sende forespørsler som uansett vil feile. I stedet "bryter kretsen" og returnerer en feilmelding umiddelbart, slik at backend-tjenesten får tid til å gjenopprette seg.
|
|
|
|
For Azure AI-tjenester er circuit breaker spesielt viktig fordi Azure OpenAI returnerer 429 (Too Many Requests) med en `Retry-After`-header som kan ha verdier opp til 24 timer. Uten circuit breaker vil applikasjoner fortsette å hamre på en throttlet tjeneste, noe som forverrer situasjonen og kaster bort gateway-ressurser. APIMsintegrerte circuit breaker håndterer dette automatisk ved å respektere `Retry-After`-headeren.
|
|
|
|
I kombinasjon med backend pools og lastbalansering utgjør circuit breaker selve nervesystemet i en intelligent AI-gateway: den detekterer problemer, isolerer feilende backends, ruter trafikk til friske alternativer, og gjenoppretter normal drift automatisk når backend er tilbake.
|
|
|
|
---
|
|
|
|
## Circuit Breaker State Machine
|
|
|
|
### Tre tilstander
|
|
|
|
Circuit breaker opererer som en tilstandsmaskin med tre tilstander:
|
|
|
|
```
|
|
Feil > threshold
|
|
┌─────────┐ ───────────────► ┌──────────┐
|
|
│ CLOSED │ │ OPEN │
|
|
│ (normal)│ ◄────────────── │ (trip) │
|
|
└─────────┘ Alle OK i └──────────┘
|
|
│ half-open │
|
|
│ │ Trip duration
|
|
│ │ utløpt
|
|
│ ┌──────────────┐ │
|
|
│ │ HALF-OPEN │◄───┘
|
|
│ │ (test) │
|
|
│ └──────────────┘
|
|
│ │
|
|
│ Suksess │ Feil
|
|
◄──────────────┘───────────► OPEN
|
|
```
|
|
|
|
| Tilstand | Oppførsel | Varighet |
|
|
|----------|-----------|----------|
|
|
| **Closed** | Normal drift, alle requests videresendes til backend | Ubegrenset (til feilbetingelse oppstår) |
|
|
| **Open** | Alle requests avvises umiddelbart med 503 | Trip duration (konfigurerbar, eller fra Retry-After) |
|
|
| **Half-Open** | Et begrenset antall test-requests sendes til backend | Til nok suksesser ELLER ny feil |
|
|
|
|
### APIM-spesifikk oppførsel
|
|
|
|
APIMcircuit breaker har noen viktige forskjeller fra den generelle circuit breaker-mønsteret:
|
|
|
|
| Egenskap | APIM Circuit Breaker |
|
|
|----------|---------------------|
|
|
| **Konfigurasjons-scope** | Per backend (ikke per API eller policy) |
|
|
| **Antall regler** | Kun én regel per backend (p.t.) |
|
|
| **Synkronisering** | Ingen mellom gateway-instanser (approksimasjon) |
|
|
| **Retry-After respekt** | Ja, dynamisk trip duration basert på backend-respons |
|
|
| **Støttede tiers** | Alle unntatt Consumption |
|
|
| **Respons ved Open** | 503 Service Unavailable |
|
|
|
|
---
|
|
|
|
## Konfigurasjon
|
|
|
|
### Grunnleggende circuit breaker for Azure OpenAI
|
|
|
|
```bicep
|
|
resource openaiBackend 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
|
|
parent: apim
|
|
name: 'openai-norwayeast'
|
|
properties: {
|
|
url: 'https://aoai-norwayeast.openai.azure.com/openai'
|
|
protocol: 'http'
|
|
circuitBreaker: {
|
|
rules: [
|
|
{
|
|
name: 'ai-resilience-rule'
|
|
failureCondition: {
|
|
count: 3
|
|
interval: 'PT1M'
|
|
statusCodeRanges: [
|
|
{ min: 429, max: 429 }
|
|
{ min: 500, max: 599 }
|
|
]
|
|
}
|
|
tripDuration: 'PT10S'
|
|
acceptRetryAfter: true
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Attributter forklart
|
|
|
|
| Attributt | Type | Beskrivelse | Anbefalt verdi for AI |
|
|
|-----------|------|-------------|----------------------|
|
|
| `failureCondition.count` | int | Antall feil som trigger trip | 3-5 |
|
|
| `failureCondition.interval` | duration | Tidsvindu for feil-telling | PT1M (1 minutt) |
|
|
| `failureCondition.statusCodeRanges` | array | HTTP-koder som teller som feil | 429 + 500-599 |
|
|
| `tripDuration` | duration | Hvor lenge circuit er åpent | PT10S - PT1M |
|
|
| `acceptRetryAfter` | bool | Bruk Retry-After header som trip duration | **true** (alltid for AI) |
|
|
|
|
### Prosentbasert vs. count-basert triggering
|
|
|
|
APIM støtter to modeller for feil-deteksjon:
|
|
|
|
**Count-basert (anbefalt for AI):**
|
|
```json
|
|
{
|
|
"failureCondition": {
|
|
"count": 3,
|
|
"interval": "PT1M",
|
|
"statusCodeRanges": [{"min": 429, "max": 429}]
|
|
}
|
|
}
|
|
```
|
|
Trigger: 3 eller flere 429-responser innen 1 minutt.
|
|
|
|
**Prosentbasert:**
|
|
```json
|
|
{
|
|
"failureCondition": {
|
|
"percentage": 50,
|
|
"interval": "PT1M",
|
|
"statusCodeRanges": [{"min": 429, "max": 429}]
|
|
}
|
|
}
|
|
```
|
|
Trigger: 50% eller mer av requests feiler innen 1 minutt.
|
|
|
|
**Anbefaling:** Count-basert er mer forutsigbar for AI-workloads. Prosentbasert kan gi falske positiver ved lav trafikk (2 av 3 requests feiler = 66%).
|
|
|
|
---
|
|
|
|
## Failure Threshold Tuning
|
|
|
|
### Faktorene som påvirker threshold
|
|
|
|
| Faktor | Lav threshold (1-2) | Høy threshold (5-10) |
|
|
|--------|---------------------|---------------------|
|
|
| **Reaksjonstid** | Rask, reagerer umiddelbart | Tregere, tolererer noen feil |
|
|
| **Falske positiver** | Høy risiko | Lav risiko |
|
|
| **Backend-beskyttelse** | Sterk, minimal ekstra belastning | Svakere, flere feil-requests |
|
|
| **Anbefalt for** | Kritiske, kapasitetsbegrensede backends | Robuste backends med transiente feil |
|
|
|
|
### Anbefalte innstillinger per scenario
|
|
|
|
| Scenario | count | interval | tripDuration | acceptRetryAfter |
|
|
|----------|-------|----------|--------------|-----------------|
|
|
| **PTU-instans (high priority)** | 3 | PT1M | PT10S | true |
|
|
| **PAYGO-instans (fallback)** | 5 | PT2M | PT30S | true |
|
|
| **Dev/test** | 2 | PT30S | PT5S | true |
|
|
| **Business-critical** | 3 | PT1M | PT10S | true |
|
|
| **Batch processing** | 10 | PT5M | PT1M | true |
|
|
|
|
### Dynamisk trip duration med Retry-After
|
|
|
|
Når `acceptRetryAfter: true` er satt, overstyrer Azure OpenAIs `Retry-After`-header den konfigurerte `tripDuration`:
|
|
|
|
```
|
|
Scenario: OpenAI returnerer 429 med Retry-After: 30
|
|
→ Circuit breaker åpner i 30 sekunder (ikke konfiguert tripDuration)
|
|
→ Etter 30 sekunder: half-open → test request
|
|
→ Suksess: circuit lukkes
|
|
→ Feil: circuit åpner igjen med ny Retry-After
|
|
|
|
Scenario: OpenAI returnerer 429 med Retry-After: 86400 (1 dag!)
|
|
→ Circuit breaker åpner i 24 timer
|
|
→ All trafikk rutes til andre backends i poolen
|
|
```
|
|
|
|
> **Viktig advarsel:** Azure OpenAI kan returnere Retry-After verdier opp til 1 dag (86400 sekunder). Med `acceptRetryAfter: true` vil dette bety at backend er utilgjengelig i 24 timer. Sørg for at backend-poolen har nok kapasitet i andre backends til å håndtere dette.
|
|
|
|
---
|
|
|
|
## Fallback-policies
|
|
|
|
### Mønster 1: Backend Pool med automatisk failover
|
|
|
|
Den enkleste og mest robuste tilnærmingen:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<!-- Backend pool håndterer failover automatisk -->
|
|
<set-backend-service backend-id="openai-pool-priority" />
|
|
<authentication-managed-identity
|
|
resource="https://cognitiveservices.azure.com/" />
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
Når circuit breaker utløses på Priority 1-backends, ruter APIM automatisk til Priority 2, osv.
|
|
|
|
### Mønster 2: Retry med backend-bytte
|
|
|
|
Eksplisitt retry-logikk som bytter backend ved 429:
|
|
|
|
```xml
|
|
<policies>
|
|
<backend>
|
|
<retry condition="@(context.Response.StatusCode == 429)"
|
|
count="3"
|
|
interval="0"
|
|
first-fast-retry="true">
|
|
<set-backend-service backend-id="openai-pool-priority" />
|
|
</retry>
|
|
</backend>
|
|
</policies>
|
|
```
|
|
|
|
**Viktig:** `interval="0"` betyr umiddelbar retry til neste backend, IKKE ventetid. Server-side retries bør aldri ha delay — det holder opp klienten og bruker gateway-ressurser.
|
|
|
|
### Mønster 3: Graceful Degradation med cache-fallback
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<!-- Prøv semantic cache først -->
|
|
<azure-openai-semantic-cache-lookup
|
|
score-threshold="0.20"
|
|
embeddings-backend-id="embeddings-backend"
|
|
embeddings-backend-auth="system-assigned" />
|
|
|
|
<set-backend-service backend-id="openai-pool-priority" />
|
|
</inbound>
|
|
|
|
<on-error>
|
|
<choose>
|
|
<!-- Alle backends utilgjengelige -->
|
|
<when condition="@(context.Response.StatusCode == 503)">
|
|
<return-response>
|
|
<set-status code="503" reason="AI Service Temporarily Unavailable" />
|
|
<set-header name="Retry-After" exists-action="override">
|
|
<value>60</value>
|
|
</set-header>
|
|
<set-body>@{
|
|
return new JObject(
|
|
new JProperty("error", new JObject(
|
|
new JProperty("code", "service_unavailable"),
|
|
new JProperty("message", "AI-tjenesten er midlertidig utilgjengelig. Prøv igjen om 60 sekunder."),
|
|
new JProperty("type", "circuit_breaker_open")
|
|
))
|
|
).ToString();
|
|
}</set-body>
|
|
</return-response>
|
|
</when>
|
|
</choose>
|
|
</on-error>
|
|
</policies>
|
|
```
|
|
|
|
### Mønster 4: Fallback til enklere modell
|
|
|
|
```xml
|
|
<policies>
|
|
<backend>
|
|
<retry condition="@(context.Response.StatusCode == 429 || context.Response.StatusCode == 503)"
|
|
count="1"
|
|
interval="0"
|
|
first-fast-retry="true">
|
|
<!-- Fallback til mindre modell -->
|
|
<set-backend-service backend-id="openai-gpt4o-mini-backend" />
|
|
<rewrite-uri template="/deployments/gpt-4o-mini/chat/completions" />
|
|
</retry>
|
|
</backend>
|
|
</policies>
|
|
```
|
|
|
|
---
|
|
|
|
## Recovery-mekanismer
|
|
|
|
### Automatisk recovery
|
|
|
|
Circuit breaker håndterer recovery automatisk:
|
|
|
|
```
|
|
1. Trip: Circuit åpner (503 til klienter)
|
|
2. Wait: tripDuration utløper (eller Retry-After)
|
|
3. Half-Open: Test-request sendes til backend
|
|
4. Success: Circuit lukkes, normal drift gjenopptas
|
|
5. Failure: Circuit åpner igjen, ny tripDuration starter
|
|
```
|
|
|
|
### Recovery-timing
|
|
|
|
| Kilde | Prioritet | Typisk verdi |
|
|
|-------|-----------|--------------|
|
|
| `Retry-After` header (når `acceptRetryAfter: true`) | Høyest | 1-86400 sekunder |
|
|
| Konfigurert `tripDuration` | Fallback | PT10S - PT1M |
|
|
| Default (uten konfigurasjon) | Lavest | PT1M |
|
|
|
|
### Overvåking av circuit breaker-tilstand
|
|
|
|
```xml
|
|
<outbound>
|
|
<!-- Log circuit breaker state -->
|
|
<trace source="circuit-breaker" severity="information">
|
|
<message>@{
|
|
var backendId = context.Backend?.Id ?? "unknown";
|
|
var statusCode = context.Response.StatusCode;
|
|
return $"Backend: {backendId}, Status: {statusCode}";
|
|
}</message>
|
|
</trace>
|
|
</outbound>
|
|
```
|
|
|
|
**KQL for circuit breaker-hendelser:**
|
|
|
|
```kusto
|
|
ApiManagementGatewayLogs
|
|
| where ResponseCode == 503
|
|
| extend backendId = tostring(parse_json(BackendResponseBody).backendId)
|
|
| summarize CircuitBreakerTrips = count() by backendId, bin(TimeGenerated, 5m)
|
|
| render timechart
|
|
```
|
|
|
|
---
|
|
|
|
## Timeout-konfigurasjon
|
|
|
|
### Forward-request timeout
|
|
|
|
Kontroller hvor lenge APIM venter på backend-respons:
|
|
|
|
```xml
|
|
<policies>
|
|
<backend>
|
|
<forward-request timeout="120" />
|
|
</backend>
|
|
</policies>
|
|
```
|
|
|
|
**Anbefalte timeouts for AI-workloads:**
|
|
|
|
| Operasjon | Anbefalt timeout | Begrunnelse |
|
|
|-----------|------------------|-------------|
|
|
| Chat Completion (standard) | 60-120 sek | GPT-4o kan bruke tid på komplekse prompts |
|
|
| Chat Completion (streaming) | 120-180 sek | Streaming starter raskt, men kan vare lenge |
|
|
| Embeddings | 30-60 sek | Raskere operasjon, typisk < 10 sek |
|
|
| Image Generation (DALL-E) | 120-180 sek | Bildegenerering er CPU-intensivt |
|
|
| Assistants API | 180-300 sek | Multi-step agent workflows |
|
|
| Batch API | 300-600 sek | Store batch-operasjoner |
|
|
|
|
### Timeout + Circuit Breaker interaksjon
|
|
|
|
```
|
|
Timeout utløper (120 sek)
|
|
→ APIM registrerer det som feil
|
|
→ Teller mot circuit breaker threshold
|
|
→ Etter N timeouts → Circuit breaker trip
|
|
→ Backend fjernes fra pool → Trafikk til alternativer
|
|
```
|
|
|
|
> **Best practice:** Sett timeout lavere enn det du tror er nødvendig. Bedre å få rask feil og retry til annen backend enn å vente 120 sekunder på en hengende request.
|
|
|
|
---
|
|
|
|
## Avanserte mønstre
|
|
|
|
### Mønster: Cascading Circuit Breakers
|
|
|
|
For multi-tier arkitekturer der APIM ruter til mellomtjenester som igjen kaller Azure OpenAI:
|
|
|
|
```
|
|
Client → APIM [CB-1] → Custom Service [CB-2] → Azure OpenAI
|
|
```
|
|
|
|
```bicep
|
|
// CB-1: APIM → Custom Service
|
|
resource customServiceBackend 'Microsoft.ApiManagement/service/backends@2023-09-01-preview' = {
|
|
properties: {
|
|
circuitBreaker: {
|
|
rules: [{
|
|
failureCondition: {
|
|
count: 5
|
|
interval: 'PT2M'
|
|
statusCodeRanges: [
|
|
{ min: 502, max: 504 }
|
|
]
|
|
}
|
|
tripDuration: 'PT30S'
|
|
}]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Applikasjons-nivå CB-2 implementeres med Polly (.NET), resilience4j (Java), eller tilsvarende.
|
|
|
|
### Mønster: Health Endpoint Monitoring
|
|
|
|
Kombiner circuit breaker med aktiv health checking:
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<!-- Sjekk health endpoint før ruting -->
|
|
<send-request mode="new"
|
|
response-variable-name="healthCheck"
|
|
timeout="5"
|
|
ignore-error="true">
|
|
<set-url>@("https://aoai-norwayeast.openai.azure.com/openai/models?api-version=2024-10-21")</set-url>
|
|
<set-method>GET</set-method>
|
|
<authentication-managed-identity
|
|
resource="https://cognitiveservices.azure.com/" />
|
|
</send-request>
|
|
|
|
<choose>
|
|
<when condition="@(((IResponse)context.Variables["healthCheck"]).StatusCode != 200)">
|
|
<!-- Backend er nede, bruk fallback direkte -->
|
|
<set-backend-service backend-id="openai-fallback-pool" />
|
|
</when>
|
|
<otherwise>
|
|
<set-backend-service backend-id="openai-primary-pool" />
|
|
</otherwise>
|
|
</choose>
|
|
</inbound>
|
|
</policies>
|
|
```
|
|
|
|
> **Merk:** Health endpoint monitoring legger til latens og backend-belastning. Bruk det kun for scenarier der circuit breaker alene ikke gir rask nok failover.
|
|
|
|
---
|
|
|
|
## Anti-mønstre
|
|
|
|
| Anti-mønster | Problem | Løsning |
|
|
|--------------|---------|---------|
|
|
| **Ingen circuit breaker** | Backend overbelastes, cascading failure | Aktiver circuit breaker på alle AI-backends |
|
|
| **For lav threshold (count=1)** | Falske positiver ved transiente feil | Bruk count=3-5 for produksjon |
|
|
| **For lang tripDuration** | Backend er unødvendig utilgjengelig | Bruk `acceptRetryAfter: true` |
|
|
| **Ignorere Retry-After** | Hammer backend som eksplisitt ber om pause | Sett `acceptRetryAfter: true` alltid |
|
|
| **Server-side delay** | Retry med sleep/delay holder opp klienter | Bruk `interval="0"` med backend pool failover |
|
|
| **Timeout for lang** | Gateway-ressurser brukt opp på hengende requests | Sett realistiske timeouts per operasjonstype |
|
|
| **Mangle monitoring** | Ingen innsikt i circuit breaker-oppførsel | Emit metrikk + KQL dashboards |
|
|
|
|
---
|
|
|
|
## Komplett resiliens-policy
|
|
|
|
```xml
|
|
<policies>
|
|
<inbound>
|
|
<!-- 1. Token rate limiting -->
|
|
<llm-token-limit
|
|
counter-key="@(context.Subscription.Id)"
|
|
tokens-per-minute="50000"
|
|
estimate-prompt-tokens="true" />
|
|
|
|
<!-- 2. Backend pool med circuit breaker -->
|
|
<set-backend-service backend-id="openai-pool-priority" />
|
|
|
|
<!-- 3. Managed Identity auth -->
|
|
<authentication-managed-identity
|
|
resource="https://cognitiveservices.azure.com/" />
|
|
</inbound>
|
|
|
|
<backend>
|
|
<!-- 4. Retry til annen backend ved 429 -->
|
|
<retry condition="@(context.Response.StatusCode == 429)"
|
|
count="3"
|
|
interval="0"
|
|
first-fast-retry="true">
|
|
<set-backend-service backend-id="openai-pool-priority" />
|
|
</retry>
|
|
|
|
<!-- 5. Timeout -->
|
|
<forward-request timeout="120" />
|
|
</backend>
|
|
|
|
<outbound>
|
|
<!-- 6. Token metrikk -->
|
|
<llm-emit-token-metric namespace="ai-gateway">
|
|
<dimension name="Backend" value="@(context.Backend?.Id ?? "unknown")" />
|
|
<dimension name="StatusCode" value="@(context.Response.StatusCode.ToString())" />
|
|
</llm-emit-token-metric>
|
|
</outbound>
|
|
|
|
<on-error>
|
|
<!-- 7. Graceful degradation -->
|
|
<choose>
|
|
<when condition="@(context.Response.StatusCode == 503)">
|
|
<return-response>
|
|
<set-status code="503" reason="AI Service Unavailable" />
|
|
<set-header name="Retry-After" exists-action="override">
|
|
<value>60</value>
|
|
</set-header>
|
|
<set-body>{"error":{"code":"all_backends_unavailable","message":"Alle AI-backends er midlertidig utilgjengelige"}}</set-body>
|
|
</return-response>
|
|
</when>
|
|
</choose>
|
|
</on-error>
|
|
</policies>
|
|
```
|
|
|
|
---
|
|
|
|
## For Cosmo
|
|
|
|
- Circuit breaker er obligatorisk for alle Azure OpenAI backends i produksjon -- uten det risikerer du cascading failures og bortkastet gateway-kapasitet mot throttlede backends.
|
|
- Sett ALLTID `acceptRetryAfter: true` for Azure OpenAI backends. Azure OpenAI returnerer presise Retry-After verdier som gir optimal recovery-timing. Uten dette bruker du den statiske tripDuration som kan være for kort eller for lang.
|
|
- Anbefalt baseline: `count: 3`, `interval: PT1M`, `tripDuration: PT10S`, `acceptRetryAfter: true`. Juster count opp for PAYGO-backends med mer toleranse.
|
|
- Kombiner circuit breaker med backend pools for automatisk failover: når PTU-backend tripper, ruter APIM automatisk til PAYGO-backends uten klient-endringer.
|
|
- Advarsler: Azure OpenAI kan returnere Retry-After opp til 86400 sekunder (1 dag). Sørg for at arkitekturen har nok alternative backends til å håndtere langvarige circuit breaks.
|