<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ev Vasilev</title>
    <description>The latest articles on DEV Community by Ev Vasilev (@ev_vasilev).</description>
    <link>https://dev.to/ev_vasilev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2715676%2F26268bae-adf0-4bbc-ab7c-244e0a943754.jpg</url>
      <title>DEV Community: Ev Vasilev</title>
      <link>https://dev.to/ev_vasilev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ev_vasilev"/>
    <language>en</language>
    <item>
      <title>How We Build Reliable .NET Applications: Clean Architecture &amp; Development Culture</title>
      <dc:creator>Ev Vasilev</dc:creator>
      <pubDate>Thu, 13 Feb 2025 14:54:28 +0000</pubDate>
      <link>https://dev.to/ev_vasilev/how-we-build-reliable-net-applications-clean-architecture-development-culture-l8f</link>
      <guid>https://dev.to/ev_vasilev/how-we-build-reliable-net-applications-clean-architecture-development-culture-l8f</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Introduction&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;1.1. What Is Clean Architecture &amp;amp; Why It Matters&lt;/li&gt;
&lt;li&gt;1.2. Our Commitment to Quality &amp;amp; Simplicity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture Overview&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
2.1. Project Structure Overview

&lt;ul&gt;
&lt;li&gt;Dependency Diagram&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2.2. Principles: Separation of Concerns, Clear Boundaries, Scalability&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Achieving Reliability&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;3.1. Flexible Testing Strategy&lt;/li&gt;
&lt;li&gt;3.2. Automated Migrations &amp;amp; Monitoring&lt;/li&gt;
&lt;li&gt;3.3. Health Checks &amp;amp; Observability&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team Collaboration &amp;amp; Growth&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;4.1. ADR as a Knowledge Sharing Tool&lt;/li&gt;
&lt;li&gt;4.2. Code Reviews &amp;amp; Mentorship&lt;/li&gt;
&lt;li&gt;4.3. Reducing Onboarding Time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strengthening Teams Through Simplicity&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;5.1. Controllers &amp;amp; Use Cases Free of Excess Logic&lt;/li&gt;
&lt;li&gt;5.2. Transparent Folder Structure &amp;amp; Code Style&lt;/li&gt;
&lt;li&gt;5.3. Minimal Boilerplate via Shared Configs&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adapting to Change&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;6.1. Service Scaling&lt;/li&gt;
&lt;li&gt;6.2. Long-Term Maintenance&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Looking Ahead&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;7.1. Continuous Improvement Plan&lt;/li&gt;
&lt;li&gt;7.2. Invitation to Collaborate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conclusion&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;8.1. Key Benefits for Clients, Developers, &amp;amp; Business&lt;/li&gt;
&lt;li&gt;8.2. Final Thoughts on Innovation &amp;amp; Culture&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1. What Is Clean Architecture &amp;amp; Why It Matters
&lt;/h3&gt;

&lt;p&gt;Clean Architecture splits application code into distinct layers. Its main goal is &lt;strong&gt;separation of concerns&lt;/strong&gt;, &lt;strong&gt;simplicity&lt;/strong&gt;, and &lt;strong&gt;adaptability&lt;/strong&gt;. This lets us change or add components without breaking existing logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Easier Maintenance:&lt;/strong&gt; Clearly separated code is simpler to test, debug, and update.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility:&lt;/strong&gt; Not tied to a specific framework or database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-Term Viability:&lt;/strong&gt; Scalable and easier to maintain as the project grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In .NET, it means isolating business logic (Core) from implementations (Infrastructure, API, etc.), boosting reliability and ease of change.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2. Our Commitment to Quality &amp;amp; Simplicity
&lt;/h3&gt;

&lt;p&gt;Clean Architecture isn’t just a buzzword. We focus on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Quality:&lt;/strong&gt; Transparent code structure minimizes errors and duplication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity:&lt;/strong&gt; Less noise means faster feature delivery and maintenance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We invest in processes like ADRs to document decisions and help everyone on the team understand the architecture and its evolution.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Architecture Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1. Project Structure Overview
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Solution/
├── src/
│   ├── API                      // ASP.NET Core Web API: Controllers, middleware, routing, configuration.
│   ├── Core                     // Business logic, use cases, interfaces, dependency injection registration.
│   ├── Infrastructure           // EF Core DbContext, repository implementations, external integrations, caching, dependency injection registration.
│   ├── Consumers                // Message consumers (e.g., using Kafka/RabbitMQ clients).
│   ├── Jobs                     // Background job processing (using Hangfire or similar).
│   ├── Contracts                // Domain models, Shared DTOs and API contracts.
│   ├── Data.Migration           // Console app for running database migrations.
│   └── Tests                    // Tests
│       ├── Tests.Unit           // Unit tests for Core and isolated components.
│       └── Tests.Integration    // Integration tests using Docker/Testcontainers.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our .NET ecosystem is split into several projects with defined roles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;API&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles HTTP requests/responses.&lt;/li&gt;
&lt;li&gt;Focuses on controllers and middleware for security, routing, and input validation.&lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;Core&lt;/strong&gt; for business logic and &lt;strong&gt;Infrastructure&lt;/strong&gt; for data access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Core&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains business logic (Services, UseCases), rules (Validators), and interfaces.&lt;/li&gt;
&lt;li&gt;Framework-agnostic to avoid binding logic to specific implementations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implements data access, external service integration, etc.&lt;/li&gt;
&lt;li&gt;Provides concrete implementations of Core interfaces.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consumers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Processes messages from external queues (e.g., Kafka, RabbitMQ).&lt;/li&gt;
&lt;li&gt;Leverages Core for business logic and Infrastructure for data operations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Jobs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages background tasks (using tools like Hangfire).&lt;/li&gt;
&lt;li&gt;Uses Core for rules and Infrastructure for data access.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit Tests:&lt;/strong&gt; Isolated testing of Core components with mocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Tests:&lt;/strong&gt; Verify interactions between API, Core, and Infrastructure, often using Docker Compose.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Migrations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contains scripts and migrations to update the database schema.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Contracts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines shared DTOs, models, and controller interfaces.&lt;/li&gt;
&lt;li&gt;Provides universal descriptions for communication across services.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Dependency Diagram&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbr355o75r7lci39brvsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbr355o75r7lci39brvsb.png" alt="Image description" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;PlantUml&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml CleanArchitectureDependencies
allowmixing

!define ProjectColor LightSteelBlue
!define FolderColor LightGray
!define InterfaceColor LightGoldenRodYellow
!define ClassColor PaleGreen

skinparam package {
  BackgroundColor ProjectColor
  BorderColor Black
}

skinparam class {
  BackgroundColor FolderColor
  BorderColor Black
  ArrowColor Black
}

package "API Project" as Api {
  folder "Controllers" as Api.Controllers {
    class Controller {
    }
  }
}

package "Contracts Project" as Contracts {
  folder "DTOs" as Contracts.DTOs {
    class DTOs
  }
  folder "Models" as Contracts.Models {
    class Models
  }
  folder "Controller Interfaces" as Contracts.ControllerInterfaces {
    interface IController
  }
  folder "Clients" as Contracts.Clients {
    interface IApiClient
  }  
  folder "Events" as Contracts.Events {
    class Event
  }
}

package "Core Project" as Core {
  folder "UseCases" as Core.UseCases {
    class UseCase
  }
  folder "Services" as Core.Services {
    class Service
  }
  folder "Interfaces" as Core.Interfaces {
    interface IRepository
    interface IExternalServiceClient
  }
  folder "Validators" as Core.Validators {
    class Validator
  }
}

package "Infrastructure Project" as Infrastructure {
  folder "Repositories" as Infrastructure.Repositories {
    class Repository {
       +DbContext
    }
  }
  folder "Entities" as Infrastructure.Entities {
    class Entity
  }
  folder "External Service Clients" as Infrastructure.ExternalServiceClients {
    class ExternalServiceClient {
        +HttpClient
    }
  }
  folder "DI" as Infrastructure.DI {
    class InfrastructureDI
  }
}

package "Consumers Project" as Consumers {
  folder "Event Processing" as Consumers.EventProcessing {
    class EventProcessor
  }
}

package "Jobs Project" as Jobs {
  folder "Jobs" as Jobs.Jobs {
    class Job
  }
}

package "Data Migrations Project" as DataMigrations {
  folder "Migrations" as DataMigrations.Migrations {
    class Migration
  }
}

package "Tests" as Tests {
    package "Unit Tests Project" as Tests.Unit {
      folder "Unit Tests" as Tests.Unit.UnitTests {
        class UnitTest
      }
    }

    package "Integration Tests Project" as Tests.Integration {
      folder "Integration Tests" as Tests.Integration.IntegrationTests {
        class IntegrationTest
      }
    }
}

database "Database" as DB

' Dependencies arrows
Api.Controllers.Controller -u-&amp;gt; Contracts.ControllerInterfaces.IController : Implements
Api.Controllers.Controller --&amp;gt; Core.UseCases.UseCase : Uses
Api.Controllers.Controller -d-&amp;gt; Contracts.Events.Event : Uses
Api --&amp;gt; Infrastructure.DI.InfrastructureDI : Uses
Api -r-&amp;gt; Contracts : Uses

Contracts.ControllerInterfaces.IController -d-&amp;gt; Contracts.DTOs.DTOs : Uses
Contracts.Clients.IApiClient -u-&amp;gt; Contracts.ControllerInterfaces.IController : Implements

Core.UseCases.UseCase --&amp;gt; Core.Services.Service : Uses
Core.UseCases.UseCase --&amp;gt; Core.Interfaces.IRepository : Uses
Core.UseCases.UseCase --&amp;gt; Core.Interfaces.IExternalServiceClient : Uses
Core.UseCases.UseCase -r-&amp;gt; Contracts.DTOs.DTOs : Uses
Core.Services.Service --&amp;gt; Core.Interfaces.IRepository : Uses
Core.Services.Service -u-&amp;gt; Core.Validators.Validator : Uses
Core.Services.Service -r-&amp;gt; Contracts.Models.Models : Uses

Infrastructure.Repositories.Repository -u-&amp;gt; Core.Interfaces.IRepository : Implements
Infrastructure.Repositories.Repository --&amp;gt; Infrastructure.Entities.Entity : Uses
Infrastructure.Repositories.Repository -d-&amp;gt; DB : Uses
Infrastructure.ExternalServiceClients.ExternalServiceClient -u-&amp;gt; Core.Interfaces.IExternalServiceClient : Implements
Infrastructure.ExternalServiceClients.ExternalServiceClient -r-&amp;gt; Contracts.Clients.IApiClient : Uses
Infrastructure.DI.InfrastructureDI --&amp;gt; Core.Interfaces.IRepository : Registers
Infrastructure.DI.InfrastructureDI --&amp;gt; Core.Interfaces.IExternalServiceClient : Registers

Consumers.EventProcessing.EventProcessor -u-&amp;gt; Contracts.Events.Event : Uses
Consumers.EventProcessing.EventProcessor -d-&amp;gt; Contracts.Clients.IApiClient : Uses

Jobs.Jobs.Job --&amp;gt; Core.UseCases.UseCase : Uses

DataMigrations.Migrations.Migration --&amp;gt; Infrastructure.Repositories.Repository : Uses

Tests.Unit.UnitTests.UnitTest --&amp;gt; Core : Tests
Tests.Integration.IntegrationTests.IntegrationTest -r-&amp;gt; Api : Tests
Tests.Integration.IntegrationTests.IntegrationTest --&amp;gt; Core : Tests
Tests.Integration.IntegrationTests.IntegrationTest -l-&amp;gt; Infrastructure : Tests

' Styling
hide empty members
@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2.2. Principles: Separation of Concerns, Clear Boundaries, Scalability
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separation of Concerns (SoC)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each class or module has a single responsibility.&lt;/li&gt;
&lt;li&gt;Controllers don’t contain business logic; they simply forward data to Use Cases.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Clear Boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Layers interact only through well-defined interfaces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core&lt;/strong&gt; relies on abstractions, not on specific implementations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure&lt;/strong&gt; handles external communication without business logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modular design lets us scale API, Consumers, or Jobs independently.&lt;/li&gt;
&lt;li&gt;Facilitates refactoring and task distribution across teams.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. Achieving Reliability
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1. Flexible Testing Strategy
&lt;/h3&gt;

&lt;p&gt;We cover all application layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unit Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test individual Core classes and methods with mocks.&lt;/li&gt;
&lt;li&gt;Fast feedback and isolated error detection.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Integration Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify interactions among API, Core, and Infrastructure.&lt;/li&gt;
&lt;li&gt;Use Testcontainers (PostgreSQL) with Docker Compose for real-environment emulation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;End-to-End Tests (E2E)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simulate the full system flow from HTTP request to DB write.&lt;/li&gt;
&lt;li&gt;Run tests in Docker Compose, including external services, for full-cycle validation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests catch logic errors quickly.&lt;/li&gt;
&lt;li&gt;Integration/E2E tests prevent surprises with real services.&lt;/li&gt;
&lt;li&gt;All tests run in CI/CD to catch regressions early.&lt;/li&gt;
&lt;li&gt;Use retry policies (Polly) to handle unstable environments.&lt;/li&gt;
&lt;li&gt;Scripts like &lt;code&gt;dotnet test&lt;/code&gt; and ReportGenerator simplify test execution and analysis.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2. Automated Migrations &amp;amp; Monitoring
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Migrations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A dedicated project holds SQL scripts and FluentMigrator migrations.&lt;/li&gt;
&lt;li&gt;Schema updates are automatically checked during startup or CI/CD.&lt;/li&gt;
&lt;li&gt;Ensures consistent DB evolution without hidden changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tools like Elastic APM track performance, errors, and metrics in real time.&lt;/li&gt;
&lt;li&gt;Helps spot and fix issues before they impact users.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Alerting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alerts (e.g., via Slack) trigger on abnormal response times or error rates.&lt;/li&gt;
&lt;li&gt;Enables quick incident response.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.3. Health Checks &amp;amp; Observability
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Health Checks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each service exposes a &lt;code&gt;/health&lt;/code&gt; endpoint checking DB, cache, external APIs, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Logging&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Serilog for structured logging to the console.&lt;/li&gt;
&lt;li&gt;Logs integrate with tools like Filebeat; sensitive data is carefully handled.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracing &amp;amp; Metrics&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenTelemetry or Elastic APM trace requests across all layers.&lt;/li&gt;
&lt;li&gt;Metrics (operation durations, message counts) help analyze load and scale services.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Team Collaboration &amp;amp; Growth
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1. ADR as a Knowledge Sharing Tool
&lt;/h3&gt;

&lt;p&gt;Architecture Decision Records (ADRs) document key decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Record decisions on project structure, tools, data schemas, etc.&lt;/li&gt;
&lt;li&gt;Explain the rationale and alternatives considered.&lt;/li&gt;
&lt;li&gt;Provide transparent context for the whole team, aiding new members.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.2. Code Reviews &amp;amp; Mentorship
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Reviews:&lt;/strong&gt; Every pull request is checked for both correctness and adherence to Clean Architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pair/Mob Programming:&lt;/strong&gt; Accelerate complex tasks and share expertise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mentorship:&lt;/strong&gt; Senior developers guide newcomers, reducing the onboarding curve.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4.3. Reducing Onboarding Time
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear Separation:&lt;/strong&gt; Distinct roles for API, Core, Infrastructure, Consumers, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ADRs:&lt;/strong&gt; Offer historical context for decisions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standardized Practices:&lt;/strong&gt; Unified DI, logging, and testing setups help new developers get up to speed fast.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Strengthening Teams Through Simplicity
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1. Controllers &amp;amp; Use Cases Free of Excess Logic
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Controllers:&lt;/strong&gt; Handle request validation and forward data to Use Cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use Cases:&lt;/strong&gt; Encapsulate business logic without peripheral concerns.&lt;/li&gt;
&lt;li&gt;Reduces coupling and makes functionality easy to locate.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.2. Transparent Folder Structure &amp;amp; Code Style
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Core:&lt;/strong&gt; Contains models, services, use cases, and interfaces.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure:&lt;/strong&gt; Houses entities, repositories, external clients, and DI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API, Jobs, Consumers:&lt;/strong&gt; Each has its own folders and middleware.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contracts:&lt;/strong&gt; Defines shared DTOs and interfaces.&lt;/li&gt;
&lt;li&gt;EditorConfig and Directory.Build.props standardize formatting and linting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5.3. Minimal Boilerplate via Shared Configs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared Configs:&lt;/strong&gt; 

&lt;ul&gt;
&lt;li&gt;Directory.Packages.props manages NuGet package versions.&lt;/li&gt;
&lt;li&gt;DI extensions centralize service configuration (logging, Swagger, Health Checks).&lt;/li&gt;
&lt;li&gt;Docker Compose provides a one-command local setup.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;This minimizes boilerplate and lets developers focus on business logic.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  6. Adapting to Change
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1. Service Scaling
&lt;/h3&gt;

&lt;p&gt;As the project grows, new subdomains emerge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Subdomain Segregation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Core, create namespaces like &lt;code&gt;Core.Services.[Subdomain]&lt;/code&gt; and &lt;code&gt;Core.UseCases.[Subdomain]&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In Infrastructure, segregate repositories similarly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; For Customer Management:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Core.Services.CustomerManagement&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Core.UseCases.CustomerManagement&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Infrastructure.Repositories.CustomerManagement&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Gradual Splitting&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a module becomes too complex, extract it into its own microservice.&lt;/li&gt;
&lt;li&gt;Core logic remains reusable, thanks to clear separations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Example:&lt;/strong&gt; For Order Management:

&lt;ul&gt;
&lt;li&gt;Separate libraries for Core and Infrastructure.&lt;/li&gt;
&lt;li&gt;Create an OrderService with its own repository.&lt;/li&gt;
&lt;li&gt;Communicate via REST or message brokers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Scaling Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API:&lt;/strong&gt; Horizontal scaling via load balancers; caching (Redis) to reduce DB load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumers:&lt;/strong&gt; Use competing consumers or partition topics for parallel processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jobs:&lt;/strong&gt; Run tasks in parallel (Hangfire) or distribute by task type.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.2. Long-Term Maintenance
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Regular Refactoring&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scheduled sessions to address technical debt and optimize code.&lt;/li&gt;
&lt;li&gt;ADRs track changes and reasons.&lt;/li&gt;
&lt;li&gt;Example: Splitting complex modules or optimizing DB queries.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Smooth Tech Transitions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching technologies (e.g., PostgreSQL to MS SQL) only affects Infrastructure.&lt;/li&gt;
&lt;li&gt;Example: Implement a new &lt;code&gt;IRepository&lt;/code&gt; for MS SQL, update DI, test, and adjust migrations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  7. Looking Ahead
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1. Continuous Improvement Plan
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Regularly review and update ADRs.&lt;/li&gt;
&lt;li&gt;Internal meetups and workshops on Clean Architecture, DevOps, etc.&lt;/li&gt;
&lt;li&gt;Maximize automation—from testing to environment setup—to focus on business logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.2. Invitation to Collaborate
&lt;/h3&gt;

&lt;p&gt;We’re growing and always seek talented developers, DevOps engineers, and analysts who value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clean, Simple Code&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Openness to New Ideas &amp;amp; Continuous Learning&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teamwork &amp;amp; Knowledge Sharing&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. Conclusion
&lt;/h2&gt;

&lt;h3&gt;
  
  
  8.1. Key Benefits for Clients, Developers, &amp;amp; Business
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clients:&lt;/strong&gt; Stable, reliable services that evolve quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developers:&lt;/strong&gt; Clean, modular code reduces risk and boosts professional growth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Business:&lt;/strong&gt; Faster time-to-market with high quality and predictable support.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  8.2. Final Thoughts on Innovation &amp;amp; Culture
&lt;/h3&gt;

&lt;p&gt;Our architecture is a living system reflecting our values: &lt;strong&gt;transparency&lt;/strong&gt;, &lt;strong&gt;collaboration&lt;/strong&gt;, and &lt;strong&gt;sustainable growth&lt;/strong&gt;. Regular ADRs, comprehensive testing, flexible infrastructure, and a supportive team environment keep us competitive and adaptive in a changing market.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Quick Start: Elasticsearch + OpenTelemetry Collector</title>
      <dc:creator>Ev Vasilev</dc:creator>
      <pubDate>Thu, 16 Jan 2025 12:07:34 +0000</pubDate>
      <link>https://dev.to/ev_vasilev/quick-start-elasticsearch-opentelemetry-collector-d65</link>
      <guid>https://dev.to/ev_vasilev/quick-start-elasticsearch-opentelemetry-collector-d65</guid>
      <description>&lt;p&gt;I recently began overhauling our logging architecture to address several challenges, including centralized log management, debugging difficulties, performance overhead, rate limiting, authentication concerns, data enrichment needs, and robust error handling. To validate the new approach, I developed a Proof of Concept (PoC) and an Architectural Decision Record (ADR), which I have detailed in this blog post to share our findings and the benefits of the updated architecture.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why This Architecture?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
In a distributed microservices world, observability becomes a &lt;em&gt;must-have&lt;/em&gt; rather than a &lt;em&gt;nice-to-have&lt;/em&gt;. Traditional “log to disk” methods don’t cut it when you need to troubleshoot cross-service issues or correlate logs with traces. By &lt;strong&gt;combining&lt;/strong&gt; Elasticsearch with an &lt;strong&gt;OpenTelemetry Collector&lt;/strong&gt;, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Centralized Logs&lt;/strong&gt; for simplified searching and analytics.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured&lt;/strong&gt;, &lt;strong&gt;ECS-compliant&lt;/strong&gt; log data, making it easy to parse and visualize.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt; and &lt;strong&gt;resilience&lt;/strong&gt; via the Collector’s buffering, batching, and resource limits.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-proofing&lt;/strong&gt;: OpenTelemetry is quickly becoming the standard for logs, metrics, and traces.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problems Solved&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexible Indexing&lt;/strong&gt;: This architecture allows you to control which index the logs are sent to.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficult debugging&lt;/strong&gt;: With distributed tracing and ECS fields, you can quickly pinpoint errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance overhead&lt;/strong&gt;: The Collector offloads log processing tasks from your app, so your services can focus on business logic.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate Limiting&lt;/strong&gt;: Manage the flow of log data to prevent system overload and ensure consistent performance.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Authentication&lt;/strong&gt;: Secure the log pipeline by ensuring only authorized sources can send logs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Enrichment&lt;/strong&gt;: Enhance logs with additional context for more insightful analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling&lt;/strong&gt;: Implement robust error handling to gracefully manage failures in log processing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring and Alerts&lt;/strong&gt;: Set up monitoring and alerting to proactively address issues in the logging pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Additional Reading&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://opentelemetry.io/docs/" rel="noopener noreferrer"&gt;OpenTelemetry Official Docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/guide/en/ecs/current/ecs-reference.html" rel="noopener noreferrer"&gt;Elastic Common Schema (ECS)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.elastic.co/observability" rel="noopener noreferrer"&gt;Elasticsearch Observability Overview&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why Set Up Elasticsearch + OpenTelemetry Collector?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Centralize Logs&lt;/strong&gt;: Capture logs from various sources in a single store (Elasticsearch).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured Logging&lt;/strong&gt;: By sending logs via OTLP (OpenTelemetry) in JSON format, your data is easier to query and analyze.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability&lt;/strong&gt;: The Collector buffers and processes logs, reducing overhead on your apps.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt;: Combine logs with distributed tracing or metrics to gain deeper insights.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1: Generate &lt;code&gt;docker-compose.yml&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$composeContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@"
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.10.1
    environment:
      - discovery.type=single-node
    ports:
      - "39200:9200"
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 3s
      timeout: 3s
      retries: 15

  opentelemetry-collector:
    image: otel/opentelemetry-collector-contrib:latest
    environment:
      - ELASTIC_API_KEY=elasticsearch-apikey
    command: --config /etc/otel-collector-config.yml
    volumes:
      - ./otel-collector-config.yml:/etc/otel-collector-config.yml
    ports:
      - "4317:4317"
      - "4318:4318"
    depends_on:
      elasticsearch:
        condition: service_healthy
"@&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$composeFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker-compose.yml"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$composeContent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$composeFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;UTF8&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Explanation&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch&lt;/strong&gt; runs in single-node mode, exposed at port &lt;code&gt;39200&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry Collector&lt;/strong&gt; listens on ports &lt;code&gt;4317&lt;/code&gt; (gRPC) and &lt;code&gt;4318&lt;/code&gt; (HTTP).
&lt;/li&gt;
&lt;li&gt;We write the Compose file to &lt;code&gt;docker-compose.yml&lt;/code&gt; on the fly, ensuring everyone gets the same environment.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 2: Create &lt;code&gt;otel-collector-config.yml&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$otelConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@"
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318

processors:
  memory_limiter:
    check_interval: 1s
    limit_mib: 4000
    spike_limit_mib: 800

# natively that setup do not support substitute values for creating indexes like that '[service.name]-logs-yyyy.MM'
# So we need to fill in elasticsearch.index.prefix and elasticsearch.index.suffix or use logstash
  resource:
    attributes:
      - action: upsert
        key: "elasticsearch.index.prefix"
        from_attribute: "service.name"

  batch:
    timeout: 10s
    send_batch_size: 10000
    send_batch_max_size: 11000

exporters:
  elasticsearch:
    endpoint: "http://elasticsearch:9200"
    headers:
      Authorization: "ApiKey &lt;/span&gt;&lt;span class="nv"&gt;${ELASTIC_API_KEY}&lt;/span&gt;&lt;span class="sh"&gt;"
    logs_index: "logs"
    logs_dynamic_index:
      enabled: true
    mapping:
      mode: "ecs"
    timeout: 5s

service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [memory_limiter, resource, batch]
      exporters: [elasticsearch]
"@&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$otelConfigFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"otel-collector-config.yml"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$otelConfig&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Out-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilePath&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$otelConfigFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Encoding&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;UTF8&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Explanation&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Receivers&lt;/strong&gt;: Accept OTLP logs over HTTP/gRPC.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Processors&lt;/strong&gt;: Add a memory limiter, resource enrichment (e.g., &lt;code&gt;elasticsearch.index.prefix&lt;/code&gt;), and batching.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exporters&lt;/strong&gt;: Send logs to Elasticsearch in &lt;strong&gt;ECS&lt;/strong&gt;-compliant format.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 3: Spin Up Docker Services
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;--- Bringing up Docker Compose services ---"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Down any existing services&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;otel-poc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$composeFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;down&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;docker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;compose&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;otel-poc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$composeFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Optional: Wait for Elasticsearch / Collector to be healthy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;Waiting up to 60 seconds for Elasticsearch to become healthy..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$healthCheckPassed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-le&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# Check if ES responds on port 39200&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:39200"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-UseBasicParsing&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ErrorAction&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Stop&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Elasticsearch is healthy."&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nv"&gt;$healthCheckPassed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$true&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="kr"&gt;break&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Still waiting..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Start-Sleep&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Seconds&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$healthCheckPassed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;Elasticsearch did not become healthy in time. Exiting..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Explanation&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docker compose down&lt;/code&gt;&lt;/strong&gt;: Stops any existing containers with the same project name (&lt;code&gt;otel-poc&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docker compose up -d&lt;/code&gt;&lt;/strong&gt;: Spins up fresh containers in the background.
&lt;/li&gt;
&lt;li&gt;We then &lt;strong&gt;poll&lt;/strong&gt; &lt;code&gt;http://localhost:39200&lt;/code&gt; to confirm Elasticsearch is ready before proceeding.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4: Send a Test Log to the Collector
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;--- Sending test log to the Collector ---"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Adjust $otlpEndpoint or add Authorization headers as needed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$otlpEndpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:4318/v1/logs"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Content-Type"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="c"&gt;# "Authorization" = "Bearer otlp-collector-token"  # If needed&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$logPayload&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="sh"&gt;@"
{
  "resourceLogs": [
    {
      "resource": {
        "attributes": [
          {
            "key": "service.name",
            "value": { "stringValue": "myservice-" }
          },
          {
            "key": "elasticsearch.index.prefix",
            "value": { "stringValue": "myservice-" }
          },
          {
            "key": "elasticsearch.index.suffix",
            "value": { "stringValue": "-data" }
          }
        ]
      },
      "scopeLogs": [
        {
          "scope": {
            "name": "test-logger",
            "version": "1.0"
          },
          "logRecords": [
            {
              "timeUnixNano": "1673452395000000000",
              "severityNumber": 9,
              "severityText": "ERROR",
              "body": { "stringValue": "This is a test log from PowerShell" },
              "attributes": [
                {
                  "key": "attribute_key",
                  "value": { "stringValue": "attribute_value" }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}
"@&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$otlpEndpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$logPayload&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Collector Response: &lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Error sending log: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We build a &lt;strong&gt;JSON payload&lt;/strong&gt; with a single log record:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;service.name&lt;/code&gt; is set to “myservice-”.
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;severityText&lt;/code&gt; is “ERROR” (just an example).
&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;body&lt;/code&gt; is a short string indicating a test log.
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;We then &lt;strong&gt;POST&lt;/strong&gt; it to &lt;code&gt;&lt;a href="http://localhost:4318/v1/logs" rel="noopener noreferrer"&gt;http://localhost:4318/v1/logs&lt;/a&gt;&lt;/code&gt;, the OTLP (HTTP) endpoint for the Collector.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 5: Query Elasticsearch
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- STEP 5: Query Elasticsearch to verify log ingestion ---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# &amp;lt;# We need to wait a bit for the log to be ingested #&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Start-Sleep -Seconds 60&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;--- Querying Elasticsearch to see if our log is present ---"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$esQueryBody&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nx"&gt;match_all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nv"&gt;$searchResponse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Invoke-RestMethod&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Method&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Uri&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:39200/myservice-logs-data/_search?pretty"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-ContentType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"application/json"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`
&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="nt"&gt;-Body&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$esQueryBody&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-Json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="nv"&gt;$hits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$searchResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hits&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$hits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-gt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;Found &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;$hits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; log(s) in Elasticsearch!"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Here are the hits:"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nv"&gt;$hits&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ConvertTo-Json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Depth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;10&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="kr"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;No logs found in Elasticsearch index 'test-logs'."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="kr"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Error querying Elasticsearch: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Write-Host&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;`n&lt;/span&gt;&lt;span class="s2"&gt;Done!"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We construct a &lt;strong&gt;match_all&lt;/strong&gt; query to the &lt;code&gt;myservice-logs-data&lt;/code&gt; index.
&lt;/li&gt;
&lt;li&gt;If logs exist, it prints the result to the console in JSON format.
&lt;/li&gt;
&lt;li&gt;This confirms the log made the full journey: &lt;strong&gt;Collector&lt;/strong&gt; → &lt;strong&gt;Elasticsearch&lt;/strong&gt; → &lt;strong&gt;Queried&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This step-by-step script helps you quickly &lt;strong&gt;validate&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Elasticsearch&lt;/strong&gt; is up and responding.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenTelemetry Collector&lt;/strong&gt; is receiving logs and exporting them in &lt;strong&gt;ECS-compatible&lt;/strong&gt; format.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your&lt;/strong&gt; custom log payload arrives in Elasticsearch as expected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By breaking the script into &lt;strong&gt;separate blocks&lt;/strong&gt;, you can understand each phase and modify it as needed—maybe you want to add a Kibana container, use TLS, or fine-tune the Collector’s memory settings. This &lt;strong&gt;quick start&lt;/strong&gt; approach should get you up and running fast, so you can focus on &lt;strong&gt;observability&lt;/strong&gt; rather than wrestling with config files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enjoy&lt;/strong&gt; building out your structured logging pipeline! Drop a comment below if you have any questions or improvements. Happy logging!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building a Solid SQL Backup &amp; Recovery Strategy (With T-SQL PoC)</title>
      <dc:creator>Ev Vasilev</dc:creator>
      <pubDate>Wed, 15 Jan 2025 12:45:07 +0000</pubDate>
      <link>https://dev.to/ev_vasilev/building-a-solid-sql-backup-recovery-strategy-with-t-sql-poc-140g</link>
      <guid>https://dev.to/ev_vasilev/building-a-solid-sql-backup-recovery-strategy-with-t-sql-poc-140g</guid>
      <description>&lt;h2&gt;
  
  
  While Documenting Backup Strategies, I Turned My Notes into This Quick Post
&lt;/h2&gt;

&lt;p&gt;I’ve been working on thorough documentation for backup strategies across different databases. While putting together the artifacts, I figured I'd share a short post on &lt;strong&gt;SQL Server backup and restore techniques&lt;/strong&gt;. I hope it's useful for anyone looking to ramp up their SQL backups. I'll cover strategies for other databases in upcoming posts—stay tuned!&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;A reliable backup and recovery strategy is &lt;strong&gt;critical&lt;/strong&gt; for any production SQL Server environment. Here’s a quick guide on how to set up &lt;strong&gt;full&lt;/strong&gt;, &lt;strong&gt;differential&lt;/strong&gt;, and &lt;strong&gt;transaction log&lt;/strong&gt; backups (T-Log) for point-in-time restoration. We’ll also walk through a &lt;strong&gt;proof-of-concept script&lt;/strong&gt; showing how to back up data, restore it, and skip unwanted transactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Care?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Protect&lt;/strong&gt; against data loss and corruption
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable&lt;/strong&gt; faster restores when things go wrong
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meet&lt;/strong&gt; compliance/audit requirements
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. Backup Types &amp;amp; Scheduling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Full Backup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Captures &lt;strong&gt;everything&lt;/strong&gt;: data, schema, indexes.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequency&lt;/strong&gt;: Often once a week or after major DB changes.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Full.bak'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Full Backup'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Differential Backup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Only changes since the &lt;strong&gt;last full&lt;/strong&gt; backup.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequency&lt;/strong&gt;: Daily or multiple times a day.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Diff.bak'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;DIFFERENTIAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Differential Backup'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Transaction Log Backup (T-Log)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Captures &lt;strong&gt;transaction log&lt;/strong&gt; records since the last T-Log backup.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequency&lt;/strong&gt;: Every 15–30 minutes (or more often if you can’t afford to lose data).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Log.trn'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'T-Log Backup'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key&lt;/strong&gt;: Make sure your database is in &lt;code&gt;FULL&lt;/code&gt; (or &lt;code&gt;BULK_LOGGED&lt;/code&gt;) recovery mode to allow T-Log backups.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Restoring Databases
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic Restore Flow
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Full Backup&lt;/strong&gt; → &lt;code&gt;WITH NORECOVERY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Differential Backup&lt;/strong&gt; → &lt;code&gt;WITH NORECOVERY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;T-Log Backup&lt;/strong&gt; → &lt;code&gt;WITH NORECOVERY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final Step&lt;/strong&gt; → &lt;code&gt;WITH RECOVERY&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Full.bak'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NORECOVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Diff.bak'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NORECOVERY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Log.trn'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NORECOVERY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Once done with all backups/logs, finalize: */&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;RECOVERY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Point-in-Time Recovery
&lt;/h3&gt;

&lt;p&gt;If you need to &lt;strong&gt;exclude&lt;/strong&gt; bad transactions, you can stop at a specific time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;YourDatabase&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'C:&lt;/span&gt;&lt;span class="se"&gt;\B&lt;/span&gt;&lt;span class="s1"&gt;ackups&lt;/span&gt;&lt;span class="se"&gt;\Y&lt;/span&gt;&lt;span class="s1"&gt;ourDB_Log.trn'&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;STOPAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YYYY-MM-DD HH:MM:SS'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="n"&gt;RECOVERY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Transaction Log Is Full (Error 9002)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Backup the Log&lt;/strong&gt; to free space in the log file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check for open transactions&lt;/strong&gt; using &lt;code&gt;DBCC OPENTRAN(YourDatabase)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid&lt;/strong&gt; switching to SIMPLE unless in an absolute emergency (it breaks point-in-time recovery).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Corrupted or Missing Backups
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Always &lt;strong&gt;verify&lt;/strong&gt; backups with &lt;code&gt;RESTORE VERIFYONLY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Include timestamps in filenames (e.g., &lt;code&gt;YourDB_Full_20250114.bak&lt;/code&gt;) to avoid overwrites.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. The Proof-of-Concept (PoC) T-SQL Script
&lt;/h2&gt;

&lt;p&gt;Below is a &lt;strong&gt;single script&lt;/strong&gt; you can run in a test environment. It creates a sample database, does &lt;strong&gt;full&lt;/strong&gt;, &lt;strong&gt;differential&lt;/strong&gt;, and &lt;strong&gt;transaction log&lt;/strong&gt; backups, then simulates a disaster and recovers &lt;strong&gt;up to&lt;/strong&gt; a chosen point in time (excluding unwanted rows).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Adjust file paths to match your environment (e.g., &lt;code&gt;C:\Backups\...&lt;/code&gt;, &lt;code&gt;/var/opt/mssql/backups/&lt;/code&gt;, etc.).&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="cm"&gt;/****************************************************************************
 SINGLE SCRIPT: FULL, DIFF, TLOG BACKUPS + POINT-IN-TIME RESTORE (STOPAT)
****************************************************************************/&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 0: Clean Up from Previous POC Runs (If Any)
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;DB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PoCDatabase'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Dropping existing PoCDatabase for a clean start ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt; 
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;SINGLE_USER&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;ROLLBACK&lt;/span&gt; &lt;span class="k"&gt;IMMEDIATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 1: Create PoCDatabase in FULL Recovery
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Creating PoCDatabase in FULL recovery mode ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt; 
    &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;RECOVERY&lt;/span&gt; &lt;span class="k"&gt;FULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 2: Create Table &amp;amp; Insert Initial Data
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;OBJECT_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dbo.SampleData'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
    &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;ID&lt;/span&gt; &lt;span class="nb"&gt;INT&lt;/span&gt; &lt;span class="k"&gt;IDENTITY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Description&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;CreatedDate&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;GETDATE&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Initial row 1'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Initial row 2'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Initial row 3'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'Initial Data in PoCDatabase:'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 3: Perform a FULL Backup
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Performing FULL Backup ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Full.bak'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PoCDatabase Full Backup'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Verifying FULL Backup ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;VERIFYONLY&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Full.bak'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 4: Insert Data &amp;amp; Perform DIFFERENTIAL Backup
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Inserting data for differential backup ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Diff insert row A'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Diff insert row B'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'After Diff Inserts:'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Performing DIFFERENTIAL Backup ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Diff.bak'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;DIFFERENTIAL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PoCDatabase Differential Backup'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Verifying DIFFERENTIAL Backup ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;VERIFYONLY&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Diff.bak'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 5: Additional Data &amp;amp; TLog #1
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Inserting TLog #1 data ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'TLog #1 row - Keep me'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'After TLog #1 Insert:'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Performing TRANSACTION LOG Backup #1 ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Log1.trn'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PoCDatabase Transaction Log Backup #1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Verifying TLog #1 ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;VERIFYONLY&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Log1.trn'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 6: Insert Two Rows &amp;amp; TLog #2 
   - Keep the 1st
   - Exclude the 2nd with STOPAT
***************************************************/&lt;/span&gt;

&lt;span class="cm"&gt;/*
   We'll store the "stop time" in a global #temp table so we can 
   retrieve it even after new GO statements or dropping the DB.
   This avoids variable-scope issues.
*/&lt;/span&gt;
&lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;OBJECT_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'tempdb..##StopTimeStore'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
    &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="o"&gt;##&lt;/span&gt;&lt;span class="n"&gt;StopTimeStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="o"&gt;##&lt;/span&gt;&lt;span class="n"&gt;StopTimeStore&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;StopTime&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Inserting two new records for point-in-time demo ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* 1) Insert the first record (we DO want to keep). */&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Point-in-time row 1 - Good'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;WAITFOR&lt;/span&gt; &lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="s1"&gt;'00:00:01'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="cm"&gt;/* 2) Capture the exact timestamp AFTER the first insert. */&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="o"&gt;##&lt;/span&gt;&lt;span class="n"&gt;StopTimeStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StopTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GETDATE&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="cm"&gt;/* 
   3) Wait 5 seconds so the second row definitely 
      has a later timestamp than the first.
*/&lt;/span&gt;
&lt;span class="n"&gt;WAITFOR&lt;/span&gt; &lt;span class="n"&gt;DELAY&lt;/span&gt; &lt;span class="s1"&gt;'00:00:05'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- 5 seconds&lt;/span&gt;

&lt;span class="cm"&gt;/* 4) Insert second row (we want to exclude it). */&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Point-in-time row 2 - EXCLUDE'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'Two new rows inserted (pre TLog #2):'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Performing TRANSACTION LOG Backup #2 (contains both new rows) ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;BACKUP&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Log2.trn'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;INIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'PoCDatabase Transaction Log Backup #2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Verifying TLog #2 ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;VERIFYONLY&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Log2.trn'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 7: Simulate Disaster &amp;amp; Restore to STOPAT
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Dropping PoCDatabase to simulate disaster ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;DB_ID&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PoCDatabase'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
    &lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt; 
        &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;SINGLE_USER&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;ROLLBACK&lt;/span&gt; &lt;span class="k"&gt;IMMEDIATE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Restoring FULL Backup (NORECOVERY) ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Full.bak'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NORECOVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="k"&gt;REPLACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Restoring DIFFERENTIAL Backup (NORECOVERY) ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Diff.bak'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NORECOVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Restoring TLog #1 (NORECOVERY) ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Log1.trn'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;NORECOVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/*
   Retrieve the saved stop time (captured after first row insertion).
   We'll restore TLog #2 up to (but NOT including) the second row.
*/&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;StopTime&lt;/span&gt; &lt;span class="nb"&gt;DATETIME&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;TOP&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;StopTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StopTime&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="o"&gt;##&lt;/span&gt;&lt;span class="n"&gt;StopTimeStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- read from global temp table&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Restoring TLog #2 with STOPAT (RECOVERY) ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;RESTORE&lt;/span&gt; &lt;span class="n"&gt;LOG&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;DISK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="s1"&gt;'/var/opt/mssql/backups/PoCDatabase_Log2.trn'&lt;/span&gt;
    &lt;span class="k"&gt;WITH&lt;/span&gt; 
        &lt;span class="n"&gt;STOPAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;StopTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;-- exclude row(s) inserted after this time&lt;/span&gt;
        &lt;span class="n"&gt;RECOVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;STATS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="cm"&gt;/**************************************************
 STEP 8: Validate Data
***************************************************/&lt;/span&gt;
&lt;span class="n"&gt;USE&lt;/span&gt; &lt;span class="n"&gt;PoCDatabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Final State: only the first new row is present ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="s1"&gt;'FINAL DATABASE STATE:'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;dbo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SampleData&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;ID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;PRINT&lt;/span&gt; &lt;span class="s1"&gt;'--- Point-in-time restore POC COMPLETE ---'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GO&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this script, you’ll see that the &lt;strong&gt;second row&lt;/strong&gt; (the one we wanted to exclude) never makes it back into the restored database.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Final Tips
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: Use &lt;strong&gt;SQL Server Agent&lt;/strong&gt; or your preferred CI/CD system (Jenkins, Azure DevOps, etc.) to schedule and monitor backups.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test Restores&lt;/strong&gt;: Periodically restore your backups in a dev environment to confirm they’re valid.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep Enough Retention&lt;/strong&gt;: Balance retention needs (e.g., 2 weeks to months) with storage costs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document Everything&lt;/strong&gt;: Store your scripts in version control (Git) or a wiki/Confluence page for easy updates and collaboration.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  That’s It!
&lt;/h3&gt;

&lt;p&gt;A robust backup and restore strategy can &lt;strong&gt;save your bacon&lt;/strong&gt; when the unexpected happens. Try out the PoC script in a sandbox, tweak the scheduling to match your RPO/RTO needs, and enjoy a bit more peace of mind knowing your SQL databases are protected.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Questions?&lt;/strong&gt; Feel free to drop a comment below or share your own backup/restore tips!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
    </item>
  </channel>
</rss>
