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 2014/11/06 11:36:54 UTC

[5/9] git commit: optimize rebind process

optimize rebind process

remove extraneous obj store lists+reads from process, and a bunch of code/redundancy tidy, and a bit more log tidy


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

Branch: refs/heads/master
Commit: be5aac2574227c4089e8a8fa45f275a4539368f8
Parents: 2815011
Author: Alex Heneveld <al...@cloudsoftcorp.com>
Authored: Thu Nov 6 02:01:31 2014 +0000
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
Committed: Thu Nov 6 02:42:20 2014 +0000

----------------------------------------------------------------------
 .../entity/rebind/BrooklynObjectType.java       |  28 +-
 .../mementos/BrooklynMementoPersister.java      |  35 ++-
 .../mementos/BrooklynMementoRawData.java        |  43 +++
 .../rebind/PeriodicDeltaChangeListener.java     |   4 +-
 .../entity/rebind/PersisterDeltaImpl.java       |  31 ++
 .../rebind/RebindExceptionHandlerImpl.java      |   6 +-
 .../entity/rebind/RebindManagerImpl.java        |   5 +-
 .../rebind/dto/BrooklynMementoManifestImpl.java |  15 +
 .../AbstractBrooklynMementoPersister.java       |  18 +-
 .../BrooklynMementoPersisterInMemory.java       |   5 +-
 .../BrooklynMementoPersisterToMultiFile.java    |  16 +
 .../BrooklynMementoPersisterToObjectStore.java  | 292 ++++++++-----------
 .../ha/HighAvailabilityManagerImpl.java         |  10 +-
 .../entity/rebind/RebindFailuresTest.java       |   3 +-
 .../entity/rebind/RebindTestFixture.java        |   2 +-
 .../rebind/RecordingRebindExceptionHandler.java |   6 +-
 .../BrooklynMementoPersisterTestFixture.java    |   2 +-
 17 files changed, 325 insertions(+), 196 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/api/src/main/java/brooklyn/entity/rebind/BrooklynObjectType.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/entity/rebind/BrooklynObjectType.java b/api/src/main/java/brooklyn/entity/rebind/BrooklynObjectType.java
index d887ffe..b00d3b3 100644
--- a/api/src/main/java/brooklyn/entity/rebind/BrooklynObjectType.java
+++ b/api/src/main/java/brooklyn/entity/rebind/BrooklynObjectType.java
@@ -19,14 +19,28 @@
 package brooklyn.entity.rebind;
 
 import com.google.common.annotations.Beta;
+import com.google.common.base.CaseFormat;
 
 @Beta
 public enum BrooklynObjectType {
-    ENTITY,
-    LOCATION,
-    POLICY,
-    ENRICHER,
-    FEED,
-    CATALOG_ITEM,
-    UNKNOWN;
+    ENTITY("entities"),
+    LOCATION("locations"),
+    POLICY("policies"),
+    ENRICHER("enrichers"),
+    FEED("feeds"),
+    CATALOG_ITEM("catalog"),
+    UNKNOWN("unknown");
+    
+    private final String subPathName;
+    
+    BrooklynObjectType(String subPathName) {
+        this.subPathName = subPathName;
+    }
+    public String toCamelCase() {
+        return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, this.name());
+    }
+
+    public String getSubPathName() {
+        return subPathName;
+    }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java b/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
index 0317059..229c7c3 100644
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoPersister.java
@@ -23,9 +23,13 @@ import java.util.Collection;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import javax.annotation.Nullable;
+
+import brooklyn.basic.BrooklynObject;
 import brooklyn.catalog.CatalogItem;
 import brooklyn.entity.Entity;
 import brooklyn.entity.Feed;
+import brooklyn.entity.rebind.BrooklynObjectType;
 import brooklyn.entity.rebind.PersistenceExceptionHandler;
 import brooklyn.entity.rebind.RebindExceptionHandler;
 import brooklyn.entity.rebind.RebindManager;
@@ -52,13 +56,37 @@ public interface BrooklynMementoPersister {
         Feed lookupFeed(String id);
         CatalogItem<?, ?> lookupCatalogItem(String id);
     }
+    
+    /**
+     * Loads raw data contents of the mementos.
+     * <p>
+     * Some classes (esp deprecated ones) may return null here,
+     * meaning that the {@link #loadMementoManifest(BrooklynMementoRawData, RebindExceptionHandler)}
+     * and {@link #loadMemento(BrooklynMementoRawData, LookupContext, RebindExceptionHandler)} methods
+     * will populate the raw data via another source.
+     */
+    BrooklynMementoRawData loadMementoRawData(RebindExceptionHandler exceptionHandler);
 
+    /** @deprecated since 0.7.0 use {@link #loadMementoManifest(BrooklynMementoRawData, RebindExceptionHandler)} */
     BrooklynMementoManifest loadMementoManifest(RebindExceptionHandler exceptionHandler) throws IOException;
-
+    
     /**
-     * Note that this method is *not* thread safe.
+     * Loads minimal manifest information (almost entirely *not* deserialized).
+     * Implementations should load the raw data if {@link BrooklynMementoRawData} is not supplied,
+     * but callers are encouraged to supply that for optimal performance.
      */
+    BrooklynMementoManifest loadMementoManifest(@Nullable BrooklynMementoRawData mementoData, RebindExceptionHandler exceptionHandler) throws IOException;
+
+    /** @deprecated since 0.7.0 use {@link #loadMemento(RebindExceptionHandler)} */
     BrooklynMemento loadMemento(LookupContext lookupContext, RebindExceptionHandler exceptionHandler) throws IOException;
+     /**
+      * Retrieves the memento class, containing deserialized objects (but not the {@link BrooklynObject} class).
+      * Implementations should load the raw data if {@link BrooklynMementoRawData} is not supplied,
+      * but callers are encouraged to supply that for optimal performance.
+      * <p>
+      * Note that this method is *not* thread safe.
+      */
+    BrooklynMemento loadMemento(@Nullable BrooklynMementoRawData mementoData, LookupContext lookupContext, RebindExceptionHandler exceptionHandler) throws IOException;
     
     void checkpoint(BrooklynMemento memento, PersistenceExceptionHandler exceptionHandler);
 
@@ -94,6 +122,9 @@ public interface BrooklynMementoPersister {
         Collection<String> removedEnricherIds();
         Collection<String> removedFeedIds();
         Collection<String> removedCatalogItemIds();
+        
+        Collection<? extends Memento> getObjectsOfType(BrooklynObjectType type);
+        Collection<String> getRemovedObjectsOfType(BrooklynObjectType type);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java b/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
index abb61d4..32c7232 100644
--- a/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
+++ b/api/src/main/java/brooklyn/mementos/BrooklynMementoRawData.java
@@ -21,6 +21,8 @@ package brooklyn.mementos;
 import java.util.Collections;
 import java.util.Map;
 
+import brooklyn.entity.rebind.BrooklynObjectType;
+
 import com.google.common.annotations.Beta;
 import com.google.common.collect.Maps;
 
@@ -85,6 +87,34 @@ public class BrooklynMementoRawData {
         public Builder catalogItems(Map<String, String> vals) {
             catalogItems.putAll(vals); return this;
         }
+        
+        public Builder put(BrooklynObjectType type, String id, String val) {
+            switch (type) {
+            case ENTITY: return entity(id, val);
+            case LOCATION: return location(id, val);
+            case POLICY: return policy(id, val);
+            case ENRICHER: return enricher(id, val);
+            case FEED: return feed(id, val);
+            case CATALOG_ITEM: return catalogItem(id, val);
+            case UNKNOWN:
+            default:
+                throw new IllegalArgumentException(type+" not supported");
+            }
+        }
+        public Builder putAll(BrooklynObjectType type, Map<String,String> vals) {
+            switch (type) {
+            case ENTITY: return entities(vals);
+            case LOCATION: return locations(vals);
+            case POLICY: return policies(vals);
+            case ENRICHER: return enrichers(vals);
+            case FEED: return feeds(vals);
+            case CATALOG_ITEM: return catalogItems(vals);
+            case UNKNOWN:
+            default:
+                throw new IllegalArgumentException(type+" not supported");
+            }
+        }
+
         public BrooklynMementoRawData build() {
             return new BrooklynMementoRawData(this);
         }
@@ -133,4 +163,17 @@ public class BrooklynMementoRawData {
     public boolean isEmpty() {
         return entities.isEmpty() && locations.isEmpty() && policies.isEmpty() && enrichers.isEmpty() && feeds.isEmpty() && catalogItems.isEmpty();
     }
+    
+    public Map<String, String> getObjectsOfType(BrooklynObjectType type) {
+        switch (type) {
+        case ENTITY: return getEntities();
+        case LOCATION: return getLocations();
+        case POLICY: return getPolicies();
+        case ENRICHER: return getEnrichers();
+        case FEED: return getFeeds();
+        case CATALOG_ITEM: return getCatalogItems();
+        default:
+            throw new IllegalArgumentException("Type "+type+" not supported");
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java b/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
index 1696767..b104365 100644
--- a/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
+++ b/core/src/main/java/brooklyn/entity/rebind/PeriodicDeltaChangeListener.java
@@ -323,7 +323,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
                 deltaCollector = new DeltaCollector();
             }
             
-            if (LOG.isDebugEnabled()) LOG.debug("Persister delta as reported: "
+            if (LOG.isDebugEnabled()) LOG.debug("Checkpointing delta of memento: "
                     + "updating entities={}, locations={}, policies={}, enrichers={}, catalog items={}; "
                     + "removing entities={}, locations={}, policies={}, enrichers={}, catalog items={}",
                     new Object[] {
@@ -332,7 +332,7 @@ public class PeriodicDeltaChangeListener implements ChangeListener {
 
             addReferencedObjects(prevDeltaCollector);
 
-            if (LOG.isTraceEnabled()) LOG.trace("Persister delta with references: "
+            if (LOG.isTraceEnabled()) LOG.trace("Checkpointing delta of memento with references: "
                     + "updating {} entities, {} locations, {} policies, {} enrichers, {} catalog items; "
                     + "removing {} entities, {} locations, {} policies, {} enrichers, {} catalog items",
                     new Object[] {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/PersisterDeltaImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/PersisterDeltaImpl.java b/core/src/main/java/brooklyn/entity/rebind/PersisterDeltaImpl.java
index fd19ebc..c971a2f 100644
--- a/core/src/main/java/brooklyn/entity/rebind/PersisterDeltaImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/PersisterDeltaImpl.java
@@ -26,6 +26,7 @@ import brooklyn.mementos.EnricherMemento;
 import brooklyn.mementos.EntityMemento;
 import brooklyn.mementos.FeedMemento;
 import brooklyn.mementos.LocationMemento;
+import brooklyn.mementos.Memento;
 import brooklyn.mementos.PolicyMemento;
 
 import com.google.common.collect.Sets;
@@ -164,4 +165,34 @@ public class PersisterDeltaImpl implements Delta {
     public Collection<String> removedCatalogItemIds() {
         return removedCatalogItemIds;
     }
+    
+    @Override
+    public Collection<? extends Memento> getObjectsOfType(BrooklynObjectType type) {
+        switch (type) {
+        case ENTITY: return entities();
+        case LOCATION: return locations();
+        case POLICY: return policies();
+        case ENRICHER: return enrichers();
+        case FEED: return feeds();
+        case CATALOG_ITEM: return catalogItems();
+        case UNKNOWN: 
+        default:
+            throw new IllegalArgumentException(type+" not supported");
+        }
+    }
+    
+    @Override
+    public Collection<String> getRemovedObjectsOfType(BrooklynObjectType type) {
+        switch (type) {
+        case ENTITY: return removedEntityIds();
+        case LOCATION: return removedLocationIds();
+        case POLICY: return removedPolicyIds();
+        case ENRICHER: return removedEnricherIds();
+        case FEED: return removedFeedIds();
+        case CATALOG_ITEM: return removedCatalogItemIds();
+        case UNKNOWN: 
+        default:
+            throw new IllegalArgumentException(type+" not supported");
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/RebindExceptionHandlerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindExceptionHandlerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindExceptionHandlerImpl.java
index 870a031..f595e54 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindExceptionHandlerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindExceptionHandlerImpl.java
@@ -222,7 +222,7 @@ public class RebindExceptionHandlerImpl implements RebindExceptionHandler {
         if (creationFailedIds.contains(id)) {
             // already know about this; ignore
         } else {
-            String errmsg = type+" '"+id+"' not found";
+            String errmsg = type.toCamelCase()+" '"+id+"' not found";
             exceptions.add(new IllegalStateException(errmsg));
             onErrorImpl(errmsg);
         }
@@ -231,7 +231,7 @@ public class RebindExceptionHandlerImpl implements RebindExceptionHandler {
     @Override
     public void onRebindFailed(BrooklynObjectType type, BrooklynObject instance, Exception e) {
         Exceptions.propagateIfFatal(e);
-        String errmsg = "problem rebinding "+type+" "+instance.getId()+" ("+instance+")";
+        String errmsg = "problem rebinding "+type.toCamelCase()+" "+instance.getId()+" ("+instance+")";
         
         switch (type) {
         case FEED:
@@ -318,7 +318,7 @@ public class RebindExceptionHandlerImpl implements RebindExceptionHandler {
     @Override
     public void onManageFailed(BrooklynObjectType type, BrooklynObject instance, Exception e) {
         Exceptions.propagateIfFatal(e);
-        String errmsg = "problem managing "+type+" "+instance.getId()+" ("+instance+")";
+        String errmsg = "problem managing "+type.toCamelCase()+" "+instance.getId()+" ("+instance+")";
         
         exceptions.add(new IllegalStateException(errmsg, e));
         onErrorImpl(errmsg, e);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
index c2b271e..e29d94f 100644
--- a/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/RebindManagerImpl.java
@@ -543,7 +543,8 @@ public class RebindManagerImpl implements RebindManager {
             
             LOG.debug("Rebinding ("+mode+", iteration "+readOnlyRebindCount+") from "+getPersister().getBackingStoreDescription()+"...");
 
-            BrooklynMementoManifest mementoManifest = persistenceStoreAccess.loadMementoManifest(exceptionHandler);
+            BrooklynMementoRawData mementoRawData = persistenceStoreAccess.loadMementoRawData(exceptionHandler);
+            BrooklynMementoManifest mementoManifest = persistenceStoreAccess.loadMementoManifest(mementoRawData, exceptionHandler);
 
             boolean isEmpty = mementoManifest.isEmpty();
             if (mode!=ManagementNodeState.HOT_STANDBY) {
@@ -670,7 +671,7 @@ public class RebindManagerImpl implements RebindManager {
             // PHASE FOUR
             //
             
-            BrooklynMemento memento = persistenceStoreAccess.loadMemento(realLookupContext, exceptionHandler);
+            BrooklynMemento memento = persistenceStoreAccess.loadMemento(mementoRawData, realLookupContext, exceptionHandler);
             
             
             //

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java b/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
index 2b52003..4850309 100644
--- a/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
+++ b/core/src/main/java/brooklyn/entity/rebind/dto/BrooklynMementoManifestImpl.java
@@ -23,6 +23,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Map;
 
+import brooklyn.entity.rebind.BrooklynObjectType;
 import brooklyn.mementos.BrooklynMementoManifest;
 import brooklyn.mementos.CatalogItemMemento;
 
@@ -83,6 +84,20 @@ public class BrooklynMementoManifestImpl implements BrooklynMementoManifest, Ser
             catalogItems.put(val.getId(), val); return this;
         }
 
+        public Builder putType(BrooklynObjectType type, String id, String javaType) {
+            switch (type) {
+            case ENTITY: throw new IllegalArgumentException(type.toCamelCase()+" requires additional parameters");
+            case LOCATION: return location(id, javaType);
+            case POLICY: return policy(id, javaType);
+            case ENRICHER: return enricher(id, javaType);
+            case FEED: return feed(id, javaType);
+            case CATALOG_ITEM: throw new IllegalArgumentException(type.toCamelCase()+" requires different parameters");
+            case UNKNOWN: 
+            default: 
+                throw new IllegalArgumentException(type.toCamelCase()+" not supported");
+            }
+        }
+
         public BrooklynMementoManifest build() {
             return new BrooklynMementoManifestImpl(this);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java b/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
index 91baa26..42643ab 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/AbstractBrooklynMementoPersister.java
@@ -26,6 +26,7 @@ import brooklyn.entity.rebind.dto.MutableBrooklynMemento;
 import brooklyn.mementos.BrooklynMemento;
 import brooklyn.mementos.BrooklynMementoManifest;
 import brooklyn.mementos.BrooklynMementoPersister;
+import brooklyn.mementos.BrooklynMementoRawData;
 import brooklyn.mementos.CatalogItemMemento;
 import brooklyn.mementos.EnricherMemento;
 import brooklyn.mementos.EntityMemento;
@@ -42,13 +43,28 @@ public abstract class AbstractBrooklynMementoPersister implements BrooklynMement
     protected volatile MutableBrooklynMemento memento = new MutableBrooklynMemento();
     
     @Override
-    public BrooklynMemento loadMemento(LookupContext lookupContext, RebindExceptionHandler exceptionHandler) {
+    public BrooklynMementoRawData loadMementoRawData(RebindExceptionHandler exceptionHandler) {
+        return null;
+    }
+    
+    @Override
+    public BrooklynMemento loadMemento(BrooklynMementoRawData mementoData, LookupContext lookupContext, RebindExceptionHandler exceptionHandler) {
         // Trusting people not to cast+modify, because the in-memory persister wouldn't be used in production code
         return memento;
     }
     
     @Override
+    public BrooklynMemento loadMemento(LookupContext lookupContext, RebindExceptionHandler exceptionHandler) {
+        return loadMemento(null, lookupContext, exceptionHandler);
+    }
+    
+    @Override
     public BrooklynMementoManifest loadMementoManifest(RebindExceptionHandler exceptionHandler) {
+        return loadMementoManifest(null, exceptionHandler);
+    }
+    
+    @Override
+    public BrooklynMementoManifest loadMementoManifest(BrooklynMementoRawData mementoData, RebindExceptionHandler exceptionHandler) {
         BrooklynMementoManifestImpl.Builder builder = BrooklynMementoManifestImpl.builder();
         for (EntityMemento entity : memento.getEntityMementos().values()) {
             builder.entity(entity.getId(), entity.getType(), entity.getParent(), entity.getCatalogItemId());

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
index 847dfa7..f4e4fc0 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterInMemory.java
@@ -42,7 +42,6 @@ import brooklyn.location.Location;
 import brooklyn.management.ManagementContext;
 import brooklyn.mementos.BrooklynMemento;
 import brooklyn.mementos.BrooklynMementoManifest;
-import brooklyn.mementos.BrooklynMementoManifest.EntityMementoManifest;
 import brooklyn.policy.Enricher;
 import brooklyn.policy.Policy;
 import brooklyn.util.collections.MutableList;
@@ -116,7 +115,7 @@ public class BrooklynMementoPersisterInMemory extends AbstractBrooklynMementoPer
                         .build();
                 PersistenceExceptionHandler persistenceExceptionHandler = PersistenceExceptionHandlerImpl.builder().build();
                 persister.checkpoint(memento, persistenceExceptionHandler);
-                final BrooklynMementoManifest manifest = persister.loadMementoManifest(rebindExceptionHandler);
+                final BrooklynMementoManifest manifest = persister.loadMementoManifest(null, rebindExceptionHandler);
                 LookupContext dummyLookupContext = new LookupContext() {
                     @Override
                     public ManagementContext lookupManagementContext() {
@@ -181,7 +180,7 @@ public class BrooklynMementoPersisterInMemory extends AbstractBrooklynMementoPer
                 };
 
                 // Not actually reconstituting, because need to use a real lookupContext to reconstitute all the entities
-                persister.loadMemento(dummyLookupContext, rebindExceptionHandler);
+                persister.loadMemento(null, dummyLookupContext, rebindExceptionHandler);
             } finally {
                 Os.deleteRecursively(tempDir);
             }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
index 2d0fe99..392a496 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToMultiFile.java
@@ -40,6 +40,7 @@ import brooklyn.entity.rebind.dto.BrooklynMementoManifestImpl;
 import brooklyn.mementos.BrooklynMemento;
 import brooklyn.mementos.BrooklynMementoManifest;
 import brooklyn.mementos.BrooklynMementoPersister;
+import brooklyn.mementos.BrooklynMementoRawData;
 import brooklyn.mementos.CatalogItemMemento;
 import brooklyn.mementos.EnricherMemento;
 import brooklyn.mementos.EntityMemento;
@@ -153,7 +154,17 @@ public class BrooklynMementoPersisterToMultiFile implements BrooklynMementoPersi
     }
     
     @Override
+    public BrooklynMementoRawData loadMementoRawData(RebindExceptionHandler exceptionHandler) {
+        return null;
+    }
+    
+    @Override
     public BrooklynMementoManifest loadMementoManifest(RebindExceptionHandler exceptionHandler) throws IOException {
+        return loadMementoManifest(null, exceptionHandler);
+    }
+    
+    @Override
+    public BrooklynMementoManifest loadMementoManifest(BrooklynMementoRawData mementoData, RebindExceptionHandler exceptionHandler) throws IOException {
         if (!running) {
             throw new IllegalStateException("Persister not running; cannot load memento manifest from "+dir);
         }
@@ -251,6 +262,11 @@ public class BrooklynMementoPersisterToMultiFile implements BrooklynMementoPersi
 
     @Override
     public BrooklynMemento loadMemento(LookupContext lookupContext, RebindExceptionHandler exceptionHandler) throws IOException {
+        return loadMemento(null, lookupContext, exceptionHandler);
+    }
+    
+    @Override
+    public BrooklynMemento loadMemento(BrooklynMementoRawData mementoData, LookupContext lookupContext, RebindExceptionHandler exceptionHandler) throws IOException {
         if (!running) {
             throw new IllegalStateException("Persister not running; cannot load memento from "+dir);
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
index 8faa80f..91b738f 100644
--- a/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
+++ b/core/src/main/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterToObjectStore.java
@@ -52,12 +52,8 @@ import brooklyn.mementos.BrooklynMementoManifest;
 import brooklyn.mementos.BrooklynMementoPersister;
 import brooklyn.mementos.BrooklynMementoRawData;
 import brooklyn.mementos.CatalogItemMemento;
-import brooklyn.mementos.EnricherMemento;
-import brooklyn.mementos.EntityMemento;
-import brooklyn.mementos.FeedMemento;
-import brooklyn.mementos.LocationMemento;
 import brooklyn.mementos.Memento;
-import brooklyn.mementos.PolicyMemento;
+import brooklyn.util.collections.MutableMap;
 import brooklyn.util.exceptions.CompoundRuntimeException;
 import brooklyn.util.exceptions.Exceptions;
 import brooklyn.util.text.Strings;
@@ -67,6 +63,7 @@ import brooklyn.util.xstream.XmlUtil;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Objects;
 import com.google.common.base.Stopwatch;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
@@ -96,6 +93,10 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
             "Maximum number of attempts to serialize a memento (e.g. if first attempts fail because of concurrent modifications of an entity)", 
             5);
 
+    static final BrooklynObjectType[] PERSISTED_OBJECT_TYPES_IN_ORDER = new BrooklynObjectType[] { 
+        BrooklynObjectType.ENTITY, BrooklynObjectType.LOCATION, BrooklynObjectType.POLICY,
+        BrooklynObjectType.ENRICHER, BrooklynObjectType.FEED, BrooklynObjectType.CATALOG_ITEM };
+
     private final PersistenceObjectStore objectStore;
     private final MementoSerializer<Object> serializer;
 
@@ -192,46 +193,75 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
         }
     }
 
-    @Beta
-    public BrooklynMementoRawData loadMementoRawData(final RebindExceptionHandler exceptionHandler) throws IOException {
-        final BrooklynMementoRawData.Builder builder = BrooklynMementoRawData.builder();
+    private Map<String,String> makeIdSubPathMap(Iterable<String> subPathLists) {
+        Map<String,String> result = MutableMap.of();
+        for (String subpath: subPathLists) {
+            String id = subpath;
+            id = id.substring(id.lastIndexOf('/')+1);
+            id = id.substring(id.lastIndexOf('\\')+1);
+            // assumes id is the filename; should work even if not, as id is later read from xpath
+            // but you'll get warnings (and possibility of loss if there is a collision)
+            result.put(id, subpath);
+        }
+        return result;
+    }
+    
+    protected BrooklynMementoRawData listMementoSubPathsAsData(final RebindExceptionHandler exceptionHandler) {
+        final BrooklynMementoRawData.Builder subPathDataBuilder = BrooklynMementoRawData.builder();
 
-        Visitor visitor = new Visitor() {
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        try {
+            for (BrooklynObjectType type: PERSISTED_OBJECT_TYPES_IN_ORDER)
+                subPathDataBuilder.putAll(type, makeIdSubPathMap(objectStore.listContentsWithSubPath(type.getSubPathName())));
+            
+        } catch (Exception e) {
+            Exceptions.propagateIfFatal(e);
+            exceptionHandler.onLoadMementoFailed(BrooklynObjectType.UNKNOWN, "Failed to list files", e);
+            throw new IllegalStateException("Failed to list memento files in "+objectStore, e);
+        }
+
+        BrooklynMementoRawData subPathData = subPathDataBuilder.build();
+        LOG.debug("Loaded rebind lists; took {}: {} entities, {} locations, {} policies, {} enrichers, {} feeds, {} catalog items; from {}", new Object[]{
+            Time.makeTimeStringRounded(stopwatch),
+            subPathData.getEntities().size(), subPathData.getLocations().size(), subPathData.getPolicies().size(), subPathData.getEnrichers().size(), 
+            subPathData.getFeeds().size(), subPathData.getCatalogItems().size(),
+            objectStore.getSummaryName() });
+        
+        return subPathData;
+    }
+    
+    public BrooklynMementoRawData loadMementoRawData(final RebindExceptionHandler exceptionHandler) {
+        BrooklynMementoRawData subPathData = listMementoSubPathsAsData(exceptionHandler);
+        
+        final BrooklynMementoRawData.Builder builder = BrooklynMementoRawData.builder();
+        
+        Visitor loaderVisitor = new Visitor() {
             @Override
-            public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception {
-                switch (type) {
-                    case ENTITY:
-                        builder.entity((String) XmlUtil.xpath(contents, "/entity/id"), contents);
-                        break;
-                    case LOCATION:
-                        builder.location((String) XmlUtil.xpath(contents, "/location/id"), contents);
-                        break;
-                    case POLICY:
-                        builder.policy((String) XmlUtil.xpath(contents, "/policy/id"), contents);
-                        break;
-                    case ENRICHER:
-                        builder.enricher((String) XmlUtil.xpath(contents, "/enricher/id"), contents);
-                        break;
-                    case FEED:
-                        builder.feed((String) XmlUtil.xpath(contents, "/feed/id"), contents);
-                        break;
-                    case CATALOG_ITEM:
-                        builder.catalogItem((String) XmlUtil.xpath(contents, "/catalogItem/id"), contents);
-                        break;
-                    default:
-                        throw new IllegalStateException("Unexpected brooklyn type: "+type);
+            public void visit(BrooklynObjectType type, String id, String contentsSubpath) throws Exception {
+                String contents = null;
+                try {
+                    contents = read(contentsSubpath);
+                } catch (Exception e) {
+                    Exceptions.propagateIfFatal(e);
+                    exceptionHandler.onLoadMementoFailed(type, "memento "+id+" read error", e);
                 }
+                
+                String xmlId = (String) XmlUtil.xpath(contents, "/"+type.toCamelCase()+"/id");
+                if (!Objects.equal(id, xmlId))
+                    LOG.warn("ID mismatch on "+type.toCamelCase()+", "+id+" from path, "+xmlId+" from xml");
+                
+                builder.put(type, xmlId, contents);
             }
         };
 
         Stopwatch stopwatch = Stopwatch.createStarted();
 
-        visitMemento(visitor, exceptionHandler);
+        visitMemento("loading raw", subPathData, loaderVisitor, exceptionHandler);
         
         BrooklynMementoRawData result = builder.build();
 
         if (LOG.isDebugEnabled()) {
-            LOG.debug("Loaded memento manifest; took {}; {} entities, {} locations, {} policies, {} enrichers, {} feeds, {} catalog items, from {}", new Object[]{
+            LOG.debug("Loaded rebind raw data; took {}; {} entities, {} locations, {} policies, {} enrichers, {} feeds, {} catalog items, from {}", new Object[]{
                      Time.makeTimeStringRounded(stopwatch.elapsed(TimeUnit.MILLISECONDS)), result.getEntities().size(), 
                      result.getLocations().size(), result.getPolicies().size(), result.getEnrichers().size(),
                      result.getFeeds().size(), result.getCatalogItems().size(),
@@ -243,49 +273,49 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
 
     @Override
     public BrooklynMementoManifest loadMementoManifest(final RebindExceptionHandler exceptionHandler) throws IOException {
+        return loadMementoManifest(null, exceptionHandler);
+    }
+    
+    @Override
+    public BrooklynMementoManifest loadMementoManifest(BrooklynMementoRawData mementoData, final RebindExceptionHandler exceptionHandler) throws IOException {
+        if (mementoData==null)
+            mementoData = loadMementoRawData(exceptionHandler);
+        
         final BrooklynMementoManifestImpl.Builder builder = BrooklynMementoManifestImpl.builder();
 
         Visitor visitor = new Visitor() {
             @Override
-            public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception {
+            public void visit(BrooklynObjectType type, String objectId, final String contents) throws Exception {
+                final String prefix = "/"+type.toCamelCase()+"/";
+
+                class XPathHelper {
+                    private String get(String innerPath) {
+                        return (String) XmlUtil.xpath(contents, prefix+innerPath);
+                    }
+                }
+                XPathHelper x = new XPathHelper();
+                
                 switch (type) {
                     case ENTITY:
-                        String id = (String) XmlUtil.xpath(contents, "/entity/id");
-                        String objType = (String) XmlUtil.xpath(contents, "/entity/type");
-                        String parentId = (String) XmlUtil.xpath(contents, "/entity/parent");
-                        String catalogItemId = (String) XmlUtil.xpath(contents, "/entity/catalogItemId");
-                        builder.entity(id, objType, Strings.emptyToNull(parentId), Strings.emptyToNull(catalogItemId));
+                        builder.entity(x.get("id"), x.get("type"), 
+                            Strings.emptyToNull(x.get("parent")), Strings.emptyToNull(x.get("catalogItemId")));
                         break;
                     case LOCATION:
-                        id = (String) XmlUtil.xpath(contents, "/location/id");
-                        objType = (String) XmlUtil.xpath(contents, "/location/type");
-                        builder.location(id, objType);
-                        break;
                     case POLICY:
-                        id = (String) XmlUtil.xpath(contents, "/policy/id");
-                        objType = (String) XmlUtil.xpath(contents, "/policy/type");
-                        builder.policy(id, objType);
-                        break;
                     case ENRICHER:
-                        id = (String) XmlUtil.xpath(contents, "/enricher/id");
-                        objType = (String) XmlUtil.xpath(contents, "/enricher/type");
-                        builder.enricher(id, objType);
-                        break;
                     case FEED:
-                        id = (String) XmlUtil.xpath(contents, "/feed/id");
-                        objType = (String) XmlUtil.xpath(contents, "/feed/type");
-                        builder.feed(id, objType);
+                        builder.putType(type, x.get("id"), x.get("type"));
                         break;
                     case CATALOG_ITEM:
                         try {
                             CatalogItemMemento memento = (CatalogItemMemento) serializer.fromString(contents);
                             if (memento == null) {
-                                LOG.warn("No "+type.toString().toLowerCase()+"-memento deserialized from " + subPath + "; ignoring and continuing");
+                                LOG.warn("No "+type.toCamelCase()+"-memento deserialized from " + objectId + "; ignoring and continuing");
                             } else {
                                 builder.catalogItem(memento);
                             }
                         } catch (Exception e) {
-                            exceptionHandler.onLoadMementoFailed(type, "Memento "+subPath, e);
+                            exceptionHandler.onLoadMementoFailed(type, "memento "+objectId+" early catalog deserialization error", e);
                         }
                         break;
                     default:
@@ -296,7 +326,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
 
         Stopwatch stopwatch = Stopwatch.createStarted();
 
-        visitMemento(visitor, exceptionHandler);
+        visitMemento("manifests", mementoData, visitor, exceptionHandler);
         
         BrooklynMementoManifest result = builder.build();
 
@@ -314,29 +344,37 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
 
     @Override
     public BrooklynMemento loadMemento(LookupContext lookupContext, final RebindExceptionHandler exceptionHandler) throws IOException {
+        return loadMemento(null, lookupContext, exceptionHandler);
+    }
+    
+    @Override
+    public BrooklynMemento loadMemento(BrooklynMementoRawData mementoData, LookupContext lookupContext, final RebindExceptionHandler exceptionHandler) throws IOException {
+        if (mementoData==null)
+            mementoData = loadMementoRawData(exceptionHandler);
+
         Stopwatch stopwatch = Stopwatch.createStarted();
 
         final BrooklynMementoImpl.Builder builder = BrooklynMementoImpl.builder();
         
         Visitor visitor = new Visitor() {
             @Override
-            public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception {
+            public void visit(BrooklynObjectType type, String objectId, String contents) throws Exception {
                 try {
                     Memento memento = (Memento) serializer.fromString(contents);
                     if (memento == null) {
-                        LOG.warn("No "+type.toString().toLowerCase()+"-memento deserialized from " + subPath + "; ignoring and continuing");
+                        LOG.warn("No "+type.toCamelCase()+"-memento deserialized from " + objectId + "; ignoring and continuing");
                     } else {
                         builder.memento(memento);
                     }
                 } catch (Exception e) {
-                    exceptionHandler.onLoadMementoFailed(type, "Memento "+subPath, e);
+                    exceptionHandler.onLoadMementoFailed(type, "memento "+objectId+" deserialization error", e);
                 }
             }
         };
 
         serializer.setLookupContext(lookupContext);
         try {
-            visitMemento(visitor, exceptionHandler);
+            visitMemento("deserialization", mementoData, visitor, exceptionHandler);
         } finally {
             serializer.unsetLookupContext();
         }
@@ -355,73 +393,33 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
     }
     
     protected interface Visitor {
-        public void visit(String contents, BrooklynObjectType type, String subPath) throws Exception;
+        public void visit(BrooklynObjectType type, String id, String contents) throws Exception;
     }
-    protected void visitMemento(final Visitor visitor, final RebindExceptionHandler exceptionHandler) throws IOException {
-        List<String> entitySubPathList;
-        List<String> locationSubPathList;
-        List<String> policySubPathList;
-        List<String> enricherSubPathList;
-        List<String> feedSubPathList;
-        List<String> catalogSubPathList;
-        
-        Stopwatch stopwatch = Stopwatch.createStarted();
-        try {
-            entitySubPathList = objectStore.listContentsWithSubPath("entities");
-            locationSubPathList = objectStore.listContentsWithSubPath("locations");
-            policySubPathList = objectStore.listContentsWithSubPath("policies");
-            enricherSubPathList = objectStore.listContentsWithSubPath("enrichers");
-            feedSubPathList = objectStore.listContentsWithSubPath("feeds");
-            catalogSubPathList = objectStore.listContentsWithSubPath("catalog");
-        } catch (Exception e) {
-            Exceptions.propagateIfFatal(e);
-            exceptionHandler.onLoadMementoFailed(BrooklynObjectType.UNKNOWN, "Failed to list files", e);
-            throw new IllegalStateException("Failed to list memento files in "+objectStore, e);
-        }
-
-        LOG.debug("Loaded rebind lists; took {}: {} entities, {} locations, {} policies, {} enrichers, {} feeds, {} catalog items; from {}", new Object[]{
-            Time.makeTimeStringRounded(stopwatch),
-            entitySubPathList.size(), locationSubPathList.size(), policySubPathList.size(), enricherSubPathList.size(), 
-            feedSubPathList.size(), catalogSubPathList.size(),
-            objectStore.getSummaryName() });
-
+    
+    protected void visitMemento(final String phase, final BrooklynMementoRawData rawData, final Visitor visitor, final RebindExceptionHandler exceptionHandler) {
         List<ListenableFuture<?>> futures = Lists.newArrayList();
         
         class VisitorWrapper implements Runnable {
-            private final String subPath;
             private final BrooklynObjectType type;
-            public VisitorWrapper(String subPath, BrooklynObjectType type) {
-                this.subPath = subPath;
+            private final Map.Entry<String,String> objectIdAndData;
+            public VisitorWrapper(BrooklynObjectType type, Map.Entry<String,String> objectIdAndData) {
                 this.type = type;
+                this.objectIdAndData = objectIdAndData;
             }
             public void run() {
                 try {
-                    String contents = read(subPath);
-                    visitor.visit(contents, type, subPath);
+                    visitor.visit(type, objectIdAndData.getKey(), objectIdAndData.getValue());
                 } catch (Exception e) {
                     Exceptions.propagateIfFatal(e);
-                    exceptionHandler.onLoadMementoFailed(type, "Memento "+subPath, e);
+                    exceptionHandler.onLoadMementoFailed(type, "memento "+objectIdAndData.getKey()+" "+phase+" error", e);
                 }
             }
         }
         
-        for (final String subPath : entitySubPathList) {
-            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.ENTITY)));
-        }
-        for (final String subPath : locationSubPathList) {
-            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.LOCATION)));
-        }
-        for (final String subPath : policySubPathList) {
-            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.POLICY)));
-        }
-        for (final String subPath : enricherSubPathList) {
-            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.ENRICHER)));
-        }
-        for (final String subPath : feedSubPathList) {
-            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.FEED)));
-        }
-        for (final String subPath : catalogSubPathList) {
-            futures.add(executor.submit(new VisitorWrapper(subPath, BrooklynObjectType.CATALOG_ITEM)));
+        for (BrooklynObjectType type: PERSISTED_OBJECT_TYPES_IN_ORDER) {
+            for (final Map.Entry<String,String> entry : rawData.getObjectsOfType(type).entrySet()) {
+                futures.add(executor.submit(new VisitorWrapper(type, entry)));
+            }
         }
 
         try {
@@ -439,7 +437,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                     } catch (InterruptedException e2) {
                         throw Exceptions.propagate(e2);
                     } catch (ExecutionException e2) {
-                        LOG.warn("Problem loading memento manifest", e2);
+                        LOG.warn("Problem loading memento ("+phase+"): "+e2, e2);
                         exceptions.add(e2);
                     }
                     future.cancel(true);
@@ -449,7 +447,7 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
                 throw Exceptions.propagate(e);
             } else {
                 // Normally there should be at lesat one failure; otherwise all.get() would not have failed.
-                throw new CompoundRuntimeException("Problem loading mementos", exceptions);
+                throw new CompoundRuntimeException("Problem loading mementos ("+phase+")", exceptions);
             }
         }
     }
@@ -476,23 +474,10 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
             Stopwatch stopwatch = Stopwatch.createStarted();
             List<ListenableFuture<?>> futures = Lists.newArrayList();
             
-            for (Map.Entry<String, String> entry : newMemento.getEntities().entrySet()) {
-                futures.add(asyncPersist("entities", BrooklynObjectType.ENTITY, entry.getKey(), entry.getValue(), exceptionHandler));
-            }
-            for (Map.Entry<String, String> entry : newMemento.getLocations().entrySet()) {
-                futures.add(asyncPersist("locations", BrooklynObjectType.LOCATION, entry.getKey(), entry.getValue(), exceptionHandler));
-            }
-            for (Map.Entry<String, String> entry : newMemento.getPolicies().entrySet()) {
-                futures.add(asyncPersist("policies", BrooklynObjectType.POLICY, entry.getKey(), entry.getValue(), exceptionHandler));
-            }
-            for (Map.Entry<String, String> entry : newMemento.getEnrichers().entrySet()) {
-                futures.add(asyncPersist("enrichers", BrooklynObjectType.ENRICHER, entry.getKey(), entry.getValue(), exceptionHandler));
-            }
-            for (Map.Entry<String, String> entry : newMemento.getFeeds().entrySet()) {
-                futures.add(asyncPersist("feeds", BrooklynObjectType.FEED, entry.getKey(), entry.getValue(), exceptionHandler));
-            }
-            for (Map.Entry<String, String> entry : newMemento.getCatalogItems().entrySet()) {
-                futures.add(asyncPersist("catalog", BrooklynObjectType.CATALOG_ITEM, entry.getKey(), entry.getValue(), exceptionHandler));
+            for (BrooklynObjectType type: PERSISTED_OBJECT_TYPES_IN_ORDER) {
+                for (Map.Entry<String, String> entry : newMemento.getObjectsOfType(type).entrySet()) {
+                    futures.add(asyncPersist(type.getSubPathName(), type, entry.getKey(), entry.getValue(), exceptionHandler));
+                }
             }
             
             try {
@@ -561,42 +546,15 @@ public class BrooklynMementoPersisterToObjectStore implements BrooklynMementoPer
             Stopwatch stopwatch = Stopwatch.createStarted();
             List<ListenableFuture<?>> futures = Lists.newArrayList();
             
-            for (EntityMemento entity : delta.entities()) {
-                futures.add(asyncPersist("entities", entity, exceptionHandler));
-            }
-            for (LocationMemento location : delta.locations()) {
-                futures.add(asyncPersist("locations", location, exceptionHandler));
-            }
-            for (PolicyMemento policy : delta.policies()) {
-                futures.add(asyncPersist("policies", policy, exceptionHandler));
-            }
-            for (EnricherMemento enricher : delta.enrichers()) {
-                futures.add(asyncPersist("enrichers", enricher, exceptionHandler));
-            }
-            for (FeedMemento feed : delta.feeds()) {
-                futures.add(asyncPersist("feeds", feed, exceptionHandler));
-            }
-            for (CatalogItemMemento catalogItem : delta.catalogItems()) {
-                futures.add(asyncPersist("catalog", catalogItem, exceptionHandler));
-            }
-            
-            for (String id : delta.removedEntityIds()) {
-                futures.add(asyncDelete("entities", id, exceptionHandler));
-            }
-            for (String id : delta.removedLocationIds()) {
-                futures.add(asyncDelete("locations", id, exceptionHandler));
-            }
-            for (String id : delta.removedPolicyIds()) {
-                futures.add(asyncDelete("policies", id, exceptionHandler));
-            }
-            for (String id : delta.removedEnricherIds()) {
-                futures.add(asyncDelete("enrichers", id, exceptionHandler));
-            }
-            for (String id : delta.removedFeedIds()) {
-                futures.add(asyncDelete("feeds", id, exceptionHandler));
+            for (BrooklynObjectType type: PERSISTED_OBJECT_TYPES_IN_ORDER) {
+                for (Memento entity : delta.getObjectsOfType(type)) {
+                    futures.add(asyncPersist(type.getSubPathName(), entity, exceptionHandler));
+                }
             }
-            for (String id : delta.removedCatalogItemIds()) {
-                futures.add(asyncDelete("catalog", id, exceptionHandler));
+            for (BrooklynObjectType type: PERSISTED_OBJECT_TYPES_IN_ORDER) {
+                for (String id : delta.getRemovedObjectsOfType(type)) {
+                    futures.add(asyncDelete(type.getSubPathName(), id, exceptionHandler));
+                }
             }
             
             try {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
index 8dd8091..852885a 100644
--- a/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
+++ b/core/src/main/java/brooklyn/management/ha/HighAvailabilityManagerImpl.java
@@ -57,6 +57,7 @@ import brooklyn.util.task.ScheduledTask;
 import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 import brooklyn.util.time.Duration;
+import brooklyn.util.time.Time;
 
 import com.google.common.annotations.Beta;
 import com.google.common.annotations.VisibleForTesting;
@@ -634,11 +635,11 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
             String message = "Management node "+ownNodeId+" detected ";
             if (weAreNewMaster) message += "we should be master, changing from ";
             else message += "master change, from ";
-            message +=currMasterNodeId + " (" + (currMasterNodeRecord==null ? "?" : currMasterNodeRecord.getRemoteTimestamp()) + ")"
+            message +=currMasterNodeId + " (" + (currMasterNodeRecord==null ? "?" : timestampString(currMasterNodeRecord.getRemoteTimestamp())) + ")"
                 + " to "
                 + (newMasterNodeId == null ? "<none>" :
                     (weAreNewMaster ? "us " : "")
-                    + newMasterNodeId + " (" + newMasterNodeRecord.getRemoteTimestamp() + ")" 
+                    + newMasterNodeId + " (" + timestampString(newMasterNodeRecord.getRemoteTimestamp()) + ")" 
                     + (newMasterNodeUri!=null ? " "+newMasterNodeUri : "")  );
             LOG.warn(message);
         }
@@ -649,6 +650,11 @@ public class HighAvailabilityManagerImpl implements HighAvailabilityManager {
         }
     }
     
+    private static String timestampString(Long remoteTimestamp) {
+        if (remoteTimestamp==null) return null;
+        return remoteTimestamp+" / "+Time.makeTimeStringRounded( Duration.sinceUtc(remoteTimestamp))+" ago";
+    }
+
     protected void promoteToMaster() {
         if (!running) {
             LOG.warn("Ignoring promote-to-master request, as HighAvailabilityManager is no longer running");

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
index e070bd0..045d91c8 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindFailuresTest.java
@@ -179,8 +179,7 @@ public class RebindFailuresTest extends RebindTestFixtureWithApp {
         }
         
         // exception handler should have been told about failure
-        // two exceptions: one for loadMementoManifest; one for loadMemento
-        assertEquals(exceptionHandler.loadMementoFailures.size(), 2, "exceptions="+exceptionHandler.loadMementoFailures);
+        assertFalse(exceptionHandler.loadMementoFailures.isEmpty(), "exceptions="+exceptionHandler.loadMementoFailures);
     }
 
     protected void assertFailureRebindingError(Exception e) {

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
index 1466405..60d245a 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RebindTestFixture.java
@@ -173,7 +173,7 @@ public abstract class RebindTestFixture<T extends StartableApplication> {
                 ((ManagementContextInternal)newManagementContext).getBrooklynProperties(),
                 classLoader);
         RebindExceptionHandler exceptionHandler = new RecordingRebindExceptionHandler(RebindManager.RebindFailureMode.FAIL_AT_END, RebindManager.RebindFailureMode.FAIL_AT_END);
-        BrooklynMementoManifest mementoManifest = persister.loadMementoManifest(exceptionHandler);
+        BrooklynMementoManifest mementoManifest = persister.loadMementoManifest(null, exceptionHandler);
         persister.stop(false);
         return mementoManifest;
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/test/java/brooklyn/entity/rebind/RecordingRebindExceptionHandler.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/RecordingRebindExceptionHandler.java b/core/src/test/java/brooklyn/entity/rebind/RecordingRebindExceptionHandler.java
index d2c8b8c..41cead5 100644
--- a/core/src/test/java/brooklyn/entity/rebind/RecordingRebindExceptionHandler.java
+++ b/core/src/test/java/brooklyn/entity/rebind/RecordingRebindExceptionHandler.java
@@ -43,7 +43,7 @@ public class RecordingRebindExceptionHandler extends RebindExceptionHandlerImpl
 
     @Override
     public void onLoadMementoFailed(BrooklynObjectType type, String msg, Exception e) {
-        loadMementoFailures.add(new IllegalStateException("problem loading "+type+" memento: "+msg, e));
+        loadMementoFailures.add(new IllegalStateException("problem loading "+type.toCamelCase()+" memento: "+msg, e));
         super.onLoadMementoFailed(type, msg, e);
     }
     
@@ -71,13 +71,13 @@ public class RecordingRebindExceptionHandler extends RebindExceptionHandlerImpl
     
     @Override
     public void onRebindFailed(BrooklynObjectType type, BrooklynObject instance, Exception e) {
-        rebindFailures.put(instance, new IllegalStateException("problem rebinding "+type+" "+instance.getId()+" ("+instance+")", e));
+        rebindFailures.put(instance, new IllegalStateException("problem rebinding "+type.toCamelCase()+" "+instance.getId()+" ("+instance+")", e));
         super.onRebindFailed(type, instance, e);
     }
 
     @Override
     public void onManageFailed(BrooklynObjectType type, BrooklynObject instance, Exception e) {
-        manageFailures.put(instance, new IllegalStateException("problem managing "+type+" "+instance.getId()+" ("+instance+")", e));
+        manageFailures.put(instance, new IllegalStateException("problem managing "+type.toCamelCase()+" "+instance.getId()+" ("+instance+")", e));
         super.onManageFailed(type, instance, e);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/be5aac25/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
index 0ae2a9e..3306a32 100644
--- a/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
+++ b/core/src/test/java/brooklyn/entity/rebind/persister/BrooklynMementoPersisterTestFixture.java
@@ -111,7 +111,7 @@ public abstract class BrooklynMementoPersisterTestFixture {
         rebindContext.registerEntity(app.getId(), app);
         rebindContext.registerEntity(entity.getId(), entity);
         
-        BrooklynMemento reloadedMemento = persister.loadMemento(lookupContext, failFast);
+        BrooklynMemento reloadedMemento = persister.loadMemento(null, lookupContext, failFast);
         return reloadedMemento;
     }