Mert Özen Further With Every Line

Blog

Mert Özen's blog posts on software development, backend, frontend, and DevOps.

Series
.NET Core Web API 36 Angular 31 JavaScript Basics 19 JavaScript Intermediate 18 JavaScript Advanced 16 TypeScript Basics 14 TypeScript Intermediate 14 TypeScript Advanced 12 Entity Framework Core 28 Dapper 16 Git & GitHub 29 Web Security (OWASP Top 10) 21 LLM Fundamentals 20 C# Fundamentals 20 Object-Oriented Programming in C# 16 C# Collections and Generics 11 LINQ 17 Asynchronous Programming in C# 17 Advanced C# 14 C# Versions 6 Solution Patterns 22 SOLID and Design Principles 12 Clean Code 13 Design Patterns 23 UML and Modeling 18 Introduction to Software Architecture 9 Clean, Onion and Hexagonal Architecture 8 Vertical Slice Architecture 6 Domain-Driven Design 12 CQRS and Event Sourcing 10 Messaging and Event-Driven Architecture 9 Modular Monolith 2 Microservices 10 UI Architecture Patterns 9 Architecture Decisions 7 System Design 15 Distributed Systems Fundamentals 11 Background Jobs and Scheduled Jobs 11 Working with Message Queues 14 Logging in .NET 10 Caching 10 Real-time Applications 9 ASP.NET Core MVC and Razor Pages 13 CI/CD Fundamentals 8 GitHub Actions 16 Jenkins 12 Web Servers and Hosting 12 Deployment Strategies 9 Kubernetes 19 Authentication and Authorization 18 ASP.NET Core Security 8 Frontend Security 9 API Security (OWASP API Top 10) 11 AI with .NET 14 RAG 13 AI Agents and MCP 14 Test Strategy 4 Unit Testing 13 SDLC (Software Development Life Cycle) 6 Agile Mindset 6 Scrum 11 Kanban 7 Introduction to Product Management 9 SEO Fundamentals 7 Technical SEO 12 GEO (Generative Engine Optimization) 7 Algorithms and Data Structures 22 Debugging and Problem Solving 13
10 posts found #backend
Caching Strategy: What to Cache, at Which Layer and for How Long
Backend PART 01

Caching Strategy: What to Cache, at Which Layer and for How Long

A caching decision starts with measurement: in Bookstore the category menu, with 2 queries per request and a 19.9 ms p95, was the only real candidate, and HybridCache cut p95 to 3.2 ms and 4,000 queries to 0, while the cheap, per-user cart should not be cached. TTL is derived from freshness requirements, and each layer's invalidation owner is decided up front.

8 Nov 2026 10 min 6
IMemoryCache in Depth: SizeLimit, Eviction, Change Tokens and the Hidden Stampede in GetOrCreate
Backend PART 02

IMemoryCache in Depth: SizeLimit, Eviction, Change Tokens and the Hidden Stampede in GetOrCreate

In an IMemoryCache with a full SizeLimit, an entry that exceeds the limit is never cached: book 101 added to a 100-unit cache was rejected, and background compaction evicted Low priority entries first, then the least recently used, bringing the cache down to 85. GetOrCreateAsync isn't atomic; 100 concurrent requests on an empty cache ran the factory 100 times, while striped SemaphoreSlim locks and HybridCache cut that to 1. Expiration has no timer; group invalidation uses change tokens.

8 Nov 2026 15 min 4
IDistributedCache and Running Redis as a Cache: Serialization, Key Design, TTL and Eviction Policy
Backend PART 03

IDistributedCache and Running Redis as a Cache: Serialization, Key Design, TTL and Eviction Policy

Running Redis as a cache takes a single ConnectionMultiplexer, short timeouts, versioned keys and allkeys-lru. With default settings every request took 11-12 s once Redis stopped; a 200 ms WaitAsync cap cut that to 0.4 s and a simple circuit breaker to under 1 ms. The official redis:8.2 image ships with noeviction: at 8 MB writes failed with OOM, while allkeys-lru kept going by evicting 2,534 keys. Redis 8 adds an AGPLv3 option; Valkey is BSD and Garnet MIT licensed.

8 Nov 2026 13 min 5
HybridCache in Depth: L1/L2, Tags, Logical Invalidation and L1 Staleness Across Servers
Backend PART 04

HybridCache in Depth: L1/L2, Tags, Logical Invalidation and L1 Staleness Across Servers

RemoveByTagAsync deletes nothing; it writes a timestamp to Redis and tells no other server's L1: in a two-instance test, B with a 10-second L1 still served the old menu after 22 seconds, because it reads a tag's timestamp once and keeps it. Removing by key plus a short LocalCacheExpiration cut the window to 10 seconds, and FusionCache's Redis backplane fixed B immediately.

8 Nov 2026 13 min 5
HTTP Caching: Cache-Control, ETag, 304 Not Modified and Vary to Spare the Server
Backend PART 05

HTTP Caching: Cache-Control, ETag, 304 Not Modified and Vary to Spare the Server

The HTTP cache is driven by headers, not the server: while max-age holds the request never arrives, and afterwards an ETag request gets a bodiless 304. In our test the menu's second request came from the browser with 0 bytes, and Angular HttpClient saw the 304 as a 200. Behind nginx, a book response without Vary gave an English user the Turkish price, and a cart marked public showed Bob Alice's cart.

8 Nov 2026 13 min 4
ASP.NET Core Output Cache in Depth: Policies, Vary-By, Tag Eviction, Resource Locking and the Redis Store
Backend PART 06

ASP.NET Core Output Cache in Depth: Policies, Vary-By, Tag Eviction, Resource Locking and the Redis Store

Policies, vary-by rules and middleware order decide whether output cache is safe and effective: in Bookstore, resource locking ran the handler once for 100 concurrent requests (100 times with locking off), and placing UseOutputCache before authentication served Ada's personal recommendations to an anonymous user and to Grace on the 10.0.3 runtime. The Redis store gives two instances a shared cache and eviction, while locking stays per instance.

8 Nov 2026 15 min 5
Cache Invalidation Strategies: TTL, Explicit Removal, Event-Driven Invalidation and Versioned Keys
Backend PART 07

Cache Invalidation Strategies: TTL, Explicit Removal, Event-Driven Invalidation and Versioned Keys

Correct invalidation means tying the delete to the commit and bounding the remaining race with a TTL, generation keys or events: in Bookstore even "write, then delete" left the old 12.00 price in the cache for 600 seconds, an interceptor deleting in SavedChangesAsync made it worse under an explicit transaction, and TransactionCommittedAsync fixed it. Generation keys closed the race on the same rig, and Redis pub/sub cleared the second instance's L1 in 0.7 ms.

8 Nov 2026 15 min 6
Cache Stampede, Penetration and Avalanche: Three Problems That Break Caches in Production
Backend PART 08

Cache Stampede, Penetration and Avalanche: Three Problems That Break Caches in Production

All three push the same load onto the database, but the fixes differ: in a 500-VU k6 run HybridCache cut a stampede from 223 queries to 3 and a Redis lock to 1; against unique ISBNs negative caching let 49,980 queries through, while ISBN validation plus a Bloom filter cut it to 9; 20% TTL jitter lowered the avalanche peak from 1,627 to 247 queries/s; with Redis down, FailFast and short timeouts cut p95 from 6 s to 7.3 ms.

8 Nov 2026 18 min 4
Measuring and Testing Caches: Hit Ratio, Metrics, Memory Usage and Consistency Tests
Backend PART 09

Measuring and Testing Caches: Hit Ratio, Metrics, Memory Usage and Consistency Tests

A cache proves its worth through p95 and database load, not hit ratio: the report cache cut p95 from 245 ms to 1.3 ms and 3,001 queries to 1; the book cache at 87% hit ratio didn't change p95 but cut queries by 87%; the one at 16% should go. In .NET 10 you measure with TrackStatistics, a Meter and HybridCache's EventSource; FakeTimeProvider and Testcontainers tests caught tag invalidation not reaching another server.

8 Nov 2026 16 min 4
What's New in Caching with .NET 11: MemoryCache Metrics, HybridCache Factory Context and IOutputCachePolicyProvider
Backend PART 10

What's New in Caching with .NET 11: MemoryCache Metrics, HybridCache Factory Context and IOutputCachePolicyProvider

.NET 11 brings three caching changes: with TrackStatistics on, MemoryCache publishes dotnet.cache.requests, evictions, entries and estimated_size metrics on its own; the HybridCache factory can choose the duration from the value; and IOutputCachePolicyProvider resolves output cache policies at runtime. The catch we measured: with today's package the new HybridCache overload isn't atomic (40 factory runs instead of 20 for 20 cold keys), and a custom provider silently drops AddPolicy definitions.

8 Nov 2026 18 min 4
Blog Map