I'll answer your questions in a different order than you asked them as each answer I think builds upon the other...
Then, what does "buildRegion" mean? Since you expect it to return such an empty shell, why do we need that Region instance at all?
Well as we discussed on phone, there are many places where Hibernate exposes operations/information on a per-Region (name) basis (statistics, eviction, etc). Obviously having a singular object that a region name points to helps with that. Again, Region is a Hibernate-specific concept. I cannot say that any more clearly
I am making an assumption that Region maps to a shared map-like data storage. Is that too much?
Yes, that is too much. Region, at the end of the day, is a Hibernate specific concept for the express purposes listed above. What else each provide bakes into its Region impl is up to that provider. I would expect that means whatever is needed to build RegionAccess objects - which again is going to be provider-specific. As we discussed on that phone call we had, I'd fully expect the combination of (region-name, role, accessType) to be the "unique key" referring to a particular provider's cache object (an Infinispan org.infinispan.Cache e.g.). Really the accessType is superfluous in terms of making the key here unique. (region-name, role) is unique by itself - a given "role" can only specify a single AccessType. And again, you have all that info by the time you are asked to build a RegionAccess. Call it CachedDataAccess if you like that name better. Regardless of the name, the intent is that it provides access to the specific kind of data indicated (entity, collection, natural-id) in the indicated named-region using the indicated AccessType.
All instance of RegionAccess coming from the same region will keep references to the same components, therefore there's no state per RegionAccess. But this is a bit of digression, and speaks about the current implementation - but do you expect these objects to keep any state?
I'd expect it to keep a reference to the underlying cache that it is providing access to. Again that is not the same as Region. Also again, for Infinispan e.g. this would be a org.infinispan.Cache as I understand it.
But my problem is that I don't have all this info when someone calls buildEntityRegionAccess because I don't know if someone will call that method again with different arguments in the next second. I have to either use the most generic implementation, or optimistically assume that there won't be a second call and if there is such invocation with incompatible arguments, throw an exception (or do a complicated fallback to the generic implementation).
I really have no idea what you are getting at when you say "I don't know if someone will call that method again with different arguments in the next second". That method will get called multiple times. And 80% of the time the difference in arguments is completely irrelevant for what you elsewhere state is what you want to achieve. Really (I think) it boils down to you want to know ahead of time how many CacheDataDescriptions we will be storing in that Region. E.g. if we will be storing Person entity data and Company entity data in the same region - that is important information to you so you can pick the proper CacheKeyFactory to use e.g. Again, for any given CacheDataDescription (Person, Company) the AccessType will always be the same. What you want to additionally enforce is for a Region to only be able to host a single AccessType. I am not going to make that a baked-in restriction, unless everyone here agrees that that should be a restriction across all providers. But personally I think that really just shows a mis-understanding of Region and RegionAccess and how to implement them.
Regarding the wish list: I had no particular format in mind, I meant just "all data", though I've assumed something like the (Entity)CacheDataDescriptions. Something implementation could use to turn some optimizations if the data is immutable, if we store just single type and so on.
Handing the Region its CacheDataDescriptions up-front introduces a chicken-egg. The CacheDataDescription is, e.g., the EntityPersister and CollectionPersister which is also the thing that need the RegionAccess during it initialization. We had decided to make the persisters the CacheDataDescription impls mainly to save instantiations. It is possibly I guess that we could implement CacheDataDescription separate from the persisters and create those for each call to get a UserModelRegionAccess. Resolving the UserModelRegionAccess only happens once per CacheDataDescription, at SessionFactory bootstrap. So even though this approach would cause an additional instantiation per persister creation, it would be limited to just bootstrap. Still I tend to prefer to avoid unnecessary object instantiations - and your group does as well
All told, let's step back and look at an example, which I think might help. Let's imagine we have a finctional cache library called Cacheaholic that we want to integrate with Hibernate via RegionFactory. Cacheaholic's main cache contract is called Cache. It can host multiple Cache instances per "runtime". A user then defines a domain model like follows for a SessionFactory configured to use Cacheaholic:
@Cache(region="a.b.c",usage=TRANSACTIONAL)
public class Person {
...
@Cache(region="a.b.c",usage=TRANSACTIONAL)
@ManyToMany..
Collection<Person> friends;
}
So to be clear, here we have:
- 2 references to the region name "a.b.c" - one to store Person entity data, another to store Person#friends data. Perfectly valid as discussed above
- Both accesses to this "a.b.c" region happen to use the same AccessType for this example - again this is what you want to bake in as a restriction - we will come back to this in another example...
This will cause the following calls to happen when Hibernate bootstraps the SF...
- CacheaholicRegionFactory#<init> (see previous discussion)
- CacheaholicRegionFactory#buildRegion( "a.b.c" ) - Hibernate caches this based on region name
- CacheaholicRegion["a.b.c"]#buildEntityRegionAccess(AccessType.TRANSACTIONAL, EntityCacheDataDescription[Person])
- CacheaholicRegion["a.b.c"]#buildCollectionRegionAccess(AccessType.TRANSACTIONAL, CollectionCacheDataDescriptionPerson#friends)
That's it. Hibernate caches these references it gets back - so we won't keep calling this over and over for the same args. What I would envision doing as the CacheaholicRegionFactory developer would be to:
- Write CacheaholicRegionFactory such that it has a reference to Cacheaholic's underlying CacheTemplateRepository which gets a series of named config templates for creating Cacheaholic Cache impls. We'll come back to this...
- When CacheaholicRegionFactory is asked to build a Region, to me that would imply a possible CacheTemplate based on the region name.
- Additionally, when CacheaholicRegion is asked to build a UserModelRegionAccess it now has 2 additional possible hints as to the CacheTemplate to use:
- Based on the role name - i.e. "Person" v. "Person#friends"
- Based on the type of data to be cached - entity v. collection v. natural-id
In terms of these "templates", what I would envision is a series of resolution steps - more-specific to least:
- See if there is a CacheTemplate keyed by the role name; if so use that...
- See if there is a CacheTemplate keyed by the region name; if so use that...
- See if there is a CacheTemplate keyed for the type of data - "entity", "collection", etc. Or maybe even "hibernate-entity", etc; if so use that...
- Otherwise use some "default" CacheTemplate.
Regardless of which CacheTemplate is used, when we build a Cacheaholic UserModelRegionAccess impl, it would hold a reference to the underlying Cacheaholic Cache where its data is stored.
So let's apply this to your concern over mixed AccessTypes...
@Cache(region="a.b.c",usage=TRANSACTIONAL)
public class Person {
...
@Cache(region="a.b.c",usage=READ_WRITE)
@ManyToMany..
Collection<Person> friends;
}
Again, bear in mind that each of these is getting mapped to individual RegionAccess objects (individual Cacheaholic Cache impls). So I am just not seeing how that is a problem. Now I can see it being a problem depending on how a provider decided to implement Region/RegionAccess. But that would be an implementation problem |