Until Successful bez magii: drzewo decyzji, RETRY_EXHAUSTED i suppressedErrors
Until Successful to nie amulet na niestabilne integracje. To synchroniczny scope, który ponawia procesory aż wszystkie się uda albo wyczerpie maxRetries — wtedy rzuca MULE:RETRY_EXHAUSTED. Zespoły wpadają, gdy retryują błędy biznesowe, wstawiają On Error Continue tam, gdzie potrzebny był Propagate (scope „kończy się sukcesem” i nie retryuje), albo obsługują exhaustion bez odczytu suppressed błędów bazowych.
Czego ten tekst nie jest: klonem Medium z samym XML, tutorialem klikania w Studio ani radą „opakuj wszystko w Until Successful”. To drzewo decyzji + taksonomia + antywzorce dla developerów / architektów, którzy potrzebują retry connectivity bez zamiany błędów walidacji w storm retry.
Poniżej: kiedy retryować, jak Try + On Error steruje retry vs skip, co znaczą RETRY_EXHAUSTED i suppressedErrors, antywzorce, checklista, FAQ.
Co Until Successful naprawdę robi
Z docs Until Successful Scope:
- Procesory w środku lecą po kolei, synchronicznie.
- Gdy któryś padnie, Until Successful ponawia wszystkie procesory w scope, w tym ten, który padł, aż do sukcesu albo wyczerpania retry.
- Po ostatniej porażce: komunikat w stylu
'until-successful' retries exhausted.— typMULE:RETRY_EXHAUSTED. - Atrybuty:
maxRetries(liczba lub expression);millisBetweenRetries(minimalny odstęp; domyślnie 60000 ms; rzeczywisty odstęp zależy od czasu poprzedniej próby, nie powinien przekroczyć dwukrotności wartości). - Propagacja zmiennych: każda próba startuje ze zmiennymi sprzed scope. Zmiany z nieudanej próby nie wchodzą do następnej. Po sukcesie zmienne i payload idą dalej w flow.
Typowe przypadki z docs: outbound z problemami dostępności, komponenty zależne od niestabilnych zasobów, krótki łańcuch akcji aż się uda.
Drzewo decyzji: retry czy nie?
Zanim wrzucisz Until Successful na call, idź po kolei:
1. Czy failure jest przejściowy (connectivity, krótki outage, rate-limit z backoffem)?
NIE → NIE używaj Until Successful dla tego typu błędu.
TAK → dalej.
2. Czy operacja jest idempotentna (albo bezpiecznie ponowić cały scope)?
NIE → retry re-wykonuje WSZYSTKIE procesory w scope — ryzyko side-effectów.
TAK → dalej.
3. Czy umiesz nazwać retryowalne typy (np. HTTP:CONNECTIVITY, DB:CONNECTIVITY)?
NIE → przypadkiem retryujesz BAD_REQUEST / UNAUTHORIZED / walidację biznesową.
TAK → zagnieźdź Try; Propagate tylko te typy.
4. Po exhaustion: potrzebujesz DLQ / NACK / alert / zmapowany błąd API?
TAK → handler na poziomie flow na RETRY_EXHAUSTED (+ inspect suppressed cause).
| Sytuacja | Preferuj |
|---|---|
Przejściowy *:CONNECTIVITY / krótki outage |
Until Successful + Try; Propagate typów retryowalnych |
HTTP:BAD_REQUEST, auth, walidacja schematu |
Bez retry — On Error Continue (lub Propagate poza US) na ścieżkę client/DLQ |
| Wyczerpane retry | Obsłuż MULE:RETRY_EXHAUSTED; DLQ / NACK / alert |
| Critical JVM / overload | Nie traktuj jako „po prostu retry” — CRITICAL / feature flags |
Taksonomia: Continue vs Propagate wewnątrz Until Successful
Until Successful retryuje tylko wtedy, gdy procesor psuje scope. Sterujesz tym obsługą błędów w środku — niemal zawsze przez zagnieżdżony Try.
Z On-Error Components / Error Handlers:
| Handler | Efekt na ownera (Try / flow) | Efekt na Until Successful |
|---|---|---|
| On Error Propagate | Owner pada; błąd re-throw | Scope widzi failure → retry |
| On Error Continue | Owner traktowany jako sukces | Scope widzi success → bez retry; flow idzie dalej |
Wzorzec (retry tylko connectivity):
<until-successful maxRetries="3" millisBetweenRetries="2000">
<try>
<http:request method="GET" config-ref="HTTP_Request_configuration" path="/resource"/>
<error-handler>
<on-error-propagate type="HTTP:CONNECTIVITY"/>
<on-error-continue type="HTTP:BAD_REQUEST, HTTP:UNAUTHORIZED, HTTP:NOT_FOUND">
<!-- mapuj na payload client/DLQ; US NIE będzie retryował -->
</on-error-continue>
</error-handler>
</try>
</until-successful>
Kolejność ma znaczenie: typy szczegółowe przed ANY. Matching jest sekwencyjny (On-Error docs).
MULE:RETRY_EXHAUSTED po ostatniej próbie
Gdy ostatnia próba pada, Until Successful rzuca MULE:RETRY_EXHAUSTED. Docs pokazują handler na poziomie flow:
<error-handler>
<on-error-continue type="RETRY_EXHAUSTED">
<logger level="INFO" message="File upload failed"/>
</on-error-continue>
</error-handler>
(Until Successful Scope — przykład FTP.)
Mule loguje każdą nieudaną próbę przed finalnym błędem (Retrying execution of event, attempt N of M).
Mule 3 → 4: deadLetterQueue-ref zniknął. Łap RETRY_EXHAUSTED w error handlerze i wyślij na DLQ / endpoint (Migrating the Until Successful).
Z Mule Errors: RETRY_EXHAUSTED = wyczerpane retry bloku wykonania (Until Successful lub retry connectora). Connectory mają w hierarchii też CONNECTIVITY i RETRY_EXHAUSTED.
Error suppression i suppressedErrors
Przy włączonej fladze mule.suppress.mule.exceptions (domyślnie) komponenty takie jak Until Successful i Web Service Consumer raportują błędy w swoich namespace’ach (np. MULE:RETRY_EXHAUSTED), a oryginalny błąd connectora staje się underlying / suppressed cause. Docs: Suppressed errors are treated as underlying causes that can also be matched by On Error handlers (Feature Flagging Mechanism; Help: How to disable the error suppression feature in runtime 4.4).
Po exhaustion w praktyce:
- Typ powierzchniowy to często
MULE:RETRY_EXHAUSTED(detailedDescription:'until-successful' retries exhausted). - Ostatni underlying (np.
HTTP:CONNECTIVITY) ląduje wsuppressedErrorsw obiekcie błędu (dump z Help pokazujesuppressedErrors=[ { errorType=HTTP:CONNECTIVITY, ... } ]). - Handlery mogą matchować typy suppressed jako underlying causes — nie jesteś ograniczony tylko do powierzchniowego
RETRY_EXHAUSTED(feature-flagging docs). - Wyłączenie suppression (
mule.suppress.mule.exceptions=false) to właściwość system / server-level (Help); nie zakładaj przełącznika per-app na CloudHub bez sprawdzenia opcji runtime.
Nota uczciwości: publiczne tabele selektorów DataWeave wymieniają description, errorType, cause, errorMessage, childErrors (Mule Errors, Predefined Variables) — bez osobnego wiersza suppressedErrors. Help dump + feature-flagging to główne źródła Anypoint dla modelu suppression; preferuj type-matching suppressed causes i/lub inspect struktury błędu w logach/debuggerze zamiast wymyślać nieudokumentowane API.
Antywzorce (i co zamiast)
1. On Error Continue jako jedyny handler w Until Successful
Objaw: retry nigdy nie startuje; flow idzie jakby call się udał.
Przyczyna: Continue oznacza sukces Try/ownera → Until Successful nie retryuje.
Fix: Propagate dla typów retryowalnych; Continue tylko dla świadomie pomijanych błędów biznesowych.
2. Retry BAD_REQUEST / walidacji / auth
Objaw: ten sam 400/401/422 walony maxRetries razy; latency; rate limity partnera.
Przyczyna: goły Until Successful wokół HTTP bez filtracji Try.
Fix: krok 3 drzewa — Propagate tylko typów przejściowych.
3. Nieidempotentne procesory w scope
Objaw: podwójne POST-y, podwójne płatności, podwójne zapisy pliku przy każdej próbie.
Przyczyna: docs — przy failure Until Successful ponawia wszystkie procesory w scope, nie tylko ten, który padł.
Fix: scope minimalny (jeden outbound); side-effecty poza lub idempotentne (patrz artykuł o idempotencji OSv2).
4. Obsługa tylko RETRY_EXHAUSTED bez root cause
Objaw: w ops same „retries exhausted”; brak statusu HTTP / connectivity do triage.
Przyczyna: suppression pokazuje namespace MULE; underlying siedzi w suppressed causes.
Fix: loguj/matchuj suppressed cause; alertuj exhaustion i typ bazowy.
5. Oczekiwanie Mule 3 failureExpression / deadLetterQueue-ref
Objaw: konfiguracja nie migruje; DLQ milczy.
Fix: Validation zamiast failureExpression; error handler na RETRY_EXHAUSTED zamiast deadLetterQueue-ref (migration guide).
6. CRITICAL / overload jako „retryowalne”
Objaw: pętle retry przy stresie JVM.
Docs: flaga mule.untilSuccessful.retryOnCriticalError.disallow — gdy włączona, eventy nie są retryowane przy MULE:CRITICAL w Until Successful (Feature Flagging). Preferuj fail-fast + restart/health zamiast ślepego retry.
Checklista
- Wypisz typy retryowalne vs nieretryowalne dla tego calla.
- Zagnieźdź Try w Until Successful; Propagate tylko typów retryowalnych.
- Continue (lub wyjście) dla business/validation/auth — bez retry.
- Ustaw świadomie
maxRetriesimillisBetweenRetries(domyślny odstęp to 1 minuta). - Scope mały i idempotentny — cały scope się powtarza.
- Handler flow na
RETRY_EXHAUSTED→ DLQ / NACK / zmapowana odpowiedź / alert. - Uwzględnij error suppression: powierzchnia może być
MULE:RETRY_EXHAUSTED; inspect suppressed underlying. - Pamiętaj o resecie zmiennych między nieudanymi próbami.
- MUnit: assert liczby retry na ścieżce Propagate i zera retry na Continue.
- Nie zastępuj Until Successful brokerem (MQ), gdy potrzebujesz trwałego async retry.
FAQ
1. Jaki błąd rzuca Until Successful po wyczerpaniu retry?
MULE:RETRY_EXHAUSTED (komunikat: 'until-successful' retries exhausted.) — Until Successful Scope.
2. Dlaczego Until Successful nie retryuje?
Zwykle On Error Continue (albo cokolwiek, co oznacza sukces próby) w środku scope. Propagate dla błędów, które mają być ponawiane.
3. Czy retry odpala tylko padnięty procesor?
Nie — docs: ponawia wszystkie procesory w scope, w tym ten, który padł.
4. Co to suppressed errors?
Przy mule.suppress.mule.exceptions (domyślnie) Until Successful raportuje w namespace MULE; underlying błędy connectora to suppressed causes, które nadal mogą być matchowane przez On Error (Feature Flagging; Help).
5. Jak zastąpić Mule 3 deadLetterQueue-ref?
Łap RETRY_EXHAUSTED w error handlerze i kieruj na DLQ (migration).
6. Domyślne millisBetweenRetries?
60000 (jedna minuta), jeśli nie ustawisz (Until Successful Scope).
7. Czy retryować HTTP 400?
Prawie nigdy — nieretryowalne przez On Error Continue (albo fail bez US).
8. Czy zmienne przechodzą między nieudanymi próbami?
Nie — każda próba startuje ze zmiennych sprzed scope; zmiany z failed attempt są odrzucane (Until Successful Scope).
9. Continue vs Propagate w jednym zdaniu?
Continue = owner sukces (bez retry US). Propagate = owner fail (US retryuje).
Soft CTA
Projektujesz retry vs fail-fast dla calli System API (connectivity vs walidacja) i chcesz przejrzeć nesting Until Successful, handlery RETRY_EXHAUSTED oraz suppression zanim produkcja urośnie w storm retry? Solita to nordycki partner MuleSoft z dostawą z Polski (EU-shoring) — pomagamy ułożyć taksonomię błędów i ścieżki DLQ. Bez obietnic „#1” i bez checklisty marketingowej.
Źródła
Dokumentacja
- Until Successful Scope
- On-Error Components
- Error Handlers
- Try Scope
- Mule Errors
- Migrating the Until Successful
- Feature Flagging Mechanism (
mule.suppress.mule.exceptions,mule.untilSuccessful.retryOnCriticalError.disallow) - Predefined Variables
Help
- How to disable the error suppression feature in runtime 4.4 (dump
suppressedErrorsprzyMULE:RETRY_EXHAUSTED)
YouTube (oEmbed-verified 2026-09-17)
- https://www.youtube.com/watch?v=DqW-SWQsf4k — Until Successful Scope in Mule Application (Sanjeev Tripathi)
- https://www.youtube.com/watch?v=gKjEjAD851M — HTTP Request within Until Successful & Try Scope (MuleSoft-TechZone)
- https://www.youtube.com/watch?v=xfAyWA9Ijpo — Until Successful / Propagate System API Errors (MuleSoft-TechZone)
- https://www.youtube.com/watch?v=a86tdUBu9lQ — Mule 4 Error Handling — 3 simple rules (MuleSoft-TechZone)