From 01cca01476ae709bf953ef18e2898dc94025394c Mon Sep 17 00:00:00 2001 From: DavidHLP Date: Sun, 26 Jul 2026 00:52:42 +0800 Subject: [PATCH 1/7] fix(observability): add bounded chain timer dimensions --- .ai/issues/index.md | 29 +++++ .ai/issues/issue-4.md | 73 ++++++++++++ .../redis/chain/CacheHandlerChainFactory.java | 4 +- .../spring/cache/redis/chain/ChainEngine.java | 61 ++++++++-- .../redis/chain/observer/ChainObserver.java | 36 +++++- .../observer/ChainObserverRegistration.java | 2 +- .../observer/ChainTimerChainObserver.java | 109 ++++++----------- .../cache/redis/chain/ChainEngineTest.java | 65 +++++++++- .../chain/observer/ChainObserverTest.java | 112 +++++++++++++++--- wiki/modules/observability.md | 9 +- 10 files changed, 386 insertions(+), 114 deletions(-) create mode 100644 .ai/issues/index.md create mode 100644 .ai/issues/issue-4.md diff --git a/.ai/issues/index.md b/.ai/issues/index.md new file mode 100644 index 00000000..bf6eb0d1 --- /dev/null +++ b/.ai/issues/index.md @@ -0,0 +1,29 @@ +# ResiCache Open Issue Ledger + +Last synchronized: 2026-07-26 +Repository baseline: `main` @ `75ed279a71b17f227c3170d738eb93e50d876c8a` (`origin/main`, ahead 0 / behind 0) +GitHub repository: `DavidHLP/ResiCache` + +## Execution order + +| Order | Issue | Priority | Status | Dependency / triage | +|---:|---|---|---|---| +| 1 | [#4](issue-4.md) Per-handler Micrometer tags | P0 observability | INVESTIGATING | Independent and explicitly highest priority | +| 2 | [#2](issue-2.md) Redis Cluster slot IT | P1 correctness | DISCOVERED | Validates existing production key construction; must use real Cluster | +| 3 | [#5](issue-5.md) Serialization migration CLI | P1 compatibility | DISCOVERED | Large migration capability; pre-flight probe already exists | +| 4 | [#3](issue-3.md) JMH module | P1 performance | DISCOVERED | External contributor declared intent on 2026-07-21 but no PR/branch is linked; review after higher-priority work and re-sync before takeover | + +## Live GitHub facts + +- Open Issues: `#2`, `#3`, `#4`, `#5`. +- Open PRs: none. +- Linked closing PRs: none for all four issues. +- Assignees: none for all four issues. +- Only active external signal: `Shubh2-0` volunteered for #3 on 2026-07-21; no implementation is currently reviewable. + +## Validation gates + +- Preserve `testcontainers-bom:1.20.4`; Spring Boot 4.0.0's transitive 1.20.6/docker-java path is incompatible with the target older Docker daemon. +- Real-Redis acceptance requires all three signals: container startup evidence, target test count > 0, and skipped count = 0. +- Every implementation receives Standards + Spec independent review before its focused commit. +- GitHub remote writes remain subject to the session GitHub Write Gate and require an explicit review package and approval. diff --git a/.ai/issues/issue-4.md b/.ai/issues/issue-4.md new file mode 100644 index 00000000..7b54962a --- /dev/null +++ b/.ai/issues/issue-4.md @@ -0,0 +1,73 @@ +# Issue #4 — Per-handler Micrometer tags on `resicache.chain.execute` + +- **Issue:** https://github.com/DavidHLP/ResiCache/issues/4 +- **Status:** COMMITTED +- **Priority:** P0 observability +- **Dependencies:** none +- **External work:** no assignee, comments, branch, or linked PR + +## Current findings + +- Current implementation moved the old inline timer from `CacheHandlerChain` into `ChainTimerChainObserver`. +- `ChainTimerChainObserver` creates one tag-less, lazily cached `Timer`; its scope token contains only chain start nanos. +- `ChainObserver#afterNode` already receives both the concrete `CacheHandler` and its `HandlerResult`, so it is the natural per-handler/per-decision observation point. +- Current timer description promises full-chain lifecycle timing. The Issue asks for handler + decision tags; implementation must avoid falsely assigning the same whole-chain duration to every handler. +- Existing `resicache.handler.fired` already counts handler evaluation and must not be duplicated. + +## Acceptance criteria + +1. `resicache.chain.execute` is queryable by bounded `handler`, `decision`, and `cacheName` tags. +2. `handler` comes from the finite installed handler set; `decision` is one of `CONTINUE`, `SKIP_ALL`, `TERMINATE`. +3. No `redisKey`, user input, exception text, or dynamic ID is used as a tag. +4. Each timer sample measures the corresponding handler invocation, not an incorrectly duplicated whole-chain duration. +5. Registry absence remains a no-op and concurrent calls do not share mutable per-call timing state. +6. Existing fired counter semantics remain unchanged. +7. Wiki observability documentation matches the final metric semantics. + +## Implementation plan + +- Refine `ChainTimerChainObserver` to record per-node timing using observer scope that remains per call / per thread-safe invocation. +- Use Micrometer registration keyed by bounded tag tuple, not one global cached timer. +- Expand `ChainObserverTest.TimerTests` with decisions, cache names, changing redis keys, and meter-count/cardinality assertions. +- Update `wiki/modules/observability.md` source references and metric table. + +## Files/modules involved + +- `src/main/java/io/github/davidhlp/spring/cache/redis/chain/observer/ChainTimerChainObserver.java` +- `src/main/java/io/github/davidhlp/spring/cache/redis/chain/ChainEngine.java` (read-only unless observer contract cannot express timing) +- `src/test/java/io/github/davidhlp/spring/cache/redis/chain/observer/ChainObserverTest.java` +- `wiki/modules/observability.md` + +## Tests required + +- Targeted unit tests for all three decisions and multiple handlers/cache names. +- Cardinality test varying only `redisKey` and asserting meter count remains constant. +- Null-registry no-op. +- `./mvnw -B -Dtest=ChainObserverTest,CacheHandlerChainFactoryTest test` +- `./mvnw -B checkstyle:check` + +## Validation result + +- Java runtime: Temurin/OpenJDK `21.0.2+13` installed user-locally via `vfox` because the session default was Java 17 and system Java 25 broke current Lombok processing. +- `./mvnw -B -Dtest=ChainObserverTest,ChainEngineTest,CacheHandlerChainFactoryTest test`: `BUILD SUCCESS`; 43 tests, 0 failures, 0 errors, 0 skipped. +- `./mvnw -B checkstyle:check`: `BUILD SUCCESS`; 0 violations. +- `git diff --check`: clean. + +## Review findings + +Independent reviewer jobs failed to yield within the review window and were cancelled; the maintainer process then completed the same frozen Standards + Spec checklist directly against the full diff and all callers/tests. + +- **critical/high/medium:** none. +- **low:** none requiring code changes. +- Concurrency/resource verdict: per-node state is an immutable token paired by Engine `finally`; no shared mutable `ThreadLocal` or stale stack. +- Compatibility verdict: existing `beforeNode/afterNode` exception behavior and `resicache.handler.fired` semantics are unchanged; new observer methods are default no-op. +- Cardinality verdict: only `handler`, three-value `decision`, and configured `cacheName`; varying `redisKey` is explicitly tested not to create meters. +- Semantic verdict: each sample is one handler invocation, so `handler` and `decision` describe the measured duration rather than duplicating whole-chain duration under every handler. + +## Commit / PR + +Local commit: current Issue commit (`fix(observability): add bounded chain timer dimensions`). Remote PR/Issue closure requires GitHub Write Gate approval. + +## Remaining work + +Prepare the remote-write review package after all executable Issues are locally complete. diff --git a/src/main/java/io/github/davidhlp/spring/cache/redis/chain/CacheHandlerChainFactory.java b/src/main/java/io/github/davidhlp/spring/cache/redis/chain/CacheHandlerChainFactory.java index 7d83cc40..0b71dc38 100644 --- a/src/main/java/io/github/davidhlp/spring/cache/redis/chain/CacheHandlerChainFactory.java +++ b/src/main/java/io/github/davidhlp/spring/cache/redis/chain/CacheHandlerChainFactory.java @@ -21,13 +21,13 @@ *
    *
  1. {@link MDCStampChainObserver} — 无 registry 依赖,必注册
  2. *
  3. {@link ChainDebugLogChainObserver} — 无 registry 依赖,必注册
  4. - *
  5. {@link ChainTimerChainObserver} — registry 缺失时全 no-op 计时,仍注册(懒初始化)
  6. + *
  7. {@link ChainTimerChainObserver} — registry 缺失时全 no-op,仍注册
  8. *
  9. {@link FiredCounterChainObserver} — registry 缺失时全 no-op,仍注册
  10. *
* *

observer 装配时机:首次 {@link #createChain} 调时。ChainHandlerChain * 自身不再持有 metric 状态,所有 per-handler / per-chain 观测收口到 Engine 的 - * observer 列表。 + * observer 列表。Timer 在节点 around-hook 中记录 handler + decision + cacheName。 * *

设计改进: *