[{"data":1,"prerenderedAt":19402},["ShallowReactive",2],{"navigation_docs":3,"-aws-rds":3338,"-aws-rds-surround":19397},[4,1707,1904,2358,2539,2608,2815,2937,2987,3044,3078,3204,3281,3334],{"title":5,"icon":6,"path":7,"stem":8,"children":9},"C#","i-devicon-csharp","\u002Fcsharp","01.csharp",[10,13,60,90,120,202,219,253,379,404,457,650,1364,1654,1703],{"title":11,"path":7,"stem":12},"C# та .NET","01.csharp\u002Findex",{"title":14,"icon":15,"path":16,"stem":17,"children":18,"page":59},"Fundamentals","i-lucide-book-open","\u002Fcsharp\u002Ffundamentals","01.csharp\u002F01.fundamentals",[19,23,27,31,35,39,43,47,51,55],{"title":20,"path":21,"stem":22},"Вступ до екосистеми .NET","\u002Fcsharp\u002Ffundamentals\u002Fintroduction-to-ecosystem","01.csharp\u002F01.fundamentals\u002F01.introduction-to-ecosystem",{"title":24,"path":25,"stem":26},"Структура програми на C#","\u002Fcsharp\u002Ffundamentals\u002Fprogram-structure","01.csharp\u002F01.fundamentals\u002F02.program-structure",{"title":28,"path":29,"stem":30},"Змінні та Типи Даних","\u002Fcsharp\u002Ffundamentals\u002Fvariables-data-types","01.csharp\u002F01.fundamentals\u002F03.variables-data-types",{"title":32,"path":33,"stem":34},"Масиви","\u002Fcsharp\u002Ffundamentals\u002Farrays","01.csharp\u002F01.fundamentals\u002F04.arrays",{"title":36,"path":37,"stem":38},"Strings & Text Handling","\u002Fcsharp\u002Ffundamentals\u002Fstrings-text-handling","01.csharp\u002F01.fundamentals\u002F05.strings-text-handling",{"title":40,"path":41,"stem":42},"Дати і Час","\u002Fcsharp\u002Ffundamentals\u002Fdates-time-handling","01.csharp\u002F01.fundamentals\u002F06.dates-time-handling",{"title":44,"path":45,"stem":46},"Потік Керування","\u002Fcsharp\u002Ffundamentals\u002Fcontrol-flow","01.csharp\u002F01.fundamentals\u002F07.control-flow",{"title":48,"path":49,"stem":50},"Методи","\u002Fcsharp\u002Ffundamentals\u002Fmethods","01.csharp\u002F01.fundamentals\u002F08.methods",{"title":52,"path":53,"stem":54},"Основи Відлагодження","\u002Fcsharp\u002Ffundamentals\u002Fdebugging-basics","01.csharp\u002F01.fundamentals\u002F09.debugging-basics",{"title":56,"path":57,"stem":58},"Інтерактивна Консоль (Classic)","\u002Fcsharp\u002Ffundamentals\u002Finteractive-console","01.csharp\u002F01.fundamentals\u002F10.interactive-console",false,{"title":61,"icon":62,"path":63,"stem":64,"children":65,"page":59},"OOP","i-lucide-box","\u002Fcsharp\u002Foop","01.csharp\u002F02.oop",[66,70,74,78,82,86],{"title":67,"path":68,"stem":69},"Package Management (Управління Пакетами)","\u002Fcsharp\u002Foop\u002Fpackage-management","01.csharp\u002F02.oop\u002F01.package-management",{"title":71,"path":72,"stem":73},"Класи та Об'єкти","\u002Fcsharp\u002Foop\u002Fclasses-objects","01.csharp\u002F02.oop\u002F02.classes-objects",{"title":75,"path":76,"stem":77},"Властивості та Поля","\u002Fcsharp\u002Foop\u002Fproperties-fields","01.csharp\u002F02.oop\u002F03.properties-fields",{"title":79,"path":80,"stem":81},"Стовпи ООП","\u002Fcsharp\u002Foop\u002Foop-pillars","01.csharp\u002F02.oop\u002F04.oop-pillars",{"title":83,"path":84,"stem":85},"Advanced Types","\u002Fcsharp\u002Foop\u002Fadvanced-types","01.csharp\u002F02.oop\u002F05.advanced-types",{"title":87,"path":88,"stem":89},"Namespaces (Простори Імен)","\u002Fcsharp\u002Foop\u002Fnamespaces","01.csharp\u002F02.oop\u002F06.namespaces",{"title":91,"icon":92,"path":93,"stem":94,"children":95,"page":59},"Advanced Core","i-lucide-zap","\u002Fcsharp\u002Fadvanced-core","01.csharp\u002F03.advanced-core",[96,100,104,108,112,116],{"title":97,"path":98,"stem":99},"Generics (Узагальнення)","\u002Fcsharp\u002Fadvanced-core\u002Fgenerics","01.csharp\u002F03.advanced-core\u002F01.generics",{"title":101,"path":102,"stem":103},"Делегати, Події та Лямбда-вирази","\u002Fcsharp\u002Fadvanced-core\u002Fdelegates-events-lambdas","01.csharp\u002F03.advanced-core\u002F02.delegates-events-lambdas",{"title":105,"path":106,"stem":107},"Interfaces Deep Dive (Інтерфейси: Поглиблений Розгляд)","\u002Fcsharp\u002Fadvanced-core\u002Finterfaces-deep-dive","01.csharp\u002F03.advanced-core\u002F03.interfaces-deep-dive",{"title":109,"path":110,"stem":111},"Обробка Винятків","\u002Fcsharp\u002Fadvanced-core\u002Fexception-handling","01.csharp\u002F03.advanced-core\u002F04.exception-handling",{"title":113,"path":114,"stem":115},"Pattern Matching","\u002Fcsharp\u002Fadvanced-core\u002Fpattern-matching","01.csharp\u002F03.advanced-core\u002F05.pattern-matching",{"title":117,"path":118,"stem":119},"Додаткові Можливості C#","\u002Fcsharp\u002Fadvanced-core\u002Fadditional-features","01.csharp\u002F03.advanced-core\u002F06.additional-features",{"title":121,"icon":122,"path":123,"stem":124,"children":125,"page":59},"Architecture Best Practices","i-lucide-building-2","\u002Fcsharp\u002Farchitecture-best-practices","01.csharp\u002F04.architecture-best-practices",[126,130,149,153,157,161,165,169],{"title":127,"path":128,"stem":129},"Software Design Principles (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fsoftware-design-principles","01.csharp\u002F04.architecture-best-practices\u002F01.software-design-principles",{"title":131,"icon":132,"path":133,"stem":134,"children":135,"page":59},"Design Patterns","i-lucide-folder","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns",[136],{"title":137,"icon":132,"path":138,"stem":139,"children":140,"page":59},"Creational","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational",[141,145],{"title":142,"path":143,"stem":144},"Singleton (Одинак)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fsingleton","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F01.singleton",{"title":146,"path":147,"stem":148},"Builder (Будівельник)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdesign-patterns\u002Fcreational\u002Fbuilder","01.csharp\u002F04.architecture-best-practices\u002F02.design-patterns\u002Fcreational\u002F02.builder",{"title":150,"path":151,"stem":152},"Building Professional CLIs","\u002Fcsharp\u002Farchitecture-best-practices\u002Fbuilding-professional-clis","01.csharp\u002F04.architecture-best-practices\u002F03.building-professional-clis",{"title":154,"path":155,"stem":156},"Validation & Flow Control","\u002Fcsharp\u002Farchitecture-best-practices\u002Fvalidation-flow-control","01.csharp\u002F04.architecture-best-practices\u002F04.validation-flow-control",{"title":158,"path":159,"stem":160},"The Modern .NET Host (Microsoft.Extensions)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fmodern-dotnet-host","01.csharp\u002F04.architecture-best-practices\u002F05.modern-dotnet-host",{"title":162,"path":163,"stem":164},"Data Mapper: Repository та DAO патерни (Частина 1)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part1","01.csharp\u002F04.architecture-best-practices\u002F06.data-mapper-part1",{"title":166,"path":167,"stem":168},"Data Mapper: Repository та DAO патерни (Частина 2)","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdata-mapper-part2","01.csharp\u002F04.architecture-best-practices\u002F07.data-mapper-part2",{"title":170,"icon":132,"path":171,"stem":172,"children":173,"page":59},"Di Ioc","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc",[174,178,182,186,190,194,198],{"title":175,"path":176,"stem":177},"Проблема залежностей та Інверсія Контролю","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fthe-dependency-problem","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F01.the-dependency-problem",{"title":179,"path":180,"stem":181},"Будуємо власний Service Container","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fbuild-your-own-container","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F02.build-your-own-container",{"title":183,"path":184,"stem":185},"Service Locator: Паттерн та Анти-паттерн","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-locator-pattern","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F03.service-locator-pattern",{"title":187,"path":188,"stem":189},"Паттерни Dependency Injection","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdependency-injection-patterns","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F04.dependency-injection-patterns",{"title":191,"path":192,"stem":193},"Microsoft DI: IServiceCollection та IServiceProvider","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fmicrosoft-di-deep-dive","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F05.microsoft-di-deep-dive",{"title":195,"path":196,"stem":197},"Service Lifetimes та Scopes","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fservice-lifetimes-and-scopes","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F06.service-lifetimes-and-scopes",{"title":199,"path":200,"stem":201},"DI Анти-паттерни та Найкращі Практики","\u002Fcsharp\u002Farchitecture-best-practices\u002Fdi-ioc\u002Fdi-anti-patterns-and-best-practices","01.csharp\u002F04.architecture-best-practices\u002F08.di-ioc\u002F07.di-anti-patterns-and-best-practices",{"title":203,"icon":132,"path":204,"stem":205,"children":206,"page":59},"Standard Library","\u002Fcsharp\u002Fstandard-library","01.csharp\u002F05.standard-library",[207,211,215],{"title":208,"path":209,"stem":210},"Collections (Колекції)","\u002Fcsharp\u002Fstandard-library\u002Fcollections","01.csharp\u002F05.standard-library\u002F01.collections",{"title":212,"path":213,"stem":214},"High Performance Types (Високопродуктивні Типи)","\u002Fcsharp\u002Fstandard-library\u002Fhigh-performance-types","01.csharp\u002F05.standard-library\u002F02.high-performance-types",{"title":216,"path":217,"stem":218},"LINQ (Language Integrated Query)","\u002Fcsharp\u002Fstandard-library\u002Flinq","01.csharp\u002F05.standard-library\u002F03.linq",{"title":220,"icon":221,"path":222,"stem":223,"children":224,"page":59},"System Internals Concurrency","i-lucide-server","\u002Fcsharp\u002Fsystem-internals-concurrency","01.csharp\u002F06.system-internals-concurrency",[225,229,233,237,241,245,249],{"title":226,"path":227,"stem":228},"Memory Management","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmemory-management","01.csharp\u002F06.system-internals-concurrency\u002F01.memory-management",{"title":230,"path":231,"stem":232},"Reflection API: System.Type та Метадані","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Freflection-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F02.reflection-fundamentals",{"title":234,"path":235,"stem":236},"Attributes та Dynamic Language Runtime","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fattributes-dynamic","01.csharp\u002F06.system-internals-concurrency\u002F03.attributes-dynamic",{"title":238,"path":239,"stem":240},"Expression Trees: Швидка Альтернатива Рефлексії","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fexpression-trees-compiled","01.csharp\u002F06.system-internals-concurrency\u002F04.expression-trees-compiled",{"title":242,"path":243,"stem":244},"Source Generators: Compile-Time Code Generation","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsource-generators","01.csharp\u002F06.system-internals-concurrency\u002F05.source-generators",{"title":246,"path":247,"stem":248},"Multithreading Fundamentals","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fmultithreading-fundamentals","01.csharp\u002F06.system-internals-concurrency\u002F06.multithreading-fundamentals",{"title":250,"path":251,"stem":252},"Synchronization Primitives","\u002Fcsharp\u002Fsystem-internals-concurrency\u002Fsynchronization-primitives","01.csharp\u002F06.system-internals-concurrency\u002F07.synchronization-primitives",{"title":254,"icon":255,"path":256,"stem":257,"children":258,"page":59},"System Programming Windows","i-lucide-cpu","\u002Fcsharp\u002Fsystem-programming-windows","01.csharp\u002F07.system-programming-windows",[259,263,267,271,275,279,283,287,291,295,299,303,307,311,315,319,323,327,331,335,339,343,347,351,355,359,363,367,371,375],{"title":260,"path":261,"stem":262},"Як Працює Операційна Система","\u002Fcsharp\u002Fsystem-programming-windows\u002Fhow-os-works","01.csharp\u002F07.system-programming-windows\u002F01.how-os-works",{"title":264,"path":265,"stem":266},"Процеси в .NET — API та Запуск","\u002Fcsharp\u002Fsystem-programming-windows\u002Fprocesses-in-dotnet","01.csharp\u002F07.system-programming-windows\u002F02.processes-in-dotnet",{"title":268,"path":269,"stem":270},"Процеси в .NET — IPC та Міжпроцесна Комунікація","\u002Fcsharp\u002Fsystem-programming-windows\u002F02a.processes-ipc","01.csharp\u002F07.system-programming-windows\u002F02a.processes-ipc",{"title":272,"path":273,"stem":274},"Application Domains та Збірки — AppDomain і AssemblyLoadContext","\u002Fcsharp\u002Fsystem-programming-windows\u002Fappdomains-assemblies","01.csharp\u002F07.system-programming-windows\u002F03.appdomains-assemblies",{"title":276,"path":277,"stem":278},"Application Domains та Збірки — Plug-in Система з Hot-Reload","\u002Fcsharp\u002Fsystem-programming-windows\u002F03a.appdomains-plugin-system","01.csharp\u002F07.system-programming-windows\u002F03a.appdomains-plugin-system",{"title":280,"path":281,"stem":282},"Потоки — Основи та API Thread","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-fundamentals","01.csharp\u002F07.system-programming-windows\u002F04.thread-fundamentals",{"title":284,"path":285,"stem":286},"Потоки — Lifecycle, Пріоритети та Безпечне Завершення","\u002Fcsharp\u002Fsystem-programming-windows\u002F04a.thread-lifecycle-priorities","01.csharp\u002F07.system-programming-windows\u002F04a.thread-lifecycle-priorities",{"title":288,"path":289,"stem":290},"Проблеми Спільного Стану — Race Condition та Data Race","\u002Fcsharp\u002Fsystem-programming-windows\u002Fshared-state-problems","01.csharp\u002F07.system-programming-windows\u002F05.shared-state-problems",{"title":292,"path":293,"stem":294},"Проблеми Спільного Стану — Memory Model та volatile","\u002Fcsharp\u002Fsystem-programming-windows\u002F05a.shared-state-memory-model","01.csharp\u002F07.system-programming-windows\u002F05a.shared-state-memory-model",{"title":296,"path":297,"stem":298},"Синхронізація — Monitor, lock та еволюція примітивів","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-fundamentals","01.csharp\u002F07.system-programming-windows\u002F06.synchronization-fundamentals",{"title":300,"path":301,"stem":302},"Синхронізація — Наскрізний Приклад та Deadlock Detection","\u002Fcsharp\u002Fsystem-programming-windows\u002F06a.synchronization-walkthrough","01.csharp\u002F07.system-programming-windows\u002F06a.synchronization-walkthrough",{"title":304,"path":305,"stem":306},"Синхронізація — Mutex, Semaphore та Event-Based Primitives","\u002Fcsharp\u002Fsystem-programming-windows\u002Fsynchronization-advanced","01.csharp\u002F07.system-programming-windows\u002F07.synchronization-advanced",{"title":308,"path":309,"stem":310},"Синхронізація — Interlocked, Volatile та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002F07a.synchronization-advanced-walkthrough","01.csharp\u002F07.system-programming-windows\u002F07a.synchronization-advanced-walkthrough",{"title":312,"path":313,"stem":314},"Interlocked, CAS та Lock-Free Структури","\u002Fcsharp\u002Fsystem-programming-windows\u002Finterlocked-cas-lockfree","01.csharp\u002F07.system-programming-windows\u002F08.interlocked-cas-lockfree",{"title":316,"path":317,"stem":318},"Volatile, Memory Model та Spinning","\u002Fcsharp\u002Fsystem-programming-windows\u002F08a.volatile-memory-model","01.csharp\u002F07.system-programming-windows\u002F08a.volatile-memory-model",{"title":320,"path":321,"stem":322},"ThreadPool — Пул Потоків для Ефективного Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fthread-pool","01.csharp\u002F07.system-programming-windows\u002F09.thread-pool",{"title":324,"path":325,"stem":326},"ThreadPool — Просунуті Сценарії та Внутрішня Будова","\u002Fcsharp\u002Fsystem-programming-windows\u002F09a.thread-pool-advanced","01.csharp\u002F07.system-programming-windows\u002F09a.thread-pool-advanced",{"title":328,"path":329,"stem":330},"Concurrent та Immutable Collections","\u002Fcsharp\u002Fsystem-programming-windows\u002Fconcurrent-collections","01.csharp\u002F07.system-programming-windows\u002F10.concurrent-collections",{"title":332,"path":333,"stem":334},"TPL, Task та Композиція — Від Thread до Task","\u002Fcsharp\u002Fsystem-programming-windows\u002Ftpl-parallel-plinq","01.csharp\u002F07.system-programming-windows\u002F11.tpl-parallel-plinq",{"title":336,"path":337,"stem":338},"Parallel Class та PLINQ — Data Parallelism","\u002Fcsharp\u002Fsystem-programming-windows\u002F11a.tpl-parallel-plinq-advanced","01.csharp\u002F07.system-programming-windows\u002F11a.tpl-parallel-plinq-advanced",{"title":340,"path":341,"stem":342},"Async\u002FAwait — Фундамент Асинхронного Програмування","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-fundamentals","01.csharp\u002F07.system-programming-windows\u002F12.async-fundamentals",{"title":344,"path":345,"stem":346},"SynchronizationContext та ConfigureAwait — Контекст Виконання","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-context-configureawait","01.csharp\u002F07.system-programming-windows\u002F13.async-context-configureawait",{"title":348,"path":349,"stem":350},"Async — Просунуті Паттерни","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-advanced","01.csharp\u002F07.system-programming-windows\u002F14.async-advanced",{"title":352,"path":353,"stem":354},"System.Threading.Channels — Async Producer-Consumer","\u002Fcsharp\u002Fsystem-programming-windows\u002Fchannels","01.csharp\u002F07.system-programming-windows\u002F15.channels",{"title":356,"path":357,"stem":358},"Асинхронна Синхронізація","\u002Fcsharp\u002Fsystem-programming-windows\u002Fasync-synchronization","01.csharp\u002F07.system-programming-windows\u002F16.async-synchronization",{"title":360,"path":361,"stem":362},"Unsafe Code та Вказівники","\u002Fcsharp\u002Fsystem-programming-windows\u002Funsafe-code","01.csharp\u002F07.system-programming-windows\u002F17.unsafe-code",{"title":364,"path":365,"stem":366},"P\u002FInvoke та Windows API — Міст між .NET та Native Code","\u002Fcsharp\u002Fsystem-programming-windows\u002Fpinvoke-winapi","01.csharp\u002F07.system-programming-windows\u002F18.pinvoke-winapi",{"title":368,"path":369,"stem":370},"Реєстр Windows — Центральна База Конфігурації Системи","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-registry","01.csharp\u002F07.system-programming-windows\u002F19.windows-registry",{"title":372,"path":373,"stem":374},"Windows Hooks, Hotkeys та Services — Глибока Інтеграція з ОС","\u002Fcsharp\u002Fsystem-programming-windows\u002Fwindows-hooks-services","01.csharp\u002F07.system-programming-windows\u002F20.windows-hooks-services",{"title":376,"path":377,"stem":378},"Системне Програмування C# (Windows) — 07.system-programming-windows","\u002Fcsharp\u002Fsystem-programming-windows\u002Fimplementation_plan","01.csharp\u002F07.system-programming-windows\u002Fimplementation_plan",{"title":380,"icon":132,"path":381,"stem":382,"children":383,"page":59},"Io","\u002Fcsharp\u002Fio","01.csharp\u002F08.io",[384,388,392,396,400],{"title":385,"path":386,"stem":387},"8.1.1. Основи роботи з файловою системою","\u002Fcsharp\u002Fio\u002Ffile-system-basics","01.csharp\u002F08.io\u002F01.file-system-basics",{"title":389,"path":390,"stem":391},"8.1.2. Потоки (Streams) та Серіалізація Даних","\u002Fcsharp\u002Fio\u002Fstreams-serialization","01.csharp\u002F08.io\u002F02.streams-serialization",{"title":393,"path":394,"stem":395},"8.2.1. JSON Serialization з System.Text.Json","\u002Fcsharp\u002Fio\u002Fjson-serialization","01.csharp\u002F08.io\u002F03.json-serialization",{"title":397,"path":398,"stem":399},"8.2.2. XML Serialization та LINQ to XML","\u002Fcsharp\u002Fio\u002Fxml-serialization","01.csharp\u002F08.io\u002F04.xml-serialization",{"title":401,"path":402,"stem":403},"8.2.3. Binary Serialization: MessagePack та Protocol Buffers","\u002Fcsharp\u002Fio\u002Fbinary-serialization","01.csharp\u002F08.io\u002F05.binary-serialization",{"title":405,"icon":132,"path":406,"stem":407,"children":408,"page":59},"Ado Net","\u002Fcsharp\u002Fado-net","01.csharp\u002F09.ado-net",[409,413,417,421,425,429,433,437,441,445,449,453],{"title":410,"path":411,"stem":412},"9.1. Введення в ADO.NET","\u002Fcsharp\u002Fado-net\u002Fintroduction-to-adonet","01.csharp\u002F09.ado-net\u002F01.introduction-to-adonet",{"title":414,"path":415,"stem":416},"9.2. Клас DbConnection — з'єднання з базою даних","\u002Fcsharp\u002Fado-net\u002Fconnection","01.csharp\u002F09.ado-net\u002F02.connection",{"title":418,"path":419,"stem":420},"9.3. Клас DbCommand — виконання SQL-запитів","\u002Fcsharp\u002Fado-net\u002Fcommand-and-queries","01.csharp\u002F09.ado-net\u002F03.command-and-queries",{"title":422,"path":423,"stem":424},"9.4. Клас DbDataReader — ефективне читання даних","\u002Fcsharp\u002Fado-net\u002Fdatareader","01.csharp\u002F09.ado-net\u002F04.datareader",{"title":426,"path":427,"stem":428},"9.5. Параметризовані запити та захист від SQL Injection","\u002Fcsharp\u002Fado-net\u002Fparameters-and-sql-injection","01.csharp\u002F09.ado-net\u002F05.parameters-and-sql-injection",{"title":430,"path":431,"stem":432},"9.6. Транзакції в ADO.NET","\u002Fcsharp\u002Fado-net\u002Ftransactions","01.csharp\u002F09.ado-net\u002F06.transactions",{"title":434,"path":435,"stem":436},"9.7. DbProviderFactory — провайдер-незалежний код","\u002Fcsharp\u002Fado-net\u002Fprovider-factory","01.csharp\u002F09.ado-net\u002F07.provider-factory",{"title":438,"path":439,"stem":440},"9.8. Асинхронний доступ до даних","\u002Fcsharp\u002Fado-net\u002Fasync-data-access","01.csharp\u002F09.ado-net\u002F08.async-data-access",{"title":442,"path":443,"stem":444},"9.9. Від'єднаний режим: DataSet, DataTable, DataRow","\u002Fcsharp\u002Fado-net\u002Fdisconnected-mode-dataset","01.csharp\u002F09.ado-net\u002F09.disconnected-mode-dataset",{"title":446,"path":447,"stem":448},"9.10. DataAdapter — міст між DataSet та базою даних","\u002Fcsharp\u002Fado-net\u002Fdata-adapter","01.csharp\u002F09.ado-net\u002F10.data-adapter",{"title":450,"path":451,"stem":452},"9.11. Data Mapper та Repository: Архітектура доступу до даних","\u002Fcsharp\u002Fado-net\u002Fdata-mapper-repository","01.csharp\u002F09.ado-net\u002F11.data-mapper-repository",{"title":454,"path":455,"stem":456},"9.12. Identity Map, Unit of Work та Specification Pattern","\u002Fcsharp\u002Fado-net\u002Fadvanced-patterns","01.csharp\u002F09.ado-net\u002F12.advanced-patterns",{"title":458,"icon":255,"path":459,"stem":460,"children":461,"page":59},"Ef Core","\u002Fcsharp\u002Fef-core","01.csharp\u002F10.ef-core",[462,466,470,474,478,482,486,490,494,498,502,506,510,514,518,522,526,532,538,542,546,550,554,558,562,566,570,574,578,582,586,590,594,598,602,606,610,614,618,622,626,630,634,638,642,646],{"title":463,"path":464,"stem":465},"Що таке ORM? Від SQL до об'єктів","\u002Fcsharp\u002Fef-core\u002Fwhat-is-orm","01.csharp\u002F10.ef-core\u002F01.what-is-orm",{"title":467,"path":468,"stem":469},"Перший проєкт — від нуля до CRUD","\u002Fcsharp\u002Fef-core\u002Ffirst-project","01.csharp\u002F10.ef-core\u002F02.first-project",{"title":471,"path":472,"stem":473},"DbContext — Серце EF Core","\u002Fcsharp\u002Fef-core\u002Fdbcontext-deep-dive","01.csharp\u002F10.ef-core\u002F03.dbcontext-deep-dive",{"title":475,"path":476,"stem":477},"Провайдери баз даних — Архітектура та Вибір СУБД","\u002Fcsharp\u002Fef-core\u002Fdatabase-providers","01.csharp\u002F10.ef-core\u002F04.database-providers",{"title":479,"path":480,"stem":481},"Конвенції EF Core — Магія без конфігурації","\u002Fcsharp\u002Fef-core\u002Fconventions","01.csharp\u002F10.ef-core\u002F05.conventions",{"title":483,"path":484,"stem":485},"Fluent API та Data Annotations — Явна конфігурація моделі","\u002Fcsharp\u002Fef-core\u002Ffluent-api-vs-annotations","01.csharp\u002F10.ef-core\u002F06.fluent-api-vs-annotations",{"title":487,"path":488,"stem":489},"Зв'язки — One-to-One та One-to-Many","\u002Fcsharp\u002Fef-core\u002Frelationships-basics","01.csharp\u002F10.ef-core\u002F07.relationships-basics",{"title":491,"path":492,"stem":493},"Зв'язки Advanced — Many-to-Many та Складні Сценарії","\u002Fcsharp\u002Fef-core\u002Frelationships-advanced","01.csharp\u002F10.ef-core\u002F08.relationships-advanced",{"title":495,"path":496,"stem":497},"Властивості — Типи, Конвертери, Компаратори (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part1","01.csharp\u002F10.ef-core\u002F09.property-configuration-part1",{"title":499,"path":500,"stem":501},"Властивості — Value Comparers, Generators, Shadow Properties (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fproperty-configuration-part2","01.csharp\u002F10.ef-core\u002F09.property-configuration-part2",{"title":503,"path":504,"stem":505},"Складні типи — Owned Types та Complex Types (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part1","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part1",{"title":507,"path":508,"stem":509},"Складні типи — Complex Types, Keyless Entities, Порівняння (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fcomplex-types-owned-part2","01.csharp\u002F10.ef-core\u002F10.complex-types-owned-part2",{"title":511,"path":512,"stem":513},"JSON Columns — Складні дані у JSON (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part1","01.csharp\u002F10.ef-core\u002F11.json-columns-part1",{"title":515,"path":516,"stem":517},"JSON Columns — Value Comparers, Індекси, Провайдери (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fjson-columns-part2","01.csharp\u002F10.ef-core\u002F11.json-columns-part2",{"title":519,"path":520,"stem":521},"Успадкування — Абстрактні класи та TPH (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finheritance-part1","01.csharp\u002F10.ef-core\u002F12.inheritance-part1",{"title":523,"path":524,"stem":525},"Успадкування — TPT, TPC та Порівняння Стратегій (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finheritance-part2","01.csharp\u002F10.ef-core\u002F12.inheritance-part2",{"title":527,"path":528,"stem":529,"children":530},"Індекси, Обмеження та Схема (Частина 1)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part1","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part1",[531],{"title":527,"path":528,"stem":529},{"title":533,"path":534,"stem":535,"children":536},"Індекси, Обмеження та Схема (Частина 2)","\u002Fcsharp\u002Fef-core\u002Findexes-constraints-part2","01.csharp\u002F10.ef-core\u002F13.indexes-constraints-part2",[537],{"title":533,"path":534,"stem":535},{"title":539,"path":540,"stem":541},"Seed Data — Початкові Дані (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fseeding-part1","01.csharp\u002F10.ef-core\u002F14.seeding-part1",{"title":543,"path":544,"stem":545},"Seed Data — SQL-скрипти, Bogus та Стратегії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fseeding-part2","01.csharp\u002F10.ef-core\u002F14.seeding-part2",{"title":547,"path":548,"stem":549},"Global Query Filters — Глобальні Фільтри (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part1","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part1",{"title":551,"path":552,"stem":553},"Global Query Filters — Підводні камені та Інтеграція (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fglobal-query-filters-part2","01.csharp\u002F10.ef-core\u002F15.global-query-filters-part2",{"title":555,"path":556,"stem":557},"LINQ-запити в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part1","01.csharp\u002F10.ef-core\u002F16.linq-queries-part1",{"title":559,"path":560,"stem":561},"LINQ-запити в EF Core (Частина 2)","\u002Fcsharp\u002Fef-core\u002Flinq-queries-part2","01.csharp\u002F10.ef-core\u002F16.linq-queries-part2",{"title":563,"path":564,"stem":565},"Завантаження Пов'язаних Даних (Частина 1)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part1","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part1",{"title":567,"path":568,"stem":569},"Завантаження Пов'язаних Даних (Частина 2)","\u002Fcsharp\u002Fef-core\u002Floading-related-data-part2","01.csharp\u002F10.ef-core\u002F17.loading-related-data-part2",{"title":571,"path":572,"stem":573},"Raw SQL, Views та Stored Procedures (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part1","01.csharp\u002F10.ef-core\u002F18.raw-sql-part1",{"title":575,"path":576,"stem":577},"Raw SQL — Stored Procedures, DbFunction та Bulk Operations (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fraw-sql-part2","01.csharp\u002F10.ef-core\u002F18.raw-sql-part2",{"title":579,"path":580,"stem":581},"Продвинуті Запити — Compiled Queries, Bulk та Оптимізація (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part1","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part1",{"title":583,"path":584,"stem":585},"Продвинуті Запити — Query Tags, Bulk та Interceptors (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fadvanced-queries-part2","01.csharp\u002F10.ef-core\u002F19.advanced-queries-part2",{"title":587,"path":588,"stem":589},"Change Tracker — Відстеження Змін (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part1","01.csharp\u002F10.ef-core\u002F20.change-tracking-part1",{"title":591,"path":592,"stem":593},"Change Tracker — Графи Об'єктів та Disconnected (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fchange-tracking-part2","01.csharp\u002F10.ef-core\u002F20.change-tracking-part2",{"title":595,"path":596,"stem":597},"Збереження Даних та Транзакції (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part1","01.csharp\u002F10.ef-core\u002F21.saving-data-part1",{"title":599,"path":600,"stem":601},"Збереження Даних — Concurrency та Outbox (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fsaving-data-part2","01.csharp\u002F10.ef-core\u002F21.saving-data-part2",{"title":603,"path":604,"stem":605},"Конкурентність та Блокування (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part1","01.csharp\u002F10.ef-core\u002F22.concurrency-part1",{"title":607,"path":608,"stem":609},"Конкурентність — Дедлоки та Queue Processing (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fconcurrency-part2","01.csharp\u002F10.ef-core\u002F22.concurrency-part2",{"title":611,"path":612,"stem":613},"Міграції в EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part1","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part1",{"title":615,"path":616,"stem":617},"Міграції в EF Core — Основи (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-basics-part2","01.csharp\u002F10.ef-core\u002F23.migrations-basics-part2",{"title":619,"path":620,"stem":621},"Міграції — Просунуті Сценарії (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part1","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part1",{"title":623,"path":624,"stem":625},"Міграції — Просунуті Сценарії (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fmigrations-advanced-part2","01.csharp\u002F10.ef-core\u002F24.migrations-advanced-part2",{"title":627,"path":628,"stem":629},"Управління Схемою та Database-First (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part1","01.csharp\u002F10.ef-core\u002F25.schema-management-part1",{"title":631,"path":632,"stem":633},"Управління Схемою та Database-First (Частина 2)","\u002Fcsharp\u002Fef-core\u002Fschema-management-part2","01.csharp\u002F10.ef-core\u002F25.schema-management-part2",{"title":635,"path":636,"stem":637},"Продуктивність EF Core — Основи (Частина 1)","\u002Fcsharp\u002Fef-core\u002Fperformance-fundamentals-part1","01.csharp\u002F10.ef-core\u002F26.performance-fundamentals-part1",{"title":639,"path":640,"stem":641},"Interceptors в EF Core (Частина 1)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part1","01.csharp\u002F10.ef-core\u002F29.interceptors-part1",{"title":643,"path":644,"stem":645},"Interceptors в EF Core — Connection, Transaction та Materialization (Частина 2)","\u002Fcsharp\u002Fef-core\u002Finterceptors-part2","01.csharp\u002F10.ef-core\u002F29.interceptors-part2",{"title":647,"path":648,"stem":649},"План вивчення Entity Framework Core — Повний курс","\u002Fcsharp\u002Fef-core\u002Fimplementation_plan","01.csharp\u002F10.ef-core\u002Fimplementation_plan",{"title":651,"icon":652,"path":653,"stem":654,"children":655,"page":59},"ASP.NET","i-devicon-dotnetcore","\u002Fcsharp\u002Faspnet","01.csharp\u002F11.aspnet",[656,730,791,869,927,941,967,1057,1111,1182,1212,1289,1346],{"title":657,"icon":658,"path":659,"stem":660,"children":661,"page":59},"Minimal API","i-lucide-network","\u002Fcsharp\u002Faspnet\u002Fminimal-api","01.csharp\u002F11.aspnet\u002F01.minimal-api",[662,666,670,674,678,682,686,690,694,698,702,706,710,714,718,722,726],{"title":663,"path":664,"stem":665},"Вступ до ASP.NET та еволюція фреймворку","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fintroduction","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F01.introduction",{"title":667,"path":668,"stem":669},"Перший додаток на ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Ffirst-application","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F02.first-application",{"title":671,"path":672,"stem":673},"WebApplication, Builder та Dependency Injection","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fwebapplication-builder","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F03.webapplication-builder",{"title":675,"path":676,"stem":677},"Конвеєр запитів та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frequest-pipeline-middleware","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F04.request-pipeline-middleware",{"title":679,"path":680,"stem":681},"Маршрутизація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F05.routing-basics",{"title":683,"path":684,"stem":685},"Маршрутизація в ASP.NET Core: Розширені можливості","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Frouting-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F06.routing-advanced",{"title":687,"path":688,"stem":689},"Статичні файли в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-files","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F07.static-files",{"title":691,"path":692,"stem":693},"Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstatic-assets","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F08.static-assets",{"title":695,"path":696,"stem":697},"Конфігурація в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-fundamentals","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F09.configuration-fundamentals",{"title":699,"path":700,"stem":701},"Конфігурація: Паттерн Options","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fconfiguration-options","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F10.configuration-options",{"title":703,"path":704,"stem":705},"Логування в ASP.NET Core: Основи","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-basics","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F11.logging-basics",{"title":707,"path":708,"stem":709},"Логування: Serilog та Middleware","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Flogging-advanced","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F12.logging-advanced",{"title":711,"path":712,"stem":713},"Управління станом: HttpContext.Items та Cookies","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fstate-management","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F13.state-management",{"title":715,"path":716,"stem":717},"Стан сесії: Sessions","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fsession-state","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F14.session-state",{"title":719,"path":720,"stem":721},"Структура проєкту: від хаосу до архітектури","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fproject-structure","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F15.project-structure",{"title":723,"path":724,"stem":725},"Scalar у Minimal API: повний проєкт і Fluent OpenAPI","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fscalar-openapi-fluent","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F16.scalar-openapi-fluent",{"title":727,"path":728,"stem":729},"Swagger \u002F Swashbuckle у Minimal API: окремий класичний шлях","\u002Fcsharp\u002Faspnet\u002Fminimal-api\u002Fswagger-swashbuckle","01.csharp\u002F11.aspnet\u002F01.minimal-api\u002F17.swagger-swashbuckle",{"title":731,"icon":658,"path":732,"stem":733,"children":734,"page":59},"API","\u002Fcsharp\u002Faspnet\u002Fapi","01.csharp\u002F11.aspnet\u002F02.api",[735,739,743,747,751,755,759,763,767,771,775,779,783,787],{"title":736,"path":737,"stem":738},"Що таке API. Клієнт-серверна архітектура","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fwhat-is-api","01.csharp\u002F11.aspnet\u002F02.api\u002F01.what-is-api",{"title":740,"path":741,"stem":742},"Формати даних: JSON, XML, TOML та бінарні формати","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fdata-formats","01.csharp\u002F11.aspnet\u002F02.api\u002F02.data-formats",{"title":744,"path":745,"stem":746},"Парадигми API та концепція REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-paradigms-rest","01.csharp\u002F11.aspnet\u002F02.api\u002F03.api-paradigms-rest",{"title":748,"path":749,"stem":750},"HTTP-методи, статус-коди та заголовки","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fhttp-methods-status-codes","01.csharp\u002F11.aspnet\u002F02.api\u002F04.http-methods-status-codes",{"title":752,"path":753,"stem":754},"Організація HTTP API за принципами REST","\u002Fcsharp\u002Faspnet\u002Fapi\u002Frest-organizing","01.csharp\u002F11.aspnet\u002F02.api\u002F05.rest-organizing",{"title":756,"path":757,"stem":758},"Номенклатура URL та CRUD-операції","\u002Fcsharp\u002Faspnet\u002Fapi\u002Furl-nomenclature-crud","01.csharp\u002F11.aspnet\u002F02.api\u002F06.url-nomenclature-crud",{"title":760,"path":761,"stem":762},"Правила дизайну: іменування та стандарти","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-naming","01.csharp\u002F11.aspnet\u002F02.api\u002F07.api-design-naming",{"title":764,"path":765,"stem":766},"Валідація, ліміти та обробка помилок","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-validation","01.csharp\u002F11.aspnet\u002F02.api\u002F08.api-design-validation",{"title":768,"path":769,"stem":770},"Обробка помилок у Minimal API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Ferror-handling-http","01.csharp\u002F11.aspnet\u002F02.api\u002F09.error-handling-http",{"title":772,"path":773,"stem":774},"Ідемпотентність та синхронізація стану","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fidempotency-sync","01.csharp\u002F11.aspnet\u002F02.api\u002F10.idempotency-sync",{"title":776,"path":777,"stem":778},"Пагінація та організація списків","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fpagination-lists","01.csharp\u002F11.aspnet\u002F02.api\u002F11.pagination-lists",{"title":780,"path":781,"stem":782},"Безпека API, кешування та інтернаціоналізація","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fsecurity-auth","01.csharp\u002F11.aspnet\u002F02.api\u002F12.security-auth",{"title":784,"path":785,"stem":786},"Процес проєктування API та документування","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fapi-design-process","01.csharp\u002F11.aspnet\u002F02.api\u002F13.api-design-process",{"title":788,"path":789,"stem":790},"OpenAPI: контракт, специфікація та документація API","\u002Fcsharp\u002Faspnet\u002Fapi\u002Fopenapi","01.csharp\u002F11.aspnet\u002F02.api\u002F14.openapi",{"title":792,"icon":793,"path":794,"stem":795,"children":796,"page":59},"Auth","i-lucide-shield-check","\u002Fcsharp\u002Faspnet\u002Fauth","01.csharp\u002F11.aspnet\u002F03.auth",[797,801,805,809,813,817,821,825,829,833,837,841,845,849,853,857,861,865],{"title":798,"path":799,"stem":800},"Основи аутентифікації та авторизації","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauth-fundamentals","01.csharp\u002F11.aspnet\u002F03.auth\u002F01.auth-fundamentals",{"title":802,"path":803,"stem":804},"JWT-аутентифікація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fjwt-authentication","01.csharp\u002F11.aspnet\u002F03.auth\u002F02.jwt-authentication",{"title":806,"path":807,"stem":808},"Авторизація: ролі, політики та resource-based доступ","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fauthorization-policies","01.csharp\u002F11.aspnet\u002F03.auth\u002F03.authorization-policies",{"title":810,"path":811,"stem":812},"Cookie-аутентифікація та ASP.NET Core Identity","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcookie-auth-identity","01.csharp\u002F11.aspnet\u002F03.auth\u002F04.cookie-auth-identity",{"title":814,"path":815,"stem":816},"JWT + Refresh Tokens (HttpOnly Cookie)","\u002Fcsharp\u002Faspnet\u002Fauth\u002F04b.identity-auth-jwt","01.csharp\u002F11.aspnet\u002F03.auth\u002F04b.identity-auth-jwt",{"title":818,"path":819,"stem":820},"Identity: Підтвердження Email та Скидання Пароля","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-email-confirmation","01.csharp\u002F11.aspnet\u002F03.auth\u002F05.identity-email-confirmation",{"title":822,"path":823,"stem":824},"Identity: Двофакторна Аутентифікація (2FA)","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-two-factor","01.csharp\u002F11.aspnet\u002F03.auth\u002F06.identity-two-factor",{"title":826,"path":827,"stem":828},"Identity: Внутрішня Архітектура та Кастомізація","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fidentity-internals","01.csharp\u002F11.aspnet\u002F03.auth\u002F07.identity-internals",{"title":830,"path":831,"stem":832},"OAuth 2.0 та зовнішні провайдери","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-external-providers","01.csharp\u002F11.aspnet\u002F03.auth\u002F08.oauth-external-providers",{"title":834,"path":835,"stem":836},"Безпека на практиці: CORS, HTTPS та захист від атак","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fsecurity-hardening","01.csharp\u002F11.aspnet\u002F03.auth\u002F09.security-hardening",{"title":838,"path":839,"stem":840},"Теорія OAuth 2.0: Поняття, Аналогії та Флоу","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foauth-theory","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oauth-theory",{"title":842,"path":843,"stem":844},"OIDC, OAuth 2.0 та Keycloak в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Foidc-keycloak","01.csharp\u002F11.aspnet\u002F03.auth\u002F10.oidc-keycloak",{"title":846,"path":847,"stem":848},"API Keys аутентифікація в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fapi-keys","01.csharp\u002F11.aspnet\u002F03.auth\u002F11.api-keys",{"title":850,"path":851,"stem":852},"Rate Limiting та Throttling в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frate-limiting","01.csharp\u002F11.aspnet\u002F03.auth\u002F12.rate-limiting",{"title":854,"path":855,"stem":856},"Refresh Token Rotation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frefresh-token-rotation","01.csharp\u002F11.aspnet\u002F03.auth\u002F13.refresh-token-rotation",{"title":858,"path":859,"stem":860},"Certificate Authentication та mTLS в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fcertificate-auth","01.csharp\u002F11.aspnet\u002F03.auth\u002F14.certificate-auth",{"title":862,"path":863,"stem":864},"RBAC, ABAC та ReBAC в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Frbac-abac-rebac","01.csharp\u002F11.aspnet\u002F03.auth\u002F15.rbac-abac-rebac",{"title":866,"path":867,"stem":868},"Multi-tenancy та ізоляція даних в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fauth\u002Fmulti-tenancy","01.csharp\u002F11.aspnet\u002F03.auth\u002F16.multi-tenancy",{"title":870,"icon":871,"path":872,"stem":873,"children":874,"page":59},"Нотифікації","i-lucide-bell","\u002Fcsharp\u002Faspnet\u002Fnotifications","01.csharp\u002F11.aspnet\u002F04.notifications",[875,879,883,887,891,895,899,903,907,911,915,919,923],{"title":876,"path":877,"stem":878},"In-App нотифікації через базу даних","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fin-app-database-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F01.in-app-database-notifications",{"title":880,"path":881,"stem":882},"Polling: Регулярний запит оновлень","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fpolling","01.csharp\u002F11.aspnet\u002F04.notifications\u002F02.polling",{"title":884,"path":885,"stem":886},"Server-Sent Events: Однострімовий push від сервера","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fserver-sent-events","01.csharp\u002F11.aspnet\u002F04.notifications\u002F03.server-sent-events",{"title":888,"path":889,"stem":890},"WebSockets: Двостороннє з'єднання в реальному часі","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fwebsockets","01.csharp\u002F11.aspnet\u002F04.notifications\u002F04.websockets",{"title":892,"path":893,"stem":894},"SignalR: Абстракція над транспортами реального часу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fsignalr","01.csharp\u002F11.aspnet\u002F04.notifications\u002F05.signalr",{"title":896,"path":897,"stem":898},"Background Services: Фонові задачі в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fbackground-services","01.csharp\u002F11.aspnet\u002F04.notifications\u002F06.background-services",{"title":900,"path":901,"stem":902},"Web Push нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fweb-push","01.csharp\u002F11.aspnet\u002F04.notifications\u002F07.web-push",{"title":904,"path":905,"stem":906},"Email нотифікації","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Femail-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F08.email-notifications",{"title":908,"path":909,"stem":910},"Порівняння підходів: Як вибрати правильну технологію нотифікацій","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fchoosing-the-right-approach","01.csharp\u002F11.aspnet\u002F04.notifications\u002F09.choosing-the-right-approach",{"title":912,"path":913,"stem":914},"Hangfire: Надійне планування фонових задач","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire","01.csharp\u002F11.aspnet\u002F04.notifications\u002F10.hangfire",{"title":916,"path":917,"stem":918},"Практика: Конвертація зображень у WebP через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-image-webp","01.csharp\u002F11.aspnet\u002F04.notifications\u002F11.hangfire-image-webp",{"title":920,"path":921,"stem":922},"Практика: Підготовка відео до HLS-стрімінгу через Hangfire","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Fhangfire-video-hls","01.csharp\u002F11.aspnet\u002F04.notifications\u002F12.hangfire-video-hls",{"title":924,"path":925,"stem":926},"Telegram-нотифікації: від одного повідомлення до масових розсилок і мульти-канального підходу","\u002Fcsharp\u002Faspnet\u002Fnotifications\u002Ftelegram-notifications","01.csharp\u002F11.aspnet\u002F04.notifications\u002F13.telegram-notifications",{"title":928,"icon":929,"path":930,"stem":931,"children":932,"page":59},"Інтернаціоналізація","i-lucide-languages","\u002Fcsharp\u002Faspnet\u002Fi18n","01.csharp\u002F11.aspnet\u002F05.i18n",[933,937],{"title":934,"path":935,"stem":936},"Інтернаціоналізація (i18n) у Minimal API: від A до Я","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Finternationalization","01.csharp\u002F11.aspnet\u002F05.i18n\u002F01.internationalization",{"title":938,"path":939,"stem":940},"Humanizer: людиномовні рядки у .NET","\u002Fcsharp\u002Faspnet\u002Fi18n\u002Fhumanizer","01.csharp\u002F11.aspnet\u002F05.i18n\u002F02.humanizer",{"title":942,"icon":943,"path":944,"stem":945,"children":946,"page":59},"Кешування","i-lucide-layers","\u002Fcsharp\u002Faspnet\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching",[947,951,955,959,963],{"title":948,"path":949,"stem":950},"Огляд кешування: чотири рівні і коли що обирати","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fcaching","01.csharp\u002F11.aspnet\u002F06.caching\u002F01.caching",{"title":952,"path":953,"stem":954},"IMemoryCache: кеш в оперативній пам'яті","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fmemory-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F02.memory-cache",{"title":956,"path":957,"stem":958},"IDistributedCache і Redis: розподілений кеш","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fdistributed-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F03.distributed-cache",{"title":960,"path":961,"stem":962},"Response Cache: HTTP-кешування через Cache-Control","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Fresponse-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F04.response-cache",{"title":964,"path":965,"stem":966},"Output Cache: серверний кеш HTTP-відповідей (.NET 7+)","\u002Fcsharp\u002Faspnet\u002Fcaching\u002Foutput-cache","01.csharp\u002F11.aspnet\u002F06.caching\u002F05.output-cache",{"title":968,"icon":969,"path":970,"stem":971,"children":972,"page":59},"Тестування","i-lucide-test-tube","\u002Fcsharp\u002Faspnet\u002Ftesting","01.csharp\u002F11.aspnet\u002F07.testing",[973,977,981,985,989,993,997,1001,1005,1009,1013,1017,1021,1025,1029,1033,1037,1041,1045,1049,1053],{"title":974,"path":975,"stem":976},"Що таке тестування? Від інтуїції до науки","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-is-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F01.what-is-testing",{"title":978,"path":979,"stem":980},"Піраміда тестування — Стратегія, а не Догма","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-pyramid","01.csharp\u002F11.aspnet\u002F07.testing\u002F02.testing-pyramid",{"title":982,"path":983,"stem":984},"Дві Школи Тестування — Лондон проти Детройту","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-schools","01.csharp\u002F11.aspnet\u002F07.testing\u002F03.testing-schools",{"title":986,"path":987,"stem":988},"TDD та BDD — Тести як Дизайн-інструмент","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftdd-and-bdd","01.csharp\u002F11.aspnet\u002F07.testing\u002F04.tdd-and-bdd",{"title":990,"path":991,"stem":992},"Що саме тестувати — Техніки аналізу та Циклomatична складність","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwhat-to-test","01.csharp\u002F11.aspnet\u002F07.testing\u002F05.what-to-test",{"title":994,"path":995,"stem":996},"Тестові Фреймворки — Навіщо вони і що всередині","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftest-frameworks","01.csharp\u002F11.aspnet\u002F07.testing\u002F06.test-frameworks",{"title":998,"path":999,"stem":1000},"xUnit — Факти, Теорії та Lifecycle тестів","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-basics","01.csharp\u002F11.aspnet\u002F07.testing\u002F07.xunit-basics",{"title":1002,"path":1003,"stem":1004},"xUnit Advanced — Fixtures, Кастомізація та Розширення","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fxunit-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F08.xunit-advanced",{"title":1006,"path":1007,"stem":1008},"Moq — Глибоке занурення в мокування","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fmocking-with-moq","01.csharp\u002F11.aspnet\u002F07.testing\u002F09.mocking-with-moq",{"title":1010,"path":1011,"stem":1012},"Тестування Баз Даних — EF Core, SQLite та Testcontainers","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fdatabase-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F10.database-testing",{"title":1014,"path":1015,"stem":1016},"Integration Testing — Частина 1 [Теорія та WebApplicationFactory]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F11.integration-testing",{"title":1018,"path":1019,"stem":1020},"Інтеграційне тестування — Практика","\u002Fcsharp\u002Faspnet\u002Ftesting\u002F11a.integration-testing-practice","01.csharp\u002F11.aspnet\u002F07.testing\u002F11a.integration-testing-practice",{"title":1022,"path":1023,"stem":1024},"Integration Testing — Частина 2 [Просунуті Сценарії та Testcontainers]","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fintegration-testing-advanced","01.csharp\u002F11.aspnet\u002F07.testing\u002F12.integration-testing-advanced",{"title":1026,"path":1027,"stem":1028},"Професійний Postman: Колекції, Змінні та GitHub Інтеграція","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fpostman-professional","01.csharp\u002F11.aspnet\u002F07.testing\u002F13.postman-professional",{"title":1030,"path":1031,"stem":1032},"HttpClient у Тестах Частина 1: Архітектура та MockHttpMessageHandler","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fhttpclient-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F14.httpclient-testing",{"title":1034,"path":1035,"stem":1036},"HttpClient у Тестах Частина 2: WireMock.Net та Resilience","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fwiremock-net","01.csharp\u002F11.aspnet\u002F07.testing\u002F15.wiremock-net",{"title":1038,"path":1039,"stem":1040},"Патерни та Анти-патерни Тестування: Test Smells","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Ftesting-patterns","01.csharp\u002F11.aspnet\u002F07.testing\u002F16.testing-patterns",{"title":1042,"path":1043,"stem":1044},"Просунуті інструменти: Time, Snapshots та Властивості","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fadvanced-testing-tools","01.csharp\u002F11.aspnet\u002F07.testing\u002F17.advanced-testing-tools",{"title":1046,"path":1047,"stem":1048},"Тестування Архітектури з NetArchTest","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Farchitecture-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F18.architecture-testing",{"title":1050,"path":1051,"stem":1052},"Тестування Продуктивності: BenchmarkDotNet, NBomber та k6","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fperformance-testing","01.csharp\u002F11.aspnet\u002F07.testing\u002F19.performance-testing",{"title":1054,"path":1055,"stem":1056},"Залишок плану для курсу \"Тестування ASP.NET Minimal API\"","\u002Fcsharp\u002Faspnet\u002Ftesting\u002Fremaining_plan","01.csharp\u002F11.aspnet\u002F07.testing\u002Fremaining_plan",{"title":1058,"icon":1059,"path":1060,"stem":1061,"children":1062,"page":59},"Платежі","i-lucide-credit-card","\u002Fcsharp\u002Faspnet\u002Fpayments","01.csharp\u002F11.aspnet\u002F08.payments",[1063,1067,1071,1075,1079,1083,1087,1091,1095,1099,1103,1107],{"title":1064,"path":1065,"stem":1066},"Основи платіжної інфраструктури","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-fundamentals","01.csharp\u002F11.aspnet\u002F08.payments\u002F01.payment-fundamentals",{"title":1068,"path":1069,"stem":1070},"Методи оплати в Україні","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-methods-ukraine","01.csharp\u002F11.aspnet\u002F08.payments\u002F02.payment-methods-ukraine",{"title":1072,"path":1073,"stem":1074},"PCI DSS та безпека платежів","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpci-dss-security","01.csharp\u002F11.aspnet\u002F08.payments\u002F03.pci-dss-security",{"title":1076,"path":1077,"stem":1078},"Архітектура платіжної підсистеми","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fpayment-architecture","01.csharp\u002F11.aspnet\u002F08.payments\u002F04.payment-architecture",{"title":1080,"path":1081,"stem":1082},"Інтеграція LiqPay (ПриватБанк)","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fliqpay-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F05.liqpay-integration",{"title":1084,"path":1085,"stem":1086},"Інтеграція Monobank Acquiring API","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fmonobank-acquiring","01.csharp\u002F11.aspnet\u002F08.payments\u002F06.monobank-acquiring",{"title":1088,"path":1089,"stem":1090},"Інтеграція Stripe","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fstripe-integration","01.csharp\u002F11.aspnet\u002F08.payments\u002F07.stripe-integration",{"title":1092,"path":1093,"stem":1094},"Webhooks — глибоке занурення","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fwebhooks-deep-dive","01.csharp\u002F11.aspnet\u002F08.payments\u002F08.webhooks-deep-dive",{"title":1096,"path":1097,"stem":1098},"Підписки та рекурентні платежі","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fsubscriptions-recurring","01.csharp\u002F11.aspnet\u002F08.payments\u002F09.subscriptions-recurring",{"title":1100,"path":1101,"stem":1102},"Повернення коштів та диспути","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Frefunds-disputes","01.csharp\u002F11.aspnet\u002F08.payments\u002F10.refunds-disputes",{"title":1104,"path":1105,"stem":1106},"Тестування платіжних інтеграцій","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Ftesting-payments","01.csharp\u002F11.aspnet\u002F08.payments\u002F11.testing-payments",{"title":1108,"path":1109,"stem":1110},"Чекліст виходу в Production","\u002Fcsharp\u002Faspnet\u002Fpayments\u002Fproduction-checklist","01.csharp\u002F11.aspnet\u002F08.payments\u002F12.production-checklist",{"title":1112,"icon":1113,"items":1114,"path":1127,"stem":1128,"children":1129,"page":59},"Популярні бібліотеки","lucide:box",[1115,1116,1117,1118,1119,1120,1121,1122,1123,1124,1125,1126],"01.fluent-validation","02.mapster","03.erroror-result-pattern","04.serilog","05.mediatr","06.polly","07.health-checks","08.feature-management","09.fluent-email","10.quest-pdf","11.bogus","12.humanizer-guard","\u002Fcsharp\u002Faspnet\u002Flibraries","01.csharp\u002F11.aspnet\u002F09.libraries",[1130,1134,1138,1142,1146,1150,1154,1158,1162,1166,1170,1174,1178],{"title":1131,"path":1132,"stem":1133},"Валідація з FluentValidation в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-validation","01.csharp\u002F11.aspnet\u002F09.libraries\u002F01.fluent-validation",{"title":1135,"path":1136,"stem":1137},"Маппінг об","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmapster","01.csharp\u002F11.aspnet\u002F09.libraries\u002F02.mapster",{"title":1139,"path":1140,"stem":1141},"Обробка помилок з ErrorOr та Result Pattern в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ferroror-result-pattern","01.csharp\u002F11.aspnet\u002F09.libraries\u002F03.erroror-result-pattern",{"title":1143,"path":1144,"stem":1145},"Структуроване логування з Serilog в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fserilog","01.csharp\u002F11.aspnet\u002F09.libraries\u002F04.serilog",{"title":1147,"path":1148,"stem":1149},"CQRS та Mediator з MediatR в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fmediatr","01.csharp\u002F11.aspnet\u002F09.libraries\u002F05.mediatr",{"title":1151,"path":1152,"stem":1153},"Відмовостійкість з Polly в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fpolly","01.csharp\u002F11.aspnet\u002F09.libraries\u002F06.polly",{"title":1155,"path":1156,"stem":1157},"Health Checks в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F09.libraries\u002F07.health-checks",{"title":1159,"path":1160,"stem":1161},"Feature Management та Feature Flags в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffeature-management","01.csharp\u002F11.aspnet\u002F09.libraries\u002F08.feature-management",{"title":1163,"path":1164,"stem":1165},"Відправка Email з FluentEmail в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Ffluent-email","01.csharp\u002F11.aspnet\u002F09.libraries\u002F09.fluent-email",{"title":1167,"path":1168,"stem":1169},"Генерація PDF з QuestPDF в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fquest-pdf","01.csharp\u002F11.aspnet\u002F09.libraries\u002F10.quest-pdf",{"title":1171,"path":1172,"stem":1173},"Генерація тестових даних з Bogus в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fbogus","01.csharp\u002F11.aspnet\u002F09.libraries\u002F11.bogus",{"title":1175,"path":1176,"stem":1177},"Humanizer та Guard Clauses в ASP.NET Core","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fhumanizer-guard","01.csharp\u002F11.aspnet\u002F09.libraries\u002F12.humanizer-guard",{"title":1179,"path":1180,"stem":1181},"План модуля 10.libraries — Популярні бібліотеки ASP.NET","\u002Fcsharp\u002Faspnet\u002Flibraries\u002Fplan","01.csharp\u002F11.aspnet\u002F09.libraries\u002Fplan",{"title":1183,"icon":1184,"path":1185,"stem":1186,"children":1187,"page":59},"Razor Pages","i-lucide-layout-template","\u002Fcsharp\u002Faspnet\u002Frazor-pages","01.csharp\u002F11.aspnet\u002F10.razor-pages",[1188,1192,1196,1200,1204,1208],{"title":1189,"path":1190,"stem":1191},"Від Minimal API до Razor Pages: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ffrom-minimal-api","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F01.from-minimal-api",{"title":1193,"path":1194,"stem":1195},"PageModel: логіка сторінки Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fpage-model","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F02.page-model",{"title":1197,"path":1198,"stem":1199},"Razor синтаксис: шаблонізатор у .cshtml","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Frazor-syntax","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F03.razor-syntax",{"title":1201,"path":1202,"stem":1203},"Tag Helpers: типізований HTML","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Ftag-helpers","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F04.tag-helpers",{"title":1205,"path":1206,"stem":1207},"Форми і валідація: повний цикл обробки даних","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fforms-validation","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F05.forms-validation",{"title":1209,"path":1210,"stem":1211},"Практичний проєкт: TaskManager на Razor Pages","\u002Fcsharp\u002Faspnet\u002Frazor-pages\u002Fproject-task-manager","01.csharp\u002F11.aspnet\u002F10.razor-pages\u002F06.project-task-manager",{"title":1213,"path":1214,"stem":1215,"children":1216,"page":59},"ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc","01.csharp\u002F11.aspnet\u002F11.mvc",[1217,1221,1225,1229,1233,1237,1241,1245,1249,1253,1257,1261,1265,1269,1273,1277,1281,1285],{"title":1218,"path":1219,"stem":1220},"Патерн MVC: архітектура, що змінила веб","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-pattern","01.csharp\u002F11.aspnet\u002F11.mvc\u002F01.mvc-pattern",{"title":1222,"path":1223,"stem":1224},"Від Razor Pages до MVC: концептуальний перехід","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffrom-razor-pages","01.csharp\u002F11.aspnet\u002F11.mvc\u002F02.from-razor-pages",{"title":1226,"path":1227,"stem":1228},"Controllers та Actions: серце MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fcontrollers-actions","01.csharp\u002F11.aspnet\u002F11.mvc\u002F03.controllers-actions",{"title":1230,"path":1231,"stem":1232},"Маршрутизація в MVC: Convention vs Attribute Routing","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Frouting-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F04.routing-mvc",{"title":1234,"path":1235,"stem":1236},"Model Binding: від HTTP до C#","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmodel-binding","01.csharp\u002F11.aspnet\u002F11.mvc\u002F05.model-binding",{"title":1238,"path":1239,"stem":1240},"Views, ViewData, ViewBag, TempData і ViewModel","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fviews-viewdata-tempdata","01.csharp\u002F11.aspnet\u002F11.mvc\u002F06.views-viewdata-tempdata",{"title":1242,"path":1243,"stem":1244},"Filters: аспектно-орієнтоване програмування в MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffilters","01.csharp\u002F11.aspnet\u002F11.mvc\u002F07.filters",{"title":1246,"path":1247,"stem":1248},"Areas: структурування великих застосунків","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fareas","01.csharp\u002F11.aspnet\u002F11.mvc\u002F08.areas",{"title":1250,"path":1251,"stem":1252},"View Components: повторювані незалежні блоки UI","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fview-components","01.csharp\u002F11.aspnet\u002F11.mvc\u002F09.view-components",{"title":1254,"path":1255,"stem":1256},"Display та Editor Templates","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fdisplay-editor-templates","01.csharp\u002F11.aspnet\u002F11.mvc\u002F10.display-editor-templates",{"title":1258,"path":1259,"stem":1260},"Валідація: IValidatableObject та FluentValidation","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fvalidation-advanced","01.csharp\u002F11.aspnet\u002F11.mvc\u002F11.validation-advanced",{"title":1262,"path":1263,"stem":1264},"HTMX: інтерактивність через HTML-атрибути","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx","01.csharp\u002F11.aspnet\u002F11.mvc\u002F12.htmx",{"title":1266,"path":1267,"stem":1268},"HTMX у ASP.NET Core MVC: серверна інтеграція","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fajax-htmx-mvc","01.csharp\u002F11.aspnet\u002F11.mvc\u002F13.ajax-htmx-mvc",{"title":1270,"path":1271,"stem":1272},"Практичний проєкт: Каталог товарів з HTMX","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fhtmx-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F14.htmx-project",{"title":1274,"path":1275,"stem":1276},"Завантаження та обробка файлів","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Ffile-upload","01.csharp\u002F11.aspnet\u002F11.mvc\u002F15.file-upload",{"title":1278,"path":1279,"stem":1280},"Глобалізація та Локалізація MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fglobalization-localization","01.csharp\u002F11.aspnet\u002F11.mvc\u002F16.globalization-localization",{"title":1282,"path":1283,"stem":1284},"Підсумковий проєкт: Блог-платформа","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fmvc-project","01.csharp\u002F11.aspnet\u002F11.mvc\u002F17.mvc-project",{"title":1286,"path":1287,"stem":1288},"План курсу: ASP.NET Core MVC","\u002Fcsharp\u002Faspnet\u002Fmvc\u002Fplan","01.csharp\u002F11.aspnet\u002F11.mvc\u002Fplan",{"title":1290,"path":1291,"stem":1292,"children":1293,"page":59},"Web Api","\u002Fcsharp\u002Faspnet\u002Fweb-api","01.csharp\u002F11.aspnet\u002F12.web-api",[1294,1298,1302,1306,1310,1314,1318,1322,1326,1330,1334,1338,1342],{"title":1295,"path":1296,"stem":1297},"Від Minimal API до Controller-based API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffrom-minimal-api-to-controllers","01.csharp\u002F11.aspnet\u002F12.web-api\u002F01.from-minimal-api-to-controllers",{"title":1299,"path":1300,"stem":1301},"ControllerBase, ActionResult\u003CT> та Response Types","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontroller-base-actionresult","01.csharp\u002F11.aspnet\u002F12.web-api\u002F02.controller-base-actionresult",{"title":1303,"path":1304,"stem":1305},"Content Negotiation - JSON, XML та власні форматери","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fcontent-negotiation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F03.content-negotiation",{"title":1307,"path":1308,"stem":1309},"Версіонування API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-versioning","01.csharp\u002F11.aspnet\u002F12.web-api\u002F04.api-versioning",{"title":1311,"path":1312,"stem":1313},"ProblemDetails та структурована обробка помилок","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fproblemdetails-error-handling","01.csharp\u002F11.aspnet\u002F12.web-api\u002F05.problemdetails-error-handling",{"title":1315,"path":1316,"stem":1317},"Фільтри у Web API контексті","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Ffilters-for-api","01.csharp\u002F11.aspnet\u002F12.web-api\u002F06.filters-for-api",{"title":1319,"path":1320,"stem":1321},"Пагінація, фільтрація та сортування","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fpagination-filtering-sorting","01.csharp\u002F11.aspnet\u002F12.web-api\u002F07.pagination-filtering-sorting",{"title":1323,"path":1324,"stem":1325},"HATEOAS та Resource Expansion","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhateoas-resource-expansion","01.csharp\u002F11.aspnet\u002F12.web-api\u002F08.hateoas-resource-expansion",{"title":1327,"path":1328,"stem":1329},"Гібридна архітектура - Minimal API + Controllers","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fminimal-api-vs-controllers-hybrid","01.csharp\u002F11.aspnet\u002F12.web-api\u002F09.minimal-api-vs-controllers-hybrid",{"title":1331,"path":1332,"stem":1333},"Документація API - Swashbuckle, NSwag та генерація клієнтів","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fapi-documentation-generation","01.csharp\u002F11.aspnet\u002F12.web-api\u002F10.api-documentation-generation",{"title":1335,"path":1336,"stem":1337},"Health Checks та моніторинг API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fhealth-checks-monitoring","01.csharp\u002F11.aspnet\u002F12.web-api\u002F11.health-checks-monitoring",{"title":1339,"path":1340,"stem":1341},"Підсумковий проєкт - Production-Ready REST API","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fweb-api-project","01.csharp\u002F11.aspnet\u002F12.web-api\u002F12.web-api-project",{"title":1343,"path":1344,"stem":1345},"План курсу: ASP.NET Core Web API (Controllers)","\u002Fcsharp\u002Faspnet\u002Fweb-api\u002Fplan","01.csharp\u002F11.aspnet\u002F12.web-api\u002Fplan",{"title":1347,"icon":1348,"path":1349,"stem":1350,"children":1351,"page":59},"Моніторинг","i-lucide-activity","\u002Fcsharp\u002Faspnet\u002Fmonitoring","01.csharp\u002F11.aspnet\u002F13.monitoring",[1352,1356,1360],{"title":1353,"path":1354,"stem":1355},"Спостережуваність: від console.log до production-систем","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fobservability-intro","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F01.observability-intro",{"title":1357,"path":1358,"stem":1359},"Health Checks: перший рівень observability","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fhealth-checks","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F02.health-checks",{"title":1361,"path":1362,"stem":1363},"Вбудовані метрики .NET 10 та System.Diagnostics.Metrics","\u002Fcsharp\u002Faspnet\u002Fmonitoring\u002Fdotnet-metrics","01.csharp\u002F11.aspnet\u002F13.monitoring\u002F03.dotnet-metrics",{"title":1365,"icon":1366,"path":1367,"stem":1368,"children":1369,"page":59},"Desktop UI","i-lucide-app-window","\u002Fcsharp\u002Fdesktop-ui","01.csharp\u002F12.desktop-ui",[1370,1374,1378,1382,1386,1390,1394,1398,1402,1406,1410,1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474,1478,1482,1486,1490,1494,1498,1502,1506,1510,1514,1518,1522,1526,1530,1534,1538,1542,1546,1550,1554,1558,1562,1566,1570,1574,1578,1582,1586,1590,1594,1598,1602,1606,1610,1614,1618,1622,1626,1630,1634,1638,1642,1646,1650],{"title":1371,"path":1372,"stem":1373},"Що таке десктопна розробка?","\u002Fcsharp\u002Fdesktop-ui\u002Fwhat-is-desktop-dev","01.csharp\u002F12.desktop-ui\u002F01.what-is-desktop-dev",{"title":1375,"path":1376,"stem":1377},"Архітектура WPF — як влаштований графічний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-architecture","01.csharp\u002F12.desktop-ui\u002F02.wpf-architecture",{"title":1379,"path":1380,"stem":1381},"Перший WPF-проєкт — від нуля до вікна","\u002Fcsharp\u002Fdesktop-ui\u002Ffirst-wpf-app","01.csharp\u002F12.desktop-ui\u002F03.first-wpf-app",{"title":1383,"path":1384,"stem":1385},"Перший Avalonia-проєкт: WPF для всіх платформ","\u002Fcsharp\u002Fdesktop-ui\u002F03a.first-avalonia-app","01.csharp\u002F12.desktop-ui\u002F03a.first-avalonia-app",{"title":1387,"path":1388,"stem":1389},"XAML: декларативний інтерфейс","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-basics","01.csharp\u002F12.desktop-ui\u002F04.xaml-basics",{"title":1391,"path":1392,"stem":1393},"Fluent UI у WPF — сучасний дизайн Windows 11","\u002Fcsharp\u002Fdesktop-ui\u002F04a.wpf-fluent-ui","01.csharp\u002F12.desktop-ui\u002F04a.wpf-fluent-ui",{"title":1395,"path":1396,"stem":1397},"WPF UI — сучасна бібліотека Fluent контролів","\u002Fcsharp\u002Fdesktop-ui\u002F04b.wpf-ui-library","01.csharp\u002F12.desktop-ui\u002F04b.wpf-ui-library",{"title":1399,"path":1400,"stem":1401},"HandyControl — велика бібліотека UI контролів для WPF","\u002Fcsharp\u002Fdesktop-ui\u002F04c.handycontrol-library","01.csharp\u002F12.desktop-ui\u002F04c.handycontrol-library",{"title":1403,"path":1404,"stem":1405},"Простори імен та ресурси XAML","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-namespaces-resources","01.csharp\u002F12.desktop-ui\u002F05.xaml-namespaces-resources",{"title":1407,"path":1408,"stem":1409},"XAML в Avalonia: ключові відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F05a.avalonia-xaml-differences","01.csharp\u002F12.desktop-ui\u002F05a.avalonia-xaml-differences",{"title":1411,"path":1412,"stem":1413},"Розширення розмітки XAML (Markup Extensions)","\u002Fcsharp\u002Fdesktop-ui\u002Fxaml-markup-extensions","01.csharp\u002F12.desktop-ui\u002F06.xaml-markup-extensions",{"title":1415,"path":1416,"stem":1417},"Панелі Layout: StackPanel, WrapPanel, DockPanel","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part1","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part1",{"title":1419,"path":1420,"stem":1421},"Grid, Canvas, UniformGrid","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-panels-part2","01.csharp\u002F12.desktop-ui\u002F07.layout-panels-part2",{"title":1423,"path":1424,"stem":1425},"Просунуті техніки Layout","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-advanced","01.csharp\u002F12.desktop-ui\u002F08.layout-advanced",{"title":1427,"path":1428,"stem":1429},"Адаптивний Layout та найкращі практики","\u002Fcsharp\u002Fdesktop-ui\u002Flayout-responsive","01.csharp\u002F12.desktop-ui\u002F09.layout-responsive",{"title":1431,"path":1432,"stem":1433},"Layout в Avalonia: відмінності та нові можливості","\u002Fcsharp\u002Fdesktop-ui\u002F09a.layout-avalonia","01.csharp\u002F12.desktop-ui\u002F09a.layout-avalonia",{"title":1435,"path":1436,"stem":1437},"Button, Image, ProgressBar та інші базові контроли","\u002Fcsharp\u002Fdesktop-ui\u002Fbasic-controls","01.csharp\u002F12.desktop-ui\u002F10.basic-controls",{"title":1439,"path":1440,"stem":1441},"Контроли в Avalonia: відмінності від WPF","\u002Fcsharp\u002Fdesktop-ui\u002F10a.controls-avalonia","01.csharp\u002F12.desktop-ui\u002F10a.controls-avalonia",{"title":1443,"path":1444,"stem":1445},"Текстові контроли — TextBlock, TextBox, RichTextBox","\u002Fcsharp\u002Fdesktop-ui\u002Ftext-controls","01.csharp\u002F12.desktop-ui\u002F11.text-controls",{"title":1447,"path":1448,"stem":1449},"Контроли вибору — CheckBox, RadioButton, ComboBox, ListBox, DatePicker","\u002Fcsharp\u002Fdesktop-ui\u002Fselection-controls","01.csharp\u002F12.desktop-ui\u002F12.selection-controls",{"title":1451,"path":1452,"stem":1453},"Content Model — GroupBox, Expander, TabControl, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fcontent-controls","01.csharp\u002F12.desktop-ui\u002F13.content-controls",{"title":1455,"path":1456,"stem":1457},"UI\u002FUX принципи десктопних застосунків","\u002Fcsharp\u002Fdesktop-ui\u002F13a.ui-ux-principles","01.csharp\u002F12.desktop-ui\u002F13a.ui-ux-principles",{"title":1459,"path":1460,"stem":1461},"Dependency Properties — Концепція та Value Resolution","\u002Fcsharp\u002Fdesktop-ui\u002Fdependency-properties-part1","01.csharp\u002F12.desktop-ui\u002F14.dependency-properties-part1",{"title":1463,"path":1464,"stem":1465},"Avalonia Property System — StyledProperty та DirectProperty","\u002Fcsharp\u002Fdesktop-ui\u002F14a.avalonia-property-system","01.csharp\u002F12.desktop-ui\u002F14a.avalonia-property-system",{"title":1467,"path":1468,"stem":1469},"Attached Properties — Властивості без меж","\u002Fcsharp\u002Fdesktop-ui\u002Fattached-properties","01.csharp\u002F12.desktop-ui\u002F15.attached-properties",{"title":1471,"path":1472,"stem":1473},"Routed Events — Маршрутизація подій у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Frouted-events","01.csharp\u002F12.desktop-ui\u002F16.routed-events",{"title":1475,"path":1476,"stem":1477},"Data Binding — Від Code-Behind до Декларативності","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part1","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part1",{"title":1479,"path":1480,"stem":1481},"INotifyPropertyChanged — Живе оновлення UI","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-basics-part2","01.csharp\u002F12.desktop-ui\u002F17.data-binding-basics-part2",{"title":1483,"path":1484,"stem":1485},"Compiled Bindings в Avalonia — Безпека на етапі компіляції","\u002Fcsharp\u002Fdesktop-ui\u002F17a.avalonia-compiled-bindings","01.csharp\u002F12.desktop-ui\u002F17a.avalonia-compiled-bindings",{"title":1487,"path":1488,"stem":1489},"Просунутий Data Binding — ElementName, RelativeSource, MultiBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-binding-advanced","01.csharp\u002F12.desktop-ui\u002F18.data-binding-advanced",{"title":1491,"path":1492,"stem":1493},"Value Converters — Перетворення типів даних у Data Binding","\u002Fcsharp\u002Fdesktop-ui\u002Fvalue-converters","01.csharp\u002F12.desktop-ui\u002F19.value-converters",{"title":1495,"path":1496,"stem":1497},"Data Templates — Візуалізація об'єктів у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-templates","01.csharp\u002F12.desktop-ui\u002F20.data-templates",{"title":1499,"path":1500,"stem":1501},"Collections Binding Part 1 — ObservableCollection та ItemsControl","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part1","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part1",{"title":1503,"path":1504,"stem":1505},"Collections Binding Part 2 — ICollectionView, Filtering, Sorting та Virtualization","\u002Fcsharp\u002Fdesktop-ui\u002Fcollections-binding-part2","01.csharp\u002F12.desktop-ui\u002F21.collections-binding-part2",{"title":1507,"path":1508,"stem":1509},"MVVM Pattern — Від Spaghetti Code до архітектури","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-pattern","01.csharp\u002F12.desktop-ui\u002F22.mvvm-pattern",{"title":1511,"path":1512,"stem":1513},"ViewModel Implementation — Від BaseViewModel до валідації","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-implementation","01.csharp\u002F12.desktop-ui\u002F23.viewmodel-implementation",{"title":1515,"path":1516,"stem":1517},"Commands — Від event handlers до декларативних команд","\u002Fcsharp\u002Fdesktop-ui\u002Fcommands","01.csharp\u002F12.desktop-ui\u002F24.commands",{"title":1519,"path":1520,"stem":1521},"MVVM Toolkit — MVVM без boilerplate через Source Generators","\u002Fcsharp\u002Fdesktop-ui\u002Fmvvm-toolkit","01.csharp\u002F12.desktop-ui\u002F25.mvvm-toolkit",{"title":1523,"path":1524,"stem":1525},"Messenger Pattern — Комунікація між ViewModel без прямих посилань","\u002Fcsharp\u002Fdesktop-ui\u002Fmessenger-pattern","01.csharp\u002F12.desktop-ui\u002F26.messenger-pattern",{"title":1527,"path":1528,"stem":1529},"Стилі WPF — CSS для десктопу","\u002Fcsharp\u002Fdesktop-ui\u002Fstyles-basics","01.csharp\u002F12.desktop-ui\u002F27.styles-basics",{"title":1531,"path":1532,"stem":1533},"CSS-like стилі Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F27a.avalonia-css-styling","01.csharp\u002F12.desktop-ui\u002F27a.avalonia-css-styling",{"title":1535,"path":1536,"stem":1537},"Control Templates — Частина 1. Концепція та TemplateBinding","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part1","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part1",{"title":1539,"path":1540,"stem":1541},"Control Templates — Частина 2. Named Parts та ContentPresenter","\u002Fcsharp\u002Fdesktop-ui\u002Fcontrol-templates-part2","01.csharp\u002F12.desktop-ui\u002F28.control-templates-part2",{"title":1543,"path":1544,"stem":1545},"Control Themes в Avalonia — нова ера стилізації","\u002Fcsharp\u002Fdesktop-ui\u002F28a.avalonia-control-themes","01.csharp\u002F12.desktop-ui\u002F28a.avalonia-control-themes",{"title":1547,"path":1548,"stem":1549},"Triggers та Visual State Manager у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Ftriggers-visual-states","01.csharp\u002F12.desktop-ui\u002F29.triggers-visual-states",{"title":1551,"path":1552,"stem":1553},"Pseudo-classes в Avalonia — замість WPF Triggers","\u002Fcsharp\u002Fdesktop-ui\u002F29a.avalonia-pseudo-classes","01.csharp\u002F12.desktop-ui\u002F29a.avalonia-pseudo-classes",{"title":1555,"path":1556,"stem":1557},"Теми та ресурсні словники у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fresources-themes","01.csharp\u002F12.desktop-ui\u002F30.resources-themes",{"title":1559,"path":1560,"stem":1561},"Avalonia Themes — Fluent Design та система тематизації","\u002Fcsharp\u002Fdesktop-ui\u002F30a.avalonia-themes-fluent","01.csharp\u002F12.desktop-ui\u002F30a.avalonia-themes-fluent",{"title":1563,"path":1564,"stem":1565},"Контроли колекцій — глибоке занурення","\u002Fcsharp\u002Fdesktop-ui\u002Fcollection-controls","01.csharp\u002F12.desktop-ui\u002F31.collection-controls",{"title":1567,"path":1568,"stem":1569},"DataGrid — колонки та базове відображення","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part1","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part1",{"title":1571,"path":1572,"stem":1573},"DataGrid — сортування, фільтрація, редагування","\u002Fcsharp\u002Fdesktop-ui\u002Fdatagrid-part2","01.csharp\u002F12.desktop-ui\u002F32.datagrid-part2",{"title":1575,"path":1576,"stem":1577},"TreeView та GridView","\u002Fcsharp\u002Fdesktop-ui\u002Ftreeview-listview","01.csharp\u002F12.desktop-ui\u002F33.treeview-listview",{"title":1579,"path":1580,"stem":1581},"Меню, Toolbar, ContextMenu, StatusBar","\u002Fcsharp\u002Fdesktop-ui\u002Fmenus-toolbars","01.csharp\u002F12.desktop-ui\u002F34.menus-toolbars",{"title":1583,"path":1584,"stem":1585},"Навігація та керування вікнами. Частина 1: вікна та сторінки","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part1","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part1",{"title":1587,"path":1588,"stem":1589},"Навігація та керування вікнами. Частина 2: MVVM-навігація","\u002Fcsharp\u002Fdesktop-ui\u002Fnavigation-windows-part2","01.csharp\u002F12.desktop-ui\u002F35.navigation-windows-part2",{"title":1591,"path":1592,"stem":1593},"Avalonia — Навігація та діалоги","\u002Fcsharp\u002Fdesktop-ui\u002F35a.avalonia-navigation-dialogs","01.csharp\u002F12.desktop-ui\u002F35a.avalonia-navigation-dialogs",{"title":1595,"path":1596,"stem":1597},"Діалоги та File Pickers у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fdialogs-file-pickers","01.csharp\u002F12.desktop-ui\u002F36.dialogs-file-pickers",{"title":1599,"path":1600,"stem":1601},"UserControl: компонентний підхід у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fuser-controls","01.csharp\u002F12.desktop-ui\u002F37.user-controls",{"title":1603,"path":1604,"stem":1605},"Custom Controls: Lookless Controls у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fcustom-controls","01.csharp\u002F12.desktop-ui\u002F38.custom-controls",{"title":1607,"path":1608,"stem":1609},"Avalonia TemplatedControl — Lookless Controls","\u002Fcsharp\u002Fdesktop-ui\u002F38a.avalonia-templated-controls","01.csharp\u002F12.desktop-ui\u002F38a.avalonia-templated-controls",{"title":1611,"path":1612,"stem":1613},"Анімації у WPF: Storyboard та Easing Functions","\u002Fcsharp\u002Fdesktop-ui\u002Fanimations-transitions","01.csharp\u002F12.desktop-ui\u002F39.animations-transitions",{"title":1615,"path":1616,"stem":1617},"Анімації в Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002F39a.avalonia-animations","01.csharp\u002F12.desktop-ui\u002F39a.avalonia-animations",{"title":1619,"path":1620,"stem":1621},"2D Графіка та Мультимедіа у WPF","\u002Fcsharp\u002Fdesktop-ui\u002Fmedia-graphics","01.csharp\u002F12.desktop-ui\u002F40.media-graphics",{"title":1623,"path":1624,"stem":1625},"Dependency Injection у WPF та Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Fdi-integration","01.csharp\u002F12.desktop-ui\u002F41.di-integration",{"title":1627,"path":1628,"stem":1629},"SQLite та EF Core у десктопних додатках","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part1","01.csharp\u002F12.desktop-ui\u002F42.data-persistence-part1",{"title":1631,"path":1632,"stem":1633},"Repository Pattern та Unit of Work","\u002Fcsharp\u002Fdesktop-ui\u002Fdata-persistence-part2","01.csharp\u002F12.desktop-ui\u002F43.data-persistence-part2",{"title":1635,"path":1636,"stem":1637},"Тестування ViewModels","\u002Fcsharp\u002Fdesktop-ui\u002Fviewmodel-testing","01.csharp\u002F12.desktop-ui\u002F44.viewmodel-testing",{"title":1639,"path":1640,"stem":1641},"Avalonia Headless Testing — тестування UI без вікон","\u002Fcsharp\u002Fdesktop-ui\u002F44a.avalonia-headless-testing","01.csharp\u002F12.desktop-ui\u002F44a.avalonia-headless-testing",{"title":1643,"path":1644,"stem":1645},"Кросплатформна розробка з Avalonia","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-cross-platform","01.csharp\u002F12.desktop-ui\u002F45.avalonia-cross-platform",{"title":1647,"path":1648,"stem":1649},"Пакування та розгортання Avalonia додатків","\u002Fcsharp\u002Fdesktop-ui\u002Favalonia-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F46.avalonia-packaging-deployment",{"title":1651,"path":1652,"stem":1653},"Розгортання WPF застосунків","\u002Fcsharp\u002Fdesktop-ui\u002Fwpf-packaging-deployment","01.csharp\u002F12.desktop-ui\u002F47.wpf-packaging-deployment",{"title":1655,"icon":658,"path":1656,"stem":1657,"children":1658,"page":59},"Network Programming","\u002Fcsharp\u002Fnetwork-programming","01.csharp\u002F13.network-programming",[1659,1663,1667,1671,1675,1679,1683,1687,1691,1695,1699],{"title":1660,"path":1661,"stem":1662},"Основи комп'ютерних мереж","\u002Fcsharp\u002Fnetwork-programming\u002Ffoundations","01.csharp\u002F13.network-programming\u002F01.foundations",{"title":1664,"path":1665,"stem":1666},"Модель OSI та стек TCP\u002FIP","\u002Fcsharp\u002Fnetwork-programming\u002Fosi-model","01.csharp\u002F13.network-programming\u002F02.osi-model",{"title":1668,"path":1669,"stem":1670},"IP-протокол та адресація","\u002Fcsharp\u002Fnetwork-programming\u002Fip-addressing","01.csharp\u002F13.network-programming\u002F03.ip-addressing",{"title":1672,"path":1673,"stem":1674},"UDP — протокол без з'єднання","\u002Fcsharp\u002Fnetwork-programming\u002Fudp","01.csharp\u002F13.network-programming\u002F05.udp",{"title":1676,"path":1677,"stem":1678},"UDP Broadcast та Multicast","\u002Fcsharp\u002Fnetwork-programming\u002Fudp-broadcast-multicast","01.csharp\u002F13.network-programming\u002F06.udp-broadcast-multicast",{"title":1680,"path":1681,"stem":1682},"HTTP — протокол вебу","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-fundamentals","01.csharp\u002F13.network-programming\u002F07.http-fundamentals",{"title":1684,"path":1685,"stem":1686},"HttpListener — вбудований HTTP-сервер .NET","\u002Fcsharp\u002Fnetwork-programming\u002F07a.http-listener","01.csharp\u002F13.network-programming\u002F07a.http-listener",{"title":1688,"path":1689,"stem":1690},"HTTP Advanced — cookies, аутентифікація та HTTPS","\u002Fcsharp\u002Fnetwork-programming\u002Fhttp-advanced","01.csharp\u002F13.network-programming\u002F08.http-advanced",{"title":1692,"path":1693,"stem":1694},"SMTP та протоколи електронної пошти","\u002Fcsharp\u002Fnetwork-programming\u002Fsmtp","01.csharp\u002F13.network-programming\u002F09.smtp",{"title":1696,"path":1697,"stem":1698},"WebSocket — повнодуплексний протокол реального часу","\u002Fcsharp\u002Fnetwork-programming\u002Fwebsockets","01.csharp\u002F13.network-programming\u002F10.websockets",{"title":1700,"path":1701,"stem":1702},"TLS\u002FSSL — криптографічний захист мережевих з'єднань","\u002Fcsharp\u002Fnetwork-programming\u002Ftls-ssl","01.csharp\u002F13.network-programming\u002F11.tls-ssl",{"title":1704,"path":1705,"stem":1706},"C# & .NET: The Ultimate Roadmap","\u002Fcsharp\u002Froadmap","01.csharp\u002Froadmap",{"title":1708,"icon":1709,"path":1710,"stem":1711,"children":1712,"page":59},"C++","i-devicon-cplusplus","\u002Fcpp","02.cpp",[1713,1717,1721,1725,1729,1733,1737,1741,1745,1748,1752,1756,1760,1764,1768,1772,1776,1780,1784,1788,1792,1796,1800,1804,1808,1812,1816,1820,1824,1828,1832,1836,1840,1844,1848,1852,1856,1860,1864,1868,1872,1876,1880,1884,1888,1892,1896,1900],{"title":1714,"path":1715,"stem":1716},"Вступ у програмування та алгоритми","\u002Fcpp\u002Fintro-algorithms","02.cpp\u002F01.intro-algorithms",{"title":1718,"path":1719,"stem":1720},"Code Style: угоди про оформлення коду","\u002Fcpp\u002Fcode-style","02.cpp\u002F02.code-style",{"title":1722,"path":1723,"stem":1724},"Середовище розробки та перший проєкт","\u002Fcpp\u002Fide-setup","02.cpp\u002F03.ide-setup",{"title":1726,"path":1727,"stem":1728},"Вивід даних на екран","\u002Fcpp\u002Fdata-output","02.cpp\u002F04.data-output",{"title":1730,"path":1731,"stem":1732},"Типи даних, змінні та константи","\u002Fcpp\u002Fdata-types-variables","02.cpp\u002F05.data-types-variables",{"title":1734,"path":1735,"stem":1736},"Ввід даних з клавіатури","\u002Fcpp\u002Fdata-input","02.cpp\u002F06.data-input",{"title":1738,"path":1739,"stem":1740},"Оператори, перетворення типів та логічні операції","\u002Fcpp\u002Foperators-type-conversion","02.cpp\u002F07.operators-type-conversion",{"title":1742,"path":1743,"stem":1744},"Цикли","\u002Fcpp\u002Floops","02.cpp\u002F08.loops",{"title":32,"path":1746,"stem":1747},"\u002Fcpp\u002Farrays","02.cpp\u002F09.arrays",{"title":1749,"path":1750,"stem":1751},"Алгоритми сортування та аналіз складності","\u002Fcpp\u002Fsorting","02.cpp\u002F10.sorting",{"title":1753,"path":1754,"stem":1755},"Алгоритми пошуку","\u002Fcpp\u002Fsearching","02.cpp\u002F11.searching",{"title":1757,"path":1758,"stem":1759},"Функції: основи","\u002Fcpp\u002Ffunctions-basics","02.cpp\u002F12.functions-basics",{"title":1761,"path":1762,"stem":1763},"Функції: прототипи, область видимості та додаткові можливості","\u002Fcpp\u002Ffunctions-scope","02.cpp\u002F13.functions-scope",{"title":1765,"path":1766,"stem":1767},"Функції: перевантаження та шаблони","\u002Fcpp\u002Ffunctions-overloading-templates","02.cpp\u002F14.functions-overloading-templates",{"title":1769,"path":1770,"stem":1771},"Вказівники: основи","\u002Fcpp\u002Fpointers-basics","02.cpp\u002F15.pointers-basics",{"title":1773,"path":1774,"stem":1775},"Посилання (References)","\u002Fcpp\u002Freferences","02.cpp\u002F16.references",{"title":1777,"path":1778,"stem":1779},"Вказівники, const і масиви","\u002Fcpp\u002Fpointers-const-arrays","02.cpp\u002F17.pointers-const-arrays",{"title":1781,"path":1782,"stem":1783},"Адресна арифметика","\u002Fcpp\u002Fpointer-arithmetic","02.cpp\u002F18.pointer-arithmetic",{"title":1785,"path":1786,"stem":1787},"Динамічна пам'ять","\u002Fcpp\u002Fdynamic-memory","02.cpp\u002F19.dynamic-memory",{"title":1789,"path":1790,"stem":1791},"Вказівники типу void","\u002Fcpp\u002Fvoid-pointers","02.cpp\u002F20.void-pointers",{"title":1793,"path":1794,"stem":1795},"Вказівники на вказівники","\u002Fcpp\u002Fpointers-to-pointers","02.cpp\u002F21.pointers-to-pointers",{"title":1797,"path":1798,"stem":1799},"Оператор доступу до членів через вказівник (->)","\u002Fcpp\u002Fmember-access-operator","02.cpp\u002F22.member-access-operator",{"title":1801,"path":1802,"stem":1803},"Цикл for-each (Range-based for)","\u002Fcpp\u002Fforeach-loop","02.cpp\u002F23.foreach-loop",{"title":1805,"path":1806,"stem":1807},"Вказівники на функції","\u002Fcpp\u002Ffunction-pointers","02.cpp\u002F24.function-pointers",{"title":1809,"path":1810,"stem":1811},"Лямбда-вирази","\u002Fcpp\u002Flambdas","02.cpp\u002F25.lambdas",{"title":1813,"path":1814,"stem":1815},"Лямбда-захоплення","\u002Fcpp\u002Flambda-captures","02.cpp\u002F26.lambda-captures",{"title":1817,"path":1818,"stem":1819},"Еліпсис","\u002Fcpp\u002Fellipsis","02.cpp\u002F27.ellipsis",{"title":1821,"path":1822,"stem":1823},"Безпечні альтернативи еліпсису","\u002Fcpp\u002F27a.ellipsis","02.cpp\u002F27a.ellipsis",{"title":1825,"path":1826,"stem":1827},"Аргументи командного рядка","\u002Fcpp\u002Fcommand-line-arguments","02.cpp\u002F28.command-line-arguments",{"title":1829,"path":1830,"stem":1831},"Перерахування (enum)","\u002Fcpp\u002Fenum","02.cpp\u002F29.enum",{"title":1833,"path":1834,"stem":1835},"Класи-перерахування (enum class)","\u002Fcpp\u002Fenum-class","02.cpp\u002F30.enum-class",{"title":1837,"path":1838,"stem":1839},"Псевдоніми типів (typedef і using)","\u002Fcpp\u002Ftype-aliases","02.cpp\u002F31.type-aliases",{"title":1841,"path":1842,"stem":1843},"Системи числення та двійкова арифметика","\u002Fcpp\u002Fnumber-systems","02.cpp\u002F32.number-systems",{"title":1845,"path":1846,"stem":1847},"Структури (struct): агрегування даних","\u002Fcpp\u002Fstruct","02.cpp\u002F33.struct",{"title":1849,"path":1850,"stem":1851},"Структури у функціях","\u002Fcpp\u002Fstruct-functions","02.cpp\u002F34.struct-functions",{"title":1853,"path":1854,"stem":1855},"Масиви структур і вкладені структури","\u002Fcpp\u002Fstruct-arrays","02.cpp\u002F35.struct-arrays",{"title":1857,"path":1858,"stem":1859},"Патерни struct та межі застосування","\u002Fcpp\u002Fstruct-patterns","02.cpp\u002F36.struct-patterns",{"title":1861,"path":1862,"stem":1863},"Символи та таблиця ASCII","\u002Fcpp\u002Fascii-characters","02.cpp\u002F37.ascii-characters",{"title":1865,"path":1866,"stem":1867},"Unicode та кодування UTF","\u002Fcpp\u002Funicode-utf","02.cpp\u002F38.unicode-utf",{"title":1869,"path":1870,"stem":1871},"C-style рядки","\u002Fcpp\u002Fc-strings","02.cpp\u002F39.c-strings",{"title":1873,"path":1874,"stem":1875},"Вступ до std::string","\u002Fcpp\u002Fstd-string-intro","02.cpp\u002F40.std-string-intro",{"title":1877,"path":1878,"stem":1879},"Довжина, ємність та доступ до символів std::string","\u002Fcpp\u002Fstd-string-capacity-access","02.cpp\u002F41.std-string-capacity-access",{"title":1881,"path":1882,"stem":1883},"Модифікація std::string: присвоювання, додавання, вставка, видалення та заміна","\u002Fcpp\u002Fstd-string-modification","02.cpp\u002F42.std-string-modification",{"title":1885,"path":1886,"stem":1887},"Пошук у std::string: find, npos та практичні патерни","\u002Fcpp\u002Fstd-string-search","02.cpp\u002F43.std-string-search",{"title":1889,"path":1890,"stem":1891},"std::string_view: невласницький погляд на рядок без копіювання","\u002Fcpp\u002Fstd-string-view","02.cpp\u002F44.std-string-view",{"title":1893,"path":1894,"stem":1895},"Об'єднання (union): один блок пам'яті, кілька інтерпретацій","\u002Fcpp\u002Funion","02.cpp\u002F45.union",{"title":1897,"path":1898,"stem":1899},"Організація коду: файли, препроцесор, простори імен","\u002Fcpp\u002Fmultifile-programs","02.cpp\u002F46.multifile-programs",{"title":1901,"path":1902,"stem":1903},"План навчання: Курс C++ — Продовження (Статті 29–60+)","\u002Fcpp\u002Fcurriculum-plan","02.cpp\u002Fcurriculum-plan",{"title":1905,"icon":1906,"path":1907,"stem":1908,"children":1909,"page":59},"JavaScript","i-devicon-javascript","\u002Fjavascript","03.javascript",[1910,1936,1990,2012,2316,2354],{"title":1911,"icon":1912,"path":1913,"stem":1914,"children":1915,"page":59},"Events","i-lucide-mouse-pointer-click","\u002Fjavascript\u002Fevents","03.javascript\u002F01.events",[1916,1920,1924,1928,1932],{"title":1917,"path":1918,"stem":1919},"Вступ до подій браузера","\u002Fjavascript\u002Fevents\u002Fintro","03.javascript\u002F01.events\u002F01.intro",{"title":1921,"path":1922,"stem":1923},"Бульбашковий механізм (Bubbling) та занурення (Capturing)","\u002Fjavascript\u002Fevents\u002Fbubbling-capturing","03.javascript\u002F01.events\u002F02.bubbling-capturing",{"title":1925,"path":1926,"stem":1927},"Делегування подій (Event Delegation)","\u002Fjavascript\u002Fevents\u002Fdelegate-events","03.javascript\u002F01.events\u002F03.delegate-events",{"title":1929,"path":1930,"stem":1931},"Типові дії браузера та preventDefault()","\u002Fjavascript\u002Fevents\u002Fprevent-default","03.javascript\u002F01.events\u002F04.prevent-default",{"title":1933,"path":1934,"stem":1935},"Запуск користувацьких подій (Custom Events)","\u002Fjavascript\u002Fevents\u002Fcustom-events","03.javascript\u002F01.events\u002F05.custom-events",{"title":1937,"icon":1938,"path":1939,"stem":1940,"children":1941,"page":59},"Network","i-lucide-globe","\u002Fjavascript\u002Fnetwork","03.javascript\u002F02.network",[1942,1946,1950,1954,1958,1962,1966,1970,1974,1978,1982,1986],{"title":1943,"path":1944,"stem":1945},"Fetch API - Сучасний підхід до HTTP-запитів","\u002Fjavascript\u002Fnetwork\u002F01-fetch-api","03.javascript\u002F02.network\u002F01-fetch-api",{"title":1947,"path":1948,"stem":1949},"FormData - Робота з формами та файлами","\u002Fjavascript\u002Fnetwork\u002F02-formdata","03.javascript\u002F02.network\u002F02-formdata",{"title":1951,"path":1952,"stem":1953},"Відстеження прогресу завантаження","\u002Fjavascript\u002Fnetwork\u002F03-download-progress","03.javascript\u002F02.network\u002F03-download-progress",{"title":1955,"path":1956,"stem":1957},"Переривання fetch-запитів","\u002Fjavascript\u002Fnetwork\u002F04-abort-requests","03.javascript\u002F02.network\u002F04-abort-requests",{"title":1959,"path":1960,"stem":1961},"CORS - Запити між різними джерелами","\u002Fjavascript\u002Fnetwork\u002F05-cors","03.javascript\u002F02.network\u002F05-cors",{"title":1963,"path":1964,"stem":1965},"Fetch API - Повний довідник опцій","\u002Fjavascript\u002Fnetwork\u002F06-fetch-options","03.javascript\u002F02.network\u002F06-fetch-options",{"title":1967,"path":1968,"stem":1969},"URL Objects - Робота з посиланнями","\u002Fjavascript\u002Fnetwork\u002F07-url-objects","03.javascript\u002F02.network\u002F07-url-objects",{"title":1971,"path":1972,"stem":1973},"XMLHttpRequest - AJAX та низькорівневі запити","\u002Fjavascript\u002Fnetwork\u002F08-xmlhttprequest","03.javascript\u002F02.network\u002F08-xmlhttprequest",{"title":1975,"path":1976,"stem":1977},"Відновлюване завантаження файлів","\u002Fjavascript\u002Fnetwork\u002F09-resumable-upload","03.javascript\u002F02.network\u002F09-resumable-upload",{"title":1979,"path":1980,"stem":1981},"Cookies, document.cookie та світ після \"Cookiepocalypse\"","\u002Fjavascript\u002Fnetwork\u002F10-cookies","03.javascript\u002F02.network\u002F10-cookies",{"title":1983,"path":1984,"stem":1985},"js-cookie: Керування Cookies без Болю","\u002Fjavascript\u002Fnetwork\u002F11-js-cookie","03.javascript\u002F02.network\u002F11-js-cookie",{"title":1987,"path":1988,"stem":1989},"Axios: Потужний HTTP-клієнт для JavaScript","\u002Fjavascript\u002Fnetwork\u002F12-axios","03.javascript\u002F02.network\u002F12-axios",{"title":1991,"icon":1992,"path":1993,"stem":1994,"children":1995,"page":59},"Bom","i-lucide-monitor","\u002Fjavascript\u002Fbom","03.javascript\u002F03.bom",[1996,2000,2004,2008],{"title":1997,"path":1998,"stem":1999},"LocalStorage, SessionStorage та patterns збереження даних","\u002Fjavascript\u002Fbom\u002F01-localstorage","03.javascript\u002F03.bom\u002F01-localstorage",{"title":2001,"path":2002,"stem":2003},"Location Object - Керування адресою сторінки","\u002Fjavascript\u002Fbom\u002F02-location-object","03.javascript\u002F03.bom\u002F02-location-object",{"title":2005,"path":2006,"stem":2007},"History API - Керування історією браузера","\u002Fjavascript\u002Fbom\u002F03-history-api","03.javascript\u002F03.bom\u002F03-history-api",{"title":2009,"path":2010,"stem":2011},"Navigator Object - Ідентифікація та Можливості Пристрою","\u002Fjavascript\u002Fbom\u002F04-navigator-object","03.javascript\u002F03.bom\u002F04-navigator-object",{"title":2013,"icon":2014,"path":2015,"stem":2016,"children":2017},"React","i-devicon-react","\u002Fjavascript\u002Freact","03.javascript\u002F04.react\u002Findex",[2018,2019,2023,2027,2031,2035,2098,2133,2285],{"title":2013,"path":2015,"stem":2016},{"title":2020,"path":2021,"stem":2022},"Робота з Формами в React","\u002Fjavascript\u002Freact\u002Freact-forms","03.javascript\u002F04.react\u002F01.react-forms",{"title":2024,"path":2025,"stem":2026},"React Hook Form: Професійна Робота з Формами","\u002Fjavascript\u002Freact\u002Freact-hook-form","03.javascript\u002F04.react\u002F02.react-hook-form",{"title":2028,"path":2029,"stem":2030},"React Hook Form: Глибоке Розуміння Архітектури та Оптимізації","\u002Fjavascript\u002Freact\u002Freact-hook-form-new","03.javascript\u002F04.react\u002F02.react-hook-form-new",{"title":2032,"path":2033,"stem":2034},"Axios та React: Професійна Архітектура Запитів","\u002Fjavascript\u002Freact\u002Fdata-fetching-axios","03.javascript\u002F04.react\u002F03.data-fetching-axios",{"title":2036,"icon":132,"path":2037,"stem":2038,"children":2039},"Tanstack Query","\u002Fjavascript\u002Freact\u002Ftanstack-query","03.javascript\u002F04.react\u002F04.tanstack-query\u002Findex",[2040,2042,2046,2050,2054,2058,2062,2066,2070,2074,2078,2082,2086,2090,2094],{"title":2041,"path":2037,"stem":2038},"TanStack Query: Майстерність Керування Станом Сервера",{"title":2043,"path":2044,"stem":2045},"Парадигма Server State: Чому useEffect недостатньо","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-state-paradigm","03.javascript\u002F04.react\u002F04.tanstack-query\u002F01.server-state-paradigm",{"title":2047,"path":2048,"stem":2049},"Встановлення та Налаштування: Фундамент","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Finstallation-and-devtools","03.javascript\u002F04.react\u002F04.tanstack-query\u002F02.installation-and-devtools",{"title":2051,"path":2052,"stem":2053},"Основи Запитів та Магія Ключів","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fquery-basics-and-keys","03.javascript\u002F04.react\u002F04.tanstack-query\u002F03.query-basics-and-keys",{"title":2055,"path":2056,"stem":2057},"Синхронізація Даних: Життєвий Цикл Запиту","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fdata-synchronization","03.javascript\u002F04.react\u002F04.tanstack-query\u002F04.data-synchronization",{"title":2059,"path":2060,"stem":2061},"Мутації та Інвалідація: Зміна Даних","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fmutations-and-invalidation","03.javascript\u002F04.react\u002F04.tanstack-query\u002F05.mutations-and-invalidation",{"title":2063,"path":2064,"stem":2065},"Оптимістичні Оновлення: Швидше за Світло","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Foptimistic-updates","03.javascript\u002F04.react\u002F04.tanstack-query\u002F06.optimistic-updates",{"title":2067,"path":2068,"stem":2069},"Пагінація та Infinite Scroll","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fpagination-and-load-more","03.javascript\u002F04.react\u002F04.tanstack-query\u002F07.pagination-and-load-more",{"title":2071,"path":2072,"stem":2073},"Просунуті Патерни та Оптимізація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F04.tanstack-query\u002F08.advanced-patterns",{"title":2075,"path":2076,"stem":2077},"Архітектура та Best Practices","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Farchitecture-and-best-practices","03.javascript\u002F04.react\u002F04.tanstack-query\u002F09.architecture-and-best-practices",{"title":2079,"path":2080,"stem":2081},"Server-Side Rendering (SSR) та Гідратація","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fserver-side-rendering","03.javascript\u002F04.react\u002F04.tanstack-query\u002F10.server-side-rendering",{"title":2083,"path":2084,"stem":2085},"Стратегії Тестування","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Ftesting-strategies","03.javascript\u002F04.react\u002F04.tanstack-query\u002F11.testing-strategies",{"title":2087,"path":2088,"stem":2089},"Аутентифікація та Обробка Помилок","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fauthentication-and-errors","03.javascript\u002F04.react\u002F04.tanstack-query\u002F12.authentication-and-errors",{"title":2091,"path":2092,"stem":2093},"React Suspense та Майбутнє","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Freact-suspense","03.javascript\u002F04.react\u002F04.tanstack-query\u002F13.react-suspense",{"title":2095,"path":2096,"stem":2097},"Глибоке Занурення в Продуктивність","\u002Fjavascript\u002Freact\u002Ftanstack-query\u002Fperformance-deep-dive","03.javascript\u002F04.react\u002F04.tanstack-query\u002F14.performance-deep-dive",{"title":2099,"icon":2014,"path":2100,"stem":2101,"children":2102},"React Router","\u002Fjavascript\u002Freact\u002Freact-router","03.javascript\u002F04.react\u002F05.react-router\u002Findex",[2103,2105,2109,2113,2117,2121,2125,2129],{"title":2104,"path":2100,"stem":2101},"React Router: Навігаційна система сучасного вебу",{"title":2106,"path":2107,"stem":2108},"Налаштування та Базовий Роутинг","\u002Fjavascript\u002Freact\u002Freact-router\u002Fsetup-and-basic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F01.setup-and-basic-routing",{"title":2110,"path":2111,"stem":2112},"Динамічна Навігація","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnavigation-and-links","03.javascript\u002F04.react\u002F05.react-router\u002F02.navigation-and-links",{"title":2114,"path":2115,"stem":2116},"Вкладені Маршрути та Макети","\u002Fjavascript\u002Freact\u002Freact-router\u002Fnested-routes-and-layouts","03.javascript\u002F04.react\u002F05.react-router\u002F03.nested-routes-and-layouts",{"title":2118,"path":2119,"stem":2120},"Динамічні Маршрути та Параметри","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdynamic-routing","03.javascript\u002F04.react\u002F05.react-router\u002F04.dynamic-routing",{"title":2122,"path":2123,"stem":2124},"Data APIs: Loaders та Actions","\u002Fjavascript\u002Freact\u002Freact-router\u002Fdata-loading","03.javascript\u002F04.react\u002F05.react-router\u002F05.data-loading",{"title":2126,"path":2127,"stem":2128},"Просунуті Патерни","\u002Fjavascript\u002Freact\u002Freact-router\u002Fadvanced-patterns","03.javascript\u002F04.react\u002F05.react-router\u002F06.advanced-patterns",{"title":2130,"path":2131,"stem":2132},"Legacy Routing: Компонентний підхід","\u002Fjavascript\u002Freact\u002Freact-router\u002Flegacy-routing","03.javascript\u002F04.react\u002F05.react-router\u002F07.legacy-routing",{"title":2134,"icon":132,"path":2135,"stem":2136,"children":2137},"Redux","\u002Fjavascript\u002Freact\u002Fredux","03.javascript\u002F04.react\u002F06.redux\u002Findex",[2138,2140,2156,2185,2194,2215,2231,2260],{"title":2139,"path":2135,"stem":2136},"Redux: Еволюція управління станом",{"title":14,"icon":15,"path":2141,"stem":2142,"children":2143,"page":59},"\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals",[2144,2148,2152],{"title":2145,"path":2146,"stem":2147},"Вступ до State Management","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fintro-state-management","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F01.intro-state-management",{"title":2149,"path":2150,"stem":2151},"Філософія Redux та Три Принципи","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fredux-philosophy","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F02.redux-philosophy",{"title":2153,"path":2154,"stem":2155},"Чисті функції та Іммутабельність","\u002Fjavascript\u002Freact\u002Fredux\u002Ffundamentals\u002Fpure-functions-immutability","03.javascript\u002F04.react\u002F06.redux\u002F01.fundamentals\u002F03.pure-functions-immutability",{"title":2157,"icon":132,"path":2158,"stem":2159,"children":2160,"page":59},"Classic Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux",[2161,2165,2169,2173,2177,2181],{"title":2162,"path":2163,"stem":2164},"Створення Store (Classic Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fstore-setup","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F01.store-setup",{"title":2166,"path":2167,"stem":2168},"Actions, Constants та Action Creators","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Factions-constants","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F02.actions-constants",{"title":2170,"path":2171,"stem":2172},"Логіка Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freducers","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F03.reducers",{"title":2174,"path":2175,"stem":2176},"Комбінування Reducers (Root Reducer)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fdata-flow","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F04.data-flow",{"title":2178,"path":2179,"stem":2180},"Підключення до React (React-Redux)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Freact-redux-connection","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F05.react-redux-connection",{"title":2182,"path":2183,"stem":2184},"Middleware та Асинхронність (Redux Thunk)","\u002Fjavascript\u002Freact\u002Fredux\u002Fclassic-redux\u002Fmiddleware-thunk","03.javascript\u002F04.react\u002F06.redux\u002F02.classic-redux\u002F06.middleware-thunk",{"title":2186,"icon":132,"path":2187,"stem":2188,"children":2189,"page":59},"Transition To Rtk","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk",[2190],{"title":2191,"path":2192,"stem":2193},"Проблеми класичного Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftransition-to-rtk\u002Fproblems-with-classic","03.javascript\u002F04.react\u002F06.redux\u002F03.transition-to-rtk\u002F01.problems-with-classic",{"title":2195,"icon":132,"path":2196,"stem":2197,"children":2198,"page":59},"Redux Toolkit","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit",[2199,2203,2207,2211],{"title":2200,"path":2201,"stem":2202},"Налаштування Store з configureStore","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fconfigure-store","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F01.configure-store",{"title":2204,"path":2205,"stem":2206},"createSlice: Революція в Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fcreate-slice","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F02.create-slice",{"title":2208,"path":2209,"stem":2210},"Асинхронність з createAsyncThunk","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fasync-thunks","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F03.async-thunks",{"title":2212,"path":2213,"stem":2214},"04. Entity Adapter: Керування нормалізованим станом","\u002Fjavascript\u002Freact\u002Fredux\u002Fredux-toolkit\u002Fentity-adapter","03.javascript\u002F04.react\u002F06.redux\u002F04.redux-toolkit\u002F04.entity-adapter",{"title":2216,"icon":92,"path":2217,"stem":2218,"children":2219,"page":59},"Advanced","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced",[2220,2224,2228],{"title":2221,"path":2222,"stem":2223},"Мемоізація та Селектори: Повний Гайд по Reselect","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Fselectors-reselect","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F01.selectors-reselect",{"title":2225,"path":2226,"stem":2227},"RTK Query: Архітектура Серверного Кешу","\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Frtk-query-intro","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F02.rtk-query-intro",{"title":2075,"path":2229,"stem":2230},"\u002Fjavascript\u002Freact\u002Fredux\u002Fadvanced\u002Farchitecture-best-practices","03.javascript\u002F04.react\u002F06.redux\u002F05.advanced\u002F03.architecture-best-practices",{"title":2232,"icon":132,"path":2233,"stem":2234,"children":2235,"page":59},"Project Kanban","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban",[2236,2240,2244,2248,2252,2256],{"title":2237,"path":2238,"stem":2239},"Проєкт: Kanban Board (Trello Clone)","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fproject-overview","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F01.project-overview",{"title":2241,"path":2242,"stem":2243},"Налаштування та Типізація","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fsetup-and-types","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F02.setup-and-types",{"title":2245,"path":2246,"stem":2247},"Board Slice: Серце Дошки","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fboard-slice","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F03.board-slice",{"title":2249,"path":2250,"stem":2251},"Логіка Drag & Drop","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Fdrag-and-drop-logic","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F04.drag-and-drop-logic",{"title":2253,"path":2254,"stem":2255},"Інтеграція з RTK Query","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Frtk-query-integration","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F05.rtk-query-integration",{"title":2257,"path":2258,"stem":2259},"Optimistic Updates","\u002Fjavascript\u002Freact\u002Fredux\u002Fproject-kanban\u002Foptimistic-updates","03.javascript\u002F04.react\u002F06.redux\u002F06.project-kanban\u002F06.optimistic-updates",{"title":2261,"icon":132,"path":2262,"stem":2263,"children":2264,"page":59},"Testing","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting","03.javascript\u002F04.react\u002F06.redux\u002F07.testing",[2265,2269,2273,2277,2281],{"title":2266,"path":2267,"stem":2268},"Тестування Redux","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Fintro-testing","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F01.intro-testing",{"title":2270,"path":2271,"stem":2272},"Тестування Reducers","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-reducers","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F02.testing-reducers",{"title":2274,"path":2275,"stem":2276},"Тестування Селекторів","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-selectors","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F03.testing-selectors",{"title":2278,"path":2279,"stem":2280},"Тестування Компонентів (Integration)","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-components","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F04.testing-components",{"title":2282,"path":2283,"stem":2284},"Тестування Async Thunks","\u002Fjavascript\u002Freact\u002Fredux\u002Ftesting\u002Ftesting-thunks","03.javascript\u002F04.react\u002F06.redux\u002F07.testing\u002F05.testing-thunks",{"title":2286,"icon":132,"path":2287,"stem":2288,"children":2289},"Ui Libraries","\u002Fjavascript\u002Freact\u002Fui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002Findex",[2290,2292,2296,2300,2304,2308,2312],{"title":2291,"path":2287,"stem":2288},"UI Бібліотеки в React",{"title":2293,"path":2294,"stem":2295},"Вступ до UI Бібліотек: Навіщо Винаходити Велосипед Двічі?","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fintroduction-to-ui-libraries","03.javascript\u002F04.react\u002F07.ui-libraries\u002F01.introduction-to-ui-libraries",{"title":2297,"path":2298,"stem":2299},"Філософія shadcn\u002Fui: \"Not a Component Library\"","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-philosophy","03.javascript\u002F04.react\u002F07.ui-libraries\u002F02.shadcn-philosophy",{"title":2301,"path":2302,"stem":2303},"Установка та Налаштування shadcn\u002Fui","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-installation","03.javascript\u002F04.react\u002F07.ui-libraries\u002F03.shadcn-installation",{"title":2305,"path":2306,"stem":2307},"Базові Компоненти shadcn\u002Fui: Фундамент Інтерфейсу","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-basics","03.javascript\u002F04.react\u002F07.ui-libraries\u002F04.shadcn-components-basics",{"title":2309,"path":2310,"stem":2311},"Компоненти Форм: Побудова Інтерактивних Form","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-forms","03.javascript\u002F04.react\u002F07.ui-libraries\u002F05.shadcn-components-forms",{"title":2313,"path":2314,"stem":2315},"Складні Компоненти: Dialog, Dropdown, Table та Command","\u002Fjavascript\u002Freact\u002Fui-libraries\u002Fshadcn-components-advanced","03.javascript\u002F04.react\u002F07.ui-libraries\u002F06.shadcn-components-advanced",{"title":2317,"icon":2318,"path":2319,"stem":2320,"children":2321,"page":59},"TypeScript","i-devicon-typescript","\u002Fjavascript\u002Ftypescript","03.javascript\u002F05.typescript",[2322,2326,2330,2334,2338,2342,2346,2350],{"title":2323,"path":2324,"stem":2325},"TypeScript: Броня для вашого коду","\u002Fjavascript\u002Ftypescript\u002Fintro-and-basic-types","03.javascript\u002F05.typescript\u002F01.intro-and-basic-types",{"title":2327,"path":2328,"stem":2329},"Майстерність Моделювання Даних: Інтерфейси та Просунуті Типи","\u002Fjavascript\u002Ftypescript\u002Finterfaces-and-advanced-types","03.javascript\u002F05.typescript\u002F02.interfaces-and-advanced-types",{"title":2331,"path":2332,"stem":2333},"Алхімія Типів: Generics та Utility Types","\u002Fjavascript\u002Ftypescript\u002Fgenerics-and-utilities","03.javascript\u002F05.typescript\u002F03.generics-and-utilities",{"title":2335,"path":2336,"stem":2337},"Архітектура та Шаблони: Класи в TypeScript","\u002Fjavascript\u002Ftypescript\u002Fclasses-and-oop","03.javascript\u002F05.typescript\u002F04.classes-and-oop",{"title":2339,"path":2340,"stem":2341},"Продакшн та Екосистема: Advanced Config & Workflow","\u002Fjavascript\u002Ftypescript\u002Fadvanced-patterns-and-config","03.javascript\u002F05.typescript\u002F05.advanced-patterns-and-config",{"title":2343,"path":2344,"stem":2345},"TypeScript у світі React","\u002Fjavascript\u002Ftypescript\u002Freact-basics","03.javascript\u002F05.typescript\u002F06.react-basics",{"title":2347,"path":2348,"stem":2349},"React + TypeScript: Продвинуті патерни","\u002Fjavascript\u002Ftypescript\u002Freact-advanced","03.javascript\u002F05.typescript\u002F07.react-advanced",{"title":2351,"path":2352,"stem":2353},"React + TypeScript: Екосистема та бібліотеки","\u002Fjavascript\u002Ftypescript\u002Freact-ecosystem","03.javascript\u002F05.typescript\u002F08.react-ecosystem",{"title":2355,"path":2356,"stem":2357},"Atomic Design","\u002Fjavascript\u002Fatomic-design","03.javascript\u002F2.atomic-design",{"title":2359,"icon":2360,"path":2361,"stem":2362,"children":2363,"page":59},"Java","i-devicon-java","\u002Fjava","04.java",[2364,2367,2370,2374,2378,2382,2386],{"title":162,"path":2365,"stem":2366},"\u002Fjava\u002Fdata-mapper-part1","04.java\u002F01.data-mapper-part1",{"title":166,"path":2368,"stem":2369},"\u002Fjava\u002Fdata-mapper-part2","04.java\u002F02.data-mapper-part2",{"title":2371,"path":2372,"stem":2373},"Service Layer: Організація бізнес-логіки","\u002Fjava\u002Fservice-layer","04.java\u002F03.service-layer",{"title":2375,"path":2376,"stem":2377},"Rich Domain Model та State Pattern","\u002Fjava\u002Frich-domain-model","04.java\u002F04.rich-domain-model",{"title":2379,"path":2380,"stem":2381},"Патерни для складної бізнес-логіки","\u002Fjava\u002Fbusiness-logic-patterns","04.java\u002F05.business-logic-patterns",{"title":2383,"path":2384,"stem":2385},"Обробка помилок та валідація","\u002Fjava\u002Ferror-handling-validation","04.java\u002F06.error-handling-validation",{"title":2387,"path":2388,"stem":2389,"children":2390,"page":59},"Проектування баз даних","\u002Fjava\u002Fpr2","04.java\u002Fpr2",[2391,2395,2399,2403,2407,2411,2415,2419,2423,2427,2431,2435,2439,2443,2447,2451,2455,2459,2463,2467,2471,2475,2479,2483,2487,2491,2495,2499,2503,2507,2511,2515,2519,2523,2527,2531,2535],{"title":2392,"path":2393,"stem":2394},"Концептуальне моделювання: Мистецтво розуміння предметної області","\u002Fjava\u002Fpr2\u002Fconceptual-modeling","04.java\u002Fpr2\u002F01.conceptual-modeling",{"title":2396,"path":2397,"stem":2398},"Логічне моделювання: Від бізнес-ідей до структур даних","\u002Fjava\u002Fpr2\u002Flogical-modeling","04.java\u002Fpr2\u002F02.logical-modeling",{"title":2400,"path":2401,"stem":2402},"Нормалізація: Гігієна даних та боротьба з аномаліями","\u002Fjava\u002Fpr2\u002Fnormalization","04.java\u002Fpr2\u002F03.normalization",{"title":2404,"path":2405,"stem":2406},"Фізична схема: Від абстракції до DDL","\u002Fjava\u002Fpr2\u002Fphysical-schema","04.java\u002Fpr2\u002F04.physical-schema",{"title":2408,"path":2409,"stem":2410},"Архітектурна класифікація таблиць","\u002Fjava\u002Fpr2\u002Ftable-classification","04.java\u002Fpr2\u002F05.table-classification",{"title":2412,"path":2413,"stem":2414},"Database Migrations: Версіонування схеми з Flyway","\u002Fjava\u002Fpr2\u002Fdatabase-migrations","04.java\u002Fpr2\u002F06.database-migrations",{"title":2416,"path":2417,"stem":2418},"А що, якби це була не реляційна БД?","\u002Fjava\u002Fpr2\u002Fbeyond-relational","04.java\u002Fpr2\u002F07.beyond-relational",{"title":2420,"path":2421,"stem":2422},"Object-Relational Impedance Mismatch: Два світи, що не хочуть дружити","\u002Fjava\u002Fpr2\u002Fimpedance-mismatch","04.java\u002Fpr2\u002F09.impedance-mismatch",{"title":2424,"path":2425,"stem":2426},"JDBC: Перший контакт із базою даних","\u002Fjava\u002Fpr2\u002Fjdbc-fundamentals","04.java\u002Fpr2\u002F10.jdbc-fundamentals",{"title":2428,"path":2429,"stem":2430},"Якість коду: Spotless, SpotBugs та SonarQube","\u002Fjava\u002Fpr2\u002F10a.code-quality","04.java\u002Fpr2\u002F10a.code-quality",{"title":2432,"path":2433,"stem":2434},"Connection Pool: Патерн Object Pool для JDBC-з'єднань","\u002Fjava\u002Fpr2\u002Fconnection-pool","04.java\u002Fpr2\u002F11.connection-pool",{"title":2436,"path":2437,"stem":2438},"Row Data Gateway: Об'єкт як обгортка рядка таблиці","\u002Fjava\u002Fpr2\u002Frow-data-gateway","04.java\u002Fpr2\u002F12.row-data-gateway",{"title":2440,"path":2441,"stem":2442},"Table Data Gateway: Фасад таблиці як архітектурний відступ","\u002Fjava\u002Fpr2\u002Ftable-data-gateway","04.java\u002Fpr2\u002F13.table-data-gateway",{"title":2444,"path":2445,"stem":2446},"Repository + Data Mapper: Правильна шарова архітектура з JDBC","\u002Fjava\u002Fpr2\u002Frepository-data-mapper","04.java\u002Fpr2\u002F14.repository-data-mapper",{"title":2448,"path":2449,"stem":2450},"Identity Map: Кешування сутностей у рамках сесії","\u002Fjava\u002Fpr2\u002Fidentity-map","04.java\u002Fpr2\u002F15.identity-map",{"title":2452,"path":2453,"stem":2454},"Unit of Work: Відстеження змін і координація JDBC-транзакцій","\u002Fjava\u002Fpr2\u002Funit-of-work","04.java\u002Fpr2\u002F16.unit-of-work",{"title":2456,"path":2457,"stem":2458},"Strategy: Замінювані SQL-стратегії для підтримки різних СУБД","\u002Fjava\u002Fpr2\u002Fstrategy-sql","04.java\u002Fpr2\u002F17.strategy-sql",{"title":2460,"path":2461,"stem":2462},"Proxy: Lazy Loading для One-To-Many колекцій","\u002Fjava\u002Fpr2\u002Fproxy-lazy-loading","04.java\u002Fpr2\u002F18.proxy-lazy-loading",{"title":2464,"path":2465,"stem":2466},"Generic Repository через Java Reflection: анотації та динамічний SQL","\u002Fjava\u002Fpr2\u002Fgeneric-repository-reflection","04.java\u002Fpr2\u002F19.generic-repository-reflection",{"title":2468,"path":2469,"stem":2470},"Specification Pattern: Композиція бізнес-правил для складних запитів","\u002Fjava\u002Fpr2\u002Fspecification-pattern","04.java\u002Fpr2\u002F20.specification-pattern",{"title":2472,"path":2473,"stem":2474},"Розширені можливості Specification Pattern: підзапити, агрегації та гібридний підхід","\u002Fjava\u002Fpr2\u002F20a.advanced-specifications","04.java\u002Fpr2\u002F20a.advanced-specifications",{"title":2476,"path":2477,"stem":2478},"Асинхронність у JDBC: Від блокуючих викликів до CompletableFuture","\u002Fjava\u002Fpr2\u002Fasynchronous-jdbc","04.java\u002Fpr2\u002F21.asynchronous-jdbc",{"title":2480,"path":2481,"stem":2482},"Інтеграційне тестування JDBC-репозиторіїв: Embedded H2 та патерн AAA","\u002Fjava\u002Fpr2\u002Fintegration-testing-h2","04.java\u002Fpr2\u002F22.integration-testing-h2",{"title":2484,"path":2485,"stem":2486},"Testcontainers: Тестування з реальною PostgreSQL у Docker-контейнерах","\u002Fjava\u002Fpr2\u002Fintegration-testing-testcontainers","04.java\u002Fpr2\u002F23.integration-testing-testcontainers",{"title":2488,"path":2489,"stem":2490},"Google Guice: Впровадження залежностей у JavaFX-проєкті","\u002Fjava\u002Fpr2\u002Fdependency-injection-guice","04.java\u002Fpr2\u002F24.dependency-injection-guice",{"title":2492,"path":2493,"stem":2494},"JavaFX: Основи побудови графічних інтерфейсів","\u002Fjava\u002Fpr2\u002Fjavafx-fundamentals","04.java\u002Fpr2\u002F25.javafx-fundamentals",{"title":2496,"path":2497,"stem":2498},"Properties та Bindings: Реактивність у JavaFX","\u002Fjava\u002Fpr2\u002Fjavafx-properties-bindings","04.java\u002Fpr2\u002F26.javafx-properties-bindings",{"title":2500,"path":2501,"stem":2502},"MVC vs MVP vs MVVM: Еволюція архітектурних патернів UI","\u002Fjava\u002Fpr2\u002Fui-architecture-patterns","04.java\u002Fpr2\u002F27.ui-architecture-patterns",{"title":2504,"path":2505,"stem":2506},"MVVM на практиці: Побудова ViewModel","\u002Fjava\u002Fpr2\u002Fmvvm-viewmodel-implementation","04.java\u002Fpr2\u002F28.mvvm-viewmodel-implementation",{"title":2508,"path":2509,"stem":2510},"View та Controller: Зв'язування з ViewModel через FXML","\u002Fjava\u002Fpr2\u002Fmvvm-view-controller","04.java\u002Fpr2\u002F29.mvvm-view-controller",{"title":2512,"path":2513,"stem":2514},"Інтеграція MVVM з Guice: Автоматична ін'єкція залежностей","\u002Fjava\u002Fpr2\u002Fmvvm-guice-integration","04.java\u002Fpr2\u002F30.mvvm-guice-integration",{"title":2516,"path":2517,"stem":2518},"Валідація та обробка помилок у MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-validation-error-handling","04.java\u002Fpr2\u002F31.mvvm-validation-error-handling",{"title":2520,"path":2521,"stem":2522},"Навігація та управління екранами у JavaFX MVVM","\u002Fjava\u002Fpr2\u002Fmvvm-navigation-screen-management","04.java\u002Fpr2\u002F32.mvvm-navigation-screen-management",{"title":2524,"path":2525,"stem":2526},"Тестування JavaFX MVVM-додатків","\u002Fjava\u002Fpr2\u002Fmvvm-testing","04.java\u002Fpr2\u002F33.mvvm-testing",{"title":2528,"path":2529,"stem":2530},"Стилізація та теми у JavaFX: CSS та User Experience","\u002Fjava\u002Fpr2\u002Fjavafx-styling-themes","04.java\u002Fpr2\u002F34.javafx-styling-themes",{"title":2532,"path":2533,"stem":2534},"AtlantaFX: Сучасні теми для JavaFX додатків","\u002Fjava\u002Fpr2\u002Fatlantafx-modern-themes","04.java\u002Fpr2\u002F35.atlantafx-modern-themes",{"title":2536,"path":2537,"stem":2538},"Пакування та розповсюдження JavaFX-додатків","\u002Fjava\u002Fpr2\u002Fjar-packaging-distribution","04.java\u002Fpr2\u002F36.jar-packaging-distribution",{"title":2540,"icon":2541,"path":2542,"stem":2543,"children":2544,"page":59},"Python","i-devicon-python","\u002Fpython","05.python",[2545,2549,2552,2556,2560,2564,2568,2572,2576,2580,2584,2588,2592,2596,2600,2604],{"title":2546,"path":2547,"stem":2548},"Модулі, Пакети та Віртуальні Середовища","\u002Fpython\u002Fmodules-packages-venv","05.python\u002F00.modules-packages-venv",{"title":71,"path":2550,"stem":2551},"\u002Fpython\u002Fclasses-objects","05.python\u002F01.classes-objects",{"title":2553,"path":2554,"stem":2555},"Інкапсуляція, Керування Доступом та Властивості","\u002Fpython\u002Fencapsulation","05.python\u002F02.encapsulation",{"title":2557,"path":2558,"stem":2559},"Наслідування, MRO та суперсила super()","\u002Fpython\u002Finheritance-mro","05.python\u002F03.inheritance-mro",{"title":2561,"path":2562,"stem":2563},"Абстракція — ABC проти Статичних Протоколів (PEP 544)","\u002Fpython\u002Fabstraction-protocols","05.python\u002F04.abstraction-protocols",{"title":2565,"path":2566,"stem":2567},"Магічні методи (Dunder) та Емуляція протоколів","\u002Fpython\u002Fdunder-methods","05.python\u002F05.dunder-methods",{"title":2569,"path":2570,"stem":2571},"Декоратори та Керування життєвим циклом методів","\u002Fpython\u002Fdecorators-static-class","05.python\u002F06.decorators-static-class",{"title":2573,"path":2574,"stem":2575},"Дескриптори — Магія доступу до атрибутів","\u002Fpython\u002Fdescriptors","05.python\u002F07.descriptors",{"title":2577,"path":2578,"stem":2579},"Метакласи — Динамічне створення класів під капотом CPython","\u002Fpython\u002Fmetaclasses","05.python\u002F08.metaclasses",{"title":2581,"path":2582,"stem":2583},"Dataclasses, NamedTuple та сучасні контейнери Python","\u002Fpython\u002Fmodern-containers","05.python\u002F09.modern-containers",{"title":2585,"path":2586,"stem":2587},"GIL та модель конкурентності CPython — фундамент перед потоками і процесами","\u002Fpython\u002Fgil-concurrency-intro","05.python\u002F11.gil-concurrency-intro",{"title":2589,"path":2590,"stem":2591},"Threading — конкурентність для I\u002FO-bound задач","\u002Fpython\u002Fthreading","05.python\u002F12.threading",{"title":2593,"path":2594,"stem":2595},"Multiprocessing — справжній паралелізм для CPU-bound задач","\u002Fpython\u002Fmultiprocessing","05.python\u002F13.multiprocessing",{"title":2597,"path":2598,"stem":2599},"asyncio — кооперативна конкурентність та event loop","\u002Fpython\u002Fasyncio","05.python\u002F14.asyncio",{"title":2601,"path":2602,"stem":2603},"📦 Повний посібник з модулів, пакетів та віртуальних середовищ у Python","\u002Fpython\u002Flesson_9","05.python\u002Flesson_9",{"title":2605,"path":2606,"stem":2607},"[object Object]","\u002Fpython\u002Foop-plan","05.python\u002Foop-plan",{"title":2609,"icon":2610,"path":2611,"stem":2612,"children":2613,"page":59},"Бази даних","i-lucide-database","\u002Fdatabases","06.databases",[2614,2644,2667,2704,2733,2751,2785,2797,2806],{"title":2615,"icon":2616,"path":2617,"stem":2618,"children":2619,"page":59},"Intro","i-lucide-play","\u002Fdatabases\u002Fintro","06.databases\u002F01.intro",[2620,2624,2628,2632,2636,2640],{"title":2621,"path":2622,"stem":2623},"Введення в теорію баз даних","\u002Fdatabases\u002Fintro\u002Fintroduction-to-databases","06.databases\u002F01.intro\u002F01.introduction-to-databases",{"title":2625,"path":2626,"stem":2627},"Реляційна модель даних","\u002Fdatabases\u002Fintro\u002Frelational-model-theory","06.databases\u002F01.intro\u002F02.relational-model-theory",{"title":2629,"path":2630,"stem":2631},"ER-моделювання","\u002Fdatabases\u002Fintro\u002Fer-modeling","06.databases\u002F01.intro\u002F03.er-modeling",{"title":2633,"path":2634,"stem":2635},"Логічне проектування БД","\u002Fdatabases\u002Fintro\u002Flogical-schema","06.databases\u002F01.intro\u002F04.logical-schema",{"title":2637,"path":2638,"stem":2639},"Класифікація таблиць","\u002Fdatabases\u002Fintro\u002Ftable-classification","06.databases\u002F01.intro\u002F05.table-classification",{"title":2641,"path":2642,"stem":2643},"PlantUML для баз даних","\u002Fdatabases\u002Fintro\u002Fplantuml-diagrams","06.databases\u002F01.intro\u002F06.plantuml-diagrams",{"title":2645,"icon":2610,"path":2646,"stem":2647,"children":2648,"page":59},"MS SQL Server Start","\u002Fdatabases\u002Fms-sql-server-start","06.databases\u002F02.ms-sql-server-start",[2649,2653,2659,2663],{"title":2650,"path":2651,"stem":2652},"Типи даних у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fdata-types","06.databases\u002F02.ms-sql-server-start\u002F01.data-types",{"title":2654,"path":2655,"stem":2656,"children":2657},"Індекси у MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-indexes","06.databases\u002F02.ms-sql-server-start\u002F02.sql-indexes",[2658],{"title":2654,"path":2655,"stem":2656},{"title":2660,"path":2661,"stem":2662},"Системні бази даних MS SQL Server","\u002Fdatabases\u002Fms-sql-server-start\u002Fsystem-databases","06.databases\u002F02.ms-sql-server-start\u002F03.system-databases",{"title":2664,"path":2665,"stem":2666},"Огляд мови SQL та запитів","\u002Fdatabases\u002Fms-sql-server-start\u002Fsql-queries-overview","06.databases\u002F02.ms-sql-server-start\u002F04.sql-queries-overview",{"title":2668,"icon":2610,"path":2669,"stem":2670,"children":2671,"page":59},"SQL","\u002Fdatabases\u002Fsql","06.databases\u002F03.sql",[2672,2676,2680,2684,2688,2692,2696,2700],{"title":2673,"path":2674,"stem":2675},"Налаштування демонстраційної бази даних","\u002Fdatabases\u002Fsql\u002Fsample-database-setup","06.databases\u002F03.sql\u002F00.sample-database-setup",{"title":2677,"path":2678,"stem":2679},"DDL - Створення таблиць (CREATE TABLE)","\u002Fdatabases\u002Fsql\u002Fddl-create-table","06.databases\u002F03.sql\u002F01.ddl-create-table",{"title":2681,"path":2682,"stem":2683},"DDL - Зміна та видалення таблиць (ALTER, DROP)","\u002Fdatabases\u002Fsql\u002Fddl-alter-drop-table","06.databases\u002F03.sql\u002F02.ddl-alter-drop-table",{"title":2685,"path":2686,"stem":2687},"SELECT запити - Основи","\u002Fdatabases\u002Fsql\u002Fselect-queries-fundamentals","06.databases\u002F03.sql\u002F03.select-queries-fundamentals",{"title":2689,"path":2690,"stem":2691},"SELECT запити - Розширені можливості","\u002Fdatabases\u002Fsql\u002Fselect-queries-advanced","06.databases\u002F03.sql\u002F04.select-queries-advanced",{"title":2693,"path":2694,"stem":2695},"INSERT запити - Додавання даних","\u002Fdatabases\u002Fsql\u002Finsert-queries","06.databases\u002F03.sql\u002F05.insert-queries",{"title":2697,"path":2698,"stem":2699},"UPDATE та DELETE запити","\u002Fdatabases\u002Fsql\u002Fupdate-delete-queries","06.databases\u002F03.sql\u002F06.update-delete-queries",{"title":2701,"path":2702,"stem":2703},"Транзакції в SQL","\u002Fdatabases\u002Fsql\u002Ftransactions","06.databases\u002F03.sql\u002F07.transactions",{"title":2705,"icon":2610,"path":2706,"stem":2707,"children":2708,"page":59},"Multi Table Databases","\u002Fdatabases\u002Fmulti-table-databases","06.databases\u002F04.multi-table-databases",[2709,2713,2717,2721,2725,2729],{"title":2710,"path":2711,"stem":2712},"Зв'язки та нормалізація БД","\u002Fdatabases\u002Fmulti-table-databases\u002Frelationships-and-normalization","06.databases\u002F04.multi-table-databases\u002F00.relationships-and-normalization",{"title":2714,"path":2715,"stem":2716},"INNER JOIN - З'єднання таблиць","\u002Fdatabases\u002Fmulti-table-databases\u002Finner-join","06.databases\u002F04.multi-table-databases\u002F01.inner-join",{"title":2718,"path":2719,"stem":2720},"OUTER JOINs - LEFT, RIGHT, FULL","\u002Fdatabases\u002Fmulti-table-databases\u002Fouter-joins","06.databases\u002F04.multi-table-databases\u002F02.outer-joins",{"title":2722,"path":2723,"stem":2724},"CROSS та SELF JOINs","\u002Fdatabases\u002Fmulti-table-databases\u002Fcross-self-joins","06.databases\u002F04.multi-table-databases\u002F03.cross-self-joins",{"title":2726,"path":2727,"stem":2728},"Підзапити (Subqueries)","\u002Fdatabases\u002Fmulti-table-databases\u002Fsubqueries","06.databases\u002F04.multi-table-databases\u002F04.subqueries",{"title":2730,"path":2731,"stem":2732},"Агрегації з JOIN","\u002Fdatabases\u002Fmulti-table-databases\u002Faggregations-with-joins","06.databases\u002F04.multi-table-databases\u002F05.aggregations-with-joins",{"title":2734,"icon":2735,"path":2736,"stem":2737,"children":2738,"page":59},"Aggregate Functions","i-lucide-calculator","\u002Fdatabases\u002Faggregate-functions","06.databases\u002F05.aggregate-functions",[2739,2743,2747],{"title":2740,"path":2741,"stem":2742},"Функції агрегування в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fintroduction-aggregate-functions","06.databases\u002F05.aggregate-functions\u002F01.introduction-aggregate-functions",{"title":2744,"path":2745,"stem":2746},"Групування даних в MS SQL Server","\u002Fdatabases\u002Faggregate-functions\u002Fgrouping-data","06.databases\u002F05.aggregate-functions\u002F02.grouping-data",{"title":2748,"path":2749,"stem":2750},"Підзапити з агрегатними функціями","\u002Fdatabases\u002Faggregate-functions\u002Fsubqueries-aggregates","06.databases\u002F05.aggregate-functions\u002F03.subqueries-aggregates",{"title":2752,"icon":2753,"path":2754,"stem":2755,"children":2756,"page":59},"Тригери та зберігаємі процедури","i-lucide-database-zap","\u002Fdatabases\u002Ftriggers-stored-procedures","06.databases\u002F07.triggers-stored-procedures",[2757,2761,2765,2769,2773,2777,2781],{"title":2758,"path":2759,"stem":2760},"DML-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fdml-triggers","06.databases\u002F07.triggers-stored-procedures\u002F01.dml-triggers",{"title":2762,"path":2763,"stem":2764},"DDL-тригери","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fddl-triggers","06.databases\u002F07.triggers-stored-procedures\u002F02.ddl-triggers",{"title":2766,"path":2767,"stem":2768},"Transact-SQL розширення","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransact-sql-extensions","06.databases\u002F07.triggers-stored-procedures\u002F03.transact-sql-extensions",{"title":2770,"path":2771,"stem":2772},"Транзакції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Ftransactions","06.databases\u002F07.triggers-stored-procedures\u002F04.transactions",{"title":2774,"path":2775,"stem":2776},"Зберігаємі процедури","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fstored-procedures","06.databases\u002F07.triggers-stored-procedures\u002F05.stored-procedures",{"title":2778,"path":2779,"stem":2780},"Користувацькі функції","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fuser-defined-functions","06.databases\u002F07.triggers-stored-procedures\u002F06.user-defined-functions",{"title":2782,"path":2783,"stem":2784},"Безпека баз даних","\u002Fdatabases\u002Ftriggers-stored-procedures\u002Fsecurity","06.databases\u002F07.triggers-stored-procedures\u002F08.security",{"title":2782,"icon":793,"path":2786,"stem":2787,"children":2788,"page":59},"\u002Fdatabases\u002Fsecurity","06.databases\u002F08.security",[2789,2793],{"title":2790,"path":2791,"stem":2792},"Вступ до безпеки баз даних","\u002Fdatabases\u002Fsecurity\u002Fintroduction","06.databases\u002F08.security\u002F01.introduction",{"title":2794,"path":2795,"stem":2796},"Системні представлення та метадані","\u002Fdatabases\u002Fsecurity\u002Fsystem-views","06.databases\u002F08.security\u002F02.system-views",{"title":2798,"icon":2799,"path":2800,"stem":2801,"children":2802,"page":59},"Резервне копіювання та відновлення","i-lucide-database-backup","\u002Fdatabases\u002Fbackup-recovery","06.databases\u002F09.backup-recovery",[2803],{"title":2798,"path":2804,"stem":2805},"\u002Fdatabases\u002Fbackup-recovery\u002Fbackup-restore","06.databases\u002F09.backup-recovery\u002F01.backup-restore",{"title":2807,"icon":2808,"path":2809,"stem":2810,"children":2811,"page":59},"Повнотекстовий пошук","i-lucide-search","\u002Fdatabases\u002Ffull-text-search","06.databases\u002F10.full-text-search",[2812],{"title":2807,"path":2813,"stem":2814},"\u002Fdatabases\u002Ffull-text-search\u002Ffull-text-search","06.databases\u002F10.full-text-search\u002F01.full-text-search",{"title":2816,"icon":2817,"path":2818,"stem":2819,"children":2820,"page":59},"Tools","i-lucide-wrench","\u002Ftools","07.tools",[2821,2897],{"title":2822,"icon":2823,"path":2824,"stem":2825,"children":2826},"Docker","i-simple-icons-docker","\u002Ftools\u002Fdocker","07.tools\u002F01.docker\u002Findex",[2827,2829,2833,2837,2841,2845,2849,2853,2857,2861,2865,2869,2873,2877,2881,2885,2889,2893],{"title":2828,"path":2824,"stem":2825},"Docker: від нуля до production",{"title":2830,"path":2831,"stem":2832},"Контейнеризація — від проблеми до рішення","\u002Ftools\u002Fdocker\u002Fcontainerization-concept","07.tools\u002F01.docker\u002F01.containerization-concept",{"title":2834,"path":2835,"stem":2836},"Docker — що це і навіщо?","\u002Ftools\u002Fdocker\u002Fdocker-what-and-why","07.tools\u002F01.docker\u002F02.docker-what-and-why",{"title":2838,"path":2839,"stem":2840},"Архітектура Docker Engine","\u002Ftools\u002Fdocker\u002Fdocker-architecture","07.tools\u002F01.docker\u002F03.docker-architecture",{"title":2842,"path":2843,"stem":2844},"Встановлення Docker","\u002Ftools\u002Fdocker\u002Finstallation","07.tools\u002F01.docker\u002F04.installation",{"title":2846,"path":2847,"stem":2848},"Перший контейнер — docker run","\u002Ftools\u002Fdocker\u002Ffirst-container","07.tools\u002F01.docker\u002F05.first-container",{"title":2850,"path":2851,"stem":2852},"Життєвий цикл контейнера","\u002Ftools\u002Fdocker\u002Fcontainer-lifecycle","07.tools\u002F01.docker\u002F06.container-lifecycle",{"title":2854,"path":2855,"stem":2856},"Docker Images — фундаментальні концепції","\u002Ftools\u002Fdocker\u002Fdocker-images-fundamentals","07.tools\u002F01.docker\u002F07.docker-images-fundamentals",{"title":2858,"path":2859,"stem":2860},"Dockerfile — основи","\u002Ftools\u002Fdocker\u002Fdockerfile-basics","07.tools\u002F01.docker\u002F08.dockerfile-basics",{"title":2862,"path":2863,"stem":2864},"Dockerfile — просунуті техніки","\u002Ftools\u002Fdocker\u002Fdockerfile-advanced","07.tools\u002F01.docker\u002F09.dockerfile-advanced",{"title":2866,"path":2867,"stem":2868},"Build Context та кешування шарів","\u002Ftools\u002Fdocker\u002Fbuild-context-and-cache","07.tools\u002F01.docker\u002F10.build-context-and-cache",{"title":2870,"path":2871,"stem":2872},"Реєстри Docker-образів","\u002Ftools\u002Fdocker\u002Fimage-registries","07.tools\u002F01.docker\u002F11.image-registries",{"title":2874,"path":2875,"stem":2876},"Контейнеризація .NET додатків","\u002Ftools\u002Fdocker\u002Fdotnet-containerization","07.tools\u002F01.docker\u002F12.dotnet-containerization",{"title":2878,"path":2879,"stem":2880},"Томи та збереження даних","\u002Ftools\u002Fdocker\u002Fvolumes-and-data","07.tools\u002F01.docker\u002F13.volumes-and-data",{"title":2882,"path":2883,"stem":2884},"Основи мережі в Docker","\u002Ftools\u002Fdocker\u002Fnetworking-basics","07.tools\u002F01.docker\u002F14.networking-basics",{"title":2886,"path":2887,"stem":2888},"Змінні оточення та конфігурація","\u002Ftools\u002Fdocker\u002Fenvironment-and-configuration","07.tools\u002F01.docker\u002F15.environment-and-configuration",{"title":2890,"path":2891,"stem":2892},"Docker Compose — оркестрація контейнерів","\u002Ftools\u002Fdocker\u002Fdocker-compose-basics","07.tools\u002F01.docker\u002F16.docker-compose-basics",{"title":2894,"path":2895,"stem":2896},"Docker Compose — Multi-Service застосунки","\u002Ftools\u002Fdocker\u002Fcompose-multi-service","07.tools\u002F01.docker\u002F17.compose-multi-service",{"title":2898,"icon":2899,"path":2900,"stem":2901,"children":2902},"Kubernetes","simple-icons:kubernetes","\u002Ftools\u002Fkubernetes","07.tools\u002F02.kubernetes\u002Findex",[2903,2905,2909,2913,2917,2921,2925,2929,2933],{"title":2904,"path":2900,"stem":2901},"Kubernetes: від розробки до production",{"title":2906,"path":2907,"stem":2908},"Kubernetes — коли Docker Compose більше не вистачає","\u002Ftools\u002Fkubernetes\u002Fwhy-kubernetes","07.tools\u002F02.kubernetes\u002F01.why-kubernetes",{"title":2910,"path":2911,"stem":2912},"Архітектура Kubernetes — анатомія кластера","\u002Ftools\u002Fkubernetes\u002Fkubernetes-architecture","07.tools\u002F02.kubernetes\u002F02.kubernetes-architecture",{"title":2914,"path":2915,"stem":2916},"Локальне середовище — minikube, kind та k3s","\u002Ftools\u002Fkubernetes\u002Flocal-environment","07.tools\u002F02.kubernetes\u002F03.local-environment",{"title":2918,"path":2919,"stem":2920},"Pod — атомарна одиниця Kubernetes","\u002Ftools\u002Fkubernetes\u002Fpods-and-containers","07.tools\u002F02.kubernetes\u002F04.pods-and-containers",{"title":2922,"path":2923,"stem":2924},"Патерни використання Pod","\u002Ftools\u002Fkubernetes\u002Fpod-patterns","07.tools\u002F02.kubernetes\u002F05.pod-patterns",{"title":2926,"path":2927,"stem":2928},"Deployment — декларативне управління Pod","\u002Ftools\u002Fkubernetes\u002Fdeployment-basics","07.tools\u002F02.kubernetes\u002F06.deployment-basics",{"title":2930,"path":2931,"stem":2932},"Rolling Updates та управління життєвим циклом Deployment","\u002Ftools\u002Fkubernetes\u002Fdeployment-rolling-updates","07.tools\u002F02.kubernetes\u002F07.deployment-rolling-updates",{"title":2934,"path":2935,"stem":2936},"Service — мережева абстракція для Pod","\u002Ftools\u002Fkubernetes\u002Fservices-networking","07.tools\u002F02.kubernetes\u002F08.services-networking",{"title":2938,"icon":2939,"path":2940,"stem":2941,"children":2942,"page":59},"Software Engineering","i-lucide-code-2","\u002Fsoftware-engineering","09.software-engineering",[2943,2947,2951,2955,2959,2963,2967,2971,2975,2979,2983],{"title":2944,"path":2945,"stem":2946},"1. Аналіз предметної області. Експертні знання та складність","\u002Fsoftware-engineering\u002Fintro-subdomains","09.software-engineering\u002F01.intro-subdomains",{"title":2948,"path":2949,"stem":2950},"2. Обмежені контексти. Інтеграція обмежених контекстів","\u002Fsoftware-engineering\u002Fintegrating-limited-contexts","09.software-engineering\u002F02.integrating-limited-contexts",{"title":2952,"path":2953,"stem":2954},"3. Реалізація простої бізнес-логіки","\u002Fsoftware-engineering\u002Fsimple","09.software-engineering\u002F03.simple",{"title":2956,"path":2957,"stem":2958},"4. Опрацювання складної бізнес-логіки","\u002Fsoftware-engineering\u002Fcomplex-business-logic","09.software-engineering\u002F04.complex-business-logic",{"title":2960,"path":2961,"stem":2962},"5. Моделювання фактора часу. Подієво-орієнтована архітектура.","\u002Fsoftware-engineering\u002Fmodelling-the-time-factor","09.software-engineering\u002F05.modelling-the-time-factor",{"title":2964,"path":2965,"stem":2966},"6. Архітектурні патерни","\u002Fsoftware-engineering\u002Farchitectural-patterns","09.software-engineering\u002F06.architectural-patterns",{"title":2968,"path":2969,"stem":2970},"Паттерни взаємодії","\u002Fsoftware-engineering\u002Fpatterns-of-interaction","09.software-engineering\u002F07.patterns-of-interaction",{"title":2972,"path":2973,"stem":2974},"Евристика проєктування","\u002Fsoftware-engineering\u002Fdesign-heuristics","09.software-engineering\u002F08.design-heuristics",{"title":2976,"path":2977,"stem":2978},"Еволюція проєктних рішень","\u002Fsoftware-engineering\u002Fevolution-of-design-solutions","09.software-engineering\u002F09.evolution-of-design-solutions",{"title":2980,"path":2981,"stem":2982},"EventStorming","\u002Fsoftware-engineering\u002Feventstorming","09.software-engineering\u002F10.eventstorming",{"title":2984,"path":2985,"stem":2986},"DDD на практиці","\u002Fsoftware-engineering\u002Fddd-in-practice","09.software-engineering\u002F11.ddd-in-practice",{"title":2988,"icon":943,"path":2989,"stem":2990,"children":2991,"page":59},"DDD","\u002Fddd","10.ddd",[2992,2996,3000,3004,3008,3012,3016,3020,3024,3028,3032,3036,3040],{"title":2993,"path":2994,"stem":2995},"Аналіз предметної області","\u002Fddd\u002Fdomain-analysis","10.ddd\u002F01.domain-analysis",{"title":2997,"path":2998,"stem":2999},"Експертні знання про предметну область","\u002Fddd\u002Fdomain-expert-knowledge","10.ddd\u002F02.domain-expert-knowledge",{"title":3001,"path":3002,"stem":3003},"Як осмислити складність предметної області","\u002Fddd\u002Fmanaging-domain-complexity","10.ddd\u002F03.managing-domain-complexity",{"title":3005,"path":3006,"stem":3007},"Інтеграція обмежених контекстів","\u002Fddd\u002Fbounded-context-integration","10.ddd\u002F04.bounded-context-integration",{"title":3009,"path":3010,"stem":3011},"Реалізація простої бізнес-логіки","\u002Fddd\u002Fsimple-business-logic","10.ddd\u002F05.simple-business-logic",{"title":3013,"path":3014,"stem":3015},"Обробка складної бізнес-логіки","\u002Fddd\u002Fcomplex-business-logic","10.ddd\u002F06.complex-business-logic",{"title":3017,"path":3018,"stem":3019},"Моделювання фактора часу","\u002Fddd\u002Ftime-modeling","10.ddd\u002F07.time-modeling",{"title":3021,"path":3022,"stem":3023},"Глава 8. Архітектурні Патерни","\u002Fddd\u002Farchitectural-patterns","10.ddd\u002F08.architectural-patterns",{"title":3025,"path":3026,"stem":3027},"Глава 9. Патерни Взаємодії","\u002Fddd\u002Finteraction-patterns","10.ddd\u002F09.interaction-patterns",{"title":3029,"path":3030,"stem":3031},"Глава 10. Проектні Евристики","\u002Fddd\u002Fdesign-heuristics","10.ddd\u002F10.design-heuristics",{"title":3033,"path":3034,"stem":3035},"Глава 11. Еволюція Проектних Рішень","\u002Fddd\u002Fevolution-of-design-decisions","10.ddd\u002F11.evolution-of-design-decisions",{"title":3037,"path":3038,"stem":3039},"Глава 12. EventStorming","\u002Fddd\u002Fevent-storming","10.ddd\u002F12.event-storming",{"title":3041,"path":3042,"stem":3043},"Глава 13. DDD на Практиці","\u002Fddd\u002Fddd-in-practice","10.ddd\u002F13.ddd-in-practice",{"title":3045,"icon":3046,"path":3047,"stem":3048,"children":3049,"page":59},"Media Streaming","i-lucide-video","\u002Fmedia-streaming","11.media-streaming",[3050,3054,3058,3062,3066,3070,3074],{"title":3051,"path":3052,"stem":3053},"01. Магія Стрімінгу: Що відбувається, коли ви натискаєте \"Play\"","\u002Fmedia-streaming\u002Fintroduction","11.media-streaming\u002F01.introduction",{"title":3055,"path":3056,"stem":3057},"02. Анатомія Медіа: Кодеки, Контейнери та Стиснення","\u002Fmedia-streaming\u002Faudio-video-anatomy","11.media-streaming\u002F02.audio-video-anatomy",{"title":3059,"path":3060,"stem":3061},"03. The Gym: FFmpeg Deep Dive","\u002Fmedia-streaming\u002Fffmpeg-gym","11.media-streaming\u002F03.ffmpeg-gym",{"title":3063,"path":3064,"stem":3065},"04. HLS Protocol: HTTP Live Streaming у Деталях","\u002Fmedia-streaming\u002Fhls-protocol","11.media-streaming\u002F04.hls-protocol",{"title":3067,"path":3068,"stem":3069},"05. DASH Protocol: Відкритий Стандарт","\u002Fmedia-streaming\u002Fdash-protocol","11.media-streaming\u002F05.dash-protocol",{"title":3071,"path":3072,"stem":3073},"06. Масштабування: CDN та Adaptive Bitrate","\u002Fmedia-streaming\u002Fcdn-and-adaptive-bitrate","11.media-streaming\u002F06.cdn-and-adaptive-bitrate",{"title":3075,"path":3076,"stem":3077},"07. Війна із Затримкою (Latency)","\u002Fmedia-streaming\u002Frealtime-latency","11.media-streaming\u002F07.realtime-latency",{"title":3079,"icon":3080,"path":3081,"stem":3082,"children":3083,"page":59},"HTML & CSS","i-devicon-html5","\u002Fhtml-css","12.html-css",[3084,3088,3092,3096,3100,3104,3108,3112,3116,3120,3124,3128,3132,3136,3140,3144,3148,3152,3156,3160,3164,3168,3172,3176,3180,3184,3188,3192,3196,3200],{"title":3085,"path":3086,"stem":3087},"Вступ до HTML. Структура документа","\u002Fhtml-css\u002Fintro-html-structure","12.html-css\u002F01.intro-html-structure",{"title":3089,"path":3090,"stem":3091},"Форматування тексту в HTML","\u002Fhtml-css\u002Fhtml-text-formatting","12.html-css\u002F02.html-text-formatting",{"title":3093,"path":3094,"stem":3095},"Посилання та зображення в HTML","\u002Fhtml-css\u002Fhtml-links-images","12.html-css\u002F03.html-links-images",{"title":3097,"path":3098,"stem":3099},"Списки та таблиці в HTML","\u002Fhtml-css\u002Fhtml-lists-tables","12.html-css\u002F04.html-lists-tables",{"title":3101,"path":3102,"stem":3103},"Форми в HTML","\u002Fhtml-css\u002Fhtml-forms","12.html-css\u002F05.html-forms",{"title":3105,"path":3106,"stem":3107},"Семантичні елементи HTML5","\u002Fhtml-css\u002Fhtml-semantic-elements","12.html-css\u002F06.html-semantic-elements",{"title":3109,"path":3110,"stem":3111},"Мультимедіа та розширені елементи HTML","\u002Fhtml-css\u002Fhtml-multimedia-advanced","12.html-css\u002F07.html-multimedia-advanced",{"title":3113,"path":3114,"stem":3115},"Мікророзмітка та SEO в HTML","\u002Fhtml-css\u002Fhtml-microdata-seo","12.html-css\u002F08.html-microdata-seo",{"title":3117,"path":3118,"stem":3119},"Вступ до CSS. Селектори та специфічність","\u002Fhtml-css\u002Fcss-intro-selectors","12.html-css\u002F09.css-intro-selectors",{"title":3121,"path":3122,"stem":3123},"Блокова модель CSS. Відступи. Box Sizing","\u002Fhtml-css\u002Fcss-box-model","12.html-css\u002F10.css-box-model",{"title":3125,"path":3126,"stem":3127},"Розміри у CSS: повний довідник одиниць і ключових слів","\u002Fhtml-css\u002F10a.css-sizing","12.html-css\u002F10a.css-sizing",{"title":3129,"path":3130,"stem":3131},"Типографіка в CSS. Шрифти та текст","\u002Fhtml-css\u002Fcss-typography","12.html-css\u002F11.css-typography",{"title":3133,"path":3134,"stem":3135},"Кольори та фони в CSS","\u002Fhtml-css\u002Fcss-colors-backgrounds","12.html-css\u002F12.css-colors-backgrounds",{"title":3137,"path":3138,"stem":3139},"Тіні та фільтри в CSS","\u002Fhtml-css\u002F12b.css-shadows-filters","12.html-css\u002F12b.css-shadows-filters",{"title":3141,"path":3142,"stem":3143},"CSS Flexbox: Фундамент гнучких макетів","\u002Fhtml-css\u002Fcss-flexbox-fundamentals","12.html-css\u002F13.css-flexbox-fundamentals",{"title":3145,"path":3146,"stem":3147},"CSS Flexbox: Вирівнювання та Позиціонування","\u002Fhtml-css\u002Fcss-flexbox-alignment-sizing-and-patterns","12.html-css\u002F14.css-flexbox-alignment-sizing-and-patterns",{"title":3149,"path":3150,"stem":3151},"CSS Grid. Двовимірний макет. Частина 1","\u002Fhtml-css\u002Fcss-layout-grid","12.html-css\u002F15.css-layout-grid",{"title":3153,"path":3154,"stem":3155},"CSS Grid. Двовимірний макет. Частина 2","\u002Fhtml-css\u002Fcss-layout-grid-advanced","12.html-css\u002F16.css-layout-grid-advanced",{"title":3157,"path":3158,"stem":3159},"Позиціонування в CSS. Z-index. Stacking Context","\u002Fhtml-css\u002Fcss-positioning","12.html-css\u002F17.css-positioning",{"title":3161,"path":3162,"stem":3163},"CSS Анімації та Переходи","\u002Fhtml-css\u002Fcss-animations-transitions","12.html-css\u002F18.css-animations-transitions",{"title":3165,"path":3166,"stem":3167},"Адаптивний дизайн. Media Queries. Частина 1","\u002Fhtml-css\u002Fcss-responsive-media-queries","12.html-css\u002F19.css-responsive-media-queries",{"title":3169,"path":3170,"stem":3171},"Адаптивний дизайн. Частина 2: clamp(), Container Queries, @layer","\u002Fhtml-css\u002Fcss-responsive-advanced","12.html-css\u002F20.css-responsive-advanced",{"title":3173,"path":3174,"stem":3175},"CSS Custom Properties. Методології. Сучасний CSS","\u002Fhtml-css\u002Fcss-variables-methodologies","12.html-css\u002F21.css-variables-methodologies",{"title":3177,"path":3178,"stem":3179},"Сучасний CSS 2023–2025: Нові можливості","\u002Fhtml-css\u002Fcss-modern-features","12.html-css\u002F22.css-modern-features",{"title":3181,"path":3182,"stem":3183},"CSS Nesting, @layer, @scope та @property: нативний препроцесор","\u002Fhtml-css\u002F22a.css-nesting-modern-syntax","12.html-css\u002F22a.css-nesting-modern-syntax",{"title":3185,"path":3186,"stem":3187},"CSS для форм та інтерактивних станів","\u002Fhtml-css\u002Fcss-forms-interactive-states","12.html-css\u002F23.css-forms-interactive-states",{"title":3189,"path":3190,"stem":3191},"Доступність у CSS (CSS Accessibility)","\u002Fhtml-css\u002Fcss-accessibility","12.html-css\u002F24.css-accessibility",{"title":3193,"path":3194,"stem":3195},"CSS-функції та сучасні sizing primitives","\u002Fhtml-css\u002Fcss-functions-sizing","12.html-css\u002F25.css-functions-sizing",{"title":3197,"path":3198,"stem":3199},"Rendering Pipeline і CSS Performance","\u002Fhtml-css\u002Fcss-rendering-performance","12.html-css\u002F26.css-rendering-performance",{"title":3201,"path":3202,"stem":3203},"CSS Best Practices: типові ситуації та правильні рішення","\u002Fhtml-css\u002Fcss-best-practices","12.html-css\u002F27.css-best-practices",{"title":3205,"path":3206,"stem":3207,"children":3208,"page":59},"AWS","\u002Faws","13.aws",[3209,3213,3217,3221,3225,3229,3233,3237,3241,3245,3249,3253,3257,3261,3265,3269,3273,3277],{"title":3210,"path":3211,"stem":3212},"Реєстрація AWS акаунту та студентські програми","\u002Faws\u002Faccount-registration","13.aws\u002F00.account-registration",{"title":3214,"path":3215,"stem":3216},"Вступ до хмарних обчислень та AWS","\u002Faws\u002Fintroduction-to-cloud","13.aws\u002F01.introduction-to-cloud",{"title":3218,"path":3219,"stem":3220},"AWS IAM — Identity and Access Management","\u002Faws\u002Fiam","13.aws\u002F02.iam",{"title":3222,"path":3223,"stem":3224},"AWS IAM CLI — Довідник команд","\u002Faws\u002F02a.iam-doc","13.aws\u002F02a.iam-doc",{"title":3226,"path":3227,"stem":3228},"Docker та контейнеризація в AWS — ECR, ECS та Fargate","\u002Faws\u002Fdocker-ecs","13.aws\u002F03.docker-ecs",{"title":3230,"path":3231,"stem":3232},"AWS ECR \u002F ECS CLI — Довідник команд","\u002Faws\u002F03a.docker-ecs-doc","13.aws\u002F03a.docker-ecs-doc",{"title":3234,"path":3235,"stem":3236},"Amazon EC2 — Elastic Compute Cloud","\u002Faws\u002Fec2","13.aws\u002F04.ec2",{"title":3238,"path":3239,"stem":3240},"AWS EC2 CLI — Довідник команд","\u002Faws\u002F04a.ec2-doc","13.aws\u002F04a.ec2-doc",{"title":3242,"path":3243,"stem":3244},"Elastic Load Balancing та Auto Scaling","\u002Faws\u002Falb-asg","13.aws\u002F05.alb-asg",{"title":3246,"path":3247,"stem":3248},"Amazon S3 — Simple Storage Service","\u002Faws\u002Fs3","13.aws\u002F06.s3",{"title":3250,"path":3251,"stem":3252},"Amazon CloudFront — Content Delivery Network","\u002Faws\u002Fcloudfront","13.aws\u002F07.cloudfront",{"title":3254,"path":3255,"stem":3256},"Amazon RDS — Relational Database Service","\u002Faws\u002Frds","13.aws\u002F08.rds",{"title":3258,"path":3259,"stem":3260},"Amazon DynamoDB — NoSQL Database","\u002Faws\u002Fdynamodb","13.aws\u002F09.dynamodb",{"title":3262,"path":3263,"stem":3264},"AWS Lambda та Serverless Compute","\u002Faws\u002Flambda","13.aws\u002F10.lambda",{"title":3266,"path":3267,"stem":3268},"Amazon Bedrock - Foundation Models, RAG та Agents","\u002Faws\u002Fbedrock","13.aws\u002F22.bedrock",{"title":3270,"path":3271,"stem":3272},"Amazon Rekognition - Комп'ютерний зір","\u002Faws\u002Frekognition","13.aws\u002F23.rekognition",{"title":3274,"path":3275,"stem":3276},"Amazon Textract - Інтелектуальний аналіз документів","\u002Faws\u002Ftextract","13.aws\u002F24.textract",{"title":3278,"path":3279,"stem":3280},"Amazon Polly, Transcribe, Comprehend та Translate","\u002Faws\u002Faudio-nlp-services","13.aws\u002F25.audio-nlp-services",{"title":3282,"path":3283,"stem":3284,"children":3285,"page":59},"Tailwind","\u002Ftailwind","21.tailwind",[3286,3290,3294,3298,3302,3306,3310,3314,3318,3322,3326,3330],{"title":3287,"path":3288,"stem":3289},"Що таке Tailwind CSS і навіщо він потрібен","\u002Ftailwind\u002Ftailwind-intro-philosophy","21.tailwind\u002F01.tailwind-intro-philosophy",{"title":3291,"path":3292,"stem":3293},"Встановлення та налаштування Tailwind CSS v4","\u002Ftailwind\u002Ftailwind-installation-setup","21.tailwind\u002F02.tailwind-installation-setup",{"title":3295,"path":3296,"stem":3297},"Utility-класи: основи та система Tailwind","\u002Ftailwind\u002Ftailwind-utility-classes-core","21.tailwind\u002F03.tailwind-utility-classes-core",{"title":3299,"path":3300,"stem":3301},"Layout: Flexbox та Grid через Tailwind","\u002Ftailwind\u002Ftailwind-flexbox-grid","21.tailwind\u002F04.tailwind-flexbox-grid",{"title":3303,"path":3304,"stem":3305},"Кастомізація теми через @theme у Tailwind v4","\u002Ftailwind\u002Ftailwind-theme-customization","21.tailwind\u002F05.tailwind-theme-customization",{"title":3307,"path":3308,"stem":3309},"Варіанти: hover, focus, responsive, dark mode та нові v4","\u002Ftailwind\u002Ftailwind-variants-states","21.tailwind\u002F06.tailwind-variants-states",{"title":3311,"path":3312,"stem":3313},"Типографіка та система кольорів у Tailwind v4","\u002Ftailwind\u002Ftailwind-typography-colors","21.tailwind\u002F07.tailwind-typography-colors",{"title":3315,"path":3316,"stem":3317},"Компоненти та повторюваність: @apply, @utility та патерни","\u002Ftailwind\u002Ftailwind-components-patterns","21.tailwind\u002F08.tailwind-components-patterns",{"title":3319,"path":3320,"stem":3321},"Темна тема та система дизайн-токенів у Tailwind v4","\u002Ftailwind\u002Ftailwind-dark-mode-theming","21.tailwind\u002F09.tailwind-dark-mode-theming",{"title":3323,"path":3324,"stem":3325},"Довільні значення та контейнерні запити у Tailwind v4","\u002Ftailwind\u002Ftailwind-arbitrary-container-queries","21.tailwind\u002F10.tailwind-arbitrary-container-queries",{"title":3327,"path":3328,"stem":3329},"Анімації, трансформації та 3D у Tailwind v4","\u002Ftailwind\u002Ftailwind-animations-transforms","21.tailwind\u002F11.tailwind-animations-transforms",{"title":3331,"path":3332,"stem":3333},"Tailwind CLI, PostCSS та інтеграція з фреймворками","\u002Ftailwind\u002Ftailwind-cli-tooling","21.tailwind\u002F12.tailwind-cli-tooling",{"title":3335,"path":3336,"stem":3337},"Тестування компонентів діаграм","\u002Ftest-components","98.test-components",{"id":3339,"title":3254,"body":3340,"description":19391,"extension":19392,"links":19393,"meta":19394,"navigation":3459,"path":3255,"seo":19395,"stem":3256,"__hash__":19396},"docs\u002F13.aws\u002F08.rds.md",{"type":3341,"value":3342,"toc":19290},"minimark",[3343,3347,3352,3356,3362,3365,3399,3405,3422,3641,3646,3678,3681,3685,3692,3699,3704,3707,3727,3731,3734,3747,3751,3754,3777,3787,3815,3817,3821,3824,3828,3842,3846,3849,3853,3856,3860,3870,3884,3886,3890,3901,3905,3912,3938,3941,3945,3948,3978,3985,4231,4241,4247,4251,4253,4257,4266,4270,4276,4287,4294,4298,4301,4321,4325,4328,4450,4665,4668,4838,4840,4844,4847,4851,4931,4935,4938,4952,4957,4982,5184,5197,5201,5204,5339,5341,5345,5348,5352,5365,5372,5376,5383,5405,5410,5417,5509,5513,5519,5549,5571,5575,5578,5609,5613,5619,5645,5648,5830,5834,5845,5849,5856,5859,5873,5876,6005,6009,6012,6040,6046,6048,6052,6055,6059,6062,6092,6096,6103,6107,6110,6121,6125,6132,6134,6138,6143,6147,6150,6164,6168,6175,6195,6307,6312,6394,6398,6405,6429,6431,6435,6445,6455,6544,6549,6569,6575,6577,6581,6584,6588,6646,6650,7007,7011,7021,7211,7253,7257,7339,7356,7360,7366,7368,7372,7375,7528,7532,7559,7561,7565,8779,8781,8785,8789,8994,8998,9419,9423,9441,9770,9774,10701,10707,10709,10713,10765,11560,11562,11566,11640,11689,11691,11695,12020,12079,12081,12085,12153,12191,12193,12197,12218,12691,12695,12697,12701,12704,12709,12713,12716,12871,12875,13862,13866,13875,13879,13884,13889,13913,13917,13920,13940,13944,13947,13951,13954,14006,14010,14122,14156,14160,14164,14329,14333,14336,14409,14413,14416,14432,14435,14442,15150,15156,15455,15459,15468,15472,15475,15480,15500,15504,15597,15601,15604,15608,15611,16082,16086,16093,16099,16312,16316,16323,16382,16389,16395,16447,16456,16460,16469,16473,16479,16509,16512,16516,16520,16523,16816,16820,16823,16828,17020,17023,17035,17039,17046,17056,17203,17250,17256,17260,17262,17269,17273,17276,17290,17428,17432,17436,17439,18097,18101,18104,18107,18954,18958,18961,19030,19094,19110,19114,19197,19199,19203,19207,19213,19219,19223,19232,19250,19254,19268,19286],[3344,3345,3254],"h1",{"id":3346},"amazon-rds-relational-database-service",[3348,3349,3351],"h2",{"id":3350},"що-таке-rds-і-навіщо-він-потрібен","Що таке RDS і навіщо він потрібен",[3353,3354,3355],"p",{},"Уявіть, що ви задеплоїли .NET Web API на EC2. Застосунок працює, але потребує реляційну базу даних — PostgreSQL або SQL Server. Найпростіший підхід: встановити PostgreSQL прямо на той самий EC2 instance. Це спрацює для домашнього проєкту, але в production це архітектурна помилка.",[3353,3357,3358],{},[3359,3360,3361],"strong",{},"Чому не можна просто встановити PostgreSQL на EC2?",[3353,3363,3364],{},"Управління базою даних у production — це окрема робота на повну ставку:",[3366,3367,3368,3375,3381,3387,3393],"ul",{},[3369,3370,3371,3374],"li",{},[3359,3372,3373],{},"Резервні копії:"," хто і коли робить backup? Як перевірити що він валідний? Як відновити базу стан на конкретний момент часу (point-in-time recovery)?",[3369,3376,3377,3380],{},[3359,3378,3379],{},"Патчі безпеки:"," PostgreSQL регулярно випускає виправлення вразливостей. Хто стежить за оновленнями і застосовує їх без даунтайму?",[3369,3382,3383,3386],{},[3359,3384,3385],{},"Висока доступність:"," якщо EC2 instance впаде — база недоступна, застосунок не працює. Як зробити автоматичний failover?",[3369,3388,3389,3392],{},[3359,3390,3391],{},"Масштабування:"," навантаження зросло, потрібно більше ресурсів. Як перейти на більший сервер без втрати даних і даунтайму?",[3369,3394,3395,3398],{},[3359,3396,3397],{},"Моніторинг:"," як відслідковувати slow queries, deadlocks, вичерпання з'єднань?",[3353,3400,3401,3404],{},[3359,3402,3403],{},"Amazon RDS (Relational Database Service)"," — це керований сервіс, який бере на себе всі ці операційні турботи. Ви отримуєте повноцінну реляційну базу даних, але AWS відповідає за: автоматичні бекапи, патчі ОС та СУБД, моніторинг, автоматичний failover та масштабування інфраструктури.",[3406,3407,3408,3409,3412,3413,3417,3418,3421],"note",{},"Важлива різниця: RDS ",[3359,3410,3411],{},"не є serverless"," (є Aurora Serverless, але це окремо). RDS — це керований сервіс на виділеному instance. Ви все одно обираєте розмір instance (",[3414,3415,3416],"code",{},"db.t3.medium",", ",[3414,3419,3420],{},"db.r6g.xlarge","), але не займаєтесь адмініструванням ОС та СУБД.",[3423,3424,3425],"plant-uml",{},[3426,3427,3432],"pre",{"className":3428,"code":3429,"language":3430,"meta":3431,"style":3431},"language-plantuml shiki shiki-themes light-plus dark-plus dark-plus","@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Без RDS: самостійне управління\" as SELF #fee2e2 {\n    node \"EC2 Instance\" as EC2_SELF {\n        component \".NET API\" as API_SELF\n        component \"PostgreSQL\" as PG_SELF\n    }\n    note bottom of EC2_SELF #fee2e2\n      ❌ Бекапи вручну\n      ❌ Патчі вручну\n      ❌ Один instance = SPOF\n      ❌ Failover вручну\n    end note\n}\n\npackage \"З RDS: керований сервіс\" as MANAGED #dcfce7 {\n    node \"EC2 Instance\" as EC2_API {\n        component \".NET API\" as API\n    }\n    database \"RDS\\nPostgreSQL\" as RDS_PRI #dbeafe\n    database \"RDS Standby\\n(Multi-AZ)\" as RDS_STB #e5e7eb\n\n    EC2_API --> RDS_PRI : порт 5432\\n(TCP всередині VPC)\n    RDS_PRI .right.> RDS_STB : синхронна\\nреплікація\n\n    note bottom of RDS_PRI #dcfce7\n      ✅ Автобекапи щодня\n      ✅ Point-in-time recovery\n      ✅ Автопатчі ОС\u002FPostgreSQL\n      ✅ Failover ~60 сек (Multi-AZ)\n    end note\n}\n@enduml\n","plantuml","",[3414,3433,3434,3442,3448,3454,3461,3467,3473,3479,3485,3491,3497,3503,3509,3515,3521,3527,3533,3538,3544,3550,3556,3561,3567,3573,3578,3584,3590,3595,3601,3607,3613,3619,3625,3630,3635],{"__ignoreMap":3431},[3435,3436,3439],"span",{"class":3437,"line":3438},"line",1,[3435,3440,3441],{},"@startuml\n",[3435,3443,3445],{"class":3437,"line":3444},2,[3435,3446,3447],{},"skinparam style plain\n",[3435,3449,3451],{"class":3437,"line":3450},3,[3435,3452,3453],{},"skinparam backgroundColor #ffffff\n",[3435,3455,3457],{"class":3437,"line":3456},4,[3435,3458,3460],{"emptyLinePlaceholder":3459},true,"\n",[3435,3462,3464],{"class":3437,"line":3463},5,[3435,3465,3466],{},"package \"Без RDS: самостійне управління\" as SELF #fee2e2 {\n",[3435,3468,3470],{"class":3437,"line":3469},6,[3435,3471,3472],{},"    node \"EC2 Instance\" as EC2_SELF {\n",[3435,3474,3476],{"class":3437,"line":3475},7,[3435,3477,3478],{},"        component \".NET API\" as API_SELF\n",[3435,3480,3482],{"class":3437,"line":3481},8,[3435,3483,3484],{},"        component \"PostgreSQL\" as PG_SELF\n",[3435,3486,3488],{"class":3437,"line":3487},9,[3435,3489,3490],{},"    }\n",[3435,3492,3494],{"class":3437,"line":3493},10,[3435,3495,3496],{},"    note bottom of EC2_SELF #fee2e2\n",[3435,3498,3500],{"class":3437,"line":3499},11,[3435,3501,3502],{},"      ❌ Бекапи вручну\n",[3435,3504,3506],{"class":3437,"line":3505},12,[3435,3507,3508],{},"      ❌ Патчі вручну\n",[3435,3510,3512],{"class":3437,"line":3511},13,[3435,3513,3514],{},"      ❌ Один instance = SPOF\n",[3435,3516,3518],{"class":3437,"line":3517},14,[3435,3519,3520],{},"      ❌ Failover вручну\n",[3435,3522,3524],{"class":3437,"line":3523},15,[3435,3525,3526],{},"    end note\n",[3435,3528,3530],{"class":3437,"line":3529},16,[3435,3531,3532],{},"}\n",[3435,3534,3536],{"class":3437,"line":3535},17,[3435,3537,3460],{"emptyLinePlaceholder":3459},[3435,3539,3541],{"class":3437,"line":3540},18,[3435,3542,3543],{},"package \"З RDS: керований сервіс\" as MANAGED #dcfce7 {\n",[3435,3545,3547],{"class":3437,"line":3546},19,[3435,3548,3549],{},"    node \"EC2 Instance\" as EC2_API {\n",[3435,3551,3553],{"class":3437,"line":3552},20,[3435,3554,3555],{},"        component \".NET API\" as API\n",[3435,3557,3559],{"class":3437,"line":3558},21,[3435,3560,3490],{},[3435,3562,3564],{"class":3437,"line":3563},22,[3435,3565,3566],{},"    database \"RDS\\nPostgreSQL\" as RDS_PRI #dbeafe\n",[3435,3568,3570],{"class":3437,"line":3569},23,[3435,3571,3572],{},"    database \"RDS Standby\\n(Multi-AZ)\" as RDS_STB #e5e7eb\n",[3435,3574,3576],{"class":3437,"line":3575},24,[3435,3577,3460],{"emptyLinePlaceholder":3459},[3435,3579,3581],{"class":3437,"line":3580},25,[3435,3582,3583],{},"    EC2_API --> RDS_PRI : порт 5432\\n(TCP всередині VPC)\n",[3435,3585,3587],{"class":3437,"line":3586},26,[3435,3588,3589],{},"    RDS_PRI .right.> RDS_STB : синхронна\\nреплікація\n",[3435,3591,3593],{"class":3437,"line":3592},27,[3435,3594,3460],{"emptyLinePlaceholder":3459},[3435,3596,3598],{"class":3437,"line":3597},28,[3435,3599,3600],{},"    note bottom of RDS_PRI #dcfce7\n",[3435,3602,3604],{"class":3437,"line":3603},29,[3435,3605,3606],{},"      ✅ Автобекапи щодня\n",[3435,3608,3610],{"class":3437,"line":3609},30,[3435,3611,3612],{},"      ✅ Point-in-time recovery\n",[3435,3614,3616],{"class":3437,"line":3615},31,[3435,3617,3618],{},"      ✅ Автопатчі ОС\u002FPostgreSQL\n",[3435,3620,3622],{"class":3437,"line":3621},32,[3435,3623,3624],{},"      ✅ Failover ~60 сек (Multi-AZ)\n",[3435,3626,3628],{"class":3437,"line":3627},33,[3435,3629,3526],{},[3435,3631,3633],{"class":3437,"line":3632},34,[3435,3634,3532],{},[3435,3636,3638],{"class":3437,"line":3637},35,[3435,3639,3640],{},"@enduml\n",[3353,3642,3643],{},[3359,3644,3645],{},"RDS підтримує шість СУБД:",[3366,3647,3648,3654,3660,3666,3672],{},[3369,3649,3650,3653],{},[3359,3651,3652],{},"PostgreSQL"," — найпопулярніший вибір для .NET з EF Core. Відкритий, потужний, JSON підтримка",[3369,3655,3656,3659],{},[3359,3657,3658],{},"MySQL \u002F MariaDB"," — широко використовується у legacy проєктах",[3369,3661,3662,3665],{},[3359,3663,3664],{},"SQL Server"," — для застосунків на .NET Framework або зі специфічними MSSQL features",[3369,3667,3668,3671],{},[3359,3669,3670],{},"Oracle"," — для enterprise legacy систем",[3369,3673,3674,3677],{},[3359,3675,3676],{},"Amazon Aurora"," — власна СУБД AWS, сумісна з PostgreSQL та MySQL, але з кращою продуктивністю (розглянемо окремо)",[3679,3680],"hr",{},[3348,3682,3684],{"id":3683},"rds-instance-classes-вибір-правильного-сервера-бази","RDS Instance Classes — вибір правильного сервера бази",[3353,3686,3687,3688,3691],{},"Як і EC2, RDS instances поділяються на класи за призначенням. Формат: ",[3414,3689,3690],{},"db.[сімейство][покоління].[розмір]",".",[3353,3693,3694,3695,3698],{},"Префікс ",[3414,3696,3697],{},"db."," відрізняє RDS instances від EC2 instances, хоча логіка сімейств аналогічна.",[3700,3701,3703],"h3",{"id":3702},"standard-general-purpose-dbm-класи","Standard (General Purpose) — db.m класи",[3353,3705,3706],{},"Збалансоване CPU та RAM. Підходить для більшості production баз даних.",[3366,3708,3709,3715,3721],{},[3369,3710,3711,3714],{},[3414,3712,3713],{},"db.m6g.large"," — 2 vCPU, 8 GB RAM ← типовий production PostgreSQL для середнього навантаження",[3369,3716,3717,3720],{},[3414,3718,3719],{},"db.m6g.xlarge"," — 4 vCPU, 16 GB RAM",[3369,3722,3723,3726],{},[3414,3724,3725],{},"db.m6g.2xlarge"," — 8 vCPU, 32 GB RAM",[3700,3728,3730],{"id":3729},"memory-optimized-dbr-та-dbx-класи","Memory Optimized — db.r та db.x класи",[3353,3732,3733],{},"Більше RAM відносно CPU. Для баз даних, де велика частина робочого набору даних (working set) повинна поміщатись у RAM для швидкого доступу.",[3366,3735,3736,3742],{},[3369,3737,3738,3741],{},[3414,3739,3740],{},"db.r6g.large"," — 2 vCPU, 16 GB RAM ← якщо часто бракує shared_buffers",[3369,3743,3744,3746],{},[3414,3745,3420],{}," — 4 vCPU, 32 GB RAM",[3700,3748,3750],{"id":3749},"burstable-dbt-класи","Burstable — db.t класи",[3353,3752,3753],{},"Аналог EC2 t-сімейства з CPU Credits. Для dev\u002Ftest середовищ та малонавантажених застосунків.",[3366,3755,3756,3766,3772],{},[3369,3757,3758,3761,3762,3765],{},[3414,3759,3760],{},"db.t3.micro"," — 2 vCPU, 1 GB RAM ← ",[3359,3763,3764],{},"Free Tier",": 750 год\u002Fмісяць на 12 місяців",[3369,3767,3768,3771],{},[3414,3769,3770],{},"db.t3.small"," — 2 vCPU, 2 GB RAM",[3369,3773,3774,3776],{},[3414,3775,3416],{}," — 2 vCPU, 4 GB RAM ← мінімум для реального навчального проєкту",[3778,3779,3780,3783,3784,3786],"tip",{},[3359,3781,3782],{},"Free Tier:"," ",[3414,3785,3760],{}," з PostgreSQL або MySQL — 750 годин на місяць безкоштовно протягом першого року. Плюс 20 GB SSD storage та 20 GB backup storage. Достатньо для навчального проєкту.",[3788,3789,3790,3799,3807],"card-group",{},[3791,3792,3795,3796,3798],"card",{"icon":3793,"title":3794},"i-heroicons-bolt","db.t — Burstable (Free Tier)","CPU Credits для піків. ",[3414,3797,3760],{}," — Free Tier. Ідеально для розробки, навчання, стейджингу.",[3791,3800,3803,3804,3806],{"icon":3801,"title":3802},"i-heroicons-server","db.m — General Purpose","Фіксований CPU. ",[3414,3805,3713],{}," — production база з передбачуваним навантаженням.",[3791,3808,3811,3812,3814],{"icon":3809,"title":3810},"i-heroicons-circle-stack","db.r — Memory Optimized","Більше RAM. ",[3414,3813,3740],{}," — бази з великим робочим набором даних, аналітика.",[3679,3816],{},[3348,3818,3820],{"id":3819},"rds-storage-types-класи-накопичувачів-та-автоматичне-масштабування-сховища","RDS Storage Types — класи накопичувачів та автоматичне масштабування сховища",[3353,3822,3823],{},"Amazon RDS використовує мережеву інфраструктуру зберігання даних Amazon EBS (Elastic Block Store) і пропонує три основні типи дискових накопичувачів:",[3700,3825,3827],{"id":3826},"_1-general-purpose-ssd-gp2-та-gp3","1. General Purpose SSD (gp2 та gp3)",[3366,3829,3830,3836],{},[3369,3831,3832,3835],{},[3359,3833,3834],{},"gp2:"," Продуктивність диска безпосередньо залежить від об'єму виділеного простору (базова швидкість становить 3 IOPS на кожен гігабайт). Для невеликих баз даних продуктивність може бути низькою, якщо не накопичено кредити Bursting IOPS.",[3369,3837,3838,3841],{},[3359,3839,3840],{},"gp3:"," Сучасніший тип накопичувачів, який дозволяє незалежно налаштовувати об'єм сховища, кількість операцій введення-виведення за секунду (IOPS) та пропускну здатність (Throughput). За замовчуванням надається базова швидкість 3000 IOPS та 125 MB\u002Fs без додаткової оплати, яку можна масштабувати окремо від об'єму диска.",[3700,3843,3845],{"id":3844},"_2-provisioned-iops-ssd-io1-та-io2","2. Provisioned IOPS SSD (io1 та io2)",[3353,3847,3848],{},"Призначений для високонавантажених баз даних з інтенсивним I\u002FO, де критично важливо забезпечити стабільний час відгуку (latencies) та гарантовану продуктивність. Дозволяє виділяти до десятків тисяч IOPS на інстанс.",[3700,3850,3852],{"id":3851},"_3-magnetic-застарілий-тип","3. Magnetic (Застарілий тип)",[3353,3854,3855],{},"Використовує магнітні жорсткі диски. Має низьку продуктивність і рекомендується лише для архівних даних чи рідко використовуваних тестових середовищ із мінімальним бюджетом.",[3700,3857,3859],{"id":3858},"автоматичне-масштабування-сховища-storage-auto-scaling","Автоматичне масштабування сховища (Storage Auto-scaling)",[3353,3861,3862,3863,3866,3867,3691],{},"Для запобігання ситуації вичерпання дискового простору (що призводить до переведення бази даних у режим ",[3414,3864,3865],{},"read-only"," або аварійної зупинки СУБД) Amazon RDS підтримує функцію ",[3359,3868,3869],{},"Storage Auto-scaling",[3366,3871,3872,3878],{},[3369,3873,3874,3877],{},[3359,3875,3876],{},"Принцип роботи:"," При наближенні обсягу вільного місця до критичної межі (менше 10% від загального обсягу) та тривалості дефіциту понад 5 хвилин, сервіс автоматично збільшує розмір дискового тома на певну величину (мінімально на 10% від поточного об'єму або на 5 GB).",[3369,3879,3880,3883],{},[3359,3881,3882],{},"Обмеження:"," Користувач може встановити максимальний ліміт масштабування (Maximum Storage Threshold) для контролю витрат. Наступне автоматичне масштабування може відбутися не раніше ніж через 6 годин після попереднього збільшення.",[3679,3885],{},[3348,3887,3889],{"id":3888},"резервування-у-кількох-зонах-доступності-multi-az-як-засіб-забезпечення-високої-доступності","Резервування у кількох зонах доступності (Multi-AZ) як засіб забезпечення високої доступності",[3353,3891,3892,3895,3896,3900],{},[3359,3893,3894],{},"Режим високої доступності Multi-AZ (Multi-Availability Zone)"," є основним архітектурним шаблоном для забезпечення відмовостійкості реляційних баз даних у хмарі AWS. При активації цього режиму Amazon RDS автоматично створює та підтримує надлишкову, синхронну копію бази даних (резервний інстанс або ",[3897,3898,3899],"em",{},"standby instance",") в іншій зоні доступності (Availability Zone, AZ) у межах того самого регіону.",[3700,3902,3904],{"id":3903},"механізм-синхронної-реплікації","Механізм синхронної реплікації",[3353,3906,3907,3908,3911],{},"На відміну від логічної реплікації СУБД, у класичному режимі Multi-AZ використовується ",[3359,3909,3910],{},"синхронна реплікація на рівні фізичних блоків сховища"," (storage-level replication). Процес запису даних відбувається за такою схемою:",[3913,3914,3915,3926,3929,3932,3935],"ol",{},[3369,3916,3917,3918,3921,3922,3925],{},"Клієнтський застосунок надсилає транзакцію на запис (наприклад, команду ",[3414,3919,3920],{},"INSERT"," чи ",[3414,3923,3924],{},"UPDATE",") на основний інстанс (primary).",[3369,3927,3928],{},"Запит обробляється рушієм бази даних, і зміни записуються на локальний дисковий том (Amazon EBS) первинного інстансу.",[3369,3930,3931],{},"Одночасно ці блоки даних дублюються через мережевий канал низької затримки на дисковий том резервного інстансу в сусідній зоні доступності.",[3369,3933,3934],{},"Основний інстанс очікує підтвердження успішного запису блоків від резервного тому.",[3369,3936,3937],{},"Після отримання підтвердження транзакція фіксується (commit), і клієнтський застосунок отримує відповідь про успішне виконання операції.",[3353,3939,3940],{},"Завдяки синхронному характеру реплікації резервна копія завжди перебуває в ідентичному стані з основною базою даних. Це гарантує збереження даних без втрат (Recovery Point Objective, RPO = 0) у разі раптового виходу з ладу первинного вузла.",[3700,3942,3944],{"id":3943},"процес-аварійного-перемикання-failover","Процес аварійного перемикання (Failover)",[3353,3946,3947],{},"У разі виникнення позаштатних ситуацій (аварія обладнання, втрата живлення у зоні доступності, втрата мережевого зв'язку або планове технічне обслуговування) Amazon RDS автоматично ініціює процес аварійного перемикання (failover). Цей процес є прозорим для клієнтів і складається з таких етапів:",[3913,3949,3950,3956,3966,3972],{},[3369,3951,3952,3955],{},[3359,3953,3954],{},"Виявлення збою:"," Внутрішні системи моніторингу AWS фіксують втрату працездатності первинного інстансу.",[3369,3957,3958,3961,3962,3965],{},[3359,3959,3960],{},"DNS-мутація:"," Amazon RDS змінює запис CNAME у внутрішній службі DNS (Amazon Route 53) для endpoint бази даних (наприклад, ",[3414,3963,3964],{},"mydb.xxx.eu-central-1.rds.amazonaws.com","), перенаправляючи його на IP-адресу резервного інстансу.",[3369,3967,3968,3971],{},[3359,3969,3970],{},"Активація резервного вузла:"," Резервний інстанс переводиться зі статусу standby у статус primary, починаючи приймати нові з'єднання.",[3369,3973,3974,3977],{},[3359,3975,3976],{},"Реконструкція надлишковості:"," Після відновлення працездатності пошкодженої зони доступності (або створення нового інстансу в іншій AZ) Amazon RDS автоматично створює новий standby інстанс і відновлює синхронну реплікацію.",[3353,3979,3980,3981,3984],{},"Час аварійного перемикання зазвичай становить ",[3359,3982,3983],{},"від 60 до 120 секунд",". Під час цього інтервалу застосунок отримуватиме помилки з'єднання (Connection Errors), тому в архітектурі підключення життєво важливо використовувати механізми автоматичного повторення спроб (Retries).",[3423,3986,3987],{},[3426,3988,3990],{"className":3428,"code":3989,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam sequence {\n    ArrowColor #374151\n    ParticipantBorderColor #374151\n    ParticipantBackgroundColor #f9fafb\n    NoteBackgroundColor #dcfce7\n    NoteBorderColor #16a34a\n    LifeLineBorderColor #9ca3af\n}\n\ntitle Multi-AZ Failover: автоматичне перемикання\n\nparticipant \".NET API\\n(EC2)\" as APP\nparticipant \"RDS Endpoint\\nmydb.xxx.eu-central-1.rds.amazonaws.com\" as DNS\nparticipant \"Primary\\n(eu-central-1a)\" as PRI\nparticipant \"Standby\\n(eu-central-1b)\" as STB\n\n== Нормальна робота ==\nAPP -> DNS : з'єднання\nDNS -> PRI : маршрутизація на primary\nPRI -> STB : синхронна реплікація кожної транзакції\nPRI --> APP : відповідь\n\n== Збій primary ==\nPRI -> PRI : ❌ Hardware failure\nnote over PRI #fee2e2\n  Primary недоступний\nend note\n\nDNS -> DNS : Failover: перемикання DNS\nnote over DNS #fef3c7\n  ~60-120 секунд\n  Ваш застосунок отримає\n  connection error → retry\nend note\n\nAPP -> DNS : повторне з'єднання\nDNS -> STB : маршрутизація на колишній standby\nSTB --> APP : відповідь ✅\n\nnote over STB #dcfce7\n  Колишній standby став primary.\n  RDS автоматично запустить\n  новий standby в іншій AZ.\nend note\n@enduml\n",[3414,3991,3992,3996,4000,4004,4009,4014,4019,4024,4029,4034,4039,4043,4047,4052,4056,4061,4066,4071,4076,4080,4085,4090,4095,4100,4105,4109,4114,4119,4124,4129,4134,4138,4143,4148,4153,4158,4164,4169,4174,4180,4186,4192,4197,4203,4209,4215,4221,4226],{"__ignoreMap":3431},[3435,3993,3994],{"class":3437,"line":3438},[3435,3995,3441],{},[3435,3997,3998],{"class":3437,"line":3444},[3435,3999,3447],{},[3435,4001,4002],{"class":3437,"line":3450},[3435,4003,3453],{},[3435,4005,4006],{"class":3437,"line":3456},[3435,4007,4008],{},"skinparam sequence {\n",[3435,4010,4011],{"class":3437,"line":3463},[3435,4012,4013],{},"    ArrowColor #374151\n",[3435,4015,4016],{"class":3437,"line":3469},[3435,4017,4018],{},"    ParticipantBorderColor #374151\n",[3435,4020,4021],{"class":3437,"line":3475},[3435,4022,4023],{},"    ParticipantBackgroundColor #f9fafb\n",[3435,4025,4026],{"class":3437,"line":3481},[3435,4027,4028],{},"    NoteBackgroundColor #dcfce7\n",[3435,4030,4031],{"class":3437,"line":3487},[3435,4032,4033],{},"    NoteBorderColor #16a34a\n",[3435,4035,4036],{"class":3437,"line":3493},[3435,4037,4038],{},"    LifeLineBorderColor #9ca3af\n",[3435,4040,4041],{"class":3437,"line":3499},[3435,4042,3532],{},[3435,4044,4045],{"class":3437,"line":3505},[3435,4046,3460],{"emptyLinePlaceholder":3459},[3435,4048,4049],{"class":3437,"line":3511},[3435,4050,4051],{},"title Multi-AZ Failover: автоматичне перемикання\n",[3435,4053,4054],{"class":3437,"line":3517},[3435,4055,3460],{"emptyLinePlaceholder":3459},[3435,4057,4058],{"class":3437,"line":3523},[3435,4059,4060],{},"participant \".NET API\\n(EC2)\" as APP\n",[3435,4062,4063],{"class":3437,"line":3529},[3435,4064,4065],{},"participant \"RDS Endpoint\\nmydb.xxx.eu-central-1.rds.amazonaws.com\" as DNS\n",[3435,4067,4068],{"class":3437,"line":3535},[3435,4069,4070],{},"participant \"Primary\\n(eu-central-1a)\" as PRI\n",[3435,4072,4073],{"class":3437,"line":3540},[3435,4074,4075],{},"participant \"Standby\\n(eu-central-1b)\" as STB\n",[3435,4077,4078],{"class":3437,"line":3546},[3435,4079,3460],{"emptyLinePlaceholder":3459},[3435,4081,4082],{"class":3437,"line":3552},[3435,4083,4084],{},"== Нормальна робота ==\n",[3435,4086,4087],{"class":3437,"line":3558},[3435,4088,4089],{},"APP -> DNS : з'єднання\n",[3435,4091,4092],{"class":3437,"line":3563},[3435,4093,4094],{},"DNS -> PRI : маршрутизація на primary\n",[3435,4096,4097],{"class":3437,"line":3569},[3435,4098,4099],{},"PRI -> STB : синхронна реплікація кожної транзакції\n",[3435,4101,4102],{"class":3437,"line":3575},[3435,4103,4104],{},"PRI --> APP : відповідь\n",[3435,4106,4107],{"class":3437,"line":3580},[3435,4108,3460],{"emptyLinePlaceholder":3459},[3435,4110,4111],{"class":3437,"line":3586},[3435,4112,4113],{},"== Збій primary ==\n",[3435,4115,4116],{"class":3437,"line":3592},[3435,4117,4118],{},"PRI -> PRI : ❌ Hardware failure\n",[3435,4120,4121],{"class":3437,"line":3597},[3435,4122,4123],{},"note over PRI #fee2e2\n",[3435,4125,4126],{"class":3437,"line":3603},[3435,4127,4128],{},"  Primary недоступний\n",[3435,4130,4131],{"class":3437,"line":3609},[3435,4132,4133],{},"end note\n",[3435,4135,4136],{"class":3437,"line":3615},[3435,4137,3460],{"emptyLinePlaceholder":3459},[3435,4139,4140],{"class":3437,"line":3621},[3435,4141,4142],{},"DNS -> DNS : Failover: перемикання DNS\n",[3435,4144,4145],{"class":3437,"line":3627},[3435,4146,4147],{},"note over DNS #fef3c7\n",[3435,4149,4150],{"class":3437,"line":3632},[3435,4151,4152],{},"  ~60-120 секунд\n",[3435,4154,4155],{"class":3437,"line":3637},[3435,4156,4157],{},"  Ваш застосунок отримає\n",[3435,4159,4161],{"class":3437,"line":4160},36,[3435,4162,4163],{},"  connection error → retry\n",[3435,4165,4167],{"class":3437,"line":4166},37,[3435,4168,4133],{},[3435,4170,4172],{"class":3437,"line":4171},38,[3435,4173,3460],{"emptyLinePlaceholder":3459},[3435,4175,4177],{"class":3437,"line":4176},39,[3435,4178,4179],{},"APP -> DNS : повторне з'єднання\n",[3435,4181,4183],{"class":3437,"line":4182},40,[3435,4184,4185],{},"DNS -> STB : маршрутизація на колишній standby\n",[3435,4187,4189],{"class":3437,"line":4188},41,[3435,4190,4191],{},"STB --> APP : відповідь ✅\n",[3435,4193,4195],{"class":3437,"line":4194},42,[3435,4196,3460],{"emptyLinePlaceholder":3459},[3435,4198,4200],{"class":3437,"line":4199},43,[3435,4201,4202],{},"note over STB #dcfce7\n",[3435,4204,4206],{"class":3437,"line":4205},44,[3435,4207,4208],{},"  Колишній standby став primary.\n",[3435,4210,4212],{"class":3437,"line":4211},45,[3435,4213,4214],{},"  RDS автоматично запустить\n",[3435,4216,4218],{"class":3437,"line":4217},46,[3435,4219,4220],{},"  новий standby в іншій AZ.\n",[3435,4222,4224],{"class":3437,"line":4223},47,[3435,4225,4133],{},[3435,4227,4229],{"class":3437,"line":4228},48,[3435,4230,3640],{},[3353,4232,4233,4236,4237,4240],{},[3359,4234,4235],{},"Важливо:"," Резервний інстанс (standby) функціонує виключно у гарячому резерві. Він не має власного мережевого endpoint і не може приймати жодних запитів на читання (",[3414,4238,4239],{},"SELECT","), оскільки його операційна система та процесорні ресурси зарезервовані суто для реплікації блоків.",[3353,4242,4243,4246],{},[3359,4244,4245],{},"Вартість:"," Використання режиму Multi-AZ подвоює як вартість обчислювальних ресурсів (оренда двох інстансів), так і вартість виділеного дискового простору, оскільки сховище дублюється в обох зонах доступності.",[4248,4249,4250],"caution",{},"Для промислових (production) баз даних конфігурація Multi-AZ є обов'язковим стандартом проектування. Для тестових та навчальних середовищ цю опцію доцільно вимикати з метою економії бюджету.",[3679,4252],{},[3348,4254,4256],{"id":4255},"репліки-читання-read-replicas-як-інструмент-горизонтального-масштабування","Репліки читання (Read Replicas) як інструмент горизонтального масштабування",[3353,4258,4259,4260,4263,4264,3691],{},"Для розв'язання проблеми високої інтенсивності операцій читання (наприклад, при переважній більшості GET-запитів над POST\u002FPUT) використовується механізм ",[3359,4261,4262],{},"реплік читання (Read Replicas)",". На відміну від пасивного Multi-AZ standby, кожна репліка читання є повноцінним незалежним інстансом бази даних із власним DNS-endpoint, який доступний для підключення і обробки запитів ",[3414,4265,4239],{},[3700,4267,4269],{"id":4268},"асинхронна-реплікація-та-концепція-узгодженості-в-кінцевому-підсумку","Асинхронна реплікація та концепція узгодженості в кінцевому підсумку",[3353,4271,4272,4273,3691],{},"Реплікація даних між основним інстансом та репліками читання відбувається ",[3359,4274,4275],{},"асинхронно",[3913,4277,4278,4281,4284],{},[3369,4279,4280],{},"Основна база даних фіксує транзакцію і відразу повертає відповідь клієнту, не очікуючи на репліку.",[3369,4282,4283],{},"Транзакційні журнали (Write-Ahead Logs, WAL для PostgreSQL або binary logs для MySQL) надсилаються мережею на репліки.",[3369,4285,4286],{},"Репліки застосовують ці журнали до свого локального сховища.",[3353,4288,4289,4290,4293],{},"Оскільки процес передачі та застосування логів потребує часу, виникає затримка реплікації (Replication Lag), яка за нормальних умов становить від кількох мілісекунд до кількох секунд. Це зумовлює модель ",[3359,4291,4292],{},"узгодженості в кінцевому підсумку (eventual consistency)",": дані, записані на primary, з'являться на репліці з невеликим запізненням. Якщо користувач оновив свій профіль і миттєво зробив GET-запит через репліку, він може на частку секунди побачити старі дані.",[3700,4295,4297],{"id":4296},"архітектура-та-географічне-розташування","Архітектура та географічне розташування",[3353,4299,4300],{},"Amazon RDS підтримує створення до 5 реплік читання для класичних рушіїв СУБД. Вони можуть бути розгорнуті в різних топологіях:",[3366,4302,4303,4309,4315],{},[3369,4304,4305,4308],{},[3359,4306,4307],{},"Внутрішньорегіональні (Intra-Region Replicas):"," Розміщуються у різних зонах доступності того самого регіону для масштабування локальних читань та підвищення стійкості.",[3369,4310,4311,4314],{},[3359,4312,4313],{},"Міжрегіональні (Cross-Region Replicas):"," Розміщуються в інших географічних регіонах AWS. Це дозволяє наблизити дані до користувачів у різних куточках світу (сниження затримки мережі) та спрощує стратегії аварійного відновлення (Disaster Recovery). Крім того, міжрегіональну репліку за необхідності можна підвищити (promote) до статусу незалежної основної бази даних.",[3369,4316,4317,4320],{},[3359,4318,4319],{},"Каскадні репліки (Cascaded Replicas):"," Створення репліки з іншої репліки для зменшення навантаження на основний інстанс (доступно для MySQL та MariaDB).",[3700,4322,4324],{"id":4323},"реалізація-розподілу-потоків-читаннязапису-в-entity-framework-core","Реалізація розподілу потоків читання\u002Fзапису в Entity Framework Core",[3353,4326,4327],{},"У застосунках на базі .NET для інтеграції з репліками читання необхідно конфігурувати два окремі підключення, оскільки стандартні ORM-системи не мають вбудованого механізму автоматичної маршрутизації SQL-запитів. Це реалізується шляхом реєстрації двох контекстів бази даних:",[3423,4329,4330],{},[3426,4331,4333],{"className":3428,"code":4332,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nnode \"EC2\\n.NET API\" as APP #dbeafe\n\ndatabase \"RDS Primary\\n(eu-central-1a)\\nЧитання + Запис\" as PRI #dbeafe\ndatabase \"Read Replica 1\\n(eu-central-1b)\\nТільки читання\" as RR1 #d1fae5\ndatabase \"Read Replica 2\\n(eu-central-1c)\\nТільки читання\" as RR2 #d1fae5\n\nAPP --> PRI : INSERT \u002F UPDATE \u002F DELETE\\n(записи)\nAPP --> RR1 : SELECT (звіти, пошук)\nAPP --> RR2 : SELECT (аналітика)\n\nPRI .down.> RR1 : асинхронна реплікація\\n(WAL logs, ~мс)\nPRI .down.> RR2 : асинхронна реплікація\\n(WAL logs, ~мс)\n\nnote right of RR2\n  Read Replica можна створити\n  в іншому регіоні (Cross-Region).\n  Наприклад: primary у eu-central-1,\n  replica у ap-southeast-1 для\n  користувачів у Азії.\nend note\n@enduml\n",[3414,4334,4335,4339,4343,4347,4351,4356,4360,4365,4370,4375,4379,4384,4389,4394,4398,4403,4408,4412,4417,4422,4427,4432,4437,4442,4446],{"__ignoreMap":3431},[3435,4336,4337],{"class":3437,"line":3438},[3435,4338,3441],{},[3435,4340,4341],{"class":3437,"line":3444},[3435,4342,3447],{},[3435,4344,4345],{"class":3437,"line":3450},[3435,4346,3453],{},[3435,4348,4349],{"class":3437,"line":3456},[3435,4350,3460],{"emptyLinePlaceholder":3459},[3435,4352,4353],{"class":3437,"line":3463},[3435,4354,4355],{},"node \"EC2\\n.NET API\" as APP #dbeafe\n",[3435,4357,4358],{"class":3437,"line":3469},[3435,4359,3460],{"emptyLinePlaceholder":3459},[3435,4361,4362],{"class":3437,"line":3475},[3435,4363,4364],{},"database \"RDS Primary\\n(eu-central-1a)\\nЧитання + Запис\" as PRI #dbeafe\n",[3435,4366,4367],{"class":3437,"line":3481},[3435,4368,4369],{},"database \"Read Replica 1\\n(eu-central-1b)\\nТільки читання\" as RR1 #d1fae5\n",[3435,4371,4372],{"class":3437,"line":3487},[3435,4373,4374],{},"database \"Read Replica 2\\n(eu-central-1c)\\nТільки читання\" as RR2 #d1fae5\n",[3435,4376,4377],{"class":3437,"line":3493},[3435,4378,3460],{"emptyLinePlaceholder":3459},[3435,4380,4381],{"class":3437,"line":3499},[3435,4382,4383],{},"APP --> PRI : INSERT \u002F UPDATE \u002F DELETE\\n(записи)\n",[3435,4385,4386],{"class":3437,"line":3505},[3435,4387,4388],{},"APP --> RR1 : SELECT (звіти, пошук)\n",[3435,4390,4391],{"class":3437,"line":3511},[3435,4392,4393],{},"APP --> RR2 : SELECT (аналітика)\n",[3435,4395,4396],{"class":3437,"line":3517},[3435,4397,3460],{"emptyLinePlaceholder":3459},[3435,4399,4400],{"class":3437,"line":3523},[3435,4401,4402],{},"PRI .down.> RR1 : асинхронна реплікація\\n(WAL logs, ~мс)\n",[3435,4404,4405],{"class":3437,"line":3529},[3435,4406,4407],{},"PRI .down.> RR2 : асинхронна реплікація\\n(WAL logs, ~мс)\n",[3435,4409,4410],{"class":3437,"line":3535},[3435,4411,3460],{"emptyLinePlaceholder":3459},[3435,4413,4414],{"class":3437,"line":3540},[3435,4415,4416],{},"note right of RR2\n",[3435,4418,4419],{"class":3437,"line":3546},[3435,4420,4421],{},"  Read Replica можна створити\n",[3435,4423,4424],{"class":3437,"line":3552},[3435,4425,4426],{},"  в іншому регіоні (Cross-Region).\n",[3435,4428,4429],{"class":3437,"line":3558},[3435,4430,4431],{},"  Наприклад: primary у eu-central-1,\n",[3435,4433,4434],{"class":3437,"line":3563},[3435,4435,4436],{},"  replica у ap-southeast-1 для\n",[3435,4438,4439],{"class":3437,"line":3569},[3435,4440,4441],{},"  користувачів у Азії.\n",[3435,4443,4444],{"class":3437,"line":3575},[3435,4445,4133],{},[3435,4447,4448],{"class":3437,"line":3580},[3435,4449,3640],{},[3426,4451,4455],{"className":4452,"code":4453,"language":4454,"meta":3431,"style":3431},"language-csharp shiki shiki-themes light-plus dark-plus dark-plus","\u002F\u002F appsettings.json\n{\n  \"ConnectionStrings\": {\n    \"Primary\": \"Host=mydb.xxx.rds.amazonaws.com;Database=myapp;Username=app;Password=...\",\n    \"ReadReplica\": \"Host=mydb-replica.xxx.rds.amazonaws.com;Database=myapp;Username=app;Password=...\"\n  }\n}\n\n\u002F\u002F Program.cs — реєстрація контекстів даних\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseNpgsql(builder.Configuration.GetConnectionString(\"Primary\")));\n\nbuilder.Services.AddDbContext\u003CReadOnlyDbContext>(options =>\n    options.UseNpgsql(builder.Configuration.GetConnectionString(\"ReadReplica\"))\n           .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)); \u002F\u002F Оптимізація: вимкнення відстеження\n","csharp",[3414,4456,4457,4463,4469,4478,4492,4502,4507,4511,4515,4520,4553,4586,4590,4613,4641],{"__ignoreMap":3431},[3435,4458,4459],{"class":3437,"line":3438},[3435,4460,4462],{"class":4461},"spJ8K","\u002F\u002F appsettings.json\n",[3435,4464,4465],{"class":3437,"line":3444},[3435,4466,4468],{"class":4467},"sHH4Y","{\n",[3435,4470,4471,4475],{"class":3437,"line":3450},[3435,4472,4474],{"class":4473},"sbdoH","  \"ConnectionStrings\"",[3435,4476,4477],{"class":4467},": {\n",[3435,4479,4480,4483,4486,4489],{"class":3437,"line":3456},[3435,4481,4482],{"class":4473},"    \"Primary\"",[3435,4484,4485],{"class":4467},": ",[3435,4487,4488],{"class":4473},"\"Host=mydb.xxx.rds.amazonaws.com;Database=myapp;Username=app;Password=...\"",[3435,4490,4491],{"class":4467},",\n",[3435,4493,4494,4497,4499],{"class":3437,"line":3463},[3435,4495,4496],{"class":4473},"    \"ReadReplica\"",[3435,4498,4485],{"class":4467},[3435,4500,4501],{"class":4473},"\"Host=mydb-replica.xxx.rds.amazonaws.com;Database=myapp;Username=app;Password=...\"\n",[3435,4503,4504],{"class":3437,"line":3469},[3435,4505,4506],{"class":4467},"  }\n",[3435,4508,4509],{"class":3437,"line":3475},[3435,4510,3532],{"class":4467},[3435,4512,4513],{"class":3437,"line":3481},[3435,4514,3460],{"emptyLinePlaceholder":3459},[3435,4516,4517],{"class":3437,"line":3487},[3435,4518,4519],{"class":4461},"\u002F\u002F Program.cs — реєстрація контекстів даних\n",[3435,4521,4522,4526,4528,4531,4533,4537,4540,4544,4547,4550],{"class":3437,"line":3493},[3435,4523,4525],{"class":4524},"siwwj","builder",[3435,4527,3691],{"class":4467},[3435,4529,4530],{"class":4524},"Services",[3435,4532,3691],{"class":4467},[3435,4534,4536],{"class":4535},"s8Opu","AddDbContext",[3435,4538,4539],{"class":4467},"\u003C",[3435,4541,4543],{"class":4542},"sN1BT","AppDbContext",[3435,4545,4546],{"class":4467},">(",[3435,4548,4549],{"class":4524},"options",[3435,4551,4552],{"class":4467}," =>\n",[3435,4554,4555,4558,4560,4563,4566,4568,4570,4573,4575,4578,4580,4583],{"class":3437,"line":3499},[3435,4556,4557],{"class":4524},"    options",[3435,4559,3691],{"class":4467},[3435,4561,4562],{"class":4535},"UseNpgsql",[3435,4564,4565],{"class":4467},"(",[3435,4567,4525],{"class":4524},[3435,4569,3691],{"class":4467},[3435,4571,4572],{"class":4524},"Configuration",[3435,4574,3691],{"class":4467},[3435,4576,4577],{"class":4535},"GetConnectionString",[3435,4579,4565],{"class":4467},[3435,4581,4582],{"class":4473},"\"Primary\"",[3435,4584,4585],{"class":4467},")));\n",[3435,4587,4588],{"class":3437,"line":3505},[3435,4589,3460],{"emptyLinePlaceholder":3459},[3435,4591,4592,4594,4596,4598,4600,4602,4604,4607,4609,4611],{"class":3437,"line":3511},[3435,4593,4525],{"class":4524},[3435,4595,3691],{"class":4467},[3435,4597,4530],{"class":4524},[3435,4599,3691],{"class":4467},[3435,4601,4536],{"class":4535},[3435,4603,4539],{"class":4467},[3435,4605,4606],{"class":4542},"ReadOnlyDbContext",[3435,4608,4546],{"class":4467},[3435,4610,4549],{"class":4524},[3435,4612,4552],{"class":4467},[3435,4614,4615,4617,4619,4621,4623,4625,4627,4629,4631,4633,4635,4638],{"class":3437,"line":3517},[3435,4616,4557],{"class":4524},[3435,4618,3691],{"class":4467},[3435,4620,4562],{"class":4535},[3435,4622,4565],{"class":4467},[3435,4624,4525],{"class":4524},[3435,4626,3691],{"class":4467},[3435,4628,4572],{"class":4524},[3435,4630,3691],{"class":4467},[3435,4632,4577],{"class":4535},[3435,4634,4565],{"class":4467},[3435,4636,4637],{"class":4473},"\"ReadReplica\"",[3435,4639,4640],{"class":4467},"))\n",[3435,4642,4643,4646,4649,4651,4654,4656,4659,4662],{"class":3437,"line":3523},[3435,4644,4645],{"class":4467},"           .",[3435,4647,4648],{"class":4535},"UseQueryTrackingBehavior",[3435,4650,4565],{"class":4467},[3435,4652,4653],{"class":4524},"QueryTrackingBehavior",[3435,4655,3691],{"class":4467},[3435,4657,4658],{"class":4524},"NoTracking",[3435,4660,4661],{"class":4467},")); ",[3435,4663,4664],{"class":4461},"\u002F\u002F Оптимізація: вимкнення відстеження\n",[3353,4666,4667],{},"Приклад використання у бізнес-логіці із явною маршрутизацією:",[3426,4669,4671],{"className":4452,"code":4670,"language":4454,"meta":3431,"style":3431},"public class ProductService(AppDbContext db, ReadOnlyDbContext readDb)\n{\n    \u002F\u002F Запити на читання спрямовуються на репліку\n    public async Task\u003CList\u003CProduct>> GetAllAsync() =>\n        await readDb.Products.ToListAsync();\n\n    \u002F\u002F Операції модифікації даних виконуються виключно на основному інстансі\n    public async Task CreateAsync(Product product)\n    {\n        db.Products.Add(product);\n        await db.SaveChangesAsync();\n    }\n}\n",[3414,4672,4673,4702,4706,4711,4741,4761,4765,4770,4790,4795,4817,4830,4834],{"__ignoreMap":3431},[3435,4674,4675,4679,4682,4685,4687,4689,4692,4694,4696,4699],{"class":3437,"line":3438},[3435,4676,4678],{"class":4677},"su1O8","public",[3435,4680,4681],{"class":4677}," class",[3435,4683,4684],{"class":4542}," ProductService",[3435,4686,4565],{"class":4467},[3435,4688,4543],{"class":4542},[3435,4690,4691],{"class":4524}," db",[3435,4693,3417],{"class":4467},[3435,4695,4606],{"class":4542},[3435,4697,4698],{"class":4524}," readDb",[3435,4700,4701],{"class":4467},")\n",[3435,4703,4704],{"class":3437,"line":3444},[3435,4705,4468],{"class":4467},[3435,4707,4708],{"class":3437,"line":3450},[3435,4709,4710],{"class":4461},"    \u002F\u002F Запити на читання спрямовуються на репліку\n",[3435,4712,4713,4716,4719,4722,4724,4727,4729,4732,4735,4738],{"class":3437,"line":3456},[3435,4714,4715],{"class":4677},"    public",[3435,4717,4718],{"class":4677}," async",[3435,4720,4721],{"class":4542}," Task",[3435,4723,4539],{"class":4467},[3435,4725,4726],{"class":4542},"List",[3435,4728,4539],{"class":4467},[3435,4730,4731],{"class":4542},"Product",[3435,4733,4734],{"class":4467},">> ",[3435,4736,4737],{"class":4535},"GetAllAsync",[3435,4739,4740],{"class":4467},"() =>\n",[3435,4742,4743,4746,4748,4750,4753,4755,4758],{"class":3437,"line":3463},[3435,4744,4745],{"class":4677},"        await",[3435,4747,4698],{"class":4524},[3435,4749,3691],{"class":4467},[3435,4751,4752],{"class":4524},"Products",[3435,4754,3691],{"class":4467},[3435,4756,4757],{"class":4535},"ToListAsync",[3435,4759,4760],{"class":4467},"();\n",[3435,4762,4763],{"class":3437,"line":3469},[3435,4764,3460],{"emptyLinePlaceholder":3459},[3435,4766,4767],{"class":3437,"line":3475},[3435,4768,4769],{"class":4461},"    \u002F\u002F Операції модифікації даних виконуються виключно на основному інстансі\n",[3435,4771,4772,4774,4776,4778,4781,4783,4785,4788],{"class":3437,"line":3481},[3435,4773,4715],{"class":4677},[3435,4775,4718],{"class":4677},[3435,4777,4721],{"class":4542},[3435,4779,4780],{"class":4535}," CreateAsync",[3435,4782,4565],{"class":4467},[3435,4784,4731],{"class":4542},[3435,4786,4787],{"class":4524}," product",[3435,4789,4701],{"class":4467},[3435,4791,4792],{"class":3437,"line":3487},[3435,4793,4794],{"class":4467},"    {\n",[3435,4796,4797,4800,4802,4804,4806,4809,4811,4814],{"class":3437,"line":3493},[3435,4798,4799],{"class":4524},"        db",[3435,4801,3691],{"class":4467},[3435,4803,4752],{"class":4524},[3435,4805,3691],{"class":4467},[3435,4807,4808],{"class":4535},"Add",[3435,4810,4565],{"class":4467},[3435,4812,4813],{"class":4524},"product",[3435,4815,4816],{"class":4467},");\n",[3435,4818,4819,4821,4823,4825,4828],{"class":3437,"line":3499},[3435,4820,4745],{"class":4677},[3435,4822,4691],{"class":4524},[3435,4824,3691],{"class":4467},[3435,4826,4827],{"class":4535},"SaveChangesAsync",[3435,4829,4760],{"class":4467},[3435,4831,4832],{"class":3437,"line":3505},[3435,4833,3490],{"class":4467},[3435,4835,4836],{"class":3437,"line":3511},[3435,4837,3532],{"class":4467},[3679,4839],{},[3348,4841,4843],{"id":4842},"стратегії-резервного-копіювання-та-механізм-point-in-time-recovery-pitr","Стратегії резервного копіювання та механізм Point-in-Time Recovery (PITR)",[3353,4845,4846],{},"Забезпечення збереженості даних та можливості їх відновлення у разі критичних збоїв є фундаментальним аспектом адміністрування баз даних. Amazon RDS надає два взаємодоповнюючі механізми резервного копіювання: автоматичне резервне копіювання (Automated Backups) та створення знімків стану вручну (Manual Snapshots).",[3700,4848,4850],{"id":4849},"порівняльний-аналіз-автоматичних-та-ручних-копій","Порівняльний аналіз автоматичних та ручних копій",[4852,4853,4854,4871],"table",{},[4855,4856,4857],"thead",{},[4858,4859,4860,4865,4868],"tr",{},[4861,4862,4864],"th",{"align":4863},"left","Характеристика",[4861,4866,4867],{"align":4863},"Автоматичне резервне копіювання (Automated Backups)",[4861,4869,4870],{"align":4863},"Ручні знімки стану (Manual Snapshots)",[4872,4873,4874,4888,4905,4918],"tbody",{},[4858,4875,4876,4882,4885],{},[4877,4878,4879],"td",{"align":4863},[3359,4880,4881],{},"Ініціація",[4877,4883,4884],{"align":4863},"Виконується сервісом автоматично в межах визначеного вікна обслуговування (Backup Window).",[4877,4886,4887],{"align":4863},"Створюється адміністратором вручну за запитом через консоль, CLI або API.",[4858,4889,4890,4895,4902],{},[4877,4891,4892],{"align":4863},[3359,4893,4894],{},"Термін зберігання",[4877,4896,4897,4898,4901],{"align":4863},"Регулюється параметром ",[3897,4899,4900],{},"Retention Period"," (від 1 до 35 днів). Після завершення періоду копії видаляються.",[4877,4903,4904],{"align":4863},"Зберігається безстроково (до моменту явного видалення користувачем).",[4858,4906,4907,4912,4915],{},[4877,4908,4909],{"align":4863},[3359,4910,4911],{},"Життєвий цикл при видаленні БД",[4877,4913,4914],{"align":4863},"За замовчуванням усі автоматичні копії видаляються разом із видаленням інстансу бази даних.",[4877,4916,4917],{"align":4863},"Зберігаються як незалежні ресурси навіть після повного видалення первинного інстансу СУБД.",[4858,4919,4920,4925,4928],{},[4877,4921,4922],{"align":4863},[3359,4923,4924],{},"Призначення",[4877,4926,4927],{"align":4863},"Використовується для відновлення стану бази на будь-який момент часу (PITR) за останні дні.",[4877,4929,4930],{"align":4863},"Застосовується для довгострокового архівування або фіксації стану системи перед великими релізами чи міграціями.",[3700,4932,4934],{"id":4933},"механізм-відновлення-на-конкретний-момент-часу-point-in-time-recovery-pitr","Механізм відновлення на конкретний момент часу (Point-in-Time Recovery, PITR)",[3353,4936,4937],{},"Відновлення на конкретний момент часу є ключовою функцією Amazon RDS для мінімізації цільової точки відновлення (Recovery Point Objective, RPO) до рівня кількох секунд. Цей процес базується на комбінації двох джерел даних:",[3913,4939,4940,4946],{},[3369,4941,4942,4945],{},[3359,4943,4944],{},"Повні знімки сховища (Daily Snapshots):"," Фізичні копії всіх блоків диска інстансу, що створюються один раз на добу під час резервного вікна.",[3369,4947,4948,4951],{},[3359,4949,4950],{},"Архівні транзакційні журнали (Transaction Logs):"," Журнали випереджального запису (Write-Ahead Logs, WAL для PostgreSQL або Binary Logs для MySQL), які безперервно копіюються до ізольованого сховища Amazon S3 з інтервалом у 5 хвилин.",[3353,4953,4954],{},[3359,4955,4956],{},"Алгоритм виконання операції PITR:",[3913,4958,4959,4962,4969,4972,4975],{},[3369,4960,4961],{},"Користувач ініціює процес відновлення, вказуючи бажану часову мітку (наприклад, за секунду до виконання помилкового SQL-запиту).",[3369,4963,4964,4965,4968],{},"Amazon RDS аналізує метадані та розгортає найбільш актуальний повний знімок стану, створений ",[3897,4966,4967],{},"до"," вказаного моменту часу.",[3369,4970,4971],{},"Система створює новий інстанс бази даних і підключає відновлене з повного знімка сховище.",[3369,4973,4974],{},"На новому інстансі автоматично запускається процес програвання (replay) транзакційних журналів. Усі транзакції, зафіксовані в інтервалі між часом створення знімка та вказаною міткою, послідовно застосовуються до бази даних.",[3369,4976,4977,4978,4981],{},"Після завершення накату логів новий інстанс бази даних переходить у статус ",[3414,4979,4980],{},"available"," і стає доступним для використання.",[3423,4983,4984],{},[3426,4985,4987],{"className":3428,"code":4986,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam sequence {\n    ArrowColor #374151\n    ParticipantBorderColor #374151\n    ParticipantBackgroundColor #f9fafb\n    NoteBackgroundColor #fef3c7\n    NoteBorderColor #d97706\n    LifeLineBorderColor #9ca3af\n}\n\ntitle Point-in-Time Recovery: відновлення на 14:36:59\n\nparticipant \"RDS\" as RDS\nparticipant \"S3\\n(backup storage)\" as S3\nparticipant \"Новий RDS\\ninstance\" as NEW\n\nnote over RDS, S3\n  03:00 — щоденний snapshot збережено у S3\n  03:00–14:37 — transaction logs безперервно архівуються\nend note\n\nRDS -> RDS : 14:37 — ❌ DELETE FROM orders (без WHERE!)\nnote right of RDS #fee2e2\n  Всі записи у таблиці orders\n  видалені\nend note\n\n== Відновлення ==\n\nRDS -> NEW : Розгортання snapshot від 03:00\nS3 --> NEW : Transaction logs 03:00 → 14:36:59\nNEW -> NEW : Повторення транзакцій до 14:36:59\n\nnote over NEW #dcfce7\n  ✅ Новий instance з даними\n  станом на 14:36:59.\n  Таблиця orders — ціла.\n  Переключіть з'єднання додатку\n  на новий endpoint.\nend note\n@enduml\n",[3414,4988,4989,4993,4997,5001,5005,5009,5013,5017,5022,5027,5031,5035,5039,5044,5048,5053,5058,5063,5067,5072,5077,5082,5086,5090,5095,5100,5105,5110,5114,5118,5123,5127,5132,5137,5142,5146,5151,5156,5161,5166,5171,5176,5180],{"__ignoreMap":3431},[3435,4990,4991],{"class":3437,"line":3438},[3435,4992,3441],{},[3435,4994,4995],{"class":3437,"line":3444},[3435,4996,3447],{},[3435,4998,4999],{"class":3437,"line":3450},[3435,5000,3453],{},[3435,5002,5003],{"class":3437,"line":3456},[3435,5004,4008],{},[3435,5006,5007],{"class":3437,"line":3463},[3435,5008,4013],{},[3435,5010,5011],{"class":3437,"line":3469},[3435,5012,4018],{},[3435,5014,5015],{"class":3437,"line":3475},[3435,5016,4023],{},[3435,5018,5019],{"class":3437,"line":3481},[3435,5020,5021],{},"    NoteBackgroundColor #fef3c7\n",[3435,5023,5024],{"class":3437,"line":3487},[3435,5025,5026],{},"    NoteBorderColor #d97706\n",[3435,5028,5029],{"class":3437,"line":3493},[3435,5030,4038],{},[3435,5032,5033],{"class":3437,"line":3499},[3435,5034,3532],{},[3435,5036,5037],{"class":3437,"line":3505},[3435,5038,3460],{"emptyLinePlaceholder":3459},[3435,5040,5041],{"class":3437,"line":3511},[3435,5042,5043],{},"title Point-in-Time Recovery: відновлення на 14:36:59\n",[3435,5045,5046],{"class":3437,"line":3517},[3435,5047,3460],{"emptyLinePlaceholder":3459},[3435,5049,5050],{"class":3437,"line":3523},[3435,5051,5052],{},"participant \"RDS\" as RDS\n",[3435,5054,5055],{"class":3437,"line":3529},[3435,5056,5057],{},"participant \"S3\\n(backup storage)\" as S3\n",[3435,5059,5060],{"class":3437,"line":3535},[3435,5061,5062],{},"participant \"Новий RDS\\ninstance\" as NEW\n",[3435,5064,5065],{"class":3437,"line":3540},[3435,5066,3460],{"emptyLinePlaceholder":3459},[3435,5068,5069],{"class":3437,"line":3546},[3435,5070,5071],{},"note over RDS, S3\n",[3435,5073,5074],{"class":3437,"line":3552},[3435,5075,5076],{},"  03:00 — щоденний snapshot збережено у S3\n",[3435,5078,5079],{"class":3437,"line":3558},[3435,5080,5081],{},"  03:00–14:37 — transaction logs безперервно архівуються\n",[3435,5083,5084],{"class":3437,"line":3563},[3435,5085,4133],{},[3435,5087,5088],{"class":3437,"line":3569},[3435,5089,3460],{"emptyLinePlaceholder":3459},[3435,5091,5092],{"class":3437,"line":3575},[3435,5093,5094],{},"RDS -> RDS : 14:37 — ❌ DELETE FROM orders (без WHERE!)\n",[3435,5096,5097],{"class":3437,"line":3580},[3435,5098,5099],{},"note right of RDS #fee2e2\n",[3435,5101,5102],{"class":3437,"line":3586},[3435,5103,5104],{},"  Всі записи у таблиці orders\n",[3435,5106,5107],{"class":3437,"line":3592},[3435,5108,5109],{},"  видалені\n",[3435,5111,5112],{"class":3437,"line":3597},[3435,5113,4133],{},[3435,5115,5116],{"class":3437,"line":3603},[3435,5117,3460],{"emptyLinePlaceholder":3459},[3435,5119,5120],{"class":3437,"line":3609},[3435,5121,5122],{},"== Відновлення ==\n",[3435,5124,5125],{"class":3437,"line":3615},[3435,5126,3460],{"emptyLinePlaceholder":3459},[3435,5128,5129],{"class":3437,"line":3621},[3435,5130,5131],{},"RDS -> NEW : Розгортання snapshot від 03:00\n",[3435,5133,5134],{"class":3437,"line":3627},[3435,5135,5136],{},"S3 --> NEW : Transaction logs 03:00 → 14:36:59\n",[3435,5138,5139],{"class":3437,"line":3632},[3435,5140,5141],{},"NEW -> NEW : Повторення транзакцій до 14:36:59\n",[3435,5143,5144],{"class":3437,"line":3637},[3435,5145,3460],{"emptyLinePlaceholder":3459},[3435,5147,5148],{"class":3437,"line":4160},[3435,5149,5150],{},"note over NEW #dcfce7\n",[3435,5152,5153],{"class":3437,"line":4166},[3435,5154,5155],{},"  ✅ Новий instance з даними\n",[3435,5157,5158],{"class":3437,"line":4171},[3435,5159,5160],{},"  станом на 14:36:59.\n",[3435,5162,5163],{"class":3437,"line":4176},[3435,5164,5165],{},"  Таблиця orders — ціла.\n",[3435,5167,5168],{"class":3437,"line":4182},[3435,5169,5170],{},"  Переключіть з'єднання додатку\n",[3435,5172,5173],{"class":3437,"line":4188},[3435,5174,5175],{},"  на новий endpoint.\n",[3435,5177,5178],{"class":3437,"line":4194},[3435,5179,4133],{},[3435,5181,5182],{"class":3437,"line":4199},[3435,5183,3640],{},[5185,5186,5187],"blockquote",{},[3353,5188,5189,5192,5193,5196],{},[3435,5190,5191],{},"!IMPORTANT","\nПроцедура Point-in-Time Recovery завжди призводить до створення ",[3359,5194,5195],{},"нового інстансу бази даних"," із новим мережевим endpoint. Поточний інстанс залишається без змін, що запобігає випадковому перезапису даних під час відновлення та дозволяє виконати порівняльний аналіз стану до і після аварії.",[3700,5198,5200],{"id":5199},"створення-знімка-стану-вручну-manual-snapshot","Створення знімка стану вручну (Manual Snapshot)",[3353,5202,5203],{},"Перед виконанням операцій, пов'язаних із ризиком втрати цілісності даних (наприклад, великі міграції схем або масові оновлення даних), рекомендується створювати знімки стану вручну.",[5205,5206,5207,5279],"tabs",{},[5208,5209,5211],"tabs-item",{"label":5210},"AWS CLI (bash)",[3426,5212,5216],{"className":5213,"code":5214,"language":5215,"meta":3431,"style":3431},"language-bash shiki shiki-themes light-plus dark-plus dark-plus","# Створення знімка стану вручну перед виконанням міграції схеми\naws rds create-db-snapshot \\\n    --db-instance-identifier my-production-db \\\n    --db-snapshot-identifier before-migration-v2-$(date +%Y%m%d) \\\n    --region eu-central-1\n","bash",[3414,5217,5218,5223,5238,5248,5271],{"__ignoreMap":3431},[3435,5219,5220],{"class":3437,"line":3438},[3435,5221,5222],{"class":4461},"# Створення знімка стану вручну перед виконанням міграції схеми\n",[3435,5224,5225,5228,5231,5234],{"class":3437,"line":3444},[3435,5226,5227],{"class":4535},"aws",[3435,5229,5230],{"class":4473}," rds",[3435,5232,5233],{"class":4473}," create-db-snapshot",[3435,5235,5237],{"class":5236},"sjcCO"," \\\n",[3435,5239,5240,5243,5246],{"class":3437,"line":3450},[3435,5241,5242],{"class":4677},"    --db-instance-identifier",[3435,5244,5245],{"class":4473}," my-production-db",[3435,5247,5237],{"class":5236},[3435,5249,5250,5253,5256,5259,5262,5265,5268],{"class":3437,"line":3456},[3435,5251,5252],{"class":4677},"    --db-snapshot-identifier",[3435,5254,5255],{"class":4473}," before-migration-v2-",[3435,5257,5258],{"class":4467},"$(",[3435,5260,5261],{"class":4535},"date",[3435,5263,5264],{"class":4473}," +%Y%m%d",[3435,5266,5267],{"class":4467},") ",[3435,5269,5270],{"class":5236},"\\\n",[3435,5272,5273,5276],{"class":3437,"line":3463},[3435,5274,5275],{"class":4677},"    --region",[3435,5277,5278],{"class":4473}," eu-central-1\n",[5208,5280,5282],{"label":5281},"AWS CLI (PowerShell)",[3426,5283,5287],{"className":5284,"code":5285,"language":5286,"meta":3431,"style":3431},"language-powershell shiki shiki-themes light-plus dark-plus dark-plus","# Створення знімка стану вручну перед виконанням міграції схеми\n$dateStr = Get-Date -Format \"yyyyMMdd\"\naws rds create-db-snapshot `\n    --db-instance-identifier my-production-db `\n    --db-snapshot-identifier before-migration-v2-$dateStr `\n    --region eu-central-1\n","powershell",[3414,5288,5289,5293,5310,5315,5320,5330],{"__ignoreMap":3431},[3435,5290,5291],{"class":3437,"line":3438},[3435,5292,5222],{"class":4461},[3435,5294,5295,5298,5301,5304,5307],{"class":3437,"line":3444},[3435,5296,5297],{"class":4524},"$dateStr",[3435,5299,5300],{"class":4467}," = ",[3435,5302,5303],{"class":4535},"Get-Date",[3435,5305,5306],{"class":4467}," -Format ",[3435,5308,5309],{"class":4473},"\"yyyyMMdd\"\n",[3435,5311,5312],{"class":3437,"line":3450},[3435,5313,5314],{"class":4467},"aws rds create-db-snapshot `\n",[3435,5316,5317],{"class":3437,"line":3456},[3435,5318,5319],{"class":4467},"    --db-instance-identifier my-production-db `\n",[3435,5321,5322,5325,5327],{"class":3437,"line":3463},[3435,5323,5324],{"class":4467},"    --db-snapshot-identifier before-migration-v2-",[3435,5326,5297],{"class":4524},[3435,5328,5329],{"class":4467}," `\n",[3435,5331,5332,5335],{"class":3437,"line":3469},[3435,5333,5334],{"class":4467},"    --region eu-central-",[3435,5336,5338],{"class":5337},"sJj4R","1\n",[3679,5340],{},[3348,5342,5344],{"id":5343},"rds-security-захист-бази-даних","RDS Security — захист бази даних",[3353,5346,5347],{},"Безпека RDS будується на кількох незалежних рівнях, кожен з яких важливий.",[3700,5349,5351],{"id":5350},"мережева-безпека-та-ізоляція-vpc","Мережева безпека та ізоляція (VPC)",[3353,5353,5354,5357,5358,5361,5362,3691],{},[3359,5355,5356],{},"Фундаментальне правило:"," інстанс СУБД промислового рівня ніколи не повинен мати прямих публічних IP-адрес чи бути доступним безпосередньо з мережі Інтернет. Параметр ",[3414,5359,5360],{},"Publicly accessible"," під час створення інстансу завжди має бути встановлений у положення ",[3414,5363,5364],{},"No",[3353,5366,5367,5368,5371],{},"Інстанс бази даних розміщується виключно у ",[3359,5369,5370],{},"приватних підмережах (Private Subnets)"," віртуальної приватної хмари (VPC). Такі підмережі не мають маршрутів до Інтернет-шлюзу (Internet Gateway). Мережевий доступ дозволяється лише авторизованим ресурсам всередині тієї самої VPC — віртуальним машинам EC2, контейнерам ECS\u002FEKS або безсерверним функціям AWS Lambda.",[3700,5373,5375],{"id":5374},"налаштування-міжмережевих-екранів-security-groups","Налаштування міжмережевих екранів (Security Groups)",[3353,5377,5378,5379,5382],{},"Security Group для інстансу RDS виступає у ролі віртуального міжмережевого екрана (stateful firewall), який контролює вхідний і вихідний трафік. Для бази даних PostgreSQL стандартним портом підключення є ",[3414,5380,5381],{},"5432\u002FTCP",". Конфігурація правил повинна відповідати принципу найменших привілеїв:",[3366,5384,5385,5399],{},[3369,5386,5387,5390,5391,5394,5395,5398],{},[3359,5388,5389],{},"Вхідний трафік (Inbound Rules):"," дозволено TCP порт ",[3414,5392,5393],{},"5432",". Як джерело (Source) слід вказувати ідентифікатор Security Group серверів застосунків (наприклад, ",[3414,5396,5397],{},"dotnet-api-sg","), а не статичні діапазони IP-адрес.",[3369,5400,5401,5404],{},[3359,5402,5403],{},"Вихідний трафік (Outbound Rules):"," за замовчуванням повністю обмежується, оскільки інстансу бази даних немає необхідності ініціювати вихідні мережеві сесії.",[5406,5407,5409],"h4",{"id":5408},"звязування-security-groups-security-group-referencing","Зв'язування Security Groups (Security Group Referencing)",[3353,5411,5412,5413,5416],{},"Використання конкретних IP-адрес або CIDR-блоків у налаштуваннях безпеки є серйозною помилкою адміністрування в динамічних хмарних середовищах. При перезапуску інстансів EC2 їхні публічні та приватні IP-адреси можуть змінюватися (якщо не використовуються Elastic IP). Механізм зв'язування Security Groups дозволяє дозволити доступ будь-якому ресурсу, який асоційований із певною групою безпеки (Source = ",[3414,5414,5415],{},"sg-XXXX","), незалежно від його поточної мережевої адреси.",[3423,5418,5419],{},[3426,5420,5422],{"className":3428,"code":5421,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nrectangle \"VPC (10.0.0.0\u002F16)\" as VPC {\n    rectangle \"Public Subnet\\n10.0.1.0\u002F24\" as PUB #fef3c7 {\n        node \"EC2\\n.NET API\\nSG: api-sg\" as EC2\n    }\n    rectangle \"Private Subnet\\n10.0.2.0\u002F24\" as PRIV #dcfce7 {\n        database \"RDS PostgreSQL\\nSG: rds-sg\\nPublicly accessible: No\" as RDS\n    }\n}\n\ncloud \"Internet\" as NET\n\nNET -> EC2 : :443 (HTTPS)\nEC2 -> RDS : :5432 (дозволено:\\nrds-sg inbound from api-sg)\nNET -[dashed]-> RDS : ❌ заблоковано\\n(private subnet)\n@enduml\n",[3414,5423,5424,5428,5432,5436,5440,5445,5450,5455,5459,5464,5469,5473,5477,5481,5486,5490,5495,5500,5505],{"__ignoreMap":3431},[3435,5425,5426],{"class":3437,"line":3438},[3435,5427,3441],{},[3435,5429,5430],{"class":3437,"line":3444},[3435,5431,3447],{},[3435,5433,5434],{"class":3437,"line":3450},[3435,5435,3453],{},[3435,5437,5438],{"class":3437,"line":3456},[3435,5439,3460],{"emptyLinePlaceholder":3459},[3435,5441,5442],{"class":3437,"line":3463},[3435,5443,5444],{},"rectangle \"VPC (10.0.0.0\u002F16)\" as VPC {\n",[3435,5446,5447],{"class":3437,"line":3469},[3435,5448,5449],{},"    rectangle \"Public Subnet\\n10.0.1.0\u002F24\" as PUB #fef3c7 {\n",[3435,5451,5452],{"class":3437,"line":3475},[3435,5453,5454],{},"        node \"EC2\\n.NET API\\nSG: api-sg\" as EC2\n",[3435,5456,5457],{"class":3437,"line":3481},[3435,5458,3490],{},[3435,5460,5461],{"class":3437,"line":3487},[3435,5462,5463],{},"    rectangle \"Private Subnet\\n10.0.2.0\u002F24\" as PRIV #dcfce7 {\n",[3435,5465,5466],{"class":3437,"line":3493},[3435,5467,5468],{},"        database \"RDS PostgreSQL\\nSG: rds-sg\\nPublicly accessible: No\" as RDS\n",[3435,5470,5471],{"class":3437,"line":3499},[3435,5472,3490],{},[3435,5474,5475],{"class":3437,"line":3505},[3435,5476,3532],{},[3435,5478,5479],{"class":3437,"line":3511},[3435,5480,3460],{"emptyLinePlaceholder":3459},[3435,5482,5483],{"class":3437,"line":3517},[3435,5484,5485],{},"cloud \"Internet\" as NET\n",[3435,5487,5488],{"class":3437,"line":3523},[3435,5489,3460],{"emptyLinePlaceholder":3459},[3435,5491,5492],{"class":3437,"line":3529},[3435,5493,5494],{},"NET -> EC2 : :443 (HTTPS)\n",[3435,5496,5497],{"class":3437,"line":3535},[3435,5498,5499],{},"EC2 -> RDS : :5432 (дозволено:\\nrds-sg inbound from api-sg)\n",[3435,5501,5502],{"class":3437,"line":3540},[3435,5503,5504],{},"NET -[dashed]-> RDS : ❌ заблоковано\\n(private subnet)\n",[3435,5506,5507],{"class":3437,"line":3546},[3435,5508,3640],{},[3700,5510,5512],{"id":5511},"шифрування-даних-на-фізичному-рівні-encryption-at-rest","Шифрування даних на фізичному рівні (Encryption at Rest)",[3353,5514,5515,5516,3691],{},"Для захисту даних від несанкціонованого доступу на фізичному рівні (наприклад, у разі вилучення або компрометації фізичних носіїв у дата-центрі) Amazon RDS підтримує шифрування за допомогою інтеграції з ",[3359,5517,5518],{},"AWS Key Management Service (KMS)",[3366,5520,5521,5527,5537,5543],{},[3369,5522,5523,5526],{},[3359,5524,5525],{},"Алгоритм шифрування:"," Використовується симетричний стандарт AES з довжиною ключа 256 біт (AES-256).",[3369,5528,5529,5532,5533,5536],{},[3359,5530,5531],{},"Обсяг шифрування:"," Шифруванню підлягають первинний дисковий том інстансу, системні логи, тимчасові файли ",[3414,5534,5535],{},"tempdb",", автоматичні резервні копії, ручні знімки стану та всі репліки читання.",[3369,5538,5539,5542],{},[3359,5540,5541],{},"Технологія конвертного шифрування (Envelope Encryption):"," Дані шифруються унікальним ключем даних (data key), який, у свою чергу, шифрується під керуванням головного ключа (KMS Customer Managed Key або AWS Managed Key для RDS).",[3369,5544,5545,5548],{},[3359,5546,5547],{},"Вплив на продуктивність:"," Завдяки апаратній підтримці шифрування на рівні фізичних хостів AWS (використання інструкцій AES-NI або чипів AWS Nitro), вплив на затримки введення-виведення (I\u002FO latency) є мінімальним і зазвичай непомітним для застосунку.",[5185,5550,5551,5560],{},[3353,5552,5553,5555,5556,5559],{},[3435,5554,5191],{},"\nШифрування накопичувачів можна активувати ",[3359,5557,5558],{},"виключно під час створення інстансу бази даних",". Для увімкнення шифрування на існуючій незашифрованій базі даних необхідно виконати таку послідовність дій:",[3913,5561,5562,5565,5568],{},[3369,5563,5564],{},"Створити знімок стану (snapshot) незашифрованої бази даних.",[3369,5566,5567],{},"Скопіювати створений знімок, вказавши при копіюванні опцію шифрування та обравши KMS ключ.",[3369,5569,5570],{},"Відновити новий інстанс бази даних із зашифрованої копії знімка стану.",[3700,5572,5574],{"id":5573},"шифрування-трафіку-encryption-in-transit","Шифрування трафіку (Encryption in Transit)",[3353,5576,5577],{},"Для запобігання перехопленню даних у процесі їх транспортування мережею (атаки типу Man-in-the-Middle) усі транзакції мають шифруватися за допомогою протоколу SSL\u002FTLS.",[3366,5579,5580,5586],{},[3369,5581,5582,5585],{},[3359,5583,5584],{},"Довіра до сертифікатів:"," Amazon RDS надає власні регіональні та глобальні центри сертифікації (Certificate Authorities, CA). Для перевірки справжності вузла застосунок повинен валідувати сертифікат сервера за допомогою кореневого сертифіката AWS (AWS Root CA).",[3369,5587,5588,5591,5592,5600,5601,5604,5605,5608],{},[3359,5589,5590],{},"Конфігурація Npgsql:"," У бібліотеці підключення Npgsql для .NET автентифікація сервера та шифрування налаштовується в рядку з'єднання:\n",[3426,5593,5598],{"className":5594,"code":5596,"language":5597},[5595],"language-text","Host=mydb.xxx.rds.amazonaws.com;Database=myapp;Username=app;Password=secret;SSL Mode=Require;Trust Server Certificate=false;\n","text",[3414,5599,5596],{"__ignoreMap":3431},"\nПараметр ",[3414,5602,5603],{},"SSL Mode=Require"," зобов'язує клієнта встановлювати виключно шифроване з'єднання. Встановлення ",[3414,5606,5607],{},"Trust Server Certificate=false"," гарантує, що клієнт буде перевіряти підпис сервера через локальне сховище довірених кореневих сертифікатів операційної системи (де має бути встановлений сертифікат AWS RDS CA).",[3700,5610,5612],{"id":5611},"автентифікація-за-допомогою-aws-iam-iam-database-authentication","Автентифікація за допомогою AWS IAM (IAM Database Authentication)",[3353,5614,5615,5616,3691],{},"Традиційний метод доступу за допомогою статичних паролів створює ризики витоку облікових даних через конфігураційні файли. Альтернативним підходом є використання ",[3359,5617,5618],{},"IAM Database Authentication",[3366,5620,5621,5629,5639],{},[3369,5622,5623,5625,5626,3691],{},[3359,5624,3876],{}," Замість пароля клієнт використовує тимчасово генерований маркер автентифікації (Auth Token). Генерація токена виконується клієнтом локально за допомогою бібліотек AWS SDK та протоколу ",[3359,5627,5628],{},"AWS Signature Version 4",[3369,5630,5631,5634,5635,5638],{},[3359,5632,5633],{},"Термін дії маркерів:"," Кожен згенерований токен має обмежений життєвий цикл тривалістю ",[3359,5636,5637],{},"15 хвилин",". Це означає, що скомпрометований токен швидко втрачає валідність.",[3369,5640,5641,5644],{},[3359,5642,5643],{},"Безпарольний доступ:"," Віртуальні машини EC2 або функції Lambda можуть використовувати свої асоційовані IAM-ролі для автентифікації у базі даних без збереження секретів у вихідному коді чи конфігураціях.",[3353,5646,5647],{},"Приклад програмного отримання токена в .NET:",[3426,5649,5651],{"className":4452,"code":5650,"language":4454,"meta":3431,"style":3431},"using Amazon;\nusing Amazon.RDS;\n\n\u002F\u002F Створення клієнта Amazon RDS для взаємодії з API\nvar client = new AmazonRDSClient(RegionEndpoint.EUCentral1);\n\n\u002F\u002F Генерація одноразового маркеру доступу\nstring token = RDSAuthTokenGenerator.GenerateAuthToken(\n    region: \"eu-central-1\",\n    dbHostName: \"mydb.xxx.rds.amazonaws.com\",\n    port: 5432,\n    dbUserName: \"iam_db_user\"\n);\n\n\u002F\u002F Отриманий токен використовується як значення пароля у connection string\nstring connectionString = $\"Host=mydb.xxx.rds.amazonaws.com;Database=myapp;Username=iam_db_user;Password={token};SSL Mode=Require;\";\n",[3414,5652,5653,5665,5678,5682,5687,5715,5719,5724,5745,5757,5769,5780,5790,5794,5798,5803],{"__ignoreMap":3431},[3435,5654,5655,5659,5662],{"class":3437,"line":3438},[3435,5656,5658],{"class":5657},"s8xlr","using",[3435,5660,5661],{"class":4542}," Amazon",[3435,5663,5664],{"class":4467},";\n",[3435,5666,5667,5669,5671,5673,5676],{"class":3437,"line":3444},[3435,5668,5658],{"class":5657},[3435,5670,5661],{"class":4542},[3435,5672,3691],{"class":4467},[3435,5674,5675],{"class":4542},"RDS",[3435,5677,5664],{"class":4467},[3435,5679,5680],{"class":3437,"line":3450},[3435,5681,3460],{"emptyLinePlaceholder":3459},[3435,5683,5684],{"class":3437,"line":3456},[3435,5685,5686],{"class":4461},"\u002F\u002F Створення клієнта Amazon RDS для взаємодії з API\n",[3435,5688,5689,5692,5695,5697,5700,5703,5705,5708,5710,5713],{"class":3437,"line":3463},[3435,5690,5691],{"class":4677},"var",[3435,5693,5694],{"class":4524}," client",[3435,5696,5300],{"class":4467},[3435,5698,5699],{"class":4677},"new",[3435,5701,5702],{"class":4542}," AmazonRDSClient",[3435,5704,4565],{"class":4467},[3435,5706,5707],{"class":4524},"RegionEndpoint",[3435,5709,3691],{"class":4467},[3435,5711,5712],{"class":4524},"EUCentral1",[3435,5714,4816],{"class":4467},[3435,5716,5717],{"class":3437,"line":3469},[3435,5718,3460],{"emptyLinePlaceholder":3459},[3435,5720,5721],{"class":3437,"line":3475},[3435,5722,5723],{"class":4461},"\u002F\u002F Генерація одноразового маркеру доступу\n",[3435,5725,5726,5729,5732,5734,5737,5739,5742],{"class":3437,"line":3481},[3435,5727,5728],{"class":4677},"string",[3435,5730,5731],{"class":4524}," token",[3435,5733,5300],{"class":4467},[3435,5735,5736],{"class":4524},"RDSAuthTokenGenerator",[3435,5738,3691],{"class":4467},[3435,5740,5741],{"class":4535},"GenerateAuthToken",[3435,5743,5744],{"class":4467},"(\n",[3435,5746,5747,5750,5752,5755],{"class":3437,"line":3487},[3435,5748,5749],{"class":4524},"    region",[3435,5751,4485],{"class":4467},[3435,5753,5754],{"class":4473},"\"eu-central-1\"",[3435,5756,4491],{"class":4467},[3435,5758,5759,5762,5764,5767],{"class":3437,"line":3493},[3435,5760,5761],{"class":4524},"    dbHostName",[3435,5763,4485],{"class":4467},[3435,5765,5766],{"class":4473},"\"mydb.xxx.rds.amazonaws.com\"",[3435,5768,4491],{"class":4467},[3435,5770,5771,5774,5776,5778],{"class":3437,"line":3499},[3435,5772,5773],{"class":4524},"    port",[3435,5775,4485],{"class":4467},[3435,5777,5393],{"class":5337},[3435,5779,4491],{"class":4467},[3435,5781,5782,5785,5787],{"class":3437,"line":3505},[3435,5783,5784],{"class":4524},"    dbUserName",[3435,5786,4485],{"class":4467},[3435,5788,5789],{"class":4473},"\"iam_db_user\"\n",[3435,5791,5792],{"class":3437,"line":3511},[3435,5793,4816],{"class":4467},[3435,5795,5796],{"class":3437,"line":3517},[3435,5797,3460],{"emptyLinePlaceholder":3459},[3435,5799,5800],{"class":3437,"line":3523},[3435,5801,5802],{"class":4461},"\u002F\u002F Отриманий токен використовується як значення пароля у connection string\n",[3435,5804,5805,5807,5810,5812,5815,5819,5822,5825,5828],{"class":3437,"line":3529},[3435,5806,5728],{"class":4677},[3435,5808,5809],{"class":4524}," connectionString",[3435,5811,5300],{"class":4467},[3435,5813,5814],{"class":4473},"$\"Host=mydb.xxx.rds.amazonaws.com;Database=myapp;Username=iam_db_user;Password=",[3435,5816,5818],{"class":5817},"sD7JJ","{",[3435,5820,5821],{"class":4524},"token",[3435,5823,5824],{"class":5817},"}",[3435,5826,5827],{"class":4473},";SSL Mode=Require;\"",[3435,5829,5664],{"class":4467},[3700,5831,5833],{"id":5832},"конфігурування-субд-за-допомогою-parameter-groups-та-option-groups","Конфігурування СУБД за допомогою Parameter Groups та Option Groups",[3353,5835,5836,5837,5840,5841,5844],{},"У керованому сервісі Amazon RDS прямий доступ до файлової системи сервера (через SSH) відсутній, тому традиційне редагування конфігураційних файлів (таких як ",[3414,5838,5839],{},"postgresql.conf"," або ",[3414,5842,5843],{},"my.cnf",") є неможливим. Замість цього адміністрування параметрів ядра бази даних здійснюється за допомогою двох сутностей: Parameter Groups та Option Groups.",[5406,5846,5848],{"id":5847},"parameter-groups-групи-параметрів","Parameter Groups (Групи параметрів)",[3353,5850,5851,5852,5855],{},"Групи параметрів регулюють внутрішні налаштування двигуна СУБД та керування пам'яттю. За замовчуванням до кожного нового інстансу прикріплюється ",[3414,5853,5854],{},"Default Parameter Group",", параметри якої не підлягають модифікації. Для внесення змін необхідно створити кастомну групу параметрів.",[3353,5857,5858],{},"Параметри поділяються на дві категорії за способом застосування:",[3366,5860,5861,5867],{},[3369,5862,5863,5866],{},[3359,5864,5865],{},"Динамічні (Dynamic):"," зміни застосовуються миттєво в рантаймі без необхідності перезавантаження бази даних.",[3369,5868,5869,5872],{},[3359,5870,5871],{},"Статичні (Static):"," для застосування змін потрібен ручний перезапуск (reboot) інстансу бази даних.",[3353,5874,5875],{},"Основні параметри оптимізації для PostgreSQL:",[4852,5877,5878,5894],{},[4855,5879,5880],{},[4858,5881,5882,5885,5888,5891],{},[4861,5883,5884],{"align":4863},"Назва параметра",[4861,5886,5887],{"align":4863},"Тип застосування",[4861,5889,5890],{"align":4863},"Рекомендоване значення",[4861,5892,5893],{"align":4863},"Технічне обґрунтування",[4872,5895,5896,5915,5934,5953,5985],{},[4858,5897,5898,5903,5906,5912],{},[4877,5899,5900],{"align":4863},[3414,5901,5902],{},"shared_buffers",[4877,5904,5905],{"align":4863},"Статичний",[4877,5907,5908,5911],{"align":4863},[3414,5909,5910],{},"25%"," від загального обсягу RAM інстансу.",[4877,5913,5914],{"align":4863},"Визначає обсяг оперативної пам'яті, що виділяється для спільного кешування сторінок даних.",[4858,5916,5917,5922,5924,5931],{},[4877,5918,5919],{"align":4863},[3414,5920,5921],{},"max_connections",[4877,5923,5905],{"align":4863},[4877,5925,5926,5927,5930],{"align":4863},"Залежно від типу інстансу (формула: ",[3414,5928,5929],{},"DBInstanceClassMemory\u002F9531392",").",[4877,5932,5933],{"align":4863},"Встановлює ліміт одночасних клієнтських сесій.",[4858,5935,5936,5941,5944,5950],{},[4877,5937,5938],{"align":4863},[3414,5939,5940],{},"log_min_duration_statement",[4877,5942,5943],{"align":4863},"Динамічний",[4877,5945,5946,5949],{"align":4863},[3414,5947,5948],{},"1000"," (мс)",[4877,5951,5952],{"align":4863},"Визначає поріг тривалості виконання запитів, вище якого вони автоматично реєструються в логах для аналізу повільних SQL-інструкцій.",[4858,5954,5955,5960,5962,5971],{},[4877,5956,5957],{"align":4863},[3414,5958,5959],{},"work_mem",[4877,5961,5943],{"align":4863},[4877,5963,5964,5967,5968],{"align":4863},[3414,5965,5966],{},"16MB"," – ",[3414,5969,5970],{},"64MB",[4877,5972,5973,5974,3417,5977,5980,5981,5984],{"align":4863},"Обсяг пам'яті для виконання внутрішніх операцій сортування (",[3414,5975,5976],{},"ORDER BY",[3414,5978,5979],{},"DISTINCT",") та з'єднань хешуванням (",[3414,5982,5983],{},"HASH JOIN","). Виділяється під кожен запит індивідуально.",[4858,5986,5987,5992,5994,5999],{},[4877,5988,5989],{"align":4863},[3414,5990,5991],{},"log_lock_waits",[4877,5993,5943],{"align":4863},[4877,5995,5996],{"align":4863},[3414,5997,5998],{},"on",[4877,6000,6001,6002,3691],{"align":4863},"Активує логування подій, коли транзакція очікує блокування довше, ніж встановлено параметром ",[3414,6003,6004],{},"deadlock_timeout",[5406,6006,6008],{"id":6007},"option-groups-групи-опцій","Option Groups (Групи опцій)",[3353,6010,6011],{},"Option Groups призначені для активації та керування додатковими хмарними можливостями, які вимагають встановлення стороннього ПЗ на сервері бази даних, ліцензування або специфічної інтеграції. Наприклад:",[3366,6013,6014,6028,6034],{},[3369,6015,6016,6019,6020,6023,6024,6027],{},[3359,6017,6018],{},"PostgreSQL:"," Дозволяє підключати розширення (Extensions), такі як ",[3414,6021,6022],{},"PostGIS"," (геопросторові дані) або ",[3414,6025,6026],{},"pg_cron"," (планувальник завдань).",[3369,6029,6030,6033],{},[3359,6031,6032],{},"SQL Server:"," Використовується для активації функції прозорого шифрування даних (Transparent Data Encryption, TDE), інтеграції зі службами Active Directory або налаштування резервного копіювання безпосередньо в S3 (Native Backup\u002FRestore).",[3369,6035,6036,6039],{},[3359,6037,6038],{},"Oracle:"," Керує додатковими опціями оптимізації та безпеки, такими як Oracle Label Security.",[3778,6041,6042,6043,6045],{},"Для застосунків на базі .NET важливим є контроль над лімітом з'єднань. У PostgreSQL кожне нове підключення створює окремий операційний процес СУБД, що споживає оперативну пам'ять. Якщо ліміт ",[3414,6044,5921],{}," вичерпано, нові спроби підключення завершуватимуться помилками. Для вирішення цієї проблеми слід використовувати RDS Proxy.",[3679,6047],{},[3348,6049,6051],{"id":6050},"моніторинг-та-логування-monitoring-logging-у-amazon-rds","Моніторинг та логування (Monitoring & Logging) у Amazon RDS",[3353,6053,6054],{},"Для контролю за станом бази даних, оптимізації запитів та оперативного реагування на системні інциденти Amazon RDS пропонує багаторівневу систему спостереження.",[3700,6056,6058],{"id":6057},"_1-cloudwatch-metrics-базові-метрики","1. CloudWatch Metrics (Базові метрики)",[3353,6060,6061],{},"Стандартний моніторинг здійснюється через сервіс Amazon CloudWatch, який збирає метрики з частотою від 1 до 5 хвилин на рівні віртуального середовища (hypervisor). Ключові метрики:",[3366,6063,6064,6070,6076,6082],{},[3369,6065,6066,6069],{},[3414,6067,6068],{},"CPUUtilization",": Відсоток завантаження процесора.",[3369,6071,6072,6075],{},[3414,6073,6074],{},"FreeableMemory",": Обсяг доступної оперативної пам'яті.",[3369,6077,6078,6081],{},[3414,6079,6080],{},"DatabaseConnections",": Кількість активних сесій.",[3369,6083,6084,6087,6088,6091],{},[3414,6085,6086],{},"ReadIOPS"," \u002F ",[3414,6089,6090],{},"WriteIOPS",": Середня кількість операцій читання\u002Fзапису на секунду.",[3700,6093,6095],{"id":6094},"_2-enhanced-monitoring-розширений-моніторинг","2. Enhanced Monitoring (Розширений моніторинг)",[3353,6097,6098,6099,6102],{},"Enhanced Monitoring збирає метрики безпосередньо з операційної системи хоста, на якому розгорнуто СУБД, за допомогою спеціального агента. Це дозволяє отримувати детальну інформацію про використання CPU окремими процесами бази даних, стан пам'яті та дискової підсистеми з гранулярністю до ",[3359,6100,6101],{},"1 секунди",". Це є критично важливим для локалізації короткочасних сплесків навантаження (micro-bursts).",[3700,6104,6106],{"id":6105},"_3-performance-insights-аналіз-продуктивності","3. Performance Insights (Аналіз продуктивності)",[3353,6108,6109],{},"Performance Insights — це візуальний інструмент для оцінки навантаження на базу даних за категоріями очікування (DB Load за Wait Events). Він дозволяє виявити:",[3366,6111,6112,6115,6118],{},[3369,6113,6114],{},"Які саме SQL-запити генерують найбільше навантаження.",[3369,6116,6117],{},"З яких хостів (IP-адрес застосунків) або від яких користувачів СУБД надходить найбільше запитів.",[3369,6119,6120],{},"Які саме типи блокувань чи очікувань ресурсів (наприклад, очікування дискового введення-виведення або блокування рядків) гальмують роботу застосунку.",[3700,6122,6124],{"id":6123},"_4-експорт-журналів-cloudwatch-logs-integration","4. Експорт журналів (CloudWatch Logs Integration)",[3353,6126,6127,6128,6131],{},"Amazon RDS дозволяє налаштувати автоматичний експорт логів СУБД (для PostgreSQL: ",[3414,6129,6130],{},"postgresql.log",", журналів повільних записів slow query logs, логів транзакцій) до Amazon CloudWatch Logs. Це дає змогу налаштувати фільтри метрик, створювати сповіщення (Alarms) на основі критичних помилок у логах та здійснювати довгострокове архівування подій.",[3679,6133],{},[3348,6135,6137],{"id":6136},"amazon-aurora-хмарно-орієнтована-розподілена-архітектура-субд","Amazon Aurora — хмарно-орієнтована розподілена архітектура СУБД",[3353,6139,6140,6142],{},[3359,6141,3676],{}," — це сучасна власна реляційна база даних від AWS, розроблена спеціально для хмарного середовища. Вона зберігає повну сумісність на рівні драйверів та клієнтських протоколів із СУБД PostgreSQL та MySQL, але кардинально відрізняється від класичного RDS на архітектурному рівні.",[3700,6144,6146],{"id":6145},"відокремлення-обчислень-від-сховища-даних-compute-and-storage-separation","Відокремлення обчислень від сховища даних (Compute and Storage Separation)",[3353,6148,6149],{},"Ключовою архітектурною інновацією Amazon Aurora є повне розділення шару обчислень (Compute Nodes — процесорні потужності та оперативна пам'ять) та розподіленого шару збереження даних (Shared Storage Layer).",[3366,6151,6152,6158],{},[3369,6153,6154,6157],{},[3359,6155,6156],{},"У класичному RDS:"," Кожен інстанс має власний виділений дисковий том EBS. При створенні репліки дані фізично дублюються на окремий диск іншої віртуальної машини.",[3369,6159,6160,6163],{},[3359,6161,6162],{},"В Aurora:"," Існує єдиний віртуальний розподілений пул сховища (Aurora Storage Volume), який динамічно збільшується до 128 TB. Обчислювальні вузли (один Writer для запису та до 15 Reader інстансів для читання) підключаються до цього спільного пулу.",[5406,6165,6167],{"id":6166},"механізм-реплікації-та-кворуму-сховища-aurora-storage","Механізм реплікації та кворуму сховища Aurora Storage",[3353,6169,6170,6171,6174],{},"Розподілений шар зберігання даних Aurora автоматично створює ",[3359,6172,6173],{},"6 копій кожного блоку даних та розподіляє їх між 3 зонами доступності (AZ)"," в обраному регіоні.",[3366,6176,6177,6183,6189],{},[3369,6178,6179,6182],{},[3359,6180,6181],{},"Надійність:"," Вихід з ладу однієї зони доступності (2 копії даних) або навіть двох (3 копії) не призводить до втрати даних.",[3369,6184,6185,6188],{},[3359,6186,6187],{},"Кворум запису (Write Quorum):"," Для успішного завершення операції запису достатньо підтвердження від 4 з 6 копій (4\u002F6 write quorum), що забезпечує надзвичайно високу швидкість обробки транзакцій.",[3369,6190,6191,6194],{},[3359,6192,6193],{},"Кворум читання (Read Quorum):"," Для зчитування достатньо звернутися до 3 з 6 копій (3\u002F6 read quorum), хоча зазвичай читання виконується локально з кешу обчислювального вузла.",[3423,6196,6197],{},[3426,6198,6200],{"className":3428,"code":6199,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"RDS PostgreSQL (класичний)\" as RDS #fee2e2 {\n    node \"Instance\\n(compute + storage)\" as INST1\n    note bottom of INST1: Storage 20 GB на диску\\ninstance. Replica = \\nокремий диск.\n}\n\npackage \"Amazon Aurora PostgreSQL\" as AURORA #dcfce7 {\n    node \"Writer Instance\\n(compute)\" as WRITER #dbeafe\n    node \"Reader 1\\n(compute)\" as R1 #d1fae5\n    node \"Reader 2\\n(compute)\" as R2 #d1fae5\n\n    database \"Aurora Storage\\n(розподілений, 6 копій у 3 AZ)\\nАвтоматично росте до 128 TB\" as STORE #f3e8ff\n\n    WRITER --> STORE : запис\n    R1 --> STORE : читання\n    R2 --> STORE : читання\n    WRITER ..> R1 : репліка\\n~10 мс (не копія storage!)\n    WRITER ..> R2 : репліка\\n~10 мс\n}\n@enduml\n",[3414,6201,6202,6206,6210,6214,6218,6223,6228,6233,6237,6241,6246,6251,6256,6261,6265,6270,6274,6279,6284,6289,6294,6299,6303],{"__ignoreMap":3431},[3435,6203,6204],{"class":3437,"line":3438},[3435,6205,3441],{},[3435,6207,6208],{"class":3437,"line":3444},[3435,6209,3447],{},[3435,6211,6212],{"class":3437,"line":3450},[3435,6213,3453],{},[3435,6215,6216],{"class":3437,"line":3456},[3435,6217,3460],{"emptyLinePlaceholder":3459},[3435,6219,6220],{"class":3437,"line":3463},[3435,6221,6222],{},"package \"RDS PostgreSQL (класичний)\" as RDS #fee2e2 {\n",[3435,6224,6225],{"class":3437,"line":3469},[3435,6226,6227],{},"    node \"Instance\\n(compute + storage)\" as INST1\n",[3435,6229,6230],{"class":3437,"line":3475},[3435,6231,6232],{},"    note bottom of INST1: Storage 20 GB на диску\\ninstance. Replica = \\nокремий диск.\n",[3435,6234,6235],{"class":3437,"line":3481},[3435,6236,3532],{},[3435,6238,6239],{"class":3437,"line":3487},[3435,6240,3460],{"emptyLinePlaceholder":3459},[3435,6242,6243],{"class":3437,"line":3493},[3435,6244,6245],{},"package \"Amazon Aurora PostgreSQL\" as AURORA #dcfce7 {\n",[3435,6247,6248],{"class":3437,"line":3499},[3435,6249,6250],{},"    node \"Writer Instance\\n(compute)\" as WRITER #dbeafe\n",[3435,6252,6253],{"class":3437,"line":3505},[3435,6254,6255],{},"    node \"Reader 1\\n(compute)\" as R1 #d1fae5\n",[3435,6257,6258],{"class":3437,"line":3511},[3435,6259,6260],{},"    node \"Reader 2\\n(compute)\" as R2 #d1fae5\n",[3435,6262,6263],{"class":3437,"line":3517},[3435,6264,3460],{"emptyLinePlaceholder":3459},[3435,6266,6267],{"class":3437,"line":3523},[3435,6268,6269],{},"    database \"Aurora Storage\\n(розподілений, 6 копій у 3 AZ)\\nАвтоматично росте до 128 TB\" as STORE #f3e8ff\n",[3435,6271,6272],{"class":3437,"line":3529},[3435,6273,3460],{"emptyLinePlaceholder":3459},[3435,6275,6276],{"class":3437,"line":3535},[3435,6277,6278],{},"    WRITER --> STORE : запис\n",[3435,6280,6281],{"class":3437,"line":3540},[3435,6282,6283],{},"    R1 --> STORE : читання\n",[3435,6285,6286],{"class":3437,"line":3546},[3435,6287,6288],{},"    R2 --> STORE : читання\n",[3435,6290,6291],{"class":3437,"line":3552},[3435,6292,6293],{},"    WRITER ..> R1 : репліка\\n~10 мс (не копія storage!)\n",[3435,6295,6296],{"class":3437,"line":3558},[3435,6297,6298],{},"    WRITER ..> R2 : репліка\\n~10 мс\n",[3435,6300,6301],{"class":3437,"line":3563},[3435,6302,3532],{},[3435,6304,6305],{"class":3437,"line":3569},[3435,6306,3640],{},[3353,6308,6309],{},[3359,6310,6311],{},"Переваги Aurora над RDS PostgreSQL:",[4852,6313,6314,6326],{},[4855,6315,6316],{},[4858,6317,6318,6320,6323],{},[4861,6319,4864],{},[4861,6321,6322],{},"RDS PostgreSQL",[4861,6324,6325],{},"Aurora PostgreSQL",[4872,6327,6328,6339,6350,6361,6372,6383],{},[4858,6329,6330,6333,6336],{},[4877,6331,6332],{},"Storage",[4877,6334,6335],{},"EBS (один диск)",[4877,6337,6338],{},"Розподілений (6 копій у 3 AZ)",[4858,6340,6341,6344,6347],{},[4877,6342,6343],{},"Failover",[4877,6345,6346],{},"~60–120 сек",[4877,6348,6349],{},"~30 сек",[4858,6351,6352,6355,6358],{},[4877,6353,6354],{},"Read Replicas",[4877,6356,6357],{},"до 5, кожна = окрема реплікація",[4877,6359,6360],{},"до 15, спільний storage",[4858,6362,6363,6366,6369],{},[4877,6364,6365],{},"Storage auto-scaling",[4877,6367,6368],{},"Ручне збільшення",[4877,6370,6371],{},"Автоматично до 128 TB",[4858,6373,6374,6377,6380],{},[4877,6375,6376],{},"Ціна",[4877,6378,6379],{},"Дешевше",[4877,6381,6382],{},"~20% дорожче за instance",[4858,6384,6385,6388,6391],{},[4877,6386,6387],{},"Резервні копії",[4877,6389,6390],{},"7–35 днів, PITR",[4877,6392,6393],{},"Безперервні, миттєві snapshot",[3700,6395,6397],{"id":6396},"масштабування-за-допомогою-aurora-serverless-v2","Масштабування за допомогою Aurora Serverless v2",[3353,6399,6400,6401,6404],{},"Технологія ",[3359,6402,6403],{},"Aurora Serverless v2"," забезпечує повністю автоматичне динамічне масштабування обчислювальних ресурсів бази даних у режимі реального часу.",[3366,6406,6407,6417,6423],{},[3369,6408,6409,6412,6413,6416],{},[3359,6410,6411],{},"Одиниця виміру (ACU):"," Масштабування обчислювальних ресурсів (процесорної потужності та RAM) вимірюється в ",[3359,6414,6415],{},"Aurora Capacity Units (ACU)",". 1 ACU еквівалентний приблизно 2 GB оперативної пам'яті та відповідній потужності процесорних ядер.",[3369,6418,6419,6422],{},[3359,6420,6421],{},"Механізм масштабування:"," Замість тривалого процесу зміни класу інстансу (який у класичному RDS потребує хвилин та може супроводжуватися короткочасним даунтаймом), Aurora Serverless v2 динамічно збільшує або зменшує кількість ACU (наприклад, від 0.5 до 128 ACU) за мілісекунди без переривання активних транзакцій та з'єднань користувача.",[3369,6424,6425,6428],{},[3359,6426,6427],{},"Оптимізація витрат:"," Оплата здійснюється виключно за фактично спожиті ACU за секунду, що робить цю технологію ідеальною для систем із непередбачуваним або вираженим періодичним навантаженням.",[3679,6430],{},[3348,6432,6434],{"id":6433},"rds-proxy-управління-пулом-зєднань","RDS Proxy — управління пулом з'єднань",[3353,6436,6437,6440,6441,6444],{},[3359,6438,6439],{},"Проблема Connection Pooling:"," кожне з'єднання до PostgreSQL — це окремий процес на сервері бази (~5–10 MB RAM кожен). При ",[3414,6442,6443],{},"max_connections = 100"," і кількох EC2 instances по 30 з'єднань кожен — ресурс швидко вичерпується. При масштабуванні на Lambda (тисячі паралельних invocations) ситуація стає критичною: Lambda не підтримує постійний pool.",[3353,6446,6447,6450,6451,6454],{},[3359,6448,6449],{},"RDS Proxy"," — це повністю керований проксі-сервер між вашим застосунком і RDS. Він підтримує ",[3359,6452,6453],{},"довготривалий пул з'єднань"," до бази і мультиплексує багато короткоживучих з'єднань від клієнтів у кілька постійних.",[3423,6456,6457],{},[3426,6458,6460],{"className":3428,"code":6459,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\npackage \"Без RDS Proxy\" as NOPR #fee2e2 {\n    node \"Lambda ×50\" as LAM1\n    database \"RDS\\nmax_conn=100\\n❌ 50 Lambda = 50 з'єднань\" as DB1\n    LAM1 --> DB1 : 50 з'єднань\\n(кожна Lambda = окреме!)\n}\n\npackage \"З RDS Proxy\" as WITHPR #dcfce7 {\n    node \"Lambda ×50\" as LAM2\n    node \"RDS Proxy\\n(пул: 5 з'єднань до RDS)\" as PRX #dbeafe\n    database \"RDS\\n✅ лише 5 з'єднань\" as DB2\n    LAM2 --> PRX : 50 з'єднань до Proxy\n    PRX --> DB2 : 5 постійних з'єднань\\n(мультиплексування)\n}\n@enduml\n",[3414,6461,6462,6466,6470,6474,6478,6483,6488,6493,6498,6502,6506,6511,6516,6521,6526,6531,6536,6540],{"__ignoreMap":3431},[3435,6463,6464],{"class":3437,"line":3438},[3435,6465,3441],{},[3435,6467,6468],{"class":3437,"line":3444},[3435,6469,3447],{},[3435,6471,6472],{"class":3437,"line":3450},[3435,6473,3453],{},[3435,6475,6476],{"class":3437,"line":3456},[3435,6477,3460],{"emptyLinePlaceholder":3459},[3435,6479,6480],{"class":3437,"line":3463},[3435,6481,6482],{},"package \"Без RDS Proxy\" as NOPR #fee2e2 {\n",[3435,6484,6485],{"class":3437,"line":3469},[3435,6486,6487],{},"    node \"Lambda ×50\" as LAM1\n",[3435,6489,6490],{"class":3437,"line":3475},[3435,6491,6492],{},"    database \"RDS\\nmax_conn=100\\n❌ 50 Lambda = 50 з'єднань\" as DB1\n",[3435,6494,6495],{"class":3437,"line":3481},[3435,6496,6497],{},"    LAM1 --> DB1 : 50 з'єднань\\n(кожна Lambda = окреме!)\n",[3435,6499,6500],{"class":3437,"line":3487},[3435,6501,3532],{},[3435,6503,6504],{"class":3437,"line":3493},[3435,6505,3460],{"emptyLinePlaceholder":3459},[3435,6507,6508],{"class":3437,"line":3499},[3435,6509,6510],{},"package \"З RDS Proxy\" as WITHPR #dcfce7 {\n",[3435,6512,6513],{"class":3437,"line":3505},[3435,6514,6515],{},"    node \"Lambda ×50\" as LAM2\n",[3435,6517,6518],{"class":3437,"line":3511},[3435,6519,6520],{},"    node \"RDS Proxy\\n(пул: 5 з'єднань до RDS)\" as PRX #dbeafe\n",[3435,6522,6523],{"class":3437,"line":3517},[3435,6524,6525],{},"    database \"RDS\\n✅ лише 5 з'єднань\" as DB2\n",[3435,6527,6528],{"class":3437,"line":3523},[3435,6529,6530],{},"    LAM2 --> PRX : 50 з'єднань до Proxy\n",[3435,6532,6533],{"class":3437,"line":3529},[3435,6534,6535],{},"    PRX --> DB2 : 5 постійних з'єднань\\n(мультиплексування)\n",[3435,6537,6538],{"class":3437,"line":3535},[3435,6539,3532],{},[3435,6541,6542],{"class":3437,"line":3540},[3435,6543,3640],{},[3353,6545,6546],{},[3359,6547,6548],{},"Додаткові переваги RDS Proxy:",[3366,6550,6551,6557,6563],{},[3369,6552,6553,6556],{},[3359,6554,6555],{},"Failover:"," при Multi-AZ failover Proxy автоматично перенаправляє з'єднання, не розриваючи існуючі сесії клієнтів (замість ~60 сек → ~5 сек для застосунку)",[3369,6558,6559,6562],{},[3359,6560,6561],{},"IAM Authentication:"," Proxy підтримує IAM-токени — Lambda функції можуть підключатись без паролів",[3369,6564,6565,6568],{},[3359,6566,6567],{},"Secret Manager інтеграція:"," пароль бази зберігається у Secrets Manager, Proxy сам ротує і читає — ваш код паролів не бачить",[3353,6570,6571,6574],{},[3359,6572,6573],{},"Connection string до RDS Proxy"," виглядає ідентично до прямого підключення — лише hostname змінюється на endpoint Proxy. Для .NET\u002FEF Core нічого більше змінювати не потрібно.",[3679,6576],{},[3348,6578,6580],{"id":6579},"підключення-net-до-rds-entity-framework-core","Підключення .NET до RDS — Entity Framework Core",[3353,6582,6583],{},"Розглянемо повний цикл: від connection string до Code-First міграцій на production RDS instance.",[3700,6585,6587],{"id":6586},"встановлення-пакетів","Встановлення пакетів",[3426,6589,6591],{"className":5213,"code":6590,"language":5215,"meta":3431,"style":3431},"# PostgreSQL провайдер для EF Core\ndotnet add package Npgsql.EntityFrameworkCore.PostgreSQL\n\n# SQL Server провайдер (якщо RDS SQL Server)\n# dotnet add package Microsoft.EntityFrameworkCore.SqlServer\n\n# Інструменти для міграцій\ndotnet add package Microsoft.EntityFrameworkCore.Design\n",[3414,6592,6593,6598,6612,6616,6621,6626,6630,6635],{"__ignoreMap":3431},[3435,6594,6595],{"class":3437,"line":3438},[3435,6596,6597],{"class":4461},"# PostgreSQL провайдер для EF Core\n",[3435,6599,6600,6603,6606,6609],{"class":3437,"line":3444},[3435,6601,6602],{"class":4535},"dotnet",[3435,6604,6605],{"class":4473}," add",[3435,6607,6608],{"class":4473}," package",[3435,6610,6611],{"class":4473}," Npgsql.EntityFrameworkCore.PostgreSQL\n",[3435,6613,6614],{"class":3437,"line":3450},[3435,6615,3460],{"emptyLinePlaceholder":3459},[3435,6617,6618],{"class":3437,"line":3456},[3435,6619,6620],{"class":4461},"# SQL Server провайдер (якщо RDS SQL Server)\n",[3435,6622,6623],{"class":3437,"line":3463},[3435,6624,6625],{"class":4461},"# dotnet add package Microsoft.EntityFrameworkCore.SqlServer\n",[3435,6627,6628],{"class":3437,"line":3469},[3435,6629,3460],{"emptyLinePlaceholder":3459},[3435,6631,6632],{"class":3437,"line":3475},[3435,6633,6634],{"class":4461},"# Інструменти для міграцій\n",[3435,6636,6637,6639,6641,6643],{"class":3437,"line":3481},[3435,6638,6602],{"class":4535},[3435,6640,6605],{"class":4473},[3435,6642,6608],{"class":4473},[3435,6644,6645],{"class":4473}," Microsoft.EntityFrameworkCore.Design\n",[3700,6647,6649],{"id":6648},"dbcontext-та-моделі","DbContext та моделі",[3426,6651,6653],{"className":4452,"code":6652,"language":4454,"meta":3431,"style":3431},"\u002F\u002F Models\u002FProduct.cs\npublic class Product\n{\n    public int Id { get; set; }\n    public string Name { get; set; } = string.Empty;\n    public decimal Price { get; set; }\n    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;\n}\n\n\u002F\u002F Data\u002FAppDbContext.cs\npublic class AppDbContext(DbContextOptions\u003CAppDbContext> options) : DbContext(options)\n{\n    public DbSet\u003CProduct> Products => Set\u003CProduct>();\n\n    protected override void OnModelCreating(ModelBuilder modelBuilder)\n    {\n        modelBuilder.Entity\u003CProduct>(entity =>\n        {\n            entity.Property(p => p.Name).HasMaxLength(200).IsRequired();\n            entity.Property(p => p.Price).HasPrecision(18, 2);\n        });\n    }\n}\n",[3414,6654,6655,6660,6669,6673,6698,6728,6748,6778,6782,6786,6791,6826,6830,6858,6862,6886,6890,6911,6916,6956,6994,6999,7003],{"__ignoreMap":3431},[3435,6656,6657],{"class":3437,"line":3438},[3435,6658,6659],{"class":4461},"\u002F\u002F Models\u002FProduct.cs\n",[3435,6661,6662,6664,6666],{"class":3437,"line":3444},[3435,6663,4678],{"class":4677},[3435,6665,4681],{"class":4677},[3435,6667,6668],{"class":4542}," Product\n",[3435,6670,6671],{"class":3437,"line":3450},[3435,6672,4468],{"class":4467},[3435,6674,6675,6677,6680,6683,6686,6689,6692,6695],{"class":3437,"line":3456},[3435,6676,4715],{"class":4677},[3435,6678,6679],{"class":4677}," int",[3435,6681,6682],{"class":4524}," Id",[3435,6684,6685],{"class":4467}," { ",[3435,6687,6688],{"class":4677},"get",[3435,6690,6691],{"class":4467},"; ",[3435,6693,6694],{"class":4677},"set",[3435,6696,6697],{"class":4467},"; }\n",[3435,6699,6700,6702,6705,6708,6710,6712,6714,6716,6719,6721,6723,6726],{"class":3437,"line":3463},[3435,6701,4715],{"class":4677},[3435,6703,6704],{"class":4677}," string",[3435,6706,6707],{"class":4524}," Name",[3435,6709,6685],{"class":4467},[3435,6711,6688],{"class":4677},[3435,6713,6691],{"class":4467},[3435,6715,6694],{"class":4677},[3435,6717,6718],{"class":4467},"; } = ",[3435,6720,5728],{"class":4677},[3435,6722,3691],{"class":4467},[3435,6724,6725],{"class":4524},"Empty",[3435,6727,5664],{"class":4467},[3435,6729,6730,6732,6735,6738,6740,6742,6744,6746],{"class":3437,"line":3469},[3435,6731,4715],{"class":4677},[3435,6733,6734],{"class":4677}," decimal",[3435,6736,6737],{"class":4524}," Price",[3435,6739,6685],{"class":4467},[3435,6741,6688],{"class":4677},[3435,6743,6691],{"class":4467},[3435,6745,6694],{"class":4677},[3435,6747,6697],{"class":4467},[3435,6749,6750,6752,6755,6758,6760,6762,6764,6766,6768,6771,6773,6776],{"class":3437,"line":3475},[3435,6751,4715],{"class":4677},[3435,6753,6754],{"class":4542}," DateTime",[3435,6756,6757],{"class":4524}," CreatedAt",[3435,6759,6685],{"class":4467},[3435,6761,6688],{"class":4677},[3435,6763,6691],{"class":4467},[3435,6765,6694],{"class":4677},[3435,6767,6718],{"class":4467},[3435,6769,6770],{"class":4524},"DateTime",[3435,6772,3691],{"class":4467},[3435,6774,6775],{"class":4524},"UtcNow",[3435,6777,5664],{"class":4467},[3435,6779,6780],{"class":3437,"line":3481},[3435,6781,3532],{"class":4467},[3435,6783,6784],{"class":3437,"line":3487},[3435,6785,3460],{"emptyLinePlaceholder":3459},[3435,6787,6788],{"class":3437,"line":3493},[3435,6789,6790],{"class":4461},"\u002F\u002F Data\u002FAppDbContext.cs\n",[3435,6792,6793,6795,6797,6800,6802,6805,6807,6809,6812,6814,6817,6820,6822,6824],{"class":3437,"line":3499},[3435,6794,4678],{"class":4677},[3435,6796,4681],{"class":4677},[3435,6798,6799],{"class":4542}," AppDbContext",[3435,6801,4565],{"class":4467},[3435,6803,6804],{"class":4542},"DbContextOptions",[3435,6806,4539],{"class":4467},[3435,6808,4543],{"class":4542},[3435,6810,6811],{"class":4467},"> ",[3435,6813,4549],{"class":4524},[3435,6815,6816],{"class":4467},") : ",[3435,6818,6819],{"class":4542},"DbContext",[3435,6821,4565],{"class":4467},[3435,6823,4549],{"class":4524},[3435,6825,4701],{"class":4467},[3435,6827,6828],{"class":3437,"line":3505},[3435,6829,4468],{"class":4467},[3435,6831,6832,6834,6837,6839,6841,6843,6845,6848,6851,6853,6855],{"class":3437,"line":3511},[3435,6833,4715],{"class":4677},[3435,6835,6836],{"class":4542}," DbSet",[3435,6838,4539],{"class":4467},[3435,6840,4731],{"class":4542},[3435,6842,6811],{"class":4467},[3435,6844,4752],{"class":4524},[3435,6846,6847],{"class":4467}," => ",[3435,6849,6850],{"class":4535},"Set",[3435,6852,4539],{"class":4467},[3435,6854,4731],{"class":4542},[3435,6856,6857],{"class":4467},">();\n",[3435,6859,6860],{"class":3437,"line":3517},[3435,6861,3460],{"emptyLinePlaceholder":3459},[3435,6863,6864,6867,6870,6873,6876,6878,6881,6884],{"class":3437,"line":3523},[3435,6865,6866],{"class":4677},"    protected",[3435,6868,6869],{"class":4677}," override",[3435,6871,6872],{"class":4677}," void",[3435,6874,6875],{"class":4535}," OnModelCreating",[3435,6877,4565],{"class":4467},[3435,6879,6880],{"class":4542},"ModelBuilder",[3435,6882,6883],{"class":4524}," modelBuilder",[3435,6885,4701],{"class":4467},[3435,6887,6888],{"class":3437,"line":3529},[3435,6889,4794],{"class":4467},[3435,6891,6892,6895,6897,6900,6902,6904,6906,6909],{"class":3437,"line":3535},[3435,6893,6894],{"class":4524},"        modelBuilder",[3435,6896,3691],{"class":4467},[3435,6898,6899],{"class":4535},"Entity",[3435,6901,4539],{"class":4467},[3435,6903,4731],{"class":4542},[3435,6905,4546],{"class":4467},[3435,6907,6908],{"class":4524},"entity",[3435,6910,4552],{"class":4467},[3435,6912,6913],{"class":3437,"line":3540},[3435,6914,6915],{"class":4467},"        {\n",[3435,6917,6918,6921,6923,6926,6928,6930,6932,6934,6936,6939,6941,6944,6946,6949,6951,6954],{"class":3437,"line":3546},[3435,6919,6920],{"class":4524},"            entity",[3435,6922,3691],{"class":4467},[3435,6924,6925],{"class":4535},"Property",[3435,6927,4565],{"class":4467},[3435,6929,3353],{"class":4524},[3435,6931,6847],{"class":4467},[3435,6933,3353],{"class":4524},[3435,6935,3691],{"class":4467},[3435,6937,6938],{"class":4524},"Name",[3435,6940,5930],{"class":4467},[3435,6942,6943],{"class":4535},"HasMaxLength",[3435,6945,4565],{"class":4467},[3435,6947,6948],{"class":5337},"200",[3435,6950,5930],{"class":4467},[3435,6952,6953],{"class":4535},"IsRequired",[3435,6955,4760],{"class":4467},[3435,6957,6958,6960,6962,6964,6966,6968,6970,6972,6974,6977,6979,6982,6984,6987,6989,6992],{"class":3437,"line":3552},[3435,6959,6920],{"class":4524},[3435,6961,3691],{"class":4467},[3435,6963,6925],{"class":4535},[3435,6965,4565],{"class":4467},[3435,6967,3353],{"class":4524},[3435,6969,6847],{"class":4467},[3435,6971,3353],{"class":4524},[3435,6973,3691],{"class":4467},[3435,6975,6976],{"class":4524},"Price",[3435,6978,5930],{"class":4467},[3435,6980,6981],{"class":4535},"HasPrecision",[3435,6983,4565],{"class":4467},[3435,6985,6986],{"class":5337},"18",[3435,6988,3417],{"class":4467},[3435,6990,6991],{"class":5337},"2",[3435,6993,4816],{"class":4467},[3435,6995,6996],{"class":3437,"line":3558},[3435,6997,6998],{"class":4467},"        });\n",[3435,7000,7001],{"class":3437,"line":3563},[3435,7002,3490],{"class":4467},[3435,7004,7005],{"class":3437,"line":3569},[3435,7006,3532],{"class":4467},[3700,7008,7010],{"id":7009},"connection-string-безпечне-зберігання","Connection String — безпечне зберігання",[3353,7012,7013,7016,7017,7020],{},[3359,7014,7015],{},"Ніколи"," не хардкодьте паролі у ",[3414,7018,7019],{},"appsettings.json"," у репозиторії. Для .NET на AWS є кілька підходів:",[3426,7022,7024],{"className":4452,"code":7023,"language":4454,"meta":3431,"style":3431},"\u002F\u002F Program.cs — зчитуємо з AWS Secrets Manager\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n{\n    \u002F\u002F Варіант 1: через Environment Variable (задається в ECS Task Definition \u002F EC2 User Data)\n    var connStr = Environment.GetEnvironmentVariable(\"DB_CONNECTION_STRING\")\n        ?? builder.Configuration.GetConnectionString(\"Default\");\n\n    options.UseNpgsql(connStr, npgsqlOptions =>\n    {\n        \u002F\u002F Автоматичний retry при transient помилках (failover, мережеві збої)\n        npgsqlOptions.EnableRetryOnFailure(\n            maxRetryCount: 3,\n            maxRetryDelay: TimeSpan.FromSeconds(5),\n            errorCodesToAdd: null);\n    });\n});\n",[3414,7025,7026,7031,7053,7057,7062,7087,7109,7113,7133,7137,7142,7154,7166,7189,7201,7206],{"__ignoreMap":3431},[3435,7027,7028],{"class":3437,"line":3438},[3435,7029,7030],{"class":4461},"\u002F\u002F Program.cs — зчитуємо з AWS Secrets Manager\n",[3435,7032,7033,7035,7037,7039,7041,7043,7045,7047,7049,7051],{"class":3437,"line":3444},[3435,7034,4525],{"class":4524},[3435,7036,3691],{"class":4467},[3435,7038,4530],{"class":4524},[3435,7040,3691],{"class":4467},[3435,7042,4536],{"class":4535},[3435,7044,4539],{"class":4467},[3435,7046,4543],{"class":4542},[3435,7048,4546],{"class":4467},[3435,7050,4549],{"class":4524},[3435,7052,4552],{"class":4467},[3435,7054,7055],{"class":3437,"line":3450},[3435,7056,4468],{"class":4467},[3435,7058,7059],{"class":3437,"line":3456},[3435,7060,7061],{"class":4461},"    \u002F\u002F Варіант 1: через Environment Variable (задається в ECS Task Definition \u002F EC2 User Data)\n",[3435,7063,7064,7067,7070,7072,7075,7077,7080,7082,7085],{"class":3437,"line":3463},[3435,7065,7066],{"class":4677},"    var",[3435,7068,7069],{"class":4524}," connStr",[3435,7071,5300],{"class":4467},[3435,7073,7074],{"class":4524},"Environment",[3435,7076,3691],{"class":4467},[3435,7078,7079],{"class":4535},"GetEnvironmentVariable",[3435,7081,4565],{"class":4467},[3435,7083,7084],{"class":4473},"\"DB_CONNECTION_STRING\"",[3435,7086,4701],{"class":4467},[3435,7088,7089,7092,7094,7096,7098,7100,7102,7104,7107],{"class":3437,"line":3469},[3435,7090,7091],{"class":4467},"        ?? ",[3435,7093,4525],{"class":4524},[3435,7095,3691],{"class":4467},[3435,7097,4572],{"class":4524},[3435,7099,3691],{"class":4467},[3435,7101,4577],{"class":4535},[3435,7103,4565],{"class":4467},[3435,7105,7106],{"class":4473},"\"Default\"",[3435,7108,4816],{"class":4467},[3435,7110,7111],{"class":3437,"line":3475},[3435,7112,3460],{"emptyLinePlaceholder":3459},[3435,7114,7115,7117,7119,7121,7123,7126,7128,7131],{"class":3437,"line":3481},[3435,7116,4557],{"class":4524},[3435,7118,3691],{"class":4467},[3435,7120,4562],{"class":4535},[3435,7122,4565],{"class":4467},[3435,7124,7125],{"class":4524},"connStr",[3435,7127,3417],{"class":4467},[3435,7129,7130],{"class":4524},"npgsqlOptions",[3435,7132,4552],{"class":4467},[3435,7134,7135],{"class":3437,"line":3487},[3435,7136,4794],{"class":4467},[3435,7138,7139],{"class":3437,"line":3493},[3435,7140,7141],{"class":4461},"        \u002F\u002F Автоматичний retry при transient помилках (failover, мережеві збої)\n",[3435,7143,7144,7147,7149,7152],{"class":3437,"line":3499},[3435,7145,7146],{"class":4524},"        npgsqlOptions",[3435,7148,3691],{"class":4467},[3435,7150,7151],{"class":4535},"EnableRetryOnFailure",[3435,7153,5744],{"class":4467},[3435,7155,7156,7159,7161,7164],{"class":3437,"line":3505},[3435,7157,7158],{"class":4524},"            maxRetryCount",[3435,7160,4485],{"class":4467},[3435,7162,7163],{"class":5337},"3",[3435,7165,4491],{"class":4467},[3435,7167,7168,7171,7173,7176,7178,7181,7183,7186],{"class":3437,"line":3511},[3435,7169,7170],{"class":4524},"            maxRetryDelay",[3435,7172,4485],{"class":4467},[3435,7174,7175],{"class":4524},"TimeSpan",[3435,7177,3691],{"class":4467},[3435,7179,7180],{"class":4535},"FromSeconds",[3435,7182,4565],{"class":4467},[3435,7184,7185],{"class":5337},"5",[3435,7187,7188],{"class":4467},"),\n",[3435,7190,7191,7194,7196,7199],{"class":3437,"line":3517},[3435,7192,7193],{"class":4524},"            errorCodesToAdd",[3435,7195,4485],{"class":4467},[3435,7197,7198],{"class":4677},"null",[3435,7200,4816],{"class":4467},[3435,7202,7203],{"class":3437,"line":3523},[3435,7204,7205],{"class":4467},"    });\n",[3435,7207,7208],{"class":3437,"line":3529},[3435,7209,7210],{"class":4467},"});\n",[3426,7212,7216],{"className":7213,"code":7214,"language":7215,"meta":3431,"style":3431},"language-json shiki shiki-themes light-plus dark-plus dark-plus","\u002F\u002F appsettings.Development.json (тільки для локальної розробки!)\n{\n    \"ConnectionStrings\": {\n        \"Default\": \"Host=localhost;Database=myapp_dev;Username=postgres;Password=localpassword\"\n    }\n}\n","json",[3414,7217,7218,7223,7227,7235,7245,7249],{"__ignoreMap":3431},[3435,7219,7220],{"class":3437,"line":3438},[3435,7221,7222],{"class":4461},"\u002F\u002F appsettings.Development.json (тільки для локальної розробки!)\n",[3435,7224,7225],{"class":3437,"line":3444},[3435,7226,4468],{"class":4467},[3435,7228,7229,7233],{"class":3437,"line":3450},[3435,7230,7232],{"class":7231},"sLwNe","    \"ConnectionStrings\"",[3435,7234,4477],{"class":4467},[3435,7236,7237,7240,7242],{"class":3437,"line":3456},[3435,7238,7239],{"class":7231},"        \"Default\"",[3435,7241,4485],{"class":4467},[3435,7243,7244],{"class":4473},"\"Host=localhost;Database=myapp_dev;Username=postgres;Password=localpassword\"\n",[3435,7246,7247],{"class":3437,"line":3463},[3435,7248,3490],{"class":4467},[3435,7250,7251],{"class":3437,"line":3469},[3435,7252,3532],{"class":4467},[3700,7254,7256],{"id":7255},"code-first-міграції-на-rds","Code-First міграції на RDS",[3426,7258,7260],{"className":5213,"code":7259,"language":5215,"meta":3431,"style":3431},"# Генерувати міграцію локально\ndotnet ef migrations add InitialCreate\n\n# Застосувати до локальної бази\ndotnet ef database update\n\n# Застосувати до RDS (через змінну середовища з реальним connection string)\nDB_CONNECTION_STRING=\"Host=mydb.xxx.rds.amazonaws.com;Database=myapp;Username=app;Password=...\" \\\n  dotnet ef database update --no-build\n",[3414,7261,7262,7267,7282,7286,7291,7303,7307,7312,7324],{"__ignoreMap":3431},[3435,7263,7264],{"class":3437,"line":3438},[3435,7265,7266],{"class":4461},"# Генерувати міграцію локально\n",[3435,7268,7269,7271,7274,7277,7279],{"class":3437,"line":3444},[3435,7270,6602],{"class":4535},[3435,7272,7273],{"class":4473}," ef",[3435,7275,7276],{"class":4473}," migrations",[3435,7278,6605],{"class":4473},[3435,7280,7281],{"class":4473}," InitialCreate\n",[3435,7283,7284],{"class":3437,"line":3450},[3435,7285,3460],{"emptyLinePlaceholder":3459},[3435,7287,7288],{"class":3437,"line":3456},[3435,7289,7290],{"class":4461},"# Застосувати до локальної бази\n",[3435,7292,7293,7295,7297,7300],{"class":3437,"line":3463},[3435,7294,6602],{"class":4535},[3435,7296,7273],{"class":4473},[3435,7298,7299],{"class":4473}," database",[3435,7301,7302],{"class":4473}," update\n",[3435,7304,7305],{"class":3437,"line":3469},[3435,7306,3460],{"emptyLinePlaceholder":3459},[3435,7308,7309],{"class":3437,"line":3475},[3435,7310,7311],{"class":4461},"# Застосувати до RDS (через змінну середовища з реальним connection string)\n",[3435,7313,7314,7317,7320,7322],{"class":3437,"line":3481},[3435,7315,7316],{"class":4524},"DB_CONNECTION_STRING",[3435,7318,7319],{"class":4467},"=",[3435,7321,4488],{"class":4473},[3435,7323,5237],{"class":4535},[3435,7325,7326,7329,7331,7333,7336],{"class":3437,"line":3487},[3435,7327,7328],{"class":4473},"  dotnet",[3435,7330,7273],{"class":4473},[3435,7332,7299],{"class":4473},[3435,7334,7335],{"class":4473}," update",[3435,7337,7338],{"class":4677}," --no-build\n",[4248,7340,7341,7343,7344,7347,7348,7351,7352,7355],{},[3359,7342,7015],{}," не запускайте ",[3414,7345,7346],{},"database update"," автоматично при старті застосунку (",[3414,7349,7350],{},"context.Database.Migrate()"," у ",[3414,7353,7354],{},"Program.cs",") у production. Міграція може займати хвилини на великій базі, заблокувати таблиці та покласти застосунок. Виконуйте міграції як окремий крок у CI\u002FCD пайплайні перед деплоєм нової версії.",[3700,7357,7359],{"id":7358},"enableretryonfailure-чому-це-критично","EnableRetryOnFailure — чому це критично",[3353,7361,7362,7363,7365],{},"При Multi-AZ failover (~60–120 сек) існуючі з'єднання до primary instance розриваються. Без retry-логіки ваш API поверне 500 на всі запити під час failover. З ",[3414,7364,7151],{}," Npgsql автоматично повторить запит — і до моменту, коли DNS оновиться на новий primary, більшість запитів відновиться без помилок для кінцевого користувача.",[3679,7367],{},[3348,7369,7371],{"id":7370},"практичний-приклад-net-web-api-rds-postgresql-від-а-до-я","Практичний приклад: .NET Web API + RDS PostgreSQL від А до Я",[3406,7373,7374],{},"Цей розділ охоплює повний цикл: від нуля до працюючого .NET Web API на EC2, підключеного до RDS PostgreSQL. Всі кроки виконуються послідовно.",[3423,7376,7377],{},[3426,7378,7380],{"className":3428,"code":7379,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nactor \"Розробник\" as DEV\n\npackage \"Локально\" as LOCAL #fef3c7 {\n    component \".NET проєкт\\n(dotnet new webapi)\" as DOTNET\n}\n\npackage \"AWS (eu-central-1)\" as AWS {\n    package \"VPC (Default)\" as VPC {\n        package \"Public Subnet\" as PUB #dbeafe {\n            node \"EC2 t2.micro\\nUbuntu 24.04\\ndotnet-api-sg\" as EC2\n        }\n        package \"Private Subnet\" as PRIV #dcfce7 {\n            database \"RDS PostgreSQL\\ndb.t3.micro\\nrds-postgres-sg\" as RDS\n        }\n    }\n}\n\nDEV --> DOTNET : Крок 3:\\nстворити проєкт\nDEV --> EC2 : Крок 5:\\nдеплой через SCP\nEC2 --> RDS : порт 5432\\n(тільки від dotnet-api-sg)\nDOTNET .right.> EC2 : publish + scp\n\nnote right of RDS #dcfce7\n  Крок 1: RDS instance\n  Крок 2: EC2 + SGs\n  Крок 4: EF Core міграція\nend note\n@enduml\n",[3414,7381,7382,7386,7390,7394,7398,7403,7407,7412,7417,7421,7425,7430,7435,7440,7445,7450,7455,7460,7464,7468,7472,7476,7481,7486,7491,7496,7500,7505,7510,7515,7520,7524],{"__ignoreMap":3431},[3435,7383,7384],{"class":3437,"line":3438},[3435,7385,3441],{},[3435,7387,7388],{"class":3437,"line":3444},[3435,7389,3447],{},[3435,7391,7392],{"class":3437,"line":3450},[3435,7393,3453],{},[3435,7395,7396],{"class":3437,"line":3456},[3435,7397,3460],{"emptyLinePlaceholder":3459},[3435,7399,7400],{"class":3437,"line":3463},[3435,7401,7402],{},"actor \"Розробник\" as DEV\n",[3435,7404,7405],{"class":3437,"line":3469},[3435,7406,3460],{"emptyLinePlaceholder":3459},[3435,7408,7409],{"class":3437,"line":3475},[3435,7410,7411],{},"package \"Локально\" as LOCAL #fef3c7 {\n",[3435,7413,7414],{"class":3437,"line":3481},[3435,7415,7416],{},"    component \".NET проєкт\\n(dotnet new webapi)\" as DOTNET\n",[3435,7418,7419],{"class":3437,"line":3487},[3435,7420,3532],{},[3435,7422,7423],{"class":3437,"line":3493},[3435,7424,3460],{"emptyLinePlaceholder":3459},[3435,7426,7427],{"class":3437,"line":3499},[3435,7428,7429],{},"package \"AWS (eu-central-1)\" as AWS {\n",[3435,7431,7432],{"class":3437,"line":3505},[3435,7433,7434],{},"    package \"VPC (Default)\" as VPC {\n",[3435,7436,7437],{"class":3437,"line":3511},[3435,7438,7439],{},"        package \"Public Subnet\" as PUB #dbeafe {\n",[3435,7441,7442],{"class":3437,"line":3517},[3435,7443,7444],{},"            node \"EC2 t2.micro\\nUbuntu 24.04\\ndotnet-api-sg\" as EC2\n",[3435,7446,7447],{"class":3437,"line":3523},[3435,7448,7449],{},"        }\n",[3435,7451,7452],{"class":3437,"line":3529},[3435,7453,7454],{},"        package \"Private Subnet\" as PRIV #dcfce7 {\n",[3435,7456,7457],{"class":3437,"line":3535},[3435,7458,7459],{},"            database \"RDS PostgreSQL\\ndb.t3.micro\\nrds-postgres-sg\" as RDS\n",[3435,7461,7462],{"class":3437,"line":3540},[3435,7463,7449],{},[3435,7465,7466],{"class":3437,"line":3546},[3435,7467,3490],{},[3435,7469,7470],{"class":3437,"line":3552},[3435,7471,3532],{},[3435,7473,7474],{"class":3437,"line":3558},[3435,7475,3460],{"emptyLinePlaceholder":3459},[3435,7477,7478],{"class":3437,"line":3563},[3435,7479,7480],{},"DEV --> DOTNET : Крок 3:\\nстворити проєкт\n",[3435,7482,7483],{"class":3437,"line":3569},[3435,7484,7485],{},"DEV --> EC2 : Крок 5:\\nдеплой через SCP\n",[3435,7487,7488],{"class":3437,"line":3575},[3435,7489,7490],{},"EC2 --> RDS : порт 5432\\n(тільки від dotnet-api-sg)\n",[3435,7492,7493],{"class":3437,"line":3580},[3435,7494,7495],{},"DOTNET .right.> EC2 : publish + scp\n",[3435,7497,7498],{"class":3437,"line":3586},[3435,7499,3460],{"emptyLinePlaceholder":3459},[3435,7501,7502],{"class":3437,"line":3592},[3435,7503,7504],{},"note right of RDS #dcfce7\n",[3435,7506,7507],{"class":3437,"line":3597},[3435,7508,7509],{},"  Крок 1: RDS instance\n",[3435,7511,7512],{"class":3437,"line":3603},[3435,7513,7514],{},"  Крок 2: EC2 + SGs\n",[3435,7516,7517],{"class":3437,"line":3609},[3435,7518,7519],{},"  Крок 4: EF Core міграція\n",[3435,7521,7522],{"class":3437,"line":3615},[3435,7523,4133],{},[3435,7525,7526],{"class":3437,"line":3621},[3435,7527,3640],{},[3700,7529,7531],{"id":7530},"передумови","Передумови",[3788,7533,7534,7546,7554],{},[3791,7535,7538,7539,7542,7543],{"icon":7536,"title":7537},"i-heroicons-command-line","AWS CLI","Встановлений та налаштований (",[3414,7540,7541],{},"aws configure","). Перевірте: ",[3414,7544,7545],{},"aws sts get-caller-identity",[3791,7547,7550,7551],{"icon":7548,"title":7549},"i-heroicons-code-bracket",".NET SDK 9","Встановлений локально. Перевірте: ",[3414,7552,7553],{},"dotnet --version",[3791,7555,7558],{"icon":7556,"title":7557},"i-heroicons-user-circle","AWS акаунт","IAM користувач з правами EC2FullAccess + RDSFullAccess",[3679,7560],{},[3700,7562,7564],{"id":7563},"крок-1-створення-rds-postgresql-instance","Крок 1: Створення RDS PostgreSQL instance",[5205,7566,7567,7692,8301],{},[5208,7568,7570],{"label":7569},"AWS Console",[3913,7571,7572,7581,7587,7596,7608,7616,7624,7633,7643,7652,7673,7681,7687],{},[3369,7573,7574,7575,7577,7578],{},"Відкрийте ",[3359,7576,5675],{}," → ",[3359,7579,7580],{},"Create database",[3369,7582,7583,7586],{},[3359,7584,7585],{},"Choose a database creation method:"," Standard create",[3369,7588,7589,7592,7593],{},[3359,7590,7591],{},"Engine options:"," PostgreSQL → версія ",[3359,7594,7595],{},"16.x (latest)",[3369,7597,7598,7601,7602],{},[3359,7599,7600],{},"Templates:"," Free tier ",[3897,7603,7604,7605,7607],{},"(автоматично обирає ",[3414,7606,3760],{},", вимикає Multi-AZ)",[3369,7609,7610,3783,7613],{},[3359,7611,7612],{},"DB instance identifier:",[3414,7614,7615],{},"my-dotnet-app-db",[3369,7617,7618,3783,7621],{},[3359,7619,7620],{},"Master username:",[3414,7622,7623],{},"postgres",[3369,7625,7626,7629,7630],{},[3359,7627,7628],{},"Master password:"," задайте надійний пароль та ",[3359,7631,7632],{},"збережіть його",[3369,7634,7635,3783,7638,3783,7640],{},[3359,7636,7637],{},"DB instance class:",[3414,7639,3760],{},[3897,7641,7642],{},"(Free Tier)",[3369,7644,7645,7648,7649],{},[3359,7646,7647],{},"Storage:"," gp2, 20 GB ",[3897,7650,7651],{},"(Free Tier включає 20 GB)",[3369,7653,7654,7657],{},[3359,7655,7656],{},"Connectivity:",[3366,7658,7659,7662,7667],{},[3369,7660,7661],{},"VPC: Default VPC",[3369,7663,7664,7665],{},"Public access: ",[3359,7666,5364],{},[3369,7668,7669,7670],{},"VPC Security group: Create new → назва ",[3414,7671,7672],{},"rds-postgres-sg",[3369,7674,7675,3783,7678],{},[3359,7676,7677],{},"Additional configuration → Initial database name:",[3414,7679,7680],{},"myapp",[3369,7682,7683,7686],{},[3359,7684,7685],{},"Backup retention:"," 7 days",[3369,7688,7689,7691],{},[3359,7690,7580],{}," → очікуйте ~5-10 хвилин",[5208,7693,7694],{"label":5210},[3426,7695,7697],{"className":5213,"code":7696,"language":5215,"meta":3431,"style":3431},"# Знайти Default VPC\nVPC_ID=$(aws ec2 describe-vpcs \\\n    --filters \"Name=isDefault,Values=true\" \\\n    --query \"Vpcs[0].VpcId\" --output text --region eu-central-1)\necho \"VPC: $VPC_ID\"\n\nSUBNET_IDS=$(aws ec2 describe-subnets \\\n    --filters \"Name=vpcId,Values=$VPC_ID\" \\\n    --query \"Subnets[*].SubnetId\" --output text --region eu-central-1)\n\n# Створити DB Subnet Group\n# ПРИМІТКА: DB Subnet Group визначає підмережі (Subnets) у межах VPC, у яких RDS може створювати мережеві інтерфейси.\n# Для забезпечення високої доступності (навіть якщо інстанс запускається у Single-AZ режимі) AWS вимагає вказувати\n# підмережі щонайменше у ДВОХ різних зонах доступності (Availability Zones).\naws rds create-db-subnet-group \\\n    --db-subnet-group-name my-dotnet-app-subnet-group \\\n    --db-subnet-group-description \"Subnet group for my-dotnet-app\" \\\n    --subnet-ids $(echo $SUBNET_IDS | tr ' ' '\\n' | head -2 | tr '\\n' ' ') \\\n    --region eu-central-1\n\n# Створити Security Group для RDS\nSG_RDS=$(aws ec2 create-security-group \\\n    --group-name rds-postgres-sg \\\n    --description \"RDS PostgreSQL security group\" \\\n    --vpc-id $VPC_ID \\\n    --query \"GroupId\" --output text --region eu-central-1)\n\n# Тимчасово дозволити VPC-рівень (замінимо на SG-рівень у кроці 2)\naws ec2 authorize-security-group-ingress \\\n    --group-id $SG_RDS --protocol tcp --port 5432 \\\n    --cidr 10.0.0.0\u002F16 --region eu-central-1\n\n# Створити RDS instance\naws rds create-db-instance \\\n    --db-instance-identifier my-dotnet-app-db \\\n    --db-instance-class db.t3.micro \\\n    --engine postgres --engine-version \"16.3\" \\\n    --master-username postgres \\\n    --master-user-password \"YourSecurePassword123!\" \\\n    --allocated-storage 20 \\\n    --db-name myapp \\\n    --db-subnet-group-name my-dotnet-app-subnet-group \\\n    --vpc-security-group-ids $SG_RDS \\\n    --no-publicly-accessible \\\n    --backup-retention-period 7 \\\n    --no-multi-az \\\n    --region eu-central-1\n\n# Дочекатись статусу available (~5-10 хв)\naws rds wait db-instance-available \\\n    --db-instance-identifier my-dotnet-app-db --region eu-central-1\n\n# Отримати endpoint\nRDS_ENDPOINT=$(aws rds describe-db-instances \\\n    --db-instance-identifier my-dotnet-app-db \\\n    --query \"DBInstances[0].Endpoint.Address\" \\\n    --output text --region eu-central-1)\necho \"RDS Endpoint: $RDS_ENDPOINT\"\n",[3414,7698,7699,7704,7722,7732,7754,7768,7772,7788,7802,7819,7823,7828,7833,7838,7843,7854,7864,7874,7918,7924,7928,7933,7949,7959,7969,7979,7996,8000,8005,8016,8038,8050,8054,8059,8070,8079,8089,8105,8114,8124,8134,8144,8152,8161,8168,8178,8185,8191,8195,8201,8216,8227,8232,8238,8255,8264,8274,8288],{"__ignoreMap":3431},[3435,7700,7701],{"class":3437,"line":3438},[3435,7702,7703],{"class":4461},"# Знайти Default VPC\n",[3435,7705,7706,7709,7712,7714,7717,7720],{"class":3437,"line":3444},[3435,7707,7708],{"class":4524},"VPC_ID",[3435,7710,7711],{"class":4467},"=$(",[3435,7713,5227],{"class":4535},[3435,7715,7716],{"class":4473}," ec2",[3435,7718,7719],{"class":4473}," describe-vpcs",[3435,7721,5237],{"class":5236},[3435,7723,7724,7727,7730],{"class":3437,"line":3450},[3435,7725,7726],{"class":4677},"    --filters",[3435,7728,7729],{"class":4473}," \"Name=isDefault,Values=true\"",[3435,7731,5237],{"class":5236},[3435,7733,7734,7737,7740,7743,7746,7749,7752],{"class":3437,"line":3456},[3435,7735,7736],{"class":4677},"    --query",[3435,7738,7739],{"class":4473}," \"Vpcs[0].VpcId\"",[3435,7741,7742],{"class":4677}," --output",[3435,7744,7745],{"class":4473}," text",[3435,7747,7748],{"class":4677}," --region",[3435,7750,7751],{"class":4473}," eu-central-1",[3435,7753,4701],{"class":4467},[3435,7755,7756,7759,7762,7765],{"class":3437,"line":3463},[3435,7757,7758],{"class":4535},"echo",[3435,7760,7761],{"class":4473}," \"VPC: ",[3435,7763,7764],{"class":4524},"$VPC_ID",[3435,7766,7767],{"class":4473},"\"\n",[3435,7769,7770],{"class":3437,"line":3469},[3435,7771,3460],{"emptyLinePlaceholder":3459},[3435,7773,7774,7777,7779,7781,7783,7786],{"class":3437,"line":3475},[3435,7775,7776],{"class":4524},"SUBNET_IDS",[3435,7778,7711],{"class":4467},[3435,7780,5227],{"class":4535},[3435,7782,7716],{"class":4473},[3435,7784,7785],{"class":4473}," describe-subnets",[3435,7787,5237],{"class":5236},[3435,7789,7790,7792,7795,7797,7800],{"class":3437,"line":3481},[3435,7791,7726],{"class":4677},[3435,7793,7794],{"class":4473}," \"Name=vpcId,Values=",[3435,7796,7764],{"class":4524},[3435,7798,7799],{"class":4473},"\"",[3435,7801,5237],{"class":5236},[3435,7803,7804,7806,7809,7811,7813,7815,7817],{"class":3437,"line":3487},[3435,7805,7736],{"class":4677},[3435,7807,7808],{"class":4473}," \"Subnets[*].SubnetId\"",[3435,7810,7742],{"class":4677},[3435,7812,7745],{"class":4473},[3435,7814,7748],{"class":4677},[3435,7816,7751],{"class":4473},[3435,7818,4701],{"class":4467},[3435,7820,7821],{"class":3437,"line":3493},[3435,7822,3460],{"emptyLinePlaceholder":3459},[3435,7824,7825],{"class":3437,"line":3499},[3435,7826,7827],{"class":4461},"# Створити DB Subnet Group\n",[3435,7829,7830],{"class":3437,"line":3505},[3435,7831,7832],{"class":4461},"# ПРИМІТКА: DB Subnet Group визначає підмережі (Subnets) у межах VPC, у яких RDS може створювати мережеві інтерфейси.\n",[3435,7834,7835],{"class":3437,"line":3511},[3435,7836,7837],{"class":4461},"# Для забезпечення високої доступності (навіть якщо інстанс запускається у Single-AZ режимі) AWS вимагає вказувати\n",[3435,7839,7840],{"class":3437,"line":3517},[3435,7841,7842],{"class":4461},"# підмережі щонайменше у ДВОХ різних зонах доступності (Availability Zones).\n",[3435,7844,7845,7847,7849,7852],{"class":3437,"line":3523},[3435,7846,5227],{"class":4535},[3435,7848,5230],{"class":4473},[3435,7850,7851],{"class":4473}," create-db-subnet-group",[3435,7853,5237],{"class":5236},[3435,7855,7856,7859,7862],{"class":3437,"line":3529},[3435,7857,7858],{"class":4677},"    --db-subnet-group-name",[3435,7860,7861],{"class":4473}," my-dotnet-app-subnet-group",[3435,7863,5237],{"class":5236},[3435,7865,7866,7869,7872],{"class":3437,"line":3535},[3435,7867,7868],{"class":4677},"    --db-subnet-group-description",[3435,7870,7871],{"class":4473}," \"Subnet group for my-dotnet-app\"",[3435,7873,5237],{"class":5236},[3435,7875,7876,7879,7882,7884,7887,7890,7892,7895,7898,7900,7903,7906,7908,7910,7912,7914,7916],{"class":3437,"line":3540},[3435,7877,7878],{"class":4677},"    --subnet-ids",[3435,7880,7881],{"class":4467}," $(",[3435,7883,7758],{"class":4535},[3435,7885,7886],{"class":4524}," $SUBNET_IDS",[3435,7888,7889],{"class":4467}," | ",[3435,7891,4858],{"class":4535},[3435,7893,7894],{"class":4473}," ' '",[3435,7896,7897],{"class":4473}," '\\n'",[3435,7899,7889],{"class":4467},[3435,7901,7902],{"class":4535},"head",[3435,7904,7905],{"class":4677}," -2",[3435,7907,7889],{"class":4467},[3435,7909,4858],{"class":4535},[3435,7911,7897],{"class":4473},[3435,7913,7894],{"class":4473},[3435,7915,5267],{"class":4467},[3435,7917,5270],{"class":5236},[3435,7919,7920,7922],{"class":3437,"line":3546},[3435,7921,5275],{"class":4677},[3435,7923,5278],{"class":4473},[3435,7925,7926],{"class":3437,"line":3552},[3435,7927,3460],{"emptyLinePlaceholder":3459},[3435,7929,7930],{"class":3437,"line":3558},[3435,7931,7932],{"class":4461},"# Створити Security Group для RDS\n",[3435,7934,7935,7938,7940,7942,7944,7947],{"class":3437,"line":3563},[3435,7936,7937],{"class":4524},"SG_RDS",[3435,7939,7711],{"class":4467},[3435,7941,5227],{"class":4535},[3435,7943,7716],{"class":4473},[3435,7945,7946],{"class":4473}," create-security-group",[3435,7948,5237],{"class":5236},[3435,7950,7951,7954,7957],{"class":3437,"line":3569},[3435,7952,7953],{"class":4677},"    --group-name",[3435,7955,7956],{"class":4473}," rds-postgres-sg",[3435,7958,5237],{"class":5236},[3435,7960,7961,7964,7967],{"class":3437,"line":3575},[3435,7962,7963],{"class":4677},"    --description",[3435,7965,7966],{"class":4473}," \"RDS PostgreSQL security group\"",[3435,7968,5237],{"class":5236},[3435,7970,7971,7974,7977],{"class":3437,"line":3580},[3435,7972,7973],{"class":4677},"    --vpc-id",[3435,7975,7976],{"class":4524}," $VPC_ID",[3435,7978,5237],{"class":5236},[3435,7980,7981,7983,7986,7988,7990,7992,7994],{"class":3437,"line":3586},[3435,7982,7736],{"class":4677},[3435,7984,7985],{"class":4473}," \"GroupId\"",[3435,7987,7742],{"class":4677},[3435,7989,7745],{"class":4473},[3435,7991,7748],{"class":4677},[3435,7993,7751],{"class":4473},[3435,7995,4701],{"class":4467},[3435,7997,7998],{"class":3437,"line":3592},[3435,7999,3460],{"emptyLinePlaceholder":3459},[3435,8001,8002],{"class":3437,"line":3597},[3435,8003,8004],{"class":4461},"# Тимчасово дозволити VPC-рівень (замінимо на SG-рівень у кроці 2)\n",[3435,8006,8007,8009,8011,8014],{"class":3437,"line":3603},[3435,8008,5227],{"class":4535},[3435,8010,7716],{"class":4473},[3435,8012,8013],{"class":4473}," authorize-security-group-ingress",[3435,8015,5237],{"class":5236},[3435,8017,8018,8021,8024,8027,8030,8033,8036],{"class":3437,"line":3609},[3435,8019,8020],{"class":4677},"    --group-id",[3435,8022,8023],{"class":4524}," $SG_RDS",[3435,8025,8026],{"class":4677}," --protocol",[3435,8028,8029],{"class":4473}," tcp",[3435,8031,8032],{"class":4677}," --port",[3435,8034,8035],{"class":5337}," 5432",[3435,8037,5237],{"class":5236},[3435,8039,8040,8043,8046,8048],{"class":3437,"line":3615},[3435,8041,8042],{"class":4677},"    --cidr",[3435,8044,8045],{"class":4473}," 10.0.0.0\u002F16",[3435,8047,7748],{"class":4677},[3435,8049,5278],{"class":4473},[3435,8051,8052],{"class":3437,"line":3621},[3435,8053,3460],{"emptyLinePlaceholder":3459},[3435,8055,8056],{"class":3437,"line":3627},[3435,8057,8058],{"class":4461},"# Створити RDS instance\n",[3435,8060,8061,8063,8065,8068],{"class":3437,"line":3632},[3435,8062,5227],{"class":4535},[3435,8064,5230],{"class":4473},[3435,8066,8067],{"class":4473}," create-db-instance",[3435,8069,5237],{"class":5236},[3435,8071,8072,8074,8077],{"class":3437,"line":3637},[3435,8073,5242],{"class":4677},[3435,8075,8076],{"class":4473}," my-dotnet-app-db",[3435,8078,5237],{"class":5236},[3435,8080,8081,8084,8087],{"class":3437,"line":4160},[3435,8082,8083],{"class":4677},"    --db-instance-class",[3435,8085,8086],{"class":4473}," db.t3.micro",[3435,8088,5237],{"class":5236},[3435,8090,8091,8094,8097,8100,8103],{"class":3437,"line":4166},[3435,8092,8093],{"class":4677},"    --engine",[3435,8095,8096],{"class":4473}," postgres",[3435,8098,8099],{"class":4677}," --engine-version",[3435,8101,8102],{"class":4473}," \"16.3\"",[3435,8104,5237],{"class":5236},[3435,8106,8107,8110,8112],{"class":3437,"line":4171},[3435,8108,8109],{"class":4677},"    --master-username",[3435,8111,8096],{"class":4473},[3435,8113,5237],{"class":5236},[3435,8115,8116,8119,8122],{"class":3437,"line":4176},[3435,8117,8118],{"class":4677},"    --master-user-password",[3435,8120,8121],{"class":4473}," \"YourSecurePassword123!\"",[3435,8123,5237],{"class":5236},[3435,8125,8126,8129,8132],{"class":3437,"line":4182},[3435,8127,8128],{"class":4677},"    --allocated-storage",[3435,8130,8131],{"class":5337}," 20",[3435,8133,5237],{"class":5236},[3435,8135,8136,8139,8142],{"class":3437,"line":4188},[3435,8137,8138],{"class":4677},"    --db-name",[3435,8140,8141],{"class":4473}," myapp",[3435,8143,5237],{"class":5236},[3435,8145,8146,8148,8150],{"class":3437,"line":4194},[3435,8147,7858],{"class":4677},[3435,8149,7861],{"class":4473},[3435,8151,5237],{"class":5236},[3435,8153,8154,8157,8159],{"class":3437,"line":4199},[3435,8155,8156],{"class":4677},"    --vpc-security-group-ids",[3435,8158,8023],{"class":4524},[3435,8160,5237],{"class":5236},[3435,8162,8163,8166],{"class":3437,"line":4205},[3435,8164,8165],{"class":4677},"    --no-publicly-accessible",[3435,8167,5237],{"class":5236},[3435,8169,8170,8173,8176],{"class":3437,"line":4211},[3435,8171,8172],{"class":4677},"    --backup-retention-period",[3435,8174,8175],{"class":5337}," 7",[3435,8177,5237],{"class":5236},[3435,8179,8180,8183],{"class":3437,"line":4217},[3435,8181,8182],{"class":4677},"    --no-multi-az",[3435,8184,5237],{"class":5236},[3435,8186,8187,8189],{"class":3437,"line":4223},[3435,8188,5275],{"class":4677},[3435,8190,5278],{"class":4473},[3435,8192,8193],{"class":3437,"line":4228},[3435,8194,3460],{"emptyLinePlaceholder":3459},[3435,8196,8198],{"class":3437,"line":8197},49,[3435,8199,8200],{"class":4461},"# Дочекатись статусу available (~5-10 хв)\n",[3435,8202,8204,8206,8208,8211,8214],{"class":3437,"line":8203},50,[3435,8205,5227],{"class":4535},[3435,8207,5230],{"class":4473},[3435,8209,8210],{"class":4473}," wait",[3435,8212,8213],{"class":4473}," db-instance-available",[3435,8215,5237],{"class":5236},[3435,8217,8219,8221,8223,8225],{"class":3437,"line":8218},51,[3435,8220,5242],{"class":4677},[3435,8222,8076],{"class":4473},[3435,8224,7748],{"class":4677},[3435,8226,5278],{"class":4473},[3435,8228,8230],{"class":3437,"line":8229},52,[3435,8231,3460],{"emptyLinePlaceholder":3459},[3435,8233,8235],{"class":3437,"line":8234},53,[3435,8236,8237],{"class":4461},"# Отримати endpoint\n",[3435,8239,8241,8244,8246,8248,8250,8253],{"class":3437,"line":8240},54,[3435,8242,8243],{"class":4524},"RDS_ENDPOINT",[3435,8245,7711],{"class":4467},[3435,8247,5227],{"class":4535},[3435,8249,5230],{"class":4473},[3435,8251,8252],{"class":4473}," describe-db-instances",[3435,8254,5237],{"class":5236},[3435,8256,8258,8260,8262],{"class":3437,"line":8257},55,[3435,8259,5242],{"class":4677},[3435,8261,8076],{"class":4473},[3435,8263,5237],{"class":5236},[3435,8265,8267,8269,8272],{"class":3437,"line":8266},56,[3435,8268,7736],{"class":4677},[3435,8270,8271],{"class":4473}," \"DBInstances[0].Endpoint.Address\"",[3435,8273,5237],{"class":5236},[3435,8275,8277,8280,8282,8284,8286],{"class":3437,"line":8276},57,[3435,8278,8279],{"class":4677},"    --output",[3435,8281,7745],{"class":4473},[3435,8283,7748],{"class":4677},[3435,8285,7751],{"class":4473},[3435,8287,4701],{"class":4467},[3435,8289,8291,8293,8296,8299],{"class":3437,"line":8290},58,[3435,8292,7758],{"class":4535},[3435,8294,8295],{"class":4473}," \"RDS Endpoint: ",[3435,8297,8298],{"class":4524},"$RDS_ENDPOINT",[3435,8300,7767],{"class":4473},[5208,8302,8303],{"label":5281},[3426,8304,8306],{"className":5284,"code":8305,"language":5286,"meta":3431,"style":3431},"# Знайти Default VPC\n$VPC_ID = aws ec2 describe-vpcs `\n    --filters \"Name=isDefault,Values=true\" `\n    --query \"Vpcs[0].VpcId\" --output text --region eu-central-1\nWrite-Host \"VPC: $VPC_ID\"\n\n$SUBNET_IDS = (aws ec2 describe-subnets `\n    --filters \"Name=vpcId,Values=$VPC_ID\" `\n    --query \"Subnets[*].SubnetId\" --output text --region eu-central-1) -split '\\s+'\n$SUBNET_PAIR = ($SUBNET_IDS | Select-Object -First 2) -join ' '\n\n# Створити DB Subnet Group\n# ПРИМІТКА: DB Subnet Group визначає підмережі (Subnets) у межах VPC, у яких RDS може створювати мережеві інтерфейси.\n# Для забезпечення високої доступності (навіть якщо інстанс запускається у Single-AZ режимі) AWS вимагає вказувати\n# підмережі щонайменше у ДВОХ різних зонах доступності (Availability Zones).\naws rds create-db-subnet-group `\n    --db-subnet-group-name my-dotnet-app-subnet-group `\n    --db-subnet-group-description \"Subnet group for my-dotnet-app\" `\n    --subnet-ids $SUBNET_PAIR `\n    --region eu-central-1\n\n# Створити Security Group для RDS\n$SG_RDS = aws ec2 create-security-group `\n    --group-name rds-postgres-sg `\n    --description \"RDS PostgreSQL security group\" `\n    --vpc-id $VPC_ID `\n    --query \"GroupId\" --output text --region eu-central-1\n\n# Тимчасово дозволити VPC-рівень\naws ec2 authorize-security-group-ingress `\n    --group-id $SG_RDS --protocol tcp --port 5432 `\n    --cidr 10.0.0.0\u002F16 --region eu-central-1\n\n# Створити RDS instance\naws rds create-db-instance `\n    --db-instance-identifier my-dotnet-app-db `\n    --db-instance-class db.t3.micro `\n    --engine postgres --engine-version \"16.3\" `\n    --master-username postgres `\n    --master-user-password \"YourSecurePassword123!\" `\n    --allocated-storage 20 `\n    --db-name myapp `\n    --db-subnet-group-name my-dotnet-app-subnet-group `\n    --vpc-security-group-ids $SG_RDS `\n    --no-publicly-accessible `\n    --backup-retention-period 7 `\n    --no-multi-az `\n    --region eu-central-1\n\n# Дочекатись статусу available (~5-10 хв)\naws rds wait db-instance-available `\n    --db-instance-identifier my-dotnet-app-db --region eu-central-1\n\n# Отримати endpoint\n$RDS_ENDPOINT = aws rds describe-db-instances `\n    --db-instance-identifier my-dotnet-app-db `\n    --query \"DBInstances[0].Endpoint.Address\" `\n    --output text --region eu-central-1\nWrite-Host \"RDS Endpoint: $RDS_ENDPOINT\"\n",[3414,8307,8308,8312,8319,8329,8342,8353,8357,8365,8378,8396,8422,8426,8430,8434,8438,8442,8447,8458,8469,8478,8484,8488,8492,8500,8510,8520,8529,8540,8544,8549,8559,8575,8599,8603,8607,8612,8617,8622,8632,8637,8647,8657,8662,8670,8682,8687,8702,8707,8713,8717,8721,8726,8733,8737,8741,8748,8752,8761,8768],{"__ignoreMap":3431},[3435,8309,8310],{"class":3437,"line":3438},[3435,8311,7703],{"class":4461},[3435,8313,8314,8316],{"class":3437,"line":3444},[3435,8315,7764],{"class":4524},[3435,8317,8318],{"class":4467}," = aws ec2 describe-vpcs `\n",[3435,8320,8321,8324,8327],{"class":3437,"line":3450},[3435,8322,8323],{"class":4467},"    --filters ",[3435,8325,8326],{"class":4473},"\"Name=isDefault,Values=true\"",[3435,8328,5329],{"class":4467},[3435,8330,8331,8334,8337,8340],{"class":3437,"line":3456},[3435,8332,8333],{"class":4467},"    --query ",[3435,8335,8336],{"class":4473},"\"Vpcs[0].VpcId\"",[3435,8338,8339],{"class":4467}," --output text --region eu-central-",[3435,8341,5338],{"class":5337},[3435,8343,8344,8347,8349,8351],{"class":3437,"line":3463},[3435,8345,8346],{"class":4535},"Write-Host",[3435,8348,7761],{"class":4473},[3435,8350,7764],{"class":4524},[3435,8352,7767],{"class":4473},[3435,8354,8355],{"class":3437,"line":3469},[3435,8356,3460],{"emptyLinePlaceholder":3459},[3435,8358,8359,8362],{"class":3437,"line":3475},[3435,8360,8361],{"class":4524},"$SUBNET_IDS",[3435,8363,8364],{"class":4467}," = (aws ec2 describe-subnets `\n",[3435,8366,8367,8369,8372,8374,8376],{"class":3437,"line":3481},[3435,8368,8323],{"class":4467},[3435,8370,8371],{"class":4473},"\"Name=vpcId,Values=",[3435,8373,7764],{"class":4524},[3435,8375,7799],{"class":4473},[3435,8377,5329],{"class":4467},[3435,8379,8380,8382,8385,8387,8390,8393],{"class":3437,"line":3487},[3435,8381,8333],{"class":4467},[3435,8383,8384],{"class":4473},"\"Subnets[*].SubnetId\"",[3435,8386,8339],{"class":4467},[3435,8388,8389],{"class":5337},"1",[3435,8391,8392],{"class":4467},") -split ",[3435,8394,8395],{"class":4473},"'\\s+'\n",[3435,8397,8398,8401,8404,8406,8408,8411,8414,8416,8419],{"class":3437,"line":3493},[3435,8399,8400],{"class":4524},"$SUBNET_PAIR",[3435,8402,8403],{"class":4467}," = (",[3435,8405,8361],{"class":4524},[3435,8407,7889],{"class":4467},[3435,8409,8410],{"class":4535},"Select-Object",[3435,8412,8413],{"class":4467}," -First ",[3435,8415,6991],{"class":5337},[3435,8417,8418],{"class":4467},") -join ",[3435,8420,8421],{"class":4473},"' '\n",[3435,8423,8424],{"class":3437,"line":3499},[3435,8425,3460],{"emptyLinePlaceholder":3459},[3435,8427,8428],{"class":3437,"line":3505},[3435,8429,7827],{"class":4461},[3435,8431,8432],{"class":3437,"line":3511},[3435,8433,7832],{"class":4461},[3435,8435,8436],{"class":3437,"line":3517},[3435,8437,7837],{"class":4461},[3435,8439,8440],{"class":3437,"line":3523},[3435,8441,7842],{"class":4461},[3435,8443,8444],{"class":3437,"line":3529},[3435,8445,8446],{"class":4467},"aws rds create-db-subnet-group `\n",[3435,8448,8449,8452,8455],{"class":3437,"line":3535},[3435,8450,8451],{"class":4467},"    --db-subnet-",[3435,8453,8454],{"class":4535},"group-name",[3435,8456,8457],{"class":4467}," my-dotnet-app-subnet-group `\n",[3435,8459,8460,8462,8465,8467],{"class":3437,"line":3540},[3435,8461,8451],{"class":4467},[3435,8463,8464],{"class":4535},"group-description",[3435,8466,7871],{"class":4473},[3435,8468,5329],{"class":4467},[3435,8470,8471,8474,8476],{"class":3437,"line":3546},[3435,8472,8473],{"class":4467},"    --subnet-ids ",[3435,8475,8400],{"class":4524},[3435,8477,5329],{"class":4467},[3435,8479,8480,8482],{"class":3437,"line":3552},[3435,8481,5334],{"class":4467},[3435,8483,5338],{"class":5337},[3435,8485,8486],{"class":3437,"line":3558},[3435,8487,3460],{"emptyLinePlaceholder":3459},[3435,8489,8490],{"class":3437,"line":3563},[3435,8491,7932],{"class":4461},[3435,8493,8494,8497],{"class":3437,"line":3569},[3435,8495,8496],{"class":4524},"$SG_RDS",[3435,8498,8499],{"class":4467}," = aws ec2 create-security-group `\n",[3435,8501,8502,8505,8507],{"class":3437,"line":3575},[3435,8503,8504],{"class":4467},"    --",[3435,8506,8454],{"class":4535},[3435,8508,8509],{"class":4467}," rds-postgres-sg `\n",[3435,8511,8512,8515,8518],{"class":3437,"line":3580},[3435,8513,8514],{"class":4467},"    --description ",[3435,8516,8517],{"class":4473},"\"RDS PostgreSQL security group\"",[3435,8519,5329],{"class":4467},[3435,8521,8522,8525,8527],{"class":3437,"line":3586},[3435,8523,8524],{"class":4467},"    --vpc-id ",[3435,8526,7764],{"class":4524},[3435,8528,5329],{"class":4467},[3435,8530,8531,8533,8536,8538],{"class":3437,"line":3592},[3435,8532,8333],{"class":4467},[3435,8534,8535],{"class":4473},"\"GroupId\"",[3435,8537,8339],{"class":4467},[3435,8539,5338],{"class":5337},[3435,8541,8542],{"class":3437,"line":3597},[3435,8543,3460],{"emptyLinePlaceholder":3459},[3435,8545,8546],{"class":3437,"line":3603},[3435,8547,8548],{"class":4461},"# Тимчасово дозволити VPC-рівень\n",[3435,8550,8551,8554,8557],{"class":3437,"line":3609},[3435,8552,8553],{"class":4467},"aws ec2 authorize-security-",[3435,8555,8556],{"class":4535},"group-ingress",[3435,8558,5329],{"class":4467},[3435,8560,8561,8563,8566,8568,8571,8573],{"class":3437,"line":3615},[3435,8562,8504],{"class":4467},[3435,8564,8565],{"class":4535},"group-id",[3435,8567,8023],{"class":4524},[3435,8569,8570],{"class":4467}," --protocol tcp --port ",[3435,8572,5393],{"class":5337},[3435,8574,5329],{"class":4467},[3435,8576,8577,8580,8583,8585,8588,8591,8594,8597],{"class":3437,"line":3621},[3435,8578,8579],{"class":4467},"    --cidr ",[3435,8581,8582],{"class":5337},"10.0",[3435,8584,3691],{"class":4467},[3435,8586,8587],{"class":5337},"0.0",[3435,8589,8590],{"class":4467},"\u002F",[3435,8592,8593],{"class":5337},"16",[3435,8595,8596],{"class":4467}," --region eu-central-",[3435,8598,5338],{"class":5337},[3435,8600,8601],{"class":3437,"line":3627},[3435,8602,3460],{"emptyLinePlaceholder":3459},[3435,8604,8605],{"class":3437,"line":3632},[3435,8606,8058],{"class":4461},[3435,8608,8609],{"class":3437,"line":3637},[3435,8610,8611],{"class":4467},"aws rds create-db-instance `\n",[3435,8613,8614],{"class":3437,"line":4160},[3435,8615,8616],{"class":4467},"    --db-instance-identifier my-dotnet-app-db `\n",[3435,8618,8619],{"class":3437,"line":4166},[3435,8620,8621],{"class":4467},"    --db-instance-class db.t3.micro `\n",[3435,8623,8624,8627,8630],{"class":3437,"line":4171},[3435,8625,8626],{"class":4467},"    --engine postgres --engine-version ",[3435,8628,8629],{"class":4473},"\"16.3\"",[3435,8631,5329],{"class":4467},[3435,8633,8634],{"class":3437,"line":4176},[3435,8635,8636],{"class":4467},"    --master-username postgres `\n",[3435,8638,8639,8642,8645],{"class":3437,"line":4182},[3435,8640,8641],{"class":4467},"    --master-user-password ",[3435,8643,8644],{"class":4473},"\"YourSecurePassword123!\"",[3435,8646,5329],{"class":4467},[3435,8648,8649,8652,8655],{"class":3437,"line":4188},[3435,8650,8651],{"class":4467},"    --allocated-storage ",[3435,8653,8654],{"class":5337},"20",[3435,8656,5329],{"class":4467},[3435,8658,8659],{"class":3437,"line":4194},[3435,8660,8661],{"class":4467},"    --db-name myapp `\n",[3435,8663,8664,8666,8668],{"class":3437,"line":4199},[3435,8665,8451],{"class":4467},[3435,8667,8454],{"class":4535},[3435,8669,8457],{"class":4467},[3435,8671,8672,8675,8678,8680],{"class":3437,"line":4205},[3435,8673,8674],{"class":4467},"    --vpc-security-",[3435,8676,8677],{"class":4535},"group-ids",[3435,8679,8023],{"class":4524},[3435,8681,5329],{"class":4467},[3435,8683,8684],{"class":3437,"line":4211},[3435,8685,8686],{"class":4467},"    --no-publicly-accessible `\n",[3435,8688,8689,8691,8694,8697,8700],{"class":3437,"line":4217},[3435,8690,8504],{"class":4467},[3435,8692,8693],{"class":4535},"backup-retention",[3435,8695,8696],{"class":4467},"-period ",[3435,8698,8699],{"class":5337},"7",[3435,8701,5329],{"class":4467},[3435,8703,8704],{"class":3437,"line":4223},[3435,8705,8706],{"class":4467},"    --no-multi-az `\n",[3435,8708,8709,8711],{"class":3437,"line":4228},[3435,8710,5334],{"class":4467},[3435,8712,5338],{"class":5337},[3435,8714,8715],{"class":3437,"line":8197},[3435,8716,3460],{"emptyLinePlaceholder":3459},[3435,8718,8719],{"class":3437,"line":8203},[3435,8720,8200],{"class":4461},[3435,8722,8723],{"class":3437,"line":8218},[3435,8724,8725],{"class":4467},"aws rds wait db-instance-available `\n",[3435,8727,8728,8731],{"class":3437,"line":8229},[3435,8729,8730],{"class":4467},"    --db-instance-identifier my-dotnet-app-db --region eu-central-",[3435,8732,5338],{"class":5337},[3435,8734,8735],{"class":3437,"line":8234},[3435,8736,3460],{"emptyLinePlaceholder":3459},[3435,8738,8739],{"class":3437,"line":8240},[3435,8740,8237],{"class":4461},[3435,8742,8743,8745],{"class":3437,"line":8257},[3435,8744,8298],{"class":4524},[3435,8746,8747],{"class":4467}," = aws rds describe-db-instances `\n",[3435,8749,8750],{"class":3437,"line":8266},[3435,8751,8616],{"class":4467},[3435,8753,8754,8756,8759],{"class":3437,"line":8276},[3435,8755,8333],{"class":4467},[3435,8757,8758],{"class":4473},"\"DBInstances[0].Endpoint.Address\"",[3435,8760,5329],{"class":4467},[3435,8762,8763,8766],{"class":3437,"line":8290},[3435,8764,8765],{"class":4467},"    --output text --region eu-central-",[3435,8767,5338],{"class":5337},[3435,8769,8771,8773,8775,8777],{"class":3437,"line":8770},59,[3435,8772,8346],{"class":4535},[3435,8774,8295],{"class":4473},[3435,8776,8298],{"class":4524},[3435,8778,7767],{"class":4473},[3679,8780],{},[3700,8782,8784],{"id":8783},"крок-2-запуск-ec2-instance-для-net-api","Крок 2: Запуск EC2 instance для .NET API",[3700,8786,8788],{"id":8787},"створення-key-pair-для-ssh-доступу","Створення Key Pair для SSH доступу",[5205,8790,8791,8835,8912],{},[5208,8792,8793],{"label":7569},[3913,8794,8795,8806,8816,8821],{},[3369,8796,8797,7577,8800,7577,8803],{},[3359,8798,8799],{},"EC2",[3359,8801,8802],{},"Key Pairs",[3359,8804,8805],{},"Create key pair",[3369,8807,8808,8809,8812,8813],{},"Name: ",[3414,8810,8811],{},"dotnet-api-key",", Format: ",[3414,8814,8815],{},".pem",[3369,8817,8818,8820],{},[3359,8819,8805],{}," → файл завантажиться автоматично",[3369,8822,8823,8824,8827,8828,8831,8832],{},"Збережіть у директорії ",[3414,8825,8826],{},"~\u002F.ssh\u002F"," та встановіть обмежені права доступу: ",[3414,8829,8830],{},"chmod 400 ~\u002F.ssh\u002Fdotnet-api-key.pem",".\n",[3897,8833,8834],{},"(Примітка: Утиліта OpenSSH блокує використання приватних ключів із занадто широкими правами доступу. Команда chmod 400 робить файл доступним виключно для читання власником, запобігаючи доступу інших користувачів системи).",[5208,8836,8837],{"label":5210},[3426,8838,8840],{"className":5213,"code":8839,"language":5215,"meta":3431,"style":3431},"aws ec2 create-key-pair \\\n    --key-name dotnet-api-key \\\n    --query \"KeyMaterial\" \\\n    --output text \\\n    --region eu-central-1 > ~\u002F.ssh\u002Fdotnet-api-key.pem\n\n# Встановлення прав доступу \"тільки для читання власником\", що є обов'язковою вимогою SSH-клієнта\nchmod 400 ~\u002F.ssh\u002Fdotnet-api-key.pem\n",[3414,8841,8842,8853,8863,8872,8880,8892,8896,8901],{"__ignoreMap":3431},[3435,8843,8844,8846,8848,8851],{"class":3437,"line":3438},[3435,8845,5227],{"class":4535},[3435,8847,7716],{"class":4473},[3435,8849,8850],{"class":4473}," create-key-pair",[3435,8852,5237],{"class":5236},[3435,8854,8855,8858,8861],{"class":3437,"line":3444},[3435,8856,8857],{"class":4677},"    --key-name",[3435,8859,8860],{"class":4473}," dotnet-api-key",[3435,8862,5237],{"class":5236},[3435,8864,8865,8867,8870],{"class":3437,"line":3450},[3435,8866,7736],{"class":4677},[3435,8868,8869],{"class":4473}," \"KeyMaterial\"",[3435,8871,5237],{"class":5236},[3435,8873,8874,8876,8878],{"class":3437,"line":3456},[3435,8875,8279],{"class":4677},[3435,8877,7745],{"class":4473},[3435,8879,5237],{"class":5236},[3435,8881,8882,8884,8886,8889],{"class":3437,"line":3463},[3435,8883,5275],{"class":4677},[3435,8885,7751],{"class":4473},[3435,8887,8888],{"class":4467}," > ",[3435,8890,8891],{"class":4473},"~\u002F.ssh\u002Fdotnet-api-key.pem\n",[3435,8893,8894],{"class":3437,"line":3469},[3435,8895,3460],{"emptyLinePlaceholder":3459},[3435,8897,8898],{"class":3437,"line":3475},[3435,8899,8900],{"class":4461},"# Встановлення прав доступу \"тільки для читання власником\", що є обов'язковою вимогою SSH-клієнта\n",[3435,8902,8903,8906,8909],{"class":3437,"line":3481},[3435,8904,8905],{"class":4535},"chmod",[3435,8907,8908],{"class":5337}," 400",[3435,8910,8911],{"class":4473}," ~\u002F.ssh\u002Fdotnet-api-key.pem\n",[5208,8913,8914],{"label":5281},[3426,8915,8917],{"className":5284,"code":8916,"language":5286,"meta":3431,"style":3431},"$keyMaterial = aws ec2 create-key-pair `\n    --key-name dotnet-api-key `\n    --query \"KeyMaterial\" `\n    --output text --region eu-central-1\n\n$keyPath = \"$env:USERPROFILE\\.ssh\\dotnet-api-key.pem\"\n$keyMaterial | Out-File -FilePath $keyPath -Encoding ascii -NoNewline\nWrite-Host \"Key saved: $keyPath\"\n",[3414,8918,8919,8927,8932,8941,8947,8951,8966,8983],{"__ignoreMap":3431},[3435,8920,8921,8924],{"class":3437,"line":3438},[3435,8922,8923],{"class":4524},"$keyMaterial",[3435,8925,8926],{"class":4467}," = aws ec2 create-key-pair `\n",[3435,8928,8929],{"class":3437,"line":3444},[3435,8930,8931],{"class":4467},"    --key-name dotnet-api-key `\n",[3435,8933,8934,8936,8939],{"class":3437,"line":3450},[3435,8935,8333],{"class":4467},[3435,8937,8938],{"class":4473},"\"KeyMaterial\"",[3435,8940,5329],{"class":4467},[3435,8942,8943,8945],{"class":3437,"line":3456},[3435,8944,8765],{"class":4467},[3435,8946,5338],{"class":5337},[3435,8948,8949],{"class":3437,"line":3463},[3435,8950,3460],{"emptyLinePlaceholder":3459},[3435,8952,8953,8956,8958,8960,8963],{"class":3437,"line":3469},[3435,8954,8955],{"class":4524},"$keyPath",[3435,8957,5300],{"class":4467},[3435,8959,7799],{"class":4473},[3435,8961,8962],{"class":4524},"$env:USERPROFILE",[3435,8964,8965],{"class":4473},"\\.ssh\\dotnet-api-key.pem\"\n",[3435,8967,8968,8970,8972,8975,8978,8980],{"class":3437,"line":3475},[3435,8969,8923],{"class":4524},[3435,8971,7889],{"class":4467},[3435,8973,8974],{"class":4535},"Out-File",[3435,8976,8977],{"class":4467}," -FilePath ",[3435,8979,8955],{"class":4524},[3435,8981,8982],{"class":4467}," -Encoding ascii -NoNewline\n",[3435,8984,8985,8987,8990,8992],{"class":3437,"line":3481},[3435,8986,8346],{"class":4535},[3435,8988,8989],{"class":4473}," \"Key saved: ",[3435,8991,8955],{"class":4524},[3435,8993,7767],{"class":4473},[3700,8995,8997],{"id":8996},"створення-security-group-для-ec2","Створення Security Group для EC2",[5205,8999,9000,9054,9246],{},[5208,9001,9002],{"label":7569},[3913,9003,9004,9014,9019,9050],{},[3369,9005,9006,7577,9008,7577,9011],{},[3359,9007,8799],{},[3359,9009,9010],{},"Security Groups",[3359,9012,9013],{},"Create security group",[3369,9015,8808,9016,9018],{},[3414,9017,5397],{},", VPC: Default",[3369,9020,9021,9024],{},[3359,9022,9023],{},"Inbound rules:",[3366,9025,9026,9039],{},[3369,9027,9028,9031,9032,9035,9036],{},[3414,9029,9030],{},"SSH"," порт ",[3414,9033,9034],{},"22"," → Source: ",[3414,9037,9038],{},"My IP",[3369,9040,9041,9031,9044,9035,9047],{},[3414,9042,9043],{},"Custom TCP",[3414,9045,9046],{},"5000",[3414,9048,9049],{},"Anywhere (0.0.0.0\u002F0)",[3369,9051,9052],{},[3359,9053,9013],{},[5208,9055,9056],{"label":5210},[3426,9057,9059],{"className":5213,"code":9058,"language":5215,"meta":3431,"style":3431},"SG_EC2=$(aws ec2 create-security-group \\\n    --group-name dotnet-api-sg \\\n    --description \"Security group for .NET API EC2\" \\\n    --vpc-id $VPC_ID \\\n    --query \"GroupId\" --output text --region eu-central-1)\n\nMY_IP=$(curl -s https:\u002F\u002Fcheckip.amazonaws.com)\n\naws ec2 authorize-security-group-ingress \\\n    --group-id $SG_EC2 --protocol tcp --port 22 \\\n    --cidr \"${MY_IP}\u002F32\" --region eu-central-1\n\naws ec2 authorize-security-group-ingress \\\n    --group-id $SG_EC2 --protocol tcp --port 5000 \\\n    --cidr 0.0.0.0\u002F0 --region eu-central-1\n\necho \"EC2 SG: $SG_EC2\"\n",[3414,9060,9061,9076,9085,9094,9102,9118,9122,9140,9144,9154,9172,9188,9192,9202,9219,9230,9234],{"__ignoreMap":3431},[3435,9062,9063,9066,9068,9070,9072,9074],{"class":3437,"line":3438},[3435,9064,9065],{"class":4524},"SG_EC2",[3435,9067,7711],{"class":4467},[3435,9069,5227],{"class":4535},[3435,9071,7716],{"class":4473},[3435,9073,7946],{"class":4473},[3435,9075,5237],{"class":5236},[3435,9077,9078,9080,9083],{"class":3437,"line":3444},[3435,9079,7953],{"class":4677},[3435,9081,9082],{"class":4473}," dotnet-api-sg",[3435,9084,5237],{"class":5236},[3435,9086,9087,9089,9092],{"class":3437,"line":3450},[3435,9088,7963],{"class":4677},[3435,9090,9091],{"class":4473}," \"Security group for .NET API EC2\"",[3435,9093,5237],{"class":5236},[3435,9095,9096,9098,9100],{"class":3437,"line":3456},[3435,9097,7973],{"class":4677},[3435,9099,7976],{"class":4524},[3435,9101,5237],{"class":5236},[3435,9103,9104,9106,9108,9110,9112,9114,9116],{"class":3437,"line":3463},[3435,9105,7736],{"class":4677},[3435,9107,7985],{"class":4473},[3435,9109,7742],{"class":4677},[3435,9111,7745],{"class":4473},[3435,9113,7748],{"class":4677},[3435,9115,7751],{"class":4473},[3435,9117,4701],{"class":4467},[3435,9119,9120],{"class":3437,"line":3469},[3435,9121,3460],{"emptyLinePlaceholder":3459},[3435,9123,9124,9127,9129,9132,9135,9138],{"class":3437,"line":3475},[3435,9125,9126],{"class":4524},"MY_IP",[3435,9128,7711],{"class":4467},[3435,9130,9131],{"class":4535},"curl",[3435,9133,9134],{"class":4677}," -s",[3435,9136,9137],{"class":4473}," https:\u002F\u002Fcheckip.amazonaws.com",[3435,9139,4701],{"class":4467},[3435,9141,9142],{"class":3437,"line":3481},[3435,9143,3460],{"emptyLinePlaceholder":3459},[3435,9145,9146,9148,9150,9152],{"class":3437,"line":3487},[3435,9147,5227],{"class":4535},[3435,9149,7716],{"class":4473},[3435,9151,8013],{"class":4473},[3435,9153,5237],{"class":5236},[3435,9155,9156,9158,9161,9163,9165,9167,9170],{"class":3437,"line":3493},[3435,9157,8020],{"class":4677},[3435,9159,9160],{"class":4524}," $SG_EC2",[3435,9162,8026],{"class":4677},[3435,9164,8029],{"class":4473},[3435,9166,8032],{"class":4677},[3435,9168,9169],{"class":5337}," 22",[3435,9171,5237],{"class":5236},[3435,9173,9174,9176,9179,9181,9184,9186],{"class":3437,"line":3499},[3435,9175,8042],{"class":4677},[3435,9177,9178],{"class":4473}," \"${",[3435,9180,9126],{"class":4524},[3435,9182,9183],{"class":4473},"}\u002F32\"",[3435,9185,7748],{"class":4677},[3435,9187,5278],{"class":4473},[3435,9189,9190],{"class":3437,"line":3505},[3435,9191,3460],{"emptyLinePlaceholder":3459},[3435,9193,9194,9196,9198,9200],{"class":3437,"line":3511},[3435,9195,5227],{"class":4535},[3435,9197,7716],{"class":4473},[3435,9199,8013],{"class":4473},[3435,9201,5237],{"class":5236},[3435,9203,9204,9206,9208,9210,9212,9214,9217],{"class":3437,"line":3517},[3435,9205,8020],{"class":4677},[3435,9207,9160],{"class":4524},[3435,9209,8026],{"class":4677},[3435,9211,8029],{"class":4473},[3435,9213,8032],{"class":4677},[3435,9215,9216],{"class":5337}," 5000",[3435,9218,5237],{"class":5236},[3435,9220,9221,9223,9226,9228],{"class":3437,"line":3523},[3435,9222,8042],{"class":4677},[3435,9224,9225],{"class":4473}," 0.0.0.0\u002F0",[3435,9227,7748],{"class":4677},[3435,9229,5278],{"class":4473},[3435,9231,9232],{"class":3437,"line":3529},[3435,9233,3460],{"emptyLinePlaceholder":3459},[3435,9235,9236,9238,9241,9244],{"class":3437,"line":3535},[3435,9237,7758],{"class":4535},[3435,9239,9240],{"class":4473}," \"EC2 SG: ",[3435,9242,9243],{"class":4524},"$SG_EC2",[3435,9245,7767],{"class":4473},[5208,9247,9248],{"label":5281},[3426,9249,9251],{"className":5284,"code":9250,"language":5286,"meta":3431,"style":3431},"$SG_EC2 = aws ec2 create-security-group `\n    --group-name dotnet-api-sg `\n    --description \"Security group for .NET API EC2\" `\n    --vpc-id $VPC_ID `\n    --query \"GroupId\" --output text --region eu-central-1\n\n$MY_IP = (Invoke-WebRequest -Uri \"https:\u002F\u002Fcheckip.amazonaws.com\" -UseBasicParsing).Content.Trim()\n\naws ec2 authorize-security-group-ingress `\n    --group-id $SG_EC2 --protocol tcp --port 22 `\n    --cidr \"${MY_IP}\u002F32\" --region eu-central-1\n\naws ec2 authorize-security-group-ingress `\n    --group-id $SG_EC2 --protocol tcp --port 5000 `\n    --cidr 0.0.0.0\u002F0 --region eu-central-1\n\nWrite-Host \"EC2 SG: $SG_EC2\"\n",[3414,9252,9253,9259,9268,9277,9285,9295,9299,9318,9322,9330,9344,9360,9364,9372,9386,9405,9409],{"__ignoreMap":3431},[3435,9254,9255,9257],{"class":3437,"line":3438},[3435,9256,9243],{"class":4524},[3435,9258,8499],{"class":4467},[3435,9260,9261,9263,9265],{"class":3437,"line":3444},[3435,9262,8504],{"class":4467},[3435,9264,8454],{"class":4535},[3435,9266,9267],{"class":4467}," dotnet-api-sg `\n",[3435,9269,9270,9272,9275],{"class":3437,"line":3450},[3435,9271,8514],{"class":4467},[3435,9273,9274],{"class":4473},"\"Security group for .NET API EC2\"",[3435,9276,5329],{"class":4467},[3435,9278,9279,9281,9283],{"class":3437,"line":3456},[3435,9280,8524],{"class":4467},[3435,9282,7764],{"class":4524},[3435,9284,5329],{"class":4467},[3435,9286,9287,9289,9291,9293],{"class":3437,"line":3463},[3435,9288,8333],{"class":4467},[3435,9290,8535],{"class":4473},[3435,9292,8339],{"class":4467},[3435,9294,5338],{"class":5337},[3435,9296,9297],{"class":3437,"line":3469},[3435,9298,3460],{"emptyLinePlaceholder":3459},[3435,9300,9301,9304,9306,9309,9312,9315],{"class":3437,"line":3475},[3435,9302,9303],{"class":4524},"$MY_IP",[3435,9305,8403],{"class":4467},[3435,9307,9308],{"class":4535},"Invoke-WebRequest",[3435,9310,9311],{"class":4467}," -Uri ",[3435,9313,9314],{"class":4473},"\"https:\u002F\u002Fcheckip.amazonaws.com\"",[3435,9316,9317],{"class":4467}," -UseBasicParsing).Content.Trim()\n",[3435,9319,9320],{"class":3437,"line":3481},[3435,9321,3460],{"emptyLinePlaceholder":3459},[3435,9323,9324,9326,9328],{"class":3437,"line":3487},[3435,9325,8553],{"class":4467},[3435,9327,8556],{"class":4535},[3435,9329,5329],{"class":4467},[3435,9331,9332,9334,9336,9338,9340,9342],{"class":3437,"line":3493},[3435,9333,8504],{"class":4467},[3435,9335,8565],{"class":4535},[3435,9337,9160],{"class":4524},[3435,9339,8570],{"class":4467},[3435,9341,9034],{"class":5337},[3435,9343,5329],{"class":4467},[3435,9345,9346,9348,9350,9353,9356,9358],{"class":3437,"line":3499},[3435,9347,8579],{"class":4467},[3435,9349,7799],{"class":4473},[3435,9351,9352],{"class":4524},"${MY_IP}",[3435,9354,9355],{"class":4473},"\u002F32\"",[3435,9357,8596],{"class":4467},[3435,9359,5338],{"class":5337},[3435,9361,9362],{"class":3437,"line":3505},[3435,9363,3460],{"emptyLinePlaceholder":3459},[3435,9365,9366,9368,9370],{"class":3437,"line":3511},[3435,9367,8553],{"class":4467},[3435,9369,8556],{"class":4535},[3435,9371,5329],{"class":4467},[3435,9373,9374,9376,9378,9380,9382,9384],{"class":3437,"line":3517},[3435,9375,8504],{"class":4467},[3435,9377,8565],{"class":4535},[3435,9379,9160],{"class":4524},[3435,9381,8570],{"class":4467},[3435,9383,9046],{"class":5337},[3435,9385,5329],{"class":4467},[3435,9387,9388,9390,9392,9394,9396,9398,9401,9403],{"class":3437,"line":3523},[3435,9389,8579],{"class":4467},[3435,9391,8587],{"class":5337},[3435,9393,3691],{"class":4467},[3435,9395,8587],{"class":5337},[3435,9397,8590],{"class":4467},[3435,9399,9400],{"class":5337},"0",[3435,9402,8596],{"class":4467},[3435,9404,5338],{"class":5337},[3435,9406,9407],{"class":3437,"line":3529},[3435,9408,3460],{"emptyLinePlaceholder":3459},[3435,9410,9411,9413,9415,9417],{"class":3437,"line":3535},[3435,9412,8346],{"class":4535},[3435,9414,9240],{"class":4473},[3435,9416,9243],{"class":4524},[3435,9418,7767],{"class":4473},[3700,9420,9422],{"id":9421},"оновлення-rds-security-group-дозволити-тільки-ec2","Оновлення RDS Security Group: дозволити тільки EC2",[3353,9424,9425,9426,9428,9429,9431,9432,9428,9434,9436,9437,9440],{},"На цьому кроці ми налаштуємо безпеку так, щоб до бази даних RDS (",[3414,9427,7672],{},", змінна ",[3414,9430,8496],{},") міг підключатися виключно наш EC2-інстанс (",[3414,9433,5397],{},[3414,9435,9243],{},"). Це дозволить видалити тимчасове широке правило для всієї VPC і реалізувати принцип найменших привілеїв за допомогою ",[3897,9438,9439],{},"Security Group Referencing"," (коли одна група безпеки дозволяє доступ іншій групі).",[5205,9442,9443,9521,9645],{},[5208,9444,9445],{"label":7569},[3913,9446,9447,9457,9462,9472,9486,9515],{},[3369,9448,7574,9449,9451,9452,9454,9455,3691],{},[3359,9450,8799],{}," (або ",[3359,9453,5675],{},") → ",[3359,9456,9010],{},[3369,9458,9459,9460,5930],{},"Знайдіть та виберіть Security Group, створену для RDS (",[3414,9461,7672],{},[3369,9463,9464,9465,9468,9469,3691],{},"Перейдіть на вкладку ",[3359,9466,9467],{},"Inbound rules"," (Вхідні правила) та натисніть ",[3359,9470,9471],{},"Edit inbound rules",[3369,9473,9474,9475,9477,9478,9481,9482,9485],{},"Знайдіть тимчасове правило для порту ",[3414,9476,5393],{}," з CIDR ",[3414,9479,9480],{},"10.0.0.0\u002F16"," та натисніть ",[3359,9483,9484],{},"Delete",", щоб видалити його.",[3369,9487,9488,9489,9492,9493],{},"Додайте нове правило (натисніть ",[3359,9490,9491],{},"Add rule","):\n",[3366,9494,9495,9503],{},[3369,9496,9497,9500,9501,5930],{},[3359,9498,9499],{},"Type:"," PostgreSQL (порт ",[3414,9502,5393],{},[3369,9504,9505,9508,9509,9512,9513,5930],{},[3359,9506,9507],{},"Source:"," виберіть ",[3359,9510,9511],{},"Custom"," та вкажіть назву або ID Security Group, яку ви створили для EC2 (",[3414,9514,5397],{},[3369,9516,9517,9518,3691],{},"Натисніть ",[3359,9519,9520],{},"Save rules",[5208,9522,9523],{"label":5210},[3426,9524,9526],{"className":5213,"code":9525,"language":5215,"meta":3431,"style":3431},"# Якщо ви почали нову сесію терміналу або створювали ресурси через консоль, спочатку задайте змінні:\n# SG_RDS=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для RDS (rds-postgres-sg)\n# SG_EC2=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для EC2 (dotnet-api-sg)\n\n# Видалити широке VPC-правило (тимчасове CIDR-правило на рівні VPC)\naws ec2 revoke-security-group-ingress \\\n    --group-id $SG_RDS --protocol tcp --port 5432 \\\n    --cidr 10.0.0.0\u002F16 --region eu-central-1\n\n# Додати точне правило із посиланням на Security Group (Security Group Referencing)\n# Це дозволяє трафік виключно від інстансів, які асоційовані з dotnet-api-sg ($SG_EC2).\n# Будь-які зміни IP-адрес віртуальних машин EC2 не вплинуть на доступ до бази даних.\naws ec2 authorize-security-group-ingress \\\n    --group-id $SG_RDS --protocol tcp --port 5432 \\\n    --source-group $SG_EC2 --region eu-central-1\n",[3414,9527,9528,9533,9538,9543,9547,9552,9563,9579,9589,9593,9598,9603,9608,9618,9634],{"__ignoreMap":3431},[3435,9529,9530],{"class":3437,"line":3438},[3435,9531,9532],{"class":4461},"# Якщо ви почали нову сесію терміналу або створювали ресурси через консоль, спочатку задайте змінні:\n",[3435,9534,9535],{"class":3437,"line":3444},[3435,9536,9537],{"class":4461},"# SG_RDS=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для RDS (rds-postgres-sg)\n",[3435,9539,9540],{"class":3437,"line":3450},[3435,9541,9542],{"class":4461},"# SG_EC2=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для EC2 (dotnet-api-sg)\n",[3435,9544,9545],{"class":3437,"line":3456},[3435,9546,3460],{"emptyLinePlaceholder":3459},[3435,9548,9549],{"class":3437,"line":3463},[3435,9550,9551],{"class":4461},"# Видалити широке VPC-правило (тимчасове CIDR-правило на рівні VPC)\n",[3435,9553,9554,9556,9558,9561],{"class":3437,"line":3469},[3435,9555,5227],{"class":4535},[3435,9557,7716],{"class":4473},[3435,9559,9560],{"class":4473}," revoke-security-group-ingress",[3435,9562,5237],{"class":5236},[3435,9564,9565,9567,9569,9571,9573,9575,9577],{"class":3437,"line":3475},[3435,9566,8020],{"class":4677},[3435,9568,8023],{"class":4524},[3435,9570,8026],{"class":4677},[3435,9572,8029],{"class":4473},[3435,9574,8032],{"class":4677},[3435,9576,8035],{"class":5337},[3435,9578,5237],{"class":5236},[3435,9580,9581,9583,9585,9587],{"class":3437,"line":3481},[3435,9582,8042],{"class":4677},[3435,9584,8045],{"class":4473},[3435,9586,7748],{"class":4677},[3435,9588,5278],{"class":4473},[3435,9590,9591],{"class":3437,"line":3487},[3435,9592,3460],{"emptyLinePlaceholder":3459},[3435,9594,9595],{"class":3437,"line":3493},[3435,9596,9597],{"class":4461},"# Додати точне правило із посиланням на Security Group (Security Group Referencing)\n",[3435,9599,9600],{"class":3437,"line":3499},[3435,9601,9602],{"class":4461},"# Це дозволяє трафік виключно від інстансів, які асоційовані з dotnet-api-sg ($SG_EC2).\n",[3435,9604,9605],{"class":3437,"line":3505},[3435,9606,9607],{"class":4461},"# Будь-які зміни IP-адрес віртуальних машин EC2 не вплинуть на доступ до бази даних.\n",[3435,9609,9610,9612,9614,9616],{"class":3437,"line":3511},[3435,9611,5227],{"class":4535},[3435,9613,7716],{"class":4473},[3435,9615,8013],{"class":4473},[3435,9617,5237],{"class":5236},[3435,9619,9620,9622,9624,9626,9628,9630,9632],{"class":3437,"line":3517},[3435,9621,8020],{"class":4677},[3435,9623,8023],{"class":4524},[3435,9625,8026],{"class":4677},[3435,9627,8029],{"class":4473},[3435,9629,8032],{"class":4677},[3435,9631,8035],{"class":5337},[3435,9633,5237],{"class":5236},[3435,9635,9636,9639,9641,9643],{"class":3437,"line":3523},[3435,9637,9638],{"class":4677},"    --source-group",[3435,9640,9160],{"class":4524},[3435,9642,7748],{"class":4677},[3435,9644,5278],{"class":4473},[5208,9646,9647],{"label":5281},[3426,9648,9650],{"className":5284,"code":9649,"language":5286,"meta":3431,"style":3431},"# Якщо ви почали нову сесію терміналу або створювали ресурси через консоль, спочатку задайте змінні:\n# $SG_RDS=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для RDS (rds-postgres-sg)\n# $SG_EC2=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для EC2 (dotnet-api-sg)\n\n# Видалити широке VPC-правило (тимчасове CIDR-правило на рівні VPC)\naws ec2 revoke-security-group-ingress `\n    --group-id $SG_RDS --protocol tcp --port 5432 `\n    --cidr 10.0.0.0\u002F16 --region eu-central-1\n\n# Додати точне правило із посиланням на Security Group (Security Group Referencing)\n# Це дозволяє трафік виключно від інстансів, які асоційовані з dotnet-api-sg ($SG_EC2).\n# Будь-які зміни IP-адрес віртуальних машин EC2 не вплинуть на доступ до бази даних.\naws ec2 authorize-security-group-ingress `\n    --group-id $SG_RDS --protocol tcp --port 5432 `\n    --source-group $SG_EC2 --region eu-central-1\n",[3414,9651,9652,9656,9661,9666,9670,9674,9689,9703,9721,9725,9729,9733,9737,9745,9759],{"__ignoreMap":3431},[3435,9653,9654],{"class":3437,"line":3438},[3435,9655,9532],{"class":4461},[3435,9657,9658],{"class":3437,"line":3444},[3435,9659,9660],{"class":4461},"# $SG_RDS=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для RDS (rds-postgres-sg)\n",[3435,9662,9663],{"class":3437,"line":3450},[3435,9664,9665],{"class":4461},"# $SG_EC2=\"sg-XXXXXXXXXXXXXXXXX\" # ID вашої Security Group для EC2 (dotnet-api-sg)\n",[3435,9667,9668],{"class":3437,"line":3456},[3435,9669,3460],{"emptyLinePlaceholder":3459},[3435,9671,9672],{"class":3437,"line":3463},[3435,9673,9551],{"class":4461},[3435,9675,9676,9679,9682,9685,9687],{"class":3437,"line":3469},[3435,9677,9678],{"class":4467},"aws ec2 ",[3435,9680,9681],{"class":4535},"revoke-security",[3435,9683,9684],{"class":4467},"-",[3435,9686,8556],{"class":4535},[3435,9688,5329],{"class":4467},[3435,9690,9691,9693,9695,9697,9699,9701],{"class":3437,"line":3475},[3435,9692,8504],{"class":4467},[3435,9694,8565],{"class":4535},[3435,9696,8023],{"class":4524},[3435,9698,8570],{"class":4467},[3435,9700,5393],{"class":5337},[3435,9702,5329],{"class":4467},[3435,9704,9705,9707,9709,9711,9713,9715,9717,9719],{"class":3437,"line":3481},[3435,9706,8579],{"class":4467},[3435,9708,8582],{"class":5337},[3435,9710,3691],{"class":4467},[3435,9712,8587],{"class":5337},[3435,9714,8590],{"class":4467},[3435,9716,8593],{"class":5337},[3435,9718,8596],{"class":4467},[3435,9720,5338],{"class":5337},[3435,9722,9723],{"class":3437,"line":3487},[3435,9724,3460],{"emptyLinePlaceholder":3459},[3435,9726,9727],{"class":3437,"line":3493},[3435,9728,9597],{"class":4461},[3435,9730,9731],{"class":3437,"line":3499},[3435,9732,9602],{"class":4461},[3435,9734,9735],{"class":3437,"line":3505},[3435,9736,9607],{"class":4461},[3435,9738,9739,9741,9743],{"class":3437,"line":3511},[3435,9740,8553],{"class":4467},[3435,9742,8556],{"class":4535},[3435,9744,5329],{"class":4467},[3435,9746,9747,9749,9751,9753,9755,9757],{"class":3437,"line":3517},[3435,9748,8504],{"class":4467},[3435,9750,8565],{"class":4535},[3435,9752,8023],{"class":4524},[3435,9754,8570],{"class":4467},[3435,9756,5393],{"class":5337},[3435,9758,5329],{"class":4467},[3435,9760,9761,9764,9766,9768],{"class":3437,"line":3523},[3435,9762,9763],{"class":4467},"    --source-group ",[3435,9765,9243],{"class":4524},[3435,9767,8596],{"class":4467},[3435,9769,5338],{"class":5337},[3700,9771,9773],{"id":9772},"запуск-ec2-instance-з-автовстановленням-net","Запуск EC2 Instance з автовстановленням .NET",[5205,9775,9776,9955,10366],{},[5208,9777,9778,9832,9948],{"label":7569},[3913,9779,9780,9787,9795,9801,9809,9816,9827],{},[3369,9781,9782,7577,9784],{},[3359,9783,8799],{},[3359,9785,9786],{},"Launch instances",[3369,9788,9789,3783,9792],{},[3359,9790,9791],{},"Name:",[3414,9793,9794],{},"dotnet-api-server",[3369,9796,9797,9800],{},[3359,9798,9799],{},"AMI:"," Ubuntu Server 24.04 LTS (Free Tier eligible)",[3369,9802,9803,3783,9806],{},[3359,9804,9805],{},"Instance type:",[3414,9807,9808],{},"t3.micro",[3369,9810,9811,3783,9814],{},[3359,9812,9813],{},"Key pair:",[3414,9815,8811],{},[3369,9817,9818,3783,9821,3417,9823,9826],{},[3359,9819,9820],{},"Security group:",[3414,9822,5397],{},[3359,9824,9825],{},"Auto-assign public IP:"," Enable",[3369,9828,9829],{},[3359,9830,9831],{},"Advanced details → User data:",[3426,9833,9835],{"className":5213,"code":9834,"language":5215,"meta":3431,"style":3431},"#!\u002Fbin\u002Fbash\napt-get update -y\nwget https:\u002F\u002Fpackages.microsoft.com\u002Fconfig\u002Fubuntu\u002F24.04\u002Fpackages-microsoft-prod.deb \\\n    -O \u002Ftmp\u002Fms-prod.deb\ndpkg -i \u002Ftmp\u002Fms-prod.deb\napt-get update -y\napt-get install -y dotnet-sdk-9.0 postgresql-client\nmkdir -p \u002Fopt\u002Fmydotnetapp && chown ubuntu:ubuntu \u002Fopt\u002Fmydotnetapp\necho \"Ready: $(dotnet --version)\" >> \u002Fvar\u002Flog\u002Fuser-data.log\n",[3414,9836,9837,9842,9852,9862,9870,9880,9888,9904,9927],{"__ignoreMap":3431},[3435,9838,9839],{"class":3437,"line":3438},[3435,9840,9841],{"class":4461},"#!\u002Fbin\u002Fbash\n",[3435,9843,9844,9847,9849],{"class":3437,"line":3444},[3435,9845,9846],{"class":4535},"apt-get",[3435,9848,7335],{"class":4473},[3435,9850,9851],{"class":4677}," -y\n",[3435,9853,9854,9857,9860],{"class":3437,"line":3450},[3435,9855,9856],{"class":4535},"wget",[3435,9858,9859],{"class":4473}," https:\u002F\u002Fpackages.microsoft.com\u002Fconfig\u002Fubuntu\u002F24.04\u002Fpackages-microsoft-prod.deb",[3435,9861,5237],{"class":5236},[3435,9863,9864,9867],{"class":3437,"line":3456},[3435,9865,9866],{"class":4677},"    -O",[3435,9868,9869],{"class":4473}," \u002Ftmp\u002Fms-prod.deb\n",[3435,9871,9872,9875,9878],{"class":3437,"line":3463},[3435,9873,9874],{"class":4535},"dpkg",[3435,9876,9877],{"class":4677}," -i",[3435,9879,9869],{"class":4473},[3435,9881,9882,9884,9886],{"class":3437,"line":3469},[3435,9883,9846],{"class":4535},[3435,9885,7335],{"class":4473},[3435,9887,9851],{"class":4677},[3435,9889,9890,9892,9895,9898,9901],{"class":3437,"line":3475},[3435,9891,9846],{"class":4535},[3435,9893,9894],{"class":4473}," install",[3435,9896,9897],{"class":4677}," -y",[3435,9899,9900],{"class":4473}," dotnet-sdk-9.0",[3435,9902,9903],{"class":4473}," postgresql-client\n",[3435,9905,9906,9909,9912,9915,9918,9921,9924],{"class":3437,"line":3481},[3435,9907,9908],{"class":4535},"mkdir",[3435,9910,9911],{"class":4677}," -p",[3435,9913,9914],{"class":4473}," \u002Fopt\u002Fmydotnetapp",[3435,9916,9917],{"class":4467}," && ",[3435,9919,9920],{"class":4535},"chown",[3435,9922,9923],{"class":4473}," ubuntu:ubuntu",[3435,9925,9926],{"class":4473}," \u002Fopt\u002Fmydotnetapp\n",[3435,9928,9929,9931,9934,9936,9939,9942,9945],{"class":3437,"line":3487},[3435,9930,7758],{"class":4535},[3435,9932,9933],{"class":4473}," \"Ready: $(",[3435,9935,6602],{"class":4535},[3435,9937,9938],{"class":4677}," --version",[3435,9940,9941],{"class":4473},")\"",[3435,9943,9944],{"class":4467}," >> ",[3435,9946,9947],{"class":4473},"\u002Fvar\u002Flog\u002Fuser-data.log\n",[3913,9949,9950],{"start":3481},[3369,9951,9952],{},[3359,9953,9954],{},"Launch instance",[5208,9956,9957],{"label":5210},[3426,9958,9960],{"className":5213,"code":9959,"language":5215,"meta":3431,"style":3431},"AMI_ID=$(aws ec2 describe-images \\\n    --owners 099720109477 \\\n    --filters \\\n        \"Name=name,Values=ubuntu\u002Fimages\u002Fhvm-ssd-gp3\u002Fubuntu-noble-24.04-amd64-server-*\" \\\n        \"Name=state,Values=available\" \\\n    --query \"sort_by(Images, &CreationDate)[-1].ImageId\" \\\n    --output text --region eu-central-1)\n\nSUBNET_ID=$(aws ec2 describe-subnets \\\n    --filters \"Name=vpcId,Values=$VPC_ID\" \\\n    --query \"Subnets[0].SubnetId\" \\\n    --output text --region eu-central-1)\n\ncat > \u002Ftmp\u002Fuser-data.sh \u003C\u003C 'EOF'\n#!\u002Fbin\u002Fbash\napt-get update -y\nwget https:\u002F\u002Fpackages.microsoft.com\u002Fconfig\u002Fubuntu\u002F24.04\u002Fpackages-microsoft-prod.deb \\\n    -O \u002Ftmp\u002Fms-prod.deb\ndpkg -i \u002Ftmp\u002Fms-prod.deb\napt-get update -y\napt-get install -y dotnet-sdk-9.0 postgresql-client\nmkdir -p \u002Fopt\u002Fmydotnetapp && chown ubuntu:ubuntu \u002Fopt\u002Fmydotnetapp\necho \"Ready: $(dotnet --version)\" >> \u002Fvar\u002Flog\u002Fuser-data.log\nEOF\n\nINSTANCE_ID=$(aws ec2 run-instances \\\n    --image-id $AMI_ID \\\n    --instance-type t2.micro \\\n    --key-name dotnet-api-key \\\n    --security-group-ids $SG_EC2 \\\n    --subnet-id $SUBNET_ID \\\n    --associate-public-ip-address \\\n    --user-data file:\u002F\u002F\u002Ftmp\u002Fuser-data.sh \\\n    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=dotnet-api-server}]' \\\n    --query \"Instances[0].InstanceId\" \\\n    --output text --region eu-central-1)\necho \"Instance: $INSTANCE_ID\"\n\naws ec2 wait instance-running \\\n    --instance-ids $INSTANCE_ID --region eu-central-1\n\nEC2_IP=$(aws ec2 describe-instances \\\n    --instance-ids $INSTANCE_ID \\\n    --query \"Reservations[0].Instances[0].PublicIpAddress\" \\\n    --output text --region eu-central-1)\necho \"EC2 IP: $EC2_IP\"\n",[3414,9961,9962,9978,9988,9994,10001,10008,10017,10029,10033,10048,10060,10069,10081,10085,10101,10105,10110,10115,10120,10125,10129,10134,10139,10144,10149,10153,10169,10179,10189,10197,10206,10216,10223,10233,10243,10252,10264,10276,10280,10293,10305,10309,10325,10333,10342,10354],{"__ignoreMap":3431},[3435,9963,9964,9967,9969,9971,9973,9976],{"class":3437,"line":3438},[3435,9965,9966],{"class":4524},"AMI_ID",[3435,9968,7711],{"class":4467},[3435,9970,5227],{"class":4535},[3435,9972,7716],{"class":4473},[3435,9974,9975],{"class":4473}," describe-images",[3435,9977,5237],{"class":5236},[3435,9979,9980,9983,9986],{"class":3437,"line":3444},[3435,9981,9982],{"class":4677},"    --owners",[3435,9984,9985],{"class":5337}," 099720109477",[3435,9987,5237],{"class":5236},[3435,9989,9990,9992],{"class":3437,"line":3450},[3435,9991,7726],{"class":4677},[3435,9993,5237],{"class":5236},[3435,9995,9996,9999],{"class":3437,"line":3456},[3435,9997,9998],{"class":4473},"        \"Name=name,Values=ubuntu\u002Fimages\u002Fhvm-ssd-gp3\u002Fubuntu-noble-24.04-amd64-server-*\"",[3435,10000,5237],{"class":5236},[3435,10002,10003,10006],{"class":3437,"line":3463},[3435,10004,10005],{"class":4473},"        \"Name=state,Values=available\"",[3435,10007,5237],{"class":5236},[3435,10009,10010,10012,10015],{"class":3437,"line":3469},[3435,10011,7736],{"class":4677},[3435,10013,10014],{"class":4473}," \"sort_by(Images, &CreationDate)[-1].ImageId\"",[3435,10016,5237],{"class":5236},[3435,10018,10019,10021,10023,10025,10027],{"class":3437,"line":3475},[3435,10020,8279],{"class":4677},[3435,10022,7745],{"class":4473},[3435,10024,7748],{"class":4677},[3435,10026,7751],{"class":4473},[3435,10028,4701],{"class":4467},[3435,10030,10031],{"class":3437,"line":3481},[3435,10032,3460],{"emptyLinePlaceholder":3459},[3435,10034,10035,10038,10040,10042,10044,10046],{"class":3437,"line":3487},[3435,10036,10037],{"class":4524},"SUBNET_ID",[3435,10039,7711],{"class":4467},[3435,10041,5227],{"class":4535},[3435,10043,7716],{"class":4473},[3435,10045,7785],{"class":4473},[3435,10047,5237],{"class":5236},[3435,10049,10050,10052,10054,10056,10058],{"class":3437,"line":3493},[3435,10051,7726],{"class":4677},[3435,10053,7794],{"class":4473},[3435,10055,7764],{"class":4524},[3435,10057,7799],{"class":4473},[3435,10059,5237],{"class":5236},[3435,10061,10062,10064,10067],{"class":3437,"line":3499},[3435,10063,7736],{"class":4677},[3435,10065,10066],{"class":4473}," \"Subnets[0].SubnetId\"",[3435,10068,5237],{"class":5236},[3435,10070,10071,10073,10075,10077,10079],{"class":3437,"line":3505},[3435,10072,8279],{"class":4677},[3435,10074,7745],{"class":4473},[3435,10076,7748],{"class":4677},[3435,10078,7751],{"class":4473},[3435,10080,4701],{"class":4467},[3435,10082,10083],{"class":3437,"line":3511},[3435,10084,3460],{"emptyLinePlaceholder":3459},[3435,10086,10087,10090,10092,10095,10098],{"class":3437,"line":3517},[3435,10088,10089],{"class":4535},"cat",[3435,10091,8888],{"class":4467},[3435,10093,10094],{"class":4473},"\u002Ftmp\u002Fuser-data.sh",[3435,10096,10097],{"class":4467}," \u003C\u003C ",[3435,10099,10100],{"class":4467},"'EOF'\n",[3435,10102,10103],{"class":3437,"line":3523},[3435,10104,9841],{"class":4473},[3435,10106,10107],{"class":3437,"line":3529},[3435,10108,10109],{"class":4473},"apt-get update -y\n",[3435,10111,10112],{"class":3437,"line":3535},[3435,10113,10114],{"class":4473},"wget https:\u002F\u002Fpackages.microsoft.com\u002Fconfig\u002Fubuntu\u002F24.04\u002Fpackages-microsoft-prod.deb \\\n",[3435,10116,10117],{"class":3437,"line":3540},[3435,10118,10119],{"class":4473},"    -O \u002Ftmp\u002Fms-prod.deb\n",[3435,10121,10122],{"class":3437,"line":3546},[3435,10123,10124],{"class":4473},"dpkg -i \u002Ftmp\u002Fms-prod.deb\n",[3435,10126,10127],{"class":3437,"line":3552},[3435,10128,10109],{"class":4473},[3435,10130,10131],{"class":3437,"line":3558},[3435,10132,10133],{"class":4473},"apt-get install -y dotnet-sdk-9.0 postgresql-client\n",[3435,10135,10136],{"class":3437,"line":3563},[3435,10137,10138],{"class":4473},"mkdir -p \u002Fopt\u002Fmydotnetapp && chown ubuntu:ubuntu \u002Fopt\u002Fmydotnetapp\n",[3435,10140,10141],{"class":3437,"line":3569},[3435,10142,10143],{"class":4473},"echo \"Ready: $(dotnet --version)\" >> \u002Fvar\u002Flog\u002Fuser-data.log\n",[3435,10145,10146],{"class":3437,"line":3575},[3435,10147,10148],{"class":4467},"EOF\n",[3435,10150,10151],{"class":3437,"line":3580},[3435,10152,3460],{"emptyLinePlaceholder":3459},[3435,10154,10155,10158,10160,10162,10164,10167],{"class":3437,"line":3586},[3435,10156,10157],{"class":4524},"INSTANCE_ID",[3435,10159,7711],{"class":4467},[3435,10161,5227],{"class":4535},[3435,10163,7716],{"class":4473},[3435,10165,10166],{"class":4473}," run-instances",[3435,10168,5237],{"class":5236},[3435,10170,10171,10174,10177],{"class":3437,"line":3592},[3435,10172,10173],{"class":4677},"    --image-id",[3435,10175,10176],{"class":4524}," $AMI_ID",[3435,10178,5237],{"class":5236},[3435,10180,10181,10184,10187],{"class":3437,"line":3597},[3435,10182,10183],{"class":4677},"    --instance-type",[3435,10185,10186],{"class":4473}," t2.micro",[3435,10188,5237],{"class":5236},[3435,10190,10191,10193,10195],{"class":3437,"line":3603},[3435,10192,8857],{"class":4677},[3435,10194,8860],{"class":4473},[3435,10196,5237],{"class":5236},[3435,10198,10199,10202,10204],{"class":3437,"line":3609},[3435,10200,10201],{"class":4677},"    --security-group-ids",[3435,10203,9160],{"class":4524},[3435,10205,5237],{"class":5236},[3435,10207,10208,10211,10214],{"class":3437,"line":3615},[3435,10209,10210],{"class":4677},"    --subnet-id",[3435,10212,10213],{"class":4524}," $SUBNET_ID",[3435,10215,5237],{"class":5236},[3435,10217,10218,10221],{"class":3437,"line":3621},[3435,10219,10220],{"class":4677},"    --associate-public-ip-address",[3435,10222,5237],{"class":5236},[3435,10224,10225,10228,10231],{"class":3437,"line":3627},[3435,10226,10227],{"class":4677},"    --user-data",[3435,10229,10230],{"class":4473}," file:\u002F\u002F\u002Ftmp\u002Fuser-data.sh",[3435,10232,5237],{"class":5236},[3435,10234,10235,10238,10241],{"class":3437,"line":3632},[3435,10236,10237],{"class":4677},"    --tag-specifications",[3435,10239,10240],{"class":4473}," 'ResourceType=instance,Tags=[{Key=Name,Value=dotnet-api-server}]'",[3435,10242,5237],{"class":5236},[3435,10244,10245,10247,10250],{"class":3437,"line":3637},[3435,10246,7736],{"class":4677},[3435,10248,10249],{"class":4473}," \"Instances[0].InstanceId\"",[3435,10251,5237],{"class":5236},[3435,10253,10254,10256,10258,10260,10262],{"class":3437,"line":4160},[3435,10255,8279],{"class":4677},[3435,10257,7745],{"class":4473},[3435,10259,7748],{"class":4677},[3435,10261,7751],{"class":4473},[3435,10263,4701],{"class":4467},[3435,10265,10266,10268,10271,10274],{"class":3437,"line":4166},[3435,10267,7758],{"class":4535},[3435,10269,10270],{"class":4473}," \"Instance: ",[3435,10272,10273],{"class":4524},"$INSTANCE_ID",[3435,10275,7767],{"class":4473},[3435,10277,10278],{"class":3437,"line":4171},[3435,10279,3460],{"emptyLinePlaceholder":3459},[3435,10281,10282,10284,10286,10288,10291],{"class":3437,"line":4176},[3435,10283,5227],{"class":4535},[3435,10285,7716],{"class":4473},[3435,10287,8210],{"class":4473},[3435,10289,10290],{"class":4473}," instance-running",[3435,10292,5237],{"class":5236},[3435,10294,10295,10298,10301,10303],{"class":3437,"line":4182},[3435,10296,10297],{"class":4677},"    --instance-ids",[3435,10299,10300],{"class":4524}," $INSTANCE_ID",[3435,10302,7748],{"class":4677},[3435,10304,5278],{"class":4473},[3435,10306,10307],{"class":3437,"line":4188},[3435,10308,3460],{"emptyLinePlaceholder":3459},[3435,10310,10311,10314,10316,10318,10320,10323],{"class":3437,"line":4194},[3435,10312,10313],{"class":4524},"EC2_IP",[3435,10315,7711],{"class":4467},[3435,10317,5227],{"class":4535},[3435,10319,7716],{"class":4473},[3435,10321,10322],{"class":4473}," describe-instances",[3435,10324,5237],{"class":5236},[3435,10326,10327,10329,10331],{"class":3437,"line":4199},[3435,10328,10297],{"class":4677},[3435,10330,10300],{"class":4524},[3435,10332,5237],{"class":5236},[3435,10334,10335,10337,10340],{"class":3437,"line":4205},[3435,10336,7736],{"class":4677},[3435,10338,10339],{"class":4473}," \"Reservations[0].Instances[0].PublicIpAddress\"",[3435,10341,5237],{"class":5236},[3435,10343,10344,10346,10348,10350,10352],{"class":3437,"line":4211},[3435,10345,8279],{"class":4677},[3435,10347,7745],{"class":4473},[3435,10349,7748],{"class":4677},[3435,10351,7751],{"class":4473},[3435,10353,4701],{"class":4467},[3435,10355,10356,10358,10361,10364],{"class":3437,"line":4217},[3435,10357,7758],{"class":4535},[3435,10359,10360],{"class":4473}," \"EC2 IP: ",[3435,10362,10363],{"class":4524},"$EC2_IP",[3435,10365,7767],{"class":4473},[5208,10367,10368],{"label":5281},[3426,10369,10371],{"className":5284,"code":10370,"language":5286,"meta":3431,"style":3431},"$AMI_ID = aws ec2 describe-images `\n    --owners 099720109477 `\n    --filters `\n        \"Name=name,Values=ubuntu\u002Fimages\u002Fhvm-ssd-gp3\u002Fubuntu-noble-24.04-amd64-server-*\" `\n        \"Name=state,Values=available\" `\n    --query \"sort_by(Images, &CreationDate)[-1].ImageId\" `\n    --output text --region eu-central-1\n\n$SUBNET_ID = aws ec2 describe-subnets `\n    --filters \"Name=vpcId,Values=$VPC_ID\" `\n    --query \"Subnets[0].SubnetId\" `\n    --output text --region eu-central-1\n\n$userDataScript = @'\n#!\u002Fbin\u002Fbash\napt-get update -y\nwget https:\u002F\u002Fpackages.microsoft.com\u002Fconfig\u002Fubuntu\u002F24.04\u002Fpackages-microsoft-prod.deb -O \u002Ftmp\u002Fms-prod.deb\ndpkg -i \u002Ftmp\u002Fms-prod.deb\napt-get update -y\napt-get install -y dotnet-sdk-9.0 postgresql-client\nmkdir -p \u002Fopt\u002Fmydotnetapp && chown ubuntu:ubuntu \u002Fopt\u002Fmydotnetapp\necho \"Ready\" >> \u002Fvar\u002Flog\u002Fuser-data.log\n'@\n$userDataB64 = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($userDataScript))\n\n$INSTANCE_ID = aws ec2 run-instances `\n    --image-id $AMI_ID `\n    --instance-type t2.micro `\n    --key-name dotnet-api-key `\n    --security-group-ids $SG_EC2 `\n    --subnet-id $SUBNET_ID `\n    --associate-public-ip-address `\n    --user-data $userDataB64 `\n    --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=dotnet-api-server}]' `\n    --query \"Instances[0].InstanceId\" `\n    --output text --region eu-central-1\nWrite-Host \"Instance: $INSTANCE_ID\"\n\naws ec2 wait instance-running `\n    --instance-ids $INSTANCE_ID --region eu-central-1\n\n$EC2_IP = aws ec2 describe-instances `\n    --instance-ids $INSTANCE_ID `\n    --query \"Reservations[0].Instances[0].PublicIpAddress\" `\n    --output text --region eu-central-1\nWrite-Host \"EC2 IP: $EC2_IP\"\n",[3414,10372,10373,10381,10391,10396,10402,10408,10417,10423,10427,10435,10447,10456,10462,10466,10476,10480,10484,10489,10493,10497,10501,10505,10510,10515,10539,10543,10550,10559,10564,10568,10579,10588,10593,10602,10612,10621,10627,10637,10641,10646,10657,10661,10668,10676,10685,10691],{"__ignoreMap":3431},[3435,10374,10375,10378],{"class":3437,"line":3438},[3435,10376,10377],{"class":4524},"$AMI_ID",[3435,10379,10380],{"class":4467}," = aws ec2 describe-images `\n",[3435,10382,10383,10386,10389],{"class":3437,"line":3444},[3435,10384,10385],{"class":4467},"    --owners ",[3435,10387,10388],{"class":5337},"099720109477",[3435,10390,5329],{"class":4467},[3435,10392,10393],{"class":3437,"line":3450},[3435,10394,10395],{"class":4467},"    --filters `\n",[3435,10397,10398,10400],{"class":3437,"line":3456},[3435,10399,9998],{"class":4473},[3435,10401,5329],{"class":4467},[3435,10403,10404,10406],{"class":3437,"line":3463},[3435,10405,10005],{"class":4473},[3435,10407,5329],{"class":4467},[3435,10409,10410,10412,10415],{"class":3437,"line":3469},[3435,10411,8333],{"class":4467},[3435,10413,10414],{"class":4473},"\"sort_by(Images, &CreationDate)[-1].ImageId\"",[3435,10416,5329],{"class":4467},[3435,10418,10419,10421],{"class":3437,"line":3475},[3435,10420,8765],{"class":4467},[3435,10422,5338],{"class":5337},[3435,10424,10425],{"class":3437,"line":3481},[3435,10426,3460],{"emptyLinePlaceholder":3459},[3435,10428,10429,10432],{"class":3437,"line":3487},[3435,10430,10431],{"class":4524},"$SUBNET_ID",[3435,10433,10434],{"class":4467}," = aws ec2 describe-subnets `\n",[3435,10436,10437,10439,10441,10443,10445],{"class":3437,"line":3493},[3435,10438,8323],{"class":4467},[3435,10440,8371],{"class":4473},[3435,10442,7764],{"class":4524},[3435,10444,7799],{"class":4473},[3435,10446,5329],{"class":4467},[3435,10448,10449,10451,10454],{"class":3437,"line":3499},[3435,10450,8333],{"class":4467},[3435,10452,10453],{"class":4473},"\"Subnets[0].SubnetId\"",[3435,10455,5329],{"class":4467},[3435,10457,10458,10460],{"class":3437,"line":3505},[3435,10459,8765],{"class":4467},[3435,10461,5338],{"class":5337},[3435,10463,10464],{"class":3437,"line":3511},[3435,10465,3460],{"emptyLinePlaceholder":3459},[3435,10467,10468,10471,10473],{"class":3437,"line":3517},[3435,10469,10470],{"class":4524},"$userDataScript",[3435,10472,5300],{"class":4467},[3435,10474,10475],{"class":4473},"@'\n",[3435,10477,10478],{"class":3437,"line":3523},[3435,10479,9841],{"class":4473},[3435,10481,10482],{"class":3437,"line":3529},[3435,10483,10109],{"class":4473},[3435,10485,10486],{"class":3437,"line":3535},[3435,10487,10488],{"class":4473},"wget https:\u002F\u002Fpackages.microsoft.com\u002Fconfig\u002Fubuntu\u002F24.04\u002Fpackages-microsoft-prod.deb -O \u002Ftmp\u002Fms-prod.deb\n",[3435,10490,10491],{"class":3437,"line":3540},[3435,10492,10124],{"class":4473},[3435,10494,10495],{"class":3437,"line":3546},[3435,10496,10109],{"class":4473},[3435,10498,10499],{"class":3437,"line":3552},[3435,10500,10133],{"class":4473},[3435,10502,10503],{"class":3437,"line":3558},[3435,10504,10138],{"class":4473},[3435,10506,10507],{"class":3437,"line":3563},[3435,10508,10509],{"class":4473},"echo \"Ready\" >> \u002Fvar\u002Flog\u002Fuser-data.log\n",[3435,10511,10512],{"class":3437,"line":3569},[3435,10513,10514],{"class":4473},"'@\n",[3435,10516,10517,10520,10523,10526,10529,10532,10535,10537],{"class":3437,"line":3575},[3435,10518,10519],{"class":4524},"$userDataB64",[3435,10521,10522],{"class":4467}," = [",[3435,10524,10525],{"class":4677},"Convert",[3435,10527,10528],{"class":4467},"]::ToBase64String([",[3435,10530,10531],{"class":4677},"Text.Encoding",[3435,10533,10534],{"class":4467},"]::UTF8.GetBytes(",[3435,10536,10470],{"class":4524},[3435,10538,4640],{"class":4467},[3435,10540,10541],{"class":3437,"line":3580},[3435,10542,3460],{"emptyLinePlaceholder":3459},[3435,10544,10545,10547],{"class":3437,"line":3586},[3435,10546,10273],{"class":4524},[3435,10548,10549],{"class":4467}," = aws ec2 run-instances `\n",[3435,10551,10552,10555,10557],{"class":3437,"line":3592},[3435,10553,10554],{"class":4467},"    --image-id ",[3435,10556,10377],{"class":4524},[3435,10558,5329],{"class":4467},[3435,10560,10561],{"class":3437,"line":3597},[3435,10562,10563],{"class":4467},"    --instance-type t2.micro `\n",[3435,10565,10566],{"class":3437,"line":3603},[3435,10567,8931],{"class":4467},[3435,10569,10570,10573,10575,10577],{"class":3437,"line":3609},[3435,10571,10572],{"class":4467},"    --security-",[3435,10574,8677],{"class":4535},[3435,10576,9160],{"class":4524},[3435,10578,5329],{"class":4467},[3435,10580,10581,10584,10586],{"class":3437,"line":3615},[3435,10582,10583],{"class":4467},"    --subnet-id ",[3435,10585,10431],{"class":4524},[3435,10587,5329],{"class":4467},[3435,10589,10590],{"class":3437,"line":3621},[3435,10591,10592],{"class":4467},"    --associate-public-ip-address `\n",[3435,10594,10595,10598,10600],{"class":3437,"line":3627},[3435,10596,10597],{"class":4467},"    --user-data ",[3435,10599,10519],{"class":4524},[3435,10601,5329],{"class":4467},[3435,10603,10604,10607,10610],{"class":3437,"line":3632},[3435,10605,10606],{"class":4467},"    --tag-specifications ",[3435,10608,10609],{"class":4473},"'ResourceType=instance,Tags=[{Key=Name,Value=dotnet-api-server}]'",[3435,10611,5329],{"class":4467},[3435,10613,10614,10616,10619],{"class":3437,"line":3637},[3435,10615,8333],{"class":4467},[3435,10617,10618],{"class":4473},"\"Instances[0].InstanceId\"",[3435,10620,5329],{"class":4467},[3435,10622,10623,10625],{"class":3437,"line":4160},[3435,10624,8765],{"class":4467},[3435,10626,5338],{"class":5337},[3435,10628,10629,10631,10633,10635],{"class":3437,"line":4166},[3435,10630,8346],{"class":4535},[3435,10632,10270],{"class":4473},[3435,10634,10273],{"class":4524},[3435,10636,7767],{"class":4473},[3435,10638,10639],{"class":3437,"line":4171},[3435,10640,3460],{"emptyLinePlaceholder":3459},[3435,10642,10643],{"class":3437,"line":4176},[3435,10644,10645],{"class":4467},"aws ec2 wait instance-running `\n",[3435,10647,10648,10651,10653,10655],{"class":3437,"line":4182},[3435,10649,10650],{"class":4467},"    --instance-ids ",[3435,10652,10273],{"class":4524},[3435,10654,8596],{"class":4467},[3435,10656,5338],{"class":5337},[3435,10658,10659],{"class":3437,"line":4188},[3435,10660,3460],{"emptyLinePlaceholder":3459},[3435,10662,10663,10665],{"class":3437,"line":4194},[3435,10664,10363],{"class":4524},[3435,10666,10667],{"class":4467}," = aws ec2 describe-instances `\n",[3435,10669,10670,10672,10674],{"class":3437,"line":4199},[3435,10671,10650],{"class":4467},[3435,10673,10273],{"class":4524},[3435,10675,5329],{"class":4467},[3435,10677,10678,10680,10683],{"class":3437,"line":4205},[3435,10679,8333],{"class":4467},[3435,10681,10682],{"class":4473},"\"Reservations[0].Instances[0].PublicIpAddress\"",[3435,10684,5329],{"class":4467},[3435,10686,10687,10689],{"class":3437,"line":4211},[3435,10688,8765],{"class":4467},[3435,10690,5338],{"class":5337},[3435,10692,10693,10695,10697,10699],{"class":3437,"line":4217},[3435,10694,8346],{"class":4535},[3435,10696,10360],{"class":4473},[3435,10698,10363],{"class":4524},[3435,10700,7767],{"class":4473},[3778,10702,10703,10704],{},"Після запуску зачекайте ~2-3 хвилини на виконання User Data. Перевірте: ",[3414,10705,10706],{},"ssh -i ~\u002F.ssh\u002Fdotnet-api-key.pem ubuntu@$EC2_IP \"cat \u002Fvar\u002Flog\u002Fuser-data.log\"",[3679,10708],{},[3700,10710,10712],{"id":10711},"крок-3-підготовка-net-проєкту","Крок 3: Підготовка .NET проєкту",[3426,10714,10716],{"className":5213,"code":10715,"language":5215,"meta":3431,"style":3431},"dotnet new webapi -n MyDotnetApp --no-openapi\ncd MyDotnetApp\ndotnet add package Npgsql.EntityFrameworkCore.PostgreSQL\ndotnet add package Microsoft.EntityFrameworkCore.Design\n",[3414,10717,10718,10737,10745,10755],{"__ignoreMap":3431},[3435,10719,10720,10722,10725,10728,10731,10734],{"class":3437,"line":3438},[3435,10721,6602],{"class":4535},[3435,10723,10724],{"class":4473}," new",[3435,10726,10727],{"class":4473}," webapi",[3435,10729,10730],{"class":4677}," -n",[3435,10732,10733],{"class":4473}," MyDotnetApp",[3435,10735,10736],{"class":4677}," --no-openapi\n",[3435,10738,10739,10742],{"class":3437,"line":3444},[3435,10740,10741],{"class":4535},"cd",[3435,10743,10744],{"class":4473}," MyDotnetApp\n",[3435,10746,10747,10749,10751,10753],{"class":3437,"line":3450},[3435,10748,6602],{"class":4535},[3435,10750,6605],{"class":4473},[3435,10752,6608],{"class":4473},[3435,10754,6611],{"class":4473},[3435,10756,10757,10759,10761,10763],{"class":3437,"line":3456},[3435,10758,6602],{"class":4535},[3435,10760,6605],{"class":4473},[3435,10762,6608],{"class":4473},[3435,10764,6645],{"class":4473},[10766,10767,10768,10878,11070,11296,11528],"code-tree",{},[3426,10769,10772],{"className":4452,"code":10770,"filename":10771,"language":4454,"meta":3431,"style":3431},"public class Product\n{\n    public int Id { get; set; }\n    public string Name { get; set; } = string.Empty;\n    public decimal Price { get; set; }\n    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;\n}\n","Models\u002FProduct.cs",[3414,10773,10774,10782,10786,10804,10830,10848,10874],{"__ignoreMap":3431},[3435,10775,10776,10778,10780],{"class":3437,"line":3438},[3435,10777,4678],{"class":4677},[3435,10779,4681],{"class":4677},[3435,10781,6668],{"class":4542},[3435,10783,10784],{"class":3437,"line":3444},[3435,10785,4468],{"class":4467},[3435,10787,10788,10790,10792,10794,10796,10798,10800,10802],{"class":3437,"line":3450},[3435,10789,4715],{"class":4677},[3435,10791,6679],{"class":4677},[3435,10793,6682],{"class":4524},[3435,10795,6685],{"class":4467},[3435,10797,6688],{"class":4677},[3435,10799,6691],{"class":4467},[3435,10801,6694],{"class":4677},[3435,10803,6697],{"class":4467},[3435,10805,10806,10808,10810,10812,10814,10816,10818,10820,10822,10824,10826,10828],{"class":3437,"line":3456},[3435,10807,4715],{"class":4677},[3435,10809,6704],{"class":4677},[3435,10811,6707],{"class":4524},[3435,10813,6685],{"class":4467},[3435,10815,6688],{"class":4677},[3435,10817,6691],{"class":4467},[3435,10819,6694],{"class":4677},[3435,10821,6718],{"class":4467},[3435,10823,5728],{"class":4677},[3435,10825,3691],{"class":4467},[3435,10827,6725],{"class":4524},[3435,10829,5664],{"class":4467},[3435,10831,10832,10834,10836,10838,10840,10842,10844,10846],{"class":3437,"line":3463},[3435,10833,4715],{"class":4677},[3435,10835,6734],{"class":4677},[3435,10837,6737],{"class":4524},[3435,10839,6685],{"class":4467},[3435,10841,6688],{"class":4677},[3435,10843,6691],{"class":4467},[3435,10845,6694],{"class":4677},[3435,10847,6697],{"class":4467},[3435,10849,10850,10852,10854,10856,10858,10860,10862,10864,10866,10868,10870,10872],{"class":3437,"line":3469},[3435,10851,4715],{"class":4677},[3435,10853,6754],{"class":4542},[3435,10855,6757],{"class":4524},[3435,10857,6685],{"class":4467},[3435,10859,6688],{"class":4677},[3435,10861,6691],{"class":4467},[3435,10863,6694],{"class":4677},[3435,10865,6718],{"class":4467},[3435,10867,6770],{"class":4524},[3435,10869,3691],{"class":4467},[3435,10871,6775],{"class":4524},[3435,10873,5664],{"class":4467},[3435,10875,10876],{"class":3437,"line":3475},[3435,10877,3532],{"class":4467},[3426,10879,10882],{"className":4452,"code":10880,"filename":10881,"language":4454,"meta":3431,"style":3431},"public class AppDbContext(DbContextOptions\u003CAppDbContext> options) : DbContext(options)\n{\n    public DbSet\u003CProduct> Products => Set\u003CProduct>();\n\n    protected override void OnModelCreating(ModelBuilder modelBuilder)\n    {\n        modelBuilder.Entity\u003CProduct>(entity =>\n        {\n            entity.Property(p => p.Name).HasMaxLength(200).IsRequired();\n            entity.Property(p => p.Price).HasPrecision(18, 2);\n        });\n    }\n}\n","Data\u002FAppDbContext.cs",[3414,10883,10884,10914,10918,10942,10946,10964,10968,10986,10990,11024,11058,11062,11066],{"__ignoreMap":3431},[3435,10885,10886,10888,10890,10892,10894,10896,10898,10900,10902,10904,10906,10908,10910,10912],{"class":3437,"line":3438},[3435,10887,4678],{"class":4677},[3435,10889,4681],{"class":4677},[3435,10891,6799],{"class":4542},[3435,10893,4565],{"class":4467},[3435,10895,6804],{"class":4542},[3435,10897,4539],{"class":4467},[3435,10899,4543],{"class":4542},[3435,10901,6811],{"class":4467},[3435,10903,4549],{"class":4524},[3435,10905,6816],{"class":4467},[3435,10907,6819],{"class":4542},[3435,10909,4565],{"class":4467},[3435,10911,4549],{"class":4524},[3435,10913,4701],{"class":4467},[3435,10915,10916],{"class":3437,"line":3444},[3435,10917,4468],{"class":4467},[3435,10919,10920,10922,10924,10926,10928,10930,10932,10934,10936,10938,10940],{"class":3437,"line":3450},[3435,10921,4715],{"class":4677},[3435,10923,6836],{"class":4542},[3435,10925,4539],{"class":4467},[3435,10927,4731],{"class":4542},[3435,10929,6811],{"class":4467},[3435,10931,4752],{"class":4524},[3435,10933,6847],{"class":4467},[3435,10935,6850],{"class":4535},[3435,10937,4539],{"class":4467},[3435,10939,4731],{"class":4542},[3435,10941,6857],{"class":4467},[3435,10943,10944],{"class":3437,"line":3456},[3435,10945,3460],{"emptyLinePlaceholder":3459},[3435,10947,10948,10950,10952,10954,10956,10958,10960,10962],{"class":3437,"line":3463},[3435,10949,6866],{"class":4677},[3435,10951,6869],{"class":4677},[3435,10953,6872],{"class":4677},[3435,10955,6875],{"class":4535},[3435,10957,4565],{"class":4467},[3435,10959,6880],{"class":4542},[3435,10961,6883],{"class":4524},[3435,10963,4701],{"class":4467},[3435,10965,10966],{"class":3437,"line":3469},[3435,10967,4794],{"class":4467},[3435,10969,10970,10972,10974,10976,10978,10980,10982,10984],{"class":3437,"line":3475},[3435,10971,6894],{"class":4524},[3435,10973,3691],{"class":4467},[3435,10975,6899],{"class":4535},[3435,10977,4539],{"class":4467},[3435,10979,4731],{"class":4542},[3435,10981,4546],{"class":4467},[3435,10983,6908],{"class":4524},[3435,10985,4552],{"class":4467},[3435,10987,10988],{"class":3437,"line":3481},[3435,10989,6915],{"class":4467},[3435,10991,10992,10994,10996,10998,11000,11002,11004,11006,11008,11010,11012,11014,11016,11018,11020,11022],{"class":3437,"line":3487},[3435,10993,6920],{"class":4524},[3435,10995,3691],{"class":4467},[3435,10997,6925],{"class":4535},[3435,10999,4565],{"class":4467},[3435,11001,3353],{"class":4524},[3435,11003,6847],{"class":4467},[3435,11005,3353],{"class":4524},[3435,11007,3691],{"class":4467},[3435,11009,6938],{"class":4524},[3435,11011,5930],{"class":4467},[3435,11013,6943],{"class":4535},[3435,11015,4565],{"class":4467},[3435,11017,6948],{"class":5337},[3435,11019,5930],{"class":4467},[3435,11021,6953],{"class":4535},[3435,11023,4760],{"class":4467},[3435,11025,11026,11028,11030,11032,11034,11036,11038,11040,11042,11044,11046,11048,11050,11052,11054,11056],{"class":3437,"line":3493},[3435,11027,6920],{"class":4524},[3435,11029,3691],{"class":4467},[3435,11031,6925],{"class":4535},[3435,11033,4565],{"class":4467},[3435,11035,3353],{"class":4524},[3435,11037,6847],{"class":4467},[3435,11039,3353],{"class":4524},[3435,11041,3691],{"class":4467},[3435,11043,6976],{"class":4524},[3435,11045,5930],{"class":4467},[3435,11047,6981],{"class":4535},[3435,11049,4565],{"class":4467},[3435,11051,6986],{"class":5337},[3435,11053,3417],{"class":4467},[3435,11055,6991],{"class":5337},[3435,11057,4816],{"class":4467},[3435,11059,11060],{"class":3437,"line":3499},[3435,11061,6998],{"class":4467},[3435,11063,11064],{"class":3437,"line":3505},[3435,11065,3490],{"class":4467},[3435,11067,11068],{"class":3437,"line":3511},[3435,11069,3532],{"class":4467},[3426,11071,11073],{"className":4452,"code":11072,"filename":7354,"language":4454,"meta":3431,"style":3431},"var builder = WebApplication.CreateBuilder(args);\n\nvar connStr = Environment.GetEnvironmentVariable(\"DB_CONNECTION_STRING\")\n    ?? builder.Configuration.GetConnectionString(\"Default\");\n\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseNpgsql(connStr, npgsqlOptions =>\n        npgsqlOptions.EnableRetryOnFailure(\n            maxRetryCount: 3,\n            maxRetryDelay: TimeSpan.FromSeconds(5),\n            errorCodesToAdd: null)));\n\nbuilder.Services.AddControllers();\nvar app = builder.Build();\napp.MapControllers();\napp.Run();\n",[3414,11074,11075,11099,11103,11123,11144,11148,11170,11188,11198,11208,11226,11236,11240,11255,11273,11285],{"__ignoreMap":3431},[3435,11076,11077,11079,11082,11084,11087,11089,11092,11094,11097],{"class":3437,"line":3438},[3435,11078,5691],{"class":4677},[3435,11080,11081],{"class":4524}," builder",[3435,11083,5300],{"class":4467},[3435,11085,11086],{"class":4524},"WebApplication",[3435,11088,3691],{"class":4467},[3435,11090,11091],{"class":4535},"CreateBuilder",[3435,11093,4565],{"class":4467},[3435,11095,11096],{"class":4524},"args",[3435,11098,4816],{"class":4467},[3435,11100,11101],{"class":3437,"line":3444},[3435,11102,3460],{"emptyLinePlaceholder":3459},[3435,11104,11105,11107,11109,11111,11113,11115,11117,11119,11121],{"class":3437,"line":3450},[3435,11106,5691],{"class":4677},[3435,11108,7069],{"class":4524},[3435,11110,5300],{"class":4467},[3435,11112,7074],{"class":4524},[3435,11114,3691],{"class":4467},[3435,11116,7079],{"class":4535},[3435,11118,4565],{"class":4467},[3435,11120,7084],{"class":4473},[3435,11122,4701],{"class":4467},[3435,11124,11125,11128,11130,11132,11134,11136,11138,11140,11142],{"class":3437,"line":3456},[3435,11126,11127],{"class":4467},"    ?? ",[3435,11129,4525],{"class":4524},[3435,11131,3691],{"class":4467},[3435,11133,4572],{"class":4524},[3435,11135,3691],{"class":4467},[3435,11137,4577],{"class":4535},[3435,11139,4565],{"class":4467},[3435,11141,7106],{"class":4473},[3435,11143,4816],{"class":4467},[3435,11145,11146],{"class":3437,"line":3463},[3435,11147,3460],{"emptyLinePlaceholder":3459},[3435,11149,11150,11152,11154,11156,11158,11160,11162,11164,11166,11168],{"class":3437,"line":3469},[3435,11151,4525],{"class":4524},[3435,11153,3691],{"class":4467},[3435,11155,4530],{"class":4524},[3435,11157,3691],{"class":4467},[3435,11159,4536],{"class":4535},[3435,11161,4539],{"class":4467},[3435,11163,4543],{"class":4542},[3435,11165,4546],{"class":4467},[3435,11167,4549],{"class":4524},[3435,11169,4552],{"class":4467},[3435,11171,11172,11174,11176,11178,11180,11182,11184,11186],{"class":3437,"line":3475},[3435,11173,4557],{"class":4524},[3435,11175,3691],{"class":4467},[3435,11177,4562],{"class":4535},[3435,11179,4565],{"class":4467},[3435,11181,7125],{"class":4524},[3435,11183,3417],{"class":4467},[3435,11185,7130],{"class":4524},[3435,11187,4552],{"class":4467},[3435,11189,11190,11192,11194,11196],{"class":3437,"line":3481},[3435,11191,7146],{"class":4524},[3435,11193,3691],{"class":4467},[3435,11195,7151],{"class":4535},[3435,11197,5744],{"class":4467},[3435,11199,11200,11202,11204,11206],{"class":3437,"line":3487},[3435,11201,7158],{"class":4524},[3435,11203,4485],{"class":4467},[3435,11205,7163],{"class":5337},[3435,11207,4491],{"class":4467},[3435,11209,11210,11212,11214,11216,11218,11220,11222,11224],{"class":3437,"line":3493},[3435,11211,7170],{"class":4524},[3435,11213,4485],{"class":4467},[3435,11215,7175],{"class":4524},[3435,11217,3691],{"class":4467},[3435,11219,7180],{"class":4535},[3435,11221,4565],{"class":4467},[3435,11223,7185],{"class":5337},[3435,11225,7188],{"class":4467},[3435,11227,11228,11230,11232,11234],{"class":3437,"line":3499},[3435,11229,7193],{"class":4524},[3435,11231,4485],{"class":4467},[3435,11233,7198],{"class":4677},[3435,11235,4585],{"class":4467},[3435,11237,11238],{"class":3437,"line":3505},[3435,11239,3460],{"emptyLinePlaceholder":3459},[3435,11241,11242,11244,11246,11248,11250,11253],{"class":3437,"line":3511},[3435,11243,4525],{"class":4524},[3435,11245,3691],{"class":4467},[3435,11247,4530],{"class":4524},[3435,11249,3691],{"class":4467},[3435,11251,11252],{"class":4535},"AddControllers",[3435,11254,4760],{"class":4467},[3435,11256,11257,11259,11262,11264,11266,11268,11271],{"class":3437,"line":3517},[3435,11258,5691],{"class":4677},[3435,11260,11261],{"class":4524}," app",[3435,11263,5300],{"class":4467},[3435,11265,4525],{"class":4524},[3435,11267,3691],{"class":4467},[3435,11269,11270],{"class":4535},"Build",[3435,11272,4760],{"class":4467},[3435,11274,11275,11278,11280,11283],{"class":3437,"line":3523},[3435,11276,11277],{"class":4524},"app",[3435,11279,3691],{"class":4467},[3435,11281,11282],{"class":4535},"MapControllers",[3435,11284,4760],{"class":4467},[3435,11286,11287,11289,11291,11294],{"class":3437,"line":3529},[3435,11288,11277],{"class":4524},[3435,11290,3691],{"class":4467},[3435,11292,11293],{"class":4535},"Run",[3435,11295,4760],{"class":4467},[3426,11297,11300],{"className":4452,"code":11298,"filename":11299,"language":4454,"meta":3431,"style":3431},"[ApiController]\n[Route(\"api\u002F[controller]\")]\npublic class ProductsController(AppDbContext db) : ControllerBase\n{\n    [HttpGet]\n    public async Task\u003CIActionResult> GetAll() =>\n        Ok(await db.Products.ToListAsync());\n\n    [HttpPost]\n    public async Task\u003CIActionResult> Create(Product product)\n    {\n        db.Products.Add(product);\n        await db.SaveChangesAsync();\n        return CreatedAtAction(nameof(GetAll), new { id = product.Id }, product);\n    }\n}\n","Controllers\u002FProductsController.cs",[3414,11301,11302,11313,11328,11348,11352,11362,11382,11405,11409,11418,11443,11447,11465,11477,11520,11524],{"__ignoreMap":3431},[3435,11303,11304,11307,11310],{"class":3437,"line":3438},[3435,11305,11306],{"class":4467},"[",[3435,11308,11309],{"class":4542},"ApiController",[3435,11311,11312],{"class":4467},"]\n",[3435,11314,11315,11317,11320,11322,11325],{"class":3437,"line":3444},[3435,11316,11306],{"class":4467},[3435,11318,11319],{"class":4542},"Route",[3435,11321,4565],{"class":4467},[3435,11323,11324],{"class":4473},"\"api\u002F[controller]\"",[3435,11326,11327],{"class":4467},")]\n",[3435,11329,11330,11332,11334,11337,11339,11341,11343,11345],{"class":3437,"line":3450},[3435,11331,4678],{"class":4677},[3435,11333,4681],{"class":4677},[3435,11335,11336],{"class":4542}," ProductsController",[3435,11338,4565],{"class":4467},[3435,11340,4543],{"class":4542},[3435,11342,4691],{"class":4524},[3435,11344,6816],{"class":4467},[3435,11346,11347],{"class":4542},"ControllerBase\n",[3435,11349,11350],{"class":3437,"line":3456},[3435,11351,4468],{"class":4467},[3435,11353,11354,11357,11360],{"class":3437,"line":3463},[3435,11355,11356],{"class":4467},"    [",[3435,11358,11359],{"class":4542},"HttpGet",[3435,11361,11312],{"class":4467},[3435,11363,11364,11366,11368,11370,11372,11375,11377,11380],{"class":3437,"line":3469},[3435,11365,4715],{"class":4677},[3435,11367,4718],{"class":4677},[3435,11369,4721],{"class":4542},[3435,11371,4539],{"class":4467},[3435,11373,11374],{"class":4542},"IActionResult",[3435,11376,6811],{"class":4467},[3435,11378,11379],{"class":4535},"GetAll",[3435,11381,4740],{"class":4467},[3435,11383,11384,11387,11389,11392,11394,11396,11398,11400,11402],{"class":3437,"line":3475},[3435,11385,11386],{"class":4535},"        Ok",[3435,11388,4565],{"class":4467},[3435,11390,11391],{"class":4677},"await",[3435,11393,4691],{"class":4524},[3435,11395,3691],{"class":4467},[3435,11397,4752],{"class":4524},[3435,11399,3691],{"class":4467},[3435,11401,4757],{"class":4535},[3435,11403,11404],{"class":4467},"());\n",[3435,11406,11407],{"class":3437,"line":3481},[3435,11408,3460],{"emptyLinePlaceholder":3459},[3435,11410,11411,11413,11416],{"class":3437,"line":3487},[3435,11412,11356],{"class":4467},[3435,11414,11415],{"class":4542},"HttpPost",[3435,11417,11312],{"class":4467},[3435,11419,11420,11422,11424,11426,11428,11430,11432,11435,11437,11439,11441],{"class":3437,"line":3493},[3435,11421,4715],{"class":4677},[3435,11423,4718],{"class":4677},[3435,11425,4721],{"class":4542},[3435,11427,4539],{"class":4467},[3435,11429,11374],{"class":4542},[3435,11431,6811],{"class":4467},[3435,11433,11434],{"class":4535},"Create",[3435,11436,4565],{"class":4467},[3435,11438,4731],{"class":4542},[3435,11440,4787],{"class":4524},[3435,11442,4701],{"class":4467},[3435,11444,11445],{"class":3437,"line":3499},[3435,11446,4794],{"class":4467},[3435,11448,11449,11451,11453,11455,11457,11459,11461,11463],{"class":3437,"line":3505},[3435,11450,4799],{"class":4524},[3435,11452,3691],{"class":4467},[3435,11454,4752],{"class":4524},[3435,11456,3691],{"class":4467},[3435,11458,4808],{"class":4535},[3435,11460,4565],{"class":4467},[3435,11462,4813],{"class":4524},[3435,11464,4816],{"class":4467},[3435,11466,11467,11469,11471,11473,11475],{"class":3437,"line":3511},[3435,11468,4745],{"class":4677},[3435,11470,4691],{"class":4524},[3435,11472,3691],{"class":4467},[3435,11474,4827],{"class":4535},[3435,11476,4760],{"class":4467},[3435,11478,11479,11482,11485,11487,11490,11492,11494,11497,11499,11501,11504,11506,11508,11510,11513,11516,11518],{"class":3437,"line":3517},[3435,11480,11481],{"class":5657},"        return",[3435,11483,11484],{"class":4535}," CreatedAtAction",[3435,11486,4565],{"class":4467},[3435,11488,11489],{"class":4677},"nameof",[3435,11491,4565],{"class":4467},[3435,11493,11379],{"class":4524},[3435,11495,11496],{"class":4467},"), ",[3435,11498,5699],{"class":4677},[3435,11500,6685],{"class":4467},[3435,11502,11503],{"class":4524},"id",[3435,11505,5300],{"class":4467},[3435,11507,4813],{"class":4524},[3435,11509,3691],{"class":4467},[3435,11511,11512],{"class":4524},"Id",[3435,11514,11515],{"class":4467}," }, ",[3435,11517,4813],{"class":4524},[3435,11519,4816],{"class":4467},[3435,11521,11522],{"class":3437,"line":3523},[3435,11523,3490],{"class":4467},[3435,11525,11526],{"class":3437,"line":3529},[3435,11527,3532],{"class":4467},[3426,11529,11532],{"className":7213,"code":11530,"filename":11531,"language":7215,"meta":3431,"style":3431},"{\n    \"ConnectionStrings\": {\n        \"Default\": \"Host=localhost;Database=myapp_dev;Username=postgres;Password=localpassword\"\n    }\n}\n","appsettings.Development.json",[3414,11533,11534,11538,11544,11552,11556],{"__ignoreMap":3431},[3435,11535,11536],{"class":3437,"line":3438},[3435,11537,4468],{"class":4467},[3435,11539,11540,11542],{"class":3437,"line":3444},[3435,11541,7232],{"class":7231},[3435,11543,4477],{"class":4467},[3435,11545,11546,11548,11550],{"class":3437,"line":3450},[3435,11547,7239],{"class":7231},[3435,11549,4485],{"class":4467},[3435,11551,7244],{"class":4473},[3435,11553,11554],{"class":3437,"line":3456},[3435,11555,3490],{"class":4467},[3435,11557,11558],{"class":3437,"line":3463},[3435,11559,3532],{"class":4467},[3679,11561],{},[3700,11563,11565],{"id":11564},"крок-4-ef-core-міграція-до-rds","Крок 4: EF Core міграція до RDS",[3426,11567,11569],{"className":5213,"code":11568,"language":5215,"meta":3431,"style":3431},"# Генерувати міграцію\ndotnet ef migrations add InitialCreate\n\n# Застосувати до RDS (з локальної машини)\n# Пояснення: Утиліта `dotnet ef` під час виконання команди `database update` запускає проєкт у фоновому режимі\n# для зчитування конфігурації. Встановлюючи змінну середовища `DB_CONNECTION_STRING`, ми перевизначаємо\n# локальний рядок підключення на хмарний, який зчитується у файлі `Program.cs`.\nexport DB_CONNECTION_STRING=\"Host=$RDS_ENDPOINT;Database=myapp;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require\"\ndotnet ef database update\n",[3414,11570,11571,11576,11588,11592,11597,11602,11607,11612,11630],{"__ignoreMap":3431},[3435,11572,11573],{"class":3437,"line":3438},[3435,11574,11575],{"class":4461},"# Генерувати міграцію\n",[3435,11577,11578,11580,11582,11584,11586],{"class":3437,"line":3444},[3435,11579,6602],{"class":4535},[3435,11581,7273],{"class":4473},[3435,11583,7276],{"class":4473},[3435,11585,6605],{"class":4473},[3435,11587,7281],{"class":4473},[3435,11589,11590],{"class":3437,"line":3450},[3435,11591,3460],{"emptyLinePlaceholder":3459},[3435,11593,11594],{"class":3437,"line":3456},[3435,11595,11596],{"class":4461},"# Застосувати до RDS (з локальної машини)\n",[3435,11598,11599],{"class":3437,"line":3463},[3435,11600,11601],{"class":4461},"# Пояснення: Утиліта `dotnet ef` під час виконання команди `database update` запускає проєкт у фоновому режимі\n",[3435,11603,11604],{"class":3437,"line":3469},[3435,11605,11606],{"class":4461},"# для зчитування конфігурації. Встановлюючи змінну середовища `DB_CONNECTION_STRING`, ми перевизначаємо\n",[3435,11608,11609],{"class":3437,"line":3475},[3435,11610,11611],{"class":4461},"# локальний рядок підключення на хмарний, який зчитується у файлі `Program.cs`.\n",[3435,11613,11614,11617,11620,11622,11625,11627],{"class":3437,"line":3481},[3435,11615,11616],{"class":4677},"export",[3435,11618,11619],{"class":4524}," DB_CONNECTION_STRING",[3435,11621,7319],{"class":4467},[3435,11623,11624],{"class":4473},"\"Host=",[3435,11626,8298],{"class":4524},[3435,11628,11629],{"class":4473},";Database=myapp;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require\"\n",[3435,11631,11632,11634,11636,11638],{"class":3437,"line":3487},[3435,11633,6602],{"class":4535},[3435,11635,7273],{"class":4473},[3435,11637,7299],{"class":4473},[3435,11639,7302],{"class":4473},[11641,11642,11644,11656,11660,11664,11668,11672,11676,11680],"terminal-preview",{"title":11643},"Виконання міграції EF Core",[11645,11646,11648,3783,11653],"div",{"className":11647},[3437],[3435,11649,11652],{"className":11650},[11651],"opacity-40","$",[3359,11654,11655],{},"dotnet ef database update",[11645,11657,11659],{"className":11658},[3437],"Build started...",[11645,11661,11663],{"className":11662},[3437],"Build succeeded.",[11645,11665,11667],{"className":11666},[3437],"info: Microsoft.EntityFrameworkCore.Database.Command[20101]",[11645,11669,11671],{"className":11670},[3437],"      Executed DbCommand (12ms) [Parameters=[], CommandType=Text]",[11645,11673,11675],{"className":11674},[3437],"      CREATE TABLE \"__EFMigrationsHistory\" ...",[11645,11677,11679],{"className":11678},[3437],"info: Microsoft.EntityFrameworkCore.Migrations[20402]",[11645,11681,11683,11684],{"className":11682},[3437],"      ",[3435,11685,11688],{"className":11686},[11687],"text-green-400","Done. Applied 1 migration(s).",[3679,11690],{},[3700,11692,11694],{"id":11693},"крок-5-деплой-застосунку-на-ec2","Крок 5: Деплой застосунку на EC2",[11696,11697,11698,11702,11746,11750,11780,11784],"steps",{},[3700,11699,11701],{"id":11700},"публікація-та-упаковка","Публікація та упаковка",[3426,11703,11705],{"className":5213,"code":11704,"language":5215,"meta":3431,"style":3431},"dotnet publish -c Release -o .\u002Fpublish\ntar -czf mydotnetapp.tar.gz -C .\u002Fpublish .\n",[3414,11706,11707,11726],{"__ignoreMap":3431},[3435,11708,11709,11711,11714,11717,11720,11723],{"class":3437,"line":3438},[3435,11710,6602],{"class":4535},[3435,11712,11713],{"class":4473}," publish",[3435,11715,11716],{"class":4677}," -c",[3435,11718,11719],{"class":4473}," Release",[3435,11721,11722],{"class":4677}," -o",[3435,11724,11725],{"class":4473}," .\u002Fpublish\n",[3435,11727,11728,11731,11734,11737,11740,11743],{"class":3437,"line":3444},[3435,11729,11730],{"class":4535},"tar",[3435,11732,11733],{"class":4677}," -czf",[3435,11735,11736],{"class":4473}," mydotnetapp.tar.gz",[3435,11738,11739],{"class":4677}," -C",[3435,11741,11742],{"class":4473}," .\u002Fpublish",[3435,11744,11745],{"class":4473}," .\n",[3700,11747,11749],{"id":11748},"передача-на-ec2-через-scp","Передача на EC2 через SCP",[3426,11751,11753],{"className":5213,"code":11752,"language":5215,"meta":3431,"style":3431},"scp -i ~\u002F.ssh\u002Fdotnet-api-key.pem \\\n    mydotnetapp.tar.gz ubuntu@$EC2_IP:\u002Fhome\u002Fubuntu\u002F\n",[3414,11754,11755,11767],{"__ignoreMap":3431},[3435,11756,11757,11760,11762,11765],{"class":3437,"line":3438},[3435,11758,11759],{"class":4535},"scp",[3435,11761,9877],{"class":4677},[3435,11763,11764],{"class":4473}," ~\u002F.ssh\u002Fdotnet-api-key.pem",[3435,11766,5237],{"class":5236},[3435,11768,11769,11772,11775,11777],{"class":3437,"line":3444},[3435,11770,11771],{"class":4473},"    mydotnetapp.tar.gz",[3435,11773,11774],{"class":4473}," ubuntu@",[3435,11776,10363],{"class":4524},[3435,11778,11779],{"class":4473},":\u002Fhome\u002Fubuntu\u002F\n",[3700,11781,11783],{"id":11782},"розгортання-та-запуск-через-systemd","Розгортання та запуск через systemd",[3426,11785,11787],{"className":5213,"code":11786,"language":5215,"meta":3431,"style":3431},"# SSH на EC2\nssh -i ~\u002F.ssh\u002Fdotnet-api-key.pem ubuntu@$EC2_IP\n\n# Розпакувати\nmkdir -p \u002Fopt\u002Fmydotnetapp\ntar -xzf \u002Fhome\u002Fubuntu\u002Fmydotnetapp.tar.gz -C \u002Fopt\u002Fmydotnetapp\n\n# systemd service\n# Пояснення: Для забезпечення безперебійної роботи застосунку в Linux-середовищі використовується системний\n# менеджер ініціалізації `systemd`. На відміну від запуску утилітами на кшталт `nohup` або `screen`, `systemd`\n# забезпечує автоматичний перезапуск процесу при збоях, агрегує потік виведення (stdout\u002Fstderr) в системний\n# журнал `journald`, дозволяє гнучко налаштовувати змінні середовища та запускати процес від імені ізольованого користувача.\nsudo tee \u002Fetc\u002Fsystemd\u002Fsystem\u002Fmydotnetapp.service > \u002Fdev\u002Fnull \u003C\u003C 'SERVICE'\n[Unit]\nDescription=My .NET API Application\nAfter=network.target\n\n[Service]\nWorkingDirectory=\u002Fopt\u002Fmydotnetapp\nExecStart=\u002Fusr\u002Fbin\u002Fdotnet \u002Fopt\u002Fmydotnetapp\u002FMyDotnetApp.dll\nRestart=always\nRestartSec=10\nEnvironment=ASPNETCORE_ENVIRONMENT=Production\nEnvironment=ASPNETCORE_URLS=http:\u002F\u002F+:5000\nEnvironment=DB_CONNECTION_STRING=Host=REPLACE_RDS_ENDPOINT;Database=myapp;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require\nUser=ubuntu\n\n[Install]\nWantedBy=multi-user.target\nSERVICE\n\nsudo systemctl daemon-reload\nsudo systemctl enable mydotnetapp\nsudo systemctl start mydotnetapp\nsudo systemctl status mydotnetapp\n",[3414,11788,11789,11794,11808,11812,11817,11825,11839,11843,11848,11853,11858,11863,11868,11889,11894,11899,11904,11908,11913,11918,11923,11928,11933,11938,11943,11948,11953,11957,11962,11967,11972,11976,11986,11998,12009],{"__ignoreMap":3431},[3435,11790,11791],{"class":3437,"line":3438},[3435,11792,11793],{"class":4461},"# SSH на EC2\n",[3435,11795,11796,11799,11801,11803,11805],{"class":3437,"line":3444},[3435,11797,11798],{"class":4535},"ssh",[3435,11800,9877],{"class":4677},[3435,11802,11764],{"class":4473},[3435,11804,11774],{"class":4473},[3435,11806,11807],{"class":4524},"$EC2_IP\n",[3435,11809,11810],{"class":3437,"line":3450},[3435,11811,3460],{"emptyLinePlaceholder":3459},[3435,11813,11814],{"class":3437,"line":3456},[3435,11815,11816],{"class":4461},"# Розпакувати\n",[3435,11818,11819,11821,11823],{"class":3437,"line":3463},[3435,11820,9908],{"class":4535},[3435,11822,9911],{"class":4677},[3435,11824,9926],{"class":4473},[3435,11826,11827,11829,11832,11835,11837],{"class":3437,"line":3469},[3435,11828,11730],{"class":4535},[3435,11830,11831],{"class":4677}," -xzf",[3435,11833,11834],{"class":4473}," \u002Fhome\u002Fubuntu\u002Fmydotnetapp.tar.gz",[3435,11836,11739],{"class":4677},[3435,11838,9926],{"class":4473},[3435,11840,11841],{"class":3437,"line":3475},[3435,11842,3460],{"emptyLinePlaceholder":3459},[3435,11844,11845],{"class":3437,"line":3481},[3435,11846,11847],{"class":4461},"# systemd service\n",[3435,11849,11850],{"class":3437,"line":3487},[3435,11851,11852],{"class":4461},"# Пояснення: Для забезпечення безперебійної роботи застосунку в Linux-середовищі використовується системний\n",[3435,11854,11855],{"class":3437,"line":3493},[3435,11856,11857],{"class":4461},"# менеджер ініціалізації `systemd`. На відміну від запуску утилітами на кшталт `nohup` або `screen`, `systemd`\n",[3435,11859,11860],{"class":3437,"line":3499},[3435,11861,11862],{"class":4461},"# забезпечує автоматичний перезапуск процесу при збоях, агрегує потік виведення (stdout\u002Fstderr) в системний\n",[3435,11864,11865],{"class":3437,"line":3505},[3435,11866,11867],{"class":4461},"# журнал `journald`, дозволяє гнучко налаштовувати змінні середовища та запускати процес від імені ізольованого користувача.\n",[3435,11869,11870,11873,11876,11879,11881,11884,11886],{"class":3437,"line":3511},[3435,11871,11872],{"class":4535},"sudo",[3435,11874,11875],{"class":4473}," tee",[3435,11877,11878],{"class":4473}," \u002Fetc\u002Fsystemd\u002Fsystem\u002Fmydotnetapp.service",[3435,11880,8888],{"class":4467},[3435,11882,11883],{"class":4473},"\u002Fdev\u002Fnull",[3435,11885,10097],{"class":4467},[3435,11887,11888],{"class":4467},"'SERVICE'\n",[3435,11890,11891],{"class":3437,"line":3517},[3435,11892,11893],{"class":4473},"[Unit]\n",[3435,11895,11896],{"class":3437,"line":3523},[3435,11897,11898],{"class":4473},"Description=My .NET API Application\n",[3435,11900,11901],{"class":3437,"line":3529},[3435,11902,11903],{"class":4473},"After=network.target\n",[3435,11905,11906],{"class":3437,"line":3535},[3435,11907,3460],{"emptyLinePlaceholder":3459},[3435,11909,11910],{"class":3437,"line":3540},[3435,11911,11912],{"class":4473},"[Service]\n",[3435,11914,11915],{"class":3437,"line":3546},[3435,11916,11917],{"class":4473},"WorkingDirectory=\u002Fopt\u002Fmydotnetapp\n",[3435,11919,11920],{"class":3437,"line":3552},[3435,11921,11922],{"class":4473},"ExecStart=\u002Fusr\u002Fbin\u002Fdotnet \u002Fopt\u002Fmydotnetapp\u002FMyDotnetApp.dll\n",[3435,11924,11925],{"class":3437,"line":3558},[3435,11926,11927],{"class":4473},"Restart=always\n",[3435,11929,11930],{"class":3437,"line":3563},[3435,11931,11932],{"class":4473},"RestartSec=10\n",[3435,11934,11935],{"class":3437,"line":3569},[3435,11936,11937],{"class":4473},"Environment=ASPNETCORE_ENVIRONMENT=Production\n",[3435,11939,11940],{"class":3437,"line":3575},[3435,11941,11942],{"class":4473},"Environment=ASPNETCORE_URLS=http:\u002F\u002F+:5000\n",[3435,11944,11945],{"class":3437,"line":3580},[3435,11946,11947],{"class":4473},"Environment=DB_CONNECTION_STRING=Host=REPLACE_RDS_ENDPOINT;Database=myapp;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require\n",[3435,11949,11950],{"class":3437,"line":3586},[3435,11951,11952],{"class":4473},"User=ubuntu\n",[3435,11954,11955],{"class":3437,"line":3592},[3435,11956,3460],{"emptyLinePlaceholder":3459},[3435,11958,11959],{"class":3437,"line":3597},[3435,11960,11961],{"class":4473},"[Install]\n",[3435,11963,11964],{"class":3437,"line":3603},[3435,11965,11966],{"class":4473},"WantedBy=multi-user.target\n",[3435,11968,11969],{"class":3437,"line":3609},[3435,11970,11971],{"class":4467},"SERVICE\n",[3435,11973,11974],{"class":3437,"line":3615},[3435,11975,3460],{"emptyLinePlaceholder":3459},[3435,11977,11978,11980,11983],{"class":3437,"line":3621},[3435,11979,11872],{"class":4535},[3435,11981,11982],{"class":4473}," systemctl",[3435,11984,11985],{"class":4473}," daemon-reload\n",[3435,11987,11988,11990,11992,11995],{"class":3437,"line":3627},[3435,11989,11872],{"class":4535},[3435,11991,11982],{"class":4473},[3435,11993,11994],{"class":4473}," enable",[3435,11996,11997],{"class":4473}," mydotnetapp\n",[3435,11999,12000,12002,12004,12007],{"class":3437,"line":3632},[3435,12001,11872],{"class":4535},[3435,12003,11982],{"class":4473},[3435,12005,12006],{"class":4473}," start",[3435,12008,11997],{"class":4473},[3435,12010,12011,12013,12015,12018],{"class":3437,"line":3637},[3435,12012,11872],{"class":4535},[3435,12014,11982],{"class":4473},[3435,12016,12017],{"class":4473}," status",[3435,12019,11997],{"class":4473},[11641,12021,12024,12033,12037,12041,12051,12055,12059,12063,12071,12075],{"title":12022,":cursor":12023},"systemd status","true",[11645,12025,12027,3783,12030],{"className":12026},[3437],[3435,12028,11652],{"className":12029},[11651],[3359,12031,12032],{},"sudo systemctl status mydotnetapp",[11645,12034,12036],{"className":12035},[3437],"● mydotnetapp.service - My .NET API Application",[11645,12038,12040],{"className":12039},[3437],"     Loaded: loaded (\u002Fetc\u002Fsystemd\u002Fsystem\u002Fmydotnetapp.service; enabled)",[11645,12042,12044,12045,12050],{"className":12043},[3437],"     ",[3435,12046,12049],{"className":12047},[11687,12048],"font-bold","Active: active (running)"," since Mon 2025-01-15 10:30:00 UTC",[11645,12052,12054],{"className":12053},[3437],"   Main PID: 1234 (dotnet)",[11645,12056,12058],{"className":12057},[3437],"     Memory: 58.2M",[11645,12060,12062],{"className":12061},[3437],"info: Microsoft.Hosting.Lifetime[14]",[11645,12064,11683,12066],{"className":12065},[3437],[3435,12067,12070],{"className":12068},[12069],"text-blue-400","Now listening on: http:\u002F\u002F[::]:5000",[11645,12072,12074],{"className":12073},[3437],"info: Microsoft.Hosting.Lifetime[0]",[11645,12076,12078],{"className":12077},[3437],"      Application started.",[3679,12080],{},[3700,12082,12084],{"id":12083},"крок-6-перевірка-роботи-api","Крок 6: Перевірка роботи API",[3426,12086,12088],{"className":5213,"code":12087,"language":5215,"meta":3431,"style":3431},"# POST: створити продукт\ncurl -X POST http:\u002F\u002F$EC2_IP:5000\u002Fapi\u002Fproducts \\\n    -H \"Content-Type: application\u002Fjson\" \\\n    -d '{\"name\": \"Test Product\", \"price\": 99.99}'\n\n# GET: отримати список\ncurl http:\u002F\u002F$EC2_IP:5000\u002Fapi\u002Fproducts\n",[3414,12089,12090,12095,12115,12125,12133,12137,12142],{"__ignoreMap":3431},[3435,12091,12092],{"class":3437,"line":3438},[3435,12093,12094],{"class":4461},"# POST: створити продукт\n",[3435,12096,12097,12099,12102,12105,12108,12110,12113],{"class":3437,"line":3444},[3435,12098,9131],{"class":4535},[3435,12100,12101],{"class":4677}," -X",[3435,12103,12104],{"class":4473}," POST",[3435,12106,12107],{"class":4473}," http:\u002F\u002F",[3435,12109,10363],{"class":4524},[3435,12111,12112],{"class":4473},":5000\u002Fapi\u002Fproducts",[3435,12114,5237],{"class":5236},[3435,12116,12117,12120,12123],{"class":3437,"line":3450},[3435,12118,12119],{"class":4677},"    -H",[3435,12121,12122],{"class":4473}," \"Content-Type: application\u002Fjson\"",[3435,12124,5237],{"class":5236},[3435,12126,12127,12130],{"class":3437,"line":3456},[3435,12128,12129],{"class":4677},"    -d",[3435,12131,12132],{"class":4473}," '{\"name\": \"Test Product\", \"price\": 99.99}'\n",[3435,12134,12135],{"class":3437,"line":3463},[3435,12136,3460],{"emptyLinePlaceholder":3459},[3435,12138,12139],{"class":3437,"line":3469},[3435,12140,12141],{"class":4461},"# GET: отримати список\n",[3435,12143,12144,12146,12148,12150],{"class":3437,"line":3475},[3435,12145,9131],{"class":4535},[3435,12147,12107],{"class":4473},[3435,12149,10363],{"class":4524},[3435,12151,12152],{"class":4473},":5000\u002Fapi\u002Fproducts\n",[11641,12154,12156,12165,12172,12175,12184],{"title":12155,":cursor":12023},"Перевірка API",[11645,12157,12159,3783,12162],{"className":12158},[3437],[3435,12160,11652],{"className":12161},[11651],[3359,12163,12164],{},"curl -X POST http:\u002F\u002F54.12.34.56:5000\u002Fapi\u002Fproducts -H \"Content-Type: application\u002Fjson\" -d '{\"name\":\"Test\",\"price\":9.99}'",[11645,12166,12168],{"className":12167},[3437],[3435,12169,12171],{"className":12170},[11687,12048],"{\"id\":1,\"name\":\"Test\",\"price\":9.99,\"createdAt\":\"2025-01-15T10:30:00Z\"}",[11645,12173],{"className":12174},[3437],[11645,12176,12178,3783,12181],{"className":12177},[3437],[3435,12179,11652],{"className":12180},[11651],[3359,12182,12183],{},"curl http:\u002F\u002F54.12.34.56:5000\u002Fapi\u002Fproducts",[11645,12185,12187],{"className":12186},[3437],[3435,12188,12190],{"className":12189},[12069],"[{\"id\":1,\"name\":\"Test\",\"price\":9.99,\"createdAt\":\"2025-01-15T10:30:00Z\"}]",[3679,12192],{},[3700,12194,12196],{"id":12195},"крок-7-обовязково-очищення-ресурсів","Крок 7 (ОБОВ'ЯЗКОВО): Очищення ресурсів",[4248,12198,12199,12200,12202,12203,12206,12207,12210,12211,12214,12215],{},"RDS ",[3414,12201,3760],{}," поза Free Tier ≈ ",[3359,12204,12205],{},"$13\u002Fміс",", EC2 ",[3414,12208,12209],{},"t2.micro"," ≈ ",[3359,12212,12213],{},"$8\u002Fміс",". Free Tier: 750 год\u002Fмісяць на кожен сервіс — протягом першого року. ",[3359,12216,12217],{},"Видаліть обидва ресурси після лабораторної роботи.",[5205,12219,12220,12260,12497],{},[5208,12221,12222,12234,12249],{"label":7569},[3353,12223,12224,12227,12228,12230,12231],{},[3359,12225,12226],{},"Видалення EC2:"," EC2 → Instances → ",[3414,12229,9794],{}," → Instance state → ",[3359,12232,12233],{},"Terminate instance",[3353,12235,12236,12239,12240,12242,12243,12245,12246],{},[3359,12237,12238],{},"Видалення RDS:"," RDS → Databases → ",[3414,12241,7615],{}," → Actions → ",[3359,12244,9484],{}," → введіть ",[3414,12247,12248],{},"delete me",[3353,12250,12251,12254,12255,3417,12257,12259],{},[3359,12252,12253],{},"Cleanup (після видалення):"," Security Groups (",[3414,12256,5397],{},[3414,12258,7672],{},"), DB Subnet Group, Key Pair",[5208,12261,12262],{"label":5210},[3426,12263,12265],{"className":5213,"code":12264,"language":5215,"meta":3431,"style":3431},"# 1. Видалити EC2\naws ec2 terminate-instances --instance-ids $INSTANCE_ID --region eu-central-1\naws ec2 wait instance-terminated --instance-ids $INSTANCE_ID --region eu-central-1\necho \"EC2 terminated\"\n\n# 2. Видалити RDS\naws rds delete-db-instance \\\n    --db-instance-identifier my-dotnet-app-db \\\n    --final-db-snapshot-identifier my-dotnet-app-db-final-$(date +%Y%m%d) \\\n    --region eu-central-1\naws rds wait db-instance-deleted \\\n    --db-instance-identifier my-dotnet-app-db --region eu-central-1\necho \"RDS deleted\"\n\n# 3. Залежні ресурси\naws rds delete-db-subnet-group \\\n    --db-subnet-group-name my-dotnet-app-subnet-group --region eu-central-1\naws ec2 delete-security-group --group-id $SG_EC2 --region eu-central-1\naws ec2 delete-security-group --group-id $SG_RDS --region eu-central-1\naws ec2 delete-key-pair --key-name dotnet-api-key --region eu-central-1\nrm -f ~\u002F.ssh\u002Fdotnet-api-key.pem\necho \"Cleanup done\"\n",[3414,12266,12267,12272,12290,12309,12316,12320,12325,12336,12344,12362,12368,12381,12391,12398,12402,12407,12418,12428,12446,12462,12480,12490],{"__ignoreMap":3431},[3435,12268,12269],{"class":3437,"line":3438},[3435,12270,12271],{"class":4461},"# 1. Видалити EC2\n",[3435,12273,12274,12276,12278,12281,12284,12286,12288],{"class":3437,"line":3444},[3435,12275,5227],{"class":4535},[3435,12277,7716],{"class":4473},[3435,12279,12280],{"class":4473}," terminate-instances",[3435,12282,12283],{"class":4677}," --instance-ids",[3435,12285,10300],{"class":4524},[3435,12287,7748],{"class":4677},[3435,12289,5278],{"class":4473},[3435,12291,12292,12294,12296,12298,12301,12303,12305,12307],{"class":3437,"line":3450},[3435,12293,5227],{"class":4535},[3435,12295,7716],{"class":4473},[3435,12297,8210],{"class":4473},[3435,12299,12300],{"class":4473}," instance-terminated",[3435,12302,12283],{"class":4677},[3435,12304,10300],{"class":4524},[3435,12306,7748],{"class":4677},[3435,12308,5278],{"class":4473},[3435,12310,12311,12313],{"class":3437,"line":3456},[3435,12312,7758],{"class":4535},[3435,12314,12315],{"class":4473}," \"EC2 terminated\"\n",[3435,12317,12318],{"class":3437,"line":3463},[3435,12319,3460],{"emptyLinePlaceholder":3459},[3435,12321,12322],{"class":3437,"line":3469},[3435,12323,12324],{"class":4461},"# 2. Видалити RDS\n",[3435,12326,12327,12329,12331,12334],{"class":3437,"line":3475},[3435,12328,5227],{"class":4535},[3435,12330,5230],{"class":4473},[3435,12332,12333],{"class":4473}," delete-db-instance",[3435,12335,5237],{"class":5236},[3435,12337,12338,12340,12342],{"class":3437,"line":3481},[3435,12339,5242],{"class":4677},[3435,12341,8076],{"class":4473},[3435,12343,5237],{"class":5236},[3435,12345,12346,12349,12352,12354,12356,12358,12360],{"class":3437,"line":3487},[3435,12347,12348],{"class":4677},"    --final-db-snapshot-identifier",[3435,12350,12351],{"class":4473}," my-dotnet-app-db-final-",[3435,12353,5258],{"class":4467},[3435,12355,5261],{"class":4535},[3435,12357,5264],{"class":4473},[3435,12359,5267],{"class":4467},[3435,12361,5270],{"class":5236},[3435,12363,12364,12366],{"class":3437,"line":3493},[3435,12365,5275],{"class":4677},[3435,12367,5278],{"class":4473},[3435,12369,12370,12372,12374,12376,12379],{"class":3437,"line":3499},[3435,12371,5227],{"class":4535},[3435,12373,5230],{"class":4473},[3435,12375,8210],{"class":4473},[3435,12377,12378],{"class":4473}," db-instance-deleted",[3435,12380,5237],{"class":5236},[3435,12382,12383,12385,12387,12389],{"class":3437,"line":3505},[3435,12384,5242],{"class":4677},[3435,12386,8076],{"class":4473},[3435,12388,7748],{"class":4677},[3435,12390,5278],{"class":4473},[3435,12392,12393,12395],{"class":3437,"line":3511},[3435,12394,7758],{"class":4535},[3435,12396,12397],{"class":4473}," \"RDS deleted\"\n",[3435,12399,12400],{"class":3437,"line":3517},[3435,12401,3460],{"emptyLinePlaceholder":3459},[3435,12403,12404],{"class":3437,"line":3523},[3435,12405,12406],{"class":4461},"# 3. Залежні ресурси\n",[3435,12408,12409,12411,12413,12416],{"class":3437,"line":3529},[3435,12410,5227],{"class":4535},[3435,12412,5230],{"class":4473},[3435,12414,12415],{"class":4473}," delete-db-subnet-group",[3435,12417,5237],{"class":5236},[3435,12419,12420,12422,12424,12426],{"class":3437,"line":3535},[3435,12421,7858],{"class":4677},[3435,12423,7861],{"class":4473},[3435,12425,7748],{"class":4677},[3435,12427,5278],{"class":4473},[3435,12429,12430,12432,12434,12437,12440,12442,12444],{"class":3437,"line":3540},[3435,12431,5227],{"class":4535},[3435,12433,7716],{"class":4473},[3435,12435,12436],{"class":4473}," delete-security-group",[3435,12438,12439],{"class":4677}," --group-id",[3435,12441,9160],{"class":4524},[3435,12443,7748],{"class":4677},[3435,12445,5278],{"class":4473},[3435,12447,12448,12450,12452,12454,12456,12458,12460],{"class":3437,"line":3546},[3435,12449,5227],{"class":4535},[3435,12451,7716],{"class":4473},[3435,12453,12436],{"class":4473},[3435,12455,12439],{"class":4677},[3435,12457,8023],{"class":4524},[3435,12459,7748],{"class":4677},[3435,12461,5278],{"class":4473},[3435,12463,12464,12466,12468,12471,12474,12476,12478],{"class":3437,"line":3552},[3435,12465,5227],{"class":4535},[3435,12467,7716],{"class":4473},[3435,12469,12470],{"class":4473}," delete-key-pair",[3435,12472,12473],{"class":4677}," --key-name",[3435,12475,8860],{"class":4473},[3435,12477,7748],{"class":4677},[3435,12479,5278],{"class":4473},[3435,12481,12482,12485,12488],{"class":3437,"line":3558},[3435,12483,12484],{"class":4535},"rm",[3435,12486,12487],{"class":4677}," -f",[3435,12489,8911],{"class":4473},[3435,12491,12492,12494],{"class":3437,"line":3563},[3435,12493,7758],{"class":4535},[3435,12495,12496],{"class":4473}," \"Cleanup done\"\n",[5208,12498,12499],{"label":5281},[3426,12500,12502],{"className":5284,"code":12501,"language":5286,"meta":3431,"style":3431},"# 1. Видалити EC2\naws ec2 terminate-instances --instance-ids $INSTANCE_ID --region eu-central-1\naws ec2 wait instance-terminated --instance-ids $INSTANCE_ID --region eu-central-1\nWrite-Host \"EC2 terminated\"\n\n# 2. Видалити RDS\n$snap = \"my-dotnet-app-db-final-$(Get-Date -Format 'yyyyMMdd')\"\naws rds delete-db-instance `\n    --db-instance-identifier my-dotnet-app-db `\n    --final-db-snapshot-identifier $snap `\n    --region eu-central-1\naws rds wait db-instance-deleted `\n    --db-instance-identifier my-dotnet-app-db --region eu-central-1\nWrite-Host \"RDS deleted\"\n\n# 3. Залежні ресурси\naws rds delete-db-subnet-group `\n    --db-subnet-group-name my-dotnet-app-subnet-group --region eu-central-1\naws ec2 delete-security-group --group-id $SG_EC2 --region eu-central-1\naws ec2 delete-security-group --group-id $SG_RDS --region eu-central-1\naws ec2 delete-key-pair --key-name dotnet-api-key --region eu-central-1\nRemove-Item \"$env:USERPROFILE\\.ssh\\dotnet-api-key.pem\" -ErrorAction SilentlyContinue\nWrite-Host \"Cleanup done\"\n",[3414,12503,12504,12508,12519,12530,12536,12540,12544,12572,12577,12581,12590,12596,12601,12607,12613,12617,12621,12626,12637,12650,12662,12669,12685],{"__ignoreMap":3431},[3435,12505,12506],{"class":3437,"line":3438},[3435,12507,12271],{"class":4461},[3435,12509,12510,12513,12515,12517],{"class":3437,"line":3444},[3435,12511,12512],{"class":4467},"aws ec2 terminate-instances --instance-ids ",[3435,12514,10273],{"class":4524},[3435,12516,8596],{"class":4467},[3435,12518,5338],{"class":5337},[3435,12520,12521,12524,12526,12528],{"class":3437,"line":3450},[3435,12522,12523],{"class":4467},"aws ec2 wait instance-terminated --instance-ids ",[3435,12525,10273],{"class":4524},[3435,12527,8596],{"class":4467},[3435,12529,5338],{"class":5337},[3435,12531,12532,12534],{"class":3437,"line":3456},[3435,12533,8346],{"class":4535},[3435,12535,12315],{"class":4473},[3435,12537,12538],{"class":3437,"line":3463},[3435,12539,3460],{"emptyLinePlaceholder":3459},[3435,12541,12542],{"class":3437,"line":3469},[3435,12543,12324],{"class":4461},[3435,12545,12546,12549,12551,12554,12556,12558,12561,12564,12567,12570],{"class":3437,"line":3475},[3435,12547,12548],{"class":4524},"$snap",[3435,12550,5300],{"class":4467},[3435,12552,12553],{"class":4473},"\"my-dotnet-app-db-final-",[3435,12555,5258],{"class":4677},[3435,12557,5303],{"class":4535},[3435,12559,12560],{"class":4467}," -",[3435,12562,12563],{"class":5817},"Format ",[3435,12565,12566],{"class":4473},"'yyyyMMdd'",[3435,12568,12569],{"class":4677},")",[3435,12571,7767],{"class":4473},[3435,12573,12574],{"class":3437,"line":3481},[3435,12575,12576],{"class":4467},"aws rds delete-db-instance `\n",[3435,12578,12579],{"class":3437,"line":3487},[3435,12580,8616],{"class":4467},[3435,12582,12583,12586,12588],{"class":3437,"line":3493},[3435,12584,12585],{"class":4467},"    --final-db-snapshot-identifier ",[3435,12587,12548],{"class":4524},[3435,12589,5329],{"class":4467},[3435,12591,12592,12594],{"class":3437,"line":3499},[3435,12593,5334],{"class":4467},[3435,12595,5338],{"class":5337},[3435,12597,12598],{"class":3437,"line":3505},[3435,12599,12600],{"class":4467},"aws rds wait db-instance-deleted `\n",[3435,12602,12603,12605],{"class":3437,"line":3511},[3435,12604,8730],{"class":4467},[3435,12606,5338],{"class":5337},[3435,12608,12609,12611],{"class":3437,"line":3517},[3435,12610,8346],{"class":4535},[3435,12612,12397],{"class":4473},[3435,12614,12615],{"class":3437,"line":3523},[3435,12616,3460],{"emptyLinePlaceholder":3459},[3435,12618,12619],{"class":3437,"line":3529},[3435,12620,12406],{"class":4461},[3435,12622,12623],{"class":3437,"line":3535},[3435,12624,12625],{"class":4467},"aws rds delete-db-subnet-group `\n",[3435,12627,12628,12630,12632,12635],{"class":3437,"line":3540},[3435,12629,8451],{"class":4467},[3435,12631,8454],{"class":4535},[3435,12633,12634],{"class":4467}," my-dotnet-app-subnet-group --region eu-central-",[3435,12636,5338],{"class":5337},[3435,12638,12639,12642,12644,12646,12648],{"class":3437,"line":3546},[3435,12640,12641],{"class":4467},"aws ec2 delete-security-group --",[3435,12643,8565],{"class":4535},[3435,12645,9160],{"class":4524},[3435,12647,8596],{"class":4467},[3435,12649,5338],{"class":5337},[3435,12651,12652,12654,12656,12658,12660],{"class":3437,"line":3552},[3435,12653,12641],{"class":4467},[3435,12655,8565],{"class":4535},[3435,12657,8023],{"class":4524},[3435,12659,8596],{"class":4467},[3435,12661,5338],{"class":5337},[3435,12663,12664,12667],{"class":3437,"line":3558},[3435,12665,12666],{"class":4467},"aws ec2 delete-key-pair --key-name dotnet-api-key --region eu-central-",[3435,12668,5338],{"class":5337},[3435,12670,12671,12674,12677,12679,12682],{"class":3437,"line":3563},[3435,12672,12673],{"class":4535},"Remove-Item",[3435,12675,12676],{"class":4473}," \"",[3435,12678,8962],{"class":4524},[3435,12680,12681],{"class":4473},"\\.ssh\\dotnet-api-key.pem\"",[3435,12683,12684],{"class":4467}," -ErrorAction SilentlyContinue\n",[3435,12686,12687,12689],{"class":3437,"line":3569},[3435,12688,8346],{"class":4535},[3435,12690,12496],{"class":4473},[3348,12692,12694],{"id":12693},"поглиблені-практичні-сценарії-використання-amazon-rds-у-net","Поглиблені практичні сценарії використання Amazon RDS у .NET",[3679,12696],{},[3700,12698,12700],{"id":12699},"сценарій-1-програмний-доступ-без-паролів-через-iam-database-authentication","Сценарій 1: Програмний доступ без паролів через IAM Database Authentication",[3353,12702,12703],{},"У традиційних архітектурах автентифікація застосунків у базі даних базується на статичних облікових записах (ідентифікатор користувача та пароль), які зазвичай зберігаються у конфігураційних файлах або змінних середовища. Цей підхід створює суттєві ризики безпеки, оскільки паролі можуть бути випадково скомпрометовані (наприклад, потрапити у репозиторій або системні логи) та потребують регулярної ручної ротації.",[3353,12705,12706,12708],{},[3359,12707,5618],{}," розв'язує цю проблему шляхом повної відмови від статичних паролів. Замість цього клієнтський застосунок використовує тимчасові одноразові маркери (Auth Tokens), згенеровані на основі механізму підпису AWS Signature Version 4.",[5406,12710,12712],{"id":12711},"архітектурна-взаємодія-компонентів","Архітектурна взаємодія компонентів",[3353,12714,12715],{},"Наведена діаграма послідовності демонструє процес автентифікації без паролів:",[3423,12717,12718],{},[3426,12719,12721],{"className":3428,"code":12720,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam sequence {\n    ArrowColor #374151\n    ParticipantBorderColor #374151\n    ParticipantBackgroundColor #f9fafb\n    NoteBackgroundColor #dcfce7\n    NoteBorderColor #16a34a\n    LifeLineBorderColor #9ca3af\n}\n\ntitle Процес автентифікації через IAM Database Authentication\n\nparticipant \".NET Застосунок\n(на EC2\u002FECS)\" as APP\nparticipant \"AWS IAM\n(Security Token Service)\" as IAM\nparticipant \"Amazon RDS\nPostgreSQL\" as RDS\n\n== Крок 1: Отримання токена ==\nAPP -> IAM : Запит на генерацію токена доступу\\n(з використанням ролі інстансу)\nIAM --> APP : Повернення підписаного токена доступу\\n(дійсний протягом 15 хвилин)\n\n== Крок 2: Встановлення з'єднання ==\nAPP -> RDS : Підключення по TCP (порт 5432)\\nПароль = Згенерований IAM Токен\nnote over RDS\n  RDS перевіряє підпис токена\n  через інтеграцію з IAM\nend note\nRDS --> APP : З'єднання встановлено успішно (OK)\n@enduml\n",[3414,12722,12723,12727,12731,12735,12739,12743,12747,12751,12755,12759,12763,12767,12771,12776,12780,12785,12790,12795,12800,12805,12810,12814,12819,12824,12829,12833,12838,12843,12848,12853,12858,12862,12867],{"__ignoreMap":3431},[3435,12724,12725],{"class":3437,"line":3438},[3435,12726,3441],{},[3435,12728,12729],{"class":3437,"line":3444},[3435,12730,3447],{},[3435,12732,12733],{"class":3437,"line":3450},[3435,12734,3453],{},[3435,12736,12737],{"class":3437,"line":3456},[3435,12738,4008],{},[3435,12740,12741],{"class":3437,"line":3463},[3435,12742,4013],{},[3435,12744,12745],{"class":3437,"line":3469},[3435,12746,4018],{},[3435,12748,12749],{"class":3437,"line":3475},[3435,12750,4023],{},[3435,12752,12753],{"class":3437,"line":3481},[3435,12754,4028],{},[3435,12756,12757],{"class":3437,"line":3487},[3435,12758,4033],{},[3435,12760,12761],{"class":3437,"line":3493},[3435,12762,4038],{},[3435,12764,12765],{"class":3437,"line":3499},[3435,12766,3532],{},[3435,12768,12769],{"class":3437,"line":3505},[3435,12770,3460],{"emptyLinePlaceholder":3459},[3435,12772,12773],{"class":3437,"line":3511},[3435,12774,12775],{},"title Процес автентифікації через IAM Database Authentication\n",[3435,12777,12778],{"class":3437,"line":3517},[3435,12779,3460],{"emptyLinePlaceholder":3459},[3435,12781,12782],{"class":3437,"line":3523},[3435,12783,12784],{},"participant \".NET Застосунок\n",[3435,12786,12787],{"class":3437,"line":3529},[3435,12788,12789],{},"(на EC2\u002FECS)\" as APP\n",[3435,12791,12792],{"class":3437,"line":3535},[3435,12793,12794],{},"participant \"AWS IAM\n",[3435,12796,12797],{"class":3437,"line":3540},[3435,12798,12799],{},"(Security Token Service)\" as IAM\n",[3435,12801,12802],{"class":3437,"line":3546},[3435,12803,12804],{},"participant \"Amazon RDS\n",[3435,12806,12807],{"class":3437,"line":3552},[3435,12808,12809],{},"PostgreSQL\" as RDS\n",[3435,12811,12812],{"class":3437,"line":3558},[3435,12813,3460],{"emptyLinePlaceholder":3459},[3435,12815,12816],{"class":3437,"line":3563},[3435,12817,12818],{},"== Крок 1: Отримання токена ==\n",[3435,12820,12821],{"class":3437,"line":3569},[3435,12822,12823],{},"APP -> IAM : Запит на генерацію токена доступу\\n(з використанням ролі інстансу)\n",[3435,12825,12826],{"class":3437,"line":3575},[3435,12827,12828],{},"IAM --> APP : Повернення підписаного токена доступу\\n(дійсний протягом 15 хвилин)\n",[3435,12830,12831],{"class":3437,"line":3580},[3435,12832,3460],{"emptyLinePlaceholder":3459},[3435,12834,12835],{"class":3437,"line":3586},[3435,12836,12837],{},"== Крок 2: Встановлення з'єднання ==\n",[3435,12839,12840],{"class":3437,"line":3592},[3435,12841,12842],{},"APP -> RDS : Підключення по TCP (порт 5432)\\nПароль = Згенерований IAM Токен\n",[3435,12844,12845],{"class":3437,"line":3597},[3435,12846,12847],{},"note over RDS\n",[3435,12849,12850],{"class":3437,"line":3603},[3435,12851,12852],{},"  RDS перевіряє підпис токена\n",[3435,12854,12855],{"class":3437,"line":3609},[3435,12856,12857],{},"  через інтеграцію з IAM\n",[3435,12859,12860],{"class":3437,"line":3615},[3435,12861,4133],{},[3435,12863,12864],{"class":3437,"line":3621},[3435,12865,12866],{},"RDS --> APP : З'єднання встановлено успішно (OK)\n",[3435,12868,12869],{"class":3437,"line":3627},[3435,12870,3640],{},[5406,12872,12874],{"id":12873},"покрокова-реалізація","Покрокова реалізація",[11696,12876,12877,12881,12887,12900,12977,12981,12984,12991,13075,13099,13102,13106,13109,13112,13128,13134,13137,13723,13729,13850],{},[3700,12878,12880],{"id":12879},"крок-1-створення-бази-даних-та-iam-користувача-у-субд","Крок 1: Створення бази даних та IAM-користувача у СУБД",[3353,12882,12883,12884,3691],{},"Для використання IAM-автентифікації необхідно створити користувача всередині бази даних PostgreSQL та приєднати його до спеціальної ролі ",[3414,12885,12886],{},"rds_iam",[3353,12888,12889,12890,5840,12893,12896,12897,12899],{},"Підключіться до вашого інстансу RDS за допомогою стандартного клієнта (наприклад, ",[3414,12891,12892],{},"psql",[3414,12894,12895],{},"pgAdmin",") під обліковим записом адміністратора (",[3414,12898,7623],{},") та виконайте такі SQL-команди:",[3426,12901,12905],{"className":12902,"code":12903,"language":12904,"meta":3431,"style":3431},"language-sql shiki shiki-themes light-plus dark-plus dark-plus","-- Створення користувача для застосунку\nCREATE USER app_iam_user;\n\n-- Надання користувачу дозволу на використання IAM-автентифікації\nGRANT rds_iam TO app_iam_user;\n\n-- Надання базових прав доступу на схему public\nGRANT ALL PRIVILEGES ON SCHEMA public TO app_iam_user;\n","sql",[3414,12906,12907,12912,12925,12929,12934,12948,12952,12957],{"__ignoreMap":3431},[3435,12908,12909],{"class":3437,"line":3438},[3435,12910,12911],{"class":4461},"-- Створення користувача для застосунку\n",[3435,12913,12914,12917,12920,12923],{"class":3437,"line":3444},[3435,12915,12916],{"class":4677},"CREATE",[3435,12918,12919],{"class":4677}," USER",[3435,12921,12922],{"class":4535}," app_iam_user",[3435,12924,5664],{"class":4467},[3435,12926,12927],{"class":3437,"line":3450},[3435,12928,3460],{"emptyLinePlaceholder":3459},[3435,12930,12931],{"class":3437,"line":3456},[3435,12932,12933],{"class":4461},"-- Надання користувачу дозволу на використання IAM-автентифікації\n",[3435,12935,12936,12939,12942,12945],{"class":3437,"line":3463},[3435,12937,12938],{"class":4677},"GRANT",[3435,12940,12941],{"class":4467}," rds_iam ",[3435,12943,12944],{"class":4677},"TO",[3435,12946,12947],{"class":4467}," app_iam_user;\n",[3435,12949,12950],{"class":3437,"line":3469},[3435,12951,3460],{"emptyLinePlaceholder":3459},[3435,12953,12954],{"class":3437,"line":3475},[3435,12955,12956],{"class":4461},"-- Надання базових прав доступу на схему public\n",[3435,12958,12959,12961,12964,12967,12970,12973,12975],{"class":3437,"line":3481},[3435,12960,12938],{"class":4677},[3435,12962,12963],{"class":4467}," ALL PRIVILEGES ",[3435,12965,12966],{"class":4677},"ON",[3435,12968,12969],{"class":4677}," SCHEMA",[3435,12971,12972],{"class":4467}," public ",[3435,12974,12944],{"class":4677},[3435,12976,12947],{"class":4467},[3700,12978,12980],{"id":12979},"крок-2-конфігурування-політики-доступу-aws-iam","Крок 2: Конфігурування політики доступу AWS IAM",[3353,12982,12983],{},"Необхідно створити політику безпеки IAM, яка дозволяє конкретному ресурсу (наприклад, віртуальній машині EC2) підключатися до RDS під створеним користувачем бази даних.",[3353,12985,12986,12987,12990],{},"Створіть політику IAM з назвою ",[3414,12988,12989],{},"RDSConnectPolicy"," з такою JSON-структурою:",[3426,12992,12994],{"className":7213,"code":12993,"language":7215,"meta":3431,"style":3431},"{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\"rds-db:connect\"],\n            \"Resource\": [\"arn:aws:rds-db:eu-central-1:123456789012:dbuser:db-XXXXXX\u002Fapp_iam_user\"]\n        }\n    ]\n}\n",[3414,12995,12996,13000,13012,13020,13024,13036,13050,13062,13066,13071],{"__ignoreMap":3431},[3435,12997,12998],{"class":3437,"line":3438},[3435,12999,4468],{"class":4467},[3435,13001,13002,13005,13007,13010],{"class":3437,"line":3444},[3435,13003,13004],{"class":7231},"    \"Version\"",[3435,13006,4485],{"class":4467},[3435,13008,13009],{"class":4473},"\"2012-10-17\"",[3435,13011,4491],{"class":4467},[3435,13013,13014,13017],{"class":3437,"line":3450},[3435,13015,13016],{"class":7231},"    \"Statement\"",[3435,13018,13019],{"class":4467},": [\n",[3435,13021,13022],{"class":3437,"line":3456},[3435,13023,6915],{"class":4467},[3435,13025,13026,13029,13031,13034],{"class":3437,"line":3463},[3435,13027,13028],{"class":7231},"            \"Effect\"",[3435,13030,4485],{"class":4467},[3435,13032,13033],{"class":4473},"\"Allow\"",[3435,13035,4491],{"class":4467},[3435,13037,13038,13041,13044,13047],{"class":3437,"line":3469},[3435,13039,13040],{"class":7231},"            \"Action\"",[3435,13042,13043],{"class":4467},": [",[3435,13045,13046],{"class":4473},"\"rds-db:connect\"",[3435,13048,13049],{"class":4467},"],\n",[3435,13051,13052,13055,13057,13060],{"class":3437,"line":3475},[3435,13053,13054],{"class":7231},"            \"Resource\"",[3435,13056,13043],{"class":4467},[3435,13058,13059],{"class":4473},"\"arn:aws:rds-db:eu-central-1:123456789012:dbuser:db-XXXXXX\u002Fapp_iam_user\"",[3435,13061,11312],{"class":4467},[3435,13063,13064],{"class":3437,"line":3481},[3435,13065,7449],{"class":4467},[3435,13067,13068],{"class":3437,"line":3487},[3435,13069,13070],{"class":4467},"    ]\n",[3435,13072,13073],{"class":3437,"line":3493},[3435,13074,3532],{"class":4467},[5185,13076,13077],{},[3353,13078,13079,13082,13083,13086,13087,13090,13091,13094,13095,13098],{},[3435,13080,13081],{},"!WARNING","\nФормат ARN для ресурсу підключення має чітку структуру:\n",[3414,13084,13085],{},"arn:aws:rds-db:[region]:[account-id]:dbuser:[db-instance-resource-id]\u002F[db-user-name]","\nЗверніть увагу, що ",[3414,13088,13089],{},"db-instance-resource-id"," (наприклад, ",[3414,13092,13093],{},"db-ABC123XYZ",") — це унікальний внутрішній ідентифікатор інстансу RDS, який можна знайти в консолі AWS на вкладці Configuration (значення ",[3897,13096,13097],{},"Resource ID","), а не назва інстансу.",[3353,13100,13101],{},"Прив'яжіть створену політику до IAM-ролі, яка призначена для інстансу EC2 (Instance Profile), на якому розгортається ваш .NET застосунок.",[3700,13103,13105],{"id":13104},"крок-3-інтеграція-aws-sdk-та-генерація-токенів-у-net","Крок 3: Інтеграція AWS SDK та генерація токенів у .NET",[3353,13107,13108],{},"Для взаємодії з API AWS та генерації токенів у .NET-застосунок необхідно встановити офіційні бібліотеки AWS SDK.",[3353,13110,13111],{},"Встановіть необхідні пакети через NuGet CLI:",[3426,13113,13115],{"className":5213,"code":13114,"language":5215,"meta":3431,"style":3431},"dotnet add package AWSSDK.RDS\n",[3414,13116,13117],{"__ignoreMap":3431},[3435,13118,13119,13121,13123,13125],{"class":3437,"line":3438},[3435,13120,6602],{"class":4535},[3435,13122,6605],{"class":4473},[3435,13124,6608],{"class":4473},[3435,13126,13127],{"class":4473}," AWSSDK.RDS\n",[3353,13129,13130,13131,13133],{},"У процесі налаштування контексту бази даних ",[3414,13132,4543],{}," ми використаємо концепцію перехоплення (Interceptors) або динамічної генерації пароля перед кожним встановленням підключення. Оскільки життєвий цикл токена становить 15 хвилин, жорстке зчитування токена під час запуску застосунку (Startup) призведе до помилок автентифікації після закінчення цього терміну.",[3353,13135,13136],{},"Реалізуємо динамічне зчитування токена за допомогою кастомного DB Connection Interceptor в Entity Framework Core:",[3426,13138,13140],{"className":4452,"code":13139,"language":4454,"meta":3431,"style":3431},"using System.Data.Common;\nusing Amazon;\nusing Amazon.RDS;\nusing Microsoft.EntityFrameworkCore.Diagnostics;\nusing Npgsql;\n\nnamespace MyDotnetApp.Data;\n\npublic class RdsIamDbConnectionInterceptor : DbConnectionInterceptor\n{\n    private const string DbHost = \"mydb.xxx.eu-central-1.rds.amazonaws.com\";\n    private const int DbPort = 5432;\n    private const string DbUser = \"app_iam_user\";\n    private const string DbName = \"myapp\";\n    private const string Region = \"eu-central-1\";\n\n    public override async ValueTask\u003CInterceptionResult> ConnectionOpeningAsync(\n        DbConnection connection,\n        ConnectionEventData eventData,\n        InterceptionResult result,\n        CancellationToken cancellationToken = default)\n    {\n        if (connection is NpgsqlConnection npgsqlConnection)\n        {\n            \u002F\u002F Генеруємо новий тимчасовий токен перед відкриттям кожного з'єднання\n            string token = await GenerateRdsAuthTokenAsync();\n\n            \u002F\u002F Оновлюємо connection string з новим токеном як паролем\n            var builder = new NpgsqlConnectionStringBuilder(npgsqlConnection.ConnectionString)\n            {\n                Password = token\n            };\n\n            npgsqlConnection.ConnectionString = builder.ConnectionString;\n        }\n\n        return await base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);\n    }\n\n    private static async Task\u003Cstring> GenerateRdsAuthTokenAsync()\n    {\n        \u002F\u002F Створюємо клієнт RDS без явного передавання credentials.\n        \u002F\u002F Клієнт автоматично використовує Instance Metadata Service (IMDS) на EC2 для отримання ролі.\n        using var client = new AmazonRDSClient(RegionEndpoint.GetBySystemName(Region));\n\n        \u002F\u002F Генерація токена виконується локально клієнтом за допомогою підпису запиту\n        return await Task.Run(() =>\n            RDSAuthTokenGenerator.GenerateAuthToken(Region, DbHost, DbPort, DbUser));\n    }\n}\n",[3414,13141,13142,13161,13169,13181,13200,13209,13213,13226,13230,13245,13249,13269,13286,13304,13322,13339,13343,13366,13376,13386,13396,13411,13415,13437,13441,13446,13462,13466,13471,13497,13502,13512,13517,13521,13540,13544,13548,13583,13587,13591,13614,13618,13623,13628,13661,13665,13670,13685,13715,13719],{"__ignoreMap":3431},[3435,13143,13144,13146,13149,13151,13154,13156,13159],{"class":3437,"line":3438},[3435,13145,5658],{"class":5657},[3435,13147,13148],{"class":4542}," System",[3435,13150,3691],{"class":4467},[3435,13152,13153],{"class":4542},"Data",[3435,13155,3691],{"class":4467},[3435,13157,13158],{"class":4542},"Common",[3435,13160,5664],{"class":4467},[3435,13162,13163,13165,13167],{"class":3437,"line":3444},[3435,13164,5658],{"class":5657},[3435,13166,5661],{"class":4542},[3435,13168,5664],{"class":4467},[3435,13170,13171,13173,13175,13177,13179],{"class":3437,"line":3450},[3435,13172,5658],{"class":5657},[3435,13174,5661],{"class":4542},[3435,13176,3691],{"class":4467},[3435,13178,5675],{"class":4542},[3435,13180,5664],{"class":4467},[3435,13182,13183,13185,13188,13190,13193,13195,13198],{"class":3437,"line":3456},[3435,13184,5658],{"class":5657},[3435,13186,13187],{"class":4542}," Microsoft",[3435,13189,3691],{"class":4467},[3435,13191,13192],{"class":4542},"EntityFrameworkCore",[3435,13194,3691],{"class":4467},[3435,13196,13197],{"class":4542},"Diagnostics",[3435,13199,5664],{"class":4467},[3435,13201,13202,13204,13207],{"class":3437,"line":3463},[3435,13203,5658],{"class":5657},[3435,13205,13206],{"class":4542}," Npgsql",[3435,13208,5664],{"class":4467},[3435,13210,13211],{"class":3437,"line":3469},[3435,13212,3460],{"emptyLinePlaceholder":3459},[3435,13214,13215,13218,13220,13222,13224],{"class":3437,"line":3475},[3435,13216,13217],{"class":4677},"namespace",[3435,13219,10733],{"class":4542},[3435,13221,3691],{"class":4467},[3435,13223,13153],{"class":4542},[3435,13225,5664],{"class":4467},[3435,13227,13228],{"class":3437,"line":3481},[3435,13229,3460],{"emptyLinePlaceholder":3459},[3435,13231,13232,13234,13236,13239,13242],{"class":3437,"line":3487},[3435,13233,4678],{"class":4677},[3435,13235,4681],{"class":4677},[3435,13237,13238],{"class":4542}," RdsIamDbConnectionInterceptor",[3435,13240,13241],{"class":4467}," : ",[3435,13243,13244],{"class":4542},"DbConnectionInterceptor\n",[3435,13246,13247],{"class":3437,"line":3493},[3435,13248,4468],{"class":4467},[3435,13250,13251,13254,13257,13259,13262,13264,13267],{"class":3437,"line":3499},[3435,13252,13253],{"class":4677},"    private",[3435,13255,13256],{"class":4677}," const",[3435,13258,6704],{"class":4677},[3435,13260,13261],{"class":4524}," DbHost",[3435,13263,5300],{"class":4467},[3435,13265,13266],{"class":4473},"\"mydb.xxx.eu-central-1.rds.amazonaws.com\"",[3435,13268,5664],{"class":4467},[3435,13270,13271,13273,13275,13277,13280,13282,13284],{"class":3437,"line":3505},[3435,13272,13253],{"class":4677},[3435,13274,13256],{"class":4677},[3435,13276,6679],{"class":4677},[3435,13278,13279],{"class":4524}," DbPort",[3435,13281,5300],{"class":4467},[3435,13283,5393],{"class":5337},[3435,13285,5664],{"class":4467},[3435,13287,13288,13290,13292,13294,13297,13299,13302],{"class":3437,"line":3511},[3435,13289,13253],{"class":4677},[3435,13291,13256],{"class":4677},[3435,13293,6704],{"class":4677},[3435,13295,13296],{"class":4524}," DbUser",[3435,13298,5300],{"class":4467},[3435,13300,13301],{"class":4473},"\"app_iam_user\"",[3435,13303,5664],{"class":4467},[3435,13305,13306,13308,13310,13312,13315,13317,13320],{"class":3437,"line":3517},[3435,13307,13253],{"class":4677},[3435,13309,13256],{"class":4677},[3435,13311,6704],{"class":4677},[3435,13313,13314],{"class":4524}," DbName",[3435,13316,5300],{"class":4467},[3435,13318,13319],{"class":4473},"\"myapp\"",[3435,13321,5664],{"class":4467},[3435,13323,13324,13326,13328,13330,13333,13335,13337],{"class":3437,"line":3523},[3435,13325,13253],{"class":4677},[3435,13327,13256],{"class":4677},[3435,13329,6704],{"class":4677},[3435,13331,13332],{"class":4524}," Region",[3435,13334,5300],{"class":4467},[3435,13336,5754],{"class":4473},[3435,13338,5664],{"class":4467},[3435,13340,13341],{"class":3437,"line":3529},[3435,13342,3460],{"emptyLinePlaceholder":3459},[3435,13344,13345,13347,13349,13351,13354,13356,13359,13361,13364],{"class":3437,"line":3535},[3435,13346,4715],{"class":4677},[3435,13348,6869],{"class":4677},[3435,13350,4718],{"class":4677},[3435,13352,13353],{"class":4542}," ValueTask",[3435,13355,4539],{"class":4467},[3435,13357,13358],{"class":4542},"InterceptionResult",[3435,13360,6811],{"class":4467},[3435,13362,13363],{"class":4535},"ConnectionOpeningAsync",[3435,13365,5744],{"class":4467},[3435,13367,13368,13371,13374],{"class":3437,"line":3540},[3435,13369,13370],{"class":4542},"        DbConnection",[3435,13372,13373],{"class":4524}," connection",[3435,13375,4491],{"class":4467},[3435,13377,13378,13381,13384],{"class":3437,"line":3546},[3435,13379,13380],{"class":4542},"        ConnectionEventData",[3435,13382,13383],{"class":4524}," eventData",[3435,13385,4491],{"class":4467},[3435,13387,13388,13391,13394],{"class":3437,"line":3552},[3435,13389,13390],{"class":4542},"        InterceptionResult",[3435,13392,13393],{"class":4524}," result",[3435,13395,4491],{"class":4467},[3435,13397,13398,13401,13404,13406,13409],{"class":3437,"line":3558},[3435,13399,13400],{"class":4542},"        CancellationToken",[3435,13402,13403],{"class":4524}," cancellationToken",[3435,13405,5300],{"class":4467},[3435,13407,13408],{"class":4677},"default",[3435,13410,4701],{"class":4467},[3435,13412,13413],{"class":3437,"line":3563},[3435,13414,4794],{"class":4467},[3435,13416,13417,13420,13423,13426,13429,13432,13435],{"class":3437,"line":3569},[3435,13418,13419],{"class":5657},"        if",[3435,13421,13422],{"class":4467}," (",[3435,13424,13425],{"class":4524},"connection",[3435,13427,13428],{"class":4677}," is",[3435,13430,13431],{"class":4542}," NpgsqlConnection",[3435,13433,13434],{"class":4524}," npgsqlConnection",[3435,13436,4701],{"class":4467},[3435,13438,13439],{"class":3437,"line":3575},[3435,13440,6915],{"class":4467},[3435,13442,13443],{"class":3437,"line":3580},[3435,13444,13445],{"class":4461},"            \u002F\u002F Генеруємо новий тимчасовий токен перед відкриттям кожного з'єднання\n",[3435,13447,13448,13451,13453,13455,13457,13460],{"class":3437,"line":3586},[3435,13449,13450],{"class":4677},"            string",[3435,13452,5731],{"class":4524},[3435,13454,5300],{"class":4467},[3435,13456,11391],{"class":4677},[3435,13458,13459],{"class":4535}," GenerateRdsAuthTokenAsync",[3435,13461,4760],{"class":4467},[3435,13463,13464],{"class":3437,"line":3592},[3435,13465,3460],{"emptyLinePlaceholder":3459},[3435,13467,13468],{"class":3437,"line":3597},[3435,13469,13470],{"class":4461},"            \u002F\u002F Оновлюємо connection string з новим токеном як паролем\n",[3435,13472,13473,13476,13478,13480,13482,13485,13487,13490,13492,13495],{"class":3437,"line":3603},[3435,13474,13475],{"class":4677},"            var",[3435,13477,11081],{"class":4524},[3435,13479,5300],{"class":4467},[3435,13481,5699],{"class":4677},[3435,13483,13484],{"class":4542}," NpgsqlConnectionStringBuilder",[3435,13486,4565],{"class":4467},[3435,13488,13489],{"class":4524},"npgsqlConnection",[3435,13491,3691],{"class":4467},[3435,13493,13494],{"class":4524},"ConnectionString",[3435,13496,4701],{"class":4467},[3435,13498,13499],{"class":3437,"line":3609},[3435,13500,13501],{"class":4467},"            {\n",[3435,13503,13504,13507,13509],{"class":3437,"line":3615},[3435,13505,13506],{"class":4524},"                Password",[3435,13508,5300],{"class":4467},[3435,13510,13511],{"class":4524},"token\n",[3435,13513,13514],{"class":3437,"line":3621},[3435,13515,13516],{"class":4467},"            };\n",[3435,13518,13519],{"class":3437,"line":3627},[3435,13520,3460],{"emptyLinePlaceholder":3459},[3435,13522,13523,13526,13528,13530,13532,13534,13536,13538],{"class":3437,"line":3632},[3435,13524,13525],{"class":4524},"            npgsqlConnection",[3435,13527,3691],{"class":4467},[3435,13529,13494],{"class":4524},[3435,13531,5300],{"class":4467},[3435,13533,4525],{"class":4524},[3435,13535,3691],{"class":4467},[3435,13537,13494],{"class":4524},[3435,13539,5664],{"class":4467},[3435,13541,13542],{"class":3437,"line":3637},[3435,13543,7449],{"class":4467},[3435,13545,13546],{"class":3437,"line":4160},[3435,13547,3460],{"emptyLinePlaceholder":3459},[3435,13549,13550,13552,13555,13558,13560,13562,13564,13566,13568,13571,13573,13576,13578,13581],{"class":3437,"line":4166},[3435,13551,11481],{"class":5657},[3435,13553,13554],{"class":4677}," await",[3435,13556,13557],{"class":4677}," base",[3435,13559,3691],{"class":4467},[3435,13561,13363],{"class":4535},[3435,13563,4565],{"class":4467},[3435,13565,13425],{"class":4524},[3435,13567,3417],{"class":4467},[3435,13569,13570],{"class":4524},"eventData",[3435,13572,3417],{"class":4467},[3435,13574,13575],{"class":4524},"result",[3435,13577,3417],{"class":4467},[3435,13579,13580],{"class":4524},"cancellationToken",[3435,13582,4816],{"class":4467},[3435,13584,13585],{"class":3437,"line":4171},[3435,13586,3490],{"class":4467},[3435,13588,13589],{"class":3437,"line":4176},[3435,13590,3460],{"emptyLinePlaceholder":3459},[3435,13592,13593,13595,13598,13600,13602,13604,13606,13608,13611],{"class":3437,"line":4182},[3435,13594,13253],{"class":4677},[3435,13596,13597],{"class":4677}," static",[3435,13599,4718],{"class":4677},[3435,13601,4721],{"class":4542},[3435,13603,4539],{"class":4467},[3435,13605,5728],{"class":4677},[3435,13607,6811],{"class":4467},[3435,13609,13610],{"class":4535},"GenerateRdsAuthTokenAsync",[3435,13612,13613],{"class":4467},"()\n",[3435,13615,13616],{"class":3437,"line":4188},[3435,13617,4794],{"class":4467},[3435,13619,13620],{"class":3437,"line":4194},[3435,13621,13622],{"class":4461},"        \u002F\u002F Створюємо клієнт RDS без явного передавання credentials.\n",[3435,13624,13625],{"class":3437,"line":4199},[3435,13626,13627],{"class":4461},"        \u002F\u002F Клієнт автоматично використовує Instance Metadata Service (IMDS) на EC2 для отримання ролі.\n",[3435,13629,13630,13633,13636,13638,13640,13642,13644,13646,13648,13650,13653,13655,13658],{"class":3437,"line":4205},[3435,13631,13632],{"class":5657},"        using",[3435,13634,13635],{"class":4677}," var",[3435,13637,5694],{"class":4524},[3435,13639,5300],{"class":4467},[3435,13641,5699],{"class":4677},[3435,13643,5702],{"class":4542},[3435,13645,4565],{"class":4467},[3435,13647,5707],{"class":4524},[3435,13649,3691],{"class":4467},[3435,13651,13652],{"class":4535},"GetBySystemName",[3435,13654,4565],{"class":4467},[3435,13656,13657],{"class":4524},"Region",[3435,13659,13660],{"class":4467},"));\n",[3435,13662,13663],{"class":3437,"line":4211},[3435,13664,3460],{"emptyLinePlaceholder":3459},[3435,13666,13667],{"class":3437,"line":4217},[3435,13668,13669],{"class":4461},"        \u002F\u002F Генерація токена виконується локально клієнтом за допомогою підпису запиту\n",[3435,13671,13672,13674,13676,13678,13680,13682],{"class":3437,"line":4223},[3435,13673,11481],{"class":5657},[3435,13675,13554],{"class":4677},[3435,13677,4721],{"class":4524},[3435,13679,3691],{"class":4467},[3435,13681,11293],{"class":4535},[3435,13683,13684],{"class":4467},"(() =>\n",[3435,13686,13687,13690,13692,13694,13696,13698,13700,13703,13705,13708,13710,13713],{"class":3437,"line":4228},[3435,13688,13689],{"class":4524},"            RDSAuthTokenGenerator",[3435,13691,3691],{"class":4467},[3435,13693,5741],{"class":4535},[3435,13695,4565],{"class":4467},[3435,13697,13657],{"class":4524},[3435,13699,3417],{"class":4467},[3435,13701,13702],{"class":4524},"DbHost",[3435,13704,3417],{"class":4467},[3435,13706,13707],{"class":4524},"DbPort",[3435,13709,3417],{"class":4467},[3435,13711,13712],{"class":4524},"DbUser",[3435,13714,13660],{"class":4467},[3435,13716,13717],{"class":3437,"line":8197},[3435,13718,3490],{"class":4467},[3435,13720,13721],{"class":3437,"line":8203},[3435,13722,3532],{"class":4467},[3353,13724,13725,13726,13728],{},"Зареєструємо перехоплювач у файлі ",[3414,13727,7354],{}," під час конфігурування контексту:",[3426,13730,13732],{"className":4452,"code":13731,"language":4454,"meta":3431,"style":3431},"using MyDotnetApp.Data;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F Змінна середовища містить базову connection string БЕЗ пароля\nvar baseConnStr = \"Host=mydb.xxx.eu-central-1.rds.amazonaws.com;Database=myapp;Username=app_iam_user;SSL Mode=Require;\";\n\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseNpgsql(baseConnStr)\n           .AddInterceptors(new RdsIamDbConnectionInterceptor()));\n",[3414,13733,13734,13746,13750,13770,13774,13779,13793,13797,13819,13834],{"__ignoreMap":3431},[3435,13735,13736,13738,13740,13742,13744],{"class":3437,"line":3438},[3435,13737,5658],{"class":5657},[3435,13739,10733],{"class":4542},[3435,13741,3691],{"class":4467},[3435,13743,13153],{"class":4542},[3435,13745,5664],{"class":4467},[3435,13747,13748],{"class":3437,"line":3444},[3435,13749,3460],{"emptyLinePlaceholder":3459},[3435,13751,13752,13754,13756,13758,13760,13762,13764,13766,13768],{"class":3437,"line":3450},[3435,13753,5691],{"class":4677},[3435,13755,11081],{"class":4524},[3435,13757,5300],{"class":4467},[3435,13759,11086],{"class":4524},[3435,13761,3691],{"class":4467},[3435,13763,11091],{"class":4535},[3435,13765,4565],{"class":4467},[3435,13767,11096],{"class":4524},[3435,13769,4816],{"class":4467},[3435,13771,13772],{"class":3437,"line":3456},[3435,13773,3460],{"emptyLinePlaceholder":3459},[3435,13775,13776],{"class":3437,"line":3463},[3435,13777,13778],{"class":4461},"\u002F\u002F Змінна середовища містить базову connection string БЕЗ пароля\n",[3435,13780,13781,13783,13786,13788,13791],{"class":3437,"line":3469},[3435,13782,5691],{"class":4677},[3435,13784,13785],{"class":4524}," baseConnStr",[3435,13787,5300],{"class":4467},[3435,13789,13790],{"class":4473},"\"Host=mydb.xxx.eu-central-1.rds.amazonaws.com;Database=myapp;Username=app_iam_user;SSL Mode=Require;\"",[3435,13792,5664],{"class":4467},[3435,13794,13795],{"class":3437,"line":3475},[3435,13796,3460],{"emptyLinePlaceholder":3459},[3435,13798,13799,13801,13803,13805,13807,13809,13811,13813,13815,13817],{"class":3437,"line":3481},[3435,13800,4525],{"class":4524},[3435,13802,3691],{"class":4467},[3435,13804,4530],{"class":4524},[3435,13806,3691],{"class":4467},[3435,13808,4536],{"class":4535},[3435,13810,4539],{"class":4467},[3435,13812,4543],{"class":4542},[3435,13814,4546],{"class":4467},[3435,13816,4549],{"class":4524},[3435,13818,4552],{"class":4467},[3435,13820,13821,13823,13825,13827,13829,13832],{"class":3437,"line":3487},[3435,13822,4557],{"class":4524},[3435,13824,3691],{"class":4467},[3435,13826,4562],{"class":4535},[3435,13828,4565],{"class":4467},[3435,13830,13831],{"class":4524},"baseConnStr",[3435,13833,4701],{"class":4467},[3435,13835,13836,13838,13841,13843,13845,13847],{"class":3437,"line":3493},[3435,13837,4645],{"class":4467},[3435,13839,13840],{"class":4535},"AddInterceptors",[3435,13842,4565],{"class":4467},[3435,13844,5699],{"class":4677},[3435,13846,13238],{"class":4542},[3435,13848,13849],{"class":4467},"()));\n",[5185,13851,13852],{},[3353,13853,13854,13857,13858,13861],{},[3435,13855,13856],{},"!TIP","\nПри локальній розробці (на машині розробника) IAM-автентифікація також може працювати, якщо у вашій системі налаштовано файли облікових записів AWS CLI (файл ",[3414,13859,13860],{},"~\u002F.aws\u002Fcredentials",") з правами користувача, який має відповідні дозволи на підключення.",[3700,13863,13865],{"id":13864},"сценарій-2-aws-secrets-manager-та-автоматична-ротація-паролів-еволюція-безпеки","Сценарій 2: AWS Secrets Manager та автоматична ротація паролів (Еволюція безпеки)",[3353,13867,13868,13871,13872,3691],{},[3359,13869,13870],{},"Еволюційний перехід:"," Якщо для певних суміжних компонентів системи (наприклад, для адміністративних завдань СУБД, legacy-сервісів або сторонніх інтеграцій, які не підтримують динамічну генерацію IAM-токенів) все ж необхідна традиційна автентифікація за паролем, наступним кроком еволюції є повна відмова від статичних конфігураційних файлів і впровадження централізованого безпечного сховища секретів — ",[3359,13873,13874],{},"AWS Secrets Manager",[5406,13876,13878],{"id":13877},"детальний-аналіз-сервісу-aws-secrets-manager","Детальний аналіз сервісу AWS Secrets Manager",[3353,13880,13881,13883],{},[3359,13882,13874],{}," — це спеціалізований хмарний сервіс для безпечного зберігання, керування життєвим циклом та автентифікації конфіденційних даних (секретів), таких як реквізити доступу до баз даних, API-ключі та сертифікати.",[13885,13886,13888],"h5",{"id":13887},"архітектура-та-принципи-функціонування","Архітектура та принципи функціонування:",[3366,13890,13891,13901,13907],{},[3369,13892,13893,13896,13897,13900],{},[3359,13894,13895],{},"Шифрування даних (Encryption at Rest):"," Усі секрети шифруються за допомогою симетричного алгоритму AES-256 з інтеграцією з AWS KMS. Ви можете використовувати як стандартний ключ сервісу (",[3414,13898,13899],{},"aws\u002Fsecretsmanager","), так і власний Customer Managed Key (CMK) для підвищення контролю над аудитом ключів.",[3369,13902,13903,13906],{},[3359,13904,13905],{},"Структура секретів:"," Секрети зберігаються у вигляді пар ключ-значення (зазвичай у форматі JSON), що дозволяє групувати пов'язані дані (хост, порт, логін, пароль, назва БД) в межах одного об'єкта.",[3369,13908,13909,13912],{},[3359,13910,13911],{},"IAM Політики доступу:"," Доступ до секретів обмежується за допомогою політик IAM, що дозволяє надавати доступ лише певним ролям обчислювальних ресурсів (наприклад, EC2 інстансу застосунку) та логувати кожне звернення через AWS CloudTrail.",[13885,13914,13916],{"id":13915},"різниця-між-aws-secrets-manager-та-aws-systems-manager-parameter-store","Різниця між AWS Secrets Manager та AWS Systems Manager Parameter Store:",[3353,13918,13919],{},"Хоча обидва сервіси можуть зберігати конфігураційні дані, вони призначені для різних завдань:",[3913,13921,13922,13928,13934],{},[3369,13923,13924,13927],{},[3359,13925,13926],{},"Параметризація vs Секрети:"," Parameter Store є універсальним сховищем параметрів конфігурації (включаючи незашифровані рядки), тоді як Secrets Manager спроектований спеціально під секрети з інтегрованими механізмами безпеки.",[3369,13929,13930,13933],{},[3359,13931,13932],{},"Ротація облікових даних:"," Secrets Manager має вбудовану підтримку автоматичної ротації секретів через інтеграцію з AWS Lambda, чого немає в Parameter Store.",[3369,13935,13936,13939],{},[3359,13937,13938],{},"Ціноутворення:"," Parameter Store надає безкоштовні стандартні параметри (до 10 000 параметрів на акаунт), тоді як використання Secrets Manager тарифікується за кожен активний секрет на місяць плюс обсяг запитів API до нього.",[5406,13941,13943],{"id":13942},"автоматична-ротація-паролів-credential-rotation","Автоматична ротація паролів (Credential Rotation)",[3353,13945,13946],{},"Однією з головних переваг Secrets Manager є можливість автоматичної регулярної зміни пароля бази даних за розкладом без участі адміністратора.",[13885,13948,13950],{"id":13949},"механізм-ротації-single-user-rotation","Механізм ротації (Single-User Rotation):",[3353,13952,13953],{},"Для СУБД Amazon RDS Secrets Manager використовує серверлес-функцію AWS Lambda для виконання ротації. Процес складається з таких фаз:",[3913,13955,13956,13962,13972,13981,13989],{},[3369,13957,13958,13961],{},[3359,13959,13960],{},"Ініціація:"," За розкладом (наприклад, кожні 30 днів) або за запитом користувача Secrets Manager викликає Lambda-функцію ротації.",[3369,13963,13964,13967,13968,13971],{},[3359,13965,13966],{},"Створення нової версії (Create Version):"," Lambda-функція генерує новий випадковий пароль, створює нову версію секрету зі статусом ",[3414,13969,13970],{},"AWSPENDING"," та записує новий пароль у Secrets Manager.",[3369,13973,13974,13977,13978,5930],{},[3359,13975,13976],{},"Оновлення бази даних (Set Password):"," Lambda-функція підключається до бази даних RDS, використовуючи поточні адміністративні права, та виконує SQL-команду зміни пароля користувача на новий (",[3414,13979,13980],{},"ALTER USER ... WITH PASSWORD ...",[3369,13982,13983,13986,13987,3691],{},[3359,13984,13985],{},"Тестування (Test Secret):"," Lambda перевіряє можливість успішного з'єднання з базою даних, використовуючи облікові дані із версії секрету ",[3414,13988,13970],{},[3369,13990,13991,13994,13995,13997,13998,14001,14002,14005],{},[3359,13992,13993],{},"Фіксація версії (Finish Rotation):"," Secrets Manager переводить статус версії секрету з ",[3414,13996,13970],{}," в ",[3414,13999,14000],{},"AWSCURRENT"," (попередня версія отримує мітку ",[3414,14003,14004],{},"AWSPREVIOUS","). З цього моменту всі нові запити від застосунків отримують оновлений пароль.",[13885,14007,14009],{"id":14008},"архітектурна-діаграма-процесу-ротації","Архітектурна діаграма процесу ротації:",[3423,14011,14012],{},[3426,14013,14015],{"className":3428,"code":14014,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam sequence {\n    ArrowColor #374151\n    ParticipantBorderColor #374151\n    ParticipantBackgroundColor #f9fafb\n    NoteBackgroundColor #fef3c7\n    NoteBorderColor #d97706\n    LifeLineBorderColor #9ca3af\n}\n\ntitle Автоматична ротація пароля через AWS Secrets Manager\n\nparticipant \"Secrets Manager\" as SM\nparticipant \"Lambda-функція\\nротації\" as LMD\nparticipant \"RDS Database\" as DB\n\nSM -> LMD : 1. Виклик функції за розкладом\\n(Trigger Rotation)\nLMD -> SM : 2. Генерація нового пароля\\nта створення версії AWSPENDING\nLMD -> DB : 3. Виконання ALTER USER\\n(встановлення нового пароля у БД)\nLMD -> DB : 4. Тестування з'єднання\\nз новим паролем (Test Connection)\nLMD -> SM : 5. Фіксація версії як AWSCURRENT\\n(попередній пароль застарів)\n@enduml\n",[3414,14016,14017,14021,14025,14029,14033,14037,14041,14045,14049,14053,14057,14061,14065,14070,14074,14079,14084,14089,14093,14098,14103,14108,14113,14118],{"__ignoreMap":3431},[3435,14018,14019],{"class":3437,"line":3438},[3435,14020,3441],{},[3435,14022,14023],{"class":3437,"line":3444},[3435,14024,3447],{},[3435,14026,14027],{"class":3437,"line":3450},[3435,14028,3453],{},[3435,14030,14031],{"class":3437,"line":3456},[3435,14032,4008],{},[3435,14034,14035],{"class":3437,"line":3463},[3435,14036,4013],{},[3435,14038,14039],{"class":3437,"line":3469},[3435,14040,4018],{},[3435,14042,14043],{"class":3437,"line":3475},[3435,14044,4023],{},[3435,14046,14047],{"class":3437,"line":3481},[3435,14048,5021],{},[3435,14050,14051],{"class":3437,"line":3487},[3435,14052,5026],{},[3435,14054,14055],{"class":3437,"line":3493},[3435,14056,4038],{},[3435,14058,14059],{"class":3437,"line":3499},[3435,14060,3532],{},[3435,14062,14063],{"class":3437,"line":3505},[3435,14064,3460],{"emptyLinePlaceholder":3459},[3435,14066,14067],{"class":3437,"line":3511},[3435,14068,14069],{},"title Автоматична ротація пароля через AWS Secrets Manager\n",[3435,14071,14072],{"class":3437,"line":3517},[3435,14073,3460],{"emptyLinePlaceholder":3459},[3435,14075,14076],{"class":3437,"line":3523},[3435,14077,14078],{},"participant \"Secrets Manager\" as SM\n",[3435,14080,14081],{"class":3437,"line":3529},[3435,14082,14083],{},"participant \"Lambda-функція\\nротації\" as LMD\n",[3435,14085,14086],{"class":3437,"line":3535},[3435,14087,14088],{},"participant \"RDS Database\" as DB\n",[3435,14090,14091],{"class":3437,"line":3540},[3435,14092,3460],{"emptyLinePlaceholder":3459},[3435,14094,14095],{"class":3437,"line":3546},[3435,14096,14097],{},"SM -> LMD : 1. Виклик функції за розкладом\\n(Trigger Rotation)\n",[3435,14099,14100],{"class":3437,"line":3552},[3435,14101,14102],{},"LMD -> SM : 2. Генерація нового пароля\\nта створення версії AWSPENDING\n",[3435,14104,14105],{"class":3437,"line":3558},[3435,14106,14107],{},"LMD -> DB : 3. Виконання ALTER USER\\n(встановлення нового пароля у БД)\n",[3435,14109,14110],{"class":3437,"line":3563},[3435,14111,14112],{},"LMD -> DB : 4. Тестування з'єднання\\nз новим паролем (Test Connection)\n",[3435,14114,14115],{"class":3437,"line":3569},[3435,14116,14117],{},"LMD -> SM : 5. Фіксація версії як AWSCURRENT\\n(попередній пароль застарів)\n",[3435,14119,14120],{"class":3437,"line":3575},[3435,14121,3640],{},[5185,14123,14124,14132],{},[3353,14125,14126,14128,14131],{},[3435,14127,5191],{},[3359,14129,14130],{},"Обробка перехідного вікна застосунком:","\nПід час виконання кроків 3 та 4 у базі даних уже діє новий пароль, але застосунок, який кешує старий пароль, може спробувати встановити з'єднання та отримати помилку автентифікації. Для запобігання збоїв у роботі промислових систем застосовуються два підходи:",[3913,14133,14134,14142],{},[3369,14135,14136,14139,14140,3691],{},[3359,14137,14138],{},"Механізм повторних спроб (Retry Policy):"," Застосунок повинен повторити спробу підключення з невеликою експоненціальною затримкою. Під час повторного запиту застосунок знову зчитає секрет із Secrets Manager (або оновлений кеш скинеться), який вже матиме статус ",[3414,14141,14000],{},[3369,14143,14144,14147,14148,14151,14152,14155],{},[3359,14145,14146],{},"Стратегія Multi-User Rotation:"," Створюються два ідентичні користувачі бази даних (наприклад, ",[3414,14149,14150],{},"app_user_a"," та ",[3414,14153,14154],{},"app_user_b","). Ротація виконується по черзі: поки один користувач оновлюється, застосунок продовжує роботу через іншого вузла, повністю виключаючи момент несумісності пароля.",[5406,14157,14159],{"id":14158},"покрокова-реалізація-інтеграції-у-net","Покрокова реалізація інтеграції у .NET",[3700,14161,14163],{"id":14162},"крок-1-створення-секрету-в-aws-secrets-manager","Крок 1: Створення секрету в AWS Secrets Manager",[5205,14165,14166,14210,14269],{},[5208,14167,14168],{"label":7569},[3913,14169,14170,14178,14184,14190,14195,14204,14207],{},[3369,14171,14172,14173,7577,14175,3691],{},"Відкрийте консоль ",[3359,14174,13874],{},[3359,14176,14177],{},"Store a new secret",[3369,14179,14180,14181,3691],{},"Оберіть тип секрету: ",[3359,14182,14183],{},"Credentials for Amazon RDS database",[3369,14185,14186,14187,14189],{},"Введіть логін (",[3414,14188,7623],{}," або створений користувач застосунку) та пароль.",[3369,14191,14192,14193,5930],{},"У списку баз даних оберіть ваш інстанс RDS (",[3414,14194,7615],{},[3369,14196,9517,14197,14200,14201,3691],{},[3359,14198,14199],{},"Next",". Вкажіть назву секрету, наприклад: ",[3414,14202,14203],{},"production\u002Fmyapp\u002Fdatabase",[3369,14205,14206],{},"На вкладці ротації (Rotation) за необхідності увімкніть автоматичну зміну та оберіть розклад.",[3369,14208,14209],{},"Збережіть секрет.",[5208,14211,14212],{"label":5210},[3426,14213,14215],{"className":5213,"code":14214,"language":5215,"meta":3431,"style":3431},"# Створення секрету у форматі JSON\naws secretsmanager create-secret \\\n    --name \"production\u002Fmyapp\u002Fdatabase\" \\\n    --description \"Реквізити підключення до RDS PostgreSQL\" \\\n    --secret-string '{\"username\":\"postgres\",\"password\":\"YourSecurePassword123!\",\"host\":\"my-dotnet-app-db.xxx.eu-central-1.rds.amazonaws.com\",\"port\":5432,\"dbname\":\"myapp\"}' \\\n    --region eu-central-1\n",[3414,14216,14217,14222,14234,14244,14253,14263],{"__ignoreMap":3431},[3435,14218,14219],{"class":3437,"line":3438},[3435,14220,14221],{"class":4461},"# Створення секрету у форматі JSON\n",[3435,14223,14224,14226,14229,14232],{"class":3437,"line":3444},[3435,14225,5227],{"class":4535},[3435,14227,14228],{"class":4473}," secretsmanager",[3435,14230,14231],{"class":4473}," create-secret",[3435,14233,5237],{"class":5236},[3435,14235,14236,14239,14242],{"class":3437,"line":3450},[3435,14237,14238],{"class":4677},"    --name",[3435,14240,14241],{"class":4473}," \"production\u002Fmyapp\u002Fdatabase\"",[3435,14243,5237],{"class":5236},[3435,14245,14246,14248,14251],{"class":3437,"line":3456},[3435,14247,7963],{"class":4677},[3435,14249,14250],{"class":4473}," \"Реквізити підключення до RDS PostgreSQL\"",[3435,14252,5237],{"class":5236},[3435,14254,14255,14258,14261],{"class":3437,"line":3463},[3435,14256,14257],{"class":4677},"    --secret-string",[3435,14259,14260],{"class":4473}," '{\"username\":\"postgres\",\"password\":\"YourSecurePassword123!\",\"host\":\"my-dotnet-app-db.xxx.eu-central-1.rds.amazonaws.com\",\"port\":5432,\"dbname\":\"myapp\"}'",[3435,14262,5237],{"class":5236},[3435,14264,14265,14267],{"class":3437,"line":3469},[3435,14266,5275],{"class":4677},[3435,14268,5278],{"class":4473},[5208,14270,14271],{"label":5281},[3426,14272,14274],{"className":5284,"code":14273,"language":5286,"meta":3431,"style":3431},"# Створення секрету у форматі JSON\n$secretVal = '{\"username\":\"postgres\",\"password\":\"YourSecurePassword123!\",\"host\":\"my-dotnet-app-db.xxx.eu-central-1.rds.amazonaws.com\",\"port\":5432,\"dbname\":\"myapp\"}'\naws secretsmanager create-secret `\n    --name \"production\u002Fmyapp\u002Fdatabase\" `\n    --description \"Реквізити підключення до RDS PostgreSQL\" `\n    --secret-string $secretVal `\n    --region eu-central-1\n",[3414,14275,14276,14280,14290,14295,14305,14314,14323],{"__ignoreMap":3431},[3435,14277,14278],{"class":3437,"line":3438},[3435,14279,14221],{"class":4461},[3435,14281,14282,14285,14287],{"class":3437,"line":3444},[3435,14283,14284],{"class":4524},"$secretVal",[3435,14286,5300],{"class":4467},[3435,14288,14289],{"class":4473},"'{\"username\":\"postgres\",\"password\":\"YourSecurePassword123!\",\"host\":\"my-dotnet-app-db.xxx.eu-central-1.rds.amazonaws.com\",\"port\":5432,\"dbname\":\"myapp\"}'\n",[3435,14291,14292],{"class":3437,"line":3450},[3435,14293,14294],{"class":4467},"aws secretsmanager create-secret `\n",[3435,14296,14297,14300,14303],{"class":3437,"line":3456},[3435,14298,14299],{"class":4467},"    --name ",[3435,14301,14302],{"class":4473},"\"production\u002Fmyapp\u002Fdatabase\"",[3435,14304,5329],{"class":4467},[3435,14306,14307,14309,14312],{"class":3437,"line":3463},[3435,14308,8514],{"class":4467},[3435,14310,14311],{"class":4473},"\"Реквізити підключення до RDS PostgreSQL\"",[3435,14313,5329],{"class":4467},[3435,14315,14316,14319,14321],{"class":3437,"line":3469},[3435,14317,14318],{"class":4467},"    --secret-string ",[3435,14320,14284],{"class":4524},[3435,14322,5329],{"class":4467},[3435,14324,14325,14327],{"class":3437,"line":3475},[3435,14326,5334],{"class":4467},[3435,14328,5338],{"class":5337},[3700,14330,14332],{"id":14331},"крок-2-надання-прав-доступу-застосунку-через-iam","Крок 2: Надання прав доступу застосунку через IAM",[3353,14334,14335],{},"Переконайтеся, що IAM-роль, асоційована з вашим інстансом EC2, має право на зчитування створеного секрету. Додайте наступне правило до політики ролі:",[3426,14337,14339],{"className":7213,"code":14338,"language":7215,"meta":3431,"style":3431},"{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\"secretsmanager:GetSecretValue\"],\n            \"Resource\": [\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\"]\n        }\n    ]\n}\n",[3414,14340,14341,14345,14355,14361,14365,14375,14386,14397,14401,14405],{"__ignoreMap":3431},[3435,14342,14343],{"class":3437,"line":3438},[3435,14344,4468],{"class":4467},[3435,14346,14347,14349,14351,14353],{"class":3437,"line":3444},[3435,14348,13004],{"class":7231},[3435,14350,4485],{"class":4467},[3435,14352,13009],{"class":4473},[3435,14354,4491],{"class":4467},[3435,14356,14357,14359],{"class":3437,"line":3450},[3435,14358,13016],{"class":7231},[3435,14360,13019],{"class":4467},[3435,14362,14363],{"class":3437,"line":3456},[3435,14364,6915],{"class":4467},[3435,14366,14367,14369,14371,14373],{"class":3437,"line":3463},[3435,14368,13028],{"class":7231},[3435,14370,4485],{"class":4467},[3435,14372,13033],{"class":4473},[3435,14374,4491],{"class":4467},[3435,14376,14377,14379,14381,14384],{"class":3437,"line":3469},[3435,14378,13040],{"class":7231},[3435,14380,13043],{"class":4467},[3435,14382,14383],{"class":4473},"\"secretsmanager:GetSecretValue\"",[3435,14385,13049],{"class":4467},[3435,14387,14388,14390,14392,14395],{"class":3437,"line":3475},[3435,14389,13054],{"class":7231},[3435,14391,13043],{"class":4467},[3435,14393,14394],{"class":4473},"\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\"",[3435,14396,11312],{"class":4467},[3435,14398,14399],{"class":3437,"line":3481},[3435,14400,7449],{"class":4467},[3435,14402,14403],{"class":3437,"line":3487},[3435,14404,13070],{"class":4467},[3435,14406,14407],{"class":3437,"line":3493},[3435,14408,3532],{"class":4467},[3700,14410,14412],{"id":14411},"крок-3-написання-net-коду-для-інтеграції","Крок 3: Написання .NET-коду для інтеграції",[3353,14414,14415],{},"Для взаємодії з Secrets Manager встановимо відповідний NuGet-пакет AWS SDK:",[3426,14417,14419],{"className":5213,"code":14418,"language":5215,"meta":3431,"style":3431},"dotnet add package AWSSDK.SecretsManager\n",[3414,14420,14421],{"__ignoreMap":3431},[3435,14422,14423,14425,14427,14429],{"class":3437,"line":3438},[3435,14424,6602],{"class":4535},[3435,14426,6605],{"class":4473},[3435,14428,6608],{"class":4473},[3435,14430,14431],{"class":4473}," AWSSDK.SecretsManager\n",[3353,14433,14434],{},"Для інтеграції секрету з конфігурацією .NET створимо допоміжний клас, який зчитує секрет при старті застосунку та трансформує його у стандартну connection string.",[3353,14436,14437,14438,14441],{},"Створимо сервіс конфігурації у файлі ",[3414,14439,14440],{},"Services\u002FSecretsManagerConfiguration.cs",":",[3426,14443,14445],{"className":4452,"code":14444,"language":4454,"meta":3431,"style":3431},"using System.Text.Json;\nusing Amazon;\nusing Amazon.SecretsManager;\nusing Amazon.SecretsManager.Model;\n\nnamespace MyDotnetApp.Services;\n\npublic class DbCredentialSecret\n{\n    public string Username { get; set; } = string.Empty;\n    public string Password { get; set; } = string.Empty;\n    public string Host { get; set; } = string.Empty;\n    public int Port { get; set; } = 5432;\n    public string Dbname { get; set; } = string.Empty;\n\n    public string ToConnectionString() =>\n        $\"Host={Host};Port={Port};Database={Dbname};Username={Username};Password={Password};SSL Mode=Require;\";\n}\n\npublic static class SecretsManagerService\n{\n    public static async Task\u003Cstring> GetConnectionStringAsync(string secretName, string region)\n    {\n        using var client = new AmazonSecretsManagerClient(RegionEndpoint.GetBySystemName(region));\n\n        var request = new GetSecretValueRequest\n        {\n            SecretId = secretName,\n            VersionStage = \"AWSCURRENT\" \u002F\u002F Запит завжди поточної активної версії секрету\n        };\n\n        try\n        {\n            var response = await client.GetSecretValueAsync(request);\n            if (response.SecretString != null)\n            {\n                var credentials = JsonSerializer.Deserialize\u003CDbCredentialSecret>(\n                    response.SecretString,\n                    new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }\n                );\n\n                if (credentials != null)\n                {\n                    return credentials.ToConnectionString();\n                }\n            }\n            throw new InvalidOperationException(\"Вміст секрету порожній або має некоректний формат.\");\n        }\n        catch (Exception ex)\n        {\n            throw new Exception($\"Не вдалося отримати секрет з Secrets Manager: {ex.Message}\", ex);\n        }\n    }\n}\n",[3414,14446,14447,14465,14473,14486,14503,14507,14519,14523,14532,14536,14563,14590,14617,14640,14667,14671,14682,14738,14742,14746,14757,14761,14796,14800,14830,14834,14849,14853,14865,14878,14883,14887,14892,14896,14921,14943,14947,14973,14984,15010,15015,15019,15035,15040,15054,15059,15064,15081,15085,15100,15104,15138,15142,15146],{"__ignoreMap":3431},[3435,14448,14449,14451,14453,14455,14458,14460,14463],{"class":3437,"line":3438},[3435,14450,5658],{"class":5657},[3435,14452,13148],{"class":4542},[3435,14454,3691],{"class":4467},[3435,14456,14457],{"class":4542},"Text",[3435,14459,3691],{"class":4467},[3435,14461,14462],{"class":4542},"Json",[3435,14464,5664],{"class":4467},[3435,14466,14467,14469,14471],{"class":3437,"line":3444},[3435,14468,5658],{"class":5657},[3435,14470,5661],{"class":4542},[3435,14472,5664],{"class":4467},[3435,14474,14475,14477,14479,14481,14484],{"class":3437,"line":3450},[3435,14476,5658],{"class":5657},[3435,14478,5661],{"class":4542},[3435,14480,3691],{"class":4467},[3435,14482,14483],{"class":4542},"SecretsManager",[3435,14485,5664],{"class":4467},[3435,14487,14488,14490,14492,14494,14496,14498,14501],{"class":3437,"line":3456},[3435,14489,5658],{"class":5657},[3435,14491,5661],{"class":4542},[3435,14493,3691],{"class":4467},[3435,14495,14483],{"class":4542},[3435,14497,3691],{"class":4467},[3435,14499,14500],{"class":4542},"Model",[3435,14502,5664],{"class":4467},[3435,14504,14505],{"class":3437,"line":3463},[3435,14506,3460],{"emptyLinePlaceholder":3459},[3435,14508,14509,14511,14513,14515,14517],{"class":3437,"line":3469},[3435,14510,13217],{"class":4677},[3435,14512,10733],{"class":4542},[3435,14514,3691],{"class":4467},[3435,14516,4530],{"class":4542},[3435,14518,5664],{"class":4467},[3435,14520,14521],{"class":3437,"line":3475},[3435,14522,3460],{"emptyLinePlaceholder":3459},[3435,14524,14525,14527,14529],{"class":3437,"line":3481},[3435,14526,4678],{"class":4677},[3435,14528,4681],{"class":4677},[3435,14530,14531],{"class":4542}," DbCredentialSecret\n",[3435,14533,14534],{"class":3437,"line":3487},[3435,14535,4468],{"class":4467},[3435,14537,14538,14540,14542,14545,14547,14549,14551,14553,14555,14557,14559,14561],{"class":3437,"line":3493},[3435,14539,4715],{"class":4677},[3435,14541,6704],{"class":4677},[3435,14543,14544],{"class":4524}," Username",[3435,14546,6685],{"class":4467},[3435,14548,6688],{"class":4677},[3435,14550,6691],{"class":4467},[3435,14552,6694],{"class":4677},[3435,14554,6718],{"class":4467},[3435,14556,5728],{"class":4677},[3435,14558,3691],{"class":4467},[3435,14560,6725],{"class":4524},[3435,14562,5664],{"class":4467},[3435,14564,14565,14567,14569,14572,14574,14576,14578,14580,14582,14584,14586,14588],{"class":3437,"line":3499},[3435,14566,4715],{"class":4677},[3435,14568,6704],{"class":4677},[3435,14570,14571],{"class":4524}," Password",[3435,14573,6685],{"class":4467},[3435,14575,6688],{"class":4677},[3435,14577,6691],{"class":4467},[3435,14579,6694],{"class":4677},[3435,14581,6718],{"class":4467},[3435,14583,5728],{"class":4677},[3435,14585,3691],{"class":4467},[3435,14587,6725],{"class":4524},[3435,14589,5664],{"class":4467},[3435,14591,14592,14594,14596,14599,14601,14603,14605,14607,14609,14611,14613,14615],{"class":3437,"line":3505},[3435,14593,4715],{"class":4677},[3435,14595,6704],{"class":4677},[3435,14597,14598],{"class":4524}," Host",[3435,14600,6685],{"class":4467},[3435,14602,6688],{"class":4677},[3435,14604,6691],{"class":4467},[3435,14606,6694],{"class":4677},[3435,14608,6718],{"class":4467},[3435,14610,5728],{"class":4677},[3435,14612,3691],{"class":4467},[3435,14614,6725],{"class":4524},[3435,14616,5664],{"class":4467},[3435,14618,14619,14621,14623,14626,14628,14630,14632,14634,14636,14638],{"class":3437,"line":3511},[3435,14620,4715],{"class":4677},[3435,14622,6679],{"class":4677},[3435,14624,14625],{"class":4524}," Port",[3435,14627,6685],{"class":4467},[3435,14629,6688],{"class":4677},[3435,14631,6691],{"class":4467},[3435,14633,6694],{"class":4677},[3435,14635,6718],{"class":4467},[3435,14637,5393],{"class":5337},[3435,14639,5664],{"class":4467},[3435,14641,14642,14644,14646,14649,14651,14653,14655,14657,14659,14661,14663,14665],{"class":3437,"line":3517},[3435,14643,4715],{"class":4677},[3435,14645,6704],{"class":4677},[3435,14647,14648],{"class":4524}," Dbname",[3435,14650,6685],{"class":4467},[3435,14652,6688],{"class":4677},[3435,14654,6691],{"class":4467},[3435,14656,6694],{"class":4677},[3435,14658,6718],{"class":4467},[3435,14660,5728],{"class":4677},[3435,14662,3691],{"class":4467},[3435,14664,6725],{"class":4524},[3435,14666,5664],{"class":4467},[3435,14668,14669],{"class":3437,"line":3523},[3435,14670,3460],{"emptyLinePlaceholder":3459},[3435,14672,14673,14675,14677,14680],{"class":3437,"line":3529},[3435,14674,4715],{"class":4677},[3435,14676,6704],{"class":4677},[3435,14678,14679],{"class":4535}," ToConnectionString",[3435,14681,4740],{"class":4467},[3435,14683,14684,14687,14689,14692,14694,14697,14699,14702,14704,14707,14709,14712,14714,14717,14719,14722,14724,14727,14729,14732,14734,14736],{"class":3437,"line":3535},[3435,14685,14686],{"class":4473},"        $\"Host=",[3435,14688,5818],{"class":5817},[3435,14690,14691],{"class":4524},"Host",[3435,14693,5824],{"class":5817},[3435,14695,14696],{"class":4473},";Port=",[3435,14698,5818],{"class":5817},[3435,14700,14701],{"class":4524},"Port",[3435,14703,5824],{"class":5817},[3435,14705,14706],{"class":4473},";Database=",[3435,14708,5818],{"class":5817},[3435,14710,14711],{"class":4524},"Dbname",[3435,14713,5824],{"class":5817},[3435,14715,14716],{"class":4473},";Username=",[3435,14718,5818],{"class":5817},[3435,14720,14721],{"class":4524},"Username",[3435,14723,5824],{"class":5817},[3435,14725,14726],{"class":4473},";Password=",[3435,14728,5818],{"class":5817},[3435,14730,14731],{"class":4524},"Password",[3435,14733,5824],{"class":5817},[3435,14735,5827],{"class":4473},[3435,14737,5664],{"class":4467},[3435,14739,14740],{"class":3437,"line":3540},[3435,14741,3532],{"class":4467},[3435,14743,14744],{"class":3437,"line":3546},[3435,14745,3460],{"emptyLinePlaceholder":3459},[3435,14747,14748,14750,14752,14754],{"class":3437,"line":3552},[3435,14749,4678],{"class":4677},[3435,14751,13597],{"class":4677},[3435,14753,4681],{"class":4677},[3435,14755,14756],{"class":4542}," SecretsManagerService\n",[3435,14758,14759],{"class":3437,"line":3558},[3435,14760,4468],{"class":4467},[3435,14762,14763,14765,14767,14769,14771,14773,14775,14777,14780,14782,14784,14787,14789,14791,14794],{"class":3437,"line":3563},[3435,14764,4715],{"class":4677},[3435,14766,13597],{"class":4677},[3435,14768,4718],{"class":4677},[3435,14770,4721],{"class":4542},[3435,14772,4539],{"class":4467},[3435,14774,5728],{"class":4677},[3435,14776,6811],{"class":4467},[3435,14778,14779],{"class":4535},"GetConnectionStringAsync",[3435,14781,4565],{"class":4467},[3435,14783,5728],{"class":4677},[3435,14785,14786],{"class":4524}," secretName",[3435,14788,3417],{"class":4467},[3435,14790,5728],{"class":4677},[3435,14792,14793],{"class":4524}," region",[3435,14795,4701],{"class":4467},[3435,14797,14798],{"class":3437,"line":3569},[3435,14799,4794],{"class":4467},[3435,14801,14802,14804,14806,14808,14810,14812,14815,14817,14819,14821,14823,14825,14828],{"class":3437,"line":3575},[3435,14803,13632],{"class":5657},[3435,14805,13635],{"class":4677},[3435,14807,5694],{"class":4524},[3435,14809,5300],{"class":4467},[3435,14811,5699],{"class":4677},[3435,14813,14814],{"class":4542}," AmazonSecretsManagerClient",[3435,14816,4565],{"class":4467},[3435,14818,5707],{"class":4524},[3435,14820,3691],{"class":4467},[3435,14822,13652],{"class":4535},[3435,14824,4565],{"class":4467},[3435,14826,14827],{"class":4524},"region",[3435,14829,13660],{"class":4467},[3435,14831,14832],{"class":3437,"line":3580},[3435,14833,3460],{"emptyLinePlaceholder":3459},[3435,14835,14836,14839,14842,14844,14846],{"class":3437,"line":3586},[3435,14837,14838],{"class":4677},"        var",[3435,14840,14841],{"class":4524}," request",[3435,14843,5300],{"class":4467},[3435,14845,5699],{"class":4677},[3435,14847,14848],{"class":4542}," GetSecretValueRequest\n",[3435,14850,14851],{"class":3437,"line":3592},[3435,14852,6915],{"class":4467},[3435,14854,14855,14858,14860,14863],{"class":3437,"line":3597},[3435,14856,14857],{"class":4524},"            SecretId",[3435,14859,5300],{"class":4467},[3435,14861,14862],{"class":4524},"secretName",[3435,14864,4491],{"class":4467},[3435,14866,14867,14870,14872,14875],{"class":3437,"line":3603},[3435,14868,14869],{"class":4524},"            VersionStage",[3435,14871,5300],{"class":4467},[3435,14873,14874],{"class":4473},"\"AWSCURRENT\"",[3435,14876,14877],{"class":4461}," \u002F\u002F Запит завжди поточної активної версії секрету\n",[3435,14879,14880],{"class":3437,"line":3609},[3435,14881,14882],{"class":4467},"        };\n",[3435,14884,14885],{"class":3437,"line":3615},[3435,14886,3460],{"emptyLinePlaceholder":3459},[3435,14888,14889],{"class":3437,"line":3621},[3435,14890,14891],{"class":5657},"        try\n",[3435,14893,14894],{"class":3437,"line":3627},[3435,14895,6915],{"class":4467},[3435,14897,14898,14900,14903,14905,14907,14909,14911,14914,14916,14919],{"class":3437,"line":3632},[3435,14899,13475],{"class":4677},[3435,14901,14902],{"class":4524}," response",[3435,14904,5300],{"class":4467},[3435,14906,11391],{"class":4677},[3435,14908,5694],{"class":4524},[3435,14910,3691],{"class":4467},[3435,14912,14913],{"class":4535},"GetSecretValueAsync",[3435,14915,4565],{"class":4467},[3435,14917,14918],{"class":4524},"request",[3435,14920,4816],{"class":4467},[3435,14922,14923,14926,14928,14931,14933,14936,14939,14941],{"class":3437,"line":3637},[3435,14924,14925],{"class":5657},"            if",[3435,14927,13422],{"class":4467},[3435,14929,14930],{"class":4524},"response",[3435,14932,3691],{"class":4467},[3435,14934,14935],{"class":4524},"SecretString",[3435,14937,14938],{"class":4467}," != ",[3435,14940,7198],{"class":4677},[3435,14942,4701],{"class":4467},[3435,14944,14945],{"class":3437,"line":4160},[3435,14946,13501],{"class":4467},[3435,14948,14949,14952,14955,14957,14960,14962,14965,14967,14970],{"class":3437,"line":4166},[3435,14950,14951],{"class":4677},"                var",[3435,14953,14954],{"class":4524}," credentials",[3435,14956,5300],{"class":4467},[3435,14958,14959],{"class":4524},"JsonSerializer",[3435,14961,3691],{"class":4467},[3435,14963,14964],{"class":4535},"Deserialize",[3435,14966,4539],{"class":4467},[3435,14968,14969],{"class":4542},"DbCredentialSecret",[3435,14971,14972],{"class":4467},">(\n",[3435,14974,14975,14978,14980,14982],{"class":3437,"line":4171},[3435,14976,14977],{"class":4524},"                    response",[3435,14979,3691],{"class":4467},[3435,14981,14935],{"class":4524},[3435,14983,4491],{"class":4467},[3435,14985,14986,14989,14992,14994,14997,14999,15002,15004,15007],{"class":3437,"line":4176},[3435,14987,14988],{"class":4677},"                    new",[3435,14990,14991],{"class":4542}," JsonSerializerOptions",[3435,14993,6685],{"class":4467},[3435,14995,14996],{"class":4524},"PropertyNamingPolicy",[3435,14998,5300],{"class":4467},[3435,15000,15001],{"class":4524},"JsonNamingPolicy",[3435,15003,3691],{"class":4467},[3435,15005,15006],{"class":4524},"CamelCase",[3435,15008,15009],{"class":4467}," }\n",[3435,15011,15012],{"class":3437,"line":4182},[3435,15013,15014],{"class":4467},"                );\n",[3435,15016,15017],{"class":3437,"line":4188},[3435,15018,3460],{"emptyLinePlaceholder":3459},[3435,15020,15021,15024,15026,15029,15031,15033],{"class":3437,"line":4194},[3435,15022,15023],{"class":5657},"                if",[3435,15025,13422],{"class":4467},[3435,15027,15028],{"class":4524},"credentials",[3435,15030,14938],{"class":4467},[3435,15032,7198],{"class":4677},[3435,15034,4701],{"class":4467},[3435,15036,15037],{"class":3437,"line":4199},[3435,15038,15039],{"class":4467},"                {\n",[3435,15041,15042,15045,15047,15049,15052],{"class":3437,"line":4205},[3435,15043,15044],{"class":5657},"                    return",[3435,15046,14954],{"class":4524},[3435,15048,3691],{"class":4467},[3435,15050,15051],{"class":4535},"ToConnectionString",[3435,15053,4760],{"class":4467},[3435,15055,15056],{"class":3437,"line":4211},[3435,15057,15058],{"class":4467},"                }\n",[3435,15060,15061],{"class":3437,"line":4217},[3435,15062,15063],{"class":4467},"            }\n",[3435,15065,15066,15069,15071,15074,15076,15079],{"class":3437,"line":4223},[3435,15067,15068],{"class":5657},"            throw",[3435,15070,10724],{"class":4677},[3435,15072,15073],{"class":4542}," InvalidOperationException",[3435,15075,4565],{"class":4467},[3435,15077,15078],{"class":4473},"\"Вміст секрету порожній або має некоректний формат.\"",[3435,15080,4816],{"class":4467},[3435,15082,15083],{"class":3437,"line":4228},[3435,15084,7449],{"class":4467},[3435,15086,15087,15090,15092,15095,15098],{"class":3437,"line":8197},[3435,15088,15089],{"class":5657},"        catch",[3435,15091,13422],{"class":4467},[3435,15093,15094],{"class":4542},"Exception",[3435,15096,15097],{"class":4524}," ex",[3435,15099,4701],{"class":4467},[3435,15101,15102],{"class":3437,"line":8203},[3435,15103,6915],{"class":4467},[3435,15105,15106,15108,15110,15113,15115,15118,15120,15123,15125,15128,15130,15132,15134,15136],{"class":3437,"line":8218},[3435,15107,15068],{"class":5657},[3435,15109,10724],{"class":4677},[3435,15111,15112],{"class":4542}," Exception",[3435,15114,4565],{"class":4467},[3435,15116,15117],{"class":4473},"$\"Не вдалося отримати секрет з Secrets Manager: ",[3435,15119,5818],{"class":5817},[3435,15121,15122],{"class":4524},"ex",[3435,15124,3691],{"class":5817},[3435,15126,15127],{"class":4524},"Message",[3435,15129,5824],{"class":5817},[3435,15131,7799],{"class":4473},[3435,15133,3417],{"class":4467},[3435,15135,15122],{"class":4524},[3435,15137,4816],{"class":4467},[3435,15139,15140],{"class":3437,"line":8229},[3435,15141,7449],{"class":4467},[3435,15143,15144],{"class":3437,"line":8234},[3435,15145,3490],{"class":4467},[3435,15147,15148],{"class":3437,"line":8240},[3435,15149,3532],{"class":4467},[3353,15151,15152,15153,15155],{},"Тепер налаштуємо ",[3414,15154,7354],{}," для зчитування конфігурації перед ініціалізацією EF Core:",[3426,15157,15159],{"className":4452,"code":15158,"language":4454,"meta":3431,"style":3431},"using MyDotnetApp.Services;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F Зчитуємо назву секрету та регіон з конфігурації (наприклад, з appsettings.json чи змінних середоваща)\nvar secretName = builder.Configuration[\"AwsSettings:SecretName\"] ?? \"production\u002Fmyapp\u002Fdatabase\";\nvar awsRegion = builder.Configuration[\"AwsSettings:Region\"] ?? \"eu-central-1\";\n\n\u002F\u002F Запит з'єднання з Secrets Manager\nstring connectionString = await SecretsManagerService.GetConnectionStringAsync(secretName, awsRegion);\n\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseNpgsql(connectionString, npgsqlOptions =>\n        npgsqlOptions.EnableRetryOnFailure(\n            maxRetryCount: 5,\n            maxRetryDelay: TimeSpan.FromSeconds(3),\n            errorCodesToAdd: null\n        )\n    )\n);\n\nbuilder.Services.AddControllers();\nvar app = builder.Build();\napp.MapControllers();\napp.Run();\n",[3414,15160,15161,15173,15177,15197,15201,15206,15232,15258,15262,15267,15295,15299,15321,15340,15350,15360,15378,15387,15392,15397,15401,15405,15419,15435,15445],{"__ignoreMap":3431},[3435,15162,15163,15165,15167,15169,15171],{"class":3437,"line":3438},[3435,15164,5658],{"class":5657},[3435,15166,10733],{"class":4542},[3435,15168,3691],{"class":4467},[3435,15170,4530],{"class":4542},[3435,15172,5664],{"class":4467},[3435,15174,15175],{"class":3437,"line":3444},[3435,15176,3460],{"emptyLinePlaceholder":3459},[3435,15178,15179,15181,15183,15185,15187,15189,15191,15193,15195],{"class":3437,"line":3450},[3435,15180,5691],{"class":4677},[3435,15182,11081],{"class":4524},[3435,15184,5300],{"class":4467},[3435,15186,11086],{"class":4524},[3435,15188,3691],{"class":4467},[3435,15190,11091],{"class":4535},[3435,15192,4565],{"class":4467},[3435,15194,11096],{"class":4524},[3435,15196,4816],{"class":4467},[3435,15198,15199],{"class":3437,"line":3456},[3435,15200,3460],{"emptyLinePlaceholder":3459},[3435,15202,15203],{"class":3437,"line":3463},[3435,15204,15205],{"class":4461},"\u002F\u002F Зчитуємо назву секрету та регіон з конфігурації (наприклад, з appsettings.json чи змінних середоваща)\n",[3435,15207,15208,15210,15212,15214,15216,15218,15220,15222,15225,15228,15230],{"class":3437,"line":3469},[3435,15209,5691],{"class":4677},[3435,15211,14786],{"class":4524},[3435,15213,5300],{"class":4467},[3435,15215,4525],{"class":4524},[3435,15217,3691],{"class":4467},[3435,15219,4572],{"class":4524},[3435,15221,11306],{"class":4467},[3435,15223,15224],{"class":4473},"\"AwsSettings:SecretName\"",[3435,15226,15227],{"class":4467},"] ?? ",[3435,15229,14302],{"class":4473},[3435,15231,5664],{"class":4467},[3435,15233,15234,15236,15239,15241,15243,15245,15247,15249,15252,15254,15256],{"class":3437,"line":3475},[3435,15235,5691],{"class":4677},[3435,15237,15238],{"class":4524}," awsRegion",[3435,15240,5300],{"class":4467},[3435,15242,4525],{"class":4524},[3435,15244,3691],{"class":4467},[3435,15246,4572],{"class":4524},[3435,15248,11306],{"class":4467},[3435,15250,15251],{"class":4473},"\"AwsSettings:Region\"",[3435,15253,15227],{"class":4467},[3435,15255,5754],{"class":4473},[3435,15257,5664],{"class":4467},[3435,15259,15260],{"class":3437,"line":3481},[3435,15261,3460],{"emptyLinePlaceholder":3459},[3435,15263,15264],{"class":3437,"line":3487},[3435,15265,15266],{"class":4461},"\u002F\u002F Запит з'єднання з Secrets Manager\n",[3435,15268,15269,15271,15273,15275,15277,15280,15282,15284,15286,15288,15290,15293],{"class":3437,"line":3493},[3435,15270,5728],{"class":4677},[3435,15272,5809],{"class":4524},[3435,15274,5300],{"class":4467},[3435,15276,11391],{"class":4677},[3435,15278,15279],{"class":4524}," SecretsManagerService",[3435,15281,3691],{"class":4467},[3435,15283,14779],{"class":4535},[3435,15285,4565],{"class":4467},[3435,15287,14862],{"class":4524},[3435,15289,3417],{"class":4467},[3435,15291,15292],{"class":4524},"awsRegion",[3435,15294,4816],{"class":4467},[3435,15296,15297],{"class":3437,"line":3499},[3435,15298,3460],{"emptyLinePlaceholder":3459},[3435,15300,15301,15303,15305,15307,15309,15311,15313,15315,15317,15319],{"class":3437,"line":3505},[3435,15302,4525],{"class":4524},[3435,15304,3691],{"class":4467},[3435,15306,4530],{"class":4524},[3435,15308,3691],{"class":4467},[3435,15310,4536],{"class":4535},[3435,15312,4539],{"class":4467},[3435,15314,4543],{"class":4542},[3435,15316,4546],{"class":4467},[3435,15318,4549],{"class":4524},[3435,15320,4552],{"class":4467},[3435,15322,15323,15325,15327,15329,15331,15334,15336,15338],{"class":3437,"line":3511},[3435,15324,4557],{"class":4524},[3435,15326,3691],{"class":4467},[3435,15328,4562],{"class":4535},[3435,15330,4565],{"class":4467},[3435,15332,15333],{"class":4524},"connectionString",[3435,15335,3417],{"class":4467},[3435,15337,7130],{"class":4524},[3435,15339,4552],{"class":4467},[3435,15341,15342,15344,15346,15348],{"class":3437,"line":3517},[3435,15343,7146],{"class":4524},[3435,15345,3691],{"class":4467},[3435,15347,7151],{"class":4535},[3435,15349,5744],{"class":4467},[3435,15351,15352,15354,15356,15358],{"class":3437,"line":3523},[3435,15353,7158],{"class":4524},[3435,15355,4485],{"class":4467},[3435,15357,7185],{"class":5337},[3435,15359,4491],{"class":4467},[3435,15361,15362,15364,15366,15368,15370,15372,15374,15376],{"class":3437,"line":3529},[3435,15363,7170],{"class":4524},[3435,15365,4485],{"class":4467},[3435,15367,7175],{"class":4524},[3435,15369,3691],{"class":4467},[3435,15371,7180],{"class":4535},[3435,15373,4565],{"class":4467},[3435,15375,7163],{"class":5337},[3435,15377,7188],{"class":4467},[3435,15379,15380,15382,15384],{"class":3437,"line":3535},[3435,15381,7193],{"class":4524},[3435,15383,4485],{"class":4467},[3435,15385,15386],{"class":4677},"null\n",[3435,15388,15389],{"class":3437,"line":3540},[3435,15390,15391],{"class":4467},"        )\n",[3435,15393,15394],{"class":3437,"line":3546},[3435,15395,15396],{"class":4467},"    )\n",[3435,15398,15399],{"class":3437,"line":3552},[3435,15400,4816],{"class":4467},[3435,15402,15403],{"class":3437,"line":3558},[3435,15404,3460],{"emptyLinePlaceholder":3459},[3435,15406,15407,15409,15411,15413,15415,15417],{"class":3437,"line":3563},[3435,15408,4525],{"class":4524},[3435,15410,3691],{"class":4467},[3435,15412,4530],{"class":4524},[3435,15414,3691],{"class":4467},[3435,15416,11252],{"class":4535},[3435,15418,4760],{"class":4467},[3435,15420,15421,15423,15425,15427,15429,15431,15433],{"class":3437,"line":3569},[3435,15422,5691],{"class":4677},[3435,15424,11261],{"class":4524},[3435,15426,5300],{"class":4467},[3435,15428,4525],{"class":4524},[3435,15430,3691],{"class":4467},[3435,15432,11270],{"class":4535},[3435,15434,4760],{"class":4467},[3435,15436,15437,15439,15441,15443],{"class":3437,"line":3575},[3435,15438,11277],{"class":4524},[3435,15440,3691],{"class":4467},[3435,15442,11282],{"class":4535},[3435,15444,4760],{"class":4467},[3435,15446,15447,15449,15451,15453],{"class":3437,"line":3580},[3435,15448,11277],{"class":4524},[3435,15450,3691],{"class":4467},[3435,15452,11293],{"class":4535},[3435,15454,4760],{"class":4467},[3700,15456,15458],{"id":15457},"сценарій-3-масштабування-пулу-зєднань-через-amazon-rds-proxy-еволюція-продуктивності","Сценарій 3: Масштабування пулу з'єднань через Amazon RDS Proxy (Еволюція продуктивності)",[3353,15460,15461,15463,15464,15467],{},[3359,15462,13870],{}," Коли безпека доступу (на базі IAM-автентифікації або Secrets Manager) вже налаштована, на наступному етапі масштабування системи виникає нова проблема — вичерпання ліміту підключень до бази даних при горизонтальному розширенні застосунків (особливо в безсерверних архітектурах). Третім кроком еволюційного розвитку нашої інфраструктури є інтеграція проміжного шару — ",[3359,15465,15466],{},"Amazon RDS Proxy"," — для стабілізації роботи пулу з'єднань під високим навантаженням.",[5406,15469,15471],{"id":15470},"проблема-вичерпання-пулу-зєднань-та-архітектурне-призначення-rds-proxy","Проблема вичерпання пулу з'єднань та архітектурне призначення RDS Proxy",[3353,15473,15474],{},"Кожне нове мережеве з'єднання до PostgreSQL супроводжується запуском окремого процесу операційної системи на сервері БД (процес-воркер), що споживає від 5 до 10 MB оперативної пам'яті. При значній кількості короткочасних клієнтських запитів база даних витрачає процесорний час та RAM не на виконання корисних SQL-інструкцій, а на процедури встановлення TCP-сесій та виконання рукостискань (SSL\u002FTLS handshakes).",[3353,15476,15477,15479],{},[3359,15478,15466],{}," — це повністю керований хмарний проксі-сервер із високою доступністю, який розміщується між застосунком та СУБД. Він розв'язує проблему вичерпання з'єднань за допомогою трьох механізмів:",[3913,15481,15482,15488,15494],{},[3369,15483,15484,15487],{},[3359,15485,15486],{},"Мультиплексування з'єднань (Connection Multiplexing):"," RDS Proxy утримує невеликий пул постійно відкритих з'єднань до бази даних (database connection pool) і динамічно розподіляє їх між тисячами короткочасних з'єднань від клієнтських застосунків.",[3369,15489,15490,15493],{},[3359,15491,15492],{},"Черга з'єднань (Connection Queuing):"," Якщо база даних тимчасово перевантажена, RDS Proxy не повертає клієнту помилку відмови, а ставить нові запити у чергу до моменту звільнення з'єднання з пулу.",[3369,15495,15496,15499],{},[3359,15497,15498],{},"Оптимізація аварійного перемикання (Fast Failover):"," При переході Multi-AZ на standby інстанс RDS Proxy автоматично утримує активні TCP-сесії клієнтів і швидко перенаправляє внутрішні запити на новий первинний інстанс. Це скорочує видимий застосунком час даунтайму з 60–120 секунд до менш ніж 5–10 секунд.",[13885,15501,15503],{"id":15502},"архітектурна-схема-потоків-трафіку-через-rds-proxy","Архітектурна схема потоків трафіку через RDS Proxy:",[3423,15505,15506],{},[3426,15507,15509],{"className":3428,"code":15508,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\n\nnode \"Безсерверні клієнти\\n(AWS Lambda \u002F ECS Tasks)\" as CLIENTS {\n    component \"Застосунок 1\" as APP1\n    component \"Застосунок 2\" as APP2\n    component \"Застосунок N\" as APPN\n}\n\ndatabase \"RDS Proxy\\n(rds-proxy-sg)\" as PROXY #dbeafe\ndatabase \"RDS PostgreSQL\\n(rds-postgres-sg)\" as RDS #dcfce7\n\nAPP1 --> PROXY : 100 з'єднань (TCP)\nAPP2 --> PROXY : 100 з'єднань (TCP)\nAPPN --> PROXY : 100 з'єднань (TCP)\n\nPROXY --> RDS : Пул із 10 постійних з'єднань\\n(мультиплексування)\n@enduml\n",[3414,15510,15511,15515,15519,15523,15527,15532,15537,15542,15547,15551,15555,15560,15565,15569,15574,15579,15584,15588,15593],{"__ignoreMap":3431},[3435,15512,15513],{"class":3437,"line":3438},[3435,15514,3441],{},[3435,15516,15517],{"class":3437,"line":3444},[3435,15518,3447],{},[3435,15520,15521],{"class":3437,"line":3450},[3435,15522,3453],{},[3435,15524,15525],{"class":3437,"line":3456},[3435,15526,3460],{"emptyLinePlaceholder":3459},[3435,15528,15529],{"class":3437,"line":3463},[3435,15530,15531],{},"node \"Безсерверні клієнти\\n(AWS Lambda \u002F ECS Tasks)\" as CLIENTS {\n",[3435,15533,15534],{"class":3437,"line":3469},[3435,15535,15536],{},"    component \"Застосунок 1\" as APP1\n",[3435,15538,15539],{"class":3437,"line":3475},[3435,15540,15541],{},"    component \"Застосунок 2\" as APP2\n",[3435,15543,15544],{"class":3437,"line":3481},[3435,15545,15546],{},"    component \"Застосунок N\" as APPN\n",[3435,15548,15549],{"class":3437,"line":3487},[3435,15550,3532],{},[3435,15552,15553],{"class":3437,"line":3493},[3435,15554,3460],{"emptyLinePlaceholder":3459},[3435,15556,15557],{"class":3437,"line":3499},[3435,15558,15559],{},"database \"RDS Proxy\\n(rds-proxy-sg)\" as PROXY #dbeafe\n",[3435,15561,15562],{"class":3437,"line":3505},[3435,15563,15564],{},"database \"RDS PostgreSQL\\n(rds-postgres-sg)\" as RDS #dcfce7\n",[3435,15566,15567],{"class":3437,"line":3511},[3435,15568,3460],{"emptyLinePlaceholder":3459},[3435,15570,15571],{"class":3437,"line":3517},[3435,15572,15573],{},"APP1 --> PROXY : 100 з'єднань (TCP)\n",[3435,15575,15576],{"class":3437,"line":3523},[3435,15577,15578],{},"APP2 --> PROXY : 100 з'єднань (TCP)\n",[3435,15580,15581],{"class":3437,"line":3529},[3435,15582,15583],{},"APPN --> PROXY : 100 з'єднань (TCP)\n",[3435,15585,15586],{"class":3437,"line":3535},[3435,15587,3460],{"emptyLinePlaceholder":3459},[3435,15589,15590],{"class":3437,"line":3540},[3435,15591,15592],{},"PROXY --> RDS : Пул із 10 постійних з'єднань\\n(мультиплексування)\n",[3435,15594,15595],{"class":3437,"line":3546},[3435,15596,3640],{},[5406,15598,15600],{"id":15599},"практичний-експеримент-навантажувальне-тестування-за-допомогою-k6","Практичний експеримент: Навантажувальне тестування за допомогою k6",[3353,15602,15603],{},"Для практичної демонстрації ефективності використання RDS Proxy порівняємо результати тестування під навантаженням при прямому підключенні застосунку до бази даних та через проксі-сервер.",[3700,15605,15607],{"id":15606},"крок-1-створення-rds-proxy-через-aws-cli","Крок 1: Створення RDS Proxy через AWS CLI",[3353,15609,15610],{},"Для створення проксі-сервера необхідно спочатку зберегти облікові дані доступу адміністратора бази в AWS Secrets Manager (оскільки RDS Proxy повинен самостійно автентифікуватися у базі даних для керування пулом).",[5205,15612,15613,15858],{},[5208,15614,15615],{"label":5210},[3426,15616,15618],{"className":5213,"code":15617,"language":5215,"meta":3431,"style":3431},"# 1. Створення IAM ролі для RDS Proxy\naws iam create-role \\\n    --role-name rds-proxy-role \\\n    --assume-role-policy-document '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"rds.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}' \\\n    --region eu-central-1\n\n# 2. Додавання дозволу ролі на читання секрету з Secrets Manager\naws iam put-role-policy \\\n    --role-name rds-proxy-role \\\n    --policy-name rds-proxy-secrets-policy \\\n    --policy-document '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"secretsmanager:GetSecretValue\",\"Resource\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\"}]}' \\\n    --region eu-central-1\n\n# 3. Створення RDS Proxy\naws rds create-db-proxy \\\n    --db-proxy-name my-rds-proxy \\\n    --engine-family POSTGRESQL \\\n    --auth '[{\"AuthScheme\":\"SECRETS\",\"SecretArn\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\",\"IAMAuth\":\"DISABLED\"}]' \\\n    --role-arn arn:aws:iam::123456789012:role\u002Frds-proxy-role \\\n    --vpc-subnet-ids subnet-11111111 subnet-22222222 \\\n    --vpc-security-group-ids rds-proxy-sg \\\n    --region eu-central-1\n\n# 4. Реєстрація цільової бази даних у RDS Proxy\naws rds register-db-proxy-targets \\\n    --db-proxy-name my-rds-proxy \\\n    --target-group-name default \\\n    --db-instance-identifiers my-dotnet-app-db \\\n    --region eu-central-1\n",[3414,15619,15620,15625,15637,15647,15657,15663,15667,15672,15683,15691,15701,15711,15717,15721,15726,15737,15747,15757,15767,15777,15790,15799,15805,15809,15814,15825,15833,15843,15852],{"__ignoreMap":3431},[3435,15621,15622],{"class":3437,"line":3438},[3435,15623,15624],{"class":4461},"# 1. Створення IAM ролі для RDS Proxy\n",[3435,15626,15627,15629,15632,15635],{"class":3437,"line":3444},[3435,15628,5227],{"class":4535},[3435,15630,15631],{"class":4473}," iam",[3435,15633,15634],{"class":4473}," create-role",[3435,15636,5237],{"class":5236},[3435,15638,15639,15642,15645],{"class":3437,"line":3450},[3435,15640,15641],{"class":4677},"    --role-name",[3435,15643,15644],{"class":4473}," rds-proxy-role",[3435,15646,5237],{"class":5236},[3435,15648,15649,15652,15655],{"class":3437,"line":3456},[3435,15650,15651],{"class":4677},"    --assume-role-policy-document",[3435,15653,15654],{"class":4473}," '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"rds.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}'",[3435,15656,5237],{"class":5236},[3435,15658,15659,15661],{"class":3437,"line":3463},[3435,15660,5275],{"class":4677},[3435,15662,5278],{"class":4473},[3435,15664,15665],{"class":3437,"line":3469},[3435,15666,3460],{"emptyLinePlaceholder":3459},[3435,15668,15669],{"class":3437,"line":3475},[3435,15670,15671],{"class":4461},"# 2. Додавання дозволу ролі на читання секрету з Secrets Manager\n",[3435,15673,15674,15676,15678,15681],{"class":3437,"line":3481},[3435,15675,5227],{"class":4535},[3435,15677,15631],{"class":4473},[3435,15679,15680],{"class":4473}," put-role-policy",[3435,15682,5237],{"class":5236},[3435,15684,15685,15687,15689],{"class":3437,"line":3487},[3435,15686,15641],{"class":4677},[3435,15688,15644],{"class":4473},[3435,15690,5237],{"class":5236},[3435,15692,15693,15696,15699],{"class":3437,"line":3493},[3435,15694,15695],{"class":4677},"    --policy-name",[3435,15697,15698],{"class":4473}," rds-proxy-secrets-policy",[3435,15700,5237],{"class":5236},[3435,15702,15703,15706,15709],{"class":3437,"line":3499},[3435,15704,15705],{"class":4677},"    --policy-document",[3435,15707,15708],{"class":4473}," '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"secretsmanager:GetSecretValue\",\"Resource\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\"}]}'",[3435,15710,5237],{"class":5236},[3435,15712,15713,15715],{"class":3437,"line":3505},[3435,15714,5275],{"class":4677},[3435,15716,5278],{"class":4473},[3435,15718,15719],{"class":3437,"line":3511},[3435,15720,3460],{"emptyLinePlaceholder":3459},[3435,15722,15723],{"class":3437,"line":3517},[3435,15724,15725],{"class":4461},"# 3. Створення RDS Proxy\n",[3435,15727,15728,15730,15732,15735],{"class":3437,"line":3523},[3435,15729,5227],{"class":4535},[3435,15731,5230],{"class":4473},[3435,15733,15734],{"class":4473}," create-db-proxy",[3435,15736,5237],{"class":5236},[3435,15738,15739,15742,15745],{"class":3437,"line":3529},[3435,15740,15741],{"class":4677},"    --db-proxy-name",[3435,15743,15744],{"class":4473}," my-rds-proxy",[3435,15746,5237],{"class":5236},[3435,15748,15749,15752,15755],{"class":3437,"line":3535},[3435,15750,15751],{"class":4677},"    --engine-family",[3435,15753,15754],{"class":4473}," POSTGRESQL",[3435,15756,5237],{"class":5236},[3435,15758,15759,15762,15765],{"class":3437,"line":3540},[3435,15760,15761],{"class":4677},"    --auth",[3435,15763,15764],{"class":4473}," '[{\"AuthScheme\":\"SECRETS\",\"SecretArn\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\",\"IAMAuth\":\"DISABLED\"}]'",[3435,15766,5237],{"class":5236},[3435,15768,15769,15772,15775],{"class":3437,"line":3546},[3435,15770,15771],{"class":4677},"    --role-arn",[3435,15773,15774],{"class":4473}," arn:aws:iam::123456789012:role\u002Frds-proxy-role",[3435,15776,5237],{"class":5236},[3435,15778,15779,15782,15785,15788],{"class":3437,"line":3552},[3435,15780,15781],{"class":4677},"    --vpc-subnet-ids",[3435,15783,15784],{"class":4473}," subnet-11111111",[3435,15786,15787],{"class":4473}," subnet-22222222",[3435,15789,5237],{"class":5236},[3435,15791,15792,15794,15797],{"class":3437,"line":3558},[3435,15793,8156],{"class":4677},[3435,15795,15796],{"class":4473}," rds-proxy-sg",[3435,15798,5237],{"class":5236},[3435,15800,15801,15803],{"class":3437,"line":3563},[3435,15802,5275],{"class":4677},[3435,15804,5278],{"class":4473},[3435,15806,15807],{"class":3437,"line":3569},[3435,15808,3460],{"emptyLinePlaceholder":3459},[3435,15810,15811],{"class":3437,"line":3575},[3435,15812,15813],{"class":4461},"# 4. Реєстрація цільової бази даних у RDS Proxy\n",[3435,15815,15816,15818,15820,15823],{"class":3437,"line":3580},[3435,15817,5227],{"class":4535},[3435,15819,5230],{"class":4473},[3435,15821,15822],{"class":4473}," register-db-proxy-targets",[3435,15824,5237],{"class":5236},[3435,15826,15827,15829,15831],{"class":3437,"line":3586},[3435,15828,15741],{"class":4677},[3435,15830,15744],{"class":4473},[3435,15832,5237],{"class":5236},[3435,15834,15835,15838,15841],{"class":3437,"line":3592},[3435,15836,15837],{"class":4677},"    --target-group-name",[3435,15839,15840],{"class":4473}," default",[3435,15842,5237],{"class":5236},[3435,15844,15845,15848,15850],{"class":3437,"line":3597},[3435,15846,15847],{"class":4677},"    --db-instance-identifiers",[3435,15849,8076],{"class":4473},[3435,15851,5237],{"class":5236},[3435,15853,15854,15856],{"class":3437,"line":3603},[3435,15855,5275],{"class":4677},[3435,15857,5278],{"class":4473},[5208,15859,15860],{"label":5281},[3426,15861,15863],{"className":5284,"code":15862,"language":5286,"meta":3431,"style":3431},"# 1. Створення IAM ролі для RDS Proxy\n$trustPolicy = '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"rds.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}'\naws iam create-role `\n    --role-name rds-proxy-role `\n    --assume-role-policy-document $trustPolicy `\n    --region eu-central-1\n\n# 2. Додавання дозволу ролі на читання секрету\n$secretsPolicy = '{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"secretsmanager:GetSecretValue\",\"Resource\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\"}]}'\naws iam put-role-policy `\n    --role-name rds-proxy-role `\n    --policy-name rds-proxy-secrets-policy `\n    --policy-document $secretsPolicy `\n    --region eu-central-1\n\n# 3. Створення RDS Proxy\n$authObj = '[{\"AuthScheme\":\"SECRETS\",\"SecretArn\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\",\"IAMAuth\":\"DISABLED\"}]'\naws rds create-db-proxy `\n    --db-proxy-name my-rds-proxy `\n    --engine-family POSTGRESQL `\n    --auth $authObj `\n    --role-arn arn:aws:iam::123456789012:role\u002Frds-proxy-role `\n    --vpc-subnet-ids subnet-11111111 subnet-22222222 `\n    --vpc-security-group-ids rds-proxy-sg `\n    --region eu-central-1\n\n# 4. Реєстрація цільової бази даних\naws rds register-db-proxy-targets `\n    --db-proxy-name my-rds-proxy `\n    --target-group-name default `\n    --db-instance-identifiers my-dotnet-app-db `\n    --region eu-central-1\n",[3414,15864,15865,15869,15879,15884,15889,15898,15904,15908,15913,15923,15928,15932,15937,15946,15952,15956,15960,15970,15975,15980,15985,15994,16005,16021,16030,16036,16040,16045,16056,16060,16071,16076],{"__ignoreMap":3431},[3435,15866,15867],{"class":3437,"line":3438},[3435,15868,15624],{"class":4461},[3435,15870,15871,15874,15876],{"class":3437,"line":3444},[3435,15872,15873],{"class":4524},"$trustPolicy",[3435,15875,5300],{"class":4467},[3435,15877,15878],{"class":4473},"'{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"rds.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}'\n",[3435,15880,15881],{"class":3437,"line":3450},[3435,15882,15883],{"class":4467},"aws iam create-role `\n",[3435,15885,15886],{"class":3437,"line":3456},[3435,15887,15888],{"class":4467},"    --role-name rds-proxy-role `\n",[3435,15890,15891,15894,15896],{"class":3437,"line":3463},[3435,15892,15893],{"class":4467},"    --assume-role-policy-document ",[3435,15895,15873],{"class":4524},[3435,15897,5329],{"class":4467},[3435,15899,15900,15902],{"class":3437,"line":3469},[3435,15901,5334],{"class":4467},[3435,15903,5338],{"class":5337},[3435,15905,15906],{"class":3437,"line":3475},[3435,15907,3460],{"emptyLinePlaceholder":3459},[3435,15909,15910],{"class":3437,"line":3481},[3435,15911,15912],{"class":4461},"# 2. Додавання дозволу ролі на читання секрету\n",[3435,15914,15915,15918,15920],{"class":3437,"line":3487},[3435,15916,15917],{"class":4524},"$secretsPolicy",[3435,15919,5300],{"class":4467},[3435,15921,15922],{"class":4473},"'{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"secretsmanager:GetSecretValue\",\"Resource\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\"}]}'\n",[3435,15924,15925],{"class":3437,"line":3493},[3435,15926,15927],{"class":4467},"aws iam put-role-policy `\n",[3435,15929,15930],{"class":3437,"line":3499},[3435,15931,15888],{"class":4467},[3435,15933,15934],{"class":3437,"line":3505},[3435,15935,15936],{"class":4467},"    --policy-name rds-proxy-secrets-policy `\n",[3435,15938,15939,15942,15944],{"class":3437,"line":3511},[3435,15940,15941],{"class":4467},"    --policy-document ",[3435,15943,15917],{"class":4524},[3435,15945,5329],{"class":4467},[3435,15947,15948,15950],{"class":3437,"line":3517},[3435,15949,5334],{"class":4467},[3435,15951,5338],{"class":5337},[3435,15953,15954],{"class":3437,"line":3523},[3435,15955,3460],{"emptyLinePlaceholder":3459},[3435,15957,15958],{"class":3437,"line":3529},[3435,15959,15725],{"class":4461},[3435,15961,15962,15965,15967],{"class":3437,"line":3535},[3435,15963,15964],{"class":4524},"$authObj",[3435,15966,5300],{"class":4467},[3435,15968,15969],{"class":4473},"'[{\"AuthScheme\":\"SECRETS\",\"SecretArn\":\"arn:aws:secretsmanager:eu-central-1:123456789012:secret:production\u002Fmyapp\u002Fdatabase-XXXXXX\",\"IAMAuth\":\"DISABLED\"}]'\n",[3435,15971,15972],{"class":3437,"line":3540},[3435,15973,15974],{"class":4467},"aws rds create-db-proxy `\n",[3435,15976,15977],{"class":3437,"line":3546},[3435,15978,15979],{"class":4467},"    --db-proxy-name my-rds-proxy `\n",[3435,15981,15982],{"class":3437,"line":3552},[3435,15983,15984],{"class":4467},"    --engine-family POSTGRESQL `\n",[3435,15986,15987,15990,15992],{"class":3437,"line":3558},[3435,15988,15989],{"class":4467},"    --auth ",[3435,15991,15964],{"class":4524},[3435,15993,5329],{"class":4467},[3435,15995,15996,15999,16002],{"class":3437,"line":3563},[3435,15997,15998],{"class":4467},"    --role-arn arn:aws:iam::",[3435,16000,16001],{"class":5337},"123456789012",[3435,16003,16004],{"class":4467},":role\u002Frds-proxy-role `\n",[3435,16006,16007,16010,16013,16016,16019],{"class":3437,"line":3569},[3435,16008,16009],{"class":4467},"    --vpc-subnet-ids subnet-",[3435,16011,16012],{"class":5337},"11111111",[3435,16014,16015],{"class":4467}," subnet-",[3435,16017,16018],{"class":5337},"22222222",[3435,16020,5329],{"class":4467},[3435,16022,16023,16025,16027],{"class":3437,"line":3575},[3435,16024,8674],{"class":4467},[3435,16026,8677],{"class":4535},[3435,16028,16029],{"class":4467}," rds-proxy-sg `\n",[3435,16031,16032,16034],{"class":3437,"line":3580},[3435,16033,5334],{"class":4467},[3435,16035,5338],{"class":5337},[3435,16037,16038],{"class":3437,"line":3586},[3435,16039,3460],{"emptyLinePlaceholder":3459},[3435,16041,16042],{"class":3437,"line":3592},[3435,16043,16044],{"class":4461},"# 4. Реєстрація цільової бази даних\n",[3435,16046,16047,16050,16053],{"class":3437,"line":3597},[3435,16048,16049],{"class":4467},"aws rds ",[3435,16051,16052],{"class":4535},"register-db",[3435,16054,16055],{"class":4467},"-proxy-targets `\n",[3435,16057,16058],{"class":3437,"line":3603},[3435,16059,15979],{"class":4467},[3435,16061,16062,16065,16067,16069],{"class":3437,"line":3609},[3435,16063,16064],{"class":4467},"    --target-",[3435,16066,8454],{"class":4535},[3435,16068,15840],{"class":5657},[3435,16070,5329],{"class":4467},[3435,16072,16073],{"class":3437,"line":3615},[3435,16074,16075],{"class":4467},"    --db-instance-identifiers my-dotnet-app-db `\n",[3435,16077,16078,16080],{"class":3437,"line":3621},[3435,16079,5334],{"class":4467},[3435,16081,5338],{"class":5337},[3700,16083,16085],{"id":16084},"крок-2-створення-тестового-сценарію-навантаження-у-k6","Крок 2: Створення тестового сценарію навантаження у k6",[3353,16087,16088,16089,16092],{},"Для тестування використаємо сучасний інструмент тестування навантаження ",[3359,16090,16091],{},"k6"," (на базі Go та JavaScript). Напишемо скрипт, який імітує 200 одночасних віртуальних користувачів (Virtual Users, VUs), що безперервно виконують GET-запити до нашого .NET Web API протягом 30 секунд.",[3353,16094,16095,16096,14441],{},"Створіть файл ",[3414,16097,16098],{},"load-test.js",[3426,16100,16104],{"className":16101,"code":16102,"language":16103,"meta":3431,"style":3431},"language-javascript shiki shiki-themes light-plus dark-plus dark-plus","import http from 'k6\u002Fhttp'\nimport { sleep, check } from 'k6'\n\nexport const options = {\n    vus: 200, \u002F\u002F 200 одночасних імітованих користувачів\n    duration: '30s', \u002F\u002F Тривалість тесту\n}\n\nexport default function () {\n    \u002F\u002F Виклик API ендпоінту, який робить запит до PostgreSQL\n    const res = http.get('http:\u002F\u002F54.12.34.56:5000\u002Fapi\u002Fproducts')\n\n    check(res, {\n        'status is 200': (r) => r.status === 200,\n    })\n\n    sleep(0.1) \u002F\u002F Коротка пауза між запитами\n}\n","javascript",[3414,16105,16106,16120,16143,16147,16160,16173,16186,16190,16194,16206,16211,16235,16239,16252,16284,16289,16293,16308],{"__ignoreMap":3431},[3435,16107,16108,16111,16114,16117],{"class":3437,"line":3438},[3435,16109,16110],{"class":5657},"import",[3435,16112,16113],{"class":4524}," http",[3435,16115,16116],{"class":5657}," from",[3435,16118,16119],{"class":4473}," 'k6\u002Fhttp'\n",[3435,16121,16122,16124,16126,16129,16131,16134,16137,16140],{"class":3437,"line":3444},[3435,16123,16110],{"class":5657},[3435,16125,6685],{"class":4467},[3435,16127,16128],{"class":4524},"sleep",[3435,16130,3417],{"class":4467},[3435,16132,16133],{"class":4524},"check",[3435,16135,16136],{"class":4467}," } ",[3435,16138,16139],{"class":5657},"from",[3435,16141,16142],{"class":4473}," 'k6'\n",[3435,16144,16145],{"class":3437,"line":3450},[3435,16146,3460],{"emptyLinePlaceholder":3459},[3435,16148,16149,16151,16153,16157],{"class":3437,"line":3456},[3435,16150,11616],{"class":5657},[3435,16152,13256],{"class":4677},[3435,16154,16156],{"class":16155},"s-QsJ"," options",[3435,16158,16159],{"class":4467}," = {\n",[3435,16161,16162,16165,16168,16170],{"class":3437,"line":3463},[3435,16163,16164],{"class":4524},"    vus:",[3435,16166,16167],{"class":5337}," 200",[3435,16169,3417],{"class":4467},[3435,16171,16172],{"class":4461},"\u002F\u002F 200 одночасних імітованих користувачів\n",[3435,16174,16175,16178,16181,16183],{"class":3437,"line":3469},[3435,16176,16177],{"class":4524},"    duration:",[3435,16179,16180],{"class":4473}," '30s'",[3435,16182,3417],{"class":4467},[3435,16184,16185],{"class":4461},"\u002F\u002F Тривалість тесту\n",[3435,16187,16188],{"class":3437,"line":3475},[3435,16189,3532],{"class":4467},[3435,16191,16192],{"class":3437,"line":3481},[3435,16193,3460],{"emptyLinePlaceholder":3459},[3435,16195,16196,16198,16200,16203],{"class":3437,"line":3487},[3435,16197,11616],{"class":5657},[3435,16199,15840],{"class":5657},[3435,16201,16202],{"class":4677}," function",[3435,16204,16205],{"class":4467}," () {\n",[3435,16207,16208],{"class":3437,"line":3493},[3435,16209,16210],{"class":4461},"    \u002F\u002F Виклик API ендпоінту, який робить запит до PostgreSQL\n",[3435,16212,16213,16216,16219,16221,16224,16226,16228,16230,16233],{"class":3437,"line":3499},[3435,16214,16215],{"class":4677},"    const",[3435,16217,16218],{"class":16155}," res",[3435,16220,5300],{"class":4467},[3435,16222,16223],{"class":4524},"http",[3435,16225,3691],{"class":4467},[3435,16227,6688],{"class":4535},[3435,16229,4565],{"class":4467},[3435,16231,16232],{"class":4473},"'http:\u002F\u002F54.12.34.56:5000\u002Fapi\u002Fproducts'",[3435,16234,4701],{"class":4467},[3435,16236,16237],{"class":3437,"line":3505},[3435,16238,3460],{"emptyLinePlaceholder":3459},[3435,16240,16241,16244,16246,16249],{"class":3437,"line":3511},[3435,16242,16243],{"class":4535},"    check",[3435,16245,4565],{"class":4467},[3435,16247,16248],{"class":4524},"res",[3435,16250,16251],{"class":4467},", {\n",[3435,16253,16254,16257,16259,16261,16264,16266,16269,16272,16274,16277,16280,16282],{"class":3437,"line":3517},[3435,16255,16256],{"class":4473},"        'status is 200'",[3435,16258,14441],{"class":4524},[3435,16260,13422],{"class":4467},[3435,16262,16263],{"class":4524},"r",[3435,16265,5267],{"class":4467},[3435,16267,16268],{"class":4677},"=>",[3435,16270,16271],{"class":4524}," r",[3435,16273,3691],{"class":4467},[3435,16275,16276],{"class":4524},"status",[3435,16278,16279],{"class":4467}," === ",[3435,16281,6948],{"class":5337},[3435,16283,4491],{"class":4467},[3435,16285,16286],{"class":3437,"line":3523},[3435,16287,16288],{"class":4467},"    })\n",[3435,16290,16291],{"class":3437,"line":3529},[3435,16292,3460],{"emptyLinePlaceholder":3459},[3435,16294,16295,16298,16300,16303,16305],{"class":3437,"line":3535},[3435,16296,16297],{"class":4535},"    sleep",[3435,16299,4565],{"class":4467},[3435,16301,16302],{"class":5337},"0.1",[3435,16304,5267],{"class":4467},[3435,16306,16307],{"class":4461},"\u002F\u002F Коротка пауза між запитами\n",[3435,16309,16310],{"class":3437,"line":3540},[3435,16311,3532],{"class":4467},[3700,16313,16315],{"id":16314},"крок-3-аналіз-результатів-експерименту","Крок 3: Аналіз результатів експерименту",[3353,16317,16318,16319,16322],{},"Запустимо навантаження спочатку на застосунок, підключений безпосередньо до PostgreSQL (із встановленим лімітом ",[3414,16320,16321],{},"max_connections = 50"," на стороні СУБД).",[11641,16324,16327,16337,16341,16345,16348,16352,16362,16366,16375],{"title":16325,":cursor":16326},"Запуск тесту: Пряме підключення до RDS","false",[11645,16328,16330,3783,16333],{"className":16329},[3437],[3435,16331,11652],{"className":16332},[11651],[3359,16334,16336],{"className":16335},[12048],"k6 run load-test.js",[11645,16338,16340],{"className":16339},[3437],"execution: local",[11645,16342,16344],{"className":16343},[3437],"     scenarios: (100.00%) 1 scenario, 200 VUs, 30s executor",[11645,16346],{"className":16347},[3437],[11645,16349,16351],{"className":16350},[3437],"data_received..................: 1.4 MB  46 kB\u002Fs",[11645,16353,16355,16356,16361],{"className":16354},[3437],"http_req_failed................: ",[3435,16357,16360],{"className":16358},[16359,12048],"text-rose-400","28.42%","   ✓ 1421 \u002F 5000",[11645,16363,16365],{"className":16364},[3437],"http_req_duration..............: avg=342ms min=12ms med=110ms max=2400ms",[11645,16367,16369,16370,16374],{"className":16368},[3437],"checks.........................: ",[3435,16371,16373],{"className":16372},[16359,12048],"71.58%","   ✓ 3579 \u002F 5000",[11645,16376,16378],{"className":16377},[3437],[3435,16379,16381],{"className":16380},[16359,12048],"ERRO[0025] Database connections limit exceeded. Npgsql.PostgresException: 53300: remaining connection slots are reserved for non-replication superuser connections",[3353,16383,16384,16385,16388],{},"У першому експерименті майже ",[3359,16386,16387],{},"28% запитів завершилися збійними помилками 500",", оскільки пул з'єднань драйвера .NET на багатьох паралельних потоках швидко перевищив доступний ліміт підключень до PostgreSQL.",[3353,16390,16391,16392,16394],{},"Тепер переключимо рядок з'єднання застосунку на endpoint нашого ",[3359,16393,6449],{}," (який має такий самий формат, але інший DNS-хост) та повторимо аналогічний тест.",[11641,16396,16398,16407,16410,16413,16416,16420,16428,16432,16440],{"title":16397,":cursor":16326},"Запуск тесту: Підключення через RDS Proxy",[11645,16399,16401,3783,16404],{"className":16400},[3437],[3435,16402,11652],{"className":16403},[11651],[3359,16405,16336],{"className":16406},[12048],[11645,16408,16340],{"className":16409},[3437],[11645,16411,16344],{"className":16412},[3437],[11645,16414],{"className":16415},[3437],[11645,16417,16419],{"className":16418},[3437],"data_received..................: 2.1 MB  70 kB\u002Fs",[11645,16421,16355,16423,16427],{"className":16422},[3437],[3435,16424,16426],{"className":16425},[11687,12048],"0.00%","   ✓ 0 \u002F 6842",[11645,16429,16431],{"className":16430},[3437],"http_req_duration..............: avg=64ms  min=10ms med=48ms  max=312ms",[11645,16433,16369,16435,16439],{"className":16434},[3437],[3435,16436,16438],{"className":16437},[11687,12048],"100.00%"," ✓ 6842 \u002F 6842",[11645,16441,16443],{"className":16442},[3437],[3435,16444,16446],{"className":16445},[11687,12048],"SUCCESS: All requests completed without any connection errors. Connection multiplexing active.",[3353,16448,16449,16452,16453,16455],{},[3359,16450,16451],{},"Аналіз результатів:"," Завдяки використанню RDS Proxy відсоток збійних запитів скоротився до ",[3359,16454,16426],{},", при цьому середня тривалість запиту (Latency) зменшилася з 342ms до 64ms. Прокси-сервер утримував постійні мережеві сесії клієнтів та прозоро чергував їхні SQL-запити через невеликий фіксований пул підключень до PostgreSQL, виключаючи перевантаження рушія бази даних.",[3700,16457,16459],{"id":16458},"сценарій-4-аудит-та-оптимізація-повільних-запитів-через-cloudwatch-insights-еволюція-спостережливості","Сценарій 4: Аудит та оптимізація повільних запитів через CloudWatch Insights (Еволюція спостережливості)",[3353,16461,16462,16464,16465,16468],{},[3359,16463,13870],{}," Після того як наша система стала безпечною та масштабованою за допомогою RDS Proxy, фінальним етапом архітектурної еволюції є перехід до безперервного аналізу та оптимізації роботи бази даних у реальному часі. Це досягається за допомогою збору та автоматичного аналізу логів повільних запитів через ",[3359,16466,16467],{},"CloudWatch Logs Insights"," для виявлення та усунення вузьких місць продуктивності (наприклад, Full Table Scan).",[5406,16470,16472],{"id":16471},"механізм-фіксації-повільних-інструкцій-у-postgresql","Механізм фіксації повільних інструкцій у PostgreSQL",[3353,16474,16475,16476,16478],{},"Двигун PostgreSQL фіксує виконання кожної SQL-команди, яка триває довше, ніж визначено параметром конфігурації ",[3414,16477,5940],{}," (значення вказується у мілісекундах).",[3366,16480,16481,16491,16498],{},[3369,16482,16483,16490],{},[3359,16484,16485,16486,16489],{},"Значення ",[3414,16487,16488],{},"-1"," (за замовчуванням):"," логування виконання запитів повністю вимкнено.",[3369,16492,16493,16497],{},[3359,16494,16485,16495,14441],{},[3414,16496,9400],{}," логуються абсолютно всі запити, виконані у базі даних (викликає велике навантаження на I\u002FO та переповнення дисків логами, не рекомендується для production).",[3369,16499,16500,16508],{},[3359,16501,16485,16502,13090,16505,16507],{},[3414,16503,16504],{},"> 0",[3414,16506,5948],{},"):"," реєструються лише ті запити, час виконання яких перевищив 1 секунду.",[3353,16510,16511],{},"Логи СУБД записуються у локальні файли на інстансі RDS, але за допомогою механізму інтеграції Amazon RDS автоматично здійснює безперервний експорт (streaming) цих записів у сервіс CloudWatch Logs, де вони стають доступними для довгострокового зберігання та індексованого пошуку.",[5406,16513,16515],{"id":16514},"покрокова-реалізація-налаштування-та-аналізу","Покрокова реалізація налаштування та аналізу",[3700,16517,16519],{"id":16518},"крок-1-конфігурування-parameter-group-та-експорту-логів","Крок 1: Конфігурування Parameter Group та експорту логів",[3353,16521,16522],{},"Створіть кастомну Parameter Group для вашої версії PostgreSQL та змініть значення конфігурації.",[5205,16524,16525,16684],{},[5208,16526,16527],{"label":5210},[3426,16528,16530],{"className":5213,"code":16529,"language":5215,"meta":3431,"style":3431},"# 1. Створення кастомної Parameter Group\naws rds create-db-parameter-group \\\n    --db-parameter-group-name custom-postgres16-group \\\n    --db-parameter-group-family postgres16 \\\n    --description \"Кастомна група параметрів для оптимізації логування\" \\\n    --region eu-central-1\n\n# 2. Модифікація параметру тривалості запитів (log_min_duration_statement = 1000 мс)\n# Параметр є динамічним, тому зміни застосуються в рантаймі без перезапуску бази.\naws rds modify-db-parameter-group \\\n    --db-parameter-group-name custom-postgres16-group \\\n    --parameters \"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate\" \\\n    --region eu-central-1\n\n# 3. Асоціація Parameter Group з інстансом бази даних та увімкнення експорту логів\naws rds modify-db-instance \\\n    --db-instance-identifier my-dotnet-app-db \\\n    --db-parameter-group-name custom-postgres16-group \\\n    --cloudwatch-logs-export-configuration '{\"EnableLogTypes\":[\"postgresql\"]}' \\\n    --region eu-central-1\n",[3414,16531,16532,16537,16548,16558,16568,16577,16583,16587,16592,16597,16608,16616,16626,16632,16636,16641,16652,16660,16668,16678],{"__ignoreMap":3431},[3435,16533,16534],{"class":3437,"line":3438},[3435,16535,16536],{"class":4461},"# 1. Створення кастомної Parameter Group\n",[3435,16538,16539,16541,16543,16546],{"class":3437,"line":3444},[3435,16540,5227],{"class":4535},[3435,16542,5230],{"class":4473},[3435,16544,16545],{"class":4473}," create-db-parameter-group",[3435,16547,5237],{"class":5236},[3435,16549,16550,16553,16556],{"class":3437,"line":3450},[3435,16551,16552],{"class":4677},"    --db-parameter-group-name",[3435,16554,16555],{"class":4473}," custom-postgres16-group",[3435,16557,5237],{"class":5236},[3435,16559,16560,16563,16566],{"class":3437,"line":3456},[3435,16561,16562],{"class":4677},"    --db-parameter-group-family",[3435,16564,16565],{"class":4473}," postgres16",[3435,16567,5237],{"class":5236},[3435,16569,16570,16572,16575],{"class":3437,"line":3463},[3435,16571,7963],{"class":4677},[3435,16573,16574],{"class":4473}," \"Кастомна група параметрів для оптимізації логування\"",[3435,16576,5237],{"class":5236},[3435,16578,16579,16581],{"class":3437,"line":3469},[3435,16580,5275],{"class":4677},[3435,16582,5278],{"class":4473},[3435,16584,16585],{"class":3437,"line":3475},[3435,16586,3460],{"emptyLinePlaceholder":3459},[3435,16588,16589],{"class":3437,"line":3481},[3435,16590,16591],{"class":4461},"# 2. Модифікація параметру тривалості запитів (log_min_duration_statement = 1000 мс)\n",[3435,16593,16594],{"class":3437,"line":3487},[3435,16595,16596],{"class":4461},"# Параметр є динамічним, тому зміни застосуються в рантаймі без перезапуску бази.\n",[3435,16598,16599,16601,16603,16606],{"class":3437,"line":3493},[3435,16600,5227],{"class":4535},[3435,16602,5230],{"class":4473},[3435,16604,16605],{"class":4473}," modify-db-parameter-group",[3435,16607,5237],{"class":5236},[3435,16609,16610,16612,16614],{"class":3437,"line":3499},[3435,16611,16552],{"class":4677},[3435,16613,16555],{"class":4473},[3435,16615,5237],{"class":5236},[3435,16617,16618,16621,16624],{"class":3437,"line":3505},[3435,16619,16620],{"class":4677},"    --parameters",[3435,16622,16623],{"class":4473}," \"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate\"",[3435,16625,5237],{"class":5236},[3435,16627,16628,16630],{"class":3437,"line":3511},[3435,16629,5275],{"class":4677},[3435,16631,5278],{"class":4473},[3435,16633,16634],{"class":3437,"line":3517},[3435,16635,3460],{"emptyLinePlaceholder":3459},[3435,16637,16638],{"class":3437,"line":3523},[3435,16639,16640],{"class":4461},"# 3. Асоціація Parameter Group з інстансом бази даних та увімкнення експорту логів\n",[3435,16642,16643,16645,16647,16650],{"class":3437,"line":3529},[3435,16644,5227],{"class":4535},[3435,16646,5230],{"class":4473},[3435,16648,16649],{"class":4473}," modify-db-instance",[3435,16651,5237],{"class":5236},[3435,16653,16654,16656,16658],{"class":3437,"line":3535},[3435,16655,5242],{"class":4677},[3435,16657,8076],{"class":4473},[3435,16659,5237],{"class":5236},[3435,16661,16662,16664,16666],{"class":3437,"line":3540},[3435,16663,16552],{"class":4677},[3435,16665,16555],{"class":4473},[3435,16667,5237],{"class":5236},[3435,16669,16670,16673,16676],{"class":3437,"line":3546},[3435,16671,16672],{"class":4677},"    --cloudwatch-logs-export-configuration",[3435,16674,16675],{"class":4473}," '{\"EnableLogTypes\":[\"postgresql\"]}'",[3435,16677,5237],{"class":5236},[3435,16679,16680,16682],{"class":3437,"line":3552},[3435,16681,5275],{"class":4677},[3435,16683,5278],{"class":4473},[5208,16685,16686],{"label":5281},[3426,16687,16689],{"className":5284,"code":16688,"language":5286,"meta":3431,"style":3431},"# 1. Створення кастомної Parameter Group\naws rds create-db-parameter-group `\n    --db-parameter-group-name custom-postgres16-group `\n    --db-parameter-group-family postgres16 `\n    --description \"Кастомна група параметрів для оптимізації логування\" `\n    --region eu-central-1\n\n# 2. Модифікація параметру тривалості запитів (log_min_duration_statement = 1000 мс)\naws rds modify-db-parameter-group `\n    --db-parameter-group-name custom-postgres16-group `\n    --parameters \"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate\" `\n    --region eu-central-1\n\n# 3. Асоціація Parameter Group та увімкнення експорту логів\naws rds modify-db-instance `\n    --db-instance-identifier my-dotnet-app-db `\n    --db-parameter-group-name custom-postgres16-group `\n    --cloudwatch-logs-export-configuration '{\"EnableLogTypes\":[\"postgresql\"]}' `\n    --region eu-central-1\n",[3414,16690,16691,16695,16700,16710,16720,16729,16735,16739,16743,16748,16756,16766,16772,16776,16781,16786,16790,16798,16810],{"__ignoreMap":3431},[3435,16692,16693],{"class":3437,"line":3438},[3435,16694,16536],{"class":4461},[3435,16696,16697],{"class":3437,"line":3444},[3435,16698,16699],{"class":4467},"aws rds create-db-parameter-group `\n",[3435,16701,16702,16705,16707],{"class":3437,"line":3450},[3435,16703,16704],{"class":4467},"    --db-parameter-",[3435,16706,8454],{"class":4535},[3435,16708,16709],{"class":4467}," custom-postgres16-group `\n",[3435,16711,16712,16714,16717],{"class":3437,"line":3456},[3435,16713,16704],{"class":4467},[3435,16715,16716],{"class":4535},"group-family",[3435,16718,16719],{"class":4467}," postgres16 `\n",[3435,16721,16722,16724,16727],{"class":3437,"line":3463},[3435,16723,8514],{"class":4467},[3435,16725,16726],{"class":4473},"\"Кастомна група параметрів для оптимізації логування\"",[3435,16728,5329],{"class":4467},[3435,16730,16731,16733],{"class":3437,"line":3469},[3435,16732,5334],{"class":4467},[3435,16734,5338],{"class":5337},[3435,16736,16737],{"class":3437,"line":3475},[3435,16738,3460],{"emptyLinePlaceholder":3459},[3435,16740,16741],{"class":3437,"line":3481},[3435,16742,16591],{"class":4461},[3435,16744,16745],{"class":3437,"line":3487},[3435,16746,16747],{"class":4467},"aws rds modify-db-parameter-group `\n",[3435,16749,16750,16752,16754],{"class":3437,"line":3493},[3435,16751,16704],{"class":4467},[3435,16753,8454],{"class":4535},[3435,16755,16709],{"class":4467},[3435,16757,16758,16761,16764],{"class":3437,"line":3499},[3435,16759,16760],{"class":4467},"    --parameters ",[3435,16762,16763],{"class":4473},"\"ParameterName=log_min_duration_statement,ParameterValue=1000,ApplyMethod=immediate\"",[3435,16765,5329],{"class":4467},[3435,16767,16768,16770],{"class":3437,"line":3505},[3435,16769,5334],{"class":4467},[3435,16771,5338],{"class":5337},[3435,16773,16774],{"class":3437,"line":3511},[3435,16775,3460],{"emptyLinePlaceholder":3459},[3435,16777,16778],{"class":3437,"line":3517},[3435,16779,16780],{"class":4461},"# 3. Асоціація Parameter Group та увімкнення експорту логів\n",[3435,16782,16783],{"class":3437,"line":3523},[3435,16784,16785],{"class":4467},"aws rds modify-db-instance `\n",[3435,16787,16788],{"class":3437,"line":3529},[3435,16789,8616],{"class":4467},[3435,16791,16792,16794,16796],{"class":3437,"line":3535},[3435,16793,16704],{"class":4467},[3435,16795,8454],{"class":4535},[3435,16797,16709],{"class":4467},[3435,16799,16800,16803,16806,16808],{"class":3437,"line":3540},[3435,16801,16802],{"class":4467},"    --cloudwatch-logs-",[3435,16804,16805],{"class":4535},"export-configuration",[3435,16807,16675],{"class":4473},[3435,16809,5329],{"class":4467},[3435,16811,16812,16814],{"class":3437,"line":3546},[3435,16813,5334],{"class":4467},[3435,16815,5338],{"class":5337},[3700,16817,16819],{"id":16818},"крок-2-емуляція-повільного-запиту-в-застосунку-на-net","Крок 2: Емуляція повільного запиту в застосунку на .NET",[3353,16821,16822],{},"Створимо у нашому контролері .NET Web API метод, який навмисно ініціює повільне виконання запиту на великій таблиці. Зазвичай це відбувається при скануванні великої кількості рядків без використання індексів (Sequential Scan).",[3353,16824,16825,16826,14441],{},"Додамо такий метод до ",[3414,16827,11299],{},[3426,16829,16831],{"className":4452,"code":16830,"language":4454,"meta":3431,"style":3431},"[HttpGet(\"search-slow\")]\npublic async Task\u003CIActionResult> GetSlowProducts([FromQuery] string keyword)\n{\n    \u002F\u002F Пошук за допомогою LIKE з двома wildcard (%keyword%) на неіндексованому стовпці Name.\n    \u002F\u002F Це змушує PostgreSQL виконувати Seq Scan (повне зчитування таблиці з диска).\n    \u002F\u002F Додатково імітуємо навантаження за допомогою сортування та великого обсягу даних.\n    var products = await db.Products\n        .Where(p => EF.Functions.Like(p.Name, $\"%{keyword}%\"))\n        .OrderByDescending(p => p.Price)\n        .ToListAsync();\n\n    return Ok(products);\n}\n",[3414,16832,16833,16846,16879,16883,16888,16893,16898,16916,16968,16989,16997,17001,17016],{"__ignoreMap":3431},[3435,16834,16835,16837,16839,16841,16844],{"class":3437,"line":3438},[3435,16836,11306],{"class":4467},[3435,16838,11359],{"class":4542},[3435,16840,4565],{"class":4467},[3435,16842,16843],{"class":4473},"\"search-slow\"",[3435,16845,11327],{"class":4467},[3435,16847,16848,16850,16852,16854,16856,16858,16860,16863,16866,16869,16872,16874,16877],{"class":3437,"line":3444},[3435,16849,4678],{"class":4677},[3435,16851,4718],{"class":4677},[3435,16853,4721],{"class":4542},[3435,16855,4539],{"class":4467},[3435,16857,11374],{"class":4542},[3435,16859,6811],{"class":4467},[3435,16861,16862],{"class":4535},"GetSlowProducts",[3435,16864,16865],{"class":4467},"([",[3435,16867,16868],{"class":4542},"FromQuery",[3435,16870,16871],{"class":4467},"] ",[3435,16873,5728],{"class":4677},[3435,16875,16876],{"class":4524}," keyword",[3435,16878,4701],{"class":4467},[3435,16880,16881],{"class":3437,"line":3450},[3435,16882,4468],{"class":4467},[3435,16884,16885],{"class":3437,"line":3456},[3435,16886,16887],{"class":4461},"    \u002F\u002F Пошук за допомогою LIKE з двома wildcard (%keyword%) на неіндексованому стовпці Name.\n",[3435,16889,16890],{"class":3437,"line":3463},[3435,16891,16892],{"class":4461},"    \u002F\u002F Це змушує PostgreSQL виконувати Seq Scan (повне зчитування таблиці з диска).\n",[3435,16894,16895],{"class":3437,"line":3469},[3435,16896,16897],{"class":4461},"    \u002F\u002F Додатково імітуємо навантаження за допомогою сортування та великого обсягу даних.\n",[3435,16899,16900,16902,16905,16907,16909,16911,16913],{"class":3437,"line":3475},[3435,16901,7066],{"class":4677},[3435,16903,16904],{"class":4524}," products",[3435,16906,5300],{"class":4467},[3435,16908,11391],{"class":4677},[3435,16910,4691],{"class":4524},[3435,16912,3691],{"class":4467},[3435,16914,16915],{"class":4524},"Products\n",[3435,16917,16918,16921,16924,16926,16928,16930,16933,16935,16938,16940,16943,16945,16947,16949,16951,16953,16956,16958,16961,16963,16966],{"class":3437,"line":3481},[3435,16919,16920],{"class":4467},"        .",[3435,16922,16923],{"class":4535},"Where",[3435,16925,4565],{"class":4467},[3435,16927,3353],{"class":4524},[3435,16929,6847],{"class":4467},[3435,16931,16932],{"class":4524},"EF",[3435,16934,3691],{"class":4467},[3435,16936,16937],{"class":4524},"Functions",[3435,16939,3691],{"class":4467},[3435,16941,16942],{"class":4535},"Like",[3435,16944,4565],{"class":4467},[3435,16946,3353],{"class":4524},[3435,16948,3691],{"class":4467},[3435,16950,6938],{"class":4524},[3435,16952,3417],{"class":4467},[3435,16954,16955],{"class":4473},"$\"%",[3435,16957,5818],{"class":5817},[3435,16959,16960],{"class":4524},"keyword",[3435,16962,5824],{"class":5817},[3435,16964,16965],{"class":4473},"%\"",[3435,16967,4640],{"class":4467},[3435,16969,16970,16972,16975,16977,16979,16981,16983,16985,16987],{"class":3437,"line":3487},[3435,16971,16920],{"class":4467},[3435,16973,16974],{"class":4535},"OrderByDescending",[3435,16976,4565],{"class":4467},[3435,16978,3353],{"class":4524},[3435,16980,6847],{"class":4467},[3435,16982,3353],{"class":4524},[3435,16984,3691],{"class":4467},[3435,16986,6976],{"class":4524},[3435,16988,4701],{"class":4467},[3435,16990,16991,16993,16995],{"class":3437,"line":3493},[3435,16992,16920],{"class":4467},[3435,16994,4757],{"class":4535},[3435,16996,4760],{"class":4467},[3435,16998,16999],{"class":3437,"line":3499},[3435,17000,3460],{"emptyLinePlaceholder":3459},[3435,17002,17003,17006,17009,17011,17014],{"class":3437,"line":3505},[3435,17004,17005],{"class":5657},"    return",[3435,17007,17008],{"class":4535}," Ok",[3435,17010,4565],{"class":4467},[3435,17012,17013],{"class":4524},"products",[3435,17015,4816],{"class":4467},[3435,17017,17018],{"class":3437,"line":3511},[3435,17019,3532],{"class":4467},[3353,17021,17022],{},"Викличемо цей ендпоінт, передавши запит, що змусить базу даних сканувати таблицю:",[3426,17024,17026],{"className":5213,"code":17025,"language":5215,"meta":3431,"style":3431},"curl \"http:\u002F\u002F54.12.34.56:5000\u002Fapi\u002Fproducts\u002Fsearch-slow?keyword=special-device-2026\"\n",[3414,17027,17028],{"__ignoreMap":3431},[3435,17029,17030,17032],{"class":3437,"line":3438},[3435,17031,9131],{"class":4535},[3435,17033,17034],{"class":4473}," \"http:\u002F\u002F54.12.34.56:5000\u002Fapi\u002Fproducts\u002Fsearch-slow?keyword=special-device-2026\"\n",[3700,17036,17038],{"id":17037},"крок-3-аналіз-та-виявлення-повільних-запитів-через-cloudwatch-insights","Крок 3: Аналіз та виявлення повільних запитів через CloudWatch Insights",[3353,17040,17041,17042,17045],{},"Після того, як запит виконувався понад 1 секунду, у лог-групі CloudWatch ",[3414,17043,17044],{},"\u002Faws\u002Frds\u002Finstance\u002Fmy-dotnet-app-db\u002Fpostgresql"," з'явиться відповідний запис.",[3353,17047,17048,17049,17051,17052,17055],{},"Для виявлення та агрегації таких запитів скористаємося інструментом ",[3359,17050,16467],{},". Відкрийте консоль CloudWatch → ",[3359,17053,17054],{},"Logs Insights",", оберіть лог-групу вашої бази даних та виконайте такий аналітичний запит:",[3426,17057,17061],{"className":17058,"code":17059,"language":17060,"meta":3431,"style":3431},"language-graphql shiki shiki-themes light-plus dark-plus dark-plus","# Фільтрація рядків логів, що містять інформацію про тривалість виконання запитів\nfilter @message like \"duration:\"\n# Виділення значень тривалості виконання та самого тексту SQL-запиту за допомогою регулярного виразу\n| parse @message \"duration: * ms  statement: *\" as duration, sql_statement\n# Конвертація тривалості у числовий формат\n| fields toDouble(duration) as duration_ms\n# Фільтруємо лише запити, що тривали більше 1000 мс (додатковий фільтр)\n| filter duration_ms > 1000\n# Сортуємо результати за спаданням тривалості\n| sort duration_ms desc\n# Відображаємо час події, тривалість та саму інструкцію\n| display @timestamp, duration_ms, sql_statement\n| limit 20\n","graphql",[3414,17062,17063,17068,17082,17087,17111,17116,17135,17140,17152,17157,17169,17174,17193],{"__ignoreMap":3431},[3435,17064,17065],{"class":3437,"line":3438},[3435,17066,17067],{"class":4461},"# Фільтрація рядків логів, що містять інформацію про тривалість виконання запитів\n",[3435,17069,17070,17073,17076,17079],{"class":3437,"line":3444},[3435,17071,17072],{"class":4535},"filter",[3435,17074,17075],{"class":4535}," @message",[3435,17077,17078],{"class":4535}," like",[3435,17080,17081],{"class":4461}," \"duration:\"\n",[3435,17083,17084],{"class":3437,"line":3450},[3435,17085,17086],{"class":4461},"# Виділення значень тривалості виконання та самого тексту SQL-запиту за допомогою регулярного виразу\n",[3435,17088,17089,17092,17095,17097,17100,17103,17106,17108],{"class":3437,"line":3456},[3435,17090,17091],{"class":4467},"| ",[3435,17093,17094],{"class":4535},"parse",[3435,17096,17075],{"class":4535},[3435,17098,17099],{"class":4461}," \"duration: * ms  statement: *\"",[3435,17101,17102],{"class":4535}," as",[3435,17104,17105],{"class":4535}," duration",[3435,17107,3417],{"class":4467},[3435,17109,17110],{"class":4535},"sql_statement\n",[3435,17112,17113],{"class":3437,"line":3463},[3435,17114,17115],{"class":4461},"# Конвертація тривалості у числовий формат\n",[3435,17117,17118,17120,17123,17126,17129,17132],{"class":3437,"line":3469},[3435,17119,17091],{"class":4467},[3435,17121,17122],{"class":4535},"fields",[3435,17124,17125],{"class":4535}," toDouble",[3435,17127,17128],{"class":4467},"(duration) ",[3435,17130,17131],{"class":4535},"as",[3435,17133,17134],{"class":4535}," duration_ms\n",[3435,17136,17137],{"class":3437,"line":3475},[3435,17138,17139],{"class":4461},"# Фільтруємо лише запити, що тривали більше 1000 мс (додатковий фільтр)\n",[3435,17141,17142,17144,17146,17149],{"class":3437,"line":3481},[3435,17143,17091],{"class":4467},[3435,17145,17072],{"class":4535},[3435,17147,17148],{"class":4535}," duration_ms",[3435,17150,17151],{"class":4467}," > 1000\n",[3435,17153,17154],{"class":3437,"line":3487},[3435,17155,17156],{"class":4461},"# Сортуємо результати за спаданням тривалості\n",[3435,17158,17159,17161,17164,17166],{"class":3437,"line":3493},[3435,17160,17091],{"class":4467},[3435,17162,17163],{"class":4535},"sort",[3435,17165,17148],{"class":4535},[3435,17167,17168],{"class":4535}," desc\n",[3435,17170,17171],{"class":3437,"line":3499},[3435,17172,17173],{"class":4461},"# Відображаємо час події, тривалість та саму інструкцію\n",[3435,17175,17176,17178,17181,17184,17186,17189,17191],{"class":3437,"line":3505},[3435,17177,17091],{"class":4467},[3435,17179,17180],{"class":4535},"display",[3435,17182,17183],{"class":4535}," @timestamp",[3435,17185,3417],{"class":4467},[3435,17187,17188],{"class":4535},"duration_ms",[3435,17190,3417],{"class":4467},[3435,17192,17110],{"class":4535},[3435,17194,17195,17197,17200],{"class":3437,"line":3511},[3435,17196,17091],{"class":4467},[3435,17198,17199],{"class":4535},"limit",[3435,17201,17202],{"class":4467}," 20\n",[11641,17204,17206,17210,17214,17218,17221,17230,17239,17242],{"title":17205,":cursor":16326},"CloudWatch Logs Insights Results",[11645,17207,17209],{"className":17208},[3437],"# Query results (running on \u002Faws\u002Frds\u002Finstance\u002Fmy-dotnet-app-db\u002Fpostgresql)",[11645,17211,17213],{"className":17212},[3437],"--------------------------------------------------------------------------------------",[11645,17215,17217],{"className":17216},[3437],"| @timestamp             | duration_ms | sql_statement                                 |",[11645,17219,17213],{"className":17220},[3437],[11645,17222,17224,17225,17229],{"className":17223},[3437],"| 2026-06-01 14:02:15.120| ",[3435,17226,17228],{"className":17227},[16359,12048],"2451.20","     | SELECT p.\"Id\", p.\"Name\", p.\"Price\" FROM \"Products\" AS p WHERE p.\"Name\" LIKE '%special-device-2026%' ORDER BY p.\"Price\" DESC |",[11645,17231,17233,17234,17238],{"className":17232},[3437],"| 2026-06-01 14:02:18.450| ",[3435,17235,17237],{"className":17236},[16359,12048],"1892.40","     | SELECT p.\"Id\", p.\"Name\", p.\"Price\" FROM \"Products\" AS p WHERE p.\"Name\" LIKE '%test-equipment%' ORDER BY p.\"Price\" DESC |",[11645,17240,17213],{"className":17241},[3437],[11645,17243,17245,17246],{"className":17244},[3437],"Showing 2 of 2 entries. ",[3435,17247,17249],{"className":17248},[11687,12048],"Query execution completed successfully.",[3353,17251,17252,17255],{},[3359,17253,17254],{},"Результати аналізу:"," Logs Insights дозволяє точно локалізувати проблемну SQL-інструкцію, згенеровану Entity Framework Core, її точний час запуску та тривалість виконання (2451.20 мс). На основі цього аналізу розробник може прийняти рішення про необхідність додавання індексу (наприклад, B-Tree або GIN індексу для повнотекстового пошуку) або оптимізації логіки запиту.",[3348,17257,17259],{"id":17258},"практичний-приклад-розгортання-та-оптимізація-кластера-amazon-aurora-postgresql-serverless-v2-у-net","Практичний приклад: Розгортання та оптимізація кластера Amazon Aurora PostgreSQL Serverless v2 у .NET",[3679,17261],{},[3353,17263,17264,17265,17268],{},"У цьому практичному прикладі розглянемо процес розгортання та конфігурування хмарно-орієнтованого кластера баз даних ",[3359,17266,17267],{},"Amazon Aurora PostgreSQL Serverless v2",", а також налаштування розподілу потоків читання\u002Fзапису в застосунку на базі .NET.",[3700,17270,17272],{"id":17271},"архітектура-кластера-aurora","Архітектура кластера Aurora",[3353,17274,17275],{},"На відміну від стандартного інстансу RDS, Aurora завжди створює логічний кластер (DB Cluster), який об'єднує процесорні ресурси (Compute) та розподілений диск (Storage). Клієнти не підключаються безпосередньо до конкретних серверів. Замість цього використовуються віртуальні endpoints на рівні кластера:",[3366,17277,17278,17284],{},[3369,17279,17280,17283],{},[3359,17281,17282],{},"Writer Endpoint:"," Точка входу для операцій запису. Вона динамічно вказує на поточний первинний (primary) інстанс. У разі аварії primary інстансу, Aurora автоматично призначає інший інстанс новим writer, і DNS-запис writer endpoint миттєво оновлюється.",[3369,17285,17286,17289],{},[3359,17287,17288],{},"Reader Endpoint:"," Точка входу для операцій читання. Вона розподіляє вхідні SELECT-запити між усіма доступними репліками читання за принципом Round-Robin (балансування навантаження).",[3423,17291,17292],{},[3426,17293,17295],{"className":3428,"code":17294,"language":3430,"meta":3431,"style":3431},"@startuml\nskinparam style plain\nskinparam backgroundColor #ffffff\nskinparam defaultFontName \"Helvetica\"\n\nrectangle \"AWS Region (eu-central-1)\" {\n    rectangle \"VPC (10.0.0.0\u002F16)\" {\n        rectangle \"Endpoints кластера Aurora\" as ENDPOINTS #fef3c7 {\n            interface \"Writer Endpoint\\n(my-cluster.cluster-xxx.rds.amazonaws.com)\" as WRITER_EP\n            interface \"Reader Endpoint\\n(my-cluster.cluster-ro-xxx.rds.amazonaws.com)\" as READER_EP\n        }\n\n        rectangle \"Availability Zone A\" as AZ_A #dbeafe {\n            node \"Writer Instance (db.serverless)\\n(0.5 - 4.0 ACU)\" as WRITER\n        }\n        rectangle \"Availability Zone B\" as AZ_B #d1fae5 {\n            node \"Reader Instance (db.serverless)\\n(0.5 - 4.0 ACU)\" as READER\n        }\n\n        WRITER_EP --> WRITER : Маршрутизація записів\n        READER_EP --> READER : Балансування читань\n\n        database \"Aurora Shared Storage Volume\\n(Розподілене сховище: 6 копій у 3 AZ)\" as STORE #f3e8ff\n\n        WRITER --> STORE : Читання \u002F Запис\n        READER --> STORE : Тільки читання\n    }\n}\n@enduml\n",[3414,17296,17297,17301,17305,17309,17314,17318,17323,17328,17333,17338,17343,17347,17351,17356,17361,17365,17370,17375,17379,17383,17388,17393,17397,17402,17406,17411,17416,17420,17424],{"__ignoreMap":3431},[3435,17298,17299],{"class":3437,"line":3438},[3435,17300,3441],{},[3435,17302,17303],{"class":3437,"line":3444},[3435,17304,3447],{},[3435,17306,17307],{"class":3437,"line":3450},[3435,17308,3453],{},[3435,17310,17311],{"class":3437,"line":3456},[3435,17312,17313],{},"skinparam defaultFontName \"Helvetica\"\n",[3435,17315,17316],{"class":3437,"line":3463},[3435,17317,3460],{"emptyLinePlaceholder":3459},[3435,17319,17320],{"class":3437,"line":3469},[3435,17321,17322],{},"rectangle \"AWS Region (eu-central-1)\" {\n",[3435,17324,17325],{"class":3437,"line":3475},[3435,17326,17327],{},"    rectangle \"VPC (10.0.0.0\u002F16)\" {\n",[3435,17329,17330],{"class":3437,"line":3481},[3435,17331,17332],{},"        rectangle \"Endpoints кластера Aurora\" as ENDPOINTS #fef3c7 {\n",[3435,17334,17335],{"class":3437,"line":3487},[3435,17336,17337],{},"            interface \"Writer Endpoint\\n(my-cluster.cluster-xxx.rds.amazonaws.com)\" as WRITER_EP\n",[3435,17339,17340],{"class":3437,"line":3493},[3435,17341,17342],{},"            interface \"Reader Endpoint\\n(my-cluster.cluster-ro-xxx.rds.amazonaws.com)\" as READER_EP\n",[3435,17344,17345],{"class":3437,"line":3499},[3435,17346,7449],{},[3435,17348,17349],{"class":3437,"line":3505},[3435,17350,3460],{"emptyLinePlaceholder":3459},[3435,17352,17353],{"class":3437,"line":3511},[3435,17354,17355],{},"        rectangle \"Availability Zone A\" as AZ_A #dbeafe {\n",[3435,17357,17358],{"class":3437,"line":3517},[3435,17359,17360],{},"            node \"Writer Instance (db.serverless)\\n(0.5 - 4.0 ACU)\" as WRITER\n",[3435,17362,17363],{"class":3437,"line":3523},[3435,17364,7449],{},[3435,17366,17367],{"class":3437,"line":3529},[3435,17368,17369],{},"        rectangle \"Availability Zone B\" as AZ_B #d1fae5 {\n",[3435,17371,17372],{"class":3437,"line":3535},[3435,17373,17374],{},"            node \"Reader Instance (db.serverless)\\n(0.5 - 4.0 ACU)\" as READER\n",[3435,17376,17377],{"class":3437,"line":3540},[3435,17378,7449],{},[3435,17380,17381],{"class":3437,"line":3546},[3435,17382,3460],{"emptyLinePlaceholder":3459},[3435,17384,17385],{"class":3437,"line":3552},[3435,17386,17387],{},"        WRITER_EP --> WRITER : Маршрутизація записів\n",[3435,17389,17390],{"class":3437,"line":3558},[3435,17391,17392],{},"        READER_EP --> READER : Балансування читань\n",[3435,17394,17395],{"class":3437,"line":3563},[3435,17396,3460],{"emptyLinePlaceholder":3459},[3435,17398,17399],{"class":3437,"line":3569},[3435,17400,17401],{},"        database \"Aurora Shared Storage Volume\\n(Розподілене сховище: 6 копій у 3 AZ)\" as STORE #f3e8ff\n",[3435,17403,17404],{"class":3437,"line":3575},[3435,17405,3460],{"emptyLinePlaceholder":3459},[3435,17407,17408],{"class":3437,"line":3580},[3435,17409,17410],{},"        WRITER --> STORE : Читання \u002F Запис\n",[3435,17412,17413],{"class":3437,"line":3586},[3435,17414,17415],{},"        READER --> STORE : Тільки читання\n",[3435,17417,17418],{"class":3437,"line":3592},[3435,17419,3490],{},[3435,17421,17422],{"class":3437,"line":3597},[3435,17423,3532],{},[3435,17425,17426],{"class":3437,"line":3603},[3435,17427,3640],{},[5406,17429,17431],{"id":17430},"покроковий-деплой-та-конфігурування","Покроковий деплой та конфігурування",[3700,17433,17435],{"id":17434},"крок-1-створення-та-конфігурування-db-cluster-writer-reader","Крок 1: Створення та конфігурування DB Cluster (Writer + Reader)",[3353,17437,17438],{},"Створимо кластер баз даних Aurora з двома безсерверними інстансами: первинним (Writer) та додатковим для масштабування читань (Reader). Діапазон масштабування встановимо від 0.5 до 4.0 ACU.",[5205,17440,17441,17569,17883],{},[5208,17442,17443],{"label":7569},[3913,17444,17445,17455,17460,17465,17471,17476,17497,17521,17527,17551,17564],{},[3369,17446,17447,17448,7577,17450,7577,17453,3691],{},"Перейдіть до сервісу ",[3359,17449,5675],{},[3359,17451,17452],{},"Databases",[3359,17454,7580],{},[3369,17456,17457,17459],{},[3359,17458,7585],{}," Standard create.",[3369,17461,17462,17464],{},[3359,17463,7591],{}," Amazon Aurora.",[3369,17466,17467,17470],{},[3359,17468,17469],{},"Edition:"," Amazon Aurora PostgreSQL-Compatible Edition (версія 16.x).",[3369,17472,17473,17475],{},[3359,17474,7600],{}," Dev\u002FTest.",[3369,17477,17478,17481],{},[3359,17479,17480],{},"Settings:",[3366,17482,17483,17489,17494],{},[3369,17484,17485,17486],{},"DB cluster identifier: ",[3414,17487,17488],{},"aurora-dotnet-cluster",[3369,17490,17491,17492],{},"Master username: ",[3414,17493,7623],{},[3369,17495,17496],{},"Master password: задайте надійний пароль.",[3369,17498,17499,17502],{},[3359,17500,17501],{},"Instance configuration:",[3366,17503,17504,17511],{},[3369,17505,17506,17507,17510],{},"DB instance class: ",[3359,17508,17509],{},"Serverless"," (Aurora Serverless v2).",[3369,17512,17513,17514,17517,17518,3691],{},"Встановіть ліміти: Minimum ACUs: ",[3414,17515,17516],{},"0.5",", Maximum ACUs: ",[3414,17519,17520],{},"4.0",[3369,17522,17523,17526],{},[3359,17524,17525],{},"Availability & durability:"," Create an Aurora Replica or Reader node in a different AZ (для забезпечення відмовостійкості та тестування Reader endpoint).",[3369,17528,17529,17531],{},[3359,17530,7656],{},[3366,17532,17533,17536,17542,17546],{},[3369,17534,17535],{},"VPC: Default VPC.",[3369,17537,17538,17539,3691],{},"DB Subnet Group: Оберіть створену раніше ",[3414,17540,17541],{},"my-dotnet-app-subnet-group",[3369,17543,7664,17544,3691],{},[3359,17545,5364],{},[3369,17547,17548,17549,3691],{},"VPC security group: Оберіть створену раніше ",[3414,17550,7672],{},[3369,17552,17553,17556],{},[3359,17554,17555],{},"Additional configuration:",[3366,17557,17558],{},[3369,17559,17560,17561,3691],{},"Initial database name: ",[3414,17562,17563],{},"auroradb",[3369,17565,9517,17566,17568],{},[3359,17567,7580],{}," (створення кластера триває приблизно 8–12 хвилин).",[5208,17570,17571],{"label":5210},[3426,17572,17574],{"className":5213,"code":17573,"language":5215,"meta":3431,"style":3431},"# 1. Створення логічного кластера Aurora з лімітами Serverless v2\naws rds create-db-cluster \\\n    --db-cluster-identifier aurora-dotnet-cluster \\\n    --engine aurora-postgresql \\\n    --engine-version \"16.1\" \\\n    --master-username postgres \\\n    --master-user-password \"YourSecurePassword123!\" \\\n    --db-subnet-group-name my-dotnet-app-subnet-group \\\n    --vpc-security-group-ids $SG_RDS \\\n    --serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=4.0 \\\n    --database-name auroradb \\\n    --region eu-central-1\n\n# Дочекайтеся статусу створення кластера\naws rds wait db-cluster-available \\\n    --db-cluster-identifier aurora-dotnet-cluster --region eu-central-1\n\n# 2. Створення Writer інстансу всередині кластера\naws rds create-db-instance \\\n    --db-instance-identifier aurora-writer-instance \\\n    --db-cluster-identifier aurora-dotnet-cluster \\\n    --db-instance-class db.serverless \\\n    --engine aurora-postgresql \\\n    --region eu-central-1\n\n# 3. Створення Reader інстансу всередині кластера\naws rds create-db-instance \\\n    --db-instance-identifier aurora-reader-instance \\\n    --db-cluster-identifier aurora-dotnet-cluster \\\n    --db-instance-class db.serverless \\\n    --engine aurora-postgresql \\\n    --region eu-central-1\n\n# Дочекайтеся працездатності інстансів\naws rds wait db-instance-available \\\n    --db-instance-identifier aurora-writer-instance --region eu-central-1\naws rds wait db-instance-available \\\n    --db-instance-identifier aurora-reader-instance --region eu-central-1\n",[3414,17575,17576,17581,17592,17602,17611,17621,17629,17637,17645,17653,17665,17675,17681,17685,17690,17703,17713,17717,17722,17732,17741,17749,17758,17766,17772,17776,17781,17791,17800,17808,17816,17824,17830,17834,17839,17851,17861,17873],{"__ignoreMap":3431},[3435,17577,17578],{"class":3437,"line":3438},[3435,17579,17580],{"class":4461},"# 1. Створення логічного кластера Aurora з лімітами Serverless v2\n",[3435,17582,17583,17585,17587,17590],{"class":3437,"line":3444},[3435,17584,5227],{"class":4535},[3435,17586,5230],{"class":4473},[3435,17588,17589],{"class":4473}," create-db-cluster",[3435,17591,5237],{"class":5236},[3435,17593,17594,17597,17600],{"class":3437,"line":3450},[3435,17595,17596],{"class":4677},"    --db-cluster-identifier",[3435,17598,17599],{"class":4473}," aurora-dotnet-cluster",[3435,17601,5237],{"class":5236},[3435,17603,17604,17606,17609],{"class":3437,"line":3456},[3435,17605,8093],{"class":4677},[3435,17607,17608],{"class":4473}," aurora-postgresql",[3435,17610,5237],{"class":5236},[3435,17612,17613,17616,17619],{"class":3437,"line":3463},[3435,17614,17615],{"class":4677},"    --engine-version",[3435,17617,17618],{"class":4473}," \"16.1\"",[3435,17620,5237],{"class":5236},[3435,17622,17623,17625,17627],{"class":3437,"line":3469},[3435,17624,8109],{"class":4677},[3435,17626,8096],{"class":4473},[3435,17628,5237],{"class":5236},[3435,17630,17631,17633,17635],{"class":3437,"line":3475},[3435,17632,8118],{"class":4677},[3435,17634,8121],{"class":4473},[3435,17636,5237],{"class":5236},[3435,17638,17639,17641,17643],{"class":3437,"line":3481},[3435,17640,7858],{"class":4677},[3435,17642,7861],{"class":4473},[3435,17644,5237],{"class":5236},[3435,17646,17647,17649,17651],{"class":3437,"line":3487},[3435,17648,8156],{"class":4677},[3435,17650,8023],{"class":4524},[3435,17652,5237],{"class":5236},[3435,17654,17655,17658,17661,17663],{"class":3437,"line":3493},[3435,17656,17657],{"class":4677},"    --serverless-v2-scaling-configuration",[3435,17659,17660],{"class":4473}," MinCapacity=0.5,MaxCapacity=",[3435,17662,17520],{"class":5337},[3435,17664,5237],{"class":5236},[3435,17666,17667,17670,17673],{"class":3437,"line":3499},[3435,17668,17669],{"class":4677},"    --database-name",[3435,17671,17672],{"class":4473}," auroradb",[3435,17674,5237],{"class":5236},[3435,17676,17677,17679],{"class":3437,"line":3505},[3435,17678,5275],{"class":4677},[3435,17680,5278],{"class":4473},[3435,17682,17683],{"class":3437,"line":3511},[3435,17684,3460],{"emptyLinePlaceholder":3459},[3435,17686,17687],{"class":3437,"line":3517},[3435,17688,17689],{"class":4461},"# Дочекайтеся статусу створення кластера\n",[3435,17691,17692,17694,17696,17698,17701],{"class":3437,"line":3523},[3435,17693,5227],{"class":4535},[3435,17695,5230],{"class":4473},[3435,17697,8210],{"class":4473},[3435,17699,17700],{"class":4473}," db-cluster-available",[3435,17702,5237],{"class":5236},[3435,17704,17705,17707,17709,17711],{"class":3437,"line":3529},[3435,17706,17596],{"class":4677},[3435,17708,17599],{"class":4473},[3435,17710,7748],{"class":4677},[3435,17712,5278],{"class":4473},[3435,17714,17715],{"class":3437,"line":3535},[3435,17716,3460],{"emptyLinePlaceholder":3459},[3435,17718,17719],{"class":3437,"line":3540},[3435,17720,17721],{"class":4461},"# 2. Створення Writer інстансу всередині кластера\n",[3435,17723,17724,17726,17728,17730],{"class":3437,"line":3546},[3435,17725,5227],{"class":4535},[3435,17727,5230],{"class":4473},[3435,17729,8067],{"class":4473},[3435,17731,5237],{"class":5236},[3435,17733,17734,17736,17739],{"class":3437,"line":3552},[3435,17735,5242],{"class":4677},[3435,17737,17738],{"class":4473}," aurora-writer-instance",[3435,17740,5237],{"class":5236},[3435,17742,17743,17745,17747],{"class":3437,"line":3558},[3435,17744,17596],{"class":4677},[3435,17746,17599],{"class":4473},[3435,17748,5237],{"class":5236},[3435,17750,17751,17753,17756],{"class":3437,"line":3563},[3435,17752,8083],{"class":4677},[3435,17754,17755],{"class":4473}," db.serverless",[3435,17757,5237],{"class":5236},[3435,17759,17760,17762,17764],{"class":3437,"line":3569},[3435,17761,8093],{"class":4677},[3435,17763,17608],{"class":4473},[3435,17765,5237],{"class":5236},[3435,17767,17768,17770],{"class":3437,"line":3575},[3435,17769,5275],{"class":4677},[3435,17771,5278],{"class":4473},[3435,17773,17774],{"class":3437,"line":3580},[3435,17775,3460],{"emptyLinePlaceholder":3459},[3435,17777,17778],{"class":3437,"line":3586},[3435,17779,17780],{"class":4461},"# 3. Створення Reader інстансу всередині кластера\n",[3435,17782,17783,17785,17787,17789],{"class":3437,"line":3592},[3435,17784,5227],{"class":4535},[3435,17786,5230],{"class":4473},[3435,17788,8067],{"class":4473},[3435,17790,5237],{"class":5236},[3435,17792,17793,17795,17798],{"class":3437,"line":3597},[3435,17794,5242],{"class":4677},[3435,17796,17797],{"class":4473}," aurora-reader-instance",[3435,17799,5237],{"class":5236},[3435,17801,17802,17804,17806],{"class":3437,"line":3603},[3435,17803,17596],{"class":4677},[3435,17805,17599],{"class":4473},[3435,17807,5237],{"class":5236},[3435,17809,17810,17812,17814],{"class":3437,"line":3609},[3435,17811,8083],{"class":4677},[3435,17813,17755],{"class":4473},[3435,17815,5237],{"class":5236},[3435,17817,17818,17820,17822],{"class":3437,"line":3615},[3435,17819,8093],{"class":4677},[3435,17821,17608],{"class":4473},[3435,17823,5237],{"class":5236},[3435,17825,17826,17828],{"class":3437,"line":3621},[3435,17827,5275],{"class":4677},[3435,17829,5278],{"class":4473},[3435,17831,17832],{"class":3437,"line":3627},[3435,17833,3460],{"emptyLinePlaceholder":3459},[3435,17835,17836],{"class":3437,"line":3632},[3435,17837,17838],{"class":4461},"# Дочекайтеся працездатності інстансів\n",[3435,17840,17841,17843,17845,17847,17849],{"class":3437,"line":3637},[3435,17842,5227],{"class":4535},[3435,17844,5230],{"class":4473},[3435,17846,8210],{"class":4473},[3435,17848,8213],{"class":4473},[3435,17850,5237],{"class":5236},[3435,17852,17853,17855,17857,17859],{"class":3437,"line":4160},[3435,17854,5242],{"class":4677},[3435,17856,17738],{"class":4473},[3435,17858,7748],{"class":4677},[3435,17860,5278],{"class":4473},[3435,17862,17863,17865,17867,17869,17871],{"class":3437,"line":4166},[3435,17864,5227],{"class":4535},[3435,17866,5230],{"class":4473},[3435,17868,8210],{"class":4473},[3435,17870,8213],{"class":4473},[3435,17872,5237],{"class":5236},[3435,17874,17875,17877,17879,17881],{"class":3437,"line":4171},[3435,17876,5242],{"class":4677},[3435,17878,17797],{"class":4473},[3435,17880,7748],{"class":4677},[3435,17882,5278],{"class":4473},[5208,17884,17885],{"label":5281},[3426,17886,17888],{"className":5284,"code":17887,"language":5286,"meta":3431,"style":3431},"# 1. Створення логічного кластера Aurora з лімітами Serverless v2\naws rds create-db-cluster `\n    --db-cluster-identifier aurora-dotnet-cluster `\n    --engine aurora-postgresql `\n    --engine-version \"16.1\" `\n    --master-username postgres `\n    --master-user-password \"YourSecurePassword123!\" `\n    --db-subnet-group-name my-dotnet-app-subnet-group `\n    --vpc-security-group-ids $SG_RDS `\n    --serverless-v2-scaling-configuration MinCapacity=0.5,MaxCapacity=4.0 `\n    --database-name auroradb `\n    --region eu-central-1\n\n# Дочекайтеся статусу створення кластера\naws rds wait db-cluster-available `\n    --db-cluster-identifier aurora-dotnet-cluster --region eu-central-1\n\n# 2. Створення Writer інстансу\naws rds create-db-instance `\n    --db-instance-identifier aurora-writer-instance `\n    --db-cluster-identifier aurora-dotnet-cluster `\n    --db-instance-class db.serverless `\n    --engine aurora-postgresql `\n    --region eu-central-1\n\n# 3. Створення Reader інстансу\naws rds create-db-instance `\n    --db-instance-identifier aurora-reader-instance `\n    --db-cluster-identifier aurora-dotnet-cluster `\n    --db-instance-class db.serverless `\n    --engine aurora-postgresql `\n    --region eu-central-1\n\n# Дочекайтеся працездатності інстансів\naws rds wait db-instance-available `\n    --db-instance-identifier aurora-writer-instance --region eu-central-1\naws rds wait db-instance-available `\n    --db-instance-identifier aurora-reader-instance --region eu-central-1\n",[3414,17889,17890,17894,17899,17904,17909,17919,17923,17931,17939,17949,17963,17968,17974,17978,17982,17987,17994,17998,18003,18007,18012,18016,18021,18025,18031,18035,18040,18044,18049,18053,18057,18061,18067,18071,18075,18079,18086,18090],{"__ignoreMap":3431},[3435,17891,17892],{"class":3437,"line":3438},[3435,17893,17580],{"class":4461},[3435,17895,17896],{"class":3437,"line":3444},[3435,17897,17898],{"class":4467},"aws rds create-db-cluster `\n",[3435,17900,17901],{"class":3437,"line":3450},[3435,17902,17903],{"class":4467},"    --db-cluster-identifier aurora-dotnet-cluster `\n",[3435,17905,17906],{"class":3437,"line":3456},[3435,17907,17908],{"class":4467},"    --engine aurora-postgresql `\n",[3435,17910,17911,17914,17917],{"class":3437,"line":3463},[3435,17912,17913],{"class":4467},"    --engine-version ",[3435,17915,17916],{"class":4473},"\"16.1\"",[3435,17918,5329],{"class":4467},[3435,17920,17921],{"class":3437,"line":3469},[3435,17922,8636],{"class":4467},[3435,17924,17925,17927,17929],{"class":3437,"line":3475},[3435,17926,8641],{"class":4467},[3435,17928,8644],{"class":4473},[3435,17930,5329],{"class":4467},[3435,17932,17933,17935,17937],{"class":3437,"line":3481},[3435,17934,8451],{"class":4467},[3435,17936,8454],{"class":4535},[3435,17938,8457],{"class":4467},[3435,17940,17941,17943,17945,17947],{"class":3437,"line":3487},[3435,17942,8674],{"class":4467},[3435,17944,8677],{"class":4535},[3435,17946,8023],{"class":4524},[3435,17948,5329],{"class":4467},[3435,17950,17951,17954,17956,17959,17961],{"class":3437,"line":3493},[3435,17952,17953],{"class":4467},"    --serverless-v2-scaling-configuration MinCapacity=",[3435,17955,17516],{"class":5337},[3435,17957,17958],{"class":4467},",MaxCapacity=",[3435,17960,17520],{"class":5337},[3435,17962,5329],{"class":4467},[3435,17964,17965],{"class":3437,"line":3499},[3435,17966,17967],{"class":4467},"    --database-name auroradb `\n",[3435,17969,17970,17972],{"class":3437,"line":3505},[3435,17971,5334],{"class":4467},[3435,17973,5338],{"class":5337},[3435,17975,17976],{"class":3437,"line":3511},[3435,17977,3460],{"emptyLinePlaceholder":3459},[3435,17979,17980],{"class":3437,"line":3517},[3435,17981,17689],{"class":4461},[3435,17983,17984],{"class":3437,"line":3523},[3435,17985,17986],{"class":4467},"aws rds wait db-cluster-available `\n",[3435,17988,17989,17992],{"class":3437,"line":3529},[3435,17990,17991],{"class":4467},"    --db-cluster-identifier aurora-dotnet-cluster --region eu-central-",[3435,17993,5338],{"class":5337},[3435,17995,17996],{"class":3437,"line":3535},[3435,17997,3460],{"emptyLinePlaceholder":3459},[3435,17999,18000],{"class":3437,"line":3540},[3435,18001,18002],{"class":4461},"# 2. Створення Writer інстансу\n",[3435,18004,18005],{"class":3437,"line":3546},[3435,18006,8611],{"class":4467},[3435,18008,18009],{"class":3437,"line":3552},[3435,18010,18011],{"class":4467},"    --db-instance-identifier aurora-writer-instance `\n",[3435,18013,18014],{"class":3437,"line":3558},[3435,18015,17903],{"class":4467},[3435,18017,18018],{"class":3437,"line":3563},[3435,18019,18020],{"class":4467},"    --db-instance-class db.serverless `\n",[3435,18022,18023],{"class":3437,"line":3569},[3435,18024,17908],{"class":4467},[3435,18026,18027,18029],{"class":3437,"line":3575},[3435,18028,5334],{"class":4467},[3435,18030,5338],{"class":5337},[3435,18032,18033],{"class":3437,"line":3580},[3435,18034,3460],{"emptyLinePlaceholder":3459},[3435,18036,18037],{"class":3437,"line":3586},[3435,18038,18039],{"class":4461},"# 3. Створення Reader інстансу\n",[3435,18041,18042],{"class":3437,"line":3592},[3435,18043,8611],{"class":4467},[3435,18045,18046],{"class":3437,"line":3597},[3435,18047,18048],{"class":4467},"    --db-instance-identifier aurora-reader-instance `\n",[3435,18050,18051],{"class":3437,"line":3603},[3435,18052,17903],{"class":4467},[3435,18054,18055],{"class":3437,"line":3609},[3435,18056,18020],{"class":4467},[3435,18058,18059],{"class":3437,"line":3615},[3435,18060,17908],{"class":4467},[3435,18062,18063,18065],{"class":3437,"line":3621},[3435,18064,5334],{"class":4467},[3435,18066,5338],{"class":5337},[3435,18068,18069],{"class":3437,"line":3627},[3435,18070,3460],{"emptyLinePlaceholder":3459},[3435,18072,18073],{"class":3437,"line":3632},[3435,18074,17838],{"class":4461},[3435,18076,18077],{"class":3437,"line":3637},[3435,18078,8725],{"class":4467},[3435,18080,18081,18084],{"class":3437,"line":4160},[3435,18082,18083],{"class":4467},"    --db-instance-identifier aurora-writer-instance --region eu-central-",[3435,18085,5338],{"class":5337},[3435,18087,18088],{"class":3437,"line":4166},[3435,18089,8725],{"class":4467},[3435,18091,18092,18095],{"class":3437,"line":4171},[3435,18093,18094],{"class":4467},"    --db-instance-identifier aurora-reader-instance --region eu-central-",[3435,18096,5338],{"class":5337},[3700,18098,18100],{"id":18099},"крок-2-конфігурування-підключення-та-контекстів-даних-у-net","Крок 2: Конфігурування підключення та контекстів даних у .NET",[3353,18102,18103],{},"Для інтеграції з Aurora нам необхідно налаштувати два рядки підключення: перший вказуватиме на Writer Endpoint (для модифікації даних), а другий — на Reader Endpoint (для вибірок).",[3353,18105,18106],{},"Створимо структуру конфігурацій та контекстів у нашому .NET проєкті:",[10766,18108,18109,18154,18271,18389,18752],{},[3426,18110,18112],{"className":7213,"code":18111,"filename":7019,"language":7215,"meta":3431,"style":3431},"{\n    \"ConnectionStrings\": {\n        \"AuroraWriter\": \"Host=aurora-dotnet-cluster.cluster-xxx.eu-central-1.rds.amazonaws.com;Database=auroradb;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require;\",\n        \"AuroraReader\": \"Host=aurora-dotnet-cluster.cluster-ro-xxx.eu-central-1.rds.amazonaws.com;Database=auroradb;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require;\"\n    }\n}\n",[3414,18113,18114,18118,18124,18136,18146,18150],{"__ignoreMap":3431},[3435,18115,18116],{"class":3437,"line":3438},[3435,18117,4468],{"class":4467},[3435,18119,18120,18122],{"class":3437,"line":3444},[3435,18121,7232],{"class":7231},[3435,18123,4477],{"class":4467},[3435,18125,18126,18129,18131,18134],{"class":3437,"line":3450},[3435,18127,18128],{"class":7231},"        \"AuroraWriter\"",[3435,18130,4485],{"class":4467},[3435,18132,18133],{"class":4473},"\"Host=aurora-dotnet-cluster.cluster-xxx.eu-central-1.rds.amazonaws.com;Database=auroradb;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require;\"",[3435,18135,4491],{"class":4467},[3435,18137,18138,18141,18143],{"class":3437,"line":3456},[3435,18139,18140],{"class":7231},"        \"AuroraReader\"",[3435,18142,4485],{"class":4467},[3435,18144,18145],{"class":4473},"\"Host=aurora-dotnet-cluster.cluster-ro-xxx.eu-central-1.rds.amazonaws.com;Database=auroradb;Username=postgres;Password=YourSecurePassword123!;SSL Mode=Require;\"\n",[3435,18147,18148],{"class":3437,"line":3463},[3435,18149,3490],{"class":4467},[3435,18151,18152],{"class":3437,"line":3469},[3435,18153,3532],{"class":4467},[3426,18155,18157],{"className":4452,"code":18156,"filename":10881,"language":4454,"meta":3431,"style":3431},"using Microsoft.EntityFrameworkCore;\nusing MyDotnetApp.Models;\n\nnamespace MyDotnetApp.Data;\n\n\u002F\u002F Контекст для виконання операцій запису (Writer)\npublic class AppDbContext(DbContextOptions\u003CAppDbContext> options) : DbContext(options)\n{\n    public DbSet\u003CProduct> Products => Set\u003CProduct>();\n}\n",[3414,18158,18159,18171,18184,18188,18200,18204,18209,18239,18243,18267],{"__ignoreMap":3431},[3435,18160,18161,18163,18165,18167,18169],{"class":3437,"line":3438},[3435,18162,5658],{"class":5657},[3435,18164,13187],{"class":4542},[3435,18166,3691],{"class":4467},[3435,18168,13192],{"class":4542},[3435,18170,5664],{"class":4467},[3435,18172,18173,18175,18177,18179,18182],{"class":3437,"line":3444},[3435,18174,5658],{"class":5657},[3435,18176,10733],{"class":4542},[3435,18178,3691],{"class":4467},[3435,18180,18181],{"class":4542},"Models",[3435,18183,5664],{"class":4467},[3435,18185,18186],{"class":3437,"line":3450},[3435,18187,3460],{"emptyLinePlaceholder":3459},[3435,18189,18190,18192,18194,18196,18198],{"class":3437,"line":3456},[3435,18191,13217],{"class":4677},[3435,18193,10733],{"class":4542},[3435,18195,3691],{"class":4467},[3435,18197,13153],{"class":4542},[3435,18199,5664],{"class":4467},[3435,18201,18202],{"class":3437,"line":3463},[3435,18203,3460],{"emptyLinePlaceholder":3459},[3435,18205,18206],{"class":3437,"line":3469},[3435,18207,18208],{"class":4461},"\u002F\u002F Контекст для виконання операцій запису (Writer)\n",[3435,18210,18211,18213,18215,18217,18219,18221,18223,18225,18227,18229,18231,18233,18235,18237],{"class":3437,"line":3475},[3435,18212,4678],{"class":4677},[3435,18214,4681],{"class":4677},[3435,18216,6799],{"class":4542},[3435,18218,4565],{"class":4467},[3435,18220,6804],{"class":4542},[3435,18222,4539],{"class":4467},[3435,18224,4543],{"class":4542},[3435,18226,6811],{"class":4467},[3435,18228,4549],{"class":4524},[3435,18230,6816],{"class":4467},[3435,18232,6819],{"class":4542},[3435,18234,4565],{"class":4467},[3435,18236,4549],{"class":4524},[3435,18238,4701],{"class":4467},[3435,18240,18241],{"class":3437,"line":3481},[3435,18242,4468],{"class":4467},[3435,18244,18245,18247,18249,18251,18253,18255,18257,18259,18261,18263,18265],{"class":3437,"line":3487},[3435,18246,4715],{"class":4677},[3435,18248,6836],{"class":4542},[3435,18250,4539],{"class":4467},[3435,18252,4731],{"class":4542},[3435,18254,6811],{"class":4467},[3435,18256,4752],{"class":4524},[3435,18258,6847],{"class":4467},[3435,18260,6850],{"class":4535},[3435,18262,4539],{"class":4467},[3435,18264,4731],{"class":4542},[3435,18266,6857],{"class":4467},[3435,18268,18269],{"class":3437,"line":3493},[3435,18270,3532],{"class":4467},[3426,18272,18275],{"className":4452,"code":18273,"filename":18274,"language":4454,"meta":3431,"style":3431},"using Microsoft.EntityFrameworkCore;\nusing MyDotnetApp.Models;\n\nnamespace MyDotnetApp.Data;\n\n\u002F\u002F Контекст для виконання операцій читання (Reader)\npublic class ReadOnlyDbContext(DbContextOptions\u003CReadOnlyDbContext> options) : DbContext(options)\n{\n    public DbSet\u003CProduct> Products => Set\u003CProduct>();\n}\n","Data\u002FReadOnlyDbContext.cs",[3414,18276,18277,18289,18301,18305,18317,18321,18326,18357,18361,18385],{"__ignoreMap":3431},[3435,18278,18279,18281,18283,18285,18287],{"class":3437,"line":3438},[3435,18280,5658],{"class":5657},[3435,18282,13187],{"class":4542},[3435,18284,3691],{"class":4467},[3435,18286,13192],{"class":4542},[3435,18288,5664],{"class":4467},[3435,18290,18291,18293,18295,18297,18299],{"class":3437,"line":3444},[3435,18292,5658],{"class":5657},[3435,18294,10733],{"class":4542},[3435,18296,3691],{"class":4467},[3435,18298,18181],{"class":4542},[3435,18300,5664],{"class":4467},[3435,18302,18303],{"class":3437,"line":3450},[3435,18304,3460],{"emptyLinePlaceholder":3459},[3435,18306,18307,18309,18311,18313,18315],{"class":3437,"line":3456},[3435,18308,13217],{"class":4677},[3435,18310,10733],{"class":4542},[3435,18312,3691],{"class":4467},[3435,18314,13153],{"class":4542},[3435,18316,5664],{"class":4467},[3435,18318,18319],{"class":3437,"line":3463},[3435,18320,3460],{"emptyLinePlaceholder":3459},[3435,18322,18323],{"class":3437,"line":3469},[3435,18324,18325],{"class":4461},"\u002F\u002F Контекст для виконання операцій читання (Reader)\n",[3435,18327,18328,18330,18332,18335,18337,18339,18341,18343,18345,18347,18349,18351,18353,18355],{"class":3437,"line":3475},[3435,18329,4678],{"class":4677},[3435,18331,4681],{"class":4677},[3435,18333,18334],{"class":4542}," ReadOnlyDbContext",[3435,18336,4565],{"class":4467},[3435,18338,6804],{"class":4542},[3435,18340,4539],{"class":4467},[3435,18342,4606],{"class":4542},[3435,18344,6811],{"class":4467},[3435,18346,4549],{"class":4524},[3435,18348,6816],{"class":4467},[3435,18350,6819],{"class":4542},[3435,18352,4565],{"class":4467},[3435,18354,4549],{"class":4524},[3435,18356,4701],{"class":4467},[3435,18358,18359],{"class":3437,"line":3481},[3435,18360,4468],{"class":4467},[3435,18362,18363,18365,18367,18369,18371,18373,18375,18377,18379,18381,18383],{"class":3437,"line":3487},[3435,18364,4715],{"class":4677},[3435,18366,6836],{"class":4542},[3435,18368,4539],{"class":4467},[3435,18370,4731],{"class":4542},[3435,18372,6811],{"class":4467},[3435,18374,4752],{"class":4524},[3435,18376,6847],{"class":4467},[3435,18378,6850],{"class":4535},[3435,18380,4539],{"class":4467},[3435,18382,4731],{"class":4542},[3435,18384,6857],{"class":4467},[3435,18386,18387],{"class":3437,"line":3493},[3435,18388,3532],{"class":4467},[3426,18390,18392],{"className":4452,"code":18391,"filename":7354,"language":4454,"meta":3431,"style":3431},"using MyDotnetApp.Data;\nusing Microsoft.EntityFrameworkCore;\n\nvar builder = WebApplication.CreateBuilder(args);\n\n\u002F\u002F Реєстрація первинного контексту (Writer) з політикою повторних спроб\nbuilder.Services.AddDbContext\u003CAppDbContext>(options =>\n    options.UseNpgsql(builder.Configuration.GetConnectionString(\"AuroraWriter\"), npgsqlOptions =>\n        npgsqlOptions.EnableRetryOnFailure(\n            maxRetryCount: 3,\n            maxRetryDelay: TimeSpan.FromSeconds(5),\n            errorCodesToAdd: null\n        )\n    )\n);\n\n\u002F\u002F Реєстрація контексту для читання (Reader) з оптимізацією відстеження\nbuilder.Services.AddDbContext\u003CReadOnlyDbContext>(options =>\n    options.UseNpgsql(builder.Configuration.GetConnectionString(\"AuroraReader\"), npgsqlOptions =>\n        npgsqlOptions.EnableRetryOnFailure(\n            maxRetryCount: 3,\n            maxRetryDelay: TimeSpan.FromSeconds(5),\n            errorCodesToAdd: null\n        )\n    ).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) \u002F\u002F READ ONLY!\n);\n\nbuilder.Services.AddControllers();\nvar app = builder.Build();\napp.MapControllers();\napp.Run();\n",[3414,18393,18394,18406,18418,18422,18442,18446,18451,18473,18504,18514,18524,18542,18550,18554,18558,18562,18566,18571,18593,18624,18634,18644,18662,18670,18674,18694,18698,18702,18716,18732,18742],{"__ignoreMap":3431},[3435,18395,18396,18398,18400,18402,18404],{"class":3437,"line":3438},[3435,18397,5658],{"class":5657},[3435,18399,10733],{"class":4542},[3435,18401,3691],{"class":4467},[3435,18403,13153],{"class":4542},[3435,18405,5664],{"class":4467},[3435,18407,18408,18410,18412,18414,18416],{"class":3437,"line":3444},[3435,18409,5658],{"class":5657},[3435,18411,13187],{"class":4542},[3435,18413,3691],{"class":4467},[3435,18415,13192],{"class":4542},[3435,18417,5664],{"class":4467},[3435,18419,18420],{"class":3437,"line":3450},[3435,18421,3460],{"emptyLinePlaceholder":3459},[3435,18423,18424,18426,18428,18430,18432,18434,18436,18438,18440],{"class":3437,"line":3456},[3435,18425,5691],{"class":4677},[3435,18427,11081],{"class":4524},[3435,18429,5300],{"class":4467},[3435,18431,11086],{"class":4524},[3435,18433,3691],{"class":4467},[3435,18435,11091],{"class":4535},[3435,18437,4565],{"class":4467},[3435,18439,11096],{"class":4524},[3435,18441,4816],{"class":4467},[3435,18443,18444],{"class":3437,"line":3463},[3435,18445,3460],{"emptyLinePlaceholder":3459},[3435,18447,18448],{"class":3437,"line":3469},[3435,18449,18450],{"class":4461},"\u002F\u002F Реєстрація первинного контексту (Writer) з політикою повторних спроб\n",[3435,18452,18453,18455,18457,18459,18461,18463,18465,18467,18469,18471],{"class":3437,"line":3475},[3435,18454,4525],{"class":4524},[3435,18456,3691],{"class":4467},[3435,18458,4530],{"class":4524},[3435,18460,3691],{"class":4467},[3435,18462,4536],{"class":4535},[3435,18464,4539],{"class":4467},[3435,18466,4543],{"class":4542},[3435,18468,4546],{"class":4467},[3435,18470,4549],{"class":4524},[3435,18472,4552],{"class":4467},[3435,18474,18475,18477,18479,18481,18483,18485,18487,18489,18491,18493,18495,18498,18500,18502],{"class":3437,"line":3481},[3435,18476,4557],{"class":4524},[3435,18478,3691],{"class":4467},[3435,18480,4562],{"class":4535},[3435,18482,4565],{"class":4467},[3435,18484,4525],{"class":4524},[3435,18486,3691],{"class":4467},[3435,18488,4572],{"class":4524},[3435,18490,3691],{"class":4467},[3435,18492,4577],{"class":4535},[3435,18494,4565],{"class":4467},[3435,18496,18497],{"class":4473},"\"AuroraWriter\"",[3435,18499,11496],{"class":4467},[3435,18501,7130],{"class":4524},[3435,18503,4552],{"class":4467},[3435,18505,18506,18508,18510,18512],{"class":3437,"line":3487},[3435,18507,7146],{"class":4524},[3435,18509,3691],{"class":4467},[3435,18511,7151],{"class":4535},[3435,18513,5744],{"class":4467},[3435,18515,18516,18518,18520,18522],{"class":3437,"line":3493},[3435,18517,7158],{"class":4524},[3435,18519,4485],{"class":4467},[3435,18521,7163],{"class":5337},[3435,18523,4491],{"class":4467},[3435,18525,18526,18528,18530,18532,18534,18536,18538,18540],{"class":3437,"line":3499},[3435,18527,7170],{"class":4524},[3435,18529,4485],{"class":4467},[3435,18531,7175],{"class":4524},[3435,18533,3691],{"class":4467},[3435,18535,7180],{"class":4535},[3435,18537,4565],{"class":4467},[3435,18539,7185],{"class":5337},[3435,18541,7188],{"class":4467},[3435,18543,18544,18546,18548],{"class":3437,"line":3505},[3435,18545,7193],{"class":4524},[3435,18547,4485],{"class":4467},[3435,18549,15386],{"class":4677},[3435,18551,18552],{"class":3437,"line":3511},[3435,18553,15391],{"class":4467},[3435,18555,18556],{"class":3437,"line":3517},[3435,18557,15396],{"class":4467},[3435,18559,18560],{"class":3437,"line":3523},[3435,18561,4816],{"class":4467},[3435,18563,18564],{"class":3437,"line":3529},[3435,18565,3460],{"emptyLinePlaceholder":3459},[3435,18567,18568],{"class":3437,"line":3535},[3435,18569,18570],{"class":4461},"\u002F\u002F Реєстрація контексту для читання (Reader) з оптимізацією відстеження\n",[3435,18572,18573,18575,18577,18579,18581,18583,18585,18587,18589,18591],{"class":3437,"line":3540},[3435,18574,4525],{"class":4524},[3435,18576,3691],{"class":4467},[3435,18578,4530],{"class":4524},[3435,18580,3691],{"class":4467},[3435,18582,4536],{"class":4535},[3435,18584,4539],{"class":4467},[3435,18586,4606],{"class":4542},[3435,18588,4546],{"class":4467},[3435,18590,4549],{"class":4524},[3435,18592,4552],{"class":4467},[3435,18594,18595,18597,18599,18601,18603,18605,18607,18609,18611,18613,18615,18618,18620,18622],{"class":3437,"line":3546},[3435,18596,4557],{"class":4524},[3435,18598,3691],{"class":4467},[3435,18600,4562],{"class":4535},[3435,18602,4565],{"class":4467},[3435,18604,4525],{"class":4524},[3435,18606,3691],{"class":4467},[3435,18608,4572],{"class":4524},[3435,18610,3691],{"class":4467},[3435,18612,4577],{"class":4535},[3435,18614,4565],{"class":4467},[3435,18616,18617],{"class":4473},"\"AuroraReader\"",[3435,18619,11496],{"class":4467},[3435,18621,7130],{"class":4524},[3435,18623,4552],{"class":4467},[3435,18625,18626,18628,18630,18632],{"class":3437,"line":3552},[3435,18627,7146],{"class":4524},[3435,18629,3691],{"class":4467},[3435,18631,7151],{"class":4535},[3435,18633,5744],{"class":4467},[3435,18635,18636,18638,18640,18642],{"class":3437,"line":3558},[3435,18637,7158],{"class":4524},[3435,18639,4485],{"class":4467},[3435,18641,7163],{"class":5337},[3435,18643,4491],{"class":4467},[3435,18645,18646,18648,18650,18652,18654,18656,18658,18660],{"class":3437,"line":3563},[3435,18647,7170],{"class":4524},[3435,18649,4485],{"class":4467},[3435,18651,7175],{"class":4524},[3435,18653,3691],{"class":4467},[3435,18655,7180],{"class":4535},[3435,18657,4565],{"class":4467},[3435,18659,7185],{"class":5337},[3435,18661,7188],{"class":4467},[3435,18663,18664,18666,18668],{"class":3437,"line":3569},[3435,18665,7193],{"class":4524},[3435,18667,4485],{"class":4467},[3435,18669,15386],{"class":4677},[3435,18671,18672],{"class":3437,"line":3575},[3435,18673,15391],{"class":4467},[3435,18675,18676,18679,18681,18683,18685,18687,18689,18691],{"class":3437,"line":3580},[3435,18677,18678],{"class":4467},"    ).",[3435,18680,4648],{"class":4535},[3435,18682,4565],{"class":4467},[3435,18684,4653],{"class":4524},[3435,18686,3691],{"class":4467},[3435,18688,4658],{"class":4524},[3435,18690,5267],{"class":4467},[3435,18692,18693],{"class":4461},"\u002F\u002F READ ONLY!\n",[3435,18695,18696],{"class":3437,"line":3586},[3435,18697,4816],{"class":4467},[3435,18699,18700],{"class":3437,"line":3592},[3435,18701,3460],{"emptyLinePlaceholder":3459},[3435,18703,18704,18706,18708,18710,18712,18714],{"class":3437,"line":3597},[3435,18705,4525],{"class":4524},[3435,18707,3691],{"class":4467},[3435,18709,4530],{"class":4524},[3435,18711,3691],{"class":4467},[3435,18713,11252],{"class":4535},[3435,18715,4760],{"class":4467},[3435,18717,18718,18720,18722,18724,18726,18728,18730],{"class":3437,"line":3603},[3435,18719,5691],{"class":4677},[3435,18721,11261],{"class":4524},[3435,18723,5300],{"class":4467},[3435,18725,4525],{"class":4524},[3435,18727,3691],{"class":4467},[3435,18729,11270],{"class":4535},[3435,18731,4760],{"class":4467},[3435,18733,18734,18736,18738,18740],{"class":3437,"line":3609},[3435,18735,11277],{"class":4524},[3435,18737,3691],{"class":4467},[3435,18739,11282],{"class":4535},[3435,18741,4760],{"class":4467},[3435,18743,18744,18746,18748,18750],{"class":3437,"line":3615},[3435,18745,11277],{"class":4524},[3435,18747,3691],{"class":4467},[3435,18749,11293],{"class":4535},[3435,18751,4760],{"class":4467},[3426,18753,18756],{"className":4452,"code":18754,"filename":18755,"language":4454,"meta":3431,"style":3431},"using Microsoft.EntityFrameworkCore;\nusing MyDotnetApp.Data;\nusing MyDotnetApp.Models;\n\nnamespace MyDotnetApp.Services;\n\npublic class ProductService(AppDbContext db, ReadOnlyDbContext readDb)\n{\n    \u002F\u002F Запити вибірки (SELECT) автоматично спрямовуються через Reader Endpoint\n    public async Task\u003CList\u003CProduct>> GetActiveProductsAsync() =>\n        await readDb.Products.ToListAsync();\n\n    \u002F\u002F Операції запису та модифікації виконуються виключно через Writer Endpoint\n    public async Task CreateProductAsync(Product product)\n    {\n        db.Products.Add(product);\n        await db.SaveChangesAsync();\n    }\n}\n","Services\u002FProductService.cs",[3414,18757,18758,18770,18782,18794,18798,18810,18814,18836,18840,18845,18868,18884,18888,18893,18912,18916,18934,18946,18950],{"__ignoreMap":3431},[3435,18759,18760,18762,18764,18766,18768],{"class":3437,"line":3438},[3435,18761,5658],{"class":5657},[3435,18763,13187],{"class":4542},[3435,18765,3691],{"class":4467},[3435,18767,13192],{"class":4542},[3435,18769,5664],{"class":4467},[3435,18771,18772,18774,18776,18778,18780],{"class":3437,"line":3444},[3435,18773,5658],{"class":5657},[3435,18775,10733],{"class":4542},[3435,18777,3691],{"class":4467},[3435,18779,13153],{"class":4542},[3435,18781,5664],{"class":4467},[3435,18783,18784,18786,18788,18790,18792],{"class":3437,"line":3450},[3435,18785,5658],{"class":5657},[3435,18787,10733],{"class":4542},[3435,18789,3691],{"class":4467},[3435,18791,18181],{"class":4542},[3435,18793,5664],{"class":4467},[3435,18795,18796],{"class":3437,"line":3456},[3435,18797,3460],{"emptyLinePlaceholder":3459},[3435,18799,18800,18802,18804,18806,18808],{"class":3437,"line":3463},[3435,18801,13217],{"class":4677},[3435,18803,10733],{"class":4542},[3435,18805,3691],{"class":4467},[3435,18807,4530],{"class":4542},[3435,18809,5664],{"class":4467},[3435,18811,18812],{"class":3437,"line":3469},[3435,18813,3460],{"emptyLinePlaceholder":3459},[3435,18815,18816,18818,18820,18822,18824,18826,18828,18830,18832,18834],{"class":3437,"line":3475},[3435,18817,4678],{"class":4677},[3435,18819,4681],{"class":4677},[3435,18821,4684],{"class":4542},[3435,18823,4565],{"class":4467},[3435,18825,4543],{"class":4542},[3435,18827,4691],{"class":4524},[3435,18829,3417],{"class":4467},[3435,18831,4606],{"class":4542},[3435,18833,4698],{"class":4524},[3435,18835,4701],{"class":4467},[3435,18837,18838],{"class":3437,"line":3481},[3435,18839,4468],{"class":4467},[3435,18841,18842],{"class":3437,"line":3487},[3435,18843,18844],{"class":4461},"    \u002F\u002F Запити вибірки (SELECT) автоматично спрямовуються через Reader Endpoint\n",[3435,18846,18847,18849,18851,18853,18855,18857,18859,18861,18863,18866],{"class":3437,"line":3493},[3435,18848,4715],{"class":4677},[3435,18850,4718],{"class":4677},[3435,18852,4721],{"class":4542},[3435,18854,4539],{"class":4467},[3435,18856,4726],{"class":4542},[3435,18858,4539],{"class":4467},[3435,18860,4731],{"class":4542},[3435,18862,4734],{"class":4467},[3435,18864,18865],{"class":4535},"GetActiveProductsAsync",[3435,18867,4740],{"class":4467},[3435,18869,18870,18872,18874,18876,18878,18880,18882],{"class":3437,"line":3499},[3435,18871,4745],{"class":4677},[3435,18873,4698],{"class":4524},[3435,18875,3691],{"class":4467},[3435,18877,4752],{"class":4524},[3435,18879,3691],{"class":4467},[3435,18881,4757],{"class":4535},[3435,18883,4760],{"class":4467},[3435,18885,18886],{"class":3437,"line":3505},[3435,18887,3460],{"emptyLinePlaceholder":3459},[3435,18889,18890],{"class":3437,"line":3511},[3435,18891,18892],{"class":4461},"    \u002F\u002F Операції запису та модифікації виконуються виключно через Writer Endpoint\n",[3435,18894,18895,18897,18899,18901,18904,18906,18908,18910],{"class":3437,"line":3517},[3435,18896,4715],{"class":4677},[3435,18898,4718],{"class":4677},[3435,18900,4721],{"class":4542},[3435,18902,18903],{"class":4535}," CreateProductAsync",[3435,18905,4565],{"class":4467},[3435,18907,4731],{"class":4542},[3435,18909,4787],{"class":4524},[3435,18911,4701],{"class":4467},[3435,18913,18914],{"class":3437,"line":3523},[3435,18915,4794],{"class":4467},[3435,18917,18918,18920,18922,18924,18926,18928,18930,18932],{"class":3437,"line":3529},[3435,18919,4799],{"class":4524},[3435,18921,3691],{"class":4467},[3435,18923,4752],{"class":4524},[3435,18925,3691],{"class":4467},[3435,18927,4808],{"class":4535},[3435,18929,4565],{"class":4467},[3435,18931,4813],{"class":4524},[3435,18933,4816],{"class":4467},[3435,18935,18936,18938,18940,18942,18944],{"class":3437,"line":3535},[3435,18937,4745],{"class":4677},[3435,18939,4691],{"class":4524},[3435,18941,3691],{"class":4467},[3435,18943,4827],{"class":4535},[3435,18945,4760],{"class":4467},[3435,18947,18948],{"class":3437,"line":3540},[3435,18949,3490],{"class":4467},[3435,18951,18952],{"class":3437,"line":3546},[3435,18953,3532],{"class":4467},[3700,18955,18957],{"id":18956},"крок-3-моніторинг-та-перевірка-динамічного-автомасшабування-acu","Крок 3: Моніторинг та перевірка динамічного автомасшабування ACU",[3353,18959,18960],{},"Однією з ключових переваг Aurora Serverless v2 є здатність адаптувати обчислювальні ресурси під коливання навантаження. Проведемо експеримент та перевіримо, як змінюється ємність кластера.",[3913,18962,18963,18972,19007],{},[3369,18964,18965,18968,18969,18971],{},[3359,18966,18967],{},"Ініціація навантаження:"," Запустіть раніше створений стрес-тест за допомогою ",[3414,18970,16091],{},", збільшивши кількість віртуальних користувачів до 500 для генерації інтенсивного потоку запитів.",[3369,18973,18974,18977,18978,19001,19004],{},[3359,18975,18976],{},"Перевірка поточного обсягу ACU через SQL-запит:","\nПідключіться до вашого Writer інстансу за допомогою терміналу та виконайте запит до внутрішньої системної таблиці AWS Aurora для перевірки поточної виділеної пам'яті та обчислювальної ємності:",[3426,18979,18981],{"className":12902,"code":18980,"language":12904,"meta":3431,"style":3431},"-- Отримання метрики ємності безпосередньо з рушія бази даних\nSELECT * FROM aurora_volume_status();\n",[3414,18982,18983,18988],{"__ignoreMap":3431},[3435,18984,18985],{"class":3437,"line":3438},[3435,18986,18987],{"class":4461},"-- Отримання метрики ємності безпосередньо з рушія бази даних\n",[3435,18989,18990,18992,18995,18998],{"class":3437,"line":3444},[3435,18991,4239],{"class":4677},[3435,18993,18994],{"class":4467}," * ",[3435,18996,18997],{"class":4677},"FROM",[3435,18999,19000],{"class":4467}," aurora_volume_status();\n",[19002,19003],"br",{},[3897,19005,19006],{},"(Примітка: Детальнішу статистику споживання ACU рекомендується дивитися в хмарному моніторингу, оскільки двигун PostgreSQL оперує внутрішніми буферами, а розподіл ресурсів виконується на рівні віртуалізації AWS).",[3369,19008,19009,19012,19013,7577,19016,7577,19019,7577,19021,19024,19025,3691],{},[3359,19010,19011],{},"Моніторинг через CloudWatch:","\nПерейдіть до сервісу ",[3359,19014,19015],{},"CloudWatch",[3359,19017,19018],{},"Metrics",[3359,19020,5675],{},[3359,19022,19023],{},"Source"," і знайдіть метрику ",[3359,19026,19027],{},[3414,19028,19029],{},"ServerlessDatabaseCapacity",[11641,19031,19033,19037,19041,19045,19048,19057,19067,19076,19084,19087],{"title":19032,":cursor":16326},"CloudWatch Metric: ServerlessDatabaseCapacity",[11645,19034,19036],{"className":19035},[3437],"Metric: ServerlessDatabaseCapacity (aurora-dotnet-cluster)",[11645,19038,19040],{"className":19039},[3437],"-------------------------------------------------------------------------",[11645,19042,19044],{"className":19043},[3437],"Time (UTC)           | ACU Capacity | DB Connections | CPU Utilization  |",[11645,19046,19040],{"className":19047},[3437],[11645,19049,19051,19052,19056],{"className":19050},[3437],"14:10:00 (Idle)      | ",[3435,19053,19055],{"className":19054},[12069],"0.50 ACU","     | 12             | 2.1%             |",[11645,19058,19060,19061,19066],{"className":19059},[3437],"14:11:00 (k6 Start)  | ",[3435,19062,19065],{"className":19063},[19064],"text-yellow-400","1.80 ACU","     | 154            | 48.6%            |",[11645,19068,19070,19071,19075],{"className":19069},[3437],"14:12:00 (k6 Peak)   | ",[3435,19072,19074],{"className":19073},[16359,12048],"3.50 ACU","     | 480            | 87.2%            |",[11645,19077,19079,19080,19083],{"className":19078},[3437],"14:13:00 (k6 Stop)   | ",[3435,19081,19055],{"className":19082},[12069],"     | 14             | 1.8%             |",[11645,19085,19040],{"className":19086},[3437],[11645,19088,19090],{"className":19089},[3437],[3435,19091,19093],{"className":19092},[11687,12048],"Observation: Dynamic scaling succeeded. Capacity scaled up to 3.5 ACU and returned to 0.5 ACU.",[3353,19095,19096,19098,19099,19102,19103,19106,19107,19109],{},[3359,19097,16451],{}," Під час спокою кластер споживає мінімально налаштовану ємність ",[3414,19100,19101],{},"0.5 ACU"," (що еквівалентно приблизно 1 GB RAM). З початком тестування система динамічно збільшила обчислювальні ресурси до ",[3414,19104,19105],{},"3.5 ACU"," для обробки 480 одночасних підключень без втрати продуктивності. Після завершення тесту ємність автоматично повернулася до базового значення ",[3414,19108,19101],{},", мінімізуючи витрати на утримання хмарної інфраструктури.",[3348,19111,19113],{"id":19112},"резюме","Резюме",[3366,19115,19116,19121,19137,19143,19149,19155,19161,19173,19179,19185],{},[3369,19117,19118,19120],{},[3359,19119,5675],{}," — керований сервіс реляційних БД. AWS відповідає за бекапи, патчі, failover, моніторинг. Ви відповідаєте за схему, запити та архітектуру.",[3369,19122,19123,3783,19126,19128,19129,19132,19133,19136],{},[3359,19124,19125],{},"Instance Classes:",[3414,19127,3760],{}," (Free Tier), ",[3414,19130,19131],{},"db.m6g"," (production), ",[3414,19134,19135],{},"db.r6g"," (memory-intensive).",[3369,19138,19139,19142],{},[3359,19140,19141],{},"Multi-AZ:"," синхронна реплікація у другу AZ. Автоматичний failover ~60–120 сек. Обов'язковий для production. Standby не приймає читання.",[3369,19144,19145,19148],{},[3359,19146,19147],{},"Read Replicas:"," асинхронні копії для масштабування читання. До 5 replicas (Aurora — до 15). Eventual consistency. Потребує явної маршрутизації у .NET.",[3369,19150,19151,19154],{},[3359,19152,19153],{},"PITR (Point-in-Time Recovery):"," відновлення на будь-яку секунду в межах retention period (до 35 днів). Результат — новий instance на новому endpoint.",[3369,19156,19157,19160],{},[3359,19158,19159],{},"Security:"," RDS у private subnet, Security Group з source = SG EC2, шифрування AES-256 (KMS), SSL\u002FTLS для з'єднань, IAM Authentication.",[3369,19162,19163,19166,19167,3417,19169,3417,19171,3691],{},[3359,19164,19165],{},"Parameter Groups:"," налаштування СУБД без SSH. ",[3414,19168,5921],{},[3414,19170,5902],{},[3414,19172,5940],{},[3369,19174,19175,19178],{},[3359,19176,19177],{},"Amazon Aurora:"," хмарна СУБД AWS. Відокремлює compute від storage (6 копій у 3 AZ). Failover ~30 сек. Aurora Serverless v2 — auto-scaling від 0.5 ACU.",[3369,19180,19181,19184],{},[3359,19182,19183],{},"RDS Proxy:"," пул з'єднань між застосунком і RDS. Критично для Lambda. Зменшує кількість з'єднань до бази у 10–20 разів. Прискорює failover до ~5 сек.",[3369,19186,19187,3783,19190,3417,19193,19196],{},[3359,19188,19189],{},"EF Core + RDS:",[3414,19191,19192],{},"Npgsql.EntityFrameworkCore.PostgreSQL",[3414,19194,19195],{},"EnableRetryOnFailure()"," для стійкості до failover. Міграції — окремий крок у CI\u002FCD, не при старті застосунку.",[3679,19198],{},[3348,19200,19202],{"id":19201},"практичні-завдання","Практичні завдання",[3700,19204,19206],{"id":19205},"рівень-1-базовий","Рівень 1 (Базовий)",[3353,19208,19209,19212],{},[3359,19210,19211],{},"Завдання 1."," Поясніть різницю між Multi-AZ та Read Replica. Чому standby у Multi-AZ не приймає запити читання? Коли вам знадобиться Read Replica?",[3353,19214,19215,19218],{},[3359,19216,19217],{},"Завдання 2."," Що таке Point-in-Time Recovery і коли воно рятує? Опишіть сценарій коли щоденного snapshot недостатньо, але PITR — достатньо.",[3700,19220,19222],{"id":19221},"рівень-2-практичний","Рівень 2 (Практичний)",[3353,19224,19225,19228,19229,19231],{},[3359,19226,19227],{},"Завдання 3."," Створіть RDS PostgreSQL ",[3414,19230,3760],{}," instance. Підключіть .NET Web API через EF Core. Виконайте Code-First міграцію. Перевірте роботу CRUD через HTTP-запити.",[3353,19233,19234,19237,19238,19240,19241,19244,19245,19247,19248,3691],{},[3359,19235,19236],{},"Завдання 4."," Налаштуйте два connection strings у вашому .NET проєкті: один для primary (запис), другий для Read Replica (читання). Реалізуйте сервіс, що явно маршрутизує ",[3414,19239,4239],{}," на репліку, а ",[3414,19242,19243],{},"INSERT\u002FUPDATE\u002FDELETE"," — на primary. Переконайтесь що ",[3414,19246,4606],{}," використовує ",[3414,19249,4658],{},[3700,19251,19253],{"id":19252},"рівень-3-архітектура","Рівень 3 (Архітектура)",[3353,19255,19256,19259,19260,19263,19264,19267],{},[3359,19257,19258],{},"Завдання 5."," Спроектуйте RDS архітектуру для e-commerce платформи: Multi-AZ PostgreSQL (primary у ",[3414,19261,19262],{},"eu-central-1a",", standby у ",[3414,19265,19266],{},"eu-central-1b","), дві Read Replicas (одна для API читання, одна для аналітичних звітів), RDS Proxy для Lambda-функцій обробки замовлень, автоматичні бекапи на 30 днів. Намалюйте схему у PlantUML.",[3353,19269,19270,19273,19274,19277,19278,19281,19282,19285],{},[3359,19271,19272],{},"Завдання 6."," Реалізуйте безпечне зберігання RDS credentials: збережіть connection string у AWS Secrets Manager, отримайте його у .NET через ",[3414,19275,19276],{},"AWSSDK.SecretsManager"," та інтегруйте з ",[3414,19279,19280],{},"IConfiguration"," так, щоб ",[3414,19283,19284],{},"GetConnectionString(\"Default\")"," повертало значення з Secrets Manager прозоро.",[19287,19288,19289],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spJ8K, html code.shiki .spJ8K{--shiki-light:#008000;--shiki-default:#6A9955;--shiki-dark:#6A9955}html pre.shiki code .sHH4Y, html code.shiki .sHH4Y{--shiki-light:#000000;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sbdoH, html code.shiki .sbdoH{--shiki-light:#A31515;--shiki-default:#CE9178;--shiki-dark:#CE9178}html pre.shiki code .siwwj, html code.shiki .siwwj{--shiki-light:#001080;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s8Opu, html code.shiki .s8Opu{--shiki-light:#795E26;--shiki-default:#DCDCAA;--shiki-dark:#DCDCAA}html pre.shiki code .sN1BT, html code.shiki .sN1BT{--shiki-light:#267F99;--shiki-default:#4EC9B0;--shiki-dark:#4EC9B0}html pre.shiki code .su1O8, html code.shiki .su1O8{--shiki-light:#0000FF;--shiki-default:#569CD6;--shiki-dark:#569CD6}html pre.shiki code .sjcCO, html code.shiki .sjcCO{--shiki-light:#EE0000;--shiki-default:#D7BA7D;--shiki-dark:#D7BA7D}html pre.shiki code .sJj4R, html code.shiki .sJj4R{--shiki-light:#098658;--shiki-default:#B5CEA8;--shiki-dark:#B5CEA8}html pre.shiki code .s8xlr, html code.shiki .s8xlr{--shiki-light:#AF00DB;--shiki-default:#C586C0;--shiki-dark:#C586C0}html pre.shiki code .sD7JJ, html code.shiki .sD7JJ{--shiki-light:#000000FF;--shiki-default:#D4D4D4;--shiki-dark:#D4D4D4}html pre.shiki code .sLwNe, html code.shiki .sLwNe{--shiki-light:#0451A5;--shiki-default:#9CDCFE;--shiki-dark:#9CDCFE}html pre.shiki code .s-QsJ, html code.shiki .s-QsJ{--shiki-light:#0070C1;--shiki-default:#4FC1FF;--shiki-dark:#4FC1FF}",{"title":3431,"searchDepth":3444,"depth":3444,"links":19291},[19292,19293,19298,19304,19308,19313,19318,19326,19332,19336,19337,19344,19361,19379,19385,19386],{"id":3350,"depth":3444,"text":3351},{"id":3683,"depth":3444,"text":3684,"children":19294},[19295,19296,19297],{"id":3702,"depth":3450,"text":3703},{"id":3729,"depth":3450,"text":3730},{"id":3749,"depth":3450,"text":3750},{"id":3819,"depth":3444,"text":3820,"children":19299},[19300,19301,19302,19303],{"id":3826,"depth":3450,"text":3827},{"id":3844,"depth":3450,"text":3845},{"id":3851,"depth":3450,"text":3852},{"id":3858,"depth":3450,"text":3859},{"id":3888,"depth":3444,"text":3889,"children":19305},[19306,19307],{"id":3903,"depth":3450,"text":3904},{"id":3943,"depth":3450,"text":3944},{"id":4255,"depth":3444,"text":4256,"children":19309},[19310,19311,19312],{"id":4268,"depth":3450,"text":4269},{"id":4296,"depth":3450,"text":4297},{"id":4323,"depth":3450,"text":4324},{"id":4842,"depth":3444,"text":4843,"children":19314},[19315,19316,19317],{"id":4849,"depth":3450,"text":4850},{"id":4933,"depth":3450,"text":4934},{"id":5199,"depth":3450,"text":5200},{"id":5343,"depth":3444,"text":5344,"children":19319},[19320,19321,19322,19323,19324,19325],{"id":5350,"depth":3450,"text":5351},{"id":5374,"depth":3450,"text":5375},{"id":5511,"depth":3450,"text":5512},{"id":5573,"depth":3450,"text":5574},{"id":5611,"depth":3450,"text":5612},{"id":5832,"depth":3450,"text":5833},{"id":6050,"depth":3444,"text":6051,"children":19327},[19328,19329,19330,19331],{"id":6057,"depth":3450,"text":6058},{"id":6094,"depth":3450,"text":6095},{"id":6105,"depth":3450,"text":6106},{"id":6123,"depth":3450,"text":6124},{"id":6136,"depth":3444,"text":6137,"children":19333},[19334,19335],{"id":6145,"depth":3450,"text":6146},{"id":6396,"depth":3450,"text":6397},{"id":6433,"depth":3444,"text":6434},{"id":6579,"depth":3444,"text":6580,"children":19338},[19339,19340,19341,19342,19343],{"id":6586,"depth":3450,"text":6587},{"id":6648,"depth":3450,"text":6649},{"id":7009,"depth":3450,"text":7010},{"id":7255,"depth":3450,"text":7256},{"id":7358,"depth":3450,"text":7359},{"id":7370,"depth":3444,"text":7371,"children":19345},[19346,19347,19348,19349,19350,19351,19352,19353,19354,19355,19356,19357,19358,19359,19360],{"id":7530,"depth":3450,"text":7531},{"id":7563,"depth":3450,"text":7564},{"id":8783,"depth":3450,"text":8784},{"id":8787,"depth":3450,"text":8788},{"id":8996,"depth":3450,"text":8997},{"id":9421,"depth":3450,"text":9422},{"id":9772,"depth":3450,"text":9773},{"id":10711,"depth":3450,"text":10712},{"id":11564,"depth":3450,"text":11565},{"id":11693,"depth":3450,"text":11694},{"id":11700,"depth":3450,"text":11701},{"id":11748,"depth":3450,"text":11749},{"id":11782,"depth":3450,"text":11783},{"id":12083,"depth":3450,"text":12084},{"id":12195,"depth":3450,"text":12196},{"id":12693,"depth":3444,"text":12694,"children":19362},[19363,19364,19365,19366,19367,19368,19369,19370,19371,19372,19373,19374,19375,19376,19377,19378],{"id":12699,"depth":3450,"text":12700},{"id":12879,"depth":3450,"text":12880},{"id":12979,"depth":3450,"text":12980},{"id":13104,"depth":3450,"text":13105},{"id":13864,"depth":3450,"text":13865},{"id":14162,"depth":3450,"text":14163},{"id":14331,"depth":3450,"text":14332},{"id":14411,"depth":3450,"text":14412},{"id":15457,"depth":3450,"text":15458},{"id":15606,"depth":3450,"text":15607},{"id":16084,"depth":3450,"text":16085},{"id":16314,"depth":3450,"text":16315},{"id":16458,"depth":3450,"text":16459},{"id":16518,"depth":3450,"text":16519},{"id":16818,"depth":3450,"text":16819},{"id":17037,"depth":3450,"text":17038},{"id":17258,"depth":3444,"text":17259,"children":19380},[19381,19382,19383,19384],{"id":17271,"depth":3450,"text":17272},{"id":17434,"depth":3450,"text":17435},{"id":18099,"depth":3450,"text":18100},{"id":18956,"depth":3450,"text":18957},{"id":19112,"depth":3444,"text":19113},{"id":19201,"depth":3444,"text":19202,"children":19387},[19388,19389,19390],{"id":19205,"depth":3450,"text":19206},{"id":19221,"depth":3450,"text":19222},{"id":19252,"depth":3450,"text":19253},"Повний посібник з Amazon RDS для .NET розробників. PostgreSQL, MySQL, SQL Server, Multi-AZ, Read Replicas, Aurora, RDS Proxy, підключення через Entity Framework Core та Code-First міграції.","md",null,{},{"title":3254,"description":19391},"caof0HHjU5S6rXBZwEgaPfpxUQyeIvEIKXUWCUr080k",[19398,19400],{"title":3250,"path":3251,"stem":3252,"description":19399,"children":-1},"Детальне пояснення що таке CDN і навіщо він потрібен. CloudFront Distributions, Origins, Edge Locations, Cache Behaviors, OAC, CloudFront Functions, Invalidations. Повна лабораторна робота з React SPA на S3 + CloudFront + HTTPS з підключенням безкоштовного домену pp.ua.",{"title":3258,"path":3259,"stem":3260,"description":19401,"children":-1},"Фундаментальний посібник з Amazon DynamoDB для .NET розробників. Від моделі даних та первинних ключів до вторинних індексів, транзакцій, Streams, TTL, Global Tables та інтеграції через AWS SDK for .NET — з акцентом на проектування схем для реальних production-сценаріїв.",1782371306120]