This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 1400" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Диагностика потерянного ответа и безопасного повтора</title>
|
||||
<desc id="desc">Вертикальная временная шкала показывает первую HTTP-попытку с requestId req-51, сохранение результата сервисом, таймаут клиента, вторую попытку req-52 с тем же ключом и повтор сохранённого результата.</desc>
|
||||
<rect width="720" height="1400" fill="#f8fafc"/>
|
||||
<rect x="28" y="26" width="664" height="112" rx="24" fill="#1e293b"/>
|
||||
<text x="62" y="72" fill="#f8fafc" font-family="Arial, sans-serif" font-size="32" font-weight="700">Потерянный ответ: что проверить</text>
|
||||
<text x="62" y="108" fill="#cbd5e1" font-family="Arial, sans-serif" font-size="20">requestId меняется, idempotency key остаётся одним</text>
|
||||
|
||||
<line x1="136" y1="194" x2="136" y2="1288" stroke="#94a3b8" stroke-width="8" stroke-linecap="round"/>
|
||||
<circle cx="136" cy="242" r="20" fill="#2563eb"/>
|
||||
<circle cx="136" cy="472" r="20" fill="#059669"/>
|
||||
<circle cx="136" cy="702" r="20" fill="#ea580c"/>
|
||||
<circle cx="136" cy="932" r="20" fill="#7c3aed"/>
|
||||
<circle cx="136" cy="1162" r="20" fill="#16a34a"/>
|
||||
|
||||
<text x="52" y="212" fill="#475569" font-family="Arial, sans-serif" font-size="20" font-weight="700">t0</text>
|
||||
<text x="52" y="442" fill="#475569" font-family="Arial, sans-serif" font-size="20" font-weight="700">t1</text>
|
||||
<text x="52" y="672" fill="#475569" font-family="Arial, sans-serif" font-size="20" font-weight="700">t2</text>
|
||||
<text x="52" y="902" fill="#475569" font-family="Arial, sans-serif" font-size="20" font-weight="700">t3</text>
|
||||
<text x="52" y="1132" fill="#475569" font-family="Arial, sans-serif" font-size="20" font-weight="700">t4</text>
|
||||
|
||||
<rect x="182" y="174" width="486" height="142" rx="22" fill="#eff6ff" stroke="#2563eb" stroke-width="3"/>
|
||||
<text x="214" y="220" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="26" font-weight="700">Попытка № 1</text>
|
||||
<text x="214" y="257" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="21">requestId = req-51</text>
|
||||
<text x="214" y="286" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="21">key = demo-key-retry-2020-06-a7f1</text>
|
||||
|
||||
<rect x="182" y="404" width="486" height="142" rx="22" fill="#ecfdf5" stroke="#059669" stroke-width="3"/>
|
||||
<text x="214" y="450" fill="#065f46" font-family="Arial, sans-serif" font-size="26" font-weight="700">Сервис сохранил result</text>
|
||||
<text x="214" y="487" fill="#065f46" font-family="Arial, sans-serif" font-size="21">decision = owner → stored status = 201</text>
|
||||
<text x="214" y="516" fill="#065f46" font-family="Arial, sans-serif" font-size="21">resultId = demo-request-42</text>
|
||||
|
||||
<rect x="182" y="634" width="486" height="142" rx="22" fill="#fff7ed" stroke="#ea580c" stroke-width="3"/>
|
||||
<text x="214" y="680" fill="#9a3412" font-family="Arial, sans-serif" font-size="26" font-weight="700">Клиентский deadline истёк</text>
|
||||
<text x="214" y="717" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">response = not observed</text>
|
||||
<text x="214" y="746" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">не делаем вывод «эффекта нет»</text>
|
||||
|
||||
<rect x="182" y="864" width="486" height="142" rx="22" fill="#f5f3ff" stroke="#7c3aed" stroke-width="3"/>
|
||||
<text x="214" y="910" fill="#4c1d95" font-family="Arial, sans-serif" font-size="26" font-weight="700">Попытка № 2</text>
|
||||
<text x="214" y="947" fill="#4c1d95" font-family="Arial, sans-serif" font-size="21">requestId = req-52 — новый</text>
|
||||
<text x="214" y="976" fill="#4c1d95" font-family="Arial, sans-serif" font-size="21">key тот же, payload hash тот же</text>
|
||||
|
||||
<rect x="182" y="1094" width="486" height="142" rx="22" fill="#f0fdf4" stroke="#16a34a" stroke-width="3"/>
|
||||
<text x="214" y="1140" fill="#166534" font-family="Arial, sans-serif" font-size="26" font-weight="700">Replay сохранённого ответа</text>
|
||||
<text x="214" y="1177" fill="#166534" font-family="Arial, sans-serif" font-size="21">decision = replay → status = 201</text>
|
||||
<text x="214" y="1206" fill="#166534" font-family="Arial, sans-serif" font-size="21">effects = 1, resultId тот же</text>
|
||||
|
||||
<rect x="52" y="1302" width="616" height="56" rx="16" fill="#dbeafe"/>
|
||||
<text x="360" y="1337" text-anchor="middle" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="21" font-weight="700">Порядок событий важнее одного timeout</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,56 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 1360" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Путь одной заявки при потерянном HTTP-ответе</title>
|
||||
<desc id="desc">Вертикальная схема показывает один пользовательский intent с ключом идемпотентности: первая попытка сохраняет результат, ответ теряется, повтор использует тот же ключ и получает сохранённый результат без второго эффекта.</desc>
|
||||
<rect width="720" height="1360" fill="#f8fafc"/>
|
||||
<rect x="28" y="26" width="664" height="112" rx="24" fill="#0f172a"/>
|
||||
<text x="62" y="72" fill="#f8fafc" font-family="Arial, sans-serif" font-size="32" font-weight="700">Один intent, две доставки</text>
|
||||
<text x="62" y="108" fill="#cbd5e1" font-family="Arial, sans-serif" font-size="20">Timeout не отменяет эффект на сервере</text>
|
||||
|
||||
<line x1="360" y1="306" x2="360" y2="362" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M342 350 L360 374 L378 350" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="360" y1="548" x2="360" y2="604" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M342 592 L360 616 L378 592" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="360" y1="790" x2="360" y2="846" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M342 834 L360 858 L378 834" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="360" y1="1032" x2="360" y2="1088" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M342 1076 L360 1100 L378 1076" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<rect x="52" y="166" width="616" height="140" rx="22" fill="#e0f2fe" stroke="#0284c7" stroke-width="3"/>
|
||||
<circle cx="96" cy="216" r="25" fill="#0284c7"/>
|
||||
<text x="96" y="225" text-anchor="middle" fill="#ffffff" font-family="Arial, sans-serif" font-size="23" font-weight="700">1</text>
|
||||
<text x="138" y="215" fill="#0c4a6e" font-family="Arial, sans-serif" font-size="27" font-weight="700">Пользователь подтверждает заявку</text>
|
||||
<text x="138" y="250" fill="#164e63" font-family="Arial, sans-serif" font-size="21">Клиент создаёт key для одного intent</text>
|
||||
<text x="138" y="278" fill="#164e63" font-family="Arial, sans-serif" font-size="21">payload и общий deadline сохраняются рядом</text>
|
||||
|
||||
<rect x="52" y="382" width="616" height="166" rx="22" fill="#eff6ff" stroke="#2563eb" stroke-width="3"/>
|
||||
<circle cx="96" cy="435" r="25" fill="#2563eb"/>
|
||||
<text x="96" y="444" text-anchor="middle" fill="#ffffff" font-family="Arial, sans-serif" font-size="23" font-weight="700">2</text>
|
||||
<text x="138" y="432" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="27" font-weight="700">Попытка № 1</text>
|
||||
<text x="138" y="467" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="21">POST + один Idempotency-Key</text>
|
||||
<rect x="138" y="487" width="382" height="37" rx="12" fill="#dbeafe"/>
|
||||
<text x="156" y="513" fill="#1d4ed8" font-family="Arial, sans-serif" font-size="20" font-weight="700">key: demo-key-retry-2020-06-a7f1</text>
|
||||
|
||||
<rect x="52" y="624" width="616" height="166" rx="22" fill="#ecfdf5" stroke="#059669" stroke-width="3"/>
|
||||
<circle cx="96" cy="677" r="25" fill="#059669"/>
|
||||
<text x="96" y="686" text-anchor="middle" fill="#ffffff" font-family="Arial, sans-serif" font-size="23" font-weight="700">3</text>
|
||||
<text x="138" y="674" fill="#065f46" font-family="Arial, sans-serif" font-size="27" font-weight="700">Сервис завершает одну операцию</text>
|
||||
<text x="138" y="709" fill="#065f46" font-family="Arial, sans-serif" font-size="21">Хранит key, hash, status и result ID</text>
|
||||
<text x="138" y="738" fill="#065f46" font-family="Arial, sans-serif" font-size="21">Эффект: demo-request-42</text>
|
||||
|
||||
<rect x="52" y="866" width="616" height="166" rx="22" fill="#fff7ed" stroke="#ea580c" stroke-width="3"/>
|
||||
<circle cx="96" cy="919" r="25" fill="#ea580c"/>
|
||||
<text x="96" y="928" text-anchor="middle" fill="#ffffff" font-family="Arial, sans-serif" font-size="23" font-weight="700">4</text>
|
||||
<text x="138" y="916" fill="#9a3412" font-family="Arial, sans-serif" font-size="27" font-weight="700">Клиент не увидел ответ</text>
|
||||
<text x="138" y="951" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">Timeout = неизвестный исход для клиента</text>
|
||||
<text x="138" y="980" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">Не означает rollback у сервиса</text>
|
||||
|
||||
<rect x="52" y="1108" width="616" height="166" rx="22" fill="#f5f3ff" stroke="#7c3aed" stroke-width="3"/>
|
||||
<circle cx="96" cy="1161" r="25" fill="#7c3aed"/>
|
||||
<text x="96" y="1170" text-anchor="middle" fill="#ffffff" font-family="Arial, sans-serif" font-size="23" font-weight="700">5</text>
|
||||
<text x="138" y="1158" fill="#4c1d95" font-family="Arial, sans-serif" font-size="27" font-weight="700">Повтор с тем же key</text>
|
||||
<text x="138" y="1193" fill="#4c1d95" font-family="Arial, sans-serif" font-size="21">Сервис читает terminal result</text>
|
||||
<text x="138" y="1222" fill="#4c1d95" font-family="Arial, sans-serif" font-size="21">Replay 201: demo-request-42, без второго effect</text>
|
||||
|
||||
<rect x="52" y="1300" width="616" height="38" rx="14" fill="#dbeafe"/>
|
||||
<text x="360" y="1327" text-anchor="middle" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="20" font-weight="700">Повтор безопасен только в одном scope и в окне хранения key</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
@@ -0,0 +1,67 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 1400" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Контракт состояния для idempotency key</title>
|
||||
<desc id="desc">Вертикальная диаграмма показывает, как сервис создаёт запись с ключом, scope и fingerprint, возвращает конфликт для другого payload или параллельного выполнения и повторяет сохранённый результат после завершения.</desc>
|
||||
<rect width="720" height="1400" fill="#f8fafc"/>
|
||||
<rect x="28" y="26" width="664" height="112" rx="24" fill="#172554"/>
|
||||
<text x="62" y="72" fill="#f8fafc" font-family="Arial, sans-serif" font-size="32" font-weight="700">Состояние ключа на сервисе</text>
|
||||
<text x="62" y="108" fill="#bfdbfe" font-family="Arial, sans-serif" font-size="20">Key + scope + fingerprint определяют одну операцию</text>
|
||||
|
||||
<rect x="52" y="172" width="616" height="154" rx="22" fill="#e0f2fe" stroke="#0284c7" stroke-width="3"/>
|
||||
<text x="84" y="218" fill="#0c4a6e" font-family="Arial, sans-serif" font-size="27" font-weight="700">Входящий POST</text>
|
||||
<text x="84" y="256" fill="#164e63" font-family="Arial, sans-serif" font-size="22">actor + operation + key</text>
|
||||
<text x="84" y="288" fill="#164e63" font-family="Arial, sans-serif" font-size="22">payload → канонизация → fingerprint</text>
|
||||
|
||||
<line x1="360" y1="328" x2="360" y2="384" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M342 372 L360 396 L378 372" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<rect x="52" y="402" width="616" height="162" rx="22" fill="#eff6ff" stroke="#2563eb" stroke-width="3"/>
|
||||
<text x="84" y="449" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="27" font-weight="700">Атомарный захват записи</text>
|
||||
<text x="84" y="486" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="22">UNIQUE(scope, key) защищает конкуренцию</text>
|
||||
<rect x="84" y="506" width="330" height="37" rx="12" fill="#dbeafe"/>
|
||||
<text x="102" y="532" fill="#1d4ed8" font-family="Arial, sans-serif" font-size="20" font-weight="700">state = in_progress</text>
|
||||
|
||||
<line x1="206" y1="566" x2="206" y2="622" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M188 610 L206 634 L224 610" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="514" y1="566" x2="514" y2="622" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M496 610 L514 634 L532 610" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<rect x="52" y="642" width="308" height="172" rx="22" fill="#ecfdf5" stroke="#059669" stroke-width="3"/>
|
||||
<text x="82" y="689" fill="#065f46" font-family="Arial, sans-serif" font-size="26" font-weight="700">Новый key</text>
|
||||
<text x="82" y="725" fill="#065f46" font-family="Arial, sans-serif" font-size="21">Один владелец</text>
|
||||
<text x="82" y="754" fill="#065f46" font-family="Arial, sans-serif" font-size="21">выполняет effect</text>
|
||||
<text x="82" y="783" fill="#065f46" font-family="Arial, sans-serif" font-size="21">и сохраняет result</text>
|
||||
|
||||
<rect x="360" y="642" width="308" height="172" rx="22" fill="#fff7ed" stroke="#ea580c" stroke-width="3"/>
|
||||
<text x="390" y="689" fill="#9a3412" font-family="Arial, sans-serif" font-size="26" font-weight="700">Запись есть</text>
|
||||
<text x="390" y="725" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">Сравнить hash</text>
|
||||
<text x="390" y="754" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">до обработчика</text>
|
||||
<text x="390" y="783" fill="#9a3412" font-family="Arial, sans-serif" font-size="21">и второго effect</text>
|
||||
|
||||
<line x1="206" y1="816" x2="206" y2="872" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M188 860 L206 884 L224 860" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<line x1="514" y1="816" x2="514" y2="872" stroke="#64748b" stroke-width="8" stroke-linecap="round"/>
|
||||
<path d="M496 860 L514 884 L532 860" fill="none" stroke="#64748b" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
|
||||
<rect x="52" y="892" width="308" height="190" rx="22" fill="#f0fdf4" stroke="#16a34a" stroke-width="3"/>
|
||||
<text x="82" y="939" fill="#166534" font-family="Arial, sans-serif" font-size="26" font-weight="700">completed</text>
|
||||
<text x="82" y="976" fill="#166534" font-family="Arial, sans-serif" font-size="21">status + body + result ID</text>
|
||||
<text x="82" y="1007" fill="#166534" font-family="Arial, sans-serif" font-size="21">повтору: replay</text>
|
||||
<text x="82" y="1038" fill="#166534" font-family="Arial, sans-serif" font-size="21">без нового effect</text>
|
||||
|
||||
<rect x="360" y="892" width="308" height="190" rx="22" fill="#fef2f2" stroke="#dc2626" stroke-width="3"/>
|
||||
<text x="390" y="939" fill="#991b1b" font-family="Arial, sans-serif" font-size="26" font-weight="700">conflict</text>
|
||||
<text x="390" y="976" fill="#991b1b" font-family="Arial, sans-serif" font-size="21">другая hash</text>
|
||||
<text x="390" y="1007" fill="#991b1b" font-family="Arial, sans-serif" font-size="21">или in_progress</text>
|
||||
<text x="390" y="1038" fill="#991b1b" font-family="Arial, sans-serif" font-size="21">не запускает handler</text>
|
||||
|
||||
<rect x="52" y="1130" width="616" height="172" rx="22" fill="#f5f3ff" stroke="#7c3aed" stroke-width="3"/>
|
||||
<text x="84" y="1178" fill="#4c1d95" font-family="Arial, sans-serif" font-size="27" font-weight="700">Граница, которую нельзя скрыть</text>
|
||||
<text x="84" y="1214" fill="#4c1d95" font-family="Arial, sans-serif" font-size="20">Локальная запись не делает внешний вызов атомарным.</text>
|
||||
<text x="84" y="1240" fill="#4c1d95" font-family="Arial, sans-serif" font-size="20">Внешний effect требует своего key или status route.</text>
|
||||
<text x="84" y="1266" fill="#4c1d95" font-family="Arial, sans-serif" font-size="20">TTL дольше retry-budget: поздний retry иначе</text>
|
||||
<text x="84" y="1292" fill="#4c1d95" font-family="Arial, sans-serif" font-size="20">станет новым действием.</text>
|
||||
|
||||
<rect x="52" y="1322" width="616" height="56" rx="14" fill="#dbeafe"/>
|
||||
<text x="360" y="1346" text-anchor="middle" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="20" font-weight="700">Резервируем intent → выполняем effect</text>
|
||||
<text x="360" y="1370" text-anchor="middle" fill="#1e3a8a" font-family="Arial, sans-serif" font-size="20" font-weight="700">→ сохраняем terminal ответ</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.9 KiB |
Reference in New Issue
Block a user