# 일곱 어댑터의 submit 이 돌려주는 줄 — 비동기로 감싸는 것은 하나뿐이다
SesNotificationProviderAdapter.java-107-    return CompletableFuture.completedFuture(send(submission));
WebPushNotificationProviderAdapter.java-82-    return CompletableFuture.completedFuture(send(submission));
SmtpNotificationProviderAdapter.java-82-    return CompletableFuture.supplyAsync(() -> send(submission), executor);
TwilioSmsProviderAdapter.java-78-    return CompletableFuture.completedFuture(send(submission));
FcmNotificationProviderAdapter.java-65-    return coordinator.submit(List.of(submission)).thenApply(results -> results.get(0));
ApnsNotificationProviderAdapter.java-76-    return CompletableFuture.completedFuture(send(submission));
WebhookNotificationProviderAdapter.java-109-    return CompletableFuture.completedFuture(send(submission));

# 게이트웨이가 던지는 자리와 잡는 자리
47:          runtime.adapter().submit(submission).toCompletableFuture().join();
48:      applyHealth(runtime, result);
50:    } catch (CompletionException failure) {

# 배차 서비스가 잡는 세 종류 — 인자 예외는 마지막에만 걸린다
221:        dev.caskeleton.application.notification.platform.provider.ProviderCallNotStartedException
227:        dev.caskeleton.application.notification.platform.api.error.NotificationException
243:    } catch (RuntimeException transportFailure) {

# 그 마지막 가지가 만드는 값
241:          ProviderExecutionEvidence.responseLost(),
248:              NotificationFailureCode.PROVIDER_RESPONSE_LOST,
249:              FailureCategory.AMBIGUOUS_SUBMISSION,
251:          ProviderExecutionEvidence.responseLost(),

# dispatch 한 회차의 순서 — 라우팅은 제출보다 앞이다
121:    RoutingDecision decision = routing.next(routingContext(work, now));
124:      releaseLease(lease);
141:      releaseLease(lease);
169:        submitOutsideTransaction(attempt, profile, contactPoint, content, work);
175:    RetryDecision next = retryPolicy.decide(retryContext(work, recorded, result, profile));
176:    applyNextAction(work, recorded, next, clock.instant(), lease);

# 중지가 남기는 상태
435:            case RetryDecision.Stop ignored ->
436-                recipients.transitionHeldBy(
437-                    work.recipient().id(),
438-                    attempt.submissionOutcome() == SubmissionOutcome.CONFIRMED_ACCEPTED
439-                        ? RecipientDeliveryState.COMPLETED
440-                        : RecipientDeliveryState.FAILED,
441-                    Optional.empty(),

# 다음 회차가 청구하는 상태
JdbcNotificationServingState.java:37:      WHERE delivery_state IN ('PENDING', 'READY_TO_DISPATCH', 'RETRY_WAITING')
RecipientClaimSql.java:30:          + "     AND delivery_state IN ('PENDING', 'READY_TO_DISPATCH', 'RETRY_WAITING')"
RecipientClaimSql.java:60:          + "    WHERE delivery_state IN ('PENDING', 'READY_TO_DISPATCH', 'RETRY_WAITING')"
