You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@usergrid.apache.org by sn...@apache.org on 2016/07/07 16:11:49 UTC

[16/16] usergrid git commit: Fixing logic around entity delete to ensure that UniqueValues and ReservationCache are both cleaned up.

Fixing logic around entity delete to ensure that UniqueValues and ReservationCache are both cleaned up.


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

Branch: refs/heads/usergrid-1268-akka-211
Commit: e6c59ee3fdc77ce32874ca6b2e0f5fc22a205a20
Parents: 917f0e3
Author: Dave Johnson <sn...@apache.org>
Authored: Thu Jul 7 12:12:42 2016 -0400
Committer: Dave Johnson <sn...@apache.org>
Committed: Thu Jul 7 12:12:42 2016 -0400

----------------------------------------------------------------------
 .../corepersistence/CpEntityManager.java        |  2 +-
 .../resources/usergrid-custom-test.properties   | 13 ++++
 .../actorsystem/ActorSystemManagerImpl.java     |  5 +-
 .../exception/WriteUniqueVerifyException.java   |  2 +-
 .../mvcc/stage/delete/MarkCommit.java           | 34 +++++++-
 .../mvcc/stage/write/WriteUniqueVerify.java     |  3 +-
 .../uniquevalues/ReservationCache.java          |  5 ++
 .../uniquevalues/ReservationCacheActor.java     | 13 +++-
 .../uniquevalues/UniqueValueActor.java          | 81 +++++++++++---------
 .../uniquevalues/UniqueValuesService.java       | 15 ++++
 .../uniquevalues/UniqueValuesServiceImpl.java   | 34 ++++++--
 .../uniquevalues/UniqueValuesTable.java         | 16 ++--
 .../uniquevalues/UniqueValuesTableImpl.java     |  8 ++
 13 files changed, 170 insertions(+), 61 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManager.java
----------------------------------------------------------------------
diff --git a/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManager.java b/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManager.java
index 7006f44..41613ac 100644
--- a/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManager.java
+++ b/stack/core/src/main/java/org/apache/usergrid/corepersistence/CpEntityManager.java
@@ -2863,7 +2863,7 @@ public class CpEntityManager implements EntityManager {
             throws DuplicateUniquePropertyExistsException {
 
         // we may have multiple conflicts, but caller expects only one
-        Map<String, Field> violiations = wuve.getVioliations();
+        Map<String, Field> violiations = wuve.getViolations();
 
         if ( violiations != null ) {
             Field conflict = violiations.get( violiations.keySet().iterator().next() );

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/core/src/test/resources/usergrid-custom-test.properties
----------------------------------------------------------------------
diff --git a/stack/core/src/test/resources/usergrid-custom-test.properties b/stack/core/src/test/resources/usergrid-custom-test.properties
index 0fa9573..dd6612f 100644
--- a/stack/core/src/test/resources/usergrid-custom-test.properties
+++ b/stack/core/src/test/resources/usergrid-custom-test.properties
@@ -34,3 +34,16 @@ elasticsearch.buffer_timeout=1
 # This property is required to be set and cannot be defaulted anywhere
 usergrid.cluster_name=usergrid
 
+# --- Start: Usergrid cluster/actor system settings
+
+usergrid.cluster.enabled=true
+usergrid.cluster.hostname=localhost
+usergrid.cluster.region.local=us-east
+usergrid.cluster.region.list=us-east
+usergrid.cluster.seeds=us-east\:localhost
+
+collection.uniquevalues.actors=300
+collection.uniquevalues.authoritative.region=us-east
+
+# --- End: Usergrid cluster/actor system settings
+

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/actorsystem/src/main/java/org/apache/usergrid/persistence/actorsystem/ActorSystemManagerImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/actorsystem/src/main/java/org/apache/usergrid/persistence/actorsystem/ActorSystemManagerImpl.java b/stack/corepersistence/actorsystem/src/main/java/org/apache/usergrid/persistence/actorsystem/ActorSystemManagerImpl.java
index 099b875..8399979 100644
--- a/stack/corepersistence/actorsystem/src/main/java/org/apache/usergrid/persistence/actorsystem/ActorSystemManagerImpl.java
+++ b/stack/corepersistence/actorsystem/src/main/java/org/apache/usergrid/persistence/actorsystem/ActorSystemManagerImpl.java
@@ -282,12 +282,13 @@ public class ActorSystemManagerImpl implements ActorSystemManager {
             String region = currentRegion;
 
             List<String> seeds = getSeedsByRegion().get( region );
-            int lastColon = seeds.get(0).lastIndexOf(":") + 1;
-            final Integer regionPort = Integer.parseInt( seeds.get(0).substring( lastColon ));
 
             logger.info( "Akka Config for region {} is:\n" + "   Hostname {}\n" + "   Seeds {}\n",
                 region, hostname, seeds );
 
+            int lastColon = seeds.get(0).lastIndexOf(":") + 1;
+            final Integer regionPort = Integer.parseInt( seeds.get(0).substring( lastColon ));
+
             Map<String, Object> configMap = new HashMap<String, Object>() {{
 
                 put( "akka", new HashMap<String, Object>() {{

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/exception/WriteUniqueVerifyException.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/exception/WriteUniqueVerifyException.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/exception/WriteUniqueVerifyException.java
index 81165fa..a223a9e 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/exception/WriteUniqueVerifyException.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/exception/WriteUniqueVerifyException.java
@@ -40,7 +40,7 @@ public class WriteUniqueVerifyException extends CollectionRuntimeException {
     /**
      * Get map of Fields in violation, keyed by field name.
      */
-    public Map<String, Field> getVioliations() {
+    public Map<String, Field> getViolations() {
         return violations;
     }
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/delete/MarkCommit.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/delete/MarkCommit.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/delete/MarkCommit.java
index 460c271..70c785e 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/delete/MarkCommit.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/delete/MarkCommit.java
@@ -21,6 +21,10 @@ package org.apache.usergrid.persistence.collection.mvcc.stage.delete;
 
 import java.util.UUID;
 
+import org.apache.usergrid.persistence.actorsystem.ActorSystemFig;
+import org.apache.usergrid.persistence.collection.uniquevalues.UniqueValueException;
+import org.apache.usergrid.persistence.collection.uniquevalues.UniqueValuesFig;
+import org.apache.usergrid.persistence.collection.uniquevalues.UniqueValuesService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,12 +64,19 @@ public class MarkCommit implements Action1<CollectionIoEvent<MvccEntity>> {
     private final SerializationFig serializationFig;
     private final UniqueValueSerializationStrategy uniqueValueStrat;
     private final Keyspace keyspace;
+    private final ActorSystemFig actorSystemFig;
+    private final UniqueValuesFig uniqueValuesFig;
+    private final UniqueValuesService uniqueValuesService;
 
 
     @Inject
     public MarkCommit( final MvccLogEntrySerializationStrategy logStrat,
                        final MvccEntitySerializationStrategy entityStrat,
-                       final UniqueValueSerializationStrategy uniqueValueStrat, final SerializationFig serializationFig,
+                       final UniqueValueSerializationStrategy uniqueValueStrat,
+                       final SerializationFig serializationFig,
+                       final ActorSystemFig actorSystemFig,
+                       final UniqueValuesFig uniqueValuesFig,
+                       final UniqueValuesService uniqueValuesService,
                        final Keyspace keyspace ) {
 
 
@@ -76,6 +87,9 @@ public class MarkCommit implements Action1<CollectionIoEvent<MvccEntity>> {
         this.entityStrat = entityStrat;
         this.serializationFig = serializationFig;
         this.uniqueValueStrat = uniqueValueStrat;
+        this.actorSystemFig = actorSystemFig;
+        this.uniqueValuesFig = uniqueValuesFig;
+        this.uniqueValuesService = uniqueValuesService;
         this.keyspace = keyspace;
     }
 
@@ -111,6 +125,24 @@ public class MarkCommit implements Action1<CollectionIoEvent<MvccEntity>> {
         catch ( ConnectionException e ) {
             throw new RuntimeException( "Unable to mark entry as deleted" );
         }
+
+        // actorSystemFig may be null in testing
+        if ( actorSystemFig != null && actorSystemFig.getEnabled() ) {
+
+            String region = idIoEvent.getRegion();
+            if ( region == null ) {
+                region = uniqueValuesFig.getAuthoritativeRegion();
+            }
+            if ( region == null ) {
+                region = actorSystemFig.getRegionLocal();
+            }
+
+            try {
+                uniqueValuesService.releaseUniqueValues( applicationScope, entityId, version, region );
+            } catch (UniqueValueException e) {
+                throw new RuntimeException( "Unable to release unique values for entity " + entityId );
+            }
+        }
     }
 }
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/WriteUniqueVerify.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/WriteUniqueVerify.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/WriteUniqueVerify.java
index e7ba967..1f86440 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/WriteUniqueVerify.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/mvcc/stage/write/WriteUniqueVerify.java
@@ -219,8 +219,7 @@ public class WriteUniqueVerify implements Action1<CollectionIoEvent<MvccEntity>>
                 logger.trace("Pre-write unique violations found, raising exception before executing first write");
             }
 
-            throw new WriteUniqueVerifyException(mvccEntity, scope,
-                preWriteUniquenessViolations );
+            throw new WriteUniqueVerifyException(mvccEntity, scope, preWriteUniquenessViolations );
         }
 
         //short circuit nothing to do

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCache.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCache.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCache.java
index f1e68b2..8e2e51e 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCache.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCache.java
@@ -72,6 +72,11 @@ public class ReservationCache {
         cache.invalidate( cancellation.getConsistentHashKey() );
     }
 
+    public void cancelReservation( UniqueValueActor.Response response ) {
+        if ( ttl == 0 ) { return; }
+        cache.invalidate( response.getConsistentHashKey() );
+    }
+
     public CacheStats getStats() {
         return cache.stats();
     }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCacheActor.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCacheActor.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCacheActor.java
index 51f5c8c..158b099 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCacheActor.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/ReservationCacheActor.java
@@ -54,12 +54,19 @@ public class ReservationCacheActor extends UntypedActor {
             }
 
         } else if ( msg instanceof UniqueValueActor.Cancellation ) {
-            UniqueValueActor.Cancellation can = (UniqueValueActor.Cancellation)msg;
+            UniqueValueActor.Cancellation can = (UniqueValueActor.Cancellation) msg;
             ReservationCache.getInstance().cancelReservation( can );
 
-            if ( ++cancellationCount % 10 == 0 ) {
-                logger.info("Received {} cancellations", cancellationCount);
+            if (++cancellationCount % 10 == 0) {
+                logger.info( "Received {} cancellations", cancellationCount );
             }
+            logger.debug("Removing cancelled {} from reservation cache", can.getConsistentHashKey());
+
+        } else if ( msg instanceof UniqueValueActor.Response ) {
+            UniqueValueActor.Response response = (UniqueValueActor.Response) msg;
+            ReservationCache.getInstance().cancelReservation( response );
+
+            logger.info("Removing completed {} from reservation cache", response.getConsistentHashKey());
 
         } else if (msg instanceof DistributedPubSubMediator.SubscribeAck) {
             logger.debug( "subscribing" );

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValueActor.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValueActor.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValueActor.java
index a14c63e..501037f 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValueActor.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValueActor.java
@@ -16,10 +16,7 @@
  */
 package org.apache.usergrid.persistence.collection.uniquevalues;
 
-import akka.actor.ActorRef;
 import akka.actor.UntypedActor;
-import akka.cluster.pubsub.DistributedPubSub;
-import akka.cluster.pubsub.DistributedPubSubMediator;
 import org.apache.commons.lang3.RandomStringUtils;
 import org.apache.usergrid.persistence.actorsystem.ActorSystemManager;
 import org.apache.usergrid.persistence.core.scope.ApplicationScope;
@@ -51,8 +48,6 @@ public class UniqueValueActor extends UntypedActor {
         // TODO: is there a way to avoid this ugly kludge? see also: ClusterSingletonRouter
         this.table = UniqueValuesServiceImpl.injector.getInstance( UniqueValuesTable.class );
         this.actorSystemManager = UniqueValuesServiceImpl.injector.getInstance( ActorSystemManager.class );
-
-        //logger.info("UniqueValueActor {} is live with table {}", name, table);
     }
 
     @Override
@@ -77,24 +72,27 @@ public class UniqueValueActor extends UntypedActor {
 
                 if ( owner != null && owner.equals( res.getOwner() )) {
                     // sender already owns this unique value
-                    getSender().tell( new Response( Response.Status.IS_UNIQUE ), getSender() );
+                    getSender().tell( new Response( Response.Status.IS_UNIQUE, res.getConsistentHashKey() ),
+                        getSender() );
                     return;
 
                 } else if ( owner != null && !owner.equals( res.getOwner() )) {
                     // tell sender value is not unique
-                    getSender().tell( new Response( Response.Status.NOT_UNIQUE ), getSender() );
+                    getSender().tell( new Response( Response.Status.NOT_UNIQUE, res.getConsistentHashKey() ),
+                        getSender() );
                     return;
                 }
 
                 table.reserve( res.getApplicationScope(), res.getOwner(), res.getOwnerVersion(), res.getField() );
 
-                getSender().tell( new Response( Response.Status.IS_UNIQUE ), getSender() );
+                getSender().tell( new Response( Response.Status.IS_UNIQUE, res.getConsistentHashKey() ),
+                    getSender() );
 
                 actorSystemManager.publishToAllRegions( "content", new Reservation( res ), getSelf() );
 
             } catch (Throwable t) {
 
-                getSender().tell( new Response( Response.Status.ERROR ), getSender() );
+                getSender().tell( new Response( Response.Status.ERROR, res.getConsistentHashKey() ), getSender() );
                 logger.error( "Error processing request", t );
 
 
@@ -112,23 +110,29 @@ public class UniqueValueActor extends UntypedActor {
 
                 if ( owner != null && !owner.equals( con.getOwner() )) {
                     // cannot reserve, somebody else owns the unique value
-                    getSender().tell( new Response( Response.Status.NOT_UNIQUE ), getSender() );
+                    Response response  = new Response( Response.Status.NOT_UNIQUE, con.getConsistentHashKey());
+                    getSender().tell( response, getSender() );
+                    actorSystemManager.publishToAllRegions( "content", response, getSelf() );
                     return;
 
                 } else if ( owner == null ) {
                     // cannot commit without first reserving
-                    getSender().tell( new Response( Response.Status.BAD_REQUEST ), getSender() );
+                    Response response  = new Response( Response.Status.BAD_REQUEST, con.getConsistentHashKey());
+                    getSender().tell( response, getSender() );
+                    actorSystemManager.publishToAllRegions( "content", response, getSelf() );
                     return;
                 }
 
                 table.confirm( con.getApplicationScope(), con.getOwner(), con.getOwnerVersion(), con.getField() );
 
-                getSender().tell( new Response( Response.Status.IS_UNIQUE ), getSender() );
+                Response response = new Response( Response.Status.IS_UNIQUE, con.getConsistentHashKey() );
+                getSender().tell( response, getSender() );
 
-                actorSystemManager.publishToAllRegions( "content", new Reservation( con ), getSelf() );
+                actorSystemManager.publishToAllRegions( "content", response, getSelf() );
 
             } catch (Throwable t) {
-                getSender().tell( new Response( Response.Status.ERROR ), getSender() );
+                getSender().tell( new Response( Response.Status.ERROR, con.getConsistentHashKey() ),
+                    getSender() );
                 logger.error( "Error processing request", t );
 
             } finally {
@@ -144,23 +148,34 @@ public class UniqueValueActor extends UntypedActor {
 
                 if ( owner != null && !owner.equals( can.getOwner() )) {
                     // cannot cancel, somebody else owns the unique value
-                    getSender().tell( new Response( Response.Status.NOT_UNIQUE ), getSender() );
+                    getSender().tell( new Response( Response.Status.NOT_UNIQUE, can.getConsistentHashKey() ),
+                        getSender() );
                     return;
 
                 } else if ( owner == null ) {
+
                     // cannot cancel unique value that does not exist
-                    getSender().tell( new Response( Response.Status.BAD_REQUEST ), getSender() );
+                    getSender().tell( new Response( Response.Status.BAD_REQUEST, can.getConsistentHashKey() ),
+                        getSender() );
+
+                    // unique value record may have already been cleaned up, also clear cache
+                    actorSystemManager.publishToAllRegions( "content", new Cancellation( can ), getSelf() );
+
                     return;
                 }
 
-                table.confirm( can.getApplicationScope(), can.getOwner(), can.getOwnerVersion(), can.getField() );
+                table.cancel( can.getApplicationScope(), can.getOwner(), can.getOwnerVersion(), can.getField() );
+
+                logger.debug("Removing {} from unique values table", can.getConsistentHashKey());
 
-                getSender().tell( new Response( Response.Status.SUCCESS ), getSender() );
+                getSender().tell( new Response( Response.Status.SUCCESS, can.getConsistentHashKey() ),
+                    getSender() );
 
-                actorSystemManager.publishToAllRegions( "content", new Reservation( can ), getSelf() );
+                actorSystemManager.publishToAllRegions( "content", new Cancellation( can ), getSelf() );
 
             } catch (Throwable t) {
-                getSender().tell( new Response( Response.Status.ERROR ), getSender() );
+                getSender().tell( new Response( Response.Status.ERROR, can.getConsistentHashKey() ),
+                    getSender() );
                 logger.error( "Error processing request", t );
             }
 
@@ -188,7 +203,7 @@ public class UniqueValueActor extends UntypedActor {
             this.field = field;
 
             StringBuilder sb = new StringBuilder();
-            sb.append( applicationScope.getApplication() );
+            sb.append( applicationScope.getApplication().getUuid() );
             sb.append(":");
             sb.append( owner.getType() );
             sb.append(":");
@@ -198,22 +213,7 @@ public class UniqueValueActor extends UntypedActor {
             this.consistentHashKey = sb.toString();
         }
         public Request( Request req ) {
-
-            this.applicationScope = req.applicationScope;
-            this.owner = req.owner;
-            this.ownerVersion = req.ownerVersion;
-            this.field = req.field;
-
-            StringBuilder sb = new StringBuilder();
-            sb.append( req.applicationScope.getApplication() );
-            sb.append(":");
-            sb.append( req.owner.getType() );
-            sb.append(":");
-            sb.append( req.field.getName() );
-            sb.append(":");
-            sb.append( req.field.getValue().toString() );
-            this.consistentHashKey = sb.toString();
-
+            this( req.getApplicationScope(), req.getOwner(), req.getOwnerVersion(), req.getField() );
         }
         public ApplicationScope getApplicationScope() {
             return applicationScope;
@@ -238,13 +238,18 @@ public class UniqueValueActor extends UntypedActor {
     public static class Response implements Serializable {
         public enum Status { IS_UNIQUE, NOT_UNIQUE, SUCCESS, ERROR, BAD_REQUEST }
         final Status status;
+        final String consistentHashKey;
 
-        public Response(Status status) {
+        public Response(Status status, String consistentHashKey ) {
             this.status = status;
+            this.consistentHashKey = consistentHashKey;
         }
         public Status getStatus() {
             return status;
         }
+        public String getConsistentHashKey() {
+            return consistentHashKey;
+        }
     }
 
     public static class Reservation extends Request implements Serializable {

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesService.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesService.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesService.java
index e9a9f50..5deddd6 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesService.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesService.java
@@ -20,8 +20,11 @@ package org.apache.usergrid.persistence.collection.uniquevalues;
 
 
 import org.apache.usergrid.persistence.actorsystem.RouterProducer;
+import org.apache.usergrid.persistence.collection.serialization.UniqueValue;
 import org.apache.usergrid.persistence.core.scope.ApplicationScope;
 import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.entity.Id;
+import org.apache.usergrid.persistence.model.field.Field;
 
 import java.util.UUID;
 
@@ -55,4 +58,16 @@ public interface UniqueValuesService extends RouterProducer {
      */
     void confirmUniqueValues( ApplicationScope scope, Entity entity, UUID version , String region )
         throws UniqueValueException;
+
+    /**
+     * Release unique values held by an entity.
+     *
+     * @param scope Application scope of entity.
+     * @param entityId Id of Entity with unique values to be released
+     * @param version Version of entity.
+     * @param region Authoritative Region to be used for this entity or null to use current region.
+     * @throws UniqueValueException if unique values cannot be reserved.
+     */
+    void releaseUniqueValues( ApplicationScope scope, Id entityId, UUID version, String region )
+        throws UniqueValueException;
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesServiceImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesServiceImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesServiceImpl.java
index fcee8fa..4102090 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesServiceImpl.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesServiceImpl.java
@@ -35,8 +35,10 @@ import com.google.inject.Singleton;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.usergrid.persistence.actorsystem.ActorSystemManager;
 import org.apache.usergrid.persistence.actorsystem.GuiceActorProducer;
+import org.apache.usergrid.persistence.collection.serialization.UniqueValue;
 import org.apache.usergrid.persistence.core.scope.ApplicationScope;
 import org.apache.usergrid.persistence.model.entity.Entity;
+import org.apache.usergrid.persistence.model.entity.Id;
 import org.apache.usergrid.persistence.model.field.Field;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -44,6 +46,7 @@ import scala.concurrent.Await;
 import scala.concurrent.Future;
 
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
@@ -109,13 +112,12 @@ public class UniqueValuesServiceImpl implements UniqueValuesService {
                     try {
                         cancelUniqueField( scope, entity, version, field, region );
                     } catch (Throwable ignored) {
-                        logger.debug( "Error canceling unique field", ignored );
+                        logger.error( "Error canceling unique field", ignored );
                     }
                 }
             }
             throw e;
         }
-
     }
 
 
@@ -149,6 +151,21 @@ public class UniqueValuesServiceImpl implements UniqueValuesService {
     }
 
 
+    @Override
+    public void releaseUniqueValues(ApplicationScope scope, Id entityId, UUID version, String region)
+        throws UniqueValueException {
+
+        ready();
+
+        Iterator<UniqueValue> iterator = table.getUniqueValues( scope, entityId );
+
+        while ( iterator.hasNext() ) {
+            UniqueValue uniqueValue = iterator.next();
+            cancelUniqueField( scope, entityId, uniqueValue.getEntityVersion(), uniqueValue.getField(), region );
+        }
+    }
+
+
     private void reserveUniqueField(
         ApplicationScope scope, Entity entity, UUID version, Field field, String region ) throws UniqueValueException {
 
@@ -177,11 +194,18 @@ public class UniqueValuesServiceImpl implements UniqueValuesService {
     }
 
 
-    private void cancelUniqueField(
-        ApplicationScope scope, Entity entity, UUID version, Field field, String region ) throws UniqueValueException {
+    private void cancelUniqueField( ApplicationScope scope,
+        Entity entity, UUID version, Field field, String region ) throws UniqueValueException {
+
+        cancelUniqueField( scope, entity.getId(), version, field, region );
+    }
+
+
+    private void cancelUniqueField( ApplicationScope scope,
+        Id entityId, UUID version, Field field, String region ) throws UniqueValueException {
 
         UniqueValueActor.Cancellation request = new UniqueValueActor.Cancellation(
-            scope, entity.getId(), version, field );
+            scope, entityId, version, field );
 
         if ( actorSystemManager.getCurrentRegion().equals( region ) ) {
 

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTable.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTable.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTable.java
index 0e69ef7..1bb2534 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTable.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTable.java
@@ -19,24 +19,24 @@
 package org.apache.usergrid.persistence.collection.uniquevalues;
 
 import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
+import org.apache.usergrid.persistence.collection.serialization.UniqueValue;
 import org.apache.usergrid.persistence.core.scope.ApplicationScope;
 import org.apache.usergrid.persistence.model.entity.Id;
 import org.apache.usergrid.persistence.model.field.Field;
 
+import java.util.Iterator;
 import java.util.UUID;
 
 
 public interface UniqueValuesTable {
 
-    Id lookupOwner(
-        ApplicationScope applicationScope, String type, Field field ) throws ConnectionException;
+    Id lookupOwner( ApplicationScope scope, String type, Field field ) throws ConnectionException;
 
-    void reserve(
-        ApplicationScope applicationScope, Id owner, UUID version, Field field ) throws ConnectionException;
+    void reserve( ApplicationScope scope, Id owner, UUID version, Field field ) throws ConnectionException;
 
-    void confirm(
-        ApplicationScope applicationScope, Id owner, UUID version, Field field ) throws ConnectionException;
+    void confirm( ApplicationScope scope, Id owner, UUID version, Field field ) throws ConnectionException;
 
-    void cancel(
-        ApplicationScope applicationScope, Id owner, UUID version, Field field ) throws ConnectionException;
+    void cancel( ApplicationScope scope, Id owner, UUID version, Field field ) throws ConnectionException;
+
+    Iterator<UniqueValue> getUniqueValues(ApplicationScope scope, Id entityId );
 }

http://git-wip-us.apache.org/repos/asf/usergrid/blob/e6c59ee3/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTableImpl.java
----------------------------------------------------------------------
diff --git a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTableImpl.java b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTableImpl.java
index 9cb13be..2cad32c 100644
--- a/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTableImpl.java
+++ b/stack/corepersistence/collection/src/main/java/org/apache/usergrid/persistence/collection/uniquevalues/UniqueValuesTableImpl.java
@@ -33,7 +33,9 @@ import org.apache.usergrid.persistence.model.field.Field;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.UUID;
 
 
@@ -83,4 +85,10 @@ public class UniqueValuesTableImpl implements UniqueValuesTable {
         final MutationBatch write = strat.delete( scope, uv );
         write.execute();
     }
+
+    @Override
+    public Iterator<UniqueValue> getUniqueValues(ApplicationScope scope, Id entityId) {
+        return strat.getAllUniqueFields( scope, entityId );
+    }
+
 }