revise February 2021 cache articles
Build and deploy / deploy (push) Successful in 14s

This commit is contained in:
2026-07-31 12:16:22 +03:00
parent 323d80b188
commit e67b416a81
7 changed files with 1048 additions and 1 deletions
@@ -0,0 +1,91 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 1230" role="img" aria-labelledby="cache-consistency-matrix-title cache-consistency-matrix-desc">
<title id="cache-consistency-matrix-title">Матрица допустимых решений чтения при инвалидации кеша</title>
<desc id="cache-consistency-matrix-desc">Пять строк показывают отношения source версии, cache entry и события. Совпадающие версии дают hit, отставшая cache версия вызывает rebuild, а private source запрещает публичный ответ и очищает entry.</desc>
<defs>
<style>
.bg { fill: #101827; }
.title { fill: #f9fafb; font: 700 33px Arial, sans-serif; }
.subtitle { fill: #cbd5e1; font: 400 20px Arial, sans-serif; }
.header { fill: #1e3a5f; stroke: #60a5fa; stroke-width: 2; }
.row { fill: #172554; stroke: #334155; stroke-width: 2; }
.row-warn { fill: #3f1d2e; stroke: #fb7185; stroke-width: 2; }
.row-private { fill: #3b2f16; stroke: #fbbf24; stroke-width: 2; }
.head { fill: #e0f2fe; font: 700 21px Arial, sans-serif; }
.cell { fill: #e5e7eb; font: 400 21px Arial, sans-serif; }
.cell-ok { fill: #d1fae5; font: 700 21px Arial, sans-serif; }
.cell-warn { fill: #fecdd3; font: 700 21px Arial, sans-serif; }
.cell-private { fill: #fef3c7; font: 700 21px Arial, sans-serif; }
.line { stroke: #475569; stroke-width: 2; }
.foot { fill: #94a3b8; font: 400 19px Arial, sans-serif; }
</style>
</defs>
<rect class="bg" width="720" height="1230" rx="28"/>
<text class="title" x="42" y="64">Матрица: что вправе read</text>
<text class="subtitle" x="42" y="97">Один owner · одна public projection · учебная модель</text>
<rect class="header" x="34" y="132" width="652" height="76" rx="14"/>
<text class="head" x="54" y="163">SOURCE</text>
<text class="head" x="214" y="163">CACHE</text>
<text class="head" x="366" y="163">EVENT</text>
<text class="head" x="510" y="163">READ</text>
<text class="head" x="510" y="189">RESULT</text>
<path class="line" d="M190 132 L190 208 M344 132 L344 208 M488 132 L488 208"/>
<rect class="row" x="34" y="224" width="652" height="154" rx="14"/>
<text class="cell" x="54" y="260">public v1</text>
<text class="cell" x="214" y="260">public v1</text>
<text class="cell" x="366" y="260">нет</text>
<text class="cell-ok" x="510" y="260">hit-current</text>
<text class="cell" x="54" y="300">version равна</text>
<text class="cell" x="214" y="300">key совпал</text>
<text class="cell" x="366" y="300">—</text>
<text class="cell-ok" x="510" y="300">вернуть v1</text>
<path class="line" d="M190 224 L190 378 M344 224 L344 378 M488 224 L488 378"/>
<rect class="row-warn" x="34" y="394" width="652" height="154" rx="14"/>
<text class="cell" x="54" y="430">public v2</text>
<text class="cell" x="214" y="430">public v1</text>
<text class="cell" x="366" y="430">pending v2</text>
<text class="cell-warn" x="510" y="430">stale-rebuilt</text>
<text class="cell" x="54" y="470">source новее</text>
<text class="cell" x="214" y="470">entry старая</text>
<text class="cell" x="366" y="470">не доставлен</text>
<text class="cell-warn" x="510" y="470">вернуть v2</text>
<path class="line" d="M190 394 L190 548 M344 394 L344 548 M488 394 L488 548"/>
<rect class="row" x="34" y="564" width="652" height="154" rx="14"/>
<text class="cell" x="54" y="600">public v2</text>
<text class="cell" x="214" y="600">нет</text>
<text class="cell" x="366" y="600">applied v2</text>
<text class="cell-ok" x="510" y="600">miss-built</text>
<text class="cell" x="54" y="640">source current</text>
<text class="cell" x="214" y="640">evicted</text>
<text class="cell" x="366" y="640">v1 deleted</text>
<text class="cell-ok" x="510" y="640">собрать v2</text>
<path class="line" d="M190 564 L190 718 M344 564 L344 718 M488 564 L488 718"/>
<rect class="row" x="34" y="734" width="652" height="154" rx="14"/>
<text class="cell" x="54" y="770">public v2</text>
<text class="cell" x="214" y="770">public v2</text>
<text class="cell" x="366" y="770">late v2</text>
<text class="cell-ok" x="510" y="770">keep hit</text>
<text class="cell" x="54" y="810">version равна</text>
<text class="cell" x="214" y="810">уже rebuilt</text>
<text class="cell" x="366" y="810">v2 duplicate</text>
<text class="cell-ok" x="510" y="810">не delete</text>
<path class="line" d="M190 734 L190 888 M344 734 L344 888 M488 734 L488 888"/>
<rect class="row-private" x="34" y="904" width="652" height="166" rx="14"/>
<text class="cell" x="54" y="940">private v3</text>
<text class="cell" x="214" y="940">public v2</text>
<text class="cell" x="366" y="940">pending v3</text>
<text class="cell-private" x="510" y="940">not-visible</text>
<text class="cell" x="54" y="980">право изменилось</text>
<text class="cell" x="214" y="980">недопустима</text>
<text class="cell" x="366" y="980">не ждём</text>
<text class="cell-private" x="510" y="980">deny + evict</text>
<path class="line" d="M190 904 L190 1070 M344 904 L344 1070 M488 904 L488 1070"/>
<text class="foot" x="42" y="1132">TTL может ограничить жизнь entry, но не доказывает current version.</text>
<text class="foot" x="42" y="1164">Это не Redis, CDN, browser cache или измерение production traffic.</text>
</svg>

After

Width:  |  Height:  |  Size: 5.5 KiB

@@ -0,0 +1,82 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 1240" role="img" aria-labelledby="cache-diagnosis-title cache-diagnosis-desc">
<title id="cache-diagnosis-title">Диагностика старой кешированной проекции</title>
<desc id="cache-diagnosis-desc">Вертикальная схема начинает со старой карточки у читателя, собирает source version, key, cache version, event и visibility. Затем она разделяет stale entry, неправильный ключ, private источник и проблему renderer вне кеша.</desc>
<defs>
<style>
.bg { fill: #111827; }
.title { fill: #f9fafb; font: 700 34px Arial, sans-serif; }
.subtitle { fill: #cbd5e1; font: 400 20px Arial, sans-serif; }
.symptom { fill: #4c1d2a; stroke: #fb7185; stroke-width: 2; }
.evidence { fill: #172554; stroke: #60a5fa; stroke-width: 2; }
.decision { fill: #312e81; stroke: #a5b4fc; stroke-width: 2; }
.action-ok { fill: #12332a; stroke: #34d399; stroke-width: 2; }
.action-warn { fill: #3b2f16; stroke: #fbbf24; stroke-width: 2; }
.head { fill: #ffffff; font: 700 26px Arial, sans-serif; }
.body { fill: #e5e7eb; font: 400 22px Arial, sans-serif; }
.body-blue { fill: #dbeafe; font: 400 22px Arial, sans-serif; }
.body-ok { fill: #d1fae5; font: 400 22px Arial, sans-serif; }
.body-warn { fill: #fef3c7; font: 400 22px Arial, sans-serif; }
.arrow { stroke: #94a3b8; stroke-width: 4; fill: none; marker-end: url(#arrow); }
.note { fill: #94a3b8; font: 400 19px Arial, sans-serif; }
</style>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M0,0 L12,6 L0,12 z" fill="#94a3b8"/>
</marker>
</defs>
<rect class="bg" width="720" height="1240" rx="28"/>
<text class="title" x="42" y="64">Диагноз stale-read</text>
<text class="subtitle" x="42" y="97">Сначала evidence, потом purge</text>
<rect class="symptom" x="48" y="140" width="624" height="118" rx="18"/>
<text class="head" x="72" y="182">Симптом</text>
<text class="body" x="72" y="216">source обновлён, reader видит old title</text>
<text class="body" x="72" y="246">Не удалять key до сохранения фактов.</text>
<path class="arrow" d="M360 258 L360 314"/>
<rect class="evidence" x="48" y="324" width="624" height="186" rx="18"/>
<text class="head" x="72" y="366">Evidence packet</text>
<text class="body-blue" x="72" y="402">1. source id и current version</text>
<text class="body-blue" x="72" y="432">2. read key и cached version</text>
<text class="body-blue" x="72" y="462">3. event state и reader visibility</text>
<text class="body-blue" x="72" y="494">Один title не доказывает причину.</text>
<path class="arrow" d="M360 510 L360 566"/>
<rect class="decision" x="48" y="576" width="624" height="112" rx="18"/>
<text class="head" x="72" y="618">Source version выше cached?</text>
<text class="body" x="72" y="652">Да → key тот же? event pending?</text>
<text class="body" x="72" y="680">Нет → искать другой read layer.</text>
<path class="arrow" d="M190 688 L190 744"/>
<path class="arrow" d="M530 688 L530 744"/>
<rect class="action-ok" x="48" y="754" width="292" height="186" rx="18"/>
<text class="head" x="72" y="796">Stale entry</text>
<text class="body-ok" x="72" y="832">source v2, cache v1</text>
<text class="body-ok" x="72" y="862">read: rebuild v2</text>
<text class="body-ok" x="72" y="892">late v2: keep current</text>
<text class="body-ok" x="72" y="922">Не выдавать v1 как hit.</text>
<rect class="action-warn" x="380" y="754" width="292" height="186" rx="18"/>
<text class="head" x="404" y="796">Не тот key</text>
<text class="body-warn" x="404" y="832">scope или locale lost</text>
<text class="body-warn" x="404" y="862">исправить key contract</text>
<text class="body-warn" x="404" y="892">не purge correct entry</text>
<text class="body-warn" x="404" y="922">до проверки collision.</text>
<path class="arrow" d="M190 940 L190 994"/>
<path class="arrow" d="M530 940 L530 994"/>
<rect class="action-warn" x="48" y="1004" width="292" height="142" rx="18"/>
<text class="head" x="72" y="1046">Source private</text>
<text class="body-warn" x="72" y="1080">deny + evict before event</text>
<text class="body-warn" x="72" y="1110">Право выше TTL.</text>
<rect class="action-ok" x="380" y="1004" width="292" height="142" rx="18"/>
<text class="head" x="404" y="1046">Versions equal</text>
<text class="body-ok" x="404" y="1080">проверить renderer, client</text>
<text class="body-ok" x="404" y="1110">или другой cache layer.</text>
<text class="note" x="48" y="1200">Учебная Map-модель · не browser · не cache store · не broker</text>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

@@ -0,0 +1,76 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 720 1380" role="img" aria-labelledby="cache-key-lifecycle-title cache-key-lifecycle-desc">
<title id="cache-key-lifecycle-title">Учебный жизненный цикл версии кешированной публичной проекции</title>
<desc id="cache-key-lifecycle-desc">Вертикальная схема показывает запись source версии два, публичный ключ, запоздавшее событие ArticleChanged версии два, сравнение cache версии один с source версией два и пересборку публичной проекции до ответа читателю.</desc>
<defs>
<style>
.bg { fill: #0f172a; }
.title { fill: #f8fafc; font: 700 34px Arial, sans-serif; }
.subtitle { fill: #cbd5e1; font: 400 21px Arial, sans-serif; }
.card { fill: #172554; stroke: #60a5fa; stroke-width: 2; }
.card-warn { fill: #3f1d2e; stroke: #fb7185; stroke-width: 2; }
.card-ok { fill: #12332a; stroke: #34d399; stroke-width: 2; }
.badge { fill: #1d4ed8; }
.badge-warn { fill: #be123c; }
.badge-ok { fill: #047857; }
.badge-text { fill: #ffffff; font: 700 20px Arial, sans-serif; }
.head { fill: #f8fafc; font: 700 26px Arial, sans-serif; }
.body { fill: #dbeafe; font: 400 22px Arial, sans-serif; }
.body-warn { fill: #fecdd3; font: 400 22px Arial, sans-serif; }
.body-ok { fill: #d1fae5; font: 400 22px Arial, sans-serif; }
.mono { fill: #e0f2fe; font: 700 21px "Courier New", monospace; }
.arrow { stroke: #94a3b8; stroke-width: 4; fill: none; marker-end: url(#arrow); }
.note { fill: #94a3b8; font: 400 19px Arial, sans-serif; }
</style>
<marker id="arrow" markerWidth="12" markerHeight="12" refX="10" refY="6" orient="auto">
<path d="M0,0 L12,6 L0,12 z" fill="#94a3b8"/>
</marker>
</defs>
<rect class="bg" width="720" height="1380" rx="28"/>
<text class="title" x="48" y="66">Key → version → read</text>
<text class="subtitle" x="48" y="101">Учебный contract одной public проекции</text>
<rect class="card" x="48" y="142" width="624" height="170" rx="18"/>
<rect class="badge" x="72" y="166" width="102" height="38" rx="19"/>
<text class="badge-text" x="91" y="192">WRITE</text>
<text class="head" x="72" y="234">Source owner записал v2</text>
<text class="body" x="72" y="266">article guide-42 · visibility public</text>
<text class="body" x="72" y="296">Только source owner создаёт version.</text>
<path class="arrow" d="M360 312 L360 372"/>
<rect class="card" x="48" y="382" width="624" height="170" rx="18"/>
<rect class="badge" x="72" y="406" width="82" height="38" rx="19"/>
<text class="badge-text" x="92" y="432">KEY</text>
<text class="head" x="72" y="474">Reader scope входит в key</text>
<text class="mono" x="72" y="506">article:public:guide-42</text>
<text class="body" x="72" y="536">editorNote не входит в public value.</text>
<path class="arrow" d="M360 552 L360 612"/>
<rect class="card-warn" x="48" y="622" width="624" height="176" rx="18"/>
<rect class="badge-warn" x="72" y="646" width="98" height="38" rx="19"/>
<text class="badge-text" x="91" y="672">EVENT</text>
<text class="head" x="72" y="714">ArticleChanged v2 запоздало</text>
<text class="body-warn" x="72" y="746">Cache ещё хранит entry sourceVersion v1.</text>
<text class="body-warn" x="72" y="776">Delivery не равно праву вернуть v1.</text>
<path class="arrow" d="M360 798 L360 858"/>
<rect class="card-warn" x="48" y="868" width="624" height="176" rx="18"/>
<rect class="badge-warn" x="72" y="892" width="94" height="38" rx="19"/>
<text class="badge-text" x="90" y="918">READ</text>
<text class="head" x="72" y="960">Сравнение до cache hit</text>
<text class="body-warn" x="72" y="992">source v2 ≠ cached v1 → stale, не hit</text>
<text class="body-warn" x="72" y="1022">Собираем projection только из public fields.</text>
<path class="arrow" d="M360 1044 L360 1104"/>
<rect class="card-ok" x="48" y="1114" width="624" height="184" rx="18"/>
<rect class="badge-ok" x="72" y="1138" width="98" height="38" rx="19"/>
<text class="badge-text" x="90" y="1164">SAFE</text>
<text class="head" x="72" y="1206">stale-rebuilt → public v2</text>
<text class="body-ok" x="72" y="1238">Поздний event v2 сохраняет current entry v2.</text>
<text class="body-ok" x="72" y="1268">Если source стал private: deny и evict.</text>
<text class="note" x="48" y="1342">Map in memory · не Redis · не broker · не CDN</text>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB