Skip to content

Commit 2b11edf

Browse files
authored
feat: add cache stampede protection pattern
Adds Cache Stampede Protection fluent and source-generated paths with docs, examples, DI integration, benchmarks, and TinyBDD coverage. Closes #449.
1 parent 760e26a commit 2b11edf

27 files changed

Lines changed: 985 additions & 14 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -473,13 +473,13 @@ var cachedRemoteProxy = Proxy<int, string>.Create(id => remoteProxy.Execute(id))
473473
---
474474

475475
## Patterns Table
476-
PatternKit currently tracks 108 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.
476+
PatternKit currently tracks 109 production-readiness patterns. Each catalog pattern is represented in tests, documentation, real-world examples, IoC integration, and the BenchmarkDotNet coverage matrix.
477477

478478
| Category | Count | Patterns |
479479
| --- | ---: | --- |
480480
| Application Architecture | 23 | Activity Tracker, Aggregate Root, Anti-Corruption Layer, Audit Log, Bounded Context, Context Map, CQRS, Data Mapper, Domain Event, Domain Service, Event Sourcing, Feature Toggle, Identity Map, Manual Task Gate, Materialized View, Repository, Service Layer, Specification, Table Data Gateway, Timeout Manager, Transaction Script, Unit of Work, Value Object |
481481
| Behavioral | 11 | Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor |
482-
| Cloud Architecture | 17 | Ambassador, Backends for Frontends, Bulkhead, Cache-Aside, Circuit Breaker, External Configuration Store, Gateway Aggregation, Gateway Routing, Health Endpoint Monitoring, Leader Election, Priority Queue, Queue-Based Load Leveling, Rate Limiting, Retry, Scheduler Agent Supervisor, Sidecar, Strangler Fig |
482+
| Cloud Architecture | 18 | Ambassador, Backends for Frontends, Bulkhead, Cache-Aside, Cache Stampede Protection, Circuit Breaker, External Configuration Store, Gateway Aggregation, Gateway Routing, Health Endpoint Monitoring, Leader Election, Priority Queue, Queue-Based Load Leveling, Rate Limiting, Retry, Scheduler Agent Supervisor, Sidecar, Strangler Fig |
483483
| Creational | 5 | Abstract Factory, Builder, Factory Method, Prototype, Singleton |
484484
| Enterprise Integration | 41 | Aggregator, Canonical Data Model, Channel Adapter, Channel Purger, Claim Check, Competing Consumers, Content Enricher, Content-Based Router, Control Bus, Correlation Identifier, Dead Letter Channel, Durable Subscriber, Dynamic Router, Event Notification, Event-Carried State Transfer, Event-Driven Consumer, Guaranteed Delivery, Invalid Message Channel, Mailbox, Message Bus, Message Channel, Message Envelope, Message Expiration, Message Filter, Message History, Message Store, Message Translator, Messaging Bridge, Messaging Gateway, Pipes and Filters, Polling Consumer, Publish-Subscribe, Recipient List, Request-Reply, Resequencer, Routing Slip, Saga / Process Manager, Scatter-Gather, Service Activator, Splitter, Wire Tap |
485485
| Messaging Reliability | 3 | Idempotent Receiver, Inbox, Outbox |
@@ -519,6 +519,8 @@ BenchmarkDotNet guidance is documented in [docs/guides/benchmarks.md](docs/guide
519519
| Bridge | Execution | 91.848 ns | 664 B | 30.004 ns | 160 B | Generated bridge forwarding was faster and allocated less for notice rendering. |
520520
| Bulkhead | Construction | 20.56 ns | 216 B | 20.48 ns | 216 B | Effectively equivalent for this microbenchmark. |
521521
| Bulkhead | Execution | 102.70 ns | 592 B | 106.11 ns | 592 B | Same allocation; fluent was slightly faster for the shipping allocation workflow. |
522+
| Cache Stampede Protection | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
523+
| Cache Stampede Protection | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
522524
| Cache-Aside | Construction | 19.91 ns | 200 B | 19.85 ns | 200 B | Effectively equivalent for this microbenchmark. |
523525
| Cache-Aside | Execution | 216.50 ns | 1,048 B | 208.60 ns | 1,048 B | Same allocation; generated was slightly faster for the miss-then-hit workflow. |
524526
| Canonical Data Model | Construction | 75.482 ns | 632 B | 59.947 ns | 496 B | Generated reduced construction time and allocation in this microbenchmark. |
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using BenchmarkDotNet.Attributes;
2+
using PatternKit.Cloud.CacheStampedeProtection;
3+
using PatternKit.Examples.CacheStampedeProtectionDemo;
4+
5+
namespace PatternKit.Benchmarks.Cloud;
6+
7+
[BenchmarkCategory("Cloud", "CacheStampedeProtection")]
8+
public class CacheStampedeProtectionBenchmarks
9+
{
10+
private static readonly ProductAvailabilityRequest Request = new("SKU-100", "us");
11+
12+
[Benchmark(Baseline = true, Description = "Fluent: create cache stampede protection policy")]
13+
[BenchmarkCategory("Fluent", "Construction")]
14+
public CacheStampedeProtectionPolicy<ProductAvailabilitySnapshot> Fluent_CreatePolicy()
15+
=> ProductCatalogStampedeProtectionPolicies.CreateFluent();
16+
17+
[Benchmark(Description = "Generated: create cache stampede protection policy")]
18+
[BenchmarkCategory("Generated", "Construction")]
19+
public CacheStampedeProtectionPolicy<ProductAvailabilitySnapshot> Generated_CreatePolicy()
20+
=> GeneratedProductCatalogStampedeProtectionPolicy.CreateGenerated();
21+
22+
[Benchmark(Description = "Fluent: share product catalog load")]
23+
[BenchmarkCategory("Fluent", "Execution")]
24+
public IReadOnlyList<ProductAvailabilitySummary> Fluent_ShareProductCatalogLoad()
25+
=> ProductCatalogStampedeProtectionDemoRunner.RunFluentAsync(Request).AsTask().GetAwaiter().GetResult();
26+
27+
[Benchmark(Description = "Generated: share product catalog load")]
28+
[BenchmarkCategory("Generated", "Execution")]
29+
public IReadOnlyList<ProductAvailabilitySummary> Generated_ShareProductCatalogLoad()
30+
=> ProductCatalogStampedeProtectionDemoRunner.RunGeneratedStaticAsync(Request).AsTask().GetAwaiter().GetResult();
31+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Product Catalog Cache Stampede Protection
2+
3+
This example protects product availability reads from duplicate origin loads when two requests miss the same catalog key at the same time.
4+
5+
```csharp
6+
var request = new ProductAvailabilityRequest("SKU-100", "us");
7+
var results = await ProductCatalogStampedeProtectionDemoRunner.RunFluentAsync(request);
8+
```
9+
10+
The generated route uses the same workflow through a generated policy factory:
11+
12+
```csharp
13+
var policy = GeneratedProductCatalogStampedeProtectionPolicy.CreateGenerated();
14+
var service = new ProductCatalogStampedeProtectionService(policy, origin);
15+
```
16+
17+
Import the demo into a host with:
18+
19+
```csharp
20+
services.AddProductCatalogStampedeProtectionDemo();
21+
```
22+
23+
The registration provides `CacheStampedeProtectionPolicy<ProductAvailabilitySnapshot>`, `ProductCatalogOrigin`, `ProductCatalogStampedeProtectionService`, and `ProductCatalogStampedeProtectionDemoRunner`.

docs/examples/toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,9 @@
280280
- name: Product Catalog Cache-Aside
281281
href: product-catalog-cache-aside.md
282282

283+
- name: Product Catalog Cache Stampede Protection
284+
href: product-catalog-cache-stampede-protection.md
285+
283286
- name: Product Search Rate Limiting
284287
href: product-search-rate-limiting.md
285288

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Cache Stampede Protection Generator
2+
3+
The Cache Stampede Protection generator creates a strongly typed factory for `CacheStampedeProtectionPolicy<TResult>` from a partial host type.
4+
5+
```csharp
6+
using PatternKit.Generators.CacheStampedeProtection;
7+
8+
[GenerateCacheStampedeProtection(typeof(ProductAvailabilitySnapshot), FactoryMethodName = "CreateGenerated", PolicyName = "product-catalog-single-flight")]
9+
public static partial class GeneratedProductCatalogStampedeProtectionPolicy;
10+
```
11+
12+
Generated usage:
13+
14+
```csharp
15+
var policy = GeneratedProductCatalogStampedeProtectionPolicy.CreateGenerated();
16+
```
17+
18+
The host type must be partial. `FactoryMethodName` and `PolicyName` must be non-empty when provided.

docs/generators/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ PatternKit includes a Roslyn incremental generator package (`PatternKit.Generato
132132
| [**Health Endpoint Monitoring**](health-endpoint-monitoring.md) | Typed service health endpoint factories | `[GenerateHealthEndpoint]` |
133133
| [**Priority Queue**](priority-queue.md) | Business-priority queue factories | `[GeneratePriorityQueue]` |
134134
| [**Cache-Aside**](cache-aside.md) | Read-through cache policy factories with TTL and cache predicates | `[GenerateCacheAsidePolicy]` |
135+
| [**Cache Stampede Protection**](cache-stampede-protection.md) | Keyed single-flight policy factories for suppressing duplicate cache-miss loads | `[GenerateCacheStampedeProtection]` |
135136
| [**Rate Limiting**](rate-limiting.md) | Key-partitioned fixed-window rate limit policy factories | `[GenerateRateLimitPolicy]` |
136137
| [**External Configuration Store**](external-configuration-store.md) | Typed centralized configuration loaders | `[GenerateExternalConfigurationStore]` |
137138
| [**Gateway Aggregation**](gateway-aggregation.md) | API gateway response composition factories | `[GenerateGatewayAggregation]` |

docs/generators/toc.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
- name: Cache-Aside
3232
href: cache-aside.md
3333

34+
- name: Cache Stampede Protection
35+
href: cache-stampede-protection.md
36+
3437
- name: Canonical Data Model
3538
href: canonical-data-model.md
3639

docs/guides/benchmark-results.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
3939
| Bridge | Execution | 91.848 ns | 664 B | 30.004 ns | 160 B | Generated bridge forwarding was faster and allocated less for notice rendering. |
4040
| Bulkhead | Construction | 20.56 ns | 216 B | 20.48 ns | 216 B | Effectively equivalent for this microbenchmark. |
4141
| Bulkhead | Execution | 102.70 ns | 592 B | 106.11 ns | 592 B | Same allocation; fluent was slightly faster for the shipping allocation workflow. |
42+
| Cache Stampede Protection | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
43+
| Cache Stampede Protection | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
4244
| Cache-Aside | Construction | 19.91 ns | 200 B | 19.85 ns | 200 B | Effectively equivalent for this microbenchmark. |
4345
| Cache-Aside | Execution | 216.50 ns | 1,048 B | 208.60 ns | 1,048 B | Same allocation; generated was slightly faster for the miss-then-hit workflow. |
4446
| Canonical Data Model | Construction | 75.482 ns | 632 B | 59.947 ns | 496 B | Generated reduced construction time and allocation in this microbenchmark. |
@@ -234,19 +236,19 @@ The latest measured timings below were captured on Windows 11, Intel Core i9-149
234236

235237
## Coverage Matrix Summary
236238

237-
The coverage matrix currently publishes 108 catalog patterns and 432 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution. The reusable hosting integration matrix publishes 9 reusable hosting integration route results for package-level `IServiceCollection` registrations.
239+
The coverage matrix currently publishes 109 catalog patterns and 436 pattern route results. Each pattern has four BenchmarkDotNet routes: fluent construction, fluent execution, source-generated construction, and source-generated execution. The reusable hosting integration matrix publishes 9 reusable hosting integration route results for package-level `IServiceCollection` registrations.
238240

239241
| Category | Patterns | Published route results |
240242
| --- | ---: | ---: |
241243
| Application Architecture | 23 | 92 |
242244
| Behavioral | 11 | 44 |
243-
| Cloud Architecture | 17 | 68 |
245+
| Cloud Architecture | 18 | 72 |
244246
| Creational | 5 | 20 |
245247
| Enterprise Integration | 41 | 164 |
246248
| Messaging Reliability | 3 | 12 |
247249
| Structural | 7 | 28 |
248250

249-
The generator matrix currently publishes 103 generator source route results.
251+
The generator matrix currently publishes 104 generator source route results.
250252

251253
## Hosting Integration Matrix Results
252254

@@ -301,9 +303,10 @@ The generator matrix currently publishes 103 generator source route results.
301303
| Behavioral | Template Method | Covered | Covered | Covered | Covered |
302304
| Behavioral | Visitor | Covered | Covered | Covered | Covered |
303305
| Cloud Architecture | Ambassador | Covered | Covered | Covered | Covered |
304-
| Cloud Architecture | Backends for Frontends | Covered | Covered | Covered | Covered |
305-
| Cloud Architecture | Bulkhead | Covered | Covered | Covered | Covered |
306-
| Cloud Architecture | Cache-Aside | Covered | Covered | Covered | Covered |
306+
| Cloud Architecture | Backends for Frontends | Covered | Covered | Covered | Covered |
307+
| Cloud Architecture | Bulkhead | Covered | Covered | Covered | Covered |
308+
| Cloud Architecture | Cache Stampede Protection | Covered | Covered | Covered | Covered |
309+
| Cloud Architecture | Cache-Aside | Covered | Covered | Covered | Covered |
307310
| Cloud Architecture | Circuit Breaker | Covered | Covered | Covered | Covered |
308311
| Cloud Architecture | External Configuration Store | Covered | Covered | Covered | Covered |
309312
| Cloud Architecture | Gateway Aggregation | Covered | Covered | Covered | Covered |
@@ -389,8 +392,9 @@ The generator matrix currently publishes 103 generator source route results.
389392
| BackendsForFrontendsGenerator | `src/PatternKit.Generators/BackendsForFrontends/BackendsForFrontendsGenerator.cs` | Covered |
390393
| BridgeGenerator | `src/PatternKit.Generators/Bridge/BridgeGenerator.cs` | Covered |
391394
| BuilderGenerator | `src/PatternKit.Generators/Builders/BuilderGenerator.cs` | Covered |
392-
| BulkheadPolicyGenerator | `src/PatternKit.Generators/Bulkhead/BulkheadPolicyGenerator.cs` | Covered |
393-
| CacheAsidePolicyGenerator | `src/PatternKit.Generators/CacheAside/CacheAsidePolicyGenerator.cs` | Covered |
395+
| BulkheadPolicyGenerator | `src/PatternKit.Generators/Bulkhead/BulkheadPolicyGenerator.cs` | Covered |
396+
| CacheAsidePolicyGenerator | `src/PatternKit.Generators/CacheAside/CacheAsidePolicyGenerator.cs` | Covered |
397+
| CacheStampedeProtectionGenerator | `src/PatternKit.Generators/CacheStampedeProtection/CacheStampedeProtectionGenerator.cs` | Covered |
394398
| CanonicalDataModelGenerator | `src/PatternKit.Generators/CanonicalDataModel/CanonicalDataModelGenerator.cs` | Covered |
395399
| ChainGenerator | `src/PatternKit.Generators/Chain/ChainGenerator.cs` | Covered |
396400
| CircuitBreakerPolicyGenerator | `src/PatternKit.Generators/CircuitBreaker/CircuitBreakerPolicyGenerator.cs` | Covered |

docs/guides/benchmarks.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ The following numbers were captured on Windows 11, Intel Core i9-14900K, .NET SD
5656
| Bridge | Execution | 91.848 ns | 664 B | 30.004 ns | 160 B | Generated bridge forwarding was faster and allocated less for notice rendering. |
5757
| Bulkhead | Construction | 20.56 ns | 216 B | 20.48 ns | 216 B | Effectively equivalent for this microbenchmark. |
5858
| Bulkhead | Execution | 102.70 ns | 592 B | 106.11 ns | 592 B | Same allocation; fluent was slightly faster for the shipping allocation workflow. |
59+
| Cache Stampede Protection | Construction | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
60+
| Cache Stampede Protection | Execution | Pending | Pending | Pending | Pending | Covered by the BenchmarkDotNet matrix; publish measured values after the next benchmark refresh. |
5961
| Cache-Aside | Construction | 19.91 ns | 200 B | 19.85 ns | 200 B | Effectively equivalent for this microbenchmark. |
6062
| Cache-Aside | Execution | 216.50 ns | 1,048 B | 208.60 ns | 1,048 B | Same allocation; generated was slightly faster for the miss-then-hit workflow. |
6163
| Canonical Data Model | Construction | 75.482 ns | 632 B | 59.947 ns | 496 B | Generated reduced construction time and allocation in this microbenchmark. |

docs/guides/pattern-coverage.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ The source of truth is `PatternKitPatternCatalog` in `src/PatternKit.Examples/Pr
9090
| Cloud Architecture | Retry | `RetryPolicy<T>` | Retry generator |
9191
| Cloud Architecture | Circuit Breaker | `CircuitBreakerPolicy<T>` | Circuit Breaker generator |
9292
| Cloud Architecture | Bulkhead | `BulkheadPolicy<T>` | Bulkhead generator |
93+
| Cloud Architecture | Cache Stampede Protection | `CacheStampedeProtectionPolicy<TResult>` | Cache Stampede Protection generator |
9394
| Cloud Architecture | Queue-Based Load Leveling | `QueueLoadLevelingPolicy<T>` | Queue Load Leveling generator |
9495
| Cloud Architecture | Health Endpoint Monitoring | `HealthEndpoint<TContext>` | Health Endpoint Monitoring generator |
9596
| Cloud Architecture | Priority Queue | `PriorityQueuePolicy<TItem, TPriority>` | Priority Queue generator |

0 commit comments

Comments
 (0)