{"id":490421,"date":"2026-08-10T12:06:03","date_gmt":"2026-08-10T12:06:03","guid":{"rendered":"https:\/\/savepearlharbor.com\/?p=490421"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=490421","title":{"rendered":"\u0412\u0430\u0448 \u043a\u044d\u0448 \u0432 Redis \u043d\u0435\u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u0435\u043d, \u0447\u0442\u043e \u0441 \u044d\u0442\u0438\u043c \u0434\u0435\u043b\u0430\u0442\u044c?"},"content":{"rendered":"<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>\u0418\u0441\u0442\u043e\u0440\u0438\u044f<\/h2>\n<p>\u041e\u0434\u043d\u0430\u0436\u0434\u044b \u0432 \u043f\u0440\u043e\u0434\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430\u0447\u0430\u043b\u0438 \u043f\u0430\u0434\u0430\u0442\u044c \u043f\u043e <code>OutOfMemory<\/code>. \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u043c \u0431\u044b\u043b\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043e \u043f\u043e <strong>4 \u0413\u0411 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438<\/strong>, \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0431\u044b\u043b\u0430 \u0432\u043f\u043e\u043b\u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0439: \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.<\/p>\n<p>\u0420\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0432 JSON-\u043a\u043e\u043d\u0444\u0438\u0433\u0435.<\/p>\n<p>\u0421\u0445\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f. \u0412 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0445\u0440\u0430\u043d\u0438\u043b\u0441\u044f JSON. \u041f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u043b\u043e \u0435\u0433\u043e \u0438\u0437 \u0431\u0430\u0437\u044b, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u2014 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c\u044b\u0445 \u043f\u043e\u043b\u0435\u0439, \u043e\u0431\u043e\u0433\u0430\u0449\u0435\u043d\u0438\u0435 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438 \u2014 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 Redis. \u041f\u043e\u0434\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u043b\u043e \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 JSON \u0438 \u0445\u043e\u0434\u0438\u043b\u043e \u0437\u0430 \u043d\u0438\u043c \u0432 Redis.<\/p>\n<p>\u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b \u0441\u0435\u0440\u0432\u0435\u0440 Valkey, \u043d\u043e \u0432 \u043a\u043e\u0434\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u043e\u0431\u044b\u0447\u043d\u0430\u044f Redis-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430.<\/p>\n<p>\u0418\u0437-\u0437\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 JSON \u0440\u0430\u0441\u043f\u0443\u0445\u0430\u043b \u0434\u043e <strong>\u0434\u0435\u0441\u044f\u0442\u043a\u043e\u0432 \u043c\u0435\u0433\u0430\u0431\u0430\u0439\u0442<\/strong>. \u0412 \u043a\u044d\u0448 \u043f\u0438\u0441\u0430\u043b\u0438\u0441\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u044b\u0435 \u043f\u043e\u043b\u044f, \u043d\u043e \u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438, \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0438.<\/p>\n<p>\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u0435\u0441\u043b\u0438 \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 JSON, \u0440\u0430\u0437\u043c\u0435\u0440 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0442\u0430\u043a \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c.<\/p>\n<p>\u041d\u043e \u0432\u0441\u0451 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435.<\/p>\n<p>\u041a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a Redis \u043e\u0437\u043d\u0430\u0447\u0430\u043b:<\/p>\n<ol>\n<li>\n<p>\u0421\u0435\u0442\u0435\u0432\u043e\u0439 round-trip \u0434\u043e \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 10 MB \u043f\u043e TCP<\/p>\n<\/li>\n<li>\n<p>\u0410\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u044e 10 MB \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0432 managed heap<\/p>\n<\/li>\n<li>\n<p>\u0414\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0442\u044b\u0441\u044f\u0447 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u043e\u0442\u0432\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u0440\u0443 \u0441\u043e\u0442\u0435\u043d\u0442 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442<\/p>\n<\/li>\n<\/ol>\n<p>\u0421\u0445\u0435\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u043e \u0442\u0430\u043a: <\/p>\n<figure class=\"\"><img decoding=\"async\" src=\"https:\/\/kroki.io\/mermaid\/svg\/eNp1UV9L21AUfxf8DoeyB4ttGqO-hFHIsjGYs0r1yT8Psb2TCzPN0mwgMkjr8GHKOjafB_MTtJ3dgnbZVzj3K_ST7HcT_-J2L4Fwfv_OObct3rwVfkM8ld5u6O1NTxFO4IWRbMjA8yNyyWuT-1oKP3oIOhp01laN2rN1coLgIaOuGXXRlG2aCcVeKxLF6amc5par1VnHpufQVrxAVoTfDFrSjyoLVs5wNKNu07q7mtOW9xFiN1r-K7lrX7NqMKXWOxGSUwJ50huQaViT-KtFPFYdmnnp1IqPd8JKVUOLABbNK4TPVawO-Qf3VY-cjWJuWEds2bmKfTRn5mcr3PIPCrJZsAsLVqFkGMZ7XbrtFAr-zJe4KV_A8YgTuO7sR2JzzqTlJ9v3qac8Uh18MSfchyjhXzeiF2srNZocfSFYDdRHHvGF6qrj-wbfAMWQDjhVXZ1IfP4Pz5SH-P-Nm2LQNPMukfoAVcxjxMU3lIRHlJkN4dHl_m1e2UXg9_90e1eSdW2ZJq0sZSs_0HuizXlUlq43cOfB4HqG-Et1ggFSwpCHqovHQe2n7lh7d-Cd6ikxXgLGCfFQddQn4j9oZax6We0vTdg-bQ\" alt=\"\u0421\u0445\u0435\u043c\u0430 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a Redis\" title=\"\u0421\u0445\u0435\u043c\u0430 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a Redis\" sizes=\"(max-width: 780px) 100vw, 50vw\" srcset=\"https:\/\/kroki.io\/mermaid\/svg\/eNp1UV9L21AUfxf8DoeyB4ttGqO-hFHIsjGYs0r1yT8Psb2TCzPN0mwgMkjr8GHKOjafB_MTtJ3dgnbZVzj3K_ST7HcT_-J2L4Fwfv_OObct3rwVfkM8ld5u6O1NTxFO4IWRbMjA8yNyyWuT-1oKP3oIOhp01laN2rN1coLgIaOuGXXRlG2aCcVeKxLF6amc5par1VnHpufQVrxAVoTfDFrSjyoLVs5wNKNu07q7mtOW9xFiN1r-K7lrX7NqMKXWOxGSUwJ50huQaViT-KtFPFYdmnnp1IqPd8JKVUOLABbNK4TPVawO-Qf3VY-cjWJuWEds2bmKfTRn5mcr3PIPCrJZsAsLVqFkGMZ7XbrtFAr-zJe4KV_A8YgTuO7sR2JzzqTlJ9v3qac8Uh18MSfchyjhXzeiF2srNZocfSFYDdRHHvGF6qrj-wbfAMWQDjhVXZ1IfP4Pz5SH-P-Nm2LQNPMukfoAVcxjxMU3lIRHlJkN4dHl_m1e2UXg9_90e1eSdW2ZJq0sZSs_0HuizXlUlq43cOfB4HqG-Et1ggFSwpCHqovHQe2n7lh7d-Cd6ikxXgLGCfFQddQn4j9oZax6We0vTdg-bQ 780w,&#10;       https:\/\/kroki.io\/mermaid\/svg\/eNp1UV9L21AUfxf8DoeyB4ttGqO-hFHIsjGYs0r1yT8Psb2TCzPN0mwgMkjr8GHKOjafB_MTtJ3dgnbZVzj3K_ST7HcT_-J2L4Fwfv_OObct3rwVfkM8ld5u6O1NTxFO4IWRbMjA8yNyyWuT-1oKP3oIOhp01laN2rN1coLgIaOuGXXRlG2aCcVeKxLF6amc5par1VnHpufQVrxAVoTfDFrSjyoLVs5wNKNu07q7mtOW9xFiN1r-K7lrX7NqMKXWOxGSUwJ50huQaViT-KtFPFYdmnnp1IqPd8JKVUOLABbNK4TPVawO-Qf3VY-cjWJuWEds2bmKfTRn5mcr3PIPCrJZsAsLVqFkGMZ7XbrtFAr-zJe4KV_A8YgTuO7sR2JzzqTlJ9v3qac8Uh18MSfchyjhXzeiF2srNZocfSFYDdRHHvGF6qrj-wbfAMWQDjhVXZ1IfP4Pz5SH-P-Nm2LQNPMukfoAVcxjxMU3lIRHlJkN4dHl_m1e2UXg9_90e1eSdW2ZJq0sZSs_0HuizXlUlq43cOfB4HqG-Et1ggFSwpCHqovHQe2n7lh7d-Cd6ikxXgLGCfFQddQn4j9oZax6We0vTdg-bQ 781w\" loading=\"lazy\" decode=\"async\"\/><\/p>\n<div><figcaption>\u0421\u0445\u0435\u043c\u0430 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a Redis<\/figcaption><\/div>\n<\/figure>\n<p>\u0421\u0438\u0442\u0443\u0430\u0446\u0438\u044e \u0443\u0441\u0443\u0433\u0443\u0431\u043b\u044f\u043b\u043e \u0442\u043e, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u0434\u0435\u0441\u044f\u0442\u043e\u043a \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0431\u044d\u043a\u0435\u043d\u0434\u0443, \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0444\u0438\u0433.<\/p>\n<p>\u041f\u0440\u0438 \u0441\u043e\u0442\u043d\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u0434\u0435\u043b\u044f\u043b\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e <strong>\u0433\u0438\u0433\u0430\u0431\u0430\u0439\u0442 \u043c\u0443\u0441\u043e\u0440\u0430 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443<\/strong>. \u041f\u0440\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0438 90% \u043e\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 4 \u0413\u0411 \u0441\u0431\u043e\u0440\u0449\u0438\u043a \u043c\u0443\u0441\u043e\u0440\u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u043b\u0441\u044f \u0432 \u0430\u0433\u0440\u0435\u0441\u0441\u0438\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c, \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0440\u043e\u0441\u043b\u043e, \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u0438\u043b\u0438\u0432\u0430\u043b\u0430\u0441\u044c.<\/p>\n<hr\/>\n<h2>\u0424\u0438\u043a\u0441 \u0431\u0430\u0433\u0430<\/h2>\n<p>\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438, \u2014 \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0443, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 JSON \u0440\u0430\u0441\u043f\u0443\u0445\u0430\u043b. \u0420\u0430\u0437\u043c\u0435\u0440\u044b JSON \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u0438\u0441\u044c \u0432 10\u201320 \u0440\u0430\u0437.<\/p>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u0442\u0430\u043b\u0430 \u043c\u0435\u043d\u0435\u0435 \u00ab\u0433\u043e\u0440\u044f\u0447\u0435\u0439\u00bb, \u043d\u043e \u043d\u0435 \u0438\u0441\u0447\u0435\u0437\u043b\u0430 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e. \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0442\u0430\u043b\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e 2 \u0413\u0411 \u043f\u0440\u0438 \u0442\u043e\u0439 \u0436\u0435 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0435. \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0432 \u0430\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u044f\u0445 \u043f\u043e\u0434 \u043e\u0442\u0432\u0435\u0442 Redis \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441.<\/p>\n<p>\u0412\u0442\u043e\u0440\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438, \u2014 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e JSON \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043d\u0430 \u043c\u0438\u043d\u0443\u0442\u0443. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0445\u043e\u0434\u0438\u0442\u044c \u0437\u0430 \u043d\u0438\u043c \u0432 \u0431\u0430\u0437\u0443 \u0438 \u0432 Redis \u0445\u043e\u0442\u044f \u0431\u044b \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.<\/p>\n<p>\u0421\u0442\u0430\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435. \u041f\u0430\u043c\u044f\u0442\u0438 \u043f\u043e\u0434 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0441\u0442\u0430\u043b\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u043c\u0435\u043d\u044c\u0448\u0435.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u044f \u0440\u0435\u0448\u0438\u043b \u0437\u0430\u043c\u0435\u0440\u0438\u0442\u044c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0436\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 Redis \u043d\u0435\u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0438 \u0447\u0442\u043e \u0441 \u044d\u0442\u0438\u043c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<hr\/>\n<h2>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442<\/h2>\n<p>\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0431\u044b\u043b\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 .NET Aspire:<\/p>\n<ul>\n<li>\n<p>PostgreSQL \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435;<\/p>\n<\/li>\n<li>\n<p>Valkey \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435;<\/p>\n<\/li>\n<li>\n<p>ASP.NET API.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u2014 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432 NBomber, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442 GET-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a API \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0440\u0435\u0441\u0443\u0440\u0441 Aspire \u043f\u043e \u043a\u043d\u043e\u043f\u043a\u0435 \u0438\u0437 Dashboard.<\/p>\n<p>\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:<\/p>\n<ul>\n<li>\n<p>100 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>60 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u0431\u0435\u0437 warmup, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0447\u0435\u0441\u0442\u043d\u043e \u0441\u0445\u043e\u0434\u0438\u043b\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437;<\/p>\n<\/li>\n<li>\n<p>1000 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u2014 \u0437\u0430\u043a\u0430\u0437\u043e\u0432 \u2014 \u0432 \u0411\u0414, \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e\u0435;<\/p>\n<\/li>\n<li>\n<p>\u043a\u0430\u0436\u0434\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u043c \u0432\u0438\u0434\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u043a\u043e\u043b\u043e 500 \u0431\u0430\u0439\u0442, \u0442\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435 \u043f\u043e\u0440\u043e\u0433\u0430 LOH;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e.<\/p>\n<\/li>\n<\/ul>\n<p>\u041a\u043e\u0434 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f:<\/p>\n<pre><code class=\"cs\">ScenarioProps BuildScenario(string name, string path){    return Scenario.Create(name, async context =&gt;    {        var id = orderIds[context.Random.Next(OrderCount)];        var request = Http.CreateRequest(\"GET\", $\"{baseUrl}{path}\/{id}\")            .WithHeader(\"Accept\", \"application\/json\");        return await Http.Send(httpClient, request);    })    .WithoutWarmUp()    .WithLoadSimulations(        Simulation.RampingConstant(ConcurrentCopies, TimeSpan.FromSeconds(10)),        Simulation.KeepConstant(ConcurrentCopies, TimeSpan.FromSeconds(50)),        Simulation.RampingConstant(0, TimeSpan.FromSeconds(3))    );}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:87px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0435\u0441\u044f\u0442\u0438\u0441\u0435\u043a\u0443\u043d\u0434\u043d\u044b\u0439 \u0440\u0430\u0437\u0433\u043e\u043d \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0443\u0436\u0435\u043d, \u0447\u0442\u043e\u0431\u044b \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0443\u044e \u0434\u0430\u0432\u043a\u0443 \u043d\u0430 \u043a\u044d\u0448 \u0438 \u043d\u0435 \u043d\u0430\u0440\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 ThreadPool starvation.<\/p>\n<h2>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c<\/h2>\n<p>\u041e\u0431\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b<\/p>\n<pre><code class=\"cs\">var builder = WebApplication.CreateBuilder(args);builder.AddServiceDefaults();builder.AddNpgsqlDbContext&lt;AppDbContext&gt;(\"ordersdb\");builder.AddRedisDistributedCache(\"valkey\");var cacheTtl = TimeSpan.FromMinutes(5);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 1: \u043f\u0440\u044f\u043c\u043e\u0439 \u043f\u043e\u0445\u043e\u0434 \u0432 PostgreSQL<\/h3>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-direct\/{id:int}\", async (    int id,    AppDbContext db,    CancellationToken cancellationToken) =&gt;{    var order = await db.Orders.FindAsync([id], cancellationToken);    return order is not null ? Results.Ok(order) : Results.NotFound();}).WithName(\"GetOrderDirect\").WithTags(\"Orders\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 2: \u043a\u044d\u0448 \u0432 Valkey \u0447\u0435\u0440\u0435\u0437 IDistributedCache<\/h3>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-redis\/{id:int}\", async (    int id,    AppDbContext db,    IDistributedCache cache,    CancellationToken cancellationToken) =&gt;{    var cacheKey = $\"order:{id}\";    var cached = await cache.GetAsync(cacheKey, cancellationToken);    if (cached is not null)    {        var fromCache = JsonSerializer.Deserialize&lt;Order&gt;(cached, JsonSerializerOptions.Web);        return Results.Ok(fromCache);    }    var order = await db.Orders.FindAsync([id], cancellationToken);    if (order is null)        return Results.NotFound();    cached = JsonSerializer.SerializeToUtf8Bytes(order, JsonSerializerOptions.Web);    await cache.SetAsync(        cacheKey,        cached,        new DistributedCacheEntryOptions        {            AbsoluteExpirationRelativeToNow = cacheTtl        },        cancellationToken);    return Results.Ok(order);}).WithName(\"GetOrderRedis\").WithTags(\"Orders\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-direct<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-redis<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>RPS<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>10 085<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>19 715<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">8.83 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">4.52 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">7.68 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.66 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">16.74 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">16.86 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">30.34 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">29.62 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>Memory<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>42 MB<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>500\u2013700 MB<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041e\u0431\u044a\u0451\u043c \u043a\u044d\u0448\u0430 \u0432 Valkey \u2014 \u043e\u043a\u043e\u043b\u043e 2 \u041c\u0411.<\/p>\n<hr\/>\n<h3>\u0412\u044b\u0432\u043e\u0434\u044b \u043f\u043e \u043f\u0435\u0440\u0432\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0443<\/h3>\n<p>\u0422\u0430\u043a\u0438\u0435 \u0442\u0430\u0439\u043c\u0438\u043d\u0433\u0438 \u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0433\u043e\u0432\u043e\u0440\u044f\u0442 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0434\u0430\u0432\u043a\u0430 \u043a\u044d\u0448\u0430. \u041a\u043e\u0433\u0434\u0430 \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0438 \u0442\u043e\u043c\u0443 \u0436\u0435 \u043a\u043b\u044e\u0447\u0443 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e, \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0439\u0442\u0438 \u0432 Redis, \u0437\u0430\u0442\u0435\u043c \u0432 \u0431\u0430\u0437\u0443, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 Redis.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u0434\u0430\u0432\u043a\u0438 \u043e\u0431\u044a\u0451\u043c \u043f\u0430\u043c\u044f\u0442\u0438 \u0434\u0435\u0440\u0436\u0430\u043b\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 500 \u041c\u0411.<\/p>\n<p>\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 Redis\/Valkey \u0441 \u043f\u043e\u0445\u043e\u0434\u043e\u043c \u0432 \u043a\u044d\u0448 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0411\u0414, \u043d\u043e \u043f\u043e\u0432\u044b\u0448\u0430\u0435\u0442 \u0440\u0430\u0441\u0445\u043e\u0434 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 GC.<\/p>\n<p>\u0414\u0430\u0436\u0435 \u0431\u0435\u0437 \u0434\u0430\u0432\u043a\u0438 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0431\u0430\u0439\u0442 \u043f\u043e\u0434 \u043e\u0442\u0432\u0435\u0442 Redis \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0431\u043e\u0440\u0449\u0438\u043a \u043c\u0443\u0441\u043e\u0440\u0430. \u0410 \u0435\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 85 000 \u0431\u0430\u0439\u0442, \u043c\u0430\u0441\u0441\u0438\u0432 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432 LOH \u0438 \u043c\u043e\u0436\u0435\u0442 \u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u043b\u0433\u043e.<\/p>\n<p>\u0418 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u0438\u043b\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0412 \u043b\u044e\u0431\u043e\u043c \u044f\u0437\u044b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0438\u0437 Redis, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0431\u0443\u0444\u0435\u0440 \u043f\u043e\u0434 \u044d\u0442\u043e\u0442 \u043e\u0442\u0432\u0435\u0442.<\/p>\n<p>\u041e\u0434\u0438\u043d \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e \u2014 \u043d\u0435 \u0445\u043e\u0434\u0438\u0442\u044c \u0432 Redis \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441, \u0430 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.<\/p>\n<hr\/>\n<h2>\u041a\u044d\u0448 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e ASP.NET Output Cache<\/h2>\n<p><a href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/performance\/caching\/output\" rel=\"noopener noreferrer nofollow\">Output Cache<\/a> (ASP.NET Core 7+) \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0439 HTTP-\u043e\u0442\u0432\u0435\u0442 \u0446\u0435\u043b\u0438\u043a\u043e\u043c: \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434, \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0442\u0435\u043b\u043e. \u041f\u0440\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 middleware \u043e\u0442\u0434\u0430\u0451\u0442 \u0431\u0430\u0439\u0442\u044b \u0438\u0437 \u043a\u044d\u0448\u0430 \u2014 \u0431\u0435\u0437 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430, \u0431\u0435\u0437 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0430\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u0439 \u0438 \u0431\u0435\u0437 \u0434\u0430\u0432\u043a\u0438.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c Output Cache, \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0438 middleware.<\/p>\n<pre><code class=\"cs\">builder.Services.AddOutputCache(options =&gt;    {        \/\/ \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e    options.AddBasePolicy(build =&gt; build.NoCache());        options.AddPolicy(\"Orders5Min\", policy =&gt; policy        .Expire(cacheTtl)        .Tag(\"orders\"));});var app = builder.Build();\/\/ \u041f\u043e\u0441\u043b\u0435 UseCors, UseRouting, UseAuthentication \u0438 UseAuthorization app.UseOutputCache(); <\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0441 Output Cache \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-output\/{id:int}\", async (    int id,    AppDbContext db,    CancellationToken cancellationToken) =&gt;{    \/\/ \u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 cache miss.    \/\/ \u041f\u0440\u0438 hit middleware \u043e\u0442\u0434\u0430\u0451\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0430\u0439\u0442\u044b \u0438 \u0441\u044e\u0434\u0430 \u043d\u0435 \u0437\u0430\u0445\u043e\u0434\u0438\u0442.    var order = await db.Orders.FindAsync([id], cancellationToken);    return order is not null ? Results.Ok(order) : Results.NotFound();}).WithName(\"GetOrderOutputCache\").WithTags(\"Orders\").CacheOutput(\"Orders5Min\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-direct<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-redis<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td>\n<p align=\"left\">3 715<\/p>\n<\/td>\n<td>\n<p align=\"left\">22 780<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>34 526<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">33.92 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">4.20 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>2.71 ms<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">7.68 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">3.11 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.64 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">30.56 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">11.28 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.36 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">305.66 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">21.54 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">21.07 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>Memory<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">42 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">500\u2013700 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">43 MB<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>Output Cache \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u043b \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438.<br \/> \u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\/tree\/78305215b920936c86c4649a73a9e9875e6b6c98\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<p>\u041c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0449\u0435 \u043b\u0443\u0447\u0448\u0435\u2026<\/p>\n<hr\/>\n<h2>\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435<\/h2>\n<p>\u041d\u0430 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f\u0445 \u044f \u0447\u0430\u0441\u0442\u043e \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e \u0443 \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u0432, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 conditional get \u0438 \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e 9 \u0438\u0437 10 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u044d\u0442\u043e\u0442 \u0432\u043e\u043f\u0440\u043e\u0441.<\/p>\n<h3>\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 conditional get<\/h3>\n<p>\u0421\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 <code>ETag<\/code> \u0438 <code>Last-Modified<\/code>, \u0430 \u0432 <code>Cache-Control<\/code> \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c.<\/p>\n<p>\u0415\u0441\u043b\u0438 <code>Cache-Control<\/code> \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d, \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u044b \u0438 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u043a\u044d\u0448\u0438 \u043c\u043e\u0433\u0443\u0442 \u0432\u0435\u0441\u0442\u0438 \u0441\u0435\u0431\u044f \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443. \u041d\u043e \u0432 \u043b\u043e\u043a\u043b\u0430\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0442\u0441\u0443\u0441\u0442\u0432\u0438\u0435 <code>Cache-Control<\/code> \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u0430\u043a <code>no-cache<\/code>. \u0414\u043b\u044f \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0434\u043b\u044f API \u043b\u0443\u0447\u0448\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u044d\u0448\u0430 \u044f\u0432\u043d\u043e.<\/p>\n<p>\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: <code>no-cache<\/code> \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u043d\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00bb. \u041e\u043d \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442: \u00ab\u043c\u043e\u0436\u0435\u0448\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0432 \u043a\u044d\u0448\u0435, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0431\u044f\u0437\u0430\u043d \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u00bb, \u0442\u043e \u0435\u0441\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c conditional get.<\/p>\n<p>\u041f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a <code>If-None-Match<\/code> \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c <code>ETag<\/code> \u0438\u043b\u0438 <code>If-Modified-Since<\/code> \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c <code>Last-Modified<\/code>. \u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0430\u043a\u0442\u0443\u0430\u043b\u0435\u043d \u043b\u0438 \u043a\u044d\u0448. \u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0443\u0430\u043b\u0435\u043d, \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 <code>304 Not Modified<\/code> \u0431\u0435\u0437 \u0442\u0435\u043b\u0430, \u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442.<\/p>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code>-- \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 --GET \/api\/orders\/42 HTTP\/1.1HTTP\/1.1 200 OKETag: \"a1b2c3d4\"Last-Modified: Tue, 22 Jul 2026 10:30:00 GMTContent-Type: application\/json{\"id\":\"42\",\"status\":\"shipped\",...}   \u2190 2.4 KB-- \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 (\u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0438\u0441\u044c) --GET \/api\/orders\/42 HTTP\/1.1If-None-Match: \"a1b2c3d4\"If-Modified-Since: Tue, 22 Jul 2026 10:30:00 GMTHTTP\/1.1 304 Not Modified                                    \u2190 0 \u0431\u0430\u0439\u0442 \u0442\u0435\u043b\u0430<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 Output Cache<\/h3>\n<p>\u0414\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a, \u0447\u0442\u043e\u0431\u044b \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0433 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430. Output Cache \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 conditional GET.<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-output-client\/{id:int}\", async (    int id,    AppDbContext db,    HttpContext context,    CancellationToken cancellationToken) =&gt;{    var order = await db.Orders.FindAsync([id], cancellationToken);    if (order is null)        return Results.NotFound();    context.Response.GetTypedHeaders().ETag = new($\"\\\"{order.Version}\\\"\");    return Results.Ok(order);}).WithName(\"GetOrderOutputCacheWithClientCache\").WithTags(\"Orders\").CacheOutput(\"Orders5Min\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0435 PostgreSQL \u0438 EF Core, \u0443 \u0432\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u0435\u0440\u0441\u0438\u044f \u0441\u0442\u0440\u043e\u043a\u0438 \u043f\u043e\u0447\u0442\u0438 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u043e. \u041c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0438\u0441\u0442\u0435\u043c\u043d\u0443\u044e \u043a\u043e\u043b\u043e\u043d\u043a\u0443 <code>xmin<\/code> \u043a\u0430\u043a concurrency token.<\/p>\n<p>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"cs\">public class Order{    public int Id { get; set; }    \/\/ \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430    [JsonIgnore]    [Timestamp]    public uint Version { get; set; }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u0432 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0447\u0442\u043e \u044d\u0442\u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043e \u0441 <code>xmin<\/code>, \u0430 \u043d\u0435 \u0441 \u043e\u0431\u044b\u0447\u043d\u043e\u0439 \u043a\u043e\u043b\u043e\u043d\u043a\u043e\u0439 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0435.<\/p>\n<h3>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u043c \u043a\u044d\u0448\u043e\u043c<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-redis<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output-client<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>RPS<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">22 293<\/p>\n<\/td>\n<td>\n<p align=\"left\">33 744<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>34 526<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">4.20 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.77 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>2.71 ms<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">3.11 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.66 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.64 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">11.28 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.61 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.36 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">21.54 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">21.79 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">21.07 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>Memory<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">500\u2013700 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">43 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">44 MB<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">0.520 KB<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.502 KB<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>0.255 KB<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u0432\u0441\u0435\u0433\u043e<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">770 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">1 126 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>584 MB<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0412 \u044d\u0442\u043e\u043c \u0442\u0435\u0441\u0442\u0435 <code>orders-output-client<\/code> \u0432\u0435\u0440\u043d\u0443\u043b 100 000 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 <code>200 OK<\/code> \u0438 2 247 768 \u0440\u0430\u0437 \u0432\u0435\u0440\u043d\u0443\u043b <code>304 Not Modified<\/code>, \u0442\u043e \u0435\u0441\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 95,7% \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u043b \u0431\u044b \u043e\u0442\u0432\u0435\u0442 \u0438\u0437 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u043a\u044d\u0448\u0430.<\/p>\n<p>\u0421 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043d\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 Output Cache \u0438 Output Cache + client cache \u043f\u043e\u0447\u0442\u0438 \u043d\u0435\u0437\u0430\u043c\u0435\u0442\u043d\u0430. \u041d\u043e \u0442\u0440\u0430\u0444\u0438\u043a\u0430 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0432 \u0434\u0432\u0430 \u0440\u0430\u0437\u0430 \u043c\u0435\u043d\u044c\u0448\u0435.<\/p>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438, \u043a\u043e\u0433\u0434\u0430 \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0434\u0430\u043b\u0435\u043a\u043e \u0438 \u0438\u043c\u0435\u0435\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u043a\u0430\u043d\u0430\u043b, \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043e\u0449\u0443\u0442\u0438\u043c\u043e\u0439.<\/p>\n<p>\u042f \u043b\u044e\u0431\u043b\u044e \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c \u0444\u043e\u0440\u043c\u0443\u043b\u0443 \u0432\u044b\u0441\u043e\u043a\u043e\u0433\u043e \u0431\u044b\u0441\u0442\u0440\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f:<\/p>\n<ol>\n<li>\n<p>\u043c\u0438\u043d\u0438\u043c\u0443\u043c 90% GET-\u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0434\u043e\u043b\u0436\u043d\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c\u0441\u044f \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u043a\u044d\u0448\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0437 \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0445\u0441\u044f 90% \u2014 \u0438\u0437 \u043a\u044d\u0448\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0438\u0437 \u043e\u0441\u0442\u0430\u0432\u0448\u0438\u0445\u0441\u044f 90% \u2014 \u0438\u0437 \u0441\u0442\u0440\u0430\u043d\u0438\u0447\u043d\u043e\u0433\u043e \u043a\u044d\u0448\u0430 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<\/li>\n<li>\n<p>\u0442\u043e, \u0447\u0442\u043e \u043e\u0441\u0442\u0430\u043b\u043e\u0441\u044c, \u043c\u043e\u0436\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0447\u0442\u0435\u043d\u0438\u0435 \u0441 \u0434\u0438\u0441\u043a\u0430 \u0432 \u0411\u0414.<\/p>\n<\/li>\n<\/ol>\n<p>\u042d\u0442\u043e, \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u0448\u0443\u0442\u043b\u0438\u0432\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430, \u043d\u043e \u0432 \u043a\u0430\u0436\u0434\u043e\u0439 \u0448\u0443\u0442\u043a\u0435 \u043b\u0438\u0448\u044c \u0434\u043e\u043b\u044f \u0448\u0443\u0442\u043a\u0438.<br \/> \u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<h3>Output Cache \u043f\u043e\u0431\u0435\u0434\u0438\u043b, \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f?<\/h3>\n<p>\u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0435\u0442.<\/p>\n<p>\u0423 Output Cache \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043f\u0440\u0438\u044f\u0442\u043d\u044b\u0445 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439:<\/p>\n<ol>\n<li>\n<p>\u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u043a\u0430\u043a-\u0442\u043e \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445;<\/p>\n<\/li>\n<li>\n<p>\u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043e\u043d \u043d\u0435 \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b;<\/p>\n<\/li>\n<li>\n<p>\u043e\u043d \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u043e\u0442\u0432\u0435\u0442 \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u0430 \u0435\u0441\u043b\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0438\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u0441\u0438\u043b\u044c\u043d\u043e \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0435, \u0442\u043e \u043d\u0443\u0436\u0435\u043d \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<\/li>\n<\/ol>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<hr\/>\n<h2>\u0418\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f Output Cache \u043f\u0440\u0438 \u0437\u0430\u043f\u0438\u0441\u0438<\/h2>\n<p>\u041f\u0435\u0440\u0432\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043a\u044d\u0448\u0430, \u2014 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043c\u0435\u043d\u044f\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0435, \u043d\u043e \u043f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0441\u0442\u0430\u0440\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0438\u0437 \u043a\u044d\u0448\u0430.<\/p>\n<p>\u0421 Redis \u044d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u043e \u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e: \u043f\u043e\u0441\u043b\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445 \u043c\u044b \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0432 \u043a\u044d\u0448 \u043d\u043e\u0432\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f. \u0421 Output Cache \u0442\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u043b\u044c\u0437\u044f: \u043d\u0435\u043b\u044c\u0437\u044f \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c HTTP-\u043e\u0442\u0432\u0435\u0442 \u0432 \u043a\u044d\u0448. \u041d\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0442\u0435\u0433\u0443.<\/p>\n<h3>\u0422\u0435\u0433\u0438 \u0434\u043b\u044f Output Cache<\/h3>\n<p>\u0422\u0435\u0433\u0438 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u0434\u0430\u0442\u044c \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0438 \u0438\u043b\u0438 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0430, \u043d\u043e \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0443\u0434\u043e\u0431\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0435\u043a\u043b\u0430\u0440\u0430\u0442\u0438\u0432\u043d\u043e. \u0418\u043d\u043e\u0433\u0434\u0430 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0435\u0433\u0438 \u043f\u0440\u044f\u043c\u043e \u0438\u0437 \u043a\u043e\u0434\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u0443\u044e \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0443.<\/p>\n<pre><code class=\"cs\">public static class CacheExtensions{    internal const string OutputCacheTagsKey = \"OutputCacheTags\";    public static void AddCacheTag(this HttpContext context, string tag)    {        if (context.Items.TryGetValue(OutputCacheTagsKey, out var value) &amp;&amp; value is List&lt;string&gt; tags)        {            tags.Add(tag);        }        else        {            context.Items[OutputCacheTagsKey] = new List&lt;string&gt; { tag };        }    }}class SetCacheTagsPolicy : IOutputCachePolicy{    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation)        =&gt; ValueTask.CompletedTask;    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation)        =&gt; ValueTask.CompletedTask;    public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation)    {        if (context.HttpContext.Items.TryGetValue(CacheExtensions.OutputCacheTagsKey, out var value)            &amp;&amp; value is List&lt;string&gt; tags)        {            foreach (var tag in tags)                context.Tags.Add(tag);        }        return ValueTask.CompletedTask;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0443:<\/p>\n<pre><code class=\"cs\">builder.Services.AddOutputCache(options =&gt;{    options.AddBasePolicy(build =&gt; build        .NoCache()        .AddPolicy&lt;SetCacheTagsPolicy&gt;()    );    options.AddPolicy(\"Orders5Min\", policy =&gt; policy        .Expire(cacheTtl)        .Tag(\"orders\"));});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0442\u0435\u0433:<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-output-client\/{id:int}\", async (    int id,    AppDbContext db,    HttpContext context,    CancellationToken cancellationToken) =&gt;{    var order = await db.Orders.FindAsync([id], cancellationToken);    if (order is null)        return Results.NotFound();    context.AddCacheTag($\"order:{id}\");    context.Response.GetTypedHeaders().ETag = new($\"\\\"{order.Version}\\\"\");    return Results.Ok(order);}).WithName(\"GetOrderOutputCacheWithClientCache\").WithTags(\"Orders\").CacheOutput(\"Orders5Min\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043a\u044d\u0448 \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"cs\">app.MapPut(\"\/api\/orders\/{id:int}\", async (    int id,    AppDbContext db,    IOutputCacheStore cacheStore,    IDistributedCache distributedCache,    CancellationToken cancellationToken) =&gt;{    var order = await db.Orders.FindAsync([id], cancellationToken);    if (order is null)        return Results.NotFound();    var statuses = new[] { \"pending\", \"processing\", \"shipped\", \"delivered\", \"cancelled\" };    order.Status = statuses[Random.Shared.Next(statuses.Length)];    await db.SaveChangesAsync(cancellationToken);    var tag = $\"order:{id}\";    \/\/ \u0418\u043d\u0432\u0430\u043b\u0438\u0434\u0438\u0440\u0443\u0435\u043c Output Cache    await cacheStore.EvictByTagAsync(tag, cancellationToken);    \/\/ \u041f\u0440\u0438 \u0436\u0435\u043b\u0430\u043d\u0438\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c Redis    var cached = JsonSerializer.SerializeToUtf8Bytes(order, JsonSerializerOptions.Web);    await distributedCache.SetAsync(        tag,        cached,        new DistributedCacheEntryOptions        {            AbsoluteExpirationRelativeToNow = cacheTtl        },        cancellationToken);    return Results.NoContent();}).WithName(\"UpdateOrder\").WithTags(\"Orders\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>Redis \u0432 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441\u0440\u0430\u0437\u0443 \u043f\u0438\u0448\u0435\u0442 \u043d\u043e\u0432\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u043a\u044d\u0448 \u0438 \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u043e\u0445\u043e\u0434\u0430 \u0432 \u0431\u0430\u0437\u0443 \u043f\u0440\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0447\u0442\u0435\u043d\u0438\u0438.<\/p>\n<h3>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0441 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435\u043c<\/h3>\n<p>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u0441 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u043c\u0438 \u043a \u0442\u0435\u043c \u0436\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u0435\u0449\u0451 \u043e\u0434\u0438\u043d \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043e\u0434\u0438\u043d \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u044b\u0439 \u0437\u0430\u043a\u0430\u0437 \u0447\u0435\u0440\u0435\u0437 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442.<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-redis<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output-client<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td>\n<p align=\"left\">24 351<\/p>\n<\/td>\n<td>\n<p align=\"left\">40 778<\/p>\n<\/td>\n<td>\n<p align=\"left\">39 965<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">3.66 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.18 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.23 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.77 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.39 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.38 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.43 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">9.85 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.37 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">20.02 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">19.07 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">20.96 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>Memory<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">900 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">43 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">44 MB<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u0432\u0441\u0435\u0433\u043e<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">779 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">1 259 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">696 MB<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u043c \u043a\u044d\u0448\u0435\u043c \u0432\u0435\u0440\u043d\u0443\u043b 309 639 \u043e\u0442\u0432\u0435\u0442\u043e\u0432 <code>200 OK<\/code> \u0438 2 208 187 \u0440\u0430\u0437 \u0432\u0435\u0440\u043d\u0443\u043b <code>304 Not Modified<\/code>.<\/p>\n<p>Cache hit ratio \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0441\u044f: <code>2 208 187 \/ (309 639 + 2 208 187) = 87.7%<\/code><\/p>\n<p>\u0415\u0441\u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435 \u043e\u0434\u043d\u043e\u0433\u043e, \u0430 10 \u0432\u0438\u0440\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u044e\u0449\u0438\u0445 \u0437\u0430\u043f\u0438\u0441\u0438 \u0432 \u0431\u0430\u0437\u0435, cache hit ratio \u043f\u0430\u0434\u0430\u0435\u0442 \u0434\u043e 48,14%<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-redis<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output-client<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td>\n<p align=\"left\">18 720<\/p>\n<\/td>\n<td>\n<p align=\"left\">32 989<\/p>\n<\/td>\n<td>\n<p align=\"left\">31 659<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">4.76 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.70 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.81 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">3.41 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.71 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.72 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">12.75 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">11.29 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">12.57 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">26.22 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">20.54 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">23.14 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u0432\u0441\u0435\u0433\u043e<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">599 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">1 018 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>785 MB<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041f\u0440\u0438 \u0442\u0430\u043a\u043e\u043c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0435 \u0437\u0430\u043f\u0438\u0441\u0435\u0439 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0443\u0436\u0435 \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u0442 \u043f\u0440\u043e\u0438\u0433\u0440\u044b\u0432\u0430\u0442\u044c \u043f\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0442\u0440\u0430\u0442\u0438\u0442\u0441\u044f \u043d\u0430 \u043f\u0435\u0440\u0435\u0441\u044b\u043b\u043a\u0443, \u043f\u0430\u0440\u0441\u0438\u043d\u0433 \u0438 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u043e\u0432. \u041d\u043e \u0432\u0441\u0435 \u0435\u0449\u0435 \u043f\u0440\u0435\u0432\u043e\u0441\u0445\u043e\u0434\u0438\u0442 Redis. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0437\u0430\u0442\u0440\u0430\u0442\u044b \u043d\u0430 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0437\u0430\u043f\u0440\u043e\u0441\u0430 \u0447\u0430\u0441\u0442\u043e \u043a\u043e\u043c\u043f\u0435\u043d\u0441\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u044d\u043a\u043e\u043d\u043e\u043c\u0438\u0435\u0439 \u0442\u0440\u0430\u0444\u0438\u043a\u0430.<\/p>\n<p>\u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\/tree\/787c3646a8c8b92e40f712866922ee0b7aa265e7\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<hr\/>\n<h2>Output Cache \u0434\u043b\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439<\/h2>\n<p>\u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e Output Cache \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u0435 \u0441 \u0435\u0433\u043e \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043e\u0442\u0432\u0435\u0442\u044b:<\/p>\n<ul>\n<li>\n<p>\u0442\u043e\u043b\u044c\u043a\u043e GET \u0438\u043b\u0438 HEAD;<\/p>\n<\/li>\n<li>\n<p>\u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0442\u0432\u0435\u0442\u044b \u0441 \u043a\u043e\u0434\u043e\u043c 200;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435 \u043a\u044d\u0448\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u043e\u0442\u0432\u0435\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u044e\u0442 cookie \u0447\u0435\u0440\u0435\u0437 <code>Set-Cookie<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435 \u043a\u044d\u0448\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u0446\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<\/li>\n<\/ul>\n<p>\u041d\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442 \u0432\u0430\u0440\u0438\u0430\u0442\u0438\u0432\u043d\u043e\u0441\u0442\u044c \u043a\u044d\u0448\u0430 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<pre><code class=\"cs\">class AllowCaching : IOutputCachePolicy{    public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation)    {        context.AllowCacheLookup = true;        context.AllowCacheStorage = true;        return ValueTask.CompletedTask;    }    public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation)        =&gt; ValueTask.CompletedTask;    public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation)    {        context.AllowCacheStorage = true;        return ValueTask.CompletedTask;    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0420\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u043b\u0438\u0442\u0438\u043a\u0443:<\/p>\n<pre><code class=\"cs\">builder.Services.AddOutputCache(options =&gt;{    options.AddPolicy(\"CachePrivate\", build =&gt;        build.AddPolicy&lt;AllowCaching&gt;()             .VaryByValue(ctx =&gt; new(\"user\", ctx.User?.Identity?.Name ?? \"anonymous\")));});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0431\u0443\u0434\u0435\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f.<\/p>\n<p>\u041d\u043e \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0442\u044c, \u0435\u0441\u043b\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0432\u0440\u043e\u0434\u0435 \u0431\u044b \u043e\u0434\u043d\u0438 \u0438 \u0442\u0435 \u0436\u0435, \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0441 \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0435\u0439, \u043d\u043e \u043e\u0442\u0432\u0435\u0442 \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043b\u0438\u0447\u0430\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439?<\/p>\n<p>\u0422\u0443\u0442 \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u0442\u044c \u043e\u0447\u0435\u043d\u044c \u0430\u043a\u043a\u0443\u0440\u0430\u0442\u043d\u044b\u043c. \u0415\u0441\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043e\u0442 \u043f\u0440\u0430\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0435\u0433\u043e \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a, \u0440\u043e\u043b\u0438, \u0442\u0435\u043d\u043d\u0430\u043d\u0442\u0430 \u0438\u043b\u0438 \u0447\u0435\u0433\u043e-\u0442\u043e \u0435\u0449\u0451, \u044d\u0442\u043e \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043a\u043b\u044e\u0447\u0435 \u043a\u044d\u0448\u0430 \u0438\u043b\u0438 \u0432 <code>ETag<\/code>. \u0418\u043d\u0430\u0447\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0434\u0430\u0442\u044c \u0447\u0443\u0436\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0438\u043b\u0438 \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c <code>304<\/code>.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0442\u0432\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0435\u0440\u0441\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u0439, \u0447\u0430\u0441\u0442\u043e \u043b\u0443\u0447\u0448\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435 \u0446\u0435\u043b\u044b\u0439 HTTP-\u043e\u0442\u0432\u0435\u0442, \u0430 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 \u043f\u0430\u043c\u044f\u0442\u0438.<\/p>\n<hr\/>\n<h2>Memory cache \u0434\u043b\u044f \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041a\u043e\u0433\u0434\u0430 \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0446\u0435\u043b\u0438\u043a\u043e\u043c, \u043c\u043e\u0436\u043d\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <a href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/performance\/caching\/memory\" rel=\"noopener noreferrer nofollow\">MemoryCache<\/a>.<\/p>\n<p>\u041f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u043d \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u043e\u043a\u043e\u0439:<\/p>\n<pre><code class=\"cs\">builder.Services.AddMemoryCache();<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430 conditional get \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e:<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-inmem-client\/{id:int}\", async (    int id,    AppDbContext db,    HttpContext context,    IMemoryCache cache,    CancellationToken cancellationToken) =&gt;{    var cacheKey = $\"order:{id}\";    if (cache.TryGetValue&lt;Order&gt;(cacheKey, out var order))    {        var tag = $\"\\\"{order!.Version}\\\"\";        if (context.Request.GetTypedHeaders().IfNoneMatch.Any(t =&gt; t.Tag == tag))        {            context.Response.GetTypedHeaders().ETag = new(tag);            return Results.StatusCode(304);        }    }    else    {        order = await db.Orders.FindAsync([id], cancellationToken);        if (order is null)            return Results.NotFound();        cache.Set(cacheKey, order, cacheTtl);    }    context.Response.GetTypedHeaders().ETag = new($\"\\\"{order.Version}\\\"\");    return Results.Ok(order);}).WithName(\"GetOrderWithMemoryCache\").WithTags(\"Orders\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u0441 Memory Cache<\/h3>\n<p>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u044e \u0442\u0435\u0441\u0442 \u0441 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-output-client<\/p>\n<\/th>\n<th>\n<p align=\"left\">orders-inmem-client<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">RPS<\/p>\n<\/td>\n<td>\n<p align=\"left\">38 125<\/p>\n<\/td>\n<td>\n<p align=\"left\">40 066<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.33 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">2.22 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.46 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">1.41 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.27 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">10.24 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">20.38 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">19.92 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u0432\u0441\u0435\u0433\u043e<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">671.1 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">704.6 MB<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>200 OK<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">321 388<\/p>\n<\/td>\n<td>\n<p align=\"left\">322 564<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>304 Not Modified<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">2 080 478<\/p>\n<\/td>\n<td>\n<p align=\"left\">2 201 620<\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u041f\u0440\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0435 \u0447\u0443\u0442\u044c \u043b\u0443\u0447\u0448\u0435 \u0431\u044b\u043b \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 Output Cache. \u041f\u043e \u0441\u0443\u0442\u0438, \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043c\u0435\u0436\u0434\u0443 Output Cache \u0438 Memory Cache \u0432 \u044d\u0442\u043e\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442.<\/p>\n<p>\u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\/tree\/a20964ad5f76073bc334f5e5e34eee74705b522d\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<hr\/>\n<h2>\u0422\u0435\u0433\u0438 \u0434\u043b\u044f Memory Cache<\/h2>\n<p>\u0412 \u0442\u0435\u0441\u0442\u0435 \u043a\u044d\u0448 \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u043b\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e \u043a\u043b\u044e\u0447\u0443. \u041d\u043e \u043d\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0435 \u044d\u0442\u043e \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e. \u041d\u0443\u0436\u043d\u043e \u0434\u0435\u0440\u0436\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u0443: \u043a\u0430\u043a\u0438\u0435 \u043a\u043b\u044e\u0447\u0438 \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0442\u044c \u043f\u0440\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u043a\u0430\u043a\u0438\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u041f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043a\u0430\u0440\u0442\u0443 \u0432 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u043e\u0436\u043d\u0435\u0435, \u0447\u0435\u043c \u0441\u0430\u043c\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435.<\/p>\n<p>\u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043a\u0440\u0443\u0442\u0438\u0442\u044c \u043a Memory Cache \u0442\u0435\u0433\u0438, \u043a\u0430\u043a \u0432 Output Cache. \u0412 \u0441\u0430\u043c\u043e\u043c <code>IMemoryCache<\/code> \u0442\u0435\u0433\u043e\u0432 \u043d\u0435\u0442, \u043d\u043e \u0435\u0441\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u0438\u0432\u044f\u0437\u0430\u0442\u044c \u043a\u043b\u044e\u0447 \u043a <code>IChangeToken<\/code>.<\/p>\n<p>\u041d\u0443\u0436\u0435\u043d \u0441\u043b\u043e\u0432\u0430\u0440\u044c <code>string =&gt; IChangeToken<\/code>. \u041d\u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0443\u0434\u0430\u043b\u0451\u043d \u0438\u0437 \u043a\u044d\u0448\u0430, \u0438 \u0442\u043e\u0433\u0434\u0430 <code>IChangeToken<\/code> \u0438 \u0441\u0430\u043c \u0442\u0435\u0433 \u0441\u0442\u0430\u043d\u0443\u0442 \u043d\u0435\u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438. \u0418\u0445 \u043d\u0430\u0434\u043e \u0443\u0434\u0430\u043b\u044f\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u043f\u0430\u043c\u044f\u0442\u044c \u043d\u0435 \u0443\u0442\u0435\u043a\u0430\u043b\u0430.<\/p>\n<p>\u041d\u0438\u0436\u0435 \u2014 \u043f\u0440\u0438\u043c\u0435\u0440 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440\u0430 \u0442\u043e\u043a\u0435\u043d\u043e\u0432. \u042d\u0442\u043e \u043d\u0435 production-ready \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430, \u043d\u043e \u0438\u0434\u0435\u044e \u043e\u043d \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0445\u043e\u0440\u043e\u0448\u043e.<\/p>\n<pre><code class=\"cs\">class ChangeTokenManager : IDisposable{    private readonly ConcurrentDictionary&lt;object, WeakReference&lt;Token&gt;&gt; tokens = new();    public IChangeToken GetChangeToken(object key)    {        if (tokens.TryGetValue(key, out var weakRef) &amp;&amp; weakRef.TryGetTarget(out var changeToken))        {            return changeToken; \/\/ \u0436\u0438\u0432\u043e\u0439 \u0442\u043e\u043a\u0435\u043d        }        changeToken = new Token(this, key);        \/\/ \u0421\u0441\u044b\u043b\u043a\u0438 \u043d\u0435 \u0431\u044b\u043b\u043e + TryAdd \u2192 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u0442\u043e\u043a\u0435\u043d        if (weakRef is null &amp;&amp; tokens.TryAdd(key, new WeakReference&lt;Token&gt;(changeToken)))        {            return changeToken; \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d        }        \/\/ \u0421\u0441\u044b\u043b\u043a\u0430 \u0431\u044b\u043b\u043e \u043c\u0435\u0440\u0442\u0432\u0430\u044f + TryUpdate \u2192 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0442\u043e\u043a\u0435\u043d        if (weakRef is not null &amp;&amp; tokens.TryUpdate(key, new WeakReference&lt;Token&gt;(changeToken), weakRef))        {            return changeToken; \/\/ \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d        }        \/\/ \u041d\u0435 \u0441\u043c\u043e\u0433\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0438\u043b\u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u043a\u043b\u044e\u0447 (\u043a\u0442\u043e-\u0442\u043e \u0434\u0440\u0443\u0433\u043e\u0439 \u0443\u0436\u0435 \u0441\u0434\u0435\u043b\u0430\u043b) - \u0432\u044b\u0431\u0440\u043e\u0441\u0438\u0442\u044c \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d \u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u044c        changeToken.Dispose();        return GetChangeToken(key);    }    public async ValueTask OnChange(object key)    {        if (tokens.Remove(key, out var weakRef) &amp;&amp; weakRef.TryGetTarget(out var changeToken))        {            await changeToken.CancelAsync();            changeToken.Dispose();        }    }    public void Dispose()    {        foreach (var (_, weakRef) in tokens)        {            if (weakRef.TryGetTarget(out var token))            {                token.Dispose();            }        }        tokens.Clear();    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u0430\u043c \u0442\u043e\u043a\u0435\u043d:<\/p>\n<pre><code class=\"cs\">sealed class Token(ChangeTokenManager manager, object key) : CancellationTokenSource, IChangeToken{    public bool ActiveChangeCallbacks =&gt; !IsCancellationRequested;    public bool HasChanged =&gt; IsCancellationRequested;    public IDisposable RegisterChangeCallback(Action&lt;object?&gt; callback, object? state)        =&gt; base.Token.Register(callback, state);    ~Token()    {        manager.tokens.Remove(key, out _);    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435:<\/p>\n<pre><code class=\"cs\">builder.Services.AddSingleton&lt;ChangeTokenManager&gt;();<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-inmem-client\/{id:int}\", async (    int id,    AppDbContext db,    HttpContext context,    IMemoryCache cache,    ChangeTokenManager changeTokens,    CancellationToken cancellationToken) =&gt;{    var cacheKey = $\"order:{id}\";    if (!cache.TryGetValue&lt;Order&gt;(cacheKey, out var order))    {        order = await db.Orders.FindAsync([id], cancellationToken);        if (order is null)            return Results.NotFound();        using var entry = cache.CreateEntry(cacheKey);        entry.SetValue(order);        entry.SetSlidingExpiration(cacheTtl);        entry.AddExpirationToken(changeTokens.GetChangeToken(cacheKey));    }    var tag = $\"\\\"{order!.Version}\\\"\";    if (context.Request.GetTypedHeaders().IfNoneMatch.Any(t =&gt; t.Tag == tag))    {        context.Response.GetTypedHeaders().ETag = new(tag);        return Results.StatusCode(304);    }    context.Response.GetTypedHeaders().ETag = new(tag);    return Results.Ok(order);});<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041d\u0430 \u0431\u044b\u0441\u0442\u0440\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043a\u044d\u0448\u0430 \u0442\u0430\u043a\u0430\u044f \u0441\u0445\u0435\u043c\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043d\u0435 \u0432\u043b\u0438\u044f\u0435\u0442, \u043d\u043e \u0434\u0430\u0451\u0442 \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u0443\u044e \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044e \u043f\u043e \u0442\u0435\u0433\u0430\u043c.<\/p>\n<p>\u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\/tree\/a673e0a9d2050a1fbb261eb753dfa84e5535246d\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<hr\/>\n<h2>\u041a\u044d\u0448 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043e\u0431\u044a\u0451\u043c\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445<\/h2>\n<p>\u041e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u0430\u044f \u043f\u0430\u043c\u044f\u0442\u044c \u043d\u0430 \u0432\u0435\u0431-\u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u2014 \u0440\u0435\u0441\u0443\u0440\u0441 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u044b\u0439 \u0438 \u0446\u0435\u043d\u043d\u044b\u0439. \u0415\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u043e\u0431\u044a\u0451\u043c\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u043b\u0443\u0447\u0448\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u0438\u0441\u043a. \u0412 \u0441\u043e\u0447\u0435\u0442\u0430\u043d\u0438\u0438 \u0441 Memory Cache \u043c\u043e\u0436\u043d\u043e \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u043b\u044e\u0447\u0438 \u0438 \u043c\u0435\u0442\u0430\u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438, \u0430 \u0441\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u2014 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435.<\/p>\n<p>\u0421\u0440\u0430\u0432\u043d\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432:<\/p>\n<ul>\n<li>\n<p>\u043f\u0440\u044f\u043c\u043e\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a \u0431\u0430\u0437\u0435 \u0437\u0430 \u0432\u0441\u0435\u043c\u0438 \u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>Redis-\u043a\u044d\u0448 \u0441\u043e \u0432\u0441\u0435\u043c\u0438 \u0437\u0430\u043a\u0430\u0437\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u0434\u0430\u0447\u0430 \u0444\u0430\u0439\u043b\u0430 \u0441 \u0434\u0438\u0441\u043a\u0430 \u0431\u0435\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0433\u043e \u043a\u044d\u0448\u0430;<\/p>\n<\/li>\n<li>\n<p>\u043e\u0442\u0434\u0430\u0447\u0430 \u0444\u0430\u0439\u043b\u0430 \u0441 \u0434\u0438\u0441\u043a\u0430 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u043c \u043a\u044d\u0448\u0435\u043c.<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0430\u0437\u043e\u0432\u044b\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442:<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-direct\", AppDbContext db =&gt;{    return Results.Ok(db.Orders);}).WithName(\"GetOrdersDirect\").WithTags(\"Orders\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u044d\u0448 \u0432 Redis:<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-redis\", async (    AppDbContext db,    IDistributedCache cache,    CancellationToken cancellationToken) =&gt;{    var cacheKey = \"orders\";    var cached = await cache.GetAsync(cacheKey, token: cancellationToken);    if (cached is not null)    {        var fromCache = JsonSerializer.Deserialize&lt;List&lt;Order&gt;&gt;(cached, JsonSerializerOptions.Web);        return Results.Ok(fromCache);    }    var orders = await db.Orders.ToListAsync(cancellationToken);    cached = JsonSerializer.SerializeToUtf8Bytes(orders, JsonSerializerOptions.Web);    await cache.SetAsync(        cacheKey,        cached,        new DistributedCacheEntryOptions        {            AbsoluteExpirationRelativeToNow = cacheTtl        },        cancellationToken);    return Results.Ok(orders);}).WithName(\"GetOrdersRedis\").WithTags(\"Orders\");<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041a\u044d\u0448 \u043d\u0430 \u0434\u0438\u0441\u043a\u0435:<\/p>\n<pre><code class=\"cs\">app.MapGet(\"\/api\/orders-disk\", async (    AppDbContext db,    IMemoryCache cache,    CancellationToken cancellationToken) =&gt;{    var cached = await cache.GetOrCreateAsync(\"orders\", async entry =&gt;    {        var file = new FileInfo(Path.GetTempFileName());        await using var stream = file.OpenWrite();        long version = -1;        var orders = db.Orders.AsAsyncEnumerable().Select(x =&gt;        {            version = Math.Max(version, x.Version);            return x;        });        await JsonSerializer.SerializeAsync(stream, orders, JsonSerializerOptions.Web, cancellationToken);        var result = new CachedFile(file.FullName, \"application\/json\", EntityTag: new($\"\\\"{version}\\\"\"));        entry.SetSlidingExpiration(cacheTtl);        entry.RegisterPostEvictionCallback(static (_, value, _, _) =&gt;        {            if (value is CachedFile cachedFile) File.Delete(cachedFile.Path);        });        return result;    });    return TypedResults.PhysicalFile(        cached!.Path,        cached.ContentType,        lastModified: cached.LastModified,        entityTag: cached.EntityTag);}).WithName(\"GetOrdersDisk\").WithTags(\"Orders\");record CachedFile(    string Path,    string ContentType,    DateTimeOffset? LastModified = null,    EntityTagHeaderValue? EntityTag = null);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0422\u0443\u0442 \u0435\u0441\u0442\u044c \u043d\u044e\u0430\u043d\u0441: \u0435\u0441\u043b\u0438 \u043a\u044d\u0448-\u0437\u0430\u043f\u0438\u0441\u044c \u0431\u0443\u0434\u0435\u0442 \u0443\u0434\u0430\u043b\u0435\u043d\u0430 \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0444\u0430\u0439\u043b\u0430, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0433\u043e\u043d\u043a\u0430. \u0412  <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\/tree\/3a034b8db1b6be2bb267abdc7d83afd65e014106\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u043c\u043c\u0438\u0442<\/a> \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432\u044b\u0437\u043e\u0432\u043e\u0432, \u043d\u043e \u0441\u044e\u0434\u0430 \u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435 \u0441\u0442\u0430\u043b, \u043a\u043e\u0434\u0430 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435 \u0438 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e.<\/p>\n<h3>\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0442\u0435\u0441\u0442\u0430 \u0441 \u0431\u043e\u043b\u044c\u0448\u0438\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u043c\u0438<\/h3>\n<div>\n<div class=\"table\">\n<table>\n<tbody>\n<tr>\n<th>\n<p align=\"left\">\u041c\u0435\u0442\u0440\u0438\u043a\u0430<\/p>\n<\/th>\n<th>\n<p align=\"left\">all-orders-direct<\/p>\n<\/th>\n<th>\n<p align=\"left\">all-orders-redis<\/p>\n<\/th>\n<th>\n<p align=\"left\">all-orders-disk-no-cache<\/p>\n<\/th>\n<th>\n<p align=\"left\">all-orders-disk<\/p>\n<\/th>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>RPS<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">510<\/p>\n<\/td>\n<td>\n<p align=\"left\">531<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>3 678<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>11 471<\/strong><\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">Mean<\/p>\n<\/td>\n<td>\n<p align=\"left\">175.46 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">168.54 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">24.22 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">7.77 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P50<\/p>\n<\/td>\n<td>\n<p align=\"left\">154.50 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">162.69 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">24.70 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">7.70 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P95<\/p>\n<\/td>\n<td>\n<p align=\"left\">297.47 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">248.19 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">37.06 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">11.85 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\">P99<\/p>\n<\/td>\n<td>\n<p align=\"left\">739.33 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">574.98 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">47.26 ms<\/p>\n<\/td>\n<td>\n<p align=\"left\">19.98 ms<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u043d\u0430 \u0437\u0430\u043f\u0440\u043e\u0441<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">253.77 KB<\/p>\n<\/td>\n<td>\n<p align=\"left\">253.77 KB<\/p>\n<\/td>\n<td>\n<p align=\"left\">253.79 KB<\/p>\n<\/td>\n<td>\n<p align=\"left\">0.313 KB<\/p>\n<\/td>\n<\/tr>\n<tr>\n<td>\n<p align=\"left\"><strong>\u0422\u0440\u0430\u0444\u0438\u043a \u0432\u0441\u0435\u0433\u043e<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\">7 962 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\">8 288 MB<\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>57 433 MB<\/strong><\/p>\n<\/td>\n<td>\n<p align=\"left\"><strong>222 MB<\/strong><\/p>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<p>\u0422\u0435\u0441\u0442\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438\u0441\u044c \u0441 \u043f\u0440\u043e\u0433\u0440\u0435\u0432\u043e\u043c, \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u043c\u0435\u043d\u044f\u043b\u0438\u0441\u044c.<\/p>\n<p>\u041d\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043e\u0431\u044a\u0451\u043c\u0430\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u2014 \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e 252 \u041a\u0411 JSON \u2014 Redis \u043d\u0435 \u0432\u044b\u0438\u0433\u0440\u044b\u0432\u0430\u0435\u0442 \u0434\u0430\u0436\u0435 \u0443 \u043f\u0440\u044f\u043c\u043e\u0433\u043e \u043f\u043e\u0445\u043e\u0434\u0430 \u0432 \u0431\u0430\u0437\u0443, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u044f \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0430\u043c\u044f\u0442\u0438. \u041e\u0442\u0434\u0430\u0447\u0430 \u0441 \u0434\u0438\u0441\u043a\u0430 \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0432 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u0435\u0435. \u0410 \u0435\u0441\u043b\u0438 \u0435\u0449\u0451 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 conditional GET, \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u043f\u043e \u0442\u0440\u0430\u0444\u0438\u043a\u0443 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043f\u0440\u0438\u043b\u0438\u0447\u043d\u043e\u0439.<\/p>\n<p>\u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/gandjustas\/CacheBenchmark\/tree\/3a034b8db1b6be2bb267abdc7d83afd65e014106\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<hr\/>\n<h2>\u041a\u0430\u043a \u044d\u0442\u043e \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u0442\u044c<\/h2>\n<p>\u041e\u0434\u043d\u0430 \u0438\u0437 \u043f\u0440\u0438\u0447\u0438\u043d \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Redis \u2014 \u0434\u0430\u043d\u043d\u044b\u0435, \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0435 \u0441 \u043e\u0434\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430, \u0431\u0443\u0434\u0443\u0442 \u043f\u0440\u043e\u0447\u0438\u0442\u0430\u043d\u044b \u043d\u0430 \u0434\u0440\u0443\u0433\u043e\u043c. \u0412 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u043a\u044d\u0448\u0430 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430, \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u0430\u043c\u043e\u043c\u0443 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u044d\u0448 \u043c\u0435\u0436\u0434\u0443 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430\u043c\u0438.<\/p>\n<p>\u041d\u043e \u00ab\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e\u00bb \u044d\u0442\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043e\u0441\u0442\u0430\u0451\u0442\u0441\u044f. \u041f\u0440\u0438 \u043c\u0430\u0441\u0441\u043e\u0432\u043e\u043c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u0431\u0430\u0437\u0435 \u0438\u043b\u0438 \u043f\u0440\u0438 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u0441\u043e\u0431\u0440\u0430\u043d\u043d\u044b\u0445 \u0438\u0437 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u0430 \u0441\u0442\u0440\u043e\u043a, \u043d\u0435\u0438\u0437\u0431\u0435\u0436\u043d\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043f\u0438\u0441\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u043a\u044d\u0448\u0430.<\/p>\n<p>\u0412\u043c\u0435\u0441\u0442\u043e \u0440\u0443\u0447\u043d\u043e\u0433\u043e \u0441\u0431\u0440\u043e\u0441\u0430 \u043a\u043b\u044e\u0447\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0443\u043d\u0438\u0432\u0435\u0440\u0441\u0430\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u0442 \u043a\u044d\u0448 \u043f\u043e \u0442\u0435\u0433\u0443 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0441\u0442\u0440\u043e\u043a\u0438 \u0432 \u0431\u0430\u0437\u0435.<\/p>\n<hr\/>\n<h2>\u0421\u0431\u0440\u043e\u0441 \u043a\u044d\u0448\u0430 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 Postgres<\/h2>\n<p>\u0414\u043b\u044f PostgreSQL \u044d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044e. \u042f \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u044d\u0442\u043e\u0432 \u0434\u043e\u043a\u043b\u0430\u0434\u0435 \u043d\u0430 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 PgBootcamp <a href=\"https:\/\/youtu.be\/Z2U7UgiKK2o\" rel=\"noopener noreferrer nofollow\">https:\/\/youtu.be\/Z2U7UgiKK2o<\/a><\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 PostgreSQL<\/h3>\n<p>\u041e\u0434\u0438\u043d \u0440\u0430\u0437 \u043d\u0443\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044e \u0432 \u043a\u043e\u043d\u0444\u0438\u0433\u0435 \u0438\u043b\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u0445 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430:<\/p>\n<pre><code>wal_level = logical<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u0430\u0442\u044c \u043f\u0440\u0430\u0432\u0430 <code>REPLICATION<\/code> \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e, \u043f\u043e\u0434 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u0431\u0430\u0437\u0435, \u0438 \u0440\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0432 <code>pg_hba.conf<\/code>.<\/p>\n<h3>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u0438<\/h3>\n<p>\u0412 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b:<\/p>\n<pre><code class=\"sql\">ALTER TABLE orders REPLICA IDENTITY FULL;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e \u0434\u043b\u044f \u043d\u0443\u0436\u043d\u044b\u0445 \u0442\u0430\u0431\u043b\u0438\u0446:<\/p>\n<pre><code class=\"sql\">CREATE PUBLICATION app_changes FOR TABLE orders;<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0442\u0430\u0431\u043b\u0438\u0446 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e, \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0438\u0445 \u0447\u0435\u0440\u0435\u0437 \u0437\u0430\u043f\u044f\u0442\u0443\u044e.<\/p>\n<h3>\u041f\u043e\u0434\u043f\u0438\u0441\u043a\u0430 \u043d\u0430 \u043f\u0443\u0431\u043b\u0438\u043a\u0430\u0446\u0438\u044e \u0438\u0437 ASP.NET<\/h3>\n<p>\u0421\u043e\u0437\u0434\u0430\u0451\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0441\u043b\u043e\u0442 \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0443\u0434\u0430\u043b\u0438\u0442\u0441\u044f \u043f\u0440\u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u0438 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f. \u0418\u0437 \u044d\u0442\u043e\u0433\u043e \u0441\u043b\u043e\u0442\u0430 \u0447\u0438\u0442\u0430\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e\u0431 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f\u0445 \u0432 \u0442\u0430\u0431\u043b\u0438\u0446\u0430\u0445.<\/p>\n<pre><code class=\"cs\">class PgOutputCacheInvalidation(    IConfiguration config,    IOutputCacheStore outputCache,    ChangeTokenManager tokenManager) : BackgroundService{    protected override async Task ExecuteAsync(CancellationToken stoppingToken)    {        NpgsqlTypes.NpgsqlLogSequenceNumber? walLocation = null;        while (!stoppingToken.IsCancellationRequested)        {            await using var conn = new LogicalReplicationConnection(config.GetConnectionString(\"ordersdb\"));            await conn.Open(stoppingToken);            var slot = await conn.CreatePgOutputReplicationSlot(                \"tx_slot\",                temporarySlot: true,                cancellationToken: stoppingToken);            var replicationStream = conn.StartReplication(                slot,                new PgOutputReplicationOptions(\"app_changes\", PgOutputProtocolVersion.V4, binary: true),                stoppingToken,                walLocation);            await foreach (var message in replicationStream)            {                await ProcessMessage(message, stoppingToken);                walLocation = message.WalEnd;            }        }    }    private async Task ProcessMessage(PgOutputReplicationMessage message, CancellationToken stoppingToken)    {        switch (message)        {            case FullUpdateMessage m:                await RemoveCache(m.Relation.RelationName, m.OldRow, stoppingToken);                await RemoveCache(m.Relation.RelationName, m.NewRow, stoppingToken);                break;            case InsertMessage m:                await RemoveCache(m.Relation.RelationName, m.NewRow, stoppingToken);                break;            case FullDeleteMessage m:                await RemoveCache(m.Relation.RelationName, m.OldRow, stoppingToken);                break;        }    }    private async Task RemoveCache(string tableName, ReplicationTuple row, CancellationToken stoppingToken)    {        await foreach (var val in row.WithCancellation(stoppingToken))        {            var columnName = val.GetFieldName();            switch (columnName)            {                case \"id\":                    var id = await val.Get&lt;int&gt;(stoppingToken);                    var key = $\"{tableName}:{id}\";                    await outputCache.EvictByTagAsync(key, stoppingToken);                    await tokenManager.OnChange(key);                    break;                \/\/ \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u043b\u044f \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e            }        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0438, \u043d\u0430\u043f\u0440\u043c\u0435\u0438\u0440 \u043a\u0440\u0430\u0442\u043a\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0435 \u043f\u0440\u043e\u043f\u0430\u0434\u0430\u043d\u0438\u0435 \u0441\u0432\u044f\u0437\u0438, \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u0442\u0435\u0440\u0430 \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0441\u043b\u0443\u0447\u0430\u0438, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0447\u0442\u0435\u043d\u0438\u0435 \u043b\u043e\u0433\u0430 \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438 \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f. \u0418\u043d\u0430\u0447\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0443\u043f\u0430\u0434\u0435\u0442.<\/p>\n<p>\u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\/tree\/818c58cfaa7161f724799ca51262c3971767fe58\" rel=\"noopener noreferrer nofollow\">\u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435<\/a>.<\/p>\n<hr\/>\n<h2>\u0421\u0431\u0440\u043e\u0441 \u043a\u044d\u0448\u0430 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 SQL Server<\/h2>\n<p>\u0412 SQL Server \u043d\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u043b\u043e\u0433\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u0438, \u043a\u0430\u043a \u0432 PostgreSQL, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c Change Tracking.<\/p>\n<h3>\u0412\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 Change Tracking<\/h3>\n<p>\u0412 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0434\u043b\u044f \u0432\u0441\u0435 \u0431\u0430\u0437\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437:<\/p>\n<pre><code class=\"sql\">ALTER DATABASE YourDatabaseNameSET CHANGE_TRACKING = ON(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b:<\/p>\n<pre><code class=\"sql\">ALTER TABLE dbo.OrdersENABLE CHANGE_TRACKING WITH (TRACK_COLUMNS_UPDATED = ON);<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<h3>\u041e\u043f\u0440\u043e\u0441 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0438\u0437 ASP.NET<\/h3>\n<p>\u0418\u0434\u0435\u044f \u043f\u0440\u043e\u0441\u0442\u0430\u044f:<\/p>\n<ol>\n<li>\n<p>\u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0443\u044e \u0432\u0435\u0440\u0441\u0438\u044e change tracking;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c \u0435\u0451 \u0441 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u043d\u043e\u0439;<\/p>\n<\/li>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u0432\u0435\u0440\u0441\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0430\u0441\u044c, \u0447\u0438\u0442\u0430\u0435\u043c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 <code>CHANGETABLE<\/code>;<\/p>\n<\/li>\n<li>\n<p>\u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043a\u044d\u0448.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"cs\">public class SqlServerChangePoller(    IConfiguration config,    IOutputCacheStore outputCache,    ChangeTokenManager tokenManager) : BackgroundService{    private static readonly string[] tables = [\"Orders\"];    protected override async Task ExecuteAsync(CancellationToken stoppingToken)    {        long lastSyncVersion = long.MaxValue;        while (!stoppingToken.IsCancellationRequested)        {            await using var conn = new SqlConnection(config.GetConnectionString(\"SqlServer\"));            await conn.OpenAsync(stoppingToken);            try            {                lastSyncVersion = await PollChanges(conn, lastSyncVersion, stoppingToken);            }            catch (Exception ex) when (ex is not OperationCanceledException)            {                \/\/ \u043b\u043e\u0433\u0438\u0440\u0443\u0435\u043c \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c            }        }    }    private async Task&lt;long&gt; PollChanges(SqlConnection conn, long lastSyncVersion, CancellationToken stoppingToken)    {        while (!stoppingToken.IsCancellationRequested)        {            await using var currentVersionCmd = new SqlCommand(\"SELECT CHANGE_TRACKING_CURRENT_VERSION()\", conn);            var result = await currentVersionCmd.ExecuteScalarAsync(stoppingToken);            if (result is null || result is DBNull)            {                await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);                continue;            }            var currentVersion = (long)result;            if (currentVersion &lt;= lastSyncVersion)            {                lastSyncVersion = currentVersion;                await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken);                continue;            }            await ProcessChanges(conn, lastSyncVersion, stoppingToken);            lastSyncVersion = currentVersion;        }        return lastSyncVersion;    }    private async Task ProcessChanges(SqlConnection conn, long lastSyncVersion, CancellationToken stoppingToken)    {        foreach (var table in tables)        {            await using var changesCmd = new SqlCommand(                $\"SELECT * FROM CHANGETABLE(CHANGES {table}, @fromVersion) AS ct\",                conn);            changesCmd.Parameters.AddWithValue(\"@fromVersion\", lastSyncVersion);            await using var reader = await changesCmd.ExecuteReaderAsync(stoppingToken);            while (await reader.ReadAsync(stoppingToken))            {                var id = reader.GetInt32(\"Id\");                var key = $\"{table.ToLower()}:{id}\";                await outputCache.EvictByTagAsync(key, stoppingToken);                await tokenManager.OnChange(key);            }        }    }}<\/code><div class=\"code-explainer\"><a href=\"https:\/\/sourcecraft.dev\/\" class=\"tm-button code-explainer__link\" style=\"visibility: hidden;\"><img style=\"width:14px;height:14px;object-fit:cover;object-position:left;\"\/><\/a><\/div><\/pre>\n<p>\u041c\u043e\u0436\u043d\u043e \u043d\u0435 \u043e\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u0431\u0430\u0437\u0443 \u043a\u0430\u0436\u0434\u0443\u044e \u0441\u0435\u043a\u0443\u043d\u0434\u0443, \u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c SqlDependency \u0438\u043b\u0438 Query Notifications, \u043d\u043e \u044d\u0442\u043e \u0441\u0438\u043b\u044c\u043d\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u0442 \u043a\u043e\u0434. \u041e\u043f\u0440\u043e\u0441 \u0440\u0430\u0437 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443 \u0434\u043b\u044f \u043e\u0434\u043d\u043e\u0439 \u0442\u0430\u0431\u043b\u0438\u0446\u044b \u043e\u0431\u044b\u0447\u043d\u043e \u043d\u0435 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0437\u0430\u043c\u0435\u0442\u043d\u043e\u0439 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0438.<\/p>\n<hr\/>\n<h2>\u0427\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u0433\u0434\u0430 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e<\/h2>\n<p>\u0415\u0441\u043b\u0438 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0444\u043e\u043d\u043e\u0432\u0443\u044e \u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u0431\u0440\u043e\u0441\u0430 \u043a\u044d\u0448\u0430 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0445, \u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043f\u043b\u043e\u0445\u043e\u0433\u043e \u043d\u0435 \u0441\u043b\u0443\u0447\u0438\u0442\u0441\u044f. \u041e\u0431\u0430 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0430 \u2014 \u0438 \u0440\u0435\u043f\u043b\u0438\u043a\u0430\u0446\u0438\u044f \u0432 PostgreSQL, \u0438 \u043e\u043f\u0440\u043e\u0441 \u0432 SQL Server \u2014 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0434\u0430\u0436\u0435 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u0445. \u041d\u043e \u0435\u0441\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0431\u0430\u0437\u0443.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0432\u044b\u0433\u043e\u0434\u043d\u043e \u0432\u044b\u043d\u0435\u0441\u0442\u0438 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441, \u0430 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u0442\u044c\u0441\u044f \u043a \u043d\u0435\u043c\u0443 \u043f\u043e gRPC, SSE \u0438\u043b\u0438 \u0447\u0435\u0440\u0435\u0437 \u0434\u0440\u0443\u0433\u043e\u0439 \u0442\u0440\u0430\u043d\u0441\u043f\u043e\u0440\u0442. \u0422\u0430\u043a \u0432\u044b \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442\u0435 \u043e\u0434\u0438\u043d \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u0438\u043d\u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u0438 \u0438 \u043c\u043d\u043e\u0433\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u044e\u0442 \u0441\u0432\u043e\u0438 \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u044b\u0435 \u043a\u044d\u0448\u0438.<\/p>\n<p>\u041a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u043c\u043e\u0434\u0443\u043b\u044c\u043d\u044b\u043c, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u043e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u043e\u0442\u043b\u0430\u0434\u043a\u0438, \u0438 \u043b\u0435\u0433\u043a\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043a \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u043c \u0441\u0435\u0440\u0432\u0438\u0441\u0430\u043c <a href=\"https:\/\/jrg.su\/cybx7g\" rel=\"noopener noreferrer nofollow\">\u044f \u0431\u0443\u0434\u0443 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043d\u0430 \u043a\u043e\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u0438 DotNext 2026<\/a>. \u041e\u0431\u0440\u0430\u0449\u0430\u0439\u0442\u0435\u0441\u044c \u0432 \u043b\u0438\u0447\u043a\u0443 \u0437\u0430 \u043f\u0440\u043e\u043c\u043e\u043a\u043e\u0434\u043e\u043c \u043d\u0430 \u0441\u043a\u0438\u0434\u043a\u0443 \u0434\u043b\u044f \u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u043a\u043e\u043d\u0444\u0435\u0440\u0435\u043d\u0446\u0438\u044e.<\/p>\n<hr\/>\n<h2>\u0418\u0442\u043e\u0433\u0438<\/h2>\n<p>Redis \u0438\u043b\u0438 Valkey \u0445\u043e\u0440\u043e\u0448\u043e \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0441\u0435\u0431\u044f, \u043a\u043e\u0433\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u043a\u0430\u043a \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0449\u0435 \u0441\u043f\u0438\u0441\u043a\u043e\u0432, \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432, \u0441\u0442\u0440\u0438\u043c\u043e\u0432, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u043d\u044b\u0445 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440, \u0441\u0447\u0451\u0442\u0447\u0438\u043a\u043e\u0432, \u043e\u0447\u0435\u0440\u0435\u0434\u0435\u0439 \u0438 \u0434\u0440\u0443\u0433\u043e\u0433\u043e \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f.<\/p>\n<p>\u041d\u043e \u043a\u0430\u043a \u0433\u043e\u0440\u044f\u0447\u0438\u0439 \u043a\u044d\u0448 \u0434\u043b\u044f HTTP-\u043e\u0442\u0432\u0435\u0442\u043e\u0432 Redis \u0447\u0430\u0441\u0442\u043e \u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u044b\u043c: \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043f\u043b\u0430\u0442\u0438\u0442 \u0437\u0430 \u0441\u0435\u0442\u044c, \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 \u0431\u0430\u0439\u0442, \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 GC.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u0430\u043c \u043d\u0443\u0436\u043d\u0430 \u0432\u044b\u0441\u043e\u043a\u0430\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c, \u043b\u0443\u0447\u0448\u0435 \u0432\u044b\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e \u0443\u0440\u043e\u0432\u043d\u044f\u043c:<\/p>\n<ol>\n<li>\n<p>\u043a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u0438\u0439 \u043a\u044d\u0448 \u0438 <code>304 Not Modified<\/code>;<\/p>\n<\/li>\n<li>\n<p>Output Cache \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435;<\/p>\n<\/li>\n<li>\n<p>Memory Cache \u0434\u043b\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>\u0434\u0438\u0441\u043a \u0438\u043b\u0438 \u043f\u043e\u0442\u043e\u043a\u043e\u0432\u0430\u044f \u043e\u0442\u0434\u0430\u0447\u0430 \u0434\u043b\u044f \u0431\u043e\u043b\u044c\u0448\u0438\u0445 \u043e\u0431\u044a\u0451\u043c\u043e\u0432;<\/p>\n<\/li>\n<li>\n<p>Redis \u0442\u043e\u043b\u044c\u043a\u043e \u0442\u0430\u043c, \u0433\u0434\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043d\u0443\u0436\u043d\u044b \u043e\u0431\u0449\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u0418 \u043f\u043e\u043c\u043d\u0438\u0442\u0435: \u0441\u0430\u043c\u044b\u0439 \u0431\u044b\u0441\u0442\u0440\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u2014 \u0442\u043e\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c.<\/p>\n<p>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0432\u0441\u0435\u043c, \u043a\u0442\u043e \u0434\u043e\u0447\u0438\u0442\u0430\u043b \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0441\u0442\u0430. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0431\u044b\u043b\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e.<\/p>\n<p>\u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439 \u0441 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u043c \u043a\u043e\u0434\u043e\u043c: <a href=\"https:\/\/github.com\/gandjustas\/habr-cache-benchmarks\" rel=\"noopener noreferrer nofollow\">https:\/\/github.com\/gandjustas\/habr-cache-benchmarks<\/a>.<\/p>\n<\/div>\n<p>\u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/1068772\/\">https:\/\/habr.com\/ru\/articles\/1068772\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0418\u0441\u0442\u043e\u0440\u0438\u044f\u041e\u0434\u043d\u0430\u0436\u0434\u044b \u0432 \u043f\u0440\u043e\u0434\u0435 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u043d\u0430\u0447\u0430\u043b\u0438 \u043f\u0430\u0434\u0430\u0442\u044c \u043f\u043e OutOfMemory. \u041a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0430\u043c \u0431\u044b\u043b\u043e \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u043e \u043f\u043e 4 \u0413\u0411 \u043e\u043f\u0435\u0440\u0430\u0442\u0438\u0432\u043d\u043e\u0439 \u043f\u0430\u043c\u044f\u0442\u0438, \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0431\u044b\u043b\u0430 \u0432\u043f\u043e\u043b\u043d\u0435 \u043e\u0431\u044b\u0447\u043d\u043e\u0439: \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0441\u0442\u0440\u0430\u043d\u0438\u0446 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443.\u0420\u0430\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e, \u0447\u0442\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0432 JSON-\u043a\u043e\u043d\u0444\u0438\u0433\u0435.\u0421\u0445\u0435\u043c\u0430 \u0431\u044b\u043b\u0430 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0430\u044f. \u0412 \u0431\u0430\u0437\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u0445\u0440\u0430\u043d\u0438\u043b\u0441\u044f JSON. \u041f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u0441\u0442\u0430\u0432\u0430\u043b\u043e \u0435\u0433\u043e \u0438\u0437 \u0431\u0430\u0437\u044b, \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u043e \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u2014 \u0437\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c\u044b\u0445 \u043f\u043e\u043b\u0435\u0439, \u043e\u0431\u043e\u0433\u0430\u0449\u0435\u043d\u0438\u0435 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0430\u043c\u0438 \u2014 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0432 Redis. \u041f\u043e\u0434\u0430\u0432\u043b\u044f\u044e\u0449\u0435\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u043b\u043e \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 JSON \u0438 \u0445\u043e\u0434\u0438\u043b\u043e \u0437\u0430 \u043d\u0438\u043c \u0432 Redis.\u0424\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0443 \u043d\u0430\u0441 \u0431\u044b\u043b \u0441\u0435\u0440\u0432\u0435\u0440 Valkey, \u043d\u043e \u0432 \u043a\u043e\u0434\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0430\u0441\u044c \u043e\u0431\u044b\u0447\u043d\u0430\u044f Redis-\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430.\u0418\u0437-\u0437\u0430 \u043e\u0448\u0438\u0431\u043a\u0438 \u0432 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 JSON \u0440\u0430\u0441\u043f\u0443\u0445\u0430\u043b \u0434\u043e \u0434\u0435\u0441\u044f\u0442\u043a\u043e\u0432 \u043c\u0435\u0433\u0430\u0431\u0430\u0439\u0442. \u0412 \u043a\u044d\u0448 \u043f\u0438\u0441\u0430\u043b\u0438\u0441\u044c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0443\u0436\u043d\u044b\u0435 \u043f\u043e\u043b\u044f, \u043d\u043e \u0438 \u0432\u043b\u043e\u0436\u0435\u043d\u043d\u044b\u0435 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438, \u0438\u0441\u0442\u043e\u0440\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0441\u043f\u0440\u0430\u0432\u043e\u0447\u043d\u0438\u043a\u0438.\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u0435\u0441\u043b\u0438 \u043c\u044b \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u043c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 JSON, \u0440\u0430\u0437\u043c\u0435\u0440 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0442\u0430\u043a \u043a\u0440\u0438\u0442\u0438\u0447\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c.\u041d\u043e \u0432\u0441\u0451 \u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435.\u041a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u043a Redis \u043e\u0437\u043d\u0430\u0447\u0430\u043b:\u0421\u0435\u0442\u0435\u0432\u043e\u0439 round-trip \u0434\u043e \u0443\u0434\u0430\u043b\u0451\u043d\u043d\u043e\u0433\u043e \u0441\u0435\u0440\u0432\u0435\u0440\u0430\u041f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0435 10 MB \u043f\u043e TCP\u0410\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u044e 10 MB \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0432 managed heap\u0414\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0434\u0435\u0441\u044f\u0442\u043a\u0438 \u0442\u044b\u0441\u044f\u0447 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u043e\u0442\u0432\u0435\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0443 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0430\u0440\u0443 \u0441\u043e\u0442\u0435\u043d\u0442 \u043a\u0438\u043b\u043e\u0431\u0430\u0439\u0442\u0421\u0445\u0435\u043c\u0430\u0442\u0438\u0447\u043d\u043e \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u043e \u0442\u0430\u043a: \u0421\u0445\u0435\u043c\u0430 \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043a Redis\u0421\u0438\u0442\u0443\u0430\u0446\u0438\u044e \u0443\u0441\u0443\u0433\u0443\u0431\u043b\u044f\u043b\u043e \u0442\u043e, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u0430\u044f \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u0434\u0435\u0441\u044f\u0442\u043e\u043a \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043a \u0431\u044d\u043a\u0435\u043d\u0434\u0443, \u0438 \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u043a\u043e\u043d\u0444\u0438\u0433.\u041f\u0440\u0438 \u0441\u043e\u0442\u043d\u0435 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u0434\u0435\u043b\u044f\u043b\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e \u0433\u0438\u0433\u0430\u0431\u0430\u0439\u0442 \u043c\u0443\u0441\u043e\u0440\u0430 \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0443. \u041f\u0440\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0438 90% \u043e\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b\u0445 4 \u0413\u0411 \u0441\u0431\u043e\u0440\u0449\u0438\u043a \u043c\u0443\u0441\u043e\u0440\u0430 \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u043b\u0441\u044f \u0432 \u0430\u0433\u0440\u0435\u0441\u0441\u0438\u0432\u043d\u044b\u0439 \u0440\u0435\u0436\u0438\u043c, \u0432\u0440\u0435\u043c\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0440\u043e\u0441\u043b\u043e, \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0442\u043e\u043b\u044c\u043a\u043e \u0443\u0441\u0438\u043b\u0438\u0432\u0430\u043b\u0430\u0441\u044c.\u0424\u0438\u043a\u0441 \u0431\u0430\u0433\u0430\u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438, \u2014 \u043a\u043e\u043d\u0435\u0447\u043d\u043e, \u043f\u043e\u043f\u0440\u0430\u0432\u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0443, \u0438\u0437-\u0437\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 JSON \u0440\u0430\u0441\u043f\u0443\u0445\u0430\u043b. \u0420\u0430\u0437\u043c\u0435\u0440\u044b JSON \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u0438\u0441\u044c \u0432 10\u201320 \u0440\u0430\u0437.\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441\u0442\u0430\u043b\u0430 \u043c\u0435\u043d\u0435\u0435 \u00ab\u0433\u043e\u0440\u044f\u0447\u0435\u0439\u00bb, \u043d\u043e \u043d\u0435 \u0438\u0441\u0447\u0435\u0437\u043b\u0430 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e. \u041f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430 \u0441\u0442\u0430\u043b\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u044f\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u043d\u043e 2 \u0413\u0411 \u043f\u0440\u0438 \u0442\u043e\u0439 \u0436\u0435 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0435. \u041e\u0441\u043d\u043e\u0432\u043d\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043e\u0441\u0442\u0430\u043b\u0430\u0441\u044c \u0432 \u0430\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u044f\u0445 \u043f\u043e\u0434 \u043e\u0442\u0432\u0435\u0442 Redis \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441.\u0412\u0442\u043e\u0440\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438, \u2014 \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0433\u043e JSON \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u043d\u0430 \u043c\u0438\u043d\u0443\u0442\u0443. \u042d\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u043b\u043e \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0445\u043e\u0434\u0438\u0442\u044c \u0437\u0430 \u043d\u0438\u043c \u0432 \u0431\u0430\u0437\u0443 \u0438 \u0432 Redis \u0445\u043e\u0442\u044f \u0431\u044b \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b.\u0421\u0442\u0430\u043b\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435. \u041f\u0430\u043c\u044f\u0442\u0438 \u043f\u043e\u0434 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u043e\u0439 \u0441\u0442\u0430\u043b\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u043c\u0435\u043d\u044c\u0448\u0435.\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u044f \u0440\u0435\u0448\u0438\u043b \u0437\u0430\u043c\u0435\u0440\u0438\u0442\u044c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0436\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 Redis \u043d\u0435\u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u0438 \u0447\u0442\u043e \u0441 \u044d\u0442\u0438\u043c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c.\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442\u0418\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0431\u044b\u043b\u0430 \u0441\u043e\u0431\u0440\u0430\u043d\u0430 \u0447\u0435\u0440\u0435\u0437 .NET Aspire:PostgreSQL \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435;Valkey \u0432 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440\u0435;ASP.NET API.\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442 \u2014 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u0432 NBomber, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u0435\u043b\u0430\u0435\u0442 GET-\u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043a API \u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a \u0440\u0435\u0441\u0443\u0440\u0441 Aspire \u043f\u043e \u043a\u043d\u043e\u043f\u043a\u0435 \u0438\u0437 Dashboard.\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b:100 \u043a\u043e\u043d\u043a\u0443\u0440\u0435\u043d\u0442\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432;60 \u0441\u0435\u043a\u0443\u043d\u0434 \u043d\u0430 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439, \u0431\u0435\u0437 warmup, \u0447\u0442\u043e\u0431\u044b \u0432\u0441\u0435 \u0447\u0435\u0441\u0442\u043d\u043e \u0441\u0445\u043e\u0434\u0438\u043b\u0438 \u0432 \u0431\u0430\u0437\u0443 \u0445\u043e\u0442\u044f \u0431\u044b \u043e\u0434\u0438\u043d \u0440\u0430\u0437;1000 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u2014 \u0437\u0430\u043a\u0430\u0437\u043e\u0432 \u2014 \u0432 \u0411\u0414, \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u0440\u0430\u0432\u043d\u043e\u043c\u0435\u0440\u043d\u043e\u0435;\u043a\u0430\u0436\u0434\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u043c \u0432\u0438\u0434\u0435 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u0442 \u043e\u043a\u043e\u043b\u043e 500 \u0431\u0430\u0439\u0442, \u0442\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u043d\u044c\u0448\u0435 \u043f\u043e\u0440\u043e\u0433\u0430 LOH;\u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e.\u041a\u043e\u0434 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u044f:ScenarioProps BuildScenario(string name, string path){    return Scenario.Create(name, async context =&gt;    {        var id = orderIds[context.Random.Next(OrderCount)];        var request = Http.CreateRequest(&#171;GET&#187;, $&#187;{baseUrl}{path}\/{id}&#187;)            .WithHeader(&#171;Accept&#187;, &#171;application\/json&#187;);        return await Http.Send(httpClient, request);    })    .WithoutWarmUp()    .WithLoadSimulations(        Simulation.RampingConstant(ConcurrentCopies, TimeSpan.FromSeconds(10)),        Simulation.KeepConstant(ConcurrentCopies, TimeSpan.FromSeconds(50)),        Simulation.RampingConstant(0, TimeSpan.FromSeconds(3))    );}\u0414\u0435\u0441\u044f\u0442\u0438\u0441\u0435\u043a\u0443\u043d\u0434\u043d\u044b\u0439 \u0440\u0430\u0437\u0433\u043e\u043d \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u043d\u0443\u0436\u0435\u043d, \u0447\u0442\u043e\u0431\u044b \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0443\u044e \u0434\u0430\u0432\u043a\u0443 \u043d\u0430 \u043a\u044d\u0448 \u0438 \u043d\u0435 \u043d\u0430\u0440\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 ThreadPool starvation.\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0435\u0441\u0442\u0438\u0440\u0443\u0435\u043c\u041e\u0431\u0449\u0438\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044bvar builder = WebApplication.CreateBuilder(args);builder.AddServiceDefaults();builder.AddNpgsqlDbContext&lt;AppDbContext&gt;(&#171;ordersdb&#187;);builder.AddRedisDistributedCache(&#171;valkey&#187;);var cacheTtl = TimeSpan.FromMinutes(5);\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 1: \u043f\u0440\u044f\u043c\u043e\u0439 \u043f\u043e\u0445\u043e\u0434 \u0432 PostgreSQLapp.MapGet(&#171;\/api\/orders-direct\/{id:int}&#187;, async (    int id,    AppDbContext db,    CancellationToken cancellationToken) =&gt;{    var order = await db.Orders.FindAsync([id], cancellationToken);    return order is not null ? Results.Ok(order) : Results.NotFound();}).WithName(&#171;GetOrderDirect&#187;).WithTags(&#171;Orders&#187;);\u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 2: \u043a\u044d\u0448 \u0432 Valkey \u0447\u0435\u0440\u0435\u0437 IDistributedCacheapp.MapGet(&#171;\/api\/orders-redis\/{id:int}&#187;, async (    int id,    AppDbContext db,    IDistributedCache cache,    CancellationToken cancellationToken) =&gt;{    var cacheKey = $&#187;order:{id}&#187;;    var cached = await cache.GetAsync(cacheKey, cancellationToken);    if (cached is not null)    {        var fromCache = JsonSerializer.Deserialize&lt;Order&gt;(cached, JsonSerializerOptions.Web);        return Results.Ok(fromCache);    }    var order = await db.Orders.FindAsync([id], cancellationToken);    if (order is null)        return Results.NotFound();    cached = JsonSerializer.SerializeToUtf8Bytes(order, JsonSerializerOptions.Web);    await cache.SetAsync(        cacheKey,        cached,        new DistributedCacheEntryOptions        {            AbsoluteExpirationRelativeToNow = cacheTtl        },        cancellationToken);    return Results.Ok(order);}).WithName(&#171;GetOrderRedis&#187;).WithTags(&#171;Orders&#187;);\u0420\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043f\u0435\u0440\u0432\u043e\u0433\u043e \u0442\u0435\u0441\u0442\u0430\u041c\u0435\u0442\u0440\u0438\u043a\u0430orders-directorders-redisRPS10 08519 715Mean8.83 ms4.52 msP507.68 ms2.66 msP9516.74 ms16.86 msP9930.34 ms29.62 msMemory42 MB500\u2013700 MB\u041e\u0431\u044a\u0451\u043c \u043a\u044d\u0448\u0430 \u0432 Valkey \u2014 \u043e\u043a\u043e\u043b\u043e 2 \u041c\u0411.\u0412\u044b\u0432\u043e\u0434\u044b \u043f\u043e \u043f\u0435\u0440\u0432\u043e\u043c\u0443 \u0442\u0435\u0441\u0442\u0443\u0422\u0430\u043a\u0438\u0435 \u0442\u0430\u0439\u043c\u0438\u043d\u0433\u0438 \u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438 \u0433\u043e\u0432\u043e\u0440\u044f\u0442 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0434\u0430\u0432\u043a\u0430 \u043a\u044d\u0448\u0430. \u041a\u043e\u0433\u0434\u0430 \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043f\u043e \u043e\u0434\u043d\u043e\u043c\u0443 \u0438 \u0442\u043e\u043c\u0443 \u0436\u0435 \u043a\u043b\u044e\u0447\u0443 \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e, \u043a\u0430\u0436\u0434\u044b\u0439 \u0438\u0437 \u043d\u0438\u0445 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0439\u0442\u0438 \u0432 Redis, \u0437\u0430\u0442\u0435\u043c \u0432 \u0431\u0430\u0437\u0443, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u043f\u044b\u0442\u0430\u0442\u044c\u0441\u044f \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u0432 Redis.\u041f\u043e\u0441\u043b\u0435 \u043e\u043a\u043e\u043d\u0447\u0430\u043d\u0438\u044f \u0434\u0430\u0432\u043a\u0438 \u043e\u0431\u044a\u0451\u043c \u043f\u0430\u043c\u044f\u0442\u0438 \u0434\u0435\u0440\u0436\u0430\u043b\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 500 \u041c\u0411.\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432 Redis\/Valkey \u0441 \u043f\u043e\u0445\u043e\u0434\u043e\u043c \u0432 \u043a\u044d\u0448 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043d\u0438\u0436\u0430\u0435\u0442 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0411\u0414, \u043d\u043e \u043f\u043e\u0432\u044b\u0448\u0430\u0435\u0442 \u0440\u0430\u0441\u0445\u043e\u0434 \u043f\u0430\u043c\u044f\u0442\u0438 \u0438 \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 GC.\u0414\u0430\u0436\u0435 \u0431\u0435\u0437 \u0434\u0430\u0432\u043a\u0438 \u0432\u044b\u0434\u0435\u043b\u0435\u043d\u0438\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u0430 \u0431\u0430\u0439\u0442 \u043f\u043e\u0434 \u043e\u0442\u0432\u0435\u0442 Redis \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043d\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u043d\u0430 \u0441\u0431\u043e\u0440\u0449\u0438\u043a \u043c\u0443\u0441\u043e\u0440\u0430. \u0410 \u0435\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 85 000 \u0431\u0430\u0439\u0442, \u043c\u0430\u0441\u0441\u0438\u0432 \u043f\u043e\u043f\u0430\u0434\u0430\u0435\u0442 \u0432 LOH \u0438 \u043c\u043e\u0436\u0435\u0442 \u043e\u0441\u0442\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0442\u0430\u043c \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u043b\u0433\u043e.\u0418 \u044d\u0442\u043e \u043d\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u0438\u043b\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0412 \u043b\u044e\u0431\u043e\u043c \u044f\u0437\u044b\u043a\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0438\u0437 Redis, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0434\u0435\u043b\u0438\u0442\u044c \u0431\u0443\u0444\u0435\u0440 \u043f\u043e\u0434 \u044d\u0442\u043e\u0442 \u043e\u0442\u0432\u0435\u0442.\u041e\u0434\u0438\u043d \u0438\u0437 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432 \u0443\u043b\u0443\u0447\u0448\u0438\u0442\u044c \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044e \u2014 \u043d\u0435 \u0445\u043e\u0434\u0438\u0442\u044c \u0432 Redis \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441, \u0430 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0430.\u041a\u044d\u0448 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e ASP.NET Output CacheOutput Cache (ASP.NET Core 7+) \u043a\u044d\u0448\u0438\u0440\u0443\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0439 HTTP-\u043e\u0442\u0432\u0435\u0442 \u0446\u0435\u043b\u0438\u043a\u043e\u043c: \u0441\u0442\u0430\u0442\u0443\u0441-\u043a\u043e\u0434, \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438, \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u043e\u0435 \u0442\u0435\u043b\u043e. \u041f\u0440\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 middleware \u043e\u0442\u0434\u0430\u0451\u0442 \u0431\u0430\u0439\u0442\u044b \u0438\u0437 \u043a\u044d\u0448\u0430 \u2014 \u0431\u0435\u0437 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u0430, \u0431\u0435\u0437 \u0434\u0435\u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u0431\u0435\u0437 \u043b\u0438\u0448\u043d\u0438\u0445 \u0430\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u0439 \u0438 \u0431\u0435\u0437 \u0434\u0430\u0432\u043a\u0438.\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c Output Cache, \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441\u044b \u0438 middleware.builder.Services.AddOutputCache(options =&gt;    {        \/\/ \u041f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u043e    options.AddBasePolicy(build =&gt; build.NoCache());        options.AddPolicy(&#171;Orders5Min&#187;, policy =&gt; policy        .Expire(cacheTtl)        .Tag(&#171;orders&#187;));});var app = builder.Build();\/\/ \u041f\u043e\u0441\u043b\u0435 UseCors, UseRouting, UseAuthentication \u0438 UseAuthorization app.UseOutputCache(); \u042d\u043d\u0434\u043f\u043e\u0438\u043d\u0442 \u0441 Output Cache \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:app.MapGet(&#171;\/api\/orders-output\/{id:int}&#187;, async (    int id,    AppDbContext db,    CancellationToken cancellationToken) =&gt;{    \/\/ \u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 cache miss.    \/\/ \u041f\u0440\u0438 hit middleware \u043e\u0442\u0434\u0430\u0451\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0430\u0439\u0442\u044b \u0438 \u0441\u044e\u0434\u0430 \u043d\u0435 \u0437\u0430\u0445\u043e\u0434\u0438\u0442.    var order = await db.Orders.FindAsync([id], cancellationToken);    return order is not null ? Results.Ok(order) : Results.NotFound();}).WithName(&#171;GetOrderOutputCache&#187;).WithTags(&#171;Orders&#187;).CacheOutput(&#171;Orders5Min&#187;);\u041d\u0430\u0433\u0440\u0443\u0437\u043e\u0447\u043d\u044b\u0439 \u0442\u0435\u0441\u0442\u041c\u0435\u0442\u0440\u0438\u043a\u0430orders-directorders-redisorders-outputRPS3 71522 78034 526Mean33.92 ms4.20 ms2.71 msP507.68 ms3.11 ms1.64 msP9530.56 ms11.28 ms10.36 msP99305.66 ms21.54 ms21.07 msMemory42 MB500\u2013700 MB43 MBOutput Cache \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0438 \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043f\u043e\u0447\u0442\u0438 \u043d\u0435 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0432\u0430\u043b \u043f\u043e\u0442\u0440\u0435\u0431\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u043c\u044f\u0442\u0438. \u041a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043f\u043e \u0441\u0441\u044b\u043b\u043a\u0435.\u041c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0449\u0435 \u043b\u0443\u0447\u0448\u0435\u2026\u041a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435\u041d\u0430 \u0441\u043e\u0431\u0435\u0441\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f\u0445 \u044f \u0447\u0430\u0441\u0442\u043e \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u044e \u0443 \u043a\u0430\u043d\u0434\u0438\u0434\u0430\u0442\u043e\u0432, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 conditional get \u0438 \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435. \u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e 9 \u0438\u0437 10 \u043d\u0435 \u043c\u043e\u0433\u0443\u0442 \u043d\u043e\u0440\u043c\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u044d\u0442\u043e\u0442 \u0432\u043e\u043f\u0440\u043e\u0441.\u041a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 conditional get\u0421\u0435\u0440\u0432\u0435\u0440 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 ETag \u0438 Last-Modified, \u0430 \u0432 Cache-Control \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0434\u0430\u043d\u043d\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c.\u0415\u0441\u043b\u0438 Cache-Control \u043d\u0435 \u0443\u043a\u0430\u0437\u0430\u043d, \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u044b \u0438 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u043a\u044d\u0448\u0438 \u043c\u043e\u0433\u0443\u0442 \u0432\u0435\u0441\u0442\u0438 \u0441\u0435\u0431\u044f \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443. \u041d\u043e \u0432 \u043b\u043e\u043a\u043b\u0430\u044c\u043d\u044b\u0445 \u0442\u0435\u0441\u0442\u0430\u0445 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u043e\u0442\u0441\u0443\u0441\u0442\u0432\u0438\u0435 Cache-Control \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u043a\u0430\u043a no-cache. \u0414\u043b\u044f \u043f\u0440\u043e\u0434\u0443\u043a\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0434\u043b\u044f API \u043b\u0443\u0447\u0448\u0435 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u043a\u044d\u0448\u0430 \u044f\u0432\u043d\u043e.\u0412\u0430\u0436\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442: no-cache \u043d\u0435 \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442 \u00ab\u043d\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u00bb. \u041e\u043d \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442: \u00ab\u043c\u043e\u0436\u0435\u0448\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u0432 \u043a\u044d\u0448\u0435, \u043d\u043e \u043f\u0435\u0440\u0435\u0434 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c \u043e\u0431\u044f\u0437\u0430\u043d \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c\u00bb, \u0442\u043e \u0435\u0441\u0442\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c conditional get.\u041f\u0440\u0438 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a If-None-Match \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c ETag \u0438\u043b\u0438 If-Modified-Since \u0441\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c Last-Modified. \u0421\u0435\u0440\u0432\u0435\u0440 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0430\u043a\u0442\u0443\u0430\u043b\u0435\u043d \u043b\u0438 \u043a\u044d\u0448. \u0415\u0441\u043b\u0438 \u0430\u043a\u0442\u0443\u0430\u043b\u0435\u043d, \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 304 Not Modified \u0431\u0435\u0437 \u0442\u0435\u043b\u0430, \u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 \u0437\u0430\u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442.\u041f\u0440\u0438\u043c\u0435\u0440:&#8212; \u041f\u0435\u0440\u0432\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 &#8212;GET \/api\/orders\/42 HTTP\/1.1HTTP\/1.1 200 OKETag: &#171;a1b2c3d4&#8243;Last-Modified: Tue, 22 Jul 2026 10:30:00 GMTContent-Type: application\/json{&#171;id&#187;:&#187;42&#8243;,&#187;status&#187;:&#187;shipped&#187;,&#8230;}   \u2190 2.4 KB&#8212; \u041f\u043e\u0432\u0442\u043e\u0440\u043d\u044b\u0439 \u0437\u0430\u043f\u0440\u043e\u0441 (\u0434\u0430\u043d\u043d\u044b\u0435 \u043d\u0435 \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0438\u0441\u044c) &#8212;GET \/api\/orders\/42 HTTP\/1.1If-None-Match: &#171;a1b2c3d4&#8243;If-Modified-Since: Tue, 22 Jul 2026 10:30:00 GMTHTTP\/1.1 304 Not Modified                                    \u2190 0 \u0431\u0430\u0439\u0442 \u0442\u0435\u043b\u0430\u041a\u043b\u0438\u0435\u043d\u0442\u0441\u043a\u043e\u0435 \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 Output Cache\u0414\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0440\u043e\u043a, \u0447\u0442\u043e\u0431\u044b \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0433 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430. Output Cache \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442 \u0437\u0430\u0433\u043e\u043b\u043e\u0432\u043a\u0438 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 conditional GET.app.MapGet(&#171;\/api\/orders-output-client\/{id:int}&#187;, async (    int id,    AppDbContext db,    HttpContext context,    CancellationToken cancellationToken) =&gt;{    var order = await db.Orders.FindAsync([id], cancellationToken);    if (order is null)        return Results.NotFound();    context.Response.GetTypedHeaders().ETag = new($&#187;\\&#187;{order.Version}\\&#187;&#187;);    return&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-490421","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/490421","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=490421"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/490421\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=490421"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=490421"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=490421"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}