Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a068194
docs: add accepted ADR 0021 caching-decorator-architecture
iancooper Jul 21, 2026
0eeee21
docs: add approved ralph-tasks for spec 014 caching-decorator
iancooper Jul 21, 2026
f1bab19
feat: add Microsoft.Extensions.Caching.Hybrid 9.10.0 to CPM
iancooper Jul 21, 2026
ebc2495
feat: scaffold Paramore.Darker.Caching project (net8.0;net9.0)
iancooper Jul 21, 2026
0185fca
feat: add Paramore.Darker.Caching.Tests project
iancooper Jul 21, 2026
f41e809
feat: add cache semantic-convention constants to core
iancooper Jul 21, 2026
7f1d4a2
feat: add DefaultCacheKeyGenerator with deterministic runtime-type key
iancooper Jul 21, 2026
ee59c1f
feat: DefaultCacheKeyGenerator honors IAmCacheable.CacheKey
iancooper Jul 21, 2026
1c140eb
feat: DefaultCacheKeyGenerator fails fast on empty IAmCacheable.CacheKey
iancooper Jul 21, 2026
a0d111d
refactor: name class-under-test variable after the class, not _sut
iancooper Jul 22, 2026
206baa9
feat: add async caching decorator with AddCaching extension
iancooper Jul 22, 2026
aadb82a
refactor: tidy caching key generator and its empty-key test
iancooper Jul 22, 2026
523e0e1
feat: verify cache entry expiry re-runs handler after TTL elapses
iancooper Jul 22, 2026
4e8b512
feat: fail fast on non-positive cache expirationSeconds at pipeline b…
iancooper Jul 22, 2026
759977a
feat: fail fast when HybridCache is not registered (FR12)
iancooper Jul 22, 2026
997fe08
feat: cache IAmCacheable query under its own key end-to-end
iancooper Jul 22, 2026
2eb58d5
feat: empty runtime CacheKey fails fast end-to-end through the pipeline
iancooper Jul 22, 2026
60697d3
feat: cache hit short-circuits pipeline, skipping inner decorator
iancooper Jul 22, 2026
8a556fe
docs: document cache step-ordering short-circuit footgun
iancooper Jul 22, 2026
ec8e322
feat: cache null result and serve it as a hit (FR11 negative caching)
iancooper Jul 22, 2026
536ae21
feat: serialization failure surfaces to the caller unswallowed (FR13)
iancooper Jul 22, 2026
2918e22
feat: record cache-outcome span attribute (hit/miss) via core Activity
iancooper Jul 22, 2026
be60c9d
feat: sync caching decorator with ValueTask fast-path returns synchro…
iancooper Jul 22, 2026
e865ff7
feat: sync decorator blocking fallback materializes a non-completed V…
iancooper Jul 22, 2026
23b101e
feat: apply Bag cache tag and enable RemoveByTagAsync eviction (FR9)
iancooper Jul 22, 2026
3b6a1ff
feat: absent/non-string Bag tag stores entry untagged without throwin…
iancooper Jul 22, 2026
c31ee48
feat: tagging on a cache without tag support still caches, never fail…
iancooper Jul 22, 2026
6839eaa
feat: AddCaching configurable overload with replaceable key generator
iancooper Jul 22, 2026
85fae8f
feat: guard that Paramore.Darker.Caching has no OpenTelemetry dependency
iancooper Jul 23, 2026
35e250d
feat: switch backing HybridCache to FusionCache purely via DI (FR6)
iancooper Jul 23, 2026
2aee1ef
feat: CacheMeter derives a hit/miss counter from cache-outcome span a…
iancooper Jul 23, 2026
50e8a20
feat: dispatch Internal spans with cache outcome to the cache meter
iancooper Jul 23, 2026
2ab3e45
feat: AddDarkerInstrumentation registers cache meter with opt-out toggle
iancooper Jul 23, 2026
c57eb75
feat: end-to-end cache metrics chain emits hit/miss counters, toggle …
iancooper Jul 23, 2026
9347a73
chore: mark ralph task 31 complete — whole-solution integration gate …
iancooper Jul 23, 2026
e399702
fix: cache key generator serializes properties by runtime type
iancooper Jul 23, 2026
a5f574c
fix: cache key generator skips indexer properties
iancooper Jul 23, 2026
30c935c
fix: sync cache decorator suppresses SynchronizationContext to avoid …
iancooper Jul 23, 2026
f36720c
docs: clarify closure-capture remarks on caching decorators
iancooper Jul 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agent_instructions/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Write developer tests using xUnit.
- Name test methods in the format: When_[condition]_should_[expected_behavior].
- Name test classes `[Behavior]Tests` — the `When_` convention is for method names and file names only, never class names. For example `QueryProcessorExecuteTests`, `PipelineBuilderDecoratorTests`.
- Name the class-under-test variable after the class, not `_sut` — for example `defaultCacheKeyGenerator` for `DefaultCacheKeyGenerator`.
- Prefer a test case per file.
- Name test files for the test method in the file i.e. When_[condition]_should_[expected_behavior].cs
- If you decide to use multiple test cases per file, for example shared complex set up, name the file after the happy path test method and the class after the shared behavior.
Expand Down
4 changes: 3 additions & 1 deletion Darker.Filter.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@
"src\\Paramore.Darker.Validation\\Paramore.Darker.Validation.csproj",
"src\\Paramore.Darker.Validation.FluentValidation\\Paramore.Darker.Validation.FluentValidation.csproj",
"src\\Paramore.Darker.Validation.DataAnnotations\\Paramore.Darker.Validation.DataAnnotations.csproj",
"src\\Paramore.Darker.Caching\\Paramore.Darker.Caching.csproj",
"test\\Paramore.Darker.Benchmarks\\Paramore.Darker.Benchmarks.csproj",
"test\\Paramore.Darker.Tests.AOT\\Paramore.Darker.Tests.AOT.csproj",
"test\\Paramore.Darker.Core.Tests\\Paramore.Darker.Core.Tests.csproj",
"test\\Paramore.Darker.Extensions.Tests\\Paramore.Darker.Extensions.Tests.csproj",
"test\\Paramore.Darker.Extensions.Diagnostics.Tests\\Paramore.Darker.Extensions.Diagnostics.Tests.csproj",
"test\\Paramore.Darker.Validation.Tests\\Paramore.Darker.Validation.Tests.csproj",
"test\\Paramore.Darker.Validation.FluentValidation.Tests\\Paramore.Darker.Validation.FluentValidation.Tests.csproj",
"test\\Paramore.Darker.Validation.DataAnnotations.Tests\\Paramore.Darker.Validation.DataAnnotations.Tests.csproj"
"test\\Paramore.Darker.Validation.DataAnnotations.Tests\\Paramore.Darker.Validation.DataAnnotations.Tests.csproj",
"test\\Paramore.Darker.Caching.Tests\\Paramore.Darker.Caching.Tests.csproj"
]
}
}
12 changes: 12 additions & 0 deletions Darker.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
<Configuration Solution="Release|x64" Project="Release|Any CPU" />
<Configuration Solution="Release|x86" Project="Release|Any CPU" />
</Project>
<Project Path="src\Paramore.Darker.Caching\Paramore.Darker.Caching.csproj">
<Configuration Solution="Debug|x64" Project="Debug|Any CPU" />
<Configuration Solution="Debug|x86" Project="Debug|Any CPU" />
<Configuration Solution="Release|x64" Project="Release|Any CPU" />
<Configuration Solution="Release|x86" Project="Release|Any CPU" />
</Project>
</Folder>
<Folder Name="/test/">
<Project Path="test\Paramore.Darker.Benchmarks\Paramore.Darker.Benchmarks.csproj">
Expand Down Expand Up @@ -120,5 +126,11 @@
<Configuration Solution="Release|x64" Project="Release|Any CPU" />
<Configuration Solution="Release|x86" Project="Release|Any CPU" />
</Project>
<Project Path="test\Paramore.Darker.Caching.Tests\Paramore.Darker.Caching.Tests.csproj">
<Configuration Solution="Debug|x64" Project="Debug|Any CPU" />
<Configuration Solution="Debug|x86" Project="Debug|Any CPU" />
<Configuration Solution="Release|x64" Project="Release|Any CPU" />
<Configuration Solution="Release|x86" Project="Release|Any CPU" />
</Project>
</Folder>
</Solution>
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
<PackageVersion Include="FluentValidation" Version="11.11.0" />
<PackageVersion Include="Microsoft.Bcl.AsyncInterfaces" Version="10.0.9" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="9.10.0" />
<!-- TEST-ONLY: FusionCache as a drop-in HybridCache implementation (no production source dependency) -->
<PackageVersion Include="ZiggyCreatures.FusionCache" Version="2.6.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.9" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.9" />
Expand Down
458 changes: 458 additions & 0 deletions docs/adr/0021-caching-decorator-architecture.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion specs/.current-spec
Original file line number Diff line number Diff line change
@@ -1 +1 @@
013-validation-decorator
014-Caching-Decorator
1 change: 1 addition & 0 deletions specs/014-Caching-Decorator/.adr-list
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0021-caching-decorator-architecture.md
Empty file.
1 change: 1 addition & 0 deletions specs/014-Caching-Decorator/.issue-number
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
291
Empty file.
Empty file.
29 changes: 29 additions & 0 deletions specs/014-Caching-Decorator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Caching Decorator

**Spec ID:** 014
**Created:** 2026-07-20
**Status:** Requirements

## Overview

An opt-in `[CacheableQuery]` decorator that caches query results through the Darker pipeline, built on Microsoft's `HybridCache` abstraction (FusionCache pluggable via DI). Supports sync + async pathways (sync uses an immediate-completion fast return), pluggable cache keys (`IAmCacheable` with a serialization fallback), TTL expiry plus externally-driven tag eviction via a well-known `IQueryContext.Bag` key, and configurable OpenTelemetry hit/miss signals. See `requirements.md` (linked issue [#291](https://github.com/BrighterCommand/Darker/issues/291)).

## Status Checklist

- [ ] **Requirements** — Define WHAT the feature must do (`/spec:requirements`)
- [ ] **Design (ADR)** — Define HOW the feature will be built (`/spec:design`)
- [ ] **Adversarial Review** — Multiple rounds of critical review (`/spec:review`)
- [ ] **Tasks** — Break the design into implementation tasks (`/spec:tasks`)
- [ ] **Implementation** — TDD implementation of tasks (`/spec:implement`)

## Artifacts

| Phase | File | Status |
|-------|------|--------|
| Requirements | `requirements.md` | Drafted (awaiting approval) |
| Design | `design.md` (ADR) | Not started |
| Tasks | `tasks.md` | Not started |

## Notes

_Add any context, links, or decisions here as the spec evolves._
Loading
Loading