Skip to content

Instantly share code, notes, and snippets.

@sebersole
Last active September 20, 2016 15:51
Show Gist options
  • Save sebersole/2c9ad6b77ea3477ba508c21c6ab61b8d to your computer and use it in GitHub Desktop.
Save sebersole/2c9ad6b77ea3477ba508c21c6ab61b8d to your computer and use it in GitHub Desktop.
/**
* Region is a conceptual contract. It defines named access to specific
* storage within the underlying cache provider. It is a conceptual idea in
* that it does not necessarily map to any single concept in the underlying
* caching implementation. How the specific cache provider (RegionFactory
* implementor) maps the idea of a Region to any underlying concept is
* completely dependent on the provider.
*
* @author Steve Ebersole
*/
public interface Region {
/**
* Retrieve the name of this region.
*
* @return The region name
*/
String getName();
/**
* Determine whether this region contains data for the given key.
* <p/>
* The semantic here is whether the cache contains data visible for the
* current call context. This should be viewed as a "best effort", meaning
* blocking should be avoid if possible.
*
* @param key The cache key
*
* @return True if the underlying cache contains corresponding data; false
* otherwise.
*/
boolean contains(Object key);
/**
* Forcibly evict an item from the cache immediately without regard for transaction
* isolation (aka, without going though {@link org.hibernate.cache.spi.access.RegionAccessStrategy}).
* <p/>
* The only expected usage of this method is from SessionFactory-level eviction calls.
*
* @param key The key of the item to evict
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*
* @see org.hibernate.Cache#evict(Class, Object)
* @see org.hibernate.Cache#evictEntity(Class, Serializable)
* @see org.hibernate.Cache#evictEntity(String, Serializable)
* @see org.hibernate.Cache#evictCollection(String, Serializable)
*/
void evict(Object key) throws CacheException;
/**
* Forcibly evict all items from the cache immediately without regard for transaction
* isolation (aka, without going though {@link org.hibernate.cache.spi.access.RegionAccessStrategy}).
* <p/>
* The only expected usage of this method is from SessionFactory-level eviction calls.
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*
* @see org.hibernate.Cache#evictAll
* @see org.hibernate.Cache#evictAllRegions
* @see org.hibernate.Cache#evictEntityRegions()
* @see org.hibernate.Cache#evictEntityRegion(Class)
* @see org.hibernate.Cache#evictEntityRegion(String)
* @see org.hibernate.Cache#evict(Class)
* @see org.hibernate.Cache#evictCollectionRegion(String)
* @see org.hibernate.Cache#evictCollectionRegions
* @see org.hibernate.Cache#evictQueryRegions()
* @see org.hibernate.Cache#evictDefaultQueryRegion()
* @see org.hibernate.Cache#evictQueryRegion
*/
void evictAll() throws CacheException;
/**
* Build a strategy for accessing entity data stored in this region
*
* @param accessType The type of access strategy/control to apply.
* @param metadata Access to information about the entity data to be stored.
*
* @return The appropriate access strategy
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
EntityRegionAccessStrategy buildEntityRegionAccessStrategy(
AccessType accessType,
CacheDataDescription metadata) throws CacheException;
/**
* Build a strategy for accessing natural-id data stored in this region
*
* @param accessType The type of access strategy/control to apply.
* @param metadata Access to information about the entity whose natural-id data is to be stored.
*
* @return The appropriate access strategy
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
NaturalIdRegionAccessStrategy buildAccessStrategy(
AccessType accessType,
CacheDataDescription metadata) throws CacheException;
/**
* Build a strategy for accessing collection data stored in this region
*
* @param accessType The type of access strategy/control to apply.
* @param metadata Access to information about the collection data to be stored.
*
* @return The appropriate access strategy
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
CollectionRegionAccessStrategy buildCollectionAccessStrategy(
AccessType accessType,
CacheDataDescription metadata) throws CacheException;
/**
* Access to a region not needing concurrency protection.
*/
UnprotectedAccessStrategy buildUnprotectedAccessStrategy();
// todo : should these expose Long instead of long w/ magic values?
/**
* The number of bytes is this cache region currently consuming in memory.
*
* @return The number of bytes consumed by this region; -1 if unknown or
* unsupported.
*/
long getSizeInMemory();
/**
* The count of entries currently contained in the regions in-memory store.
*
* @return The count of entries in memory; -1 if unknown or unsupported.
*/
long getElementCountInMemory();
/**
* The count of entries currently contained in the regions disk store.
*
* @return The count of entries on disk; -1 if unknown or unsupported.
*/
long getElementCountOnDisk();
/**
* Get the contents of this region as a map.
* <p/>
* Implementors which do not support this notion
* should simply return an empty map.
*
* @return The content map.
*/
Map toMap();
/**
* Get the next timestamp according to the underlying cache implementor.
*
* @todo Document the usages of this method so providers know exactly what is expected.
*
* @return The next timestamp
*/
long nextTimestamp();
/**
* Get a timeout value.
*
* @todo Again, document the usages of this method so providers know exactly what is expected.
*
* @return The time out value
*/
int getTimeout();
}
/**
* Base access strategy for all regions.
*
* @author Gail Badner
*/
public interface RegionAccessStrategy {
/**
* Attempt to retrieve an object from the cache. Mainly used in attempting
* to resolve entities/collections from the second level cache.
*
* @param session Current session.
* @param key The key of the item to be retrieved.
* @param txTimestamp a timestamp prior to the transaction start time
*
* @return the cached object or <tt>null</tt>
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
Object get(SharedSessionContractImplementor session, Object key, long txTimestamp) throws CacheException;
/**
* Attempt to cache an object, afterQuery loading from the database.
*
* @param session Current session.
* @param key The item key
* @param value The item
* @param txTimestamp a timestamp prior to the transaction start time
* @param version the item version number
*
* @return <tt>true</tt> if the object was successfully cached
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
boolean putFromLoad(
SharedSessionContractImplementor session,
Object key,
Object value,
long txTimestamp,
Object version) throws CacheException;
/**
* Attempt to cache an object, afterQuery loading from the database, explicitly
* specifying the minimalPut behavior.
*
* @param session Current session.
* @param key The item key
* @param value The item
* @param txTimestamp a timestamp prior to the transaction start time
* @param version the item version number
* @param minimalPutOverride Explicit minimalPut flag
*
* @return <tt>true</tt> if the object was successfully cached
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
boolean putFromLoad(
SharedSessionContractImplementor session,
Object key,
Object value,
long txTimestamp,
Object version,
boolean minimalPutOverride) throws CacheException;
/**
* We are going to attempt to update/delete the keyed object. This
* method is used by "asynchronous" concurrency strategies.
* <p/>
* The returned object must be passed back to {@link #unlockItem}, to release the
* lock. Concurrency strategies which do not support client-visible
* locks may silently return null.
*
* @param session Current session.
* @param key The key of the item to lock
* @param version The item's current version value
*
* @return A representation of our lock on the item; or null.
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
SoftLock lockItem(SharedSessionContractImplementor session, Object key, Object version) throws CacheException;
/**
* Lock the entire region
*
* @return A representation of our lock on the item; or null.
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
SoftLock lockRegion() throws CacheException;
/**
* Called when we have finished the attempted update/delete (which may or
* may not have been successful), afterQuery transaction completion. This method
* is used by "asynchronous" concurrency strategies.
*
* @param session Current session.
* @param key The item key
* @param lock The lock previously obtained from {@link #lockItem}
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
void unlockItem(SharedSessionContractImplementor session, Object key, SoftLock lock) throws CacheException;
/**
* Called afterQuery we have finished the attempted invalidation of the entire
* region
*
* @param lock The lock previously obtained from {@link #lockRegion}
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
void unlockRegion(SoftLock lock) throws CacheException;
/**
* Called after an item has become stale (beforeQuery the transaction completes).
* This method is used by "synchronous" concurrency strategies.
*
* @param session The session that the call originates from
* @param key The key of the item to remove
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
void remove(SharedSessionContractImplementor session, Object key) throws CacheException;
/**
* Called to evict data from the entire region
*
* @throws org.hibernate.cache.CacheException Wraps any exceptions propagated from
* underlying cache.
*/
void removeAll() throws CacheException;
}
/**
* Contract for building second level cache regions.
* <p/>
* The main function of a RegionFactory is to produce Region instances (duh).
* See {@link #buildRegion(String)}
* <p/>
* Implementors should define a constructor in one of two forms:<ul>
* <li>MyRegionFactoryImpl({@link java.util.Properties})</li>
* <li>MyRegionFactoryImpl()</li>
* </ul>
* Use the first when we need to read config properties prior to
* {@link #start} being called.
*
* @author Steve Ebersole
*/
public interface RegionFactory extends Service {
/**
* Lifecycle callback to perform any necessary initialization of the
* underlying cache implementation(s). Called exactly once during the
* construction of a {@link org.hibernate.internal.SessionFactoryImpl}.
*
* @param settings The settings in effect.
* @param configValues The available config values
*
* @throws org.hibernate.cache.CacheException Indicates problems starting the L2 cache impl;
* considered as a sign to stop {@link org.hibernate.SessionFactory}
* building.
*/
void start(SessionFactoryOptions settings, Map<String, Object> configValues) throws CacheException;
/**
* Lifecycle callback to perform any necessary cleanup of the underlying
* cache implementation(s). Called exactly once during
* {@link org.hibernate.SessionFactory#close}.
*/
void stop();
/**
* By default should we perform "minimal puts" when using this second
* level cache implementation?
*
* @return {@code true} if "minimal puts" should be performed by default;
* {@code false} otherwise.
*/
boolean isMinimalPutsEnabledByDefault();
/**
* Get the default access type for {@link EntityRegion entity} and
* {@link CollectionRegion collection} regions.
*
* @return This factory's default access type.
*/
AccessType getDefaultAccessType();
/**
* Generate a timestamp.
* <p/>
* This is generally used for cache content locking/unlocking purposes
* depending upon the access-strategy being used.
*
* @return The generated timestamp.
*/
long nextTimestamp();
/**
* Build a named Region.
* <p/>
* Note that "region" is an abstract Hibernate concept, not necessarily
* equivalent to any singular concept in the underlying cache provider.
* See {@link Region} for details.
* <p/>
* TBD : should Hibernate or the provider be responsible for "uniqueing" the
* Regions by name? For each name there should be only one Region instance; who
* is responsible for that. The argument for Hibernate doing it is that this
* is mandated by Hibernate and therefore should be done by Hibernate. The
* argument for the provider doing it is improved {@link #stop()} handling (currently
* Hibernate itself has to manage provider shutdown by also closing all of the regions).
*
* @param regionName The name of the region. Again, see {@link Region} for
* details regarding the purpose of region names.
*
* @return The region.
*/
Region buildRegion(String regionName);
}
/**
* A special access strategy used for access to regions that do not require concurrency protections.
* This is generally things like query caches.
*
* @author Steve Ebersole
*/
public interface UnprotectedAccessStrategy {
Region getRegion();
Object get(SharedSessionContractImplementor session, Object key) throws CacheException;
void put(SharedSessionContractImplementor session, Object key, Object value) throws CacheException;
}
@chrisdennis
Copy link

So this looks fine... I'm struggling to come up with a strong opinion on whether the region uniqueness should be enforced by Hibernate or the caching implementation. The only argument I really have is that from the point of view of a simplistic caching implementation that has no resources that need lifecycling (and therefore no need to maintain an internal map) it's nice that it can just create a new cache in response to each buildRegion(...) call when Hibernate handles the mappings. Arguments don't come much weaker than this though.

@sebersole
Copy link
Author

But it cannot be both ways. The simple truth is that some Region impls will have clean up to do (that's actually very reasonable). So either:

  1. the provider maintains a cache of the Regions and releases them when the RegionFactory is stopped, or
  2. Hibernate manages this Map<String,Region> - which means that it has to be able to release each individual Region

For the reason of potentially keeping 2 Maps (one in the provider, one in Hibernate proper) having Hibernate maintain the Map and having the provider maintain a Map/Collection (to be able to release created Regions) is simply not an option imo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment