Repository: eclipse/microprofile-lra
Pull request #144 (2019-04-27 19:25:05)

issue101 Eventual consistency requirements
#101 


Patch:
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java
index eadd319..87d285b 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Compensate.java
@@ -32,7 +32,8 @@ import java.time.temporal.ChronoUnit;
  * be used as a participant for the LRA. If this associated LRA is subsequently
  * cancelled then the method that this annotation is applied to will be invoked
  * (if the annotation is present on more than one method then an arbitrary one
- * will be chosen).
+ * will be chosen). The spec makes no guarantees about when it will be invoked,
+ * just that is will eventually be called.
  *
  * The id of the currently running LRA can be obtained by inspecting the incoming
  * JAX-RS headers. If this LRA is nested then the parent LRA MUST be present
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java
index 5967c73..9be2901 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Complete.java
@@ -28,11 +28,14 @@ import java.time.temporal.ChronoUnit;
 
 /**
  * When a resource method executes in the context of an LRA and if the containing
- * class contains a method annotated with `@Complete` then the resource will
- * be used as a participant for the LRA. If this associated LRA is subsequently
- * closed then the method that this annotation is applied to will be invoked
- * (if the annotation is present on more than one method then an arbitrary one
- * will be chosen).
+ * class contains a method annotated with `@Compensate` then the resource will
+ * be used as a participant for the LRA. If, in addition, it also contains a method
+ * annotated with `@Complete` then the method will be invoked when the associated
+ * LRA is later closed. The spec makes no guarantees about when it will be invoked,
+ * just that is will eventually be called.
+ *
+ * If the annotation is present on more than one method then an arbitrary one
+ * will be chosen.
  *
  * The id of the currently running LRA can be obtained by inspecting the incoming
  * JAX-RS headers. If this LRA is nested then the parent LRA MUST be present
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ParticipantStatus.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ParticipantStatus.java
index 96da1eb..ae4eb8e 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ParticipantStatus.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ParticipantStatus.java
@@ -27,7 +27,7 @@ import org.eclipse.microprofile.lra.annotation.ws.rs.Leave;
  * participant state model:
  *
  * The initial state Active is entered when a participant is first
- * associated with a Long Runing Action.
+ * associated with a Long Running Action.
  *
  * The state Compensating is entered when a compensate
  * notification is received (which indicates that the associated
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java
index 5b4460e..a6160f3 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/Status.java
@@ -32,8 +32,8 @@ import java.lang.annotation.Target;
  * messages/requests can be lost, delayed or duplicated etc and the
  * implementation component responsible for invoking {@link Compensate}
  * and {@link Complete} annotated methods may loose track of the status of
- * a particpant. In this case, ideally it would just resend the completion
- * or compensation notication but if the participant (the class that
+ * a participant. In this case, ideally it would just resend the completion
+ * or compensation notification but if the participant (the class that
  * contains the Compensate and Complete annotations) does not
  * support idempotency then it must be able to report its' status by
  * by annotating one of the methods with this <em>@Status</em> annotation
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java
index 4883212..aa301b0 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/LRA.java
@@ -125,9 +125,13 @@ public @interface LRA {
      *     If the method is to run in the context of an LRA and the annotated class
      *     also contains a method annotated with {@link Compensate}
      *     then the resource will be enlisted with the LRA. Enlisting with an LRA
-     *     means that the resource will be notified when the current LRA is later
+     *     means that the resource MUST be notified when the current LRA is later
      *     cancelled. The resource can also receive notifications when the LRA is
      *     closed if it additionally defines a method annotated with {@link Complete}.
+     *     The specification does not mandate when these notifications are issued
+     *     but it does guarantee that they will eventually be sent. Under failure
+     *     conditions the system will keep retrying until it is certain that all
+     *     participants have been successfully notified.
      * </p>
      *
      * <p>
@@ -321,6 +325,10 @@ public @interface LRA {
      * A value of zero indicates that the LRA will always remain valid.
      * </p>
      *
+     * <p>
+     *     Methods running with an active LRA context must be resilient to it being
+     *     cancelled while the method is still executing.
+     * </p>
      * @return the period for which the LRA is guaranteed to run for before
      * becoming eligible for cancellation.
      */
@@ -336,8 +344,10 @@ public @interface LRA {
      * <p>
      * If the annotated method runs with an LRA context then this element determines
      * whether or not it is closed when the method returns. If the element has the
-     * value {@literal true} then the LRA will be ended and if it has the value
-     * {@literal false} then the LRA will not be ended.
+     * value {@literal true} then the LRA will be ended and all participants that
+     * have the @Complete annotation MUST eventually be asked to complete.
+     * If the element has the value {@literal false} then the LRA will not be ended
+     * when the method finishes.
      * </p>
      *
      * <p>
@@ -359,7 +369,9 @@ public @interface LRA {
      * The cancelOnFamily element can be set to indicate which families of
      * HTTP response codes will cause the current LRA to cancel. If the LRA
      * has already been closed when the annotated method returns then this
-     * element is silently ignored,
+     * element is silently ignored, Cancelling an LRA means that all
+     * participants will eventually be asked to compensate (by having
+     * their @Compensate annotated method invoked).
      * </p>
      *
      * <p>
@@ -381,7 +393,9 @@ public @interface LRA {
      * The cancelOn element can be set to indicate which  HTTP response
      * codes will cause the current LRA to cancel. If the LRA
      * has already been closed when the annotated method returns then this
-     * element is silently ignored,
+     * element is silently ignored, Cancelling an LRA means that all
+     * participants will eventually be asked to compensate (by having
+     * their @Compensate annotated method invoked).
      * </p>
      *
      * <p>
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/Leave.java b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/Leave.java
index f10acde..669685a 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/Leave.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/annotation/ws/rs/Leave.java
@@ -47,7 +47,7 @@ import java.lang.annotation.Target;
  * from the incoming context and will be enlisted with the new context (and the method
  * will execute with this new context). Note that in this case the context exposed in
  * the `LRA_HTTP_CONTEXT_HEADER` JAX-RS header will be set to the new LRA (and not the original
- * one), ie the orignal context will not be available to the business logic.
+ * one), ie the original context will not be available to the business logic.
  * </p>
  *
  * <p>
@@ -60,7 +60,7 @@ import java.lang.annotation.Target;
  * Leaving a particular LRA has no effect on any other LRA - ie the same
  * resource can be enlisted with many different LRAs and leaving one
  * particular LRA will not affect its participation in any of the other
- * LRA's it has joined.
+ * LRAs it has joined.
  * </p>
  */
 @Retention(RetentionPolicy.RUNTIME)
diff --git a/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java b/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java
index 1126593..7de2c8e 100644
--- a/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java
+++ b/api/src/main/java/org/eclipse/microprofile/lra/participant/LRAParticipant.java
@@ -36,7 +36,7 @@ public interface LRAParticipant extends Serializable {
      * @param lraId the LRA that is closing
      * @return null if the participant completed successfully. If the participant
      *         cannot complete immediately it should return a future that the caller
-     *         can useto monitor progress. If the JVM crashes before the participant
+     *         can used to monitor progress. If the JVM crashes before the participant
      *         can finish it should expect this method to be called again. If the
      *         participant fails to complete it must cancel the future or throw a
      *         TerminationException.
diff --git a/spec/src/main/asciidoc/microprofile-lra-spec.adoc b/spec/src/main/asciidoc/microprofile-lra-spec.adoc
index eb4fd05..a58ecd1 100644
--- a/spec/src/main/asciidoc/microprofile-lra-spec.adoc
+++ b/spec/src/main/asciidoc/microprofile-lra-spec.adoc
@@ -210,6 +210,11 @@ enter the state model until it receives a complete or compensate message:
 .Participant State Model
 image::participant-state-model.png[align="center" link="participant-state-model"]
 
+Note that when the LRA has been asked to cancel it enters the state `Cancelling`
+and will eventually ask all registered participants to enter the state
+`Compensating`. A similar remark applies to the LRA state `Closing` and participant state
+`Completing`.
+
 The participant can be enlisted only once per LRA instance. When participant
 is about to be enlisted multiple times (e.g. client calls the same `@LRA` method
 several times) the follow-up enlistments of such participant MUST be ignored.
@@ -359,16 +364,20 @@ will run in the context of an LRA and controls whether or not:
 
 If the method is to run in the context of an LRA and the annotated class
 also contains a method annotated with `@Compensate` then the class
-(aka participant or compensator) will be enlisted with the LRA. Enlisting with
-an LRA means that the bean will be notified when the current LRA is
+(aka participant or compensator) will be enlisted with the LRA.
+This means that before starting the method the implementation must be able
+to guarantee that participant has been or will eventually be registered
+with the LRA. The practical consequence of this requirement is that the
+implementation must durably record that the participant is enlisted before
+letting the business invocation proceed.
+Enlisting with an LRA means that the bean will be notified when the current LRA is
 subsequently cancelled or closed (if the class also contains a method
 annotated with `@Complete`). In addition the implementation should generate a
-URI for this participant that can be used
-for recovery purposes and it should make it available to the participant via a JAX-RS
+URI for this participant and make it available to the business logic via a JAX-RS
 header param with name defined by the Java constant `LRA_HTTP_RECOVERY_HEADER` as defined
 <<source-LRA, in the LRA annotation>>
 (https://github.com/eclipse/microprofile-lra/tree/master/api/src/main/java/org/eclipse/microprofile/lra/annotation/LRA.java[github link]).
-This recovery header should be made available to the application whenever any
+The header should also be made available to the application whenever any
 of the participant callbacks are invoked. The application is free to ignore
 this header.
 Enlisting in an LRA is explained in more detail
@@ -501,7 +510,13 @@ activity by annotating it with both the `@Compensate` and the JAX-RS `@PUT` anno
 Whenever the associated resource is invoked in the context of
 an LRA the endpoint corresponding to the `@Compensate` method MUST be enlisted with
 the LRA. If the LRA is subsequently cancelled then the compensation method MUST be invoked.
-For example:
+
+The specification does not mandate when this method is invoked
+but it does guarantee that it will eventually be called. Under failure
+conditions the system will keep retrying until it is certain that the
+participant has been successfully notified.
+
+An example of how to use this annoation is:
 
 [source,java]
 ----
@@ -517,7 +532,8 @@ For example:
 
 The resource class MAY also contain a method marked with the `@Complete` and the
 JAX-RS `@PUT` annotations. If such a method is present then the method MUST
-be invoked when the associated LRA is closed.
+be invoked when the associated LRA is closed. Again, the specification does
+not MANDATE when the method is called, just that it will eventually be invoked.
 Typically the resource would use this call to perform any clean up actions. The
 method is optional since such clean up actions may not be necessary,
 for example consider a system that just tracks hotel reservations and has operations
@@ -798,7 +814,7 @@ The framework must guarantee that participants will still be triggered (the LRA
 nothing" guarantees that traditional transactions give).
 This means that an implementation for all JAX-RS resources which implement the _LRAParticipant_ interface directly
 or indirectly, MUST create some proxy which can be called by the spec implementation. +
-This to ensure that after this class has joined an LRA (through the _lraManagement.joinLRA()_ ) the _complete_ or
+This is to ensure that after this class has joined an LRA (through the _lraManagement.joinLRA()_ ) the _complete_ or
 _compensate_ methods can be called by the spec implementation in case the microservice crashed before this notification could
 be delivered.
 
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
index 567b4d3..64215e6 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
@@ -44,40 +44,13 @@ import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingT
 import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.START_BUT_DONT_END_PATH;
 import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.STATUS_CODE_QUERY_NAME;
 import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.TCK_NON_PARTICIPANT_RESOURCE_PATH;
-import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTING_LRA_PATH;
 import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.LEAVE_PATH;
 import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.TCK_PARTICIPANT_RESOURCE_PATH;
 
 public class LRAClientOps {
     private static final Logger LOGGER = Logger.getLogger(TckLRATypeTests.class.getName());
 
-    /**
-     * TODO recovery needs to be properly speced: see the proposal
-     * on issue https://github.com/eclipse/microprofile-lra/issues/116
-     * It is used in the two tests for participants that return 202:
-     * {@link TckTests#acceptCancelTest()}  and {@link TckTests#acceptCloseTest()}
-     * to connect to an endpoint that will trigger recovery (the alternative would
-     * be to wait)
-     */
-
-    /**
-     * Key for looking up the config property that specifies which host a
-     * recovery coordinator is running on
-     */
-    static final String LRA_RECOVERY_HOST_KEY = "lra.http.recovery.host";
-
-    /**
-     * Key for looking up the config property that specifies which port a
-     * recovery coordinator is listening on
-     */
-    static final String LRA_RECOVERY_PORT_KEY = "lra.http.recovery.port";
-
-    /**
-     * Key for looking up the config property that specifies which JAX-RS path a
-     * recovery coordinator is running on
-     */
-    static final String LRA_RECOVERY_PATH_KEY = "lra.http.recovery.path";
-
     private final WebTarget target;
     private final ScheduledExecutorService executor;
     private final Map<LRATask, ScheduledFuture<?>> lraTasks;
@@ -90,8 +63,8 @@ public class LRAClientOps {
 
     // see if it is possible to join with an LRA - if it is possible to do that then the LRA is still active
     private int tryToEnlistWithAnLRA(String lra) {
-        // call a JAX-RS endpint that should result in the enlistment of a resource into the LRA
-        int status = invokeRestEndpointAndReturnStatus(lra, TCK_PARTICIPANT_RESOURCE_PATH, JOIN_WITH_EXISTNG_LRA_PATH, 200);
+        // call a JAX-RS endpoint that should result in the enlistment of a resource into the LRA
+        int status = invokeRestEndpointAndReturnStatus(lra, TCK_PARTICIPANT_RESOURCE_PATH, JOIN_WITH_EXISTING_LRA_PATH, 200);
 
         if (status == 200) {
             // leave the LRA otherwise any tests checking completion/compensation counts would fail
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java
index ad3d76e..9705a0b 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/LraTckConfigBean.java
@@ -24,6 +24,8 @@ import javax.enterprise.context.ApplicationScoped;
 import javax.inject.Inject;
 
 import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.eclipse.microprofile.lra.annotation.LRAStatus;
+import org.eclipse.microprofile.lra.annotation.ParticipantStatus;
 
 @ApplicationScoped
 public class LraTckConfigBean {
@@ -48,6 +50,33 @@ public class LraTckConfigBean {
     @Inject @ConfigProperty(name = "lra.tck.timeout.factor", defaultValue = "1.0")
     private double timeoutFactor;
 
+    /**
+     * <p>
+     *     The LRA model guarantees eventual consistency but does not say when
+     *     participants will be brought into a consistent state, for example an
+     *     implementation may not notify participants immediately when an LRA
+     *     is closed or cancelled.
+     *
+     *     To support implementations that do not immediately attempt to notify
+     *     participants the TCK needs to know how long to delay before checking whether
+     *     or not the implementation called them.
+     *     This delay will depend on the particular implementation so it is
+     *     configurable, i.e, use this property to configure the number of
+     *     milliseconds that a test will wait for before it checks if a complete
+     *     or compensate method has been called.
+     *
+     *     The default is set to zero which implies that the implementation will
+     *     notify participants as soon as the LRA enters the
+     *     {@link LRAStatus#Closing} or {@link LRAStatus#Cancelling}
+     *     states. Note that this does not imply that the participant will
+     *     complete or compensate immediately since it may enter the
+     *     {@link ParticipantStatus#Completing} or {@link ParticipantStatus#Compensating}
+     *     states first if a participant cannot clean up or compensate immediately.
+     * </p>
+     */
+    @Inject @ConfigProperty(name = "lra.tck.consistency.delay", defaultValue = "0")
+    long consistencyDelay;
+
     /**
      * <p>
      *     the maximum number of seconds to wait for recovery
@@ -87,6 +116,10 @@ public class LraTckConfigBean {
         return timeoutFactor;
     }
 
+    long getConsistencyDelay() {
+        return consistencyDelay;
+    }
+
     public Long recoveryTimeout() {
         return recoveryTimeout;
     }
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ContextTckResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ContextTckResource.java
index 9cacbe3..f32ae73 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ContextTckResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ContextTckResource.java
@@ -316,7 +316,7 @@ public class ContextTckResource {
         return Response.ok().entity(status.name()).build();
     }
 
-    // modify participant responses based on injected behavour specified by the test
+    // modify participant responses based on injected behaviour specified by the test
     private Response getEndPhaseResponse(boolean completing) {
         switch (endPhase) {
             case ACCEPTED:
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java
index 9d0dd0a..1501f34 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/LraController.java
@@ -417,9 +417,9 @@ public class LraController {
         try {
             Thread.sleep(300); // sleep for longer than specified in the timeLimit annotation attribute
         } catch (InterruptedException e) {
-            LOGGER.log(Level.FINE, "Interrupted becaused time limit elapsed", e);
+            LOGGER.log(Level.FINE, "Interrupted because time limit elapsed", e);
         }
-        return Response.status(Response.Status.OK).entity(Entity.text("Simulate buisiness logic timeoout")).build();
+        return Response.status(Response.Status.OK).entity(Entity.text("Simulate business logic timeoout")).build();
     }
 
     /**
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java
index c63f10d..7e655eb 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/NonParticipatingTckResource.java
@@ -67,7 +67,7 @@ public class NonParticipatingTckResource {
     private static final AtomicInteger COMPLETED_COUNT = new AtomicInteger(0);
     private static final AtomicInteger COMPENSATED_COUNT = new AtomicInteger(0);
     public static final String STATUS_CODE_QUERY_NAME = "Coerce-Status";
-    public static final String START_LRA_VIA_REMOTE_INVOCATIOM = "/start-via-remote-invocation";
+    public static final String START_LRA_VIA_REMOTE_INVOCATION = "/start-via-remote-invocation";
 
     @Context
     private UriInfo context;
@@ -130,7 +130,7 @@ public class NonParticipatingTckResource {
     }
 
     @PUT
-    @Path(NonParticipatingTckResource.START_LRA_VIA_REMOTE_INVOCATIOM)
+    @Path(NonParticipatingTckResource.START_LRA_VIA_REMOTE_INVOCATION)
     @LRA(value = LRA.Type.SUPPORTS, end = false,
             cancelOnFamily = Response.Status.Family.SERVER_ERROR)
     public Response notSupportedButCallServiceWhichStartsButDoesntEndAnLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) {
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java
index 78223ab..577e8d3 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/ParticipatingTckResource.java
@@ -46,8 +46,8 @@ public class ParticipatingTckResource {
     private static final Logger LOGGER = Logger.getLogger(ParticipatingTckResource.class.getName());
 
     public static final String TCK_PARTICIPANT_RESOURCE_PATH = "participating-tck-resource";
-    public static final String JOIN_WITH_EXISTNG_LRA_PATH = "/join-with-existing-lra";
-    public static final String JOIN_WITH_EXISTNG_LRA_PATH2 = "/join-with-existing-lra2";
+    public static final String JOIN_WITH_EXISTING_LRA_PATH = "/join-with-existing-lra";
+    public static final String JOIN_WITH_EXISTING_LRA_PATH2 = "/join-with-existing-lra2";
     public static final String JOIN_WITH_NEW_LRA_PATH = "/join-with-new-LRA";
     public static final String COMPLETED_CNT_PATH = "/completed-count";
     public static final String COMPENSATED_CNT_PATH = "/compensated-count";
@@ -147,14 +147,14 @@ public class ParticipatingTckResource {
     // if this resource path is invoked outside of a LRA then, since this JAX-RS resoruce is annotated
     // as a participant, the implementation MUST report a PRECONDITION_FAILED status
     @PUT
-    @Path(ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH)
+    @Path(ParticipatingTckResource.JOIN_WITH_EXISTING_LRA_PATH)
     @LRA(value = LRA.Type.MANDATORY, end = false)
     public Response joinWithExistingLRA(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) {
         return Response.ok().build();
     }
 
     @PUT
-    @Path(ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH2)
+    @Path(ParticipatingTckResource.JOIN_WITH_EXISTING_LRA_PATH2)
     @LRA(value = LRA.Type.MANDATORY, end = false)
     public Response joinWithExistingLRA2(@HeaderParam(LRA_HTTP_CONTEXT_HEADER) String lraId) {
         return Response.ok().build();
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/Util.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/Util.java
index b879df9..8650e6a 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/Util.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/participant/api/Util.java
@@ -94,9 +94,9 @@ public class Util {
 
         if (asyncTermination[0] && !paths.containsKey(STATUS) && !paths.containsKey(FORGET)) {
             LOGGER.log(Level.WARNING, "LRA participant class "
-                    + compensatorClass + " with asynchronous temination but no @Status or @Forget annotations");
+                    + compensatorClass + " with asynchronous termination but no @Status or @Forget annotations");
             throw new GenericLRAException(null, Response.Status.BAD_REQUEST.getStatusCode(),
-                    "LRA participant class with asynchronous temination but no @Status or @Forget annotations", null);
+                    "LRA participant class with asynchronous termination but no @Status or @Forget annotations", null);
         }
 
         StringBuilder linkHeaderValue = new StringBuilder();


Test patch:
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java
index 3d8f1c5..06f23e7 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckCancelOnTests.java
@@ -22,84 +22,38 @@ package org.eclipse.microprofile.lra.tck;
 import static org.eclipse.microprofile.lra.tck.participant.api.LraCancelOnController.LRA_CANCEL_ON_CONTROLLER_PATH;
 import static org.junit.Assert.assertEquals;
 
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URL;
-import java.util.logging.Logger;
-
-import javax.ws.rs.client.Client;
-import javax.ws.rs.client.ClientBuilder;
 import javax.ws.rs.client.WebTarget;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
 import org.eclipse.microprofile.lra.tck.participant.api.LraCancelOnController;
 import org.jboss.arquillian.container.test.api.Deployment;
-import org.jboss.arquillian.container.test.api.RunAsClient;
 import org.jboss.arquillian.junit.Arquillian;
-import org.jboss.shrinkwrap.api.ShrinkWrap;
-import org.jboss.shrinkwrap.api.asset.EmptyAsset;
 import org.jboss.shrinkwrap.api.spec.WebArchive;
-import org.junit.AfterClass;
 import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.TestName;
 import org.junit.runner.RunWith;
 
 @RunWith(Arquillian.class)
-@RunAsClient
-public class TckCancelOnTests {
-    private static final Logger LOGGER = Logger.getLogger(TckCancelOnTests.class.getName());
-
-
-    @Rule public TestName testName = new TestName();
-
-    private static final String TCK_SUITE_BASE_URL = System.getProperty("lra.tck.base.url", "http://localhost:8180");
-    private static Client tckSuiteClient;
-
+public class TckCancelOnTests extends TckTestBase {
     private int beforeCompletedCount;
     private int beforeCompensatedCount;
 
-    @Deployment(name = "tcktests-cancelon", managed = true, testable = true)
+    @Deployment(name = "tcktests-cancelon")
     public static WebArchive deploy() {
-        String archiveName = TckCancelOnTests.class.getSimpleName().toLowerCase();
-        return ShrinkWrap
-            .create(WebArchive.class, archiveName + ".war")
-            .addPackages(true, "org.eclipse.microprofile.lra.tck")
-            .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
-    }
-
-    @BeforeClass
-    public static void beforeClass() {
-        tckSuiteClient = ClientBuilder.newClient();
-    }
-
-    @AfterClass
-    public static void afterClass() {
-        if(tckSuiteClient != null) {
-            tckSuiteClient.close();
-        }
+        return TckTestBase.deploy(TckCancelOnTests.class.getSimpleName().toLowerCase());
     }
 
     @Before
     public void before() {
-        LOGGER.info("Running test: " + testName.getMethodName());
+        super.before();
+
         this.beforeCompletedCount = getCompletedCount();
-        this.beforeCompensatedCount = getCompensateCount();
+        this.beforeCompensatedCount = getCompensateCount(false);
     }
 
     private WebTarget getSuiteTarget() {
-        if(TCK_SUITE_BASE_URL == null) {
-            throw new NullPointerException("The test is not provided with the system property 'lra.tck.base.url'");
-        }
-        try {
-            return tckSuiteClient.target(URI.create(new URL(TCK_SUITE_BASE_URL).toExternalForm()));
-        } catch (MalformedURLException mfe) {
-            throw new IllegalStateException("Cannot create REST test target for the LRA TCK suite base url "
-                    + TCK_SUITE_BASE_URL, mfe);
-        }
+        return tckSuiteTarget;
     }
 
     /**
@@ -110,7 +64,7 @@ public class TckCancelOnTests {
         Response response = getSuiteTarget().path(LRA_CANCEL_ON_CONTROLLER_PATH)
                 .path(LraCancelOnController.CANCEL_ON_FAMILY_DEFAULT_4XX).request().get();
         assertEquals("The 400 status response is expected", Status.BAD_REQUEST.getStatusCode(), response.getStatus());
-        assertEquals("After 400 compensate should be invoked", beforeCompensatedCount + 1, getCompensateCount());
+        assertEquals("After 400 compensate should be invoked", beforeCompensatedCount + 1, getCompensateCount(true));
         assertEquals("After 400 complete can't be invoked", beforeCompletedCount, getCompletedCount());
     }
 
@@ -122,7 +76,7 @@ public class TckCancelOnTests {
         Response response = getSuiteTarget().path(LRA_CANCEL_ON_CONTROLLER_PATH)
                 .path(LraCancelOnController.CANCEL_ON_FAMILY_DEFAULT_5XX).request().get();
         assertEquals("The 500 status response is expected", Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
-        assertEquals("After 500 compensate should be invoked", beforeCompensatedCount + 1, getCompensateCount());
+        assertEquals("After 500 compensate should be invoked", beforeCompensatedCount + 1, getCompensateCount(true));
         assertEquals("After 500 complete can't be invoked", beforeCompletedCount, getCompletedCount());
     }
 
@@ -135,7 +89,7 @@ public class TckCancelOnTests {
                 .path(LraCancelOnController.CANCEL_ON_FAMILY_3XX).request().get();
         assertEquals("The 303 status response is expected", Status.SEE_OTHER.getStatusCode(), response.getStatus());
         assertEquals("After status code 303 is received, compensate should be invoked as set by attribute cancelOnFamily",
-                beforeCompensatedCount + 1, getCompensateCount());
+                beforeCompensatedCount + 1, getCompensateCount(true));
         assertEquals("After status code 303 is received, complete can't be invoked as not defined in annotation @LRA",
                 beforeCompletedCount, getCompletedCount());
     }
@@ -149,7 +103,7 @@ public class TckCancelOnTests {
                 .path(LraCancelOnController.CANCEL_ON_301).request().get();
         assertEquals("The 301 status response is expected", Status.MOVED_PERMANENTLY.getStatusCode(), response.getStatus());
         assertEquals("After status code 301 is received, compensate should be invoked as set by attribute cancelOn",
-                beforeCompensatedCount + 1, getCompensateCount());
+                beforeCompensatedCount + 1, getCompensateCount(true));
         assertEquals("After status code 301 is received, complete can't be invoked as not defined in annotation @LRA",
                 beforeCompletedCount, getCompletedCount());
     }
@@ -163,7 +117,7 @@ public class TckCancelOnTests {
                 .path(LraCancelOnController.NOT_CANCEL_ON_FAMILY_5XX).request().get();
         assertEquals("The 500 status response is expected", Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
         assertEquals("After status code 500 is received, compensate can't be invoked as default behaviour was changed",
-                beforeCompensatedCount, getCompensateCount());
+                beforeCompensatedCount, getCompensateCount(true));
         assertEquals("After status code 500 is received, complete has to be called as default behaviour was changed",
                 beforeCompletedCount + 1, getCompletedCount());
     }
@@ -178,20 +132,23 @@ public class TckCancelOnTests {
         assertEquals("The 200 status response is expected", Status.OK.getStatusCode(), response.getStatus());
         // LraCancelOnController enlists twice the same participant, compensate is expected to be called only once
         assertEquals("Status was 200 but compensate should be called as LRA should be cancelled for remotely called participant as well",
-                beforeCompensatedCount + 1, getCompensateCount());
-        assertEquals("Even the 200 status was received the remotly called participant should cause the LRA being cancelled",
+                beforeCompensatedCount + 1, getCompensateCount(true));
+        assertEquals("Even the 200 status was received the remotely called participant should cause the LRA being cancelled",
                 beforeCompletedCount, getCompletedCount());
     }
 
-
-
     private int getCompletedCount() {
+        // no option to delay the check since all tests call getCompensateCount first
+        // which does have the option to delay
         Response response = getSuiteTarget().path(LRA_CANCEL_ON_CONTROLLER_PATH)
                 .path(LraCancelOnController.COMPLETED_COUNT_PATH).request().get();
         return response.readEntity(Integer.class);
     }
 
-    private int getCompensateCount() {
+    private int getCompensateCount(boolean delay) {
+        if (delay) {
+            applyConsistencyDelay();
+        }
         Response response = getSuiteTarget().path(LRA_CANCEL_ON_CONTROLLER_PATH)
                 .path(LraCancelOnController.COMPENSATED_COUNT_PATH).request().get();
         return Integer.valueOf(response.readEntity(String.class));
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckContextTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckContextTests.java
index 31fac2f..f270fa9 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckContextTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckContextTests.java
@@ -69,16 +69,16 @@ public class TckContextTests extends TckTestBase {
     @Before
     public void before() {
         super.before();
-        invoke(RESET_PATH, HttpMethod.PUT, null, 200, null, 200);
+        invoke(false, RESET_PATH, HttpMethod.PUT, null, 200, null, 200);
     }
 
     @Test
     public void testBasicContextPropagation() {
-        String lra = invoke(NEW_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.SUCCESS, 200);
-        invoke(REQUIRED_LRA_PATH, PUT, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+        String lra = invoke(false, NEW_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+        invoke(false, REQUIRED_LRA_PATH, PUT, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
 
         // verify that the resource was asked to complete
-        String completions = invoke(METRIC_PATH + "/" + Complete.class.getName(), GET, lra);
+        String completions = invoke(true, METRIC_PATH + "/" + Complete.class.getName(), GET, lra);
 
         assertEquals(testName.getMethodName() + ": Resource was not asked to complete",
                 Integer.toString(1), completions);
@@ -87,40 +87,40 @@ public class TckContextTests extends TckTestBase {
     @Test
     public void testStatus() throws InterruptedException {
         // call a resource that begins and ends an LRA and coerces the resource to return ACCEPTED when asked to complete
-        String lra = invoke(REQUIRED_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.ACCEPTED, 202);
+        String lra = invoke(false, REQUIRED_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.ACCEPTED, 202);
 
-        // verfiy that the resource was asked to complete and is in the state Completing
-        String status = invoke(STATUS_PATH, HttpMethod.GET, lra, 202, ContextTckResource.EndPhase.SUCCESS, 200);
+        // verify that the resource was asked to complete and is in the state Completing
+        String status = invoke(true, STATUS_PATH, HttpMethod.GET, lra, 202, ContextTckResource.EndPhase.SUCCESS, 200);
         assertEquals(testName.getMethodName() + ": participant is not completing", ParticipantStatus.Completing.name(), status);
 
         // clear the EndPhase override data so that the next status request returns completed or compensated
-        invoke(STATUS_PATH, PUT, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+        invoke(false, STATUS_PATH, PUT, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
 
         // trigger a replay of the end phase
         replayEndPhase(TCK_CONTEXT_RESOURCE_PATH);
 
-        // and verify that the resoure was asked to complete
-        status = invoke(STATUS_PATH, HttpMethod.GET, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+        // and verify that the resource was asked to complete
+        status = invoke(false, STATUS_PATH, HttpMethod.GET, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
         assertEquals(testName.getMethodName() + ": participant is not completed", ParticipantStatus.Completed.name(), status);
     }
 
     @Test
     public void testLeave() {
         // call a resource that begins but does not end an LRA
-        String lra = invoke(NEW_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.SUCCESS, 200);
-        // verfiy that the resource is active
-        String status = invoke(STATUS_PATH, HttpMethod.GET, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+        String lra = invoke(false, NEW_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+        // verify that the resource is active
+        String status = invoke(false, STATUS_PATH, HttpMethod.GET, lra, 200, ContextTckResource.EndPhase.SUCCESS, 200);
 
         assertEquals(testName.getMethodName() + ": participant is not active", ParticipantStatus.Active.name(), status);
 
         // ask the resource to leave the active LRA
-        invoke(LEAVE_PATH, PUT, lra);
+        invoke(false, LEAVE_PATH, PUT, lra);
 
         // end the LRA via a different resource since using the same one will re-enlist it
         lraClient.closeLRA(lra);
 
         // verify that the resource was not asked to complete
-        String completions = invoke(METRIC_PATH + "/" + Complete.class.getName(), GET, lra);
+        String completions = invoke(true, METRIC_PATH + "/" + Complete.class.getName(), GET, lra);
 
 
         assertEquals(testName.getMethodName() + ": Resource left but was still asked to complete",
@@ -131,30 +131,30 @@ public class TckContextTests extends TckTestBase {
     public void testForget() throws InterruptedException {
         String count;
         // call a resource that begins and ends an LRA and coerces the resource to return FAILED when asked to complete
-        String lra = invoke(REQUIRED_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.FAILED, 500);
+        String lra = invoke(false, REQUIRED_LRA_PATH, PUT, null, 200, ContextTckResource.EndPhase.FAILED, 500);
 
         // trigger a replay attempt
         replayEndPhase(TCK_CONTEXT_RESOURCE_PATH);
 
         // the implementation should have called status which will have returned 500
-        count = invoke(METRIC_PATH + "/" + Status.class.getName(), GET, lra);
+        count = invoke(true, METRIC_PATH + "/" + Status.class.getName(), GET, lra);
         assertEquals(testName.getMethodName() + " resource status should have been called", "1", count);
 
-        // the implementation should not call forget until it knows the particpant status
-        count = invoke(METRIC_PATH + "/" + Forget.class.getName(), GET, lra);
+        // the implementation should not call forget until it knows the participant status
+        count = invoke(false, METRIC_PATH + "/" + Forget.class.getName(), GET, lra);
         assertEquals(testName.getMethodName() + " resource forget should not have been called", "0", count);
 
         // clear the fault
-        invoke(STATUS_PATH, PUT, lra, 200, ContextTckResource.EndPhase.FAILED, 200);
+        invoke(false, STATUS_PATH, PUT, lra, 200, ContextTckResource.EndPhase.FAILED, 200);
 
         // trigger a replay of the end phase
         replayEndPhase(TCK_CONTEXT_RESOURCE_PATH);
 
         // the implementation should have called status again which will have returned 200
-        count = invoke(METRIC_PATH + "/" + Status.class.getName(), GET, lra);
+        count = invoke(false, METRIC_PATH + "/" + Status.class.getName(), GET, lra);
         assertEquals(testName.getMethodName() + " resource status should have been called again", "2", count);
-        // the implementation should call forget since it knows the particpant status
-        count = invoke(METRIC_PATH + "/" + Forget.class.getName(), GET, lra);
+        // the implementation should call forget since it knows the participant status
+        count = invoke(false, METRIC_PATH + "/" + Forget.class.getName(), GET, lra);
         assertEquals(testName.getMethodName() + " resource forget should have been called", "1", count);
     }
 
@@ -162,14 +162,14 @@ public class TckContextTests extends TckTestBase {
     // conforms with what is written in the specification
     @Test
     public void testContextAfterRemoteCalls() {
-        invoke(CONTEXT_CHECK_LRA_PATH, PUT, null);
+        invoke(false, CONTEXT_CHECK_LRA_PATH, PUT, null);
     }
 
-    private String invoke(String where, HttpMethod method, String lraContext) {
-        return invoke(where, method, lraContext, 200, ContextTckResource.EndPhase.SUCCESS, 200);
+    private String invoke(boolean delay, String where, HttpMethod method, String lraContext) {
+        return invoke(delay, where, method, lraContext, 200, ContextTckResource.EndPhase.SUCCESS, 200);
     }
 
-    private String invoke(String where, HttpMethod method, String lraContext, int expectStatus,
+    private String invoke(boolean delay, String where, HttpMethod method, String lraContext, int expectStatus,
                           ContextTckResource.EndPhase finishWith, int finishStatus) {
         WebTarget resourcePath = tckSuiteTarget.path(TCK_CONTEXT_RESOURCE_PATH).path(where);
         Invocation.Builder builder = resourcePath.request()
@@ -183,6 +183,10 @@ public class TckContextTests extends TckTestBase {
             builder.header(LRA_HTTP_CONTEXT_HEADER, lraContext);
         }
 
+        if (delay) {
+            applyConsistencyDelay();
+        }
+
         switch (method) {
             case GET:
                 response =  builder.get();
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
index df33bec..8195a29 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTestBase.java
@@ -130,7 +130,20 @@ public class TckTestBase {
     }
 
     /**
-     * Trigger or wait for a recovery scan to replay the protocol termination phase (complete or compenstate)
+     * @see LraTckConfigBean#consistencyDelay
+     */
+    void applyConsistencyDelay() {
+        if (config.getConsistencyDelay() > 0) {
+            try {
+                Thread.sleep(config.getConsistencyDelay());
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    /**
+     * Trigger or wait for a recovery scan to replay the protocol termination phase (complete or compensate)
      * on a participant that is in need of recovery.
      *
      * @param resource the resource on which recovery should be replayed. If null then recovery
@@ -144,7 +157,7 @@ public class TckTestBase {
          * The following variable is for keeping track of the number of completion calls made
          * on the resource (TCK_PARTICIPANT_RESOURCE_PATH). It is initialised with the value 2
          * and then passed to the resource. We then loop asking the resource to report its
-         * current value. The resouce will decrement the value each time it is asked to complete.
+         * current value. The resource will decrement the value each time it is asked to complete.
          * When the return value hits zero we will know that recovery has happened.
          */
         int recoveryPasses = 2; // 1 for the normal end call and 2 for the recovery pass call
diff --git a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java
index ab88e71..3653ba0 100644
--- a/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java
+++ b/tck/src/main/java/org/eclipse/microprofile/lra/tck/TckTests.java
@@ -25,8 +25,8 @@ import static org.eclipse.microprofile.lra.tck.participant.api.LraController.LRA
 import static org.eclipse.microprofile.lra.tck.participant.api.LraController.TRANSACTIONAL_WORK_PATH;
 import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.COMPENSATED_CNT_PATH;
 import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.COMPLETED_CNT_PATH;
-import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH;
-import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTNG_LRA_PATH2;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTING_LRA_PATH;
+import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.JOIN_WITH_EXISTING_LRA_PATH2;
 import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.TCK_PARTICIPANT_RESOURCE_PATH;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -72,9 +72,9 @@ public class TckTests extends TckTestBase {
 
     /**
      * <p>
-     * Starting LRA and canceling it.
+     * Starting LRA and cancelling it.
      * <p>
-     * It's expected the LRA won't be listed in active LRAs when canceled.
+     * It's expected the LRA won't be listed in active LRAs when cancelled.
      */
     @Test
     public void cancelLRA() throws WebApplicationException {
@@ -163,7 +163,7 @@ public class TckTests extends TckTestBase {
 
     @Test
     public void joinLRAViaHeader() throws WebApplicationException {
-        int beforeCompletedCount = getCompletedCount();
+        int beforeCompletedCount = getCompletedCount(false);
 
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
 
@@ -180,14 +180,14 @@ public class TckTests extends TckTestBase {
         // close the LRA
         lraClient.closeLRA(lra);
 
-        // check that LRA coordinator no longer knows about lraId
-        assertTrue("LRA '" + lra + "' should not be active anymore as it was closed yet.",
-                lraClient.isLRAFinished(lra));
-
         // check that participant was told to complete
-        int completedCount = getCompletedCount();
+        int completedCount = getCompletedCount(true);
         assertEquals("Wrong completion count for call " + resourcePath.getUri() + ". Expecting the method LRA was completed "
                 + "after joining the existing LRA " + lra, beforeCompletedCount + 1, completedCount);
+        // check that LRA coordinator no longer knows about lraId
+
+        assertTrue("LRA '" + lra + "' should not be active anymore as it was closed yet.",
+                lraClient.isLRAFinished(lra));
     }
 
     @Test
@@ -204,7 +204,7 @@ public class TckTests extends TckTestBase {
 
     @Test
     public void leaveLRA() throws WebApplicationException {
-        int beforeCompletedCount = getCompletedCount();
+        int beforeCompletedCount = getCompletedCount(false);
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
         Response response = resourcePath.request().header(LRA_HTTP_CONTEXT_HEADER, lra).put(Entity.text(""));
@@ -224,7 +224,7 @@ public class TckTests extends TckTestBase {
         lraClient.closeLRA(lra);
 
         // check that participant was not told to complete
-        int completedCount = getCompletedCount();
+        int completedCount = getCompletedCount(true);
 
         assertEquals("Wrong completion count when participant left the LRA. "
                 + "Expecting the completed count hasn't change between start and end of the test. "
@@ -233,7 +233,7 @@ public class TckTests extends TckTestBase {
 
     @Test
     public void dependentLRA() throws WebApplicationException {
-        // call a method annotated with NOT_SUPPORTED but one which programatically starts an LRA and returns it via a header
+        // call a method annotated with NOT_SUPPORTED but one which programmatically starts an LRA and returns it via a header
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("startViaApi");
         Response response = resourcePath.request().put(Entity.text(""));
         Object lraHeader = response.getHeaders().getFirst(LRA_HTTP_CONTEXT_HEADER);
@@ -252,8 +252,8 @@ public class TckTests extends TckTestBase {
 
     @Test
     public void timeLimit() {
-        int beforeCompletedCount = getCompletedCount();
-        int beforeCompensatedCount = getCompensatedCount();
+        int beforeCompletedCount = getCompletedCount(false);
+        int beforeCompensatedCount = getCompensatedCount(false);
         
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("timeLimit");
         Response response = resourcePath
@@ -267,8 +267,8 @@ public class TckTests extends TckTestBase {
         // (depends upon how long the coordinator keeps a record of finished LRAs
 
         // check that participant was invoked
-        int completedCount = getCompletedCount();
-        int compensatedCount = getCompensatedCount();
+        int completedCount = getCompletedCount(true);
+        int compensatedCount = getCompensatedCount(false);
 
         /*
          * The call to activities/timeLimit should have started an LRA which should have timed out
@@ -285,19 +285,19 @@ public class TckTests extends TckTestBase {
 
     @Test
     public void acceptCloseTest() throws WebApplicationException, InterruptedException {
-        joinAndEnd(true, true, LRA_CONTROLLER_PATH, ACCEPT_WORK);
+        joinAndEnd(true, LRA_CONTROLLER_PATH, ACCEPT_WORK);
     }
 
     @Test
     public void acceptCancelTest() throws WebApplicationException, InterruptedException {
-        joinAndEnd(true, false, LRA_CONTROLLER_PATH, ACCEPT_WORK);
+        joinAndEnd(false, LRA_CONTROLLER_PATH, ACCEPT_WORK);
     }
 
-    private void joinAndEnd(boolean waitForRecovery, boolean close, String path, String path2)
+    private void joinAndEnd(boolean close, String path, String path2)
             throws WebApplicationException, InterruptedException {
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
-        int beforeCompletionCount = getCompletedCount();
-        int beforeCompensationCount = getCompensatedCount();
+        int beforeCompletionCount = getCompletedCount(false);
+        int beforeCompensationCount = getCompensatedCount(false);
         WebTarget resourcePath = tckSuiteTarget.path(path).path(path2);
 
         Response response = resourcePath
@@ -311,12 +311,10 @@ public class TckTests extends TckTestBase {
             lraClient.cancelLRA(lra);
         }
 
-        if (waitForRecovery) {
-            replayEndPhase(null);
-        }
+        replayEndPhase(null);
 
-        int completionCount = getCompletedCount() - beforeCompletionCount;
-        int compensationCount = getCompensatedCount() - beforeCompensationCount;
+        int completionCount = getCompletedCount(false) - beforeCompletionCount;
+        int compensationCount = getCompensatedCount(true) - beforeCompensationCount;
 
         boolean wasCalled = (close ? completionCount > 0 : compensationCount > 0);
         boolean wasNotCalled = (close ? compensationCount == 0 : completionCount == 0);
@@ -338,8 +336,8 @@ public class TckTests extends TckTestBase {
                 .path(NoLRAController.NO_LRA_CONTROLLER_PATH)
                 .path(NoLRAController.NON_TRANSACTIONAL_WORK_PATH);
 
-        int beforeCompletedCount = getCompletedCount();
-        int beforeCompensatedCount = getCompensatedCount();
+        int beforeCompletedCount = getCompletedCount(false);
+        int beforeCompensatedCount = getCompensatedCount(false);
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
 
         Response response = resourcePath.request().header(LRA_HTTP_CONTEXT_HEADER, lra)
@@ -355,8 +353,8 @@ public class TckTests extends TckTestBase {
         // check that second service (the LRA aware one), namely
         // {@link org.eclipse.microprofile.lra.tck.participant.api.ActivityController#activityWithMandatoryLRA(String, String)}
         // was told to compensate
-        int completedCount = getCompletedCount();
-        int compensatedCount = getCompensatedCount();
+        int completedCount = getCompletedCount(true);
+        int compensatedCount = getCompensatedCount(false);
 
         assertEquals("Complete should not be called on the LRA aware service. "
                 + "The number of completed count for before and after test does not match. "
@@ -373,7 +371,7 @@ public class TckTests extends TckTestBase {
     @Test
     public void joinWithOneResourceSameMethodTwiceWithCancel() throws WebApplicationException {
         joinWithOneResource("joinWithOneResourceSameMethodTwiceWithCancel",
-                false, JOIN_WITH_EXISTNG_LRA_PATH, JOIN_WITH_EXISTNG_LRA_PATH);
+                false, JOIN_WITH_EXISTING_LRA_PATH, JOIN_WITH_EXISTING_LRA_PATH);
     }
 
     /**
@@ -384,7 +382,7 @@ public class TckTests extends TckTestBase {
     @Test
     public void joinWithOneResourceDifferentMethodTwiceWithCancel() throws WebApplicationException {
         joinWithOneResource("joinWithOneResourceDifferentMethodTwiceWithCancel",
-                false, JOIN_WITH_EXISTNG_LRA_PATH, JOIN_WITH_EXISTNG_LRA_PATH2);
+                false, JOIN_WITH_EXISTING_LRA_PATH, JOIN_WITH_EXISTING_LRA_PATH2);
     }
 
     /**
@@ -395,7 +393,7 @@ public class TckTests extends TckTestBase {
     @Test
     public void joinWithOneResourceSameMethodTwiceWithClose() throws WebApplicationException {
         joinWithOneResource("joinWithOneResourceSameMethodTwiceWithClose",
-                true, JOIN_WITH_EXISTNG_LRA_PATH, JOIN_WITH_EXISTNG_LRA_PATH);
+                true, JOIN_WITH_EXISTING_LRA_PATH, JOIN_WITH_EXISTING_LRA_PATH);
     }
 
     /**
@@ -406,7 +404,7 @@ public class TckTests extends TckTestBase {
     @Test
     public void joinWithOneResourceDifferentMethodTwiceWithClose() throws WebApplicationException {
         joinWithOneResource("joinWithOneResourceDifferentMethodTwiceWithClose",
-                true, JOIN_WITH_EXISTNG_LRA_PATH, JOIN_WITH_EXISTNG_LRA_PATH2);
+                true, JOIN_WITH_EXISTING_LRA_PATH, JOIN_WITH_EXISTING_LRA_PATH2);
     }
 
     /**
@@ -429,19 +427,19 @@ public class TckTests extends TckTestBase {
         joinWithTwoResources(false);
     }
 
-    private void joinWithOneResource(String methodName, boolean close, String resouce1Method, String resouce2Method)
+    private void joinWithOneResource(String methodName, boolean close, String resource1Method, String resource2Method)
             throws WebApplicationException {
         // get the initial values for the number of completions and compensations
-        int completedCount = getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH);
-        int compensatedCount = getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH);
+        int completedCount = getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH);
+        int compensatedCount = getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH);
 
         // set up the web target for the test
-        WebTarget resource1Path = tckSuiteTarget.path(TCK_PARTICIPANT_RESOURCE_PATH).path(resouce1Method);
-        WebTarget resource2Path = tckSuiteTarget.path(TCK_PARTICIPANT_RESOURCE_PATH).path(resouce2Method);
+        WebTarget resource1Path = tckSuiteTarget.path(TCK_PARTICIPANT_RESOURCE_PATH).path(resource1Method);
+        WebTarget resource2Path = tckSuiteTarget.path(TCK_PARTICIPANT_RESOURCE_PATH).path(resource2Method);
 
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
 
-        // invoke the same JAX-RS resources twicein the context of the lra which should enlist the resource only once:
+        // invoke the same JAX-RS resources twice in the context of the lra which should enlist the resource only once:
         Response response1 = resource1Path.request().header(LRA_HTTP_CONTEXT_HEADER, lra).put(Entity.text(""));
         checkStatusAndCloseResponse(Response.Status.OK, response1, resource1Path);
         Response response2 = resource2Path.request().header(LRA_HTTP_CONTEXT_HEADER, lra).put(Entity.text(""));
@@ -451,27 +449,27 @@ public class TckTests extends TckTestBase {
             lraClient.closeLRA(lra);
 
             assertTrue(methodName + ": resource should have completed once with no compensations",
-                    completedCount + 1 == getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH)
-                            && compensatedCount == getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH));
+                    completedCount + 1 == getActivityCount(true, TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH)
+                            && compensatedCount == getActivityCount(true, TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH));
         } else {
             lraClient.cancelLRA(lra);
 
             assertTrue(methodName + ":: resource should have compensated once with no completions",
-                    completedCount == getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH)
-                            && compensatedCount + 1 == getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH));
+                    completedCount == getActivityCount(true, TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH)
+                            && compensatedCount + 1 == getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH));
         }
     }
 
     private void joinWithTwoResources(boolean close) throws WebApplicationException {
         // get the initial values for the number of completions and compensations
-        int completedCount1 = getActivityCount(LRA_CONTROLLER_PATH, "completedactivitycount");
-        int compensatedCount1 = getActivityCount(LRA_CONTROLLER_PATH, "compensatedactivitycount");
-        int completedCount2 = getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH);
-        int compensatedCount2 = getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH);
+        int completedCount1 = getActivityCount(false, LRA_CONTROLLER_PATH, "completedactivitycount");
+        int compensatedCount1 = getActivityCount(false, LRA_CONTROLLER_PATH, "compensatedactivitycount");
+        int completedCount2 = getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH);
+        int compensatedCount2 = getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH);
 
         // set up the web target for the test
         WebTarget resource1Path = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path(TRANSACTIONAL_WORK_PATH);
-        WebTarget resource2Path = tckSuiteTarget.path(TCK_PARTICIPANT_RESOURCE_PATH).path(JOIN_WITH_EXISTNG_LRA_PATH);
+        WebTarget resource2Path = tckSuiteTarget.path(TCK_PARTICIPANT_RESOURCE_PATH).path(JOIN_WITH_EXISTING_LRA_PATH);
 
         URI lra = lraClient.startLRA(null, lraClientId(), lraTimeout(), ChronoUnit.MILLIS);
 
@@ -485,30 +483,33 @@ public class TckTests extends TckTestBase {
             lraClient.closeLRA(lra);
 
             assertTrue("joinWithTwoResourcesWithClose: both resources should have completed",
-                    getActivityCount(LRA_CONTROLLER_PATH, "completedactivitycount") == completedCount1 + 1
-                            && getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH) == completedCount2 + 1);
+                    getActivityCount(true, LRA_CONTROLLER_PATH, "completedactivitycount") == completedCount1 + 1
+                            && getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPLETED_CNT_PATH) == completedCount2 + 1);
         } else {
             lraClient.cancelLRA(lra);
 
             assertTrue("joinWithTwoResourcesWithCancel: both resources should have compensated",
-                    getActivityCount(LRA_CONTROLLER_PATH, "compensatedactivitycount") == compensatedCount1 +1
-                            && getActivityCount(TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH) == compensatedCount2 + 1);
+                    getActivityCount(true, LRA_CONTROLLER_PATH, "compensatedactivitycount") == compensatedCount1 +1
+                            && getActivityCount(false, TCK_PARTICIPANT_RESOURCE_PATH, COMPENSATED_CNT_PATH) == compensatedCount2 + 1);
         }
     }
 
-    private int getCompletedCount() {
-        return getActivityCount("completedactivitycount");
+    private int getCompletedCount(boolean delay) {
+        return getActivityCount(delay, "completedactivitycount");
     }
 
-    private int getCompensatedCount() {
-        return getActivityCount("compensatedactivitycount");
+    private int getCompensatedCount(boolean delay) {
+        return getActivityCount(delay, "compensatedactivitycount");
     }
 
-    private int getActivityCount(String activityCountTargetPath) {
-        return getActivityCount(LRA_CONTROLLER_PATH, activityCountTargetPath);
+    private int getActivityCount(boolean delay, String activityCountTargetPath) {
+        return getActivityCount(delay, LRA_CONTROLLER_PATH, activityCountTargetPath);
     }
 
-    private int getActivityCount(String basePath, String activityCountTargetPath) {
+    private int getActivityCount(boolean delay, String basePath, String activityCountTargetPath) {
+        if (delay) {
+            applyConsistencyDelay();
+        }
         WebTarget resourcePath = tckSuiteTarget.path(basePath)
                 .path(activityCountTargetPath);
 
@@ -520,8 +521,8 @@ public class TckTests extends TckTestBase {
     private void multiLevelNestedActivity(CompletionType how, int nestedCnt) throws WebApplicationException {
         WebTarget resourcePath = tckSuiteTarget.path(LRA_CONTROLLER_PATH).path("multiLevelNestedActivity");
 
-        int beforeCompletedCount = getCompletedCount();
-        int beforeCompensatedCount = getCompensatedCount();
+        int beforeCompletedCount = getCompletedCount(false);
+        int beforeCompensatedCount = getCompensatedCount(false);
 
         if (how == CompletionType.mixed && nestedCnt <= 1) {
             how = CompletionType.complete;
@@ -560,8 +561,8 @@ public class TckTests extends TckTestBase {
         assertFalse("multiLevelNestedActivity: top level LRA should be active (path called " + resourcePath.getUri() + ")",
                 lraClient.isLRAFinished(lraArray[0]));
 
-        int inMiddleCompletedCount = getCompletedCount();
-        int inMiddleCompensatedCount = getCompensatedCount();
+        int inMiddleCompletedCount = getCompletedCount(true);
+        int inMiddleCompensatedCount = getCompensatedCount(false);
 
         // check that all nested activities were told to complete
         assertEquals("multiLevelNestedActivity: step 3 (called test path " + resourcePath.getUri() + ")",
@@ -581,7 +582,7 @@ public class TckTests extends TckTestBase {
              * L1 the mandatory call (PUT "activities/multiLevelNestedActivity") registers participant C1
              *   the resource makes nestedCnt calls to "activities/nestedActivity" each of which create nested LRAs
              * L2, L3, ... each of which enlists a participant (C2, C3, ...) which are completed when the call returns
-             * L2 is canceled  which causes C2 to compensate
+             * L2 is cancelled  which causes C2 to compensate
              * L1 is closed which triggers the completion of C1
              *
              * To summarise:
@@ -596,12 +597,12 @@ public class TckTests extends TckTestBase {
 
         // validate that the top level and nested LRAs are gone
         IntStream.rangeClosed(0, nestedCnt).forEach(i -> assertTrue(
-                String.format("multiLevelNestedActivity: %s LRA still active (resouce path was %s)",
+                String.format("multiLevelNestedActivity: %s LRA still active (resource path was %s)",
                         (i == 0 ? "top level" : "nested"), resourcePath.getUri()),
                 lraClient.isLRAFinished(lraArray[i])));
 
-        int afterCompletedCount = getCompletedCount();
-        int afterCompensatedCount = getCompensatedCount();
+        int afterCompletedCount = getCompletedCount(true);
+        int afterCompensatedCount = getCompensatedCount(false);
 
         if (how == CompletionType.complete) {
             // make sure that all nested activities were not told to complete or cancel a second time
@@ -616,13 +617,13 @@ public class TckTests extends TckTestBase {
              * the test starts LRA1 calls a @Mandatory method multiLevelNestedActivity which enlists in LRA1
              * multiLevelNestedActivity then calls an @Nested method which starts L2 and enlists another participant
              *   when the method returns the nested participant is completed (ie completed count is incremented)
-             * Canceling L1 should then compensate the L1 enlistement (ie compensate count is incrememted)
-             * which will then tell L2 to compenstate (ie the compensate count is incrememted again)
+             * Canceling L1 should then compensate the L1 enlistment (ie compensate count is incremented)
+             * which will then tell L2 to compensate (ie the compensate count is incremented again)
              */
             // each nested participant should have completed (the +nestedCnt)
             assertEquals("multiLevelNestedActivity: step 7 (called test path " + resourcePath.getUri() + ")",
                     beforeCompletedCount + nestedCnt, afterCompletedCount);
-            // each nested participant should have compensated. The top level enlistement should have compensated (the +1)
+            // each nested participant should have compensated. The top level enlistment should have compensated (the +1)
             assertEquals("multiLevelNestedActivity: step 8 (called test path " + resourcePath.getUri() + ")",
                     inMiddleCompensatedCount + 1 + nestedCnt, afterCompensatedCount);
         } else {