You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2021/09/11 00:27:00 UTC

[brooklyn-server] branch master updated (5309315 -> c1991d1)

This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git.


    from 5309315  make some other common tasks logged at trace
     new 6b6f243  make more methods available in RO mode
     new 6232b64  add method to check if entity is managed by a primary (not readonly)
     new 4824712  add null check for better behaviour in edge case
     new 5a072bb  more guards against activity running on read-only entities
     new 1de22e4  catch problems when recording location usage, prevent mgmt node state changes from failing in this case
     new c1991d1  lots more exclusions for read-only mode, and ability to interrogate them

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../brooklyn/api/mgmt/rebind/RebindManager.java    |  4 +-
 .../catalog/internal/BasicBrooklynCatalog.java     |  9 ++-
 .../brooklyn/core/entity/AbstractEntity.java       | 21 ++++---
 .../org/apache/brooklyn/core/entity/Entities.java  | 22 +++++--
 .../internal/EntityTransientCopyInternal.java      | 10 ++++
 .../core/entity/lifecycle/ServiceStateLogic.java   |  6 +-
 .../core/entity/trait/StartableMethods.java        |  2 +-
 .../java/org/apache/brooklyn/core/feed/Poller.java |  2 +-
 .../brooklyn/core/mgmt/BrooklynTaskTags.java       |  1 +
 .../mgmt/internal/AbstractSubscriptionManager.java | 10 ++++
 .../mgmt/internal/LocalSubscriptionManager.java    |  5 ++
 .../core/mgmt/internal/LocalUsageManager.java      | 20 ++++++-
 .../internal/NonDeploymentManagementContext.java   |  7 ++-
 .../BrooklynMementoPersisterToObjectStore.java     | 30 ++++++----
 .../mgmt/rebind/PeriodicDeltaChangeListener.java   | 14 +++--
 .../brooklyn/core/mgmt/rebind/RebindIteration.java | 69 +++++++++++++++++++++-
 .../core/mgmt/rebind/RebindManagerImpl.java        | 12 +++-
 .../brooklyn/core/objs/AbstractEntityAdjunct.java  |  3 +-
 .../brooklyn/entity/group/DynamicClusterImpl.java  |  2 +-
 .../entity/stock/AsyncApplicationImpl.java         |  2 +-
 .../util/core/task/BasicExecutionManager.java      | 10 ++++
 .../brooklyn/core/mgmt/ha/HotStandbyTest.java      | 61 +++++++++++++------
 .../policy/autoscaling/AutoScalerPolicy.java       | 12 ++--
 .../policy/failover/ElectPrimaryPolicy.java        |  5 ++
 .../base/AbstractSoftwareProcessSshDriver.java     |  4 +-
 .../base/AbstractSoftwareProcessWinRmDriver.java   |  4 +-
 .../java/org/apache/brooklyn/test/Asserts.java     |  9 ++-
 27 files changed, 275 insertions(+), 81 deletions(-)

[brooklyn-server] 05/06: catch problems when recording location usage, prevent mgmt node state changes from failing in this case

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 1de22e4409c441f59bfd52991666df98a58716dd
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Sep 10 13:32:15 2021 +0100

    catch problems when recording location usage, prevent mgmt node state changes from failing in this case
---
 .../core/mgmt/internal/LocalUsageManager.java        | 20 +++++++++++++++++---
 1 file changed, 17 insertions(+), 3 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
index 41c5dc4..c4d6deb 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalUsageManager.java
@@ -288,8 +288,16 @@ public class LocalUsageManager implements UsageManager {
         // but no strong enough feelings yet...
         
         checkNotNull(loc, "location");
-        if (loc.getConfig(AbstractLocation.TEMPORARY_LOCATION)) {
-            log.info("Ignoring location lifecycle usage event for {} (state {}), because location is a temporary location", loc, state);
+        Boolean tempLocation = null;
+        try {
+            tempLocation = loc.getConfig(AbstractLocation.TEMPORARY_LOCATION);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.trace("Unable to get temporary location for {}", loc);
+        }
+
+        if (Boolean.TRUE.equals(tempLocation)) {
+            log.info("Ignoring location lifecycle usage event for {} (state {}), because location is a temporary location {}", loc, state, tempLocation);
             return;
         }
         checkNotNull(state, "state of location %s", loc);
@@ -302,7 +310,13 @@ public class LocalUsageManager implements UsageManager {
             return;
         }
         
-        Object callerContext = loc.getConfig(LocationConfigKeys.CALLER_CONTEXT);
+        Object callerContext = null;
+        try {
+            callerContext = loc.getConfig(LocationConfigKeys.CALLER_CONTEXT);
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            log.trace("Unable to get caller context for {}", loc);
+        }
         
         if (callerContext != null && callerContext instanceof Entity) {
             Entity caller = (Entity) callerContext;

[brooklyn-server] 01/06: make more methods available in RO mode

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 6b6f24373e86a873b03f4aeea6bf9bff05b323fa
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Sep 10 12:44:56 2021 +0100

    make more methods available in RO mode
---
 .../core/entity/internal/EntityTransientCopyInternal.java      | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityTransientCopyInternal.java b/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityTransientCopyInternal.java
index 60ac288..9106ec2 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityTransientCopyInternal.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/internal/EntityTransientCopyInternal.java
@@ -25,14 +25,19 @@ import javax.annotation.Nullable;
 import org.apache.brooklyn.api.effector.Effector;
 import org.apache.brooklyn.api.entity.Application;
 import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.api.entity.Entity.EnricherSupport;
 import org.apache.brooklyn.api.entity.Entity.GroupSupport;
+import org.apache.brooklyn.api.entity.Entity.PolicySupport;
+import org.apache.brooklyn.api.entity.Entity.SensorSupport;
 import org.apache.brooklyn.api.entity.EntityType;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.ManagementContext;
 import org.apache.brooklyn.api.mgmt.rebind.RebindSupport;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.EntityMemento;
+import org.apache.brooklyn.api.objs.BrooklynObject.RelationSupport;
 import org.apache.brooklyn.api.objs.BrooklynObject.TagSupport;
+import org.apache.brooklyn.api.objs.Configurable.ConfigurationSupport;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.config.ConfigKey;
 import org.apache.brooklyn.config.ConfigKey.HasConfigKey;
@@ -71,8 +76,13 @@ public interface EntityTransientCopyInternal {
     <T> T getAttribute(AttributeSensor<T> sensor);
     <T> T getConfig(ConfigKey<T> key);
     <T> T getConfig(HasConfigKey<T> key);
+    ConfigurationSupport config();
+    SensorSupport sensors();
+    PolicySupport policies();
+    EnricherSupport enrichers();
     TagSupport tags();
     GroupSupport groups();
+    RelationSupport<Entity> relations();
     String getCatalogItemId();
 
     // from EntityInternal:

[brooklyn-server] 02/06: add method to check if entity is managed by a primary (not readonly)

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 6232b64f99528619ec8108bfec1cfed171155803
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Sep 10 12:46:11 2021 +0100

    add method to check if entity is managed by a primary (not readonly)
---
 .../java/org/apache/brooklyn/core/entity/Entities.java | 18 ++++++++++++++----
 .../apache/brooklyn/core/mgmt/ha/HotStandbyTest.java   | 15 +++++++++++++++
 2 files changed, 29 insertions(+), 4 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
index cd28df8..4bb5835 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
@@ -790,7 +790,18 @@ public class Entities {
         }
     }
 
+    /** As {@link #isManagedOrReadOnly(Entity)}.  Consider using {@link #isManagedActive(Entity)} instead. */
     public static boolean isManaged(Entity e) {
+        return isManagedOrReadOnly(e);
+    }
+
+    /** If entity is under management and the management node is the primary for this entity, i.e. not read-only. */
+    public static boolean isManagedActive(Entity e) {
+        return ((EntityInternal)e).getManagementSupport().isDeployed() && ((EntityInternal)e).getManagementContext().isRunning() && !isReadOnly(e);
+    }
+
+    /** If entity is under management either as primary or in read-only (hot-standby) state. */
+    public static boolean isManagedOrReadOnly(Entity e) {
         return ((EntityInternal)e).getManagementSupport().isDeployed() && ((EntityInternal)e).getManagementContext().isRunning();
     }
 
@@ -798,10 +809,9 @@ public class Entities {
         return ((EntityInternal)e).getManagementSupport().isNoLongerManaged();
     }
 
-    /** as {@link EntityManagerInternal#isReadOnly(Entity)} */
-    @Beta
-    public static Boolean isReadOnly(Entity e) {
-        return ((EntityInternal)e).getManagementSupport().isReadOnly();
+    /** if entity is managed, but in a read-only state */
+    public static boolean isReadOnly(Entity e) {
+        return Boolean.TRUE.equals( ((EntityInternal)e).getManagementSupport().isReadOnlyRaw() );
     }
 
     /** Unwraps a proxy to retrieve the real item, if available.
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java
index ab0e94f..f3bd7fc 100644
--- a/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.core.mgmt.ha;
 
+import java.util.Collection;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
@@ -663,5 +664,19 @@ public class HotStandbyTest {
         RebindTestFixture.waitForTaskCountToBecome(hsb.mgmt, 5);
     }
 
+    @Test(groups="Integration")   // could be promoted to non-integration test if we can guarantee app is present
+    public void testHotStandbyEntityIsManagedFalse() throws Exception {
+        HaMgmtNode n1 = createMaster(Duration.PRACTICALLY_FOREVER);
+        TestApplication app = createFirstAppAndPersist(n1);
+        forcePersistNow(n1);
+        Assert.assertTrue(Entities.isManaged(app));
+
+        final HaMgmtNode hsb = createHotStandby(Duration.millis(10));
+        Collection<Application> apps = hsb.mgmt.getApplications();
+        Asserts.assertSize(apps, 1);
+        Assert.assertFalse(Entities.isManagedActive(apps.iterator().next()));
+        Assert.assertTrue(Entities.isManaged(apps.iterator().next()));
+    }
+
 
 }

[brooklyn-server] 03/06: add null check for better behaviour in edge case

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 4824712c9fd88414fc59a87f028161930080602f
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Sep 10 12:46:38 2021 +0100

    add null check for better behaviour in edge case
---
 .../BrooklynMementoPersisterToObjectStore.java     | 30 ++++++++++++----------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
index 80ada41..3ae3bc9 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/persist/BrooklynMementoPersisterToObjectStore.java
@@ -336,21 +336,25 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                     Exceptions.propagateIfFatal(e);
                     exceptionHandler.onLoadMementoFailed(type, "memento "+id+" read error", e);
                 }
-                
-                String xmlId = (String) XmlUtil.xpathHandlingIllegalChars(contents, "/"+type.toCamelCase()+"/id");
-                String safeXmlId = Strings.makeValidFilename(xmlId);
-                if (!Objects.equal(id, safeXmlId))
-                    LOG.warn("ID mismatch on "+type.toCamelCase()+", "+id+" from path, "+safeXmlId+" from xml");
-                
-                if (type == BrooklynObjectType.MANAGED_BUNDLE) {
-                    // TODO could R/W to cache space directly, rather than memory copy then extra file copy
-                    byte[] jarData = readBytes(contentsSubpath+".jar");
-                    if (jarData==null) {
-                        throw new IllegalStateException("No bundle data for "+contentsSubpath);
+                if (contents==null) {
+                    LOG.warn("No contents for "+contentsSubpath+" in persistence store; ignoring");
+
+                } else {
+                    String xmlId = (String) XmlUtil.xpathHandlingIllegalChars(contents, "/" + type.toCamelCase() + "/id");
+                    String safeXmlId = Strings.makeValidFilename(xmlId);
+                    if (!Objects.equal(id, safeXmlId))
+                        LOG.warn("ID mismatch on " + type.toCamelCase() + ", " + id + " from path, " + safeXmlId + " from xml");
+
+                    if (type == BrooklynObjectType.MANAGED_BUNDLE) {
+                        // TODO could R/W to cache space directly, rather than memory copy then extra file copy
+                        byte[] jarData = readBytes(contentsSubpath + ".jar");
+                        if (jarData == null) {
+                            throw new IllegalStateException("No bundle data for " + contentsSubpath);
+                        }
+                        builder.bundleJar(id, ByteSource.wrap(jarData));
                     }
-                    builder.bundleJar(id, ByteSource.wrap(jarData));
+                    builder.put(type, xmlId, contents);
                 }
-                builder.put(type, xmlId, contents);
             }
         };
 

[brooklyn-server] 06/06: lots more exclusions for read-only mode, and ability to interrogate them

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit c1991d1c440f578305c053dad3ce2f53b518d761
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Sep 10 17:52:37 2021 +0100

    lots more exclusions for read-only mode, and ability to interrogate them
    
    - no subscriptions
    - not added to persistence queue
---
 .../brooklyn/api/mgmt/rebind/RebindManager.java    |  4 +-
 .../catalog/internal/BasicBrooklynCatalog.java     |  9 ++-
 .../brooklyn/core/entity/AbstractEntity.java       | 21 ++++---
 .../org/apache/brooklyn/core/entity/Entities.java  |  4 +-
 .../brooklyn/core/mgmt/BrooklynTaskTags.java       |  1 +
 .../mgmt/internal/AbstractSubscriptionManager.java | 10 ++++
 .../mgmt/internal/LocalSubscriptionManager.java    |  5 ++
 .../internal/NonDeploymentManagementContext.java   |  7 ++-
 .../mgmt/rebind/PeriodicDeltaChangeListener.java   | 14 +++--
 .../brooklyn/core/mgmt/rebind/RebindIteration.java | 69 +++++++++++++++++++++-
 .../core/mgmt/rebind/RebindManagerImpl.java        | 12 +++-
 .../brooklyn/core/objs/AbstractEntityAdjunct.java  |  3 +-
 .../util/core/task/BasicExecutionManager.java      | 10 ++++
 .../brooklyn/core/mgmt/ha/HotStandbyTest.java      | 46 +++++++++------
 .../policy/failover/ElectPrimaryPolicy.java        |  4 +-
 .../java/org/apache/brooklyn/test/Asserts.java     |  9 ++-
 16 files changed, 182 insertions(+), 46 deletions(-)

diff --git a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/RebindManager.java b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/RebindManager.java
index 7ad3191..d9d4422 100644
--- a/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/RebindManager.java
+++ b/api/src/main/java/org/apache/brooklyn/api/mgmt/rebind/RebindManager.java
@@ -97,6 +97,8 @@ public interface RebindManager {
      * Interrupts any current activity and waits for it to cease. */
     public void stopReadOnly();
 
+    public boolean isReadOnly();
+
     /**
      * Resets the effects of previously being read-only, ready to be used again (e.g. when promoting to master).
      * Expected to be called after {@link #stopReadOnly()} (thus long after {@link #setPersister(BrooklynMementoPersister)}, 
@@ -105,7 +107,7 @@ public interface RebindManager {
     public void reset();
 
     /** Starts the appropriate background processes, {@link #startPersistence()} if {@link ManagementNodeState#MASTER},
-     * {@link #startReadOnly()} if {@link ManagementNodeState#HOT_STANDBY} or {@link ManagementNodeState#HOT_BACKUP} */
+     * {@link #startReadOnly(ManagementNodeState)} if {@link ManagementNodeState#HOT_STANDBY} or {@link ManagementNodeState#HOT_BACKUP} */
     public void start();
     /** Stops the appropriate background processes, {@link #stopPersistence()} or {@link #stopReadOnly()},
      * waiting for activity there to cease (interrupting in the case of {@link #stopReadOnly()}). */
diff --git a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
index da334d0..71098f3 100644
--- a/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
+++ b/core/src/main/java/org/apache/brooklyn/core/catalog/internal/BasicBrooklynCatalog.java
@@ -142,7 +142,9 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
     private volatile CatalogDo manualAdditionsCatalog;
     private volatile LoadedClassLoader manualAdditionsClasses;
     private final AggregateClassLoader rootClassLoader = AggregateClassLoader.newInstanceWithNoLoaders();
-    
+
+    private static boolean WARNED_RE_DSL_PARSER = false;
+
     /**
      * Cache of specs (used by {@link #peekSpec(CatalogItem)}).
      * We assume that no-one is modifying the catalog items (once added) without going through the
@@ -649,7 +651,10 @@ public class BasicBrooklynCatalog implements BrooklynCatalog {
             }
             
         } else {
-            log.info("No Camp-YAML parser registered for parsing catalog item DSL; skipping DSL-parsing");
+            if (!WARNED_RE_DSL_PARSER) {
+                log.warn("No Camp-YAML parser registered for parsing catalog item DSL; skipping DSL-parsing (no further warnings)");
+                WARNED_RE_DSL_PARSER = true;
+            }
         }
 
         Map<Object,Object> catalogMetadata = MutableMap.<Object, Object>builder()
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
index 515ed81..7c9607e 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/AbstractEntity.java
@@ -966,7 +966,7 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
             if (LOG.isTraceEnabled())
                 LOG.trace(""+AbstractEntity.this+" setAttribute "+attribute+" "+val);
             
-            if (Boolean.TRUE.equals(getManagementSupport().isReadOnlyRaw())) {
+            if (Entities.isReadOnly(AbstractEntity.this)) {
                 T oldVal = getAttribute(attribute);
                 if (Equals.approximately(val, oldVal)) {
                     // ignore, probably an enricher resetting values or something on init
@@ -984,13 +984,18 @@ public abstract class AbstractEntity extends AbstractBrooklynObject implements E
                 }
             }
             T result = attributesInternal.update(attribute, val);
-            if (result == null) {
-                // could be this is a new sensor
-                entityType.addSensorIfAbsent(attribute);
-            }
-            
-            if (!Objects.equal(result, val)) {
-                getManagementSupport().getEntityChangeListener().onAttributeChanged(attribute);
+
+            if (!Entities.isReadOnly(AbstractEntity.this)) {
+                // suppress notifications if read only
+
+                if (result == null) {
+                    // could be this is a new sensor
+                    entityType.addSensorIfAbsent(attribute);
+                }
+
+                if (!Objects.equal(result, val)) {
+                    getManagementSupport().getEntityChangeListener().onAttributeChanged(attribute);
+                }
             }
             
             return result;
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
index 4bb5835..43a5104 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/Entities.java
@@ -18,7 +18,9 @@
  */
 package org.apache.brooklyn.core.entity;
 
+import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.core.mgmt.BrooklynTags;
+import org.apache.brooklyn.core.objs.BrooklynObjectInternal;
 import static org.apache.brooklyn.util.guava.Functionals.isSatisfied;
 
 import java.io.Closeable;
@@ -241,7 +243,7 @@ public class Entities {
     }
 
     /**
-     * @deprecated since 0.7; instead use {@link Sanitizer#IS_SECRET_PREDICATE.apply(Object)}
+     * @deprecated since 0.7; instead use {@link Sanitizer#IS_SECRET_PREDICATE}
      */
     @Deprecated
     public static boolean isSecret(String name) {
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTaskTags.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTaskTags.java
index 2bbd2c8..ea2dd84 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTaskTags.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/BrooklynTaskTags.java
@@ -39,6 +39,7 @@ import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.mgmt.entitlement.EntitlementContext;
 import org.apache.brooklyn.api.objs.EntityAdjunct;
 import org.apache.brooklyn.core.config.Sanitizer;
+import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.mgmt.internal.AbstractManagementContext;
 import org.apache.brooklyn.core.objs.AbstractEntityAdjunct;
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractSubscriptionManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractSubscriptionManager.java
index e7ae59e..eda80c3 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractSubscriptionManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/AbstractSubscriptionManager.java
@@ -30,6 +30,7 @@ import org.apache.brooklyn.api.mgmt.SubscriptionManager;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.api.sensor.SensorEvent;
 import org.apache.brooklyn.api.sensor.SensorEventListener;
+import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -115,6 +116,11 @@ public abstract class AbstractSubscriptionManager implements SubscriptionManager
     /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
     @Override
     public final  <T> SubscriptionHandle subscribeToChildren(Map<String, Object> flags, final Entity parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        if (parent!=null && Entities.isReadOnly(parent)) {
+            LOG.trace("Skipping subscription in read only mode, children of {} {} {}", parent, sensor, flags);
+            return new Subscription<>(null, null, null);
+        }
+
         Predicate<SensorEvent<T>> eventFilter = new Predicate<SensorEvent<T>>() {
             @Override
             public boolean apply(SensorEvent<T> input) {
@@ -134,6 +140,10 @@ public abstract class AbstractSubscriptionManager implements SubscriptionManager
     /** @see SubscriptionManager#subscribe(Map, Entity, Sensor, SensorEventListener) */
     @Override
     public final  <T> SubscriptionHandle subscribeToMembers(Map<String, Object> flags, final Group parent, Sensor<T> sensor, SensorEventListener<? super T> listener) {
+        if (parent!=null && Entities.isReadOnly(parent)) {
+            LOG.trace("Skipping subscription in read only mode, members of {} {} {}", parent, sensor, flags);
+            return new Subscription<>(null, null, null);
+        }
         Predicate<SensorEvent<T>> eventFilter = new Predicate<SensorEvent<T>>() {
             @Override
             public boolean apply(SensorEvent<T> input) {
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalSubscriptionManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalSubscriptionManager.java
index ab82e47..9b2cd79 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalSubscriptionManager.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/LocalSubscriptionManager.java
@@ -117,6 +117,11 @@ public class LocalSubscriptionManager extends AbstractSubscriptionManager {
     @SuppressWarnings("unchecked")
     protected synchronized <T> SubscriptionHandle subscribe(Map<String, Object> flags, final Subscription<T> s) {
         Entity producer = s.producer;
+        if (producer!=null && Entities.isReadOnly(producer)) {
+            LOG.trace("Skipping subscription in read only mode {} {}", s, flags);
+            return s;
+        }
+
         Sensor<T> sensor= s.sensor;
         s.subscriber = getSubscriber(flags, s);
         s.subscriptionDescription = getSubscriptionDescription(flags, s);
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
index 9653934..5613e91 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/internal/NonDeploymentManagementContext.java
@@ -591,7 +591,12 @@ public class NonDeploymentManagementContext implements ManagementContextInternal
         public void startReadOnly(ManagementNodeState state) {
             throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
         }
-        
+
+        @Override
+        public boolean isReadOnly() {
+            return true;
+        }
+
         @Override
         public void stopReadOnly() {
             throw new IllegalStateException("Non-deployment context "+NonDeploymentManagementContext.this+" is not valid for this operation.");
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
index cf8680c..f9b9be4 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/PeriodicDeltaChangeListener.java
@@ -35,6 +35,7 @@ import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.api.mgmt.rebind.ChangeListener;
 import org.apache.brooklyn.api.mgmt.rebind.PersistenceExceptionHandler;
+import org.apache.brooklyn.api.mgmt.rebind.RebindManager;
 import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister;
 import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.objs.BrooklynObjectType;
@@ -180,6 +181,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
     }
     
     private final ExecutionContext executionContext;
+    private final RebindManager rebindManager;
     
     private final BrooklynMementoPersister persister;
 
@@ -198,7 +200,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
     private final boolean persistEnrichersEnabled;
     private final boolean persistFeedsEnabled;
     private final boolean rePersistReferencedObjectsEnabled;
-    
+
     private final Semaphore persistingMutex = new Semaphore(1);
     private final Object startStopMutex = new Object();
     private final AtomicInteger writeCount = new AtomicInteger(0);
@@ -210,6 +212,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
 
     public PeriodicDeltaChangeListener(
             Supplier<String> planeIdSupplier,
+            RebindManager rebindManager,
             ExecutionContext executionContext,
             BrooklynMementoPersister persister,
             PersistenceExceptionHandler exceptionHandler,
@@ -218,6 +221,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
         BasicExecutionManager.registerUninterestingTaskName(TASK_NAME,true);
         this.planeIdSupplier = planeIdSupplier;
         this.executionContext = executionContext;
+        this.rebindManager = rebindManager;
         this.persister = persister;
         this.exceptionHandler = exceptionHandler;
         this.metrics = metrics;
@@ -560,8 +564,10 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
     @Override
     public synchronized void onManaged(BrooklynObject instance) {
         if (LOG.isTraceEnabled()) LOG.trace("onManaged: {}", instance);
-        onChanged(instance);
-        addReferencedObjectsForInitialPersist(instance);
+        if (!rebindManager.isReadOnly()) {
+            onChanged(instance);
+            addReferencedObjectsForInitialPersist(instance);
+        }
     }
 
     private void addReferencedObjectsForInitialPersist(BrooklynObject instance) {
@@ -612,7 +618,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
     @Override
     public synchronized void onChanged(BrooklynObject instance) {
         if (LOG.isTraceEnabled()) LOG.trace("onChanged: {}", instance);
-        if (!isStopped()) {
+        if (!isStopped() && !rebindManager.isReadOnly()) {
             deltaCollector.add(instance);
         }
     }
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
index 46ae2f8..38f4af7 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindIteration.java
@@ -20,6 +20,8 @@ package org.apache.brooklyn.core.mgmt.rebind;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
+import org.apache.brooklyn.api.mgmt.Task;
 import static org.apache.brooklyn.core.BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND;
 import static org.apache.brooklyn.core.BrooklynFeatureEnablement.FEATURE_BACKWARDS_COMPATIBILITY_INFER_CATALOG_ITEM_ON_REBIND;
 import static org.apache.brooklyn.core.catalog.internal.CatalogUtils.newClassLoadingContextForCatalogItems;
@@ -83,6 +85,7 @@ import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.feed.AbstractFeed;
 import org.apache.brooklyn.core.location.AbstractLocation;
 import org.apache.brooklyn.core.location.internal.LocationInternal;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.core.mgmt.classloading.BrooklynClassLoadingContextSequential;
 import org.apache.brooklyn.core.mgmt.classloading.JavaBrooklynClassLoadingContext;
 import org.apache.brooklyn.core.mgmt.ha.OsgiManager;
@@ -111,6 +114,7 @@ import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.ClassLoaderUtils;
 import org.apache.brooklyn.util.core.flags.FlagUtils;
 import org.apache.brooklyn.util.core.task.BasicExecutionContext;
+import org.apache.brooklyn.util.core.task.BasicExecutionManager;
 import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
@@ -118,6 +122,7 @@ import org.apache.brooklyn.util.javalang.Reflections;
 import org.apache.brooklyn.util.osgi.VersionedName;
 import org.apache.brooklyn.util.stream.InputStreamSource;
 import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.time.CountdownTimer;
 import org.apache.brooklyn.util.time.Duration;
 import org.apache.brooklyn.util.time.Time;
 import org.slf4j.Logger;
@@ -256,6 +261,34 @@ public abstract class RebindIteration {
     }
     
     protected void doRun() throws Exception {
+        if (readOnlyRebindCount.get()>1) {
+            // wait for tasks
+            Collection<Task<?>> entityTasks = ((BasicExecutionManager) managementContext.getExecutionManager()).allTasksLive()
+                            .stream().filter(t -> BrooklynTaskTags.getContextEntity(t)!=null).collect(Collectors.toList());
+            List<Task<?>> openTasks;
+            CountdownTimer time = CountdownTimer.newInstanceStarted(Duration.seconds(15));
+            do {
+                openTasks = entityTasks.stream().filter(t -> !t.isDone()).collect(Collectors.toList());
+                if (openTasks.isEmpty()) break;
+                if (time.isExpired()) {
+                    LOG.warn("Aborting "+openTasks.size()+" incomplete task(s) before rebinding again: "+openTasks);
+                    openTasks.forEach(t -> t.cancel(true));
+                }
+                if (time.getDurationElapsed().isShorterThan(Duration.millis(200))) {
+                    LOG.info("Waiting on " + openTasks.size() + " task(s) before rebinding again: " + openTasks);
+                } else {
+                    LOG.debug("Waiting on " + openTasks.size() + " task(s) before rebinding again: " + openTasks);
+                }
+                Time.sleep(Duration.millis(200));
+            } while (true);
+
+            entityTasks.forEach( ((BasicExecutionManager) managementContext.getExecutionManager())::deleteTask );
+
+            List<Task<?>> otherDoneTasks = ((BasicExecutionManager) managementContext.getExecutionManager()).allTasksLive()
+                    .stream().filter(Task::isDone).collect(Collectors.toList());
+            otherDoneTasks.forEach( ((BasicExecutionManager) managementContext.getExecutionManager())::deleteTask );
+        }
+
         loadManifestFiles();
         initPlaneId();
         installBundlesAndRebuildCatalog();
@@ -263,7 +296,7 @@ public abstract class RebindIteration {
         instantiateMementos();
         // adjuncts depend on actual mementos; whereas entity works off special memento manifest, 
         // and location, bundles etc just take type and id
-        instantiateAdjuncts(instantiator); 
+        instantiateAdjuncts(instantiator);
         reconstructEverything();
         associateAdjunctsWithEntities();
         manageTheObjects();
@@ -654,7 +687,6 @@ public abstract class RebindIteration {
     }
 
     protected void associateAdjunctsWithEntities() {
-        
         checkEnteringPhase(7);
 
         logRebindingDebug("RebindManager associating adjuncts to entities");
@@ -684,12 +716,43 @@ public abstract class RebindIteration {
                 ((EntityInternal)entity).getExecutionContext().get(Tasks.<Void>builder()
                         .displayName("rebind-adjuncts-"+entity.getId())
                         .dynamic(false)
-                        .body(body)
+                        .body(new RebindAdjuncts(entityMemento, entity, rebindContext, exceptionHandler))
                         .build());
             }
         }
     }
 
+    protected static class RebindAdjuncts implements Runnable {
+        private EntityMemento entityMemento;
+        private Entity entity;
+        private RebindContextImpl rebindContext;
+        private RebindExceptionHandler exceptionHandler;
+
+        public RebindAdjuncts(EntityMemento entityMemento, Entity entity, RebindContextImpl rebindContext, RebindExceptionHandler exceptionHandler) {
+            this.entityMemento = entityMemento;
+            this.entity = entity;
+            this.rebindContext = rebindContext;
+            this.exceptionHandler = exceptionHandler;
+        }
+
+        @Override
+        public void run() {
+            try {
+                entityMemento.injectTypeClass(entity.getClass());
+                // TODO these call to the entity which in turn sets the entity on the underlying feeds and enrichers;
+                // that is taken as the cue to start, but it should not be. start should be a separate call.
+                ((EntityInternal)entity).getRebindSupport().addPolicies(rebindContext, entityMemento);
+                ((EntityInternal)entity).getRebindSupport().addEnrichers(rebindContext, entityMemento);
+                ((EntityInternal)entity).getRebindSupport().addFeeds(rebindContext, entityMemento);
+
+                entityMemento = null;
+                entity = null;
+            } catch (Exception e) {
+                exceptionHandler.onRebindFailed(BrooklynObjectType.ENTITY, entity, e);
+            }
+        }
+    }
+
     protected void manageTheObjects() {
 
         checkEnteringPhase(8);
diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindManagerImpl.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindManagerImpl.java
index 21363be..3a597ab 100644
--- a/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/rebind/RebindManagerImpl.java
@@ -220,7 +220,12 @@ public class RebindManagerImpl implements RebindManager {
     public boolean isReadOnlyRunning() {
         return readOnlyRunning;
     }
-    
+
+    @Override
+    public boolean isReadOnly() {
+        return isReadOnlyRunning();
+    }
+
     public boolean isReadOnlyStopping() {
         return readOnlyTask!=null && !readOnlyRunning;
     }
@@ -251,6 +256,7 @@ public class RebindManagerImpl implements RebindManager {
         
         this.persistenceRealChangeListener = new PeriodicDeltaChangeListener(
                 new PlaneIdSupplier(),
+                this,
                 managementContext.getServerExecutionContext(),
                 persistenceStoreAccess,
                 exceptionHandler,
@@ -290,7 +296,9 @@ public class RebindManagerImpl implements RebindManager {
         persistenceRunning = true;
         readOnlyRebindCount.set(Integer.MIN_VALUE);
         persistenceStoreAccess.enableWriteAccess();
-        if (persistenceRealChangeListener != null) persistenceRealChangeListener.start();
+        if (persistenceRealChangeListener != null) {
+            persistenceRealChangeListener.start();
+        }
     }
 
     @Override
diff --git a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
index 7d63b0e..2562c46 100644
--- a/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
+++ b/core/src/main/java/org/apache/brooklyn/core/objs/AbstractEntityAdjunct.java
@@ -18,6 +18,7 @@
  */
 package org.apache.brooklyn.core.objs;
 
+import java.util.concurrent.ConcurrentHashMap;
 import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth;
 
 import java.util.Collection;
@@ -111,7 +112,7 @@ public abstract class AbstractEntityAdjunct extends AbstractBrooklynObject imple
     @SetFromFlag(value="uniqueTag")
     protected String uniqueTag;
 
-    private Map<String, HighlightTuple> highlights = new HashMap<>();
+    private Map<String, HighlightTuple> highlights = new ConcurrentHashMap<>();
 
     /** Name of a highlight that indicates the last action taken by this adjunct. */
     public static String HIGHLIGHT_NAME_LAST_ACTION = "lastAction";
diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java b/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
index e59e9ed..29984e2 100644
--- a/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
+++ b/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java
@@ -47,6 +47,7 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
+import java.util.stream.Collectors;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.internal.BrooklynLoggingCategories;
 import org.apache.brooklyn.api.mgmt.ExecutionManager;
@@ -58,6 +59,8 @@ import org.apache.brooklyn.core.BrooklynLogging;
 import org.apache.brooklyn.core.config.Sanitizer;
 import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags.WrappedEntity;
+import org.apache.brooklyn.core.mgmt.BrooklynTaskTags.WrappedStream;
 import org.apache.brooklyn.core.mgmt.entitlement.Entitlements;
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.collections.MutableMap;
@@ -368,6 +371,13 @@ public class BasicExecutionManager implements ExecutionManager {
                 log.warn("Deleting submitted task before completion: "+removed+"; this task will continue to run in the background outwith "+this+", but perhaps it should have been cancelled?");
             }
         }
+        task.getTags().forEach(t -> {
+            // remove tags which might have references to entities etc (help out garbage collector)
+            if (t instanceof TaskInternal) {
+                Set<Object> tagsM = ((TaskInternal) t).getMutableTags();
+                tagsM.removeAll(tagsM.stream().filter(tag -> tag instanceof WrappedStream || tag instanceof WrappedEntity).collect(Collectors.toList()));
+            }
+        });
         return removed != null;
     }
 
diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java
index f3bd7fc..f1e7a41 100644
--- a/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/ha/HotStandbyTest.java
@@ -19,6 +19,7 @@
 package org.apache.brooklyn.core.mgmt.ha;
 
 import java.util.Collection;
+import org.apache.brooklyn.api.sensor.AttributeSensor;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertFalse;
 import static org.testng.Assert.assertTrue;
@@ -232,13 +233,10 @@ public class HotStandbyTest {
         assertEquals(appRO.getConfig(TestEntity.CONF_NAME), "first-app");
         assertEquals(appRO.getAttribute(TestEntity.SEQUENCE), (Integer)3);
 
-        try {
-            ((TestApplication)appRO).sensors().set(TestEntity.SEQUENCE, 4);
-            Assert.fail("Should not have allowed sensor to be set");
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
-        }
-        assertEquals(appRO.getAttribute(TestEntity.SEQUENCE), (Integer)3);
+        setSensorOnReadOnly(appRO, TestEntity.SEQUENCE, 4);
+
+        // it will revert to 3
+        EntityAsserts.assertAttributeEqualsEventually(appRO, TestEntity.SEQUENCE, (Integer)3);
     }
 
     @Test
@@ -410,11 +408,13 @@ public class HotStandbyTest {
 //        Jenkins: java.lang.AssertionError: Too much memory used - 158m > max 154m
 //        Svet local: java.lang.AssertionError: Too much memory used - 16m > max 13m
 //    The test is not deterministic so marking as "Manual", i.e. probably shouldn't be included in automated tests.
+    // But it is a very useful check that hot standby is stable!
     @Test(groups={"Integration", "Broken", "Manual"})
     public void testHotStandbyDoesNotLeakBigObjects() throws Exception {
         log.info("Starting test "+JavaClassNames.niceClassAndMethod());
         final int SIZE = 5;
-        final int SIZE_UP_BOUND = SIZE+2;
+        final int SIZE_UP_BOUND1 = SIZE+2;
+        final int SIZE_UP_BOUND2 = SIZE_UP_BOUND1;
         final int SIZE_DOWN_BOUND = SIZE-1;
         final int GRACE = 2;
         // the XML persistence uses a lot of space, we approx at between 2x and 3c
@@ -426,16 +426,18 @@ public class HotStandbyTest {
         noteUsedMemory("Finished seeding");
         Long initialUsed = memoryAssertions.peekLastUsedMemory();
         app.config().set(TestEntity.CONF_OBJECT, new BigObject(SIZE*1000*1000));
-        assertUsedMemoryMaxDelta("Set a big config object", SIZE_UP_BOUND);
+        assertUsedMemoryMaxDelta("Set a big config object", SIZE_UP_BOUND1);
         forcePersistNow(n1);
         assertUsedMemoryMaxDelta("Persisted a big config object", SIZE_IN_XML);
         
         HaMgmtNode n2 = createHotStandby(Duration.PRACTICALLY_FOREVER);
         forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("Rebinded", SIZE_UP_BOUND);
+        assertUsedMemoryMaxDelta("Rebinded", SIZE_UP_BOUND2);
         
-        for (int i=0; i<10; i++)
+        for (int i=0; i<10; i++) {
             forceRebindNow(n2);
+        }
+
         assertUsedMemoryMaxDelta("Several more rebinds", GRACE);
         for (int i=0; i<10; i++) {
             forcePersistNow(n1);
@@ -446,10 +448,10 @@ public class HotStandbyTest {
         app.config().set(TestEntity.CONF_OBJECT, "big is now small");
         assertUsedMemoryMaxDelta("Big made small at primary", -SIZE_DOWN_BOUND);
         forcePersistNow(n1);
-        assertUsedMemoryMaxDelta("And persisted", -SIZE_IN_XML_DOWN);
+        assertUsedMemoryMaxDelta("And persisted", 0); //-SIZE_IN_XML_DOWN);
         
         forceRebindNow(n2);
-        assertUsedMemoryMaxDelta("And at secondary", -SIZE_DOWN_BOUND);
+        assertUsedMemoryMaxDelta("And at secondary", 0); //-SIZE_DOWN_BOUND);
         
         Entities.unmanage(app);
         forcePersistNow(n1);
@@ -543,12 +545,7 @@ public class HotStandbyTest {
         });
 
         Application app2RO = n1.mgmt.lookup(app2.getId(), Application.class);
-        try {
-            ((TestApplication)app2RO).sensors().set(TestEntity.SEQUENCE, 4);
-            Assert.fail("Should not have allowed sensor to be set");
-        } catch (Exception e) {
-            Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
-        }
+        setSensorOnReadOnly(app2RO, TestEntity.SEQUENCE, 4);
 
         n1.ha.changeMode(HighAvailabilityMode.AUTO);
         n2.ha.changeMode(HighAvailabilityMode.HOT_STANDBY, true, false);
@@ -574,6 +571,17 @@ public class HotStandbyTest {
         EntityAsserts.assertAttributeEquals(app2BRO, TestEntity.SEQUENCE, 4);
     }
 
+    private <T> void setSensorOnReadOnly(Entity item, AttributeSensor<T> sensor, T value) {
+        try {
+            item.sensors().set(sensor, value);
+
+            // we now allow access to the sensors -- but they don't have effect and a warning is logged
+            // Assert.fail("Should not have allowed sensor to be set");
+        } catch (Exception e) {
+            // Assert.assertTrue(e.toString().toLowerCase().contains("read-only"), "Error message did not contain expected text: "+e);
+        }
+    }
+
     @Test(groups="Integration", invocationCount=20)
     public void testChangeModeManyTimes() throws Exception {
         testChangeMode();
diff --git a/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java b/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java
index a0187f7..ac9531d 100644
--- a/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java
+++ b/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java
@@ -112,8 +112,8 @@ public class ElectPrimaryPolicy extends AbstractPolicy implements ElectPrimaryCo
     public void setEntity(@SuppressWarnings("deprecation") org.apache.brooklyn.api.entity.EntityLocal entity) {
         super.setEntity(entity);
 
-        if (!Entities.isManagedActive(entity)) {
-            // hot standby or entity finished
+        if (Entities.isReadOnly(entity)) {
+            // don't run in hot standby
             return;
         }
         
diff --git a/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java b/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
index 6d38525..002e3c6 100644
--- a/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
+++ b/utils/common/src/main/java/org/apache/brooklyn/test/Asserts.java
@@ -1531,6 +1531,10 @@ public class Asserts {
         }
 
         public void assertUsedMemoryLessThan(String event, long max, boolean push) {
+            assertUsedMemoryLessThan(event, max, push, null);
+        }
+
+        public void assertUsedMemoryLessThan(String event, long max, boolean push, String extraFailMessage) {
             long nowUsed = pushUsedMemory(event);
             if (nowUsed > max) {
                 // aggressively try to force GC
@@ -1538,14 +1542,15 @@ public class Asserts {
                 popUsedMemory();
                 nowUsed = pushUsedMemory(event+" (extra GC)");
                 if (nowUsed > max) {
-                    fail("Too much memory used - "+ ByteSizeStrings.java().apply(nowUsed)+" > max "+ByteSizeStrings.java().apply(max));
+                    fail("Too much memory used - "+ ByteSizeStrings.java().apply(nowUsed)+" > max "+ByteSizeStrings.java().apply(max)+
+                            (extraFailMessage==null ? "" : " "+extraFailMessage));
                 }
             }
             if (!push) popUsedMemory();
         }
         public void assertUsedMemoryMaxDelta(String event, long deltaMegabytes, boolean push) {
             final long last = peekLastUsedMemory();
-            assertUsedMemoryLessThan(event, last + deltaMegabytes*1024*1024, push);
+            assertUsedMemoryLessThan(event, last + deltaMegabytes*1024*1024, push, "(prev was "+ByteSizeStrings.java().apply(last)+", delta limit was "+ByteSizeStrings.java().apply(deltaMegabytes*1024*1024)+")");
         }
     }
 }

[brooklyn-server] 04/06: more guards against activity running on read-only entities

Posted by he...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

heneveld pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git

commit 5a072bb9b3716917af9aa0095b415f8c65280578
Author: Alex Heneveld <al...@cloudsoftcorp.com>
AuthorDate: Fri Sep 10 12:48:31 2021 +0100

    more guards against activity running on read-only entities
---
 .../brooklyn/core/entity/lifecycle/ServiceStateLogic.java    |  6 +++---
 .../apache/brooklyn/core/entity/trait/StartableMethods.java  |  2 +-
 core/src/main/java/org/apache/brooklyn/core/feed/Poller.java |  2 +-
 .../org/apache/brooklyn/entity/group/DynamicClusterImpl.java |  2 +-
 .../apache/brooklyn/entity/stock/AsyncApplicationImpl.java   |  2 +-
 .../apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java | 12 ++++++------
 .../apache/brooklyn/policy/failover/ElectPrimaryPolicy.java  |  5 +++++
 .../software/base/AbstractSoftwareProcessSshDriver.java      |  4 ++--
 .../software/base/AbstractSoftwareProcessWinRmDriver.java    |  4 ++--
 9 files changed, 22 insertions(+), 17 deletions(-)

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 d32d0c5..8ab3d27 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
@@ -184,8 +184,8 @@ public class ServiceStateLogic {
 
     private static void waitBrieflyForServiceUpIfStateIsRunning(Entity entity, Lifecycle state) {
         if (state==Lifecycle.RUNNING) {
-            Boolean up = ((EntityInternal)entity).getAttribute(Attributes.SERVICE_UP);
-            if (!Boolean.TRUE.equals(up) && !Boolean.TRUE.equals(Entities.isReadOnly(entity))) {
+            Boolean up = entity.getAttribute(Attributes.SERVICE_UP);
+            if (!Boolean.TRUE.equals(up) && !Entities.isReadOnly(entity)) {
                 // pause briefly to allow any recent problem-clearing processing to complete
                 Stopwatch timer = Stopwatch.createStarted();
                 boolean nowUp = Repeater.create()
@@ -537,7 +537,7 @@ public class ServiceStateLogic {
 
         @Override
         protected void onUpdated() {
-            if (entity==null || !Entities.isManaged(entity)) {
+            if (entity==null || !Entities.isManagedActive(entity)) {
                 // either invoked during setup or entity has become unmanaged; just ignore
                 BrooklynLogging.log(log, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
                     "Ignoring service indicators onUpdated at {} from invalid/unmanaged entity ({})", this, entity);
diff --git a/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java b/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
index 933dd25..d887c33 100644
--- a/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
+++ b/core/src/main/java/org/apache/brooklyn/core/entity/trait/StartableMethods.java
@@ -76,7 +76,7 @@ public class StartableMethods {
         List<Startable> failedEntities = Lists.newArrayList();
         
         for (final Startable entity : entities) {
-            if (!Entities.isManaged((Entity)entity)) {
+            if (!Entities.isManagedActive((Entity)entity)) {
                 log.debug("Not stopping {} because it is not managed; continuing", entity);
                 continue;
             }
diff --git a/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java b/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
index ce12a0f..2d70baa 100644
--- a/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
+++ b/core/src/main/java/org/apache/brooklyn/core/feed/Poller.java
@@ -147,7 +147,7 @@ public class Poller<V> {
                 ScheduledTask t = ScheduledTask.builder(() -> {
                             DynamicSequentialTask<Void> task = new DynamicSequentialTask<Void>(MutableMap.of("displayName", scheduleName, "entity", entity), 
                                 new Callable<Void>() { @Override public Void call() {
-                                    if (!Entities.isManaged(entity)) {
+                                    if (!Entities.isManagedActive(entity)) {
                                         return null;
                                     }
                                     if (onlyIfServiceUp && !Boolean.TRUE.equals(entity.getAttribute(Attributes.SERVICE_UP))) {
diff --git a/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java b/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
index 7e6b96f..ba59b84 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/group/DynamicClusterImpl.java
@@ -453,7 +453,7 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus
     protected void doStart() {
         if (isQuarantineEnabled()) {
             QuarantineGroup quarantineGroup = getAttribute(QUARANTINE_GROUP);
-            if (quarantineGroup==null || !Entities.isManaged(quarantineGroup)) {
+            if (quarantineGroup==null || !Entities.isManagedActive(quarantineGroup)) {
                 quarantineGroup = addChild(EntitySpec.create(QuarantineGroup.class).displayName("quarantine"));
                 sensors().set(QUARANTINE_GROUP, quarantineGroup);
             }
diff --git a/core/src/main/java/org/apache/brooklyn/entity/stock/AsyncApplicationImpl.java b/core/src/main/java/org/apache/brooklyn/entity/stock/AsyncApplicationImpl.java
index 636ab4f..5a71504 100644
--- a/core/src/main/java/org/apache/brooklyn/entity/stock/AsyncApplicationImpl.java
+++ b/core/src/main/java/org/apache/brooklyn/entity/stock/AsyncApplicationImpl.java
@@ -276,7 +276,7 @@ public class AsyncApplicationImpl extends AbstractApplication implements AsyncAp
 
         @Override
         protected void onUpdated() {
-            if (entity == null || !isRunning() || !Entities.isManaged(entity)) {
+            if (entity == null || !isRunning() || !Entities.isManagedActive(entity)) {
                 // e.g. invoked during setup or entity has become unmanaged; just ignore
                 BrooklynLogging.log(LOG, BrooklynLogging.levelDebugOrTraceIfReadOnly(entity),
                     "Ignoring {} onUpdated when entity is not in valid state ({})", this, entity);
diff --git a/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java b/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
index 07ee817..c9de929 100644
--- a/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
+++ b/policy/src/main/java/org/apache/brooklyn/policy/autoscaling/AutoScalerPolicy.java
@@ -850,8 +850,8 @@ public class AutoScalerPolicy extends AbstractPolicy {
     }
     
     private void analyze(ScalingData data, String description) {
-        if (!Entities.isManaged(entity)) {
-            LOG.debug("Skipping autoscaling analysis because entity "+entity+" is unmanaged");
+        if (!Entities.isManagedActive(entity)) {
+            LOG.debug("Skipping autoscaling analysis because entity "+entity+" is not actively managed");
             return;
         }
 
@@ -1102,8 +1102,8 @@ public class AutoScalerPolicy extends AbstractPolicy {
 
     private void resizeNow(String reason) {
         // this runs in a thread after the fact so might not be cancelled
-        if (!Entities.isManaged(entity)) {
-            LOG.debug("Skipping autoscaling resize scheduling (due to '"+reason+"') because entity "+entity+" is unmanaged");
+        if (!Entities.isManagedActive(entity)) {
+            LOG.debug("Skipping autoscaling resize scheduling (due to '"+reason+"') because entity "+entity+" is not actively managed");
             return;
         }
 
@@ -1139,8 +1139,8 @@ public class AutoScalerPolicy extends AbstractPolicy {
                 public Void call() throws Exception {
                     // TODO Should we use int throughout, rather than casting here?
                     try {
-                        if (!Entities.isManaged(entity)) {
-                            LOG.warn("Skipping autoscaling resize task (due to '"+reason+"') because entity "+entity+" is unmanaged");
+                        if (!Entities.isManagedActive(entity)) {
+                            LOG.warn("Skipping autoscaling resize task (due to '"+reason+"') because entity "+entity+" is not actively managed");
                             return null;
                         }
 
diff --git a/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java b/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java
index e2190ac..a0187f7 100644
--- a/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java
+++ b/policy/src/main/java/org/apache/brooklyn/policy/failover/ElectPrimaryPolicy.java
@@ -111,6 +111,11 @@ public class ElectPrimaryPolicy extends AbstractPolicy implements ElectPrimaryCo
     @Override
     public void setEntity(@SuppressWarnings("deprecation") org.apache.brooklyn.api.entity.EntityLocal entity) {
         super.setEntity(entity);
+
+        if (!Entities.isManagedActive(entity)) {
+            // hot standby or entity finished
+            return;
+        }
         
         checkAndMaybeAddEffector(entity);
         checkQuorums(entity);
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
index 376577b..fc58de5 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessSshDriver.java
@@ -463,8 +463,8 @@ public abstract class AbstractSoftwareProcessSshDriver extends AbstractSoftwareP
      * @see #DEBUG
      */
     protected ScriptHelper newScript(Map<String, ?> flags, String phase) {
-        if (!Entities.isManaged(getEntity()))
-            throw new IllegalStateException(getEntity()+" is no longer managed; cannot create script to run here ("+phase+")");
+        if (!Entities.isManagedActive(getEntity()))
+            throw new IllegalStateException(getEntity()+" is not currently managed here; cannot create script to run here ("+phase+")");
 
         if (!Iterables.all(flags.keySet(), StringPredicates.equalToAny(VALID_FLAGS))) {
             throw new IllegalArgumentException("Invalid flags passed: " + flags);
diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
index 268de22..ddbcb3b 100644
--- a/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
+++ b/software/base/src/main/java/org/apache/brooklyn/entity/software/base/AbstractSoftwareProcessWinRmDriver.java
@@ -102,8 +102,8 @@ public abstract class AbstractSoftwareProcessWinRmDriver extends AbstractSoftwar
     }
 
     protected WinRmExecuteHelper newEmptyScript(String taskNamePrefix) {
-        if (!Entities.isManaged(getEntity()))
-            throw new IllegalStateException(getEntity() + " is no longer managed; cannot create script to run here (" + taskNamePrefix + ")");
+        if (!Entities.isManagedActive(getEntity()))
+            throw new IllegalStateException(getEntity() + " is not managed here; cannot create script to run here (" + taskNamePrefix + ")");
 
         WinRmExecuteHelper s = new WinRmExecuteHelper(this, taskNamePrefix + " " + elvis(entity, this));
         return s;