Repository: brooklyn-server
Updated Branches:
  refs/heads/master f22da254e -> adfa95d71


Fix out of order events triggered by ComputeServiceState

ComputeServiceState.onEvent should only be called in the event handler executor 
to keep the ordering of events.
Before the change what would happen is on calling setExpectedState(state) a 
"state.expected" event would be scheduled, with 
ComputeServiceState.onEvent(null) called in the same thread right after. This 
would create a race condition when calling setExpectedState in quick succession 
(onEvent called from the setExpectedState thread and from the event handler 
thread with different values).


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/33858931
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/33858931
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/33858931

Branch: refs/heads/master
Commit: 33858931884831bd76af8b8c209aa6ab47194a79
Parents: 880f842
Author: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com>
Authored: Tue Nov 22 12:18:54 2016 +0200
Committer: Svetoslav Neykov <svetoslav.ney...@cloudsoftcorp.com>
Committed: Tue Nov 22 12:22:08 2016 +0200

----------------------------------------------------------------------
 .../entity/lifecycle/ServiceStateLogic.java     | 24 ++++++------
 .../entity/ApplicationLifecycleStateTest.java   |  6 ---
 .../entity/lifecycle/ServiceStateLogicTest.java | 39 ++++++++++++++++++++
 3 files changed, 52 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/33858931/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
 
b/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
index df45d51..0839262 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogic.java
@@ -23,6 +23,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.annotation.Nullable;
 
@@ -45,7 +46,6 @@ import org.apache.brooklyn.core.config.ConfigKeys;
 import org.apache.brooklyn.core.enricher.AbstractEnricher;
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.Entities;
-import org.apache.brooklyn.core.entity.EntityAdjuncts;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.entity.EntityPredicates;
 import org.apache.brooklyn.core.entity.lifecycle.Lifecycle.Transition;
@@ -72,6 +72,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.reflect.TypeToken;
 
@@ -154,11 +155,6 @@ public class ServiceStateLogic {
     public static void setExpectedState(Entity entity, Lifecycle state) {
         waitBrieflyForServiceUpIfStateIsRunning(entity, state);
         
((EntityInternal)entity).sensors().set(Attributes.SERVICE_STATE_EXPECTED, new 
Lifecycle.Transition(state, new Date()));
-        
-        Maybe<Enricher> enricher = 
EntityAdjuncts.tryFindWithUniqueTag(entity.enrichers(), 
ComputeServiceState.DEFAULT_ENRICHER_UNIQUE_TAG);
-        if (enricher.isPresent() && enricher.get() instanceof 
ComputeServiceState) {
-            ((ComputeServiceState)enricher.get()).onEvent(null);
-        }
     }
     
     public static Lifecycle getExpectedState(Entity entity) {
@@ -258,9 +254,11 @@ public class ServiceStateLogic {
      * {@link ServiceStateLogic#newEnricherForServiceState(Class)} and added 
to an entity.
      */
     public static class ComputeServiceState extends AbstractEnricher 
implements SensorEventListener<Object> {
-        
+        private static final Logger log = 
LoggerFactory.getLogger(ComputeServiceState.class);
         public static final String DEFAULT_ENRICHER_UNIQUE_TAG = 
"service.state.actual";
 
+        private final AtomicInteger warnCounter = new AtomicInteger();
+
         public ComputeServiceState() {}
         public ComputeServiceState(Map<?,?> flags) { super(flags); }
             
@@ -278,14 +276,18 @@ public class ServiceStateLogic {
                 suppressDuplicates = true;
             }
             
-            subscriptions().subscribe(entity, SERVICE_PROBLEMS, this);
-            subscriptions().subscribe(entity, SERVICE_UP, this);
-            subscriptions().subscribe(entity, SERVICE_STATE_EXPECTED, this);
-            onEvent(null);
+            Map<String, ?> notifyOfInitialValue = 
ImmutableMap.of("notifyOfInitialValue", Boolean.TRUE);
+            subscriptions().subscribe(notifyOfInitialValue, entity, 
SERVICE_PROBLEMS, this);
+            subscriptions().subscribe(notifyOfInitialValue, entity, 
SERVICE_UP, this);
+            subscriptions().subscribe(notifyOfInitialValue, entity, 
SERVICE_STATE_EXPECTED, this);
         }
 
         @Override
         public void onEvent(@Nullable SensorEvent<Object> event) {
+            if (event == null && warnCounter.getAndIncrement() % 1000 == 0) {
+                log.warn("Deprecated since 0.10.0. Calling 
ServiceStateLogic.onEvent explicitly is deprecated to guarantee event 
ordering.");
+            }
+//            Preconditions.checkNotNull(event, "Calling onEvent explicitly no 
longer supported. Can only be called as an event handler to guarantee 
ordering.");
             Preconditions.checkNotNull(entity, "Cannot handle subscriptions or 
compute state until associated with an entity");
             
             Map<String, Object> serviceProblems = 
entity.getAttribute(SERVICE_PROBLEMS);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/33858931/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
index 3499f13..70057d4 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/entity/ApplicationLifecycleStateTest.java
@@ -134,12 +134,6 @@ public class ApplicationLifecycleStateTest extends 
BrooklynMgmtUnitTestSupport {
         assertHealthEventually(app, Lifecycle.ON_FIRE, false);
     }
 
-    // TODO Fails in a full `mvn clean install`, but I can't get it to fail in 
Eclipse running 
-    // lots of times, or with `mvn test -Dtest=ApplicationLifecycleStateTest`. 
The failure is:
-    //     java.lang.AssertionError: (Dumped entity info - see log); 
entity=Application[6q37l8cu]; state=on-fire; up=true; notUpIndicators={}; 
serviceProblems={service-lifecycle-indicators-from-children-and-members=Required
 entity not healthy: FailingEntityImpl{id=exz9n1pti0}}
-    //     at 
org.apache.brooklyn.core.entity.ApplicationLifecycleStateTest.assertUpAndRunningEventually(ApplicationLifecycleStateTest.java:204)
-    //     at 
org.apache.brooklyn.core.entity.ApplicationLifecycleStateTest.testChildFailuresOnStartButWithQuorumCausesAppToSucceed(ApplicationLifecycleStateTest.java:146)
-    @Test(groups="Broken")
     public void testChildFailuresOnStartButWithQuorumCausesAppToSucceed() 
throws Exception {
         TestApplication app = 
mgmt.getEntityManager().createEntity(EntitySpec.create(TestApplication.class)
                 .configure(StartableApplication.UP_QUORUM_CHECK, 
QuorumCheck.QuorumChecks.atLeastOne())

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/33858931/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java
 
b/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java
index 0932fce..61fd147 100644
--- 
a/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/core/entity/lifecycle/ServiceStateLogicTest.java
@@ -18,17 +18,26 @@
  */
 package org.apache.brooklyn.core.entity.lifecycle;
 
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Enricher;
+import org.apache.brooklyn.api.sensor.EnricherSpec;
+import org.apache.brooklyn.api.sensor.SensorEvent;
 import org.apache.brooklyn.core.entity.Attributes;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityAdjuncts;
 import org.apache.brooklyn.core.entity.EntityAsserts;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import 
org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembers;
+import 
org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceIndicatorsFromChildrenAndMembersSpec;
+import 
org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ComputeServiceState;
 import 
org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic;
 import 
org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceProblemsLogic;
 import org.apache.brooklyn.core.sensor.Sensors;
@@ -36,6 +45,7 @@ import 
org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport;
 import org.apache.brooklyn.core.test.entity.TestEntity;
 import 
org.apache.brooklyn.core.test.entity.TestEntityImpl.TestEntityWithoutEnrichers;
 import org.apache.brooklyn.entity.group.DynamicCluster;
+import org.apache.brooklyn.util.collections.QuorumCheck;
 import org.apache.brooklyn.util.collections.QuorumCheck.QuorumChecks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.time.Duration;
@@ -281,6 +291,35 @@ public class ServiceStateLogicTest extends 
BrooklynAppUnitTestSupport {
         EntityAsserts.assertAttributeEqualsContinually(cluster, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
     }
 
+    public static class CountingComputeServiceState extends 
ComputeServiceState {
+        AtomicInteger cntCalled = new AtomicInteger();
+        AtomicInteger cntCalledWithNull = new AtomicInteger();
+
+        public CountingComputeServiceState() {}
+
+        @Override
+        public void onEvent(SensorEvent<Object> event) {
+            cntCalled.incrementAndGet();
+            if (event == null) {
+                cntCalledWithNull.incrementAndGet();
+            }
+            super.onEvent(event);
+        }
+    }
+
+    @Test
+    public void testServiceStateNotCalledExplicitly() throws Exception {
+        EnricherSpec<CountingComputeServiceState> enricherSpec = 
EnricherSpec.create(CountingComputeServiceState.class);
+        CountingComputeServiceState enricher = 
mgmt.getEntityManager().createEnricher(enricherSpec);
+        app.enrichers().add(enricher);
+
+        ServiceStateLogic.setExpectedState(entity, Lifecycle.RUNNING);
+        assertAttributeEqualsEventually(entity, 
Attributes.SERVICE_STATE_ACTUAL, Lifecycle.ON_FIRE);
+
+        assertTrue(enricher.cntCalled.get() > 0);
+        assertEquals(enricher.cntCalledWithNull.get(), 0);
+    }
+
     private static <T> void assertAttributeEqualsEventually(Entity x, 
AttributeSensor<T> sensor, T value) {
         try {
             
EntityAsserts.assertAttributeEqualsEventually(ImmutableMap.of("timeout", 
Duration.seconds(3)), x, sensor, value);

Reply via email to