DATAGEODE-152 - Polish.

Resolve gh-27.
This commit is contained in:
John Blum
2019-11-05 18:05:10 -08:00
parent 4cc6d28e4b
commit 5cc10a6e61

View File

@@ -79,18 +79,19 @@ about _Spring's Transaction Management_ infrastructure as it offers a consistent
transparently across multiple APIs and can be configured either programmatically or declaratively transparently across multiple APIs and can be configured either programmatically or declaratively
(the most popular choice). (the most popular choice).
For {data-store-name}, {sdg-name} provides a dedicated, per-cache, `PlatformTransactionManager` that, For {data-store-name}, {sdg-name} provides a dedicated, per-cache, `PlatformTransactionManager` that, once declared,
once declared, allows Region operations to be executed atomically through Spring: allows Region operations to be executed atomically through Spring:
.Enable Transaction Management using XML
[source,xml] [source,xml]
---- ----
<gfe:transaction-manager id="txManager" cache-ref="myCache"/> <gfe:transaction-manager id="txManager" cache-ref="myCache"/>
---- ----
NOTE: The example above can be simplified even further by eliminating the `cache-ref` attribute if the {data-store-name} cache NOTE: The example above can be simplified even further by eliminating the `cache-ref` attribute if the {data-store-name}
is defined under the default name, `gemfireCache`. As with the other {sdg-name} namespace elements, cache is defined under the default name, `gemfireCache`. As with the other {sdg-name} namespace elements, if the cache
if the cache bean name is not configured, the aforementioned naming convention will be used. bean name is not configured, the aforementioned naming convention will be used. Additionally, the transaction manager
Additionally, the transaction manager name is "`gemfireTransactionManager`" if not explicitly specified. name is "`gemfireTransactionManager`" if not explicitly specified.
Currently, {data-store-name} supports optimistic transactions with *read committed* isolation. Furthermore, to guarantee Currently, {data-store-name} supports optimistic transactions with *read committed* isolation. Furthermore, to guarantee
this isolation, developers should avoid making *in-place* changes that manually modify values present in the cache. this isolation, developers should avoid making *in-place* changes that manually modify values present in the cache.
@@ -98,6 +99,9 @@ To prevent this from happening, the transaction manager configures the cache to
meaning a clone of the actual value is created each time a read is performed. This behavior can be disabled if needed meaning a clone of the actual value is created each time a read is performed. This behavior can be disabled if needed
through the `copyOnRead` property. through the `copyOnRead` property.
Since a copy of the value for a given key is made when *copy on read* is enabled, you must subsequently call
`Region.put(key, value)` inorder for the value to be updated, transactionally.
For more information on the semantics and behavior of the underlying Geode transaction manager, please refer to the Geode For more information on the semantics and behavior of the underlying Geode transaction manager, please refer to the Geode
{x-data-store-javadoc}/org/apache/geode/cache/CacheTransactionManager.html[CacheTransactionManager Javadoc] {x-data-store-javadoc}/org/apache/geode/cache/CacheTransactionManager.html[CacheTransactionManager Javadoc]
as well as the {x-data-store-docs}/developing/transactions/chapter_overview.html[documentation]. as well as the {x-data-store-docs}/developing/transactions/chapter_overview.html[documentation].
@@ -105,76 +109,54 @@ as well as the {x-data-store-docs}/developing/transactions/chapter_overview.html
[[apis:global-transaction-management]] [[apis:global-transaction-management]]
== Global, JTA Transaction Management == Global, JTA Transaction Management
It is also possible for {data-store-name} to participate in a Global, JTA based transaction, such as a transaction managed It is also possible for {data-store-name} to participate in Global, JTA-based transactions, such as a transaction
by an Java EE Application Server (e.g. WebSphere Application Server, a.k.a. WAS) using Container Managed Transactions managed by an Java EE Application Server (e.g. WebSphere Application Server (WAS)) using Container Managed Transactions
(CMT) along with other JTA resources. (CMT) along with other JTA resources.
However, unlike many other JTA "compliant" resources (e.g. JMS Message Brokers like ActiveMQ), {data-store-name} is *not* However, unlike many other JTA "compliant" resources (e.g. JMS Message Brokers like ActiveMQ), {data-store-name} is *not*
an XA compliant resource. Therefore, {data-store-name} must be positioned as the "_Last Resource_" in a JTA transaction an XA compliant resource. Therefore, {data-store-name} must be positioned as the "_Last Resource_" in a JTA transaction
(_prepare phase_) since it does not implement the 2-phase commit protocol, or rather does not handle (_prepare phase_) since it does not implement the 2-phase commit protocol, or rather does not handle distributed
distributed transactions. transactions.
Many managed environments with CMT maintain support for "_Last Resource_", non-XA compliant resources in JTA transactions Many managed environments capable of CMT maintain support for "_Last Resource_", non-XA compliant resources in JTA-based
though it is not actually required in the JTA spec. More information on what a non-XA compliant, "_Last Resource_" means transactions, though it is not actually required in the JTA spec. More information on what a non-XA compliant,
can be found in Red Hat's https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Administration_And_Configuration_Guide/lrco-overview.html[documentation]. "_Last Resource_" means can be found in Red Hat's https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html/Administration_And_Configuration_Guide/lrco-overview.html[documentation].
In fact, Red Hat's JBoss project, https://narayana.io/[Narayana] is one such LGPL Open Source implementation. _Narayana_ In fact, Red Hat's JBoss project, https://narayana.io/[Narayana] is one such LGPL Open Source implementation. _Narayana_
refers to this as "_Last Resource Commit Optimization_" (LRCO). More details can be found refers to this as "_Last Resource Commit Optimization_" (LRCO). More details can be found https://narayana.io//docs/project/index.html#d0e1859[here].
https://narayana.io//docs/project/index.html#d0e1859[here].
However, whether you are using {data-store-name} in a standalone environment with an Open Source JTA Transaction Management However, whether you are using {data-store-name} in a standalone environment with an Open Source JTA Transaction
implementation that supports "_Last Resource_", or a managed environment (e.g. Java EE AS such as WAS), Management implementation that supports "_Last Resource_", or a managed environment (e.g. Java EE AS such as WAS),
_Spring Data Geode_ has you covered. {sdg-name} has you covered.
There are a series of steps you must complete to properly use {data-store-name} as a "_Last Resource_" in a JTA transaction There are a series of steps you must complete to properly use {data-store-name} as a "_Last Resource_" in a JTA
involving more than 1 transactional resource. Additionally, there can only be 1 non-XA compliant resource transaction involving more than 1 transactional resource. Additionally, there can only be 1 non-XA compliant resource
(e.g. {data-store-name}) in such an arrangement. (e.g. {data-store-name}) in such an arrangement.
1) First, you must complete Steps 1-4 in {data-store-name}'s documentation 1) First, you must complete Steps 1-4 in {data-store-name}'s documentation
https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here]. {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here].
NOTE: #1 above is independent of your Spring [Boot] and/or [Data for {data-store-name}] application NOTE: #1 above is independent of your Spring [Boot] and/or [Data for {data-store-name}] application and must be
and must be completed successfully. completed successfully.
2) Referring to Step 5 in {data-store-name}'s https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation], 2) Referring to Step 5 in {data-store-name}'s {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation],
{sdg-name}'s Annotation support will attempt to set the `GemFireCache`, {x-data-store-javadoc}/org/apache/geode/cache/GemFireCache.html#setCopyOnRead-boolean-[`copyOnRead`] {sdg-name}'s Annotation support will attempt to set the `GemFireCache`, {x-data-store-javadoc}/org/apache/geode/cache/GemFireCache.html#setCopyOnRead-boolean-[`copyOnRead`]
property for you when using the `@EnableGemFireAsLastResource` annotation. property for you when using the `@EnableGemFireAsLastResource` annotation.
However, if SDG's auto-configuration is unsuccessful then you must explicitly set the `copy-on-read` attribute on the However, if SDG's auto-configuration is unsuccessful in this regard, then you must explicitly set the `copy-on-read`
`<gfe:cache>` or `<gfe:client-cache>` element in XML or the `copyOnRead` property of the SDG `CacheFactoryBean` class attribute in the `<gfe:cache>` or `<gfe:client-cache>` XML element or set the `copyOnRead` property of
in JavaConfig to *true*. For example... the `CacheFactoryBean` class in JavaConfig to *true*. For example:
Peer Cache XML: `ClientCache` XML:
[source,xml]
----
<gfe:cache ... copy-on-read="true"/>
----
Peer Cache JavaConfig:
[source,java]
----
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setCopyOnRead(true);
return gemfireCache;
}
----
Client Cache XML:
.Set copy-on-read using XML (client)
[source,xml] [source,xml]
---- ----
<gfe:client-cache ... copy-on-read="true"/> <gfe:client-cache ... copy-on-read="true"/>
---- ----
Client Cache JavaConfig: `ClientCache` _JavaConfig_:
.Set copyOnRead using JavaConfig (client)
[source,java] [source,java]
---- ----
@Bean @Bean
@@ -182,21 +164,44 @@ ClientCacheFactoryBean gemfireCache() {
ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean(); ClientCacheFactoryBean gemfireCache = new ClientCacheFactoryBean();
gemfireCache.setClose(true);
gemfireCache.setCopyOnRead(true); gemfireCache.setCopyOnRead(true);
return gemfireCache; return gemfireCache;
} }
---- ----
NOTE: explicitly setting the `copy-on-read` attribute or optionally the `copyOnRead` property Peer `Cache` XML:
really should not be necessary.
3) At this point, you *skip* Steps 6-8 in {data-store-name}'s https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation] .Set copy-on-read using XML (server)
and let _Spring Data Geode_ work its magic. All you need do is annotate your Spring `@Configuration` class [source,xml]
----
<gfe:cache ... copy-on-read="true"/>
----
Peer `Cache` _JavaConfig_:
.Set copyOnRead using JavaConfig (server)
[source,java]
----
@Bean
CacheFactoryBean gemfireCache() {
CacheFactoryBean gemfireCache = new CacheFactoryBean();
gemfireCache.setCopyOnRead(true);
return gemfireCache;
}
----
NOTE: Explicitly setting the `copy-on-read` attribute or the `copyOnRead` property is really not necessary. Enabling
transaction management takes case of copying on reads.
3) At this point, you *skip* Steps 6-8 in {data-store-name}'s {apache-geode-docs}/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[documentation]
and let _Spring Data Geode_ work its magic. All you need to do is annotate your Spring `@Configuration` class
with {sdg-name}'s *new* `@EnableGemFireAsLastResource` annotation and a combination of Spring's with {sdg-name}'s *new* `@EnableGemFireAsLastResource` annotation and a combination of Spring's
https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction[Transaction Management] {spring-framework-docs}/#transaction[Transaction Management] infrastructure and {sdg-name}'s
infrastructure and {sdg-name}'s `@EnableGemFireAsLastResource` configuration does the trick. `@EnableGemFireAsLastResource` annotation configuration does the trick.
The configuration looks like this... The configuration looks like this...
@@ -206,7 +211,6 @@ The configuration looks like this...
@EnableGemFireAsLastResource @EnableGemFireAsLastResource
@EnableTransactionManagement(order = 1) @EnableTransactionManagement(order = 1)
class GeodeConfiguration { class GeodeConfiguration {
... ...
} }
---- ----
@@ -220,7 +224,7 @@ where Spring's `@EnableTransactionManagement` annotation is also specified.
that is not `Integer.MAX_VALUE` or `Integer.MIN_VALUE` (defaults to `Integer.MAX_VALUE`). that is not `Integer.MAX_VALUE` or `Integer.MIN_VALUE` (defaults to `Integer.MAX_VALUE`).
Of course, hopefully you are aware that you also need to configure Spring's `JtaTransactionManager` Of course, hopefully you are aware that you also need to configure Spring's `JtaTransactionManager`
when using JTA Transactions like so.. when using JTA transactions like so..
[source,java] [source,java]
---- ----
@@ -236,18 +240,18 @@ public JtaTransactionManager transactionManager(UserTransaction userTransaction)
---- ----
NOTE: The configuration in section <<apis:transaction-management>> does *not* apply here. NOTE: The configuration in section <<apis:transaction-management>> does *not* apply here.
The use of {sdg-name}'s `GemfireTransactionManager` is applicable only in "Local", Cache Transactions, The use of {sdg-name}'s `GemfireTransactionManager` is applicable in "Local-only", Cache Transactions,
*not* "Global", JTA Transactions. Therefore, you do *not* configure the SDG `GemfireTransactionManager` in this case. *not* "Global", JTA Transactions. Therefore, you do *not* configure the SDG `GemfireTransactionManager` in this case.
You configure Spring's `JtaTransactionManager` as shown above. You configure Spring's `JtaTransactionManager` as shown above.
For more details on using _Spring's Transaction Management_ with JTA, For more details on using _Spring's Transaction Management_ with JTA,
see https://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#transaction-application-server-integration[here]. see {spring-framework-docs}/#transaction-application-server-integration[here].
Effectively, {sdg-name}'s `@EnableGemFireAsLastResource` annotation imports configuration containing 2 Aspect Effectively, {sdg-name}'s `@EnableGemFireAsLastResource` annotation imports configuration containing 2 Aspect
bean definitions that handles the {data-store-name} `o.a.g.ra.GFConnectionFactory.getConnection()` bean definitions that handles the {data-store-name} `o.a.g.ra.GFConnectionFactory.getConnection()`
and `o.a.g.ra.GFConnection.close()` operations at the appropriate points during the transactional operation. and `o.a.g.ra.GFConnection.close()` operations at the appropriate points during the transactional operation.
Specifically, the correct sequence of events are... Specifically, the correct sequence of events follow:
1. `jtaTransation.begin()` 1. `jtaTransation.begin()`
@@ -261,19 +265,20 @@ Specifically, the correct sequence of events are...
This is consistent with how you, as the application developer, would code this manually if you had to use the JTA API This is consistent with how you, as the application developer, would code this manually if you had to use the JTA API
+ {data-store-name} API yourself, as shown in the + {data-store-name} API yourself, as shown in the
{data-store-name} https://gemfire90.docs.pivotal.io/geode/developing/transactions/jca_adapter_example.html#concept_swv_z2p_wk[example]. {data-store-name} {apache-geode-docs}/developing/transactions/jca_adapter_example.html#concept_swv_z2p_wk[example].
Thankfully, Spring does the heavy lifting for you and all you need do after applying the appropriate configuration Thankfully, Spring does the heavy lifting for you and all you need to do after applying the appropriate configuration
(shown above) is... (shown above) is:
.Declaring a service method as @Transactional
[source,java] [source,java]
---- ----
@Service @Service
class MyTransactionalService ... { class MyTransactionalService {
@Transactional @Transactional
public <Return-Type> someTransactionalServiceMethod() { public <Return-Type> someTransactionalServiceMethod() {
// perform business logic interacting with and accessing multiple JTA resources atomically, here // perform business logic interacting with and accessing multiple JTA resources atomically
} }
... ...
@@ -281,7 +286,7 @@ class MyTransactionalService ... {
---- ----
#1 & #4 above are appropriately handled for you by Spring's JTA based `PlatformTransactionManager` once the #1 & #4 above are appropriately handled for you by Spring's JTA based `PlatformTransactionManager` once the
`@Transactional` boundary is entered by your application (i.e. when the `MyTransactionSerivce.someTransactionalServiceMethod()` `@Transactional` boundary is entered by your application (i.e. when the `MyTransactionService.someTransactionalServiceMethod()`
is called). is called).
#2 & #3 are handled by {sdg-name}'s new Aspects enabled with the `@EnableGemFireAsLastResource` annotation. #2 & #3 are handled by {sdg-name}'s new Aspects enabled with the `@EnableGemFireAsLastResource` annotation.
@@ -290,6 +295,7 @@ is called).
Indeed, with the appropriate logging configured, you will see the correct sequence of events... Indeed, with the appropriate logging configured, you will see the correct sequence of events...
.Transaction Log Output
[source,xml] [source,xml]
---- ----
2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send] 2017-Jun-22 11:11:37 TRACE TransactionInterceptor - Getting transaction for [example.app.service.MessageService.send]
@@ -306,12 +312,25 @@ JSON : [{"id":"MSG0000000000","message":"SENT"}] ]
2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]] 2017-Jun-22 11:11:37 TRACE GemFireAsLastResourceConnectionClosingAspect - Closed {data-store-name} Connection @ [Reference [...]]
---- ----
When using transactions, it may be desirable to register a listener to perform certain actions before or after the For more details on using {data-store-name} cache-level transactions, see <<apis:transaction-management,here>>.
transaction commits, or after a rollback occurs. {sdg-name} makes it easy to create listeners that will be invoked
during specific phases of a transaction with the `@TransactionalEventListener` annotation. Methods annotated with
`@TransactionalEventListener` (as shown below) will be notified of events published from transactional methods, during
the specified `phase`.
For more details on using {data-store-name} in JTA transactions,
see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html[here].
For more details on configuring {data-store-name} as a "_Last Resource_",
see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here].
[[apis:transaction-event-listener]]
== TransactionEventListener
When using transactions, it may be desirable to register a listener to perform certain actions before or after the
transaction commits, or after a rollback occurs.
{sdg-name} makes it easy to create listeners that will be invoked during specific phases of a transaction with the
`@TransactionalEventListener` annotation. Methods annotated with `@TransactionalEventListener` (as shown below) will be
notified of events published from transactional methods, during the specified `phase`.
.After Transaction Commit Event Listener
[source,java] [source,java]
---- ----
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@@ -320,36 +339,33 @@ public void handleAfterCommit(MyEvent event) {
} }
---- ----
Inorder for the above method to be invoked, you must publish an event from within your transaction, like below. Inorder for the above method to be invoked, you must publish an event from within your transaction, like below:
.Publishing a Transactional Event
[source,java] [source,java]
---- ----
@Service @Service
class MyTransactionalService ... { class MyTransactionalService {
@Autowired @Autowired
private final ApplicationEventPublisher publisher; private final ApplicationEventPublisher applicationEventPublisher;
@Transactional @Transactional
public <Return-Type> someTransactionalServiceMethod() { public <Return-Type> someTransactionalServiceMethod() {
// perform business logic interacting with and accessing multiple JTA resources atomically, here
publisher.publishEvent(new MyEvent(...)); // Perform business logic interacting with and accessing multiple transactional resources atomically, then...
applicationEventPublisher.publishEvent(new MyEvent(...));
} }
... ...
} }
---- ----
The `@TransactionalEventListener` annotation allows you to specify the transaction `phase` at which it will be invoked; The `@TransactionalEventListener` annotation allows you to specify the transaction `phase` in which the event handler
the options are `AFTER_COMMIT`, `AFTER_COMPLETION`, `AFTER_ROLLBACK`, and `BEFORE_COMMIT`. If not specified, `phase` method will be invoked. Options include: `AFTER_COMMIT`, `AFTER_COMPLETION`, `AFTER_ROLLBACK`, and `BEFORE_COMMIT`.
defaults to `AFTER_COMMIT`. If you wish the listener to be called even when no transaction is running, you may set If not specified, the `phase` defaults to `AFTER_COMMIT`. If you wish the listener to be called even when no transaction
`fallbackExecution` to true. is present, you may set `fallbackExecution` to `true`.
For more details on using {data-store-name} in JTA transactions,
see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html[here].
For more details on configuring {data-store-name} as a "_Last Resource_",
see https://gemfire90.docs.pivotal.io/geode/developing/transactions/JTA_transactions.html#concept_csy_vfb_wk[here].
:leveloffset: +1 :leveloffset: +1