From 7283752c177a5d38776905f0fa115c2405ae2175 Mon Sep 17 00:00:00 2001
From: "Brian S. O'Neill" <bronee@gmail.com>
Date: Thu, 26 Aug 2010 00:31:18 +0000
Subject: More robust corruption repairs.

---
 .../carbonado/repo/indexed/ManagedIndex.java       | 38 ++++++++++++++++++----
 .../repo/replicated/ReplicationTrigger.java        | 25 +++++++++++++-
 2 files changed, 55 insertions(+), 8 deletions(-)

(limited to 'src/main/java/com/amazon/carbonado/repo')

diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java b/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java
index 1b6fe8d..2bb2f49 100644
--- a/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java
+++ b/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java
@@ -207,7 +207,18 @@ class ManagedIndex<S extends Storable> implements IndexEntryAccessor<S> {
 
     /** Assumes caller is in a transaction */
     boolean deleteIndexEntry(S userStorable) throws PersistException {
-        return makeIndexEntry(userStorable).tryDelete();
+        try {
+            return makeIndexEntry(userStorable).tryDelete();
+        } catch (PersistException e) {
+            Throwable cause = e.getCause();
+            if (cause instanceof IllegalArgumentException) {
+                // Can be caused by a corrupt master record, which is
+                // attempting do assign an illegal value to the index. There's
+                // no way to find the old index entry to delete.
+                return false;
+            }
+            throw e;
+        }
     }
 
     /** Assumes caller is in a transaction */
@@ -219,13 +230,26 @@ class ManagedIndex<S extends Storable> implements IndexEntryAccessor<S> {
     boolean updateIndexEntry(S userStorable, S oldUserStorable) throws PersistException {
         Storable newIndexEntry = makeIndexEntry(userStorable);
 
-        if (oldUserStorable != null) {
-            Storable oldIndexEntry = makeIndexEntry(oldUserStorable);
+        if (oldUserStorable != null) deleteOldEntry: {
+            Storable oldIndexEntry;
+            try {
+                oldIndexEntry = makeIndexEntry(oldUserStorable);
+            } catch (PersistException e) {
+                Throwable cause = e.getCause();
+                if (cause instanceof IllegalArgumentException) {
+                    // Can be caused by a corrupt master record, which is
+                    // attempting do assign an illegal value to the index. There's
+                    // no way to find the old index entry to delete.
+                    break deleteOldEntry;
+                }
+                throw e;
+            }
+
             if (oldIndexEntry.equalPrimaryKeys(newIndexEntry)) {
-                // Index entry didn't change, so nothing to do. If the
-                // index entry has a version, it will lag behind the
-                // master's version until the index entry changes, at which
-                // point the version will again match the master.
+                // Index entry didn't change, so nothing to do. If the index
+                // entry has a version, it will lag behind the master's version
+                // until the index entry changes, at which point the version
+                // will again match the master.
                 return true;
             }
 
diff --git a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java
index 0e9281e..d29d568 100644
--- a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java
+++ b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicationTrigger.java
@@ -18,6 +18,8 @@
 
 package com.amazon.carbonado.repo.replicated;
 
+import java.util.Map;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -343,7 +345,28 @@ class ReplicationTrigger<S extends Storable> extends Trigger<S> {
         // properties. Be sure not to copy nulls from old replica to new
         // replica, in case new non-nullable properties have been added. This
         // is why copyUnequalProperties is called instead of copyAllProperties.
-        replicaEntry.copyUnequalProperties(newReplicaEntry);
+        try {
+            replicaEntry.copyUnequalProperties(newReplicaEntry);
+        } catch (IllegalArgumentException e) {
+            // Some property cannot be copied, so copy one at a time and skip
+            // the broken one.
+            Map<String,Object> propertyMap = replicaEntry.propertyMap();
+            for (Map.Entry<String, Object> entry : propertyMap.entrySet()) {
+                String name = entry.getKey();
+                Object oldValue = entry.getValue();
+                try {
+                    Object newValue = newReplicaEntry.getPropertyValue(name);
+                    if (oldValue == null ? newValue != null : !oldValue.equals(newValue)) {
+                        newReplicaEntry.setPropertyValue(name, oldValue);
+                    }
+                } catch (IllegalArgumentException e2) {
+                    // Skip it.
+                } catch (UnsupportedOperationException e2) {
+                    // Skip it.
+                }
+            }
+        }
+
         // Calling copyAllProperties will skip unsupported independent
         // properties in master, thus preserving old independent property values.
         masterEntry.copyAllProperties(newReplicaEntry);
-- 
cgit v1.2.3