When to use it
The DirectQuery local cache is configured viaIMarketShiftDirectQueryCachingPostProcessor.CacheConfiguration Spring beans. Each CacheConfiguration pairs a cache store name with an ILocationToCachePartitionConverter that maps a prefetch location to a cache partition.
In 6.0.8, exactly one CacheConfiguration could be wired into a shift post-processor at a time. 6.0.9 extends that contract in two backward-compatible ways:
- Multi-store wiring. Declare several
CacheConfigurationbeans and they are all wired into the same post-processor. - Service-driven dispatch. Override
IMarketDataRetrievalService.resolveShiftCacheNames(...)(or the symmetric method onIFxShift) so the post-processor picks the right store(s) for the current query coordinates instead of prefetching every registered cache on every query.
IMarketShiftDirectQueryCachingPostProcessorEx
Package: com.activeviam.mr.common.services
Extends IMarketShiftDirectQueryCachingPostProcessor and is implemented by the three standard Atoti Market Risk shift post-processors:
AShiftVectorPostProcessorFXMarketShiftPostProcessorAPnlVectorFromRiskSensiPostProcessor
Injection points
Override points
The two methods customer code is expected to override:getServiceNames returns the keys of getMarketShiftCacheConfigurationMap(), which preserves the 6.0.7 / 6.0.8 fixed-store behavior of prefetching every configured cache on every query.
Override getServiceNames to defer to an injected service. The recommended body delegates to one of the routeVia* helpers (see MarketShiftCacheNameResolverUtils below).
legacyFallbackIfEmpty is applied automatically inside the resolver and should not be called by override code. It is documented here only because it appears in the addDatabaseCachePrefetcher implementation.
legacyFallbackIfEmpty behaviour matrix
The fallback is responsible for keeping 6.0.7 / 6.0.8 single-store wiring working when a project has not overridden resolveShiftCacheNames on its IMarketDataRetrievalService.
The 1+1 override row is the bridge that keeps 6.0.7 wiring functional after the upgrade. If you see the WARN, your project is in that bridging state: either you have a single
CacheConfiguration bean but your service returns a different name, or your service routing is incomplete. Either align the names or remove one side of the wiring once the migration is complete.
ICacheResolver
Package: com.activeviam.mr.common.services
(location, filter, pivot) to a map from cache store name to the CachePartitions the prefetcher should request for that store. An empty map means nothing needs to be prefetched. It is the unit the new MRDatabaseCachePrefetcher iterates over.
Most customer code does not implement ICacheResolver directly; the framework supplies a ServiceDispatchingCacheResolver that wraps the override of getServiceNames.
ServiceDispatchingCacheResolver
Package: com.activeviam.mr.common.services
The framework-provided ICacheResolver that backs IMarketShiftDirectQueryCachingPostProcessorEx. It composes:
- A
CacheNameRouter((location, filter, pivot) -> Set<String>), normally bound togetServiceNamesvialegacyFallbackIfEmpty. - A
Supplier<Map<String, ILocationToCachePartitionConverter>>that reads the livemarketShiftCacheConfigurationMapof the post-processor.
IllegalStateException so that wiring bugs surface loudly rather than NPE deep inside the prefetcher.
MarketShiftCacheNameResolverUtils
Package: com.activeviam.mr.common.services
Stateless helpers that produce the CacheNameRouter lambdas plugged into ServiceDispatchingCacheResolver. The two routeVia* methods are the recommended bodies for getServiceNames overrides.
routeViaMarketDataService
- Wildcard-aware expansion of
locationoverriskClassLevel×sensitivityNameLevel, dropping points the cube filter denies (viaPostProcessorUtils.grantedMembersCondition). - For each surviving point: a bounded expansion over the levels reported by
leafCoordinatesFunction.getRequiredLevels(). If the expanded count exceedsleafExpansionLimit, fall back to a single call against the coarse point and let the service overapproximate (typically by reading anullleafCoordinatesargument). - Each surviving point fires one
service.resolveShiftCacheNames(sensitivityKind, riskClass, sensitivityName, leafCoordinates)call. The union of returned names is the result.
sensitivityKind is a per-post-processor constant (for example "Delta" or "Vega"). leafCoordinatesFunction is a LocationFunction that produces the leaf-payload list; pass null if your post-processor does not route on leaf coordinates.
routeViaFxShift
IFxShift.resolveShiftCacheNames does not take (sensitivityKind, riskClass, sensitivityName), this helper only drives the optional bounded leaf expansion.
IFxShift.resolveShiftCacheNames
Package: com.activeviam.mr.common.services
FXShift implementation delegates to the underlying IMarketDataRetrievalService, so overriding only the service method is sufficient in most cases. Override IFxShift.resolveShiftCacheNames directly only when the FX cache routing diverges from the market-data routing.
MRDatabaseCachePrefetcher
Package: com.activeviam.mr.common.services
The IPrefetcher installed by IMarketShiftDirectQueryCachingPostProcessorEx.addDatabaseCachePrefetcher. It fans out across a list of ICacheResolvers and issues one cacheManager.prefetchCacheAsync(...) per (cacheName, partition) pair returned by each resolver.
It is functionally equivalent to the Atoti-core DatabaseCachePrefetcher for a single registered cache (same call shape, same partition derivation), but its getName() returns MRDatabaseCachePrefetcher instead of DatabaseCachePrefetcher. Monitoring that keys on the prefetcher class name, a Grafana panel or an APM query over the Atoti query logs for instance, matches the old value and shows nothing until the filter is updated.
MRDatabaseCachePrefetcher.computePrefetches no-ops on IDistributedActivePivot instances, matching the parent prefetcher contract.
End-to-end example
FX risk-class evaluations prefetch FxDeltaShift, everything else prefetches DeltaShift. No fork or subclass of the standard Atoti Market Risk post-processors is required.
The MyDeltaService and DeltaCacheConfig snippets above form a complete, self-contained example. Each store the service can return (DeltaShift, FxDeltaShift) has a matching CacheConfiguration bean. Spring autowires both beans into the post-processor’s list-injection point.
To split routing across separate services instead of branching inside one, give each store its own IMarketDataRetrievalService. Each service’s resolveShiftCacheNames then returns just that store’s name, with a matching CacheConfiguration bean declared for each.