From 37411c4a9d7fe03583b726ded112f3f6e0fd5a44 Mon Sep 17 00:00:00 2001 From: John Blum Date: Tue, 23 Oct 2018 23:14:54 -0700 Subject: [PATCH] Add chapter/section on configurable session expiration policies. Resolve gh-5. --- docs/src/docs/asciidoc/index.adoc | 216 ++++++++++++++++++++++++++++-- 1 file changed, 206 insertions(+), 10 deletions(-) diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index 9f72a80..b49c906 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -234,36 +234,41 @@ the `@EnableGemFireHttpSession` attributes. | Description | Default +| spring.session.data.gemfire.cache.client.pool.name +| `EnableGemFireHttpSession.poolName` +| Name of the dedicated Pool used by the client Region storing/accessing Session state. +| gemfirePool + | spring.session.data.gemfire.cache.client.region.shortcut | `EnableGemFireHttpSession.clientRegionShortcut` | Sets the client Region data management policy in the client-server topology. | ClientRegionShortcut.PROXY +| spring.session.data.gemfire.cache.server.region.shortcut +| `EnableGemFireHttpSession.serverRegionShortcut` +| Sets the peer Region data management policy in the peer-to-peer (P2P) topology. +| RegionShortcut.PARTITION + | spring.session.data.gemfire.session.attributes.indexable | `EnableGemFireHttpSession.indexableSessionAttributes` | Comma-delimited list of Session attributes to indexed in the Session Region. | +| spring.session.data.gemfire.session.expiration.bean-name +| `EnableGemFireHttpSession.sessionExpirationPolicyBeanName` +| Name of the bean in the Spring container implementing the expiration strategy +| + | spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds | `EnableGemFireHttpSession.maxInactiveIntervalInSeconds` | Session expiration timeout in seconds | 1800 -| spring.session.data.gemfire.cache.client.pool.name -| `EnableGemFireHttpSession.poolName` -| Name of the dedicated Pool used by the client Region storing/accessing Session state. -| gemfirePool - | spring.session.data.gemfire.session.region.name | `EnableGemFireHttpSession.regionName` | Name of the client or peer Region used to store and access Session state. | ClusteredSpringSessions -| spring.session.data.gemfire.session.region.name -| `EnableGemFireHttpSession.serverRegionShortcut` -| Sets the peer Region data management policy in the peer-to-peer (P2P) topology. -| RegionShortcut.PARTITION - | spring.session.data.gemfire.session.serializer.bean-name | `EnableGemFireHttpSession.sessionSerializerBeanName` | Name of the bean in the Spring container implementing the serialization strategy @@ -408,6 +413,197 @@ However, if you are not using Spring Boot, then you must explicitly register a s The choice is yours. +[[httpsession-gemfire-expiration]] +=== {data-store-name} Expiration + +By default, {data-store-name} is configured with a Region Entry, Idle Timeout (TTI) Expiration Policy, using an +expiration timeout of 30 minutes and INVALIDATE entry as the action. This means when a user's Session remains inactive +(i.e. idle) for more than 30 minutes, the Session expires, or is invalidated, and the user must begin a new Session +to access the application once again. + +However, what if you have application specific requirements around Session state management and expiration, and using +the default, Idle Timeout (TTI) Expiration Policy is insufficient for your Use Case (UC)? + +Now, Spring Session for {data-store-name} supports application specific, custom expiration policies. As an application +developer, you may specify custom rules governing the expiration of a Session managed by Spring Session. + +Spring Session for {data-store-name} provides the new `SessionExpirationPolicy` strategy interface. + +.SessionExpirationPolicy interface +[source,java] +---- +@FunctionalInterface +interface SessionExpirationPolicy { + + // determine timeout for expiration of individual Session + Duration determineExpirationTimeout(Session session); + + // define the action taken on expiration + default ExpirationAction getExpirationAction() { + return ExpirationAction.INVALIDATE; + } + + enum ExpirationAction { + + DESTROY, + INVALIDATE + + } +} +---- + +You implement this interface to specify the Session expiration policies required by your application and then register +the instance as a bean in the Spring application context. + +Use the `@EnableGemFireHttpSession` annotation, `sessionExpirationPolicyBeanName` attribute to configure the name of +the `SessionExpirationPolicy` bean implementing your custom application policies and rules around Session expiration. + +For example: + +.Custom, Application `SessionExpirationPolicy` +[source,java] +---- +class MySessionExpirationPolicy implements SessionExpirationPolicy { + + public Duration determineExpirationTimeout(Session session) { + // return a java.time.Duration specifying the length of time until the Session expires + } +} +---- + +Then, in your application, you simple declare the following: + +.Custom, Appliation `SessionExpirationPolicy` configuration +[source,java] +---- +@SpringBootApplication +@EnableGemFireHttpSession( + maxInactiveIntervalInSeconds = 600, + sessionExpirationPolicyBeanName = "expirationPolicy" +) +class MySpringSessionApplication { + + ... + + @Bean + SessionExpirationPolicy expirationPolicy() { + return new MySessionExpirationPolicy(); + } +} +---- + +TIP: Alternatively, the name of the `SessionExpirationPolicy` bean can be configured using the +`spring.session.data.gemfire.session.expiration.bean-name` property, or by declaring a `SpringSessionGemFireConfigurer` +bean and overriding the `getSessionExpirationPolicyBeanName()` method. + +You are only required to implement the `expireAfter(:Session):Duration` method, which encapsulates the rules +determining when the Session should expire. The expiration timeout for a Session is expressed as a +`java.time.Duration`, which specifies the length of time until the Session will expire. + +The `expireAfter` method can be Session specific and may change with each invocation. + +Optionally, you may implement the `getAction` method to specify the action taken when the Session expires. By default, +the Region Entry is invalidated. Another option is to destroy the Region Entry, which removes both the key (Session ID) +and value (Session). Invalidate only removes the value. + +NOTE: Under-the-hood, the `SessionExpirationPolicy` is adapted as an instance of the {data-store-name} +{data-store-javadoc}/org/apache/geode/cache/CustomExpiry.html[`CustomExpiry`] interface. This Spring Session +`CustomExpiry` object is then set as the Session Region's +{data-store-javadoc}/org/apache/geode/cache/RegionFactory.html#setCustomEntryIdleTimeout-org.apache.geode.cache.CustomExpiry-[custom entry, idle timeout expiration policy]. + +TIP: {data-store-name}'s expiration thread(s) run once every second, evaluating each entry (i.e. Session) in the Region +to determine if the entry has expired. You can control the number of {data-store-name} expiration threads with the +`gemfire.EXPIRY_THREADS` property. See the {data-store-name} {data-store-docs}/developing/expiration/chapter_overview.html[docs] +for more details. + +NOTE: During expiration determination, the `CustomExpiry.getExpiry(:Region.Entry)` method is invoked +for each entry (i.e. Session) in the Region every time the expiration thread(s) run, which in turn calls our +`SessionExpirationPolicy.expireAfter(:Session)` method. The returned `java.time.Duration` is used as +the expiration timeout in the {data-store-javadoc}/org/apache/geode/cache/ExpirationAttributes.html[`ExpirationAttributes`] +returned from {data-store-javadoc}org/apache/geode/cache/CustomExpiry.html#getExpiry-org.apache.geode.cache.Region.Entry-[`CustomExpiry.getExpiry(..)`] +method invocation. + +[[httpsession-gemfire-expiration-timeout-configuration]] +==== Expiration Timeout Configuration + +If you would like to base the expiration timeout for your custom `SessionExpirationPolicy` on +the `@EnableGemFireHttpSession` annotation, `maxInactiveIntervalInSeconds` attribute, or alternatively, +the corresponding `spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds` property, +then your custom `SessionExpirationPolicy` implementation may also implement the `SessionExpirationTimeoutAware` +interface. + +The `SessionExpirationTimeoutAware` interface is defined as: + +.SessionExpirationTimeoutAware interface +[source,java] +---- +interface SessionExpirationTimeoutAware { + + void setExpirationTimeout(Duration expirationTimeout); + +} +---- + +When your custom `SessionExpirationPolicy` implementation also implements the `SessionExpirationTimeoutAware` interface, +then Spring Session for {data-store-name} will supply your implementation with the value from the +`@EnableGemFireHttpSession` annotation, `maxInactiveIntervalInSeconds` attribute, or from the +`spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds` property or from any +`SpringSessionGemFireConfigurer` bean declared in the Spring application context, as an instance of `java.time.Duration`. + +When more than 1 configuration option is used, the following order takes precedence: + +1. `SpringSessionGemFireConfigurer.getMaxInactiveIntervalInSeconds()` +2. `spring.session.data.gemfire.session.expiration.max-inactive-interval-seconds` property +3. `@EnableGemFireHttpSession` annotation, `maxInactiveIntervalInSeconds` attribute + +[[httpsession-gemfire-expiration-fixed-timeout-configuration]] +==== Fixed Timeout Session Expiration + +For added convenience, Spring Session for {data-store-name} provides an implementation of the `SessionExpirationPolicy` +strategy interface for fixed duration expiration (or "_Absolute session timeouts_" as described in Spring Session +https://github.com/spring-projects/spring-session/issues/922[Issue #922]). + +It is perhaps necessary, in certain cases, such as for security reasons, to expire the user's Session after a fixed +length of time (e.g. every hour), regardless if the user's Session is still active. + +Spring Session for {data-store-name} provides the `FixedTimeoutSessionExpirationPolicy` implementation out-of-the-box +for this exact Use Case (UC). + +To configure the `FixedTimeoutSessionExpirationPolicy`, do the following: + +.Fixed Duration Expiraton Configuration +[source,java] +---- +@SpringBootApplication +@EnableGemFireHttpSession(sessionExpirationPolicyBeanName = "fixedTimeoutExpirationPolicy") +class MySpringSessionApplication { + + ... + + @Bean + SessionExpirationPolicy fixedTimeoutExpirationPolicy() { + return new FixedTimeoutSessionExpirationPolicy(Duration.ofMinutes(60L)); + } +} +---- + +In the example above, the `FixedTimeoutSessionExpirationPolicy` was declared as a bean in the Spring application context +initialized with a fixed expiration timeout of 60 minutes. As a result the users Session will either expire after +the idle timeout or after the fixed duration expiration timeout, which ever occurs first. + +TIP: It is also possible to implement lazy, fixed duration expiration timeout on Session access by using the +`FixedDurationExpirationSessionRepositoryBeanPostProcessor`. This BPP wraps any data store specific `SessionRepository` +in a `FixedDurationExpirationSessionRepository` and evaluates a Sessions expiration on access, only. This approach +is agnostic to the underlying data store and therefore can be used with any Spring Session provider. The expiration +determination is based solely on the Session `creationTime` property and a provided, required `java.time.Duration` +specifying the fixed duration expiration timeout. + +CAUTION: The `FixedDurationExpirationSessionRepository` should not be used in strict expiration policy cases, such as +when the Session must expire immediately when the fixed duration expiration timeout has elapsed. Additionally, unlike +the `FixedTimeoutSessionExpirationPolicy`, the `FixedDurationExpirationSessionRepository` does not take idle timeout +expiration into consideration. That is, it only considers the fixed duration timeout +when determining expiration timeout. + [[httpsession-gemfire-serialization]] === {data-store-name} Serialization