{"id":54350,"date":"2025-12-04T07:56:24","date_gmt":"2025-12-04T07:56:24","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=54350"},"modified":"2025-12-04T07:56:24","modified_gmt":"2025-12-04T07:56:24","slug":"dotnet-capstone-project-for-dotnet-performance-optimization","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/dotnet-capstone-project-for-dotnet-performance-optimization\/","title":{"rendered":"DOTNET: Capstone Project for DOTNET Performance Optimization"},"content":{"rendered":"\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. Recommended GitHub Application<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Chosen app: <strong>NimblePros\/eShopOnWeb<\/strong><\/h3>\n\n\n\n<p><strong>Repo:<\/strong> <a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">https:\/\/github.com\/NimblePros\/eShopOnWeb<\/a> (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/p>\n\n\n\n<p><strong>Why this one:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Official <strong>Microsoft reference app<\/strong>, now community-maintained by NimblePros.<\/li>\n\n\n\n<li><strong>ASP.NET Core MVC<\/strong> app (classic web app) + <strong>PublicApi<\/strong> project (API you can treat as \u201cmicroservice\u201d).<\/li>\n\n\n\n<li>Uses <strong>Entity Framework Core<\/strong> with <strong>SQL Server<\/strong> support, with explicit steps to configure SQL Server in <code>appsettings.json<\/code> and apply EF migrations. (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/li>\n\n\n\n<li>Main branch is currently <strong>ASP.NET Core 9.0<\/strong>. By the time .NET 10 is out, updating target frameworks will be straightforward; the project already tracks latest .NET versions. (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/li>\n<\/ul>\n\n\n\n<p>We\u2019ll treat:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>src\/Web<\/code> = <strong>front-end web app<\/strong><\/li>\n\n\n\n<li><code>src\/PublicApi<\/code> = <strong>microservice\/API<\/strong> (this is what you\u2019ll mainly hammer with k6, dotnet-counters, Datadog, etc.)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. Capstone: High-Level Structure<\/h2>\n\n\n\n<p>Your <strong>\u201cDOTNET Performance Optimization Capstone\u201d<\/strong> will use eShopOnWeb and walk students through:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Setup &amp; Run the App<\/strong>\n<ul class=\"wp-block-list\">\n<li>Clone repo, configure SQL Server, run migrations, run Web + PublicApi.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Baseline &amp; Live Health<\/strong>\n<ul class=\"wp-block-list\">\n<li>Use <strong>dotnet-counters<\/strong> to watch CPU, GC, allocations, thread pool.<\/li>\n\n\n\n<li>Problem it solves: <em>\u201cwhat\u2019s happening right now in this process?\u201d<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>CPU Profiling<\/strong>\n<ul class=\"wp-block-list\">\n<li>Use <strong>dotTrace<\/strong> to find slow endpoints in PublicApi \/ Web.<\/li>\n\n\n\n<li>Problem it solves: <em>\u201cwhere is the time going?\u201d<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Memory Profiling<\/strong>\n<ul class=\"wp-block-list\">\n<li>Use <strong>dotMemory<\/strong> to detect high allocations, possible leaks.<\/li>\n\n\n\n<li>Problem it solves: <em>memory leaks, LOH bloat, allocation hotspots.<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Deep Runtime Analysis<\/strong>\n<ul class=\"wp-block-list\">\n<li>Use <strong>PerfView<\/strong> on traces to understand GC, CPU, allocations in detail.<\/li>\n\n\n\n<li>Problem it solves: <em>advanced GC\/CPU investigation (\u201ctruth machine\u201d).<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Micro-benchmarks<\/strong>\n<ul class=\"wp-block-list\">\n<li>Extract a core hot function into a separate class library and benchmark with <strong>BenchmarkDotNet<\/strong>.<\/li>\n\n\n\n<li>Problem it solves: <em>LINQ vs loops, Span vs array, etc.<\/em><\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Load Testing<\/strong>\n<ul class=\"wp-block-list\">\n<li>Use <strong>k6<\/strong> to generate load on PublicApi (simulate catalog browse \/ add to basket).<\/li>\n\n\n\n<li>Observe impact via dotnet-counters, PerfView, Datadog.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Datadog Monitoring &amp; Dashboard<\/strong>\n<ul class=\"wp-block-list\">\n<li>Install <strong>Datadog Agent<\/strong> on Windows. (<a href=\"https:\/\/docs.datadoghq.com\/agent\/supported_platforms\/windows\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/li>\n\n\n\n<li>Install <strong>Datadog .NET Tracer<\/strong> for automatic instrumentation. (<a href=\"https:\/\/docs.datadoghq.com\/tracing\/trace_collection\/automatic_instrumentation\/dd_libraries\/dotnet-core\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/li>\n\n\n\n<li>Create <strong>custom dashboard<\/strong> showing latency, errors, CPU, GC, SQL time.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>I\u2019ll now go step-by-step.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. Step 0 \u2013 Prerequisites<\/h2>\n\n\n\n<p>On your <strong>Windows<\/strong> machine:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">.NET &amp; Tools<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>.NET SDK:<\/strong> Latest current (today that\u2019s .NET 9; use 9.x SDK).<\/li>\n\n\n\n<li><strong>Visual Studio 2022<\/strong> or Rider:\n<ul class=\"wp-block-list\">\n<li>Workloads: <strong>ASP.NET and web development<\/strong>, <strong>.NET desktop development<\/strong>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>SQL Server<\/strong>:\n<ul class=\"wp-block-list\">\n<li>SQL Server Developer Edition or SQL Server Express (localdb is fine).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Git<\/strong>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Performance Tools<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>dotnet-counters<\/strong> (<code>dotnet tool<\/code> if not already included): <code>dotnet tool install -g dotnet-counters<\/code><\/li>\n\n\n\n<li><strong>dotTrace<\/strong> &amp; <strong>dotMemory<\/strong>: install via <strong>JetBrains Toolbox<\/strong> (or ReSharper Ultimate).<\/li>\n\n\n\n<li><strong>PerfView<\/strong>: download PerfView.exe from the official GitHub releases page.<\/li>\n\n\n\n<li><strong>BenchmarkDotNet<\/strong>: as a NuGet package (later, in your benchmark project).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Load Testing &amp; Monitoring<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>k6<\/strong> (Windows):\n<ul class=\"wp-block-list\">\n<li>Follow official guide: download MSI or use <code>choco install k6<\/code>. (<a href=\"https:\/\/grafana.com\/docs\/k6\/latest\/set-up\/install-k6\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Grafana Labs<\/a>)<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Datadog account<\/strong> (trial is fine). (<a href=\"https:\/\/dev.to\/kiranrongali\/integrate-datadog-with-a-net-application-1gac?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">DEV Community<\/a>)<\/li>\n\n\n\n<li><strong>Datadog Agent for Windows<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Download &amp; install the Agent (MSI) from Datadog or use the <code>datadog-installer-x86_64.exe<\/code>. (<a href=\"https:\/\/docs.datadoghq.com\/agent\/supported_platforms\/windows\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Datadog .NET Tracer<\/strong> for automatic instrumentation of ASP.NET Core. (<a href=\"https:\/\/docs.datadoghq.com\/tracing\/trace_collection\/automatic_instrumentation\/dd_libraries\/dotnet-core\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. Step 1 \u2013 Clone &amp; Run eShopOnWeb with SQL Server<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 Clone the repo<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">git <span class=\"hljs-keyword\">clone<\/span> https:<span class=\"hljs-comment\">\/\/github.com\/NimblePros\/eShopOnWeb.git<\/span>\ncd eShopOnWeb\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Solution files:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>eShopOnWeb.sln<\/code> \u2013 main solution.<\/li>\n\n\n\n<li>Key projects under <code>src\/<\/code>:\n<ul class=\"wp-block-list\">\n<li><code>Web<\/code> \u2013 ASP.NET Core MVC app.<\/li>\n\n\n\n<li><code>PublicApi<\/code> \u2013 ASP.NET Core Web API.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.2 Configure SQL Server<\/h3>\n\n\n\n<p>The README has a <strong>\u201cConfiguring the sample to use SQL Server\u201d<\/strong> section. (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open <code>src\/Web\/appsettings.json<\/code>.<\/li>\n\n\n\n<li>Verify connection strings point to your local SQL Server, for example:<\/li>\n<\/ol>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-string\">\"ConnectionStrings\"<\/span>: {\n  <span class=\"hljs-string\">\"CatalogConnection\"<\/span>: <span class=\"hljs-string\">\"Server=localhost;Database=CatalogDb;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;\"<\/span>,\n  <span class=\"hljs-string\">\"IdentityConnection\"<\/span>: <span class=\"hljs-string\">\"Server=localhost;Database=IdentityDb;User Id=sa;Password=YourStrong!Passw0rd;TrustServerCertificate=True;\"<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>For localdb:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-string\">\"CatalogConnection\"<\/span>: <span class=\"hljs-string\">\"Server=(localdb)\\\\mssqllocaldb;Database=CatalogDb;Trusted_Connection=True;MultipleActiveResultSets=true\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre><\/blockquote>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>Make sure <code>\"UseOnlyInMemoryDatabase\"<\/code> in <code>appsettings.json<\/code> is <strong>false<\/strong> (or remove the setting) so EF uses SQL Server. (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">4.3 Apply EF Core migrations<\/h3>\n\n\n\n<p>From the <code>src\/Web<\/code> folder:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">cd src\/Web\n\ndotnet restore\ndotnet tool restore   <span class=\"hljs-comment\"># ensures dotnet-ef is available for this repo<\/span>\n\ndotnet ef database update -c catalogcontext -p ..\/Infrastructure\/Infrastructure.csproj -s Web.csproj\ndotnet ef database update -c appidentitydbcontext -p ..\/Infrastructure\/Infrastructure.csproj -s Web.csproj\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This creates and seeds:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Catalog database<\/strong> (products, baskets, etc.)<\/li>\n\n\n\n<li><strong>Identity database<\/strong> (users, roles). (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.4 Run Web &amp; PublicApi<\/h3>\n\n\n\n<p>You need <strong>both<\/strong> running:<\/p>\n\n\n\n<p><strong>Terminal 1 (PublicApi)<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">cd src\/PublicApi\ndotnet run\n<span class=\"hljs-comment\"># Usually listens on https:\/\/localhost:5002 or similar (check console output)<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>Terminal 2 (Web)<\/strong><\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">cd src\/Web\ndotnet run --launch-profile https\n<span class=\"hljs-comment\"># Typically: https:\/\/localhost:5001<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Open browser:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Store front: <code>https:\/\/localhost:5001\/<\/code><\/li>\n\n\n\n<li>Admin (Blazor) UI: <code>https:\/\/localhost:5001\/admin<\/code> (<a href=\"https:\/\/github.com\/NimblePros\/eShopOnWeb\" target=\"_blank\" rel=\"noopener\">GitHub<\/a>)<\/li>\n<\/ul>\n\n\n\n<p>Default seeded login (per README):<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>demouser@microsoft.com<\/code> \/ (check README or seed code for the password; often <code>Pass@word1<\/code> or similar in the sample).<\/li>\n<\/ul>\n\n\n\n<p>Now the app is running and ready for your capstone labs.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Step 2 \u2013 Live Runtime Health with dotnet-counters<\/h2>\n\n\n\n<p><strong>Problem:<\/strong> \u201cIs my service healthy right now? CPU, GC, allocations, thread pool?\u201d<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5.1 Find the process ID<\/h3>\n\n\n\n<p>Run Web + PublicApi. Then:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dotnet-counters ps\n<\/code><\/span><\/pre>\n\n\n<p>You\u2019ll see something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\">12345  <span class=\"hljs-selector-tag\">eShopOnWeb<\/span><span class=\"hljs-selector-class\">.Web<\/span>\n12360  <span class=\"hljs-selector-tag\">eShopOnWeb<\/span><span class=\"hljs-selector-class\">.PublicApi<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">5.2 Monitor PublicApi under light load<\/h3>\n\n\n\n<p>Start monitoring:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">dotnet-counters<\/span> <span class=\"hljs-selector-tag\">monitor<\/span> <span class=\"hljs-selector-tag\">--process-id<\/span> 12360 <span class=\"hljs-selector-tag\">System<\/span><span class=\"hljs-selector-class\">.Runtime<\/span> <span class=\"hljs-selector-tag\">Microsoft-AspNetCore-Server-Kestrel<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Open browser and hit:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>https:\/\/localhost:5001\/catalog<\/code><\/li>\n\n\n\n<li>Or call PublicApi endpoints directly (e.g. <code>\/api\/catalogitems<\/code> \u2013 check controllers).<\/li>\n<\/ul>\n\n\n\n<p>Watch:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>CPU Usage (%)<\/strong><\/li>\n\n\n\n<li><strong>GC Heap Size<\/strong><\/li>\n\n\n\n<li><strong>Gen 0\/1\/2 collections \/ sec<\/strong><\/li>\n\n\n\n<li><strong>ThreadPool Thread Count<\/strong><\/li>\n\n\n\n<li><strong>Requests per second<\/strong> (from Kestrel counters)<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5.3 Capture a short session<\/h3>\n\n\n\n<p>For a 60-second capture:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">dotnet-counters<\/span> <span class=\"hljs-selector-tag\">collect<\/span> <span class=\"hljs-selector-tag\">--process-id<\/span> 12360 <span class=\"hljs-selector-tag\">--duration<\/span> 60 <span class=\"hljs-selector-tag\">--output<\/span> <span class=\"hljs-selector-tag\">publicapi-counters<\/span><span class=\"hljs-selector-class\">.json<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong>Capstone deliverable:<\/strong><br>Students must:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Capture baseline metrics (no load).<\/li>\n\n\n\n<li>Trigger manual browsing.<\/li>\n\n\n\n<li>Compare counters (CPU, allocations, GC) before &amp; during load.<\/li>\n\n\n\n<li>Document \u201cnormal healthy baseline\u201d for this app.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Step 3 \u2013 CPU Profiling with dotTrace<\/h2>\n\n\n\n<p><strong>Problem:<\/strong> \u201cWhy is this endpoint slow? Where is the time going?\u201d<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.1 Scenario to profile<\/h3>\n\n\n\n<p>Pick one <strong>PublicApi<\/strong> endpoint, for example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>GET <code>\/api\/catalogitems<\/code> (list products).<\/li>\n\n\n\n<li>GET <code>\/api\/catalogitems?pageIndex=10&amp;pageSize=20<\/code> (heavier query).<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6.2 Running dotTrace<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open <strong>dotTrace<\/strong>.<\/li>\n\n\n\n<li>Choose:\n<ul class=\"wp-block-list\">\n<li><strong>Profile | .NET Core Application<\/strong>.<\/li>\n\n\n\n<li>Target: <code>src\/PublicApi\/bin\/...\/PublicApi.dll<\/code> <em>or<\/em> launch via solution in VS\/Rider with dotTrace integration.<\/li>\n\n\n\n<li>Profiling type: start with <strong>Timeline<\/strong> or <strong>Sampling<\/strong>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Start profiling.<\/li>\n\n\n\n<li>While profiling, run:\n<ul class=\"wp-block-list\">\n<li>Browser: call the endpoint repeatedly, or<\/li>\n\n\n\n<li>k6 (later step) to generate load.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Stop profiling in dotTrace.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">6.3 Analyze<\/h3>\n\n\n\n<p>In the dotTrace UI:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sort by <strong>\u201cHot Spots\u201d<\/strong> or <strong>Call Tree<\/strong> \u2192 look at:\n<ul class=\"wp-block-list\">\n<li>EF Core query methods (<code>ToListAsync<\/code>, <code>Where<\/code>, <code>Include<\/code>, etc.).<\/li>\n\n\n\n<li>Any custom business methods doing heavy CPU.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Identify:\n<ul class=\"wp-block-list\">\n<li>Expensive LINQ queries.<\/li>\n\n\n\n<li>Over-use of <strong>AutoMapper<\/strong>, <strong>string concatenation<\/strong>, etc.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6.4 Code optimization example<\/h3>\n\n\n\n<p>Students can apply simple optimizations like:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Replace <code>ToList()<\/code> then filtering <strong>in memory<\/strong> with filtering <strong>in the database<\/strong>.<\/li>\n\n\n\n<li>Use <code>AsNoTracking()<\/code> on read-only EF queries.<\/li>\n\n\n\n<li>Cache reference data in memory if it\u2019s safe.<\/li>\n<\/ul>\n\n\n\n<p>Re-profile with dotTrace and show:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Reduced total CPU time.<\/li>\n\n\n\n<li>Reduced time in specific methods.<\/li>\n<\/ul>\n\n\n\n<p><strong>Capstone deliverable:<\/strong><br>Before\/after screenshots of dotTrace with explanation of:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Hot path before optimization.<\/li>\n\n\n\n<li>Code change.<\/li>\n\n\n\n<li>Hot path after optimization (time reduced by X%).<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7. Step 4 \u2013 Memory Profiling with dotMemory<\/h2>\n\n\n\n<p><strong>Problem:<\/strong> \u201cWhy is memory\/GC so high? Any leaks or heavy allocations?\u201d<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7.1 Start dotMemory session<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open <strong>dotMemory<\/strong>.<\/li>\n\n\n\n<li>Attach to <strong>Web<\/strong> or <strong>PublicApi<\/strong> process.<\/li>\n\n\n\n<li>Generate some load:\n<ul class=\"wp-block-list\">\n<li>Browse catalog \/ admin.<\/li>\n\n\n\n<li>Or run a small k6 test for 1\u20132 minutes.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Take a <strong>\u201cGet Snapshot\u201d<\/strong> in dotMemory.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">7.2 Analyze snapshot<\/h3>\n\n\n\n<p>Look for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Top types by bytes<\/strong> (large arrays, strings, DTOs).<\/li>\n\n\n\n<li><strong>Large Object Heap (LOH)<\/strong> usage.<\/li>\n\n\n\n<li><strong>Retention paths<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Are large collections retained by static caches or DI singletons?<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7.3 Optional: Introduce intentional tiny \u201cleak\u201d<\/h3>\n\n\n\n<p>For training, you can introduce a simple bug, e.g.:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;string&gt; DebugCache = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;string&gt;();\n\n<span class=\"hljs-keyword\">public<\/span> Task SomeServiceMethod(...)\n{\n    DebugCache.Add(Guid.NewGuid().ToString());\n    ...\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>After traffic, memory will grow; dotMemory will show the static list retaining objects.<\/p>\n\n\n\n<p><strong>Capstone deliverable:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Snapshot analysis: top 3 types by size.<\/li>\n\n\n\n<li>Explanation of whether memory usage is reasonable or suspicious.<\/li>\n\n\n\n<li>(Optional) detection &amp; fix of intentional \u201cleak,\u201d with before\/after snapshots.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8. Step 5 \u2013 Deep GC\/CPU Analysis with PerfView<\/h2>\n\n\n\n<p><strong>Problem:<\/strong> \u201cI need full low-level view of GC, CPU, allocations, threads.\u201d<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">8.1 Collect trace with PerfView<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Run <code>PerfView.exe<\/code> as Administrator.<\/li>\n\n\n\n<li><strong>Collect<\/strong> \u2192 <strong>Collect\u2026<\/strong><\/li>\n\n\n\n<li>Configure:\n<ul class=\"wp-block-list\">\n<li>Check <strong>CPU Samples<\/strong>, <strong>.NET Alloc<\/strong>, <strong>Thread Time<\/strong>, <strong>GC<\/strong>.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>In <strong>\u201cProcess Filter\u201d<\/strong>, specify <code>PublicApi;Web<\/code> to avoid noise.<\/li>\n\n\n\n<li>Click <strong>Start Collection<\/strong>.<\/li>\n\n\n\n<li>Generate traffic (browser or k6).<\/li>\n\n\n\n<li>Click <strong>Stop Collection<\/strong>.<\/li>\n<\/ol>\n\n\n\n<p>PerfView creates a <code>.etl.zip<\/code> file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">8.2 Analyze<\/h3>\n\n\n\n<p>Open the trace:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>View \u2192 CallTree<\/strong>\n<ul class=\"wp-block-list\">\n<li>Look at <strong>Inclusive CPU (%)<\/strong>; identify top methods.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>View \u2192 GCStats<\/strong>\n<ul class=\"wp-block-list\">\n<li>GC pause times.<\/li>\n\n\n\n<li>Gen 2 \/ LOH activity.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>View \u2192 Events \u2192 GC\/AllocationTick<\/strong>\n<ul class=\"wp-block-list\">\n<li>Allocation hotspots per type\/method.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Compare <strong>before and after your earlier optimizations<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Less CPU in certain methods.<\/li>\n\n\n\n<li>Less GC time \/ fewer Gen 2 collections.<\/li>\n\n\n\n<li>Lower allocation rates.<\/li>\n<\/ul>\n\n\n\n<p><strong>Capstone deliverable:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Short report (one slide or doc section) summarizing PerfView findings:\n<ul class=\"wp-block-list\">\n<li>\u201cTop 3 CPU-heavy methods.\u201d<\/li>\n\n\n\n<li>\u201cGC characteristics under load.\u201d<\/li>\n\n\n\n<li>\u201cImpact of optimization X on GC stats.\u201d<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">9. Step 6 \u2013 Micro-benchmarks with BenchmarkDotNet<\/h2>\n\n\n\n<p><strong>Problem:<\/strong> \u201cIs optimization A actually faster than B? LINQ vs loop, etc.\u201d<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">9.1 Create benchmark project<\/h3>\n\n\n\n<p>In solution root:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">dotnet <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">console<\/span> -n EShop.Benchmarks\ncd EShop.Benchmarks\ndotnet add package BenchmarkDotNet\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Edit <code>Program.cs<\/code> to something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">using BenchmarkDotNet.Attributes;\nusing BenchmarkDotNet.Running;\nusing System.Linq;\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">CatalogFilterBenchmarks<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">private<\/span> readonly <span class=\"hljs-keyword\">List<\/span>&lt;int&gt; _data;\n\n    <span class=\"hljs-keyword\">public<\/span> CatalogFilterBenchmarks()\n    {\n        _data = Enumerable.Range(<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">100<\/span>_000).ToList();\n    }\n\n    &#91;Benchmark]\n    <span class=\"hljs-keyword\">public<\/span> int LinqWhereCount()\n    {\n        <span class=\"hljs-keyword\">return<\/span> _data.Where(x =&gt; x % <span class=\"hljs-number\">2<\/span> == <span class=\"hljs-number\">0<\/span>).Count();\n    }\n\n    &#91;Benchmark]\n    <span class=\"hljs-keyword\">public<\/span> int ForLoopCount()\n    {\n        <span class=\"hljs-keyword\">var<\/span> count = <span class=\"hljs-number\">0<\/span>;\n        <span class=\"hljs-keyword\">for<\/span> (int i = <span class=\"hljs-number\">0<\/span>; i &lt; _data.Count; i++)\n        {\n            <span class=\"hljs-keyword\">if<\/span> (_data&#91;i] % <span class=\"hljs-number\">2<\/span> == <span class=\"hljs-number\">0<\/span>) count++;\n        }\n        <span class=\"hljs-keyword\">return<\/span> count;\n    }\n}\n\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Program<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">static<\/span> void Main(string&#91;] args)\n    {\n        BenchmarkRunner.Run&lt;CatalogFilterBenchmarks&gt;();\n    }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dotnet run -c Release\n<\/code><\/span><\/pre>\n\n\n<p>You\u2019ll get:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Mean execution times.<\/li>\n\n\n\n<li>Std deviation.<\/li>\n\n\n\n<li>Allocations per operation.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">9.2 Link to real code<\/h3>\n\n\n\n<p>For stronger capstone:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Extract a <strong>real method<\/strong> from eShopOnWeb (e.g., a pricing calculation, filtering logic, mapping).<\/li>\n\n\n\n<li>Create <strong>CurrentImplementation<\/strong> vs <strong>OptimizedImplementation<\/strong>.<\/li>\n\n\n\n<li>Benchmark both with <code>BenchmarkDotNet<\/code>.<\/li>\n<\/ul>\n\n\n\n<p><strong>Capstone deliverable:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Benchmark table showing:\n<ul class=\"wp-block-list\">\n<li>Methods A vs B.<\/li>\n\n\n\n<li>Time &amp; allocations.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Narrative: \u201cWe chose B because it\u2019s 30% faster with 50% fewer allocations.\u201d<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10. Step 7 \u2013 Load Testing with k6<\/h2>\n\n\n\n<p><strong>Goal:<\/strong> Generate load against PublicApi \/ Web, correlate with dotnet-counters, PerfView, Datadog.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">10.1 Install k6 (Windows)<\/h3>\n\n\n\n<p>Follow the k6 docs (choco, MSI or binary). (<a href=\"https:\/\/grafana.com\/docs\/k6\/latest\/set-up\/install-k6\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">Grafana Labs<\/a>)<\/p>\n\n\n\n<p>Verify:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">k6 version\n<\/code><\/span><\/pre>\n\n\n<h3 class=\"wp-block-heading\">10.2 Create test script <code>eshop-load.js<\/code><\/h3>\n\n\n\n<p>Example script hitting a catalog endpoint:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> http <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'k6\/http'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { check, sleep } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'k6'<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">let<\/span> options = {\n  <span class=\"hljs-attr\">vus<\/span>: <span class=\"hljs-number\">50<\/span>,\n  <span class=\"hljs-attr\">duration<\/span>: <span class=\"hljs-string\">'2m'<\/span>,\n  <span class=\"hljs-attr\">thresholds<\/span>: {\n    <span class=\"hljs-attr\">http_req_duration<\/span>: &#91;<span class=\"hljs-string\">'p(95)&lt;500'<\/span>], <span class=\"hljs-comment\">\/\/ 95% &lt; 500ms<\/span>\n    <span class=\"hljs-attr\">http_req_failed<\/span>: &#91;<span class=\"hljs-string\">'rate&lt;0.01'<\/span>],   <span class=\"hljs-comment\">\/\/ &lt; 1% errors<\/span>\n  },\n};\n\n<span class=\"hljs-keyword\">const<\/span> BASE_URL = <span class=\"hljs-string\">'https:\/\/localhost:5002'<\/span>; <span class=\"hljs-comment\">\/\/ PublicApi HTTPS port<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> (<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">let<\/span> res = http.get(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${BASE_URL}<\/span>\/api\/catalogitems?pageIndex=0&amp;pageSize=20`<\/span>);\n  \n  check(res, {\n    <span class=\"hljs-string\">'status is 200'<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">r<\/span>) =&gt;<\/span> r.status === <span class=\"hljs-number\">200<\/span>,\n  });\n  \n  sleep(<span class=\"hljs-number\">1<\/span>);\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>If HTTPS\/localhost causes TLS issues, you can run k6 with <code>--insecure-skip-tls-verify<\/code>.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">10.3 Run the test<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">k6<\/span> <span class=\"hljs-selector-tag\">run<\/span> <span class=\"hljs-selector-tag\">eshop-load<\/span><span class=\"hljs-selector-class\">.js<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>While k6 runs, also:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>dotnet-counters monitor<\/code> on PublicApi.<\/li>\n\n\n\n<li>Optionally, another PerfView trace capture.<\/li>\n<\/ul>\n\n\n\n<p><strong>Capstone deliverable:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>k6 output screenshots (RPS, latency, failures).<\/li>\n\n\n\n<li>Counters snapshot (GC, CPU).<\/li>\n\n\n\n<li>Discussion: \u201cUnder 50 VUs, 95p latency = X ms, error rate Y%.\u201d<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11. Step 8 \u2013 Datadog Agent &amp; Dashboard<\/h2>\n\n\n\n<p>Now we\u2019ll wire the app into <strong>Datadog APM + metrics<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">11.1 Install Datadog Agent on Windows<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Sign in to Datadog.<\/li>\n\n\n\n<li>Go to <strong>Integrations \u2192 Agent \u2192 Windows<\/strong>.<\/li>\n\n\n\n<li>Download the installer (or <code>datadog-installer-x86_64.exe<\/code>) and run it. (<a href=\"https:\/\/docs.datadoghq.com\/agent\/supported_platforms\/windows\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/li>\n\n\n\n<li>During setup, supply your <strong>Datadog API key<\/strong>.<\/li>\n\n\n\n<li>After install:\n<ul class=\"wp-block-list\">\n<li>Confirm service is running.<\/li>\n\n\n\n<li>In Datadog UI, your host should appear under <strong>Infrastructure \u2192 Host Map<\/strong>.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">11.2 Install Datadog .NET Tracer<\/h3>\n\n\n\n<p>Follow the <strong>\u201cTracing .NET Core Applications\u201d<\/strong> documentation: (<a href=\"https:\/\/docs.datadoghq.com\/tracing\/trace_collection\/automatic_instrumentation\/dd_libraries\/dotnet-core\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/p>\n\n\n\n<p>On Windows the common approach is:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Download &amp; run the latest <strong>Datadog .NET Tracer MSI<\/strong> (from Datadog docs).<\/li>\n\n\n\n<li>This sets environment variables and profiler DLLs system-wide.<\/li>\n<\/ol>\n\n\n\n<p>Key environment variables (usually set by installer, but you can override per app):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">DD_SERVICE=eshop-publicapi\nDD_ENV=perf-lab\nDD_VERSION=<span class=\"hljs-number\">1.0<\/span><span class=\"hljs-number\">.0<\/span>\nDD_AGENT_HOST=localhost\nDD_TRACE_ENABLED=<span class=\"hljs-literal\">true<\/span>\nDD_LOGS_INJECTION=<span class=\"hljs-literal\">true<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>For Web app:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">DD_SERVICE=eshop-web\nDD_ENV=perf-lab\nDD_VERSION=1.0.0\nDD_AGENT_HOST=localhost\n<\/code><\/span><\/pre>\n\n\n<p>Restart your Web &amp; PublicApi processes after installing tracer \/ setting env vars.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">11.3 Generate traffic and verify traces<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Run Web + PublicApi.<\/li>\n\n\n\n<li>Run your <strong>k6<\/strong> test again.<\/li>\n\n\n\n<li>In Datadog:\n<ul class=\"wp-block-list\">\n<li>Go to <strong>APM \u2192 Services<\/strong>.<\/li>\n\n\n\n<li>You should see services like <code>eshop-publicapi<\/code>, <code>eshop-web<\/code> appear.<\/li>\n\n\n\n<li>Click into a service, open some <strong>traces<\/strong>, and inspect:\n<ul class=\"wp-block-list\">\n<li>Request path.<\/li>\n\n\n\n<li>DB spans (SQL statements, durations).<\/li>\n\n\n\n<li>Error spans (if any).<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>Datadog docs for .NET tracing show how DB spans and HTTP spans are captured automatically via automatic instrumentation. (<a href=\"https:\/\/docs.datadoghq.com\/tracing\/trace_collection\/automatic_instrumentation\/dd_libraries\/dotnet-core\/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener\">docs.datadoghq.com<\/a>)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">11.4 Create a Datadog Dashboard<\/h3>\n\n\n\n<p>In Datadog:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Dashboards \u2192 New Dashboard<\/strong>.<\/li>\n\n\n\n<li>Add widgets:\n<ul class=\"wp-block-list\">\n<li><strong>Timeseries: <code>avg:aspnet_core.request.duration{service:eshop-publicapi}<\/code><\/strong><\/li>\n\n\n\n<li><strong>Timeseries: <code>sum:aspnet_core.request.hits{service:eshop-publicapi}<\/code> (RPS)<\/strong><\/li>\n\n\n\n<li><strong>Query: <code>sum:system.cpu.user{host:YOUR_HOST}<\/code><\/strong> (CPU).<\/li>\n\n\n\n<li><strong><code>aspnet_core.requests.errors<\/code><\/strong> for error rate.<\/li>\n\n\n\n<li>If the tracer exposes GC metrics (via runtime integration), show:\n<ul class=\"wp-block-list\">\n<li>GC pause time.<\/li>\n\n\n\n<li>Gen 2 collections.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Add a <strong>Top List<\/strong> for:\n<ul class=\"wp-block-list\">\n<li><code>top(avg:trace.http.request.duration{service:eshop-publicapi} by {resource})<\/code><\/li>\n\n\n\n<li>(Shows slowest endpoints.)<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><strong>Capstone deliverable:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A screenshot of the Datadog dashboard.<\/li>\n\n\n\n<li>Explanation of key charts:\n<ul class=\"wp-block-list\">\n<li>CPU vs request rate during k6.<\/li>\n\n\n\n<li>95p latency &amp; error rate.<\/li>\n\n\n\n<li>Slowest endpoints list.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">12. How It All Ties Together (Capstone Storyline)<\/h2>\n\n\n\n<p>To help you present this as a <strong>coherent capstone<\/strong>, here\u2019s the narrative:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Setup &amp; Baseline<\/strong>\n<ul class=\"wp-block-list\">\n<li>Run eShopOnWeb with SQL Server.<\/li>\n\n\n\n<li>Confirm basic functionality.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Observe Live Health (dotnet-counters)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Identify baseline CPU, GC, allocations under light load.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Generate Load (k6)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Simulate real traffic to PublicApi.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Investigate CPU (dotTrace)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Profile slow endpoints.<\/li>\n\n\n\n<li>Optimize EF queries \/ business logic.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Investigate Memory (dotMemory)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Look for high allocations or leaks.<\/li>\n\n\n\n<li>Fix or justify memory patterns.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Deep-Dive (PerfView)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Verify GC\/CPU behavior at low level.<\/li>\n\n\n\n<li>Validate impact of optimization.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Micro-Benchmarks (BenchmarkDotNet)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Benchmark critical internal methods to choose best implementation.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>End-to-End Observability (Datadog)<\/strong>\n<ul class=\"wp-block-list\">\n<li>Visualize everything in one dashboard: latency, errors, CPU, GC, SQL.<\/li>\n\n\n\n<li>Correlate k6 load, runtime counters, and APM traces.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>By the end, students experience <strong>the full lifecycle<\/strong>:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Bug \/ performance problem \u2192 measurement (counters, traces, profilers) \u2192 hypothesis \u2192 code &amp; DB change \u2192 benchmark \u2192 load test \u2192 monitor in Datadog.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Recommended GitHub Application Chosen app: NimblePros\/eShopOnWeb Repo: https:\/\/github.com\/NimblePros\/eShopOnWeb (GitHub) Why this one: We\u2019ll treat: 2. Capstone: High-Level Structure Your \u201cDOTNET Performance Optimization Capstone\u201d will use eShopOnWeb and walk students&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-54350","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=54350"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54350\/revisions"}],"predecessor-version":[{"id":54351,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54350\/revisions\/54351"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=54350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=54350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=54350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}