Configuration reference
Every property the library reads, what it defaults to, and how CNH wires it through Helm values and Octopus variables. Code declares safe defaults; YAML overrides them per environment without a rebuild.
The principle
- The connection is Spring's. The engine uses
spring.data.redis.*as is — host/port, Sentinel, password, timeouts, Lettuce pool. There is no second Redis configuration. - Region behaviour is declared in code, tuned in YAML. A binding or annotation declares kind, TTL, indexes.
dac.cache.regions.<name>.*overrides the tunable subset — and only that subset; the contract (types, kind, indexes) is not tunable. - Applications do not invent cache properties. A TTL lives under
dac.cache.regions.<region>.l2-ttl, a toggle underdac.domains.<domain>.enabled. An application-level flag is justified only for a precondition the framework cannot know (app.cache.user-alerts.read-enabled).
dac.cache — the cache plane (engine)
| property | default | meaning |
|---|---|---|
| dac.cache.enabled | true | Master switch. Even without the key the engine activates when spring.data.redis is configured and stays a no-op when it is not. |
| dac.cache.root | {spring.application.name}:cache | Prefix of this application's keys and locks. Shared domains ignore it (they pin dac:<domain>). |
| dac.cache.refresh-cron | unset | Spring cron; the leader reloads every versioned (static) region on this schedule. Unset = no scheduled refresh. |
| dac.cache.lease-duration | 30s | Lifetime of the leader lock; re-extended at a third of the interval. |
| dac.cache.invalidation-channel | dac:cache:channel:invalidate | The eviction pub/sub channel — one for all applications on the same Redis, so an eviction of a shared region clears every service's L1. |
dac.cache.regions.<region> — per-region tuning
| property | applies to | meaning |
|---|---|---|
| l1-max-size | both | Entries in the in-pod near cache (Caffeine). 0 turns the near cache off for this region — every read goes to L2. |
| l1-ttl | both | Expiry of L1 entries (shorter than L2 when a pod must notice changes faster than the invalidation bus delivers them). |
| l2-ttl | transactional | Expiry in Redis. Overrides the binding's .ttl(…) / @CacheSnapshot(ttl = …); AbstractCache.l2Ttl() returns the effective value and the local fallback uses it too. |
| negative-ttl | transactional | How long "not found" is remembered. |
| startup | static | attach (default) or reload-if-stale. |
| max-snapshot-age | static | Threshold for reload-if-stale. |
Durations accept Spring's forms: 30s, 5m, 24h, or ISO-8601
PT5M. Region names are the exact names from the binding (userAlerts,
eventConfig); the whole map is bound at once, so camel-case keys are fine.
dac:
cache:
refresh-cron: "0 */15 * * * *"
regions:
eventConfig: { startup: reload-if-stale, max-snapshot-age: PT30M }
userAlerts: { l2-ttl: PT5M }
idmUsers: { l2-ttl: 30m }
transactions: { l2-ttl: ${TRANSACTION_CACHE_TTL:30s}, negative-ttl: 15s, l1-max-size: 1000 }
dac.cache.refresh-events — the refresh stream consumer
| property | default | meaning |
|---|---|---|
| enabled | true | Consume {root}:channel:refresh. Publishing needs no switch. |
| poll-interval | 500ms | How often each pod polls the stream. |
| batch-size | 64 | Events applied per poll. |
| claim-min-idle | 60s | Events read by a pod that has since failed are taken over after this idle time. |
| max-deliveries | 5 | Retries before an event is acknowledged and given up on. |
dac.cache.broadcast — the cluster broadcast
| property | default | meaning |
|---|---|---|
| enabled | true | The broadcast bus: the {root}:channel:broadcast subscription, @DacBroadcast registration and the built-in health function. Off = @DacBroadcast is inert and /actuator/dachealth answers UNKNOWN. |
| timeout | 5s | How long a broadcast waits for the other pods' replies before consolidating what arrived. |
The consolidated health check is an actuator endpoint and follows the usual exposure rule —
add dachealth to management.endpoints.web.exposure.include where you want
it reachable. See Running it — cluster broadcast for what
it returns and how to expose your own functions.
dac.domains.<domain> — domain flags
| property | default | meaning |
|---|---|---|
| dac.domains.<name>.enabled | the binding's enabledByDefault (facades) or the plugin's constant (CAST: false) | Off = every facade in the domain reads from its source, every @Stitch parameter of the domain resolves to null, @DacAuthorize(domain) applies onDisabled. |
| dac.domains.cast.ttl-minutes | 180 | Shared TTL of the CAST user aggregate and its shards. |
| dac.domains.cast.preferences-ttl-minutes | 15 | TTL of CAST preferences. |
Domains CNH declares today: cast, ldap-user, user-preference,
pas-user, idm-user, pao-authorized-portfolio,
cra-relationship-manager — see the regions reference.
spring.data.redis — the connection
spring:
data:
redis:
sentinel:
master: indc-master
nodes: valkey-sentinel.valkey.svc:26379
password: ${VALKEY_PASSWORD}
password: ${VALKEY_PASSWORD}
timeout: 6000ms # must exceed indc.priority.block-timeout where priority lanes are used
lettuce:
pool: { max-active: 20, max-idle: 10, min-idle: 5, max-wait: 2000ms }
shutdown-timeout: 200ms
spring:
data:
redis:
host: ${REDIS_HOST}
port: 10000
ssl: { enabled: true }
password: ${REDIS_KEY}
The connect and command timeouts also bound how long an L1 miss waits when Redis fails at runtime before the loader reads the source. Fit them to the request SLA.
Helm and Octopus wiring (CNH)
Each chart renders the dac block of configs/application.yaml from values
that are Octopus variables, so an environment can tune TTLs, flags and schedules without a rebuild.
The convention: CNH.<app>.dac.cache.… and CNH.<app>.dac.cast.enabled,
mirroring the property path.
indc-core-chart
dac:
cache:
enabled: {{ .Values.configmapfile.configs.dac.cache.enabled }}
refresh-cron: '{{ .Values.configmapfile.configs.dac.cache.refreshCron }}'
regions:
{{- range list "eventConfig" "eventTemplate" "eventGroup" "alerts" "alertChannelMapping" "bookingCentreMapping" "eventSegmentsMapping" }}
{{ . }}: { startup: {{ $.Values…dac.cache.masterData.startup }}, max-snapshot-age: {{ $.Values…dac.cache.masterData.maxSnapshotAge }} }
{{- end }}
userAlerts: { l2-ttl: {{ .Values…dac.cache.regions.userAlerts.l2Ttl }} }
ldapUsers: { l2-ttl: {{ .Values…dac.cache.ttlMinutes.default }}m }
…
domains:
cast: { enabled: {{ .Values…dac.domains.cast.enabled }} }
idm-user: { enabled: {{ include "app.cacheEnabled" (dict "cache" .Values…dac "key" "idmUser") }} }
…
| Octopus variable | feeds | suggested |
|---|---|---|
| CNH.indc.cache.enabled | dac.cache.enabled | true |
| CNH.indc.dac.cache.refreshCron | dac.cache.refresh-cron | 0 */15 * * * * |
| CNH.indc.dac.cache.masterData.startup CNH.indc.dac.cache.masterData.maxSnapshotAge | startup / max-snapshot-age of the seven master-data regions | RELOAD_IF_STALE / PT30M |
| CNH.indc.dac.cache.regions.userAlerts.l2Ttl | dac.cache.regions.userAlerts.l2-ttl | PT5M |
| CNH.indc.cache.ttl.default CNH.indc.cache.ttl.pao.authorized.portfolio CNH.indc.cache.ttl.cra.relationship.manager | l2-ttl (minutes) of the external-lookup regions | 30 / 60 / 60 |
| CNH.indc.cache.enabled.{ldap.user, user.preference, pas.user, idm.user, pao.authorized.portfolio, cra.relationship.manager} | dac.domains.<domain>.enabled | true |
| CNH.dac.cast.enabled | dac.domains.cast.enabled | true in indc-core |
alert-be-app-chart
dac:
cache:
enabled: {{ .Values.configmapfile.configs.dac.cache.enabled }}
regions:
userDevices:
l2-ttl: {{ .Values.configmapfile.configs.dac.cache.regions.userDevices.l2Ttl }}
domains:
cast:
enabled: {{ .Values.configmapfile.configs.dac.domains.cast.enabled }}
| Octopus variable | feeds | suggested |
|---|---|---|
| CNH.alert.dac.cache.enabled | dac.cache.enabled | false until the chart wires Redis |
| CNH.alert.dac.cache.regions.userDevices.l2Ttl | dac.cache.regions.userDevices.l2-ttl | PT5M |
| CNH.alert.dac.cast.enabled | dac.domains.cast.enabled | false until DMZ connectivity is verified |
To check what a chart renders without a cluster, run helm template on a scratch copy with templates/secret.yaml removed (its b64enc fails on raw #{…} placeholders) and read the dac: block of the configmap.
Actuator
| endpoint / metric | meaning |
|---|---|
| GET /api/admin/master-data-cache/versions | Live version of every static region. |
| POST /api/admin/master-data-cache/reload POST …/reload/{region} | Reload all, or one, static region now. |
| dac.cache.l1.hits · l2.hits · misses · loads · writes | Per-region counters (tag region). |
| dac.cache.hit.ratio · l1.size | Per-region gauges. |
| dac.cache.writebehind.enqueued · flushed · failed | Write-behind queue health. |
| management.health.redis.enabled | Set false where Redis must not drag /actuator/health down (the initializer's embedded backbone does this). |