Support object delegate strategies
It occurs to me that additional delegate types would offer a great opportunity to move away from special casing caching (or other delegate types), and instead allow a strategy to be configured per type:
- Annotation on the model POJO
- A default mode specified for at API generation time
- Per type at runtime against the API
- Per-use case, allowing the strategy to be specified either via a helper function on the object itself, or surfaced on the API
HollowObjectCacheProvider and the cachedTypes configuration supports low-cardinality, pre-populated caches, however caching is beneficial for high-cardinality types where fields are accessed repeatedly and/or in tight loops.
Also feels light object access patterns insight will need to be part of this, as they're difficult to detect without deep analysis of profiling data. Optional sampling of field access that surfaces where caching would be beneficial or is inefficient would help consumers choose a good strategy for a type (or crazier still - use that as a heuristic to automatically tune a strategy for a type).
Strategies
Flyweight
The current default lookup strategy.
Interned
The current caching strategy.
Pooled
Like the current caching strategy, but a better fit for high cardinality types. Pools the most frequently accessed objects up to a maximum cardinality or perhaps a memory budget.
Hydrated
Essentially a HollowObjectProvider that calls com.netflix.hollow.api.objects.provider.HollowFactory#newCachedHollowObject for every object instantiation.
Object Caching
Some additional work would be needed on the generator however - the current CachedImpls only stores the ordinal of reference types, and wouldn't work in a case where HollowObjectCacheProvider isn't keeping track of, and returning cached instances. So the fields would need to store references to the objects themselves, rather than just the ordinals for a hydrated case.
Here's a real world example where I swapped in my own HollowObjectFactoryProvider to produce cached objects:
Because only the ordinals are cached for references, there's still significant overhead when destructing cached objects. In these caches, we do that manually where it matters.
Lazy
As above, but the generated HollowCachedDelegate cache volatile fields on first access, rather than initializing all fields in the constructor.
POJO
This is a special case in that it's not a strategy per-se, but I think should be a possibility in the generated API. See #342 for details.