docs(sample): run a second, complete caching stack on its own Redis connection - #181
Open
cosmin-staicu wants to merge 2 commits into
Open
docs(sample): run a second, complete caching stack on its own Redis connection#181cosmin-staicu wants to merge 2 commits into
cosmin-staicu wants to merge 2 commits into
Conversation
…onnection The library wires one Redis connection per container. Everything Redis-backed (caches, set caches, distributed lock, broadcast topics, warm-up, planned maintenance) takes the single IRedisConnector, and several of those classes are internal, so a consumer cannot assemble a second stack by hand. What works today is running AddCaching a second time in a child ServiceCollection and bridging what consumers resolve into the application container as keyed services. The child reads the same "Caching" section with only Connections:Redis replaced, key by key, by Connections:SecondaryRedis, so app name, policies and provider options stay identical by construction and the split is visibly a matter of which server a key lands on. A small hosted service forwards the child's warm-up, planned-maintenance and stream-maintainer services to the root host; disposing the root disposes the child. The sample app wires this end to end: SecondaryCaching.cs holds the bridge, every surface gets a Secondary* controller twin (ICache, IHashCache, ISetCache, IDistributedLock, connection status), and the Aspire AppHost provisions a second Redis container and injects its connection string. Both connections now warm up on start: the operations that check IsConnected first (all set-cache calls, L2 reads) never open a connection themselves, so without warm-up the set endpoints would silently no-op until the first string write. A recipe documents the pattern and what is shared versus separate; a test pins the shape it relies on. No library API changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K2d7HhcRTtYYHgCKAD1hzm Signed-off-by: Cosmin Staicu <cosmin.staicu@uipath.com>
cosmin-staicu
requested review from
alinahornet,
cosminvlad,
litheon,
lucianaparaschivei and
razvalex
as code owners
September 12, 2026 13:37
… and the annotated settings appsettings.all.json is the every-knob reference that docs/reference/settings.md mirrors, and neither showed Connections:SecondaryRedis, so the configuration for the second stack could only be found by reading the recipe. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K2d7HhcRTtYYHgCKAD1hzm Signed-off-by: Cosmin Staicu <cosmin.staicu@uipath.com>
|
There was a problem hiding this comment.
🟡 Changes recommended
Secondary connection validation can silently reuse the primary Redis endpoint, and documentation contains an invalid example.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds a sample and recipe for running two caching stacks against separate Redis servers.
Changes:
- Builds a secondary stack in a child DI container and exposes keyed services.
- Adds secondary Redis infrastructure, controllers, configuration, and tests.
- Documents the setup and operational behavior.
File summaries
| File | Description |
|---|---|
tests/UiPath.Caching.Tests/Config/SecondCachingContainerTests.cs |
Tests child-container isolation and lifecycle. |
samples/UiPath.Caching.Sample/UiPath.Caching.Sample.csproj |
References the Queue package. |
samples/UiPath.Caching.Sample/SecondaryCaching.cs |
Registers the secondary caching stack. |
samples/UiPath.Caching.Sample/Program.cs |
Enables set caching and secondary registration. |
samples/UiPath.Caching.Sample/Controllers/SetCacheController.cs |
Adds primary and secondary set endpoints. |
samples/UiPath.Caching.Sample/Controllers/SecondaryCacheController.cs |
Adds secondary cache and hash endpoints. |
samples/UiPath.Caching.Sample/Controllers/RedisConnectionController.cs |
Adds secondary connection diagnostics. |
samples/UiPath.Caching.Sample/Controllers/DistributedLockController.cs |
Adds lock demonstration endpoints. |
samples/UiPath.Caching.Sample/appsettings.json |
Configures warm-up and secondary Redis. |
samples/UiPath.Caching.Sample/appsettings.all.json |
Documents secondary connection settings. |
samples/UiPath.Caching.Sample.AppHost/AppHost.cs |
Provisions and injects secondary Redis. |
docs/sample-app.md |
Documents the expanded sample. |
docs/reference/settings.md |
Describes secondary connection configuration. |
docs/recipes/second-redis-connection.md |
Adds the implementation recipe. |
docs/index.md |
Links the new recipe. |
CHANGELOG.md |
Records the sample and recipe. |
Review details
- Files reviewed: 16/16 changed files
- Comments generated: 6
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+56
to
+59
| if (overrides.Count == 0) | ||
| { | ||
| throw new InvalidOperationException($"'{SecondaryConnectionSection}' is empty; the secondary caching stack needs at least a ConnectionString there."); | ||
| } |
Comment on lines
+72
to
+77
| public void An_empty_secondary_connection_section_is_refused_at_registration() | ||
| { | ||
| var act = () => Build(settings: [("Caching:Connections:SecondaryRedis:ConnectionString", null)]); | ||
|
|
||
| act.Should().Throw<InvalidOperationException>().WithMessage("*Caching:Connections:SecondaryRedis*"); | ||
| } |
Comment on lines
+176
to
+179
| if (overrides.Count == 0) | ||
| { | ||
| throw new InvalidOperationException("'Caching:Connections:SecondaryRedis' is empty; the secondary caching stack needs at least a ConnectionString there."); | ||
| } |
Comment on lines
+71
to
+74
| if (overrides.Count == 0) | ||
| { | ||
| throw new InvalidOperationException($"'{SecondaryConnectionSection}' is empty; the secondary caching stack needs at least a ConnectionString there."); | ||
| } |
|
|
||
| The child container is the whole second stack. Everything `AddCaching` and the builder chain register in it, from the `RedisConnector` down to the broadcast topic providers and the L1 memory caches of `AddInMemoryRedis`, lives once per container, so nothing Redis-facing is shared. Disposing the application container disposes the keyed `IServiceProvider` and with it the child graph. | ||
|
|
||
| The child reads the **same** `Caching` section, so `AppShortName`, `KeyCasing`, policies, provider options and the queue options are identical by construction. Only `Connections:Redis` differs: every key under `Connections:SecondaryRedis` is laid over it, so the second server can also carry its own `ConnectionStringExtraParams`, `WarmUpOnStart` or `ProfilerEnabled` when it needs them, and inherits the primary's values otherwise. If the second server must differ in *cache* options too, bind the child from its own section instead: `child.AddCaching(configuration.GetSection("CachingSecondary"), chain, opt => section.Bind(opt))`; the two sections then have to agree on `KeyCasing`, because `AddCaching` seeds the process-wide `CacheKey.DefaultCasing`. |
Comment on lines
+6
to
+7
| // The secondary stack is used the way the primary is: through its ICacheFactory, which picks the provider | ||
| // (InMemoryRedis, Redis, InMemory) from the CachingSecondary section's DefaultCache. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



What
Two complete caching stacks in the sample app, each on its own Redis server:
ICache,IHashCache,ISetCache,IDistributedLock, broadcast topics, warm-up and planned maintenance, twice. No library API changes.The library wires one Redis connection per container, and several Redis-backed classes (
RedisDistributedLock,RedisConnectionWarmup,UiPathDistributedCache) are internal, so a consumer cannot assemble a second stack by hand. What works today is runningAddCachinga second time in a childServiceCollectionand bridging what consumers resolve into the application container as keyed services.The child reads the same
Cachingsection with onlyConnections:Redisreplaced, key by key, byConnections:SecondaryRedis. App name, policies and provider options stay identical by construction; the split is visibly a matter of which server a key lands on. A ~25-line hosted service forwards the child's hosted services (warm-up, planned maintenance, stream health maintainer) to the root host. Disposing the root disposes the child.Changes
samples/UiPath.Caching.Sample/SecondaryCaching.cs: the bridge (AddSecondaryCaching), keyed under"secondary".Secondary*controller twins for every surface, plus newSetCacheandDistributedLockcontrollers on both stacks;RedisConnectionControllersplit into a base and two derived controllers.cache-secondarycontainer and injectsCaching__Connections__SecondaryRedis__ConnectionString.WarmUpOnStart. The operations that checkIsConnectedfirst (everyISetCachecall, L2 reads) never open a connection themselves, so without warm-up the set endpoints silently no-op until the first string write. Found while verifying; behaviour is pre-existing and unrelated to the second connection.docs/recipes/second-redis-connection.md: the pattern, what is shared vs. separate, when not to use it. Linked fromdocs/index.mdanddocs/sample-app.md.tests/.../Config/SecondCachingContainerTests.cs: pins the shape the recipe relies on (separate connectors/factories/locks/caches, shared logger and clock, connection-key override and inheritance, empty secondary section refused, hosted-service bridge, disposal).Verified
redis-serverprocesses (6379/6380), one app process: sets/strings/hashes/locks on both stacks, keys land only on their own server, both handshakes at startup,/healthz200.Follow-up (separate PR)
A library-level
AddNamedCaching(name, ...)doing this bridge officially, with guard rails and typed keyed access. Outline in the recipe's "When not to use".🤖 Generated with Claude Code
https://claude.ai/code/session_01K2d7HhcRTtYYHgCKAD1hzm