You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by ka...@apache.org on 2013/11/06 00:07:37 UTC

git commit: [HELIX-209] Make UserConfig and RebalancerConfig backward compatible

Updated Branches:
  refs/heads/helix-logical-model 842035a4b -> 26a9413f6


[HELIX-209] Make UserConfig and RebalancerConfig backward compatible


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

Branch: refs/heads/helix-logical-model
Commit: 26a9413f6c04c8b9eabd8f091ddffb76374e5722
Parents: 842035a
Author: Kanak Biscuitwala <ka...@apache.org>
Authored: Tue Nov 5 15:07:25 2013 -0800
Committer: Kanak Biscuitwala <ka...@apache.org>
Committed: Tue Nov 5 15:07:25 2013 -0800

----------------------------------------------------------------------
 helix-core/pom.xml                              |  2 +-
 .../helix/api/accessor/ClusterAccessor.java     | 14 +--
 .../helix/api/accessor/ParticipantAccessor.java |  8 +-
 .../helix/api/accessor/ResourceAccessor.java    | 14 ++-
 .../helix/api/config/NamespacedConfig.java      |  8 +-
 .../rebalancer/context/RebalancerConfig.java    | 30 ++++---
 .../helix/model/ClusterConfiguration.java       | 40 ++++++++-
 .../java/org/apache/helix/model/IdealState.java | 18 ++++
 .../org/apache/helix/model/InstanceConfig.java  | 38 ++++++++-
 .../helix/model/ResourceConfiguration.java      | 41 +++++++++
 .../apache/helix/api/TestNamespacedConfig.java  | 89 ++++++++++++++++++++
 11 files changed, 264 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/pom.xml
----------------------------------------------------------------------
diff --git a/helix-core/pom.xml b/helix-core/pom.xml
index af04d85..99763fb 100644
--- a/helix-core/pom.xml
+++ b/helix-core/pom.xml
@@ -148,7 +148,7 @@ under the License.
     <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
-      <version>r09</version>
+      <version>15.0</version>
     </dependency>
   </dependencies>
   <build>

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/api/accessor/ClusterAccessor.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/api/accessor/ClusterAccessor.java b/helix-core/src/main/java/org/apache/helix/api/accessor/ClusterAccessor.java
index 85b8432..c48f4f2 100644
--- a/helix-core/src/main/java/org/apache/helix/api/accessor/ClusterAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/api/accessor/ClusterAccessor.java
@@ -69,10 +69,10 @@ import org.apache.helix.model.ResourceAssignment;
 import org.apache.helix.model.ResourceConfiguration;
 import org.apache.helix.model.StateModelDefinition;
 import org.apache.log4j.Logger;
-import org.testng.internal.annotations.Sets;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 
 public class ClusterAccessor {
   private static Logger LOG = Logger.getLogger(ClusterAccessor.class);
@@ -247,12 +247,12 @@ public class ClusterAccessor {
     PauseSignal pauseSignal = _accessor.getProperty(_keyBuilder.pause());
     boolean isPaused = pauseSignal != null;
 
-    ClusterConfiguration clusterUserConfig = _accessor.getProperty(_keyBuilder.clusterConfig());
+    ClusterConfiguration clusterConfig = _accessor.getProperty(_keyBuilder.clusterConfig());
     boolean autoJoinAllowed = false;
     UserConfig userConfig;
-    if (clusterUserConfig != null) {
-      userConfig = UserConfig.from(clusterUserConfig);
-      autoJoinAllowed = clusterUserConfig.autoJoinAllowed();
+    if (clusterConfig != null) {
+      userConfig = clusterConfig.getUserConfig();
+      autoJoinAllowed = clusterConfig.autoJoinAllowed();
     } else {
       userConfig = new UserConfig(Scope.cluster(_clusterId));
     }
@@ -384,7 +384,7 @@ public class ClusterAccessor {
     Map<ParticipantId, Participant> participantMap = Maps.newHashMap();
     for (String participantName : instanceConfigMap.keySet()) {
       InstanceConfig instanceConfig = instanceConfigMap.get(participantName);
-      UserConfig userConfig = UserConfig.from(instanceConfig);
+      UserConfig userConfig = instanceConfig.getUserConfig();
       LiveInstance liveInstance = liveInstanceMap.get(participantName);
       Map<String, Message> instanceMsgMap = messageMap.get(participantName);
 
@@ -429,7 +429,7 @@ public class ClusterAccessor {
    */
   public UserConfig readUserConfig() {
     ClusterConfiguration clusterConfig = _accessor.getProperty(_keyBuilder.clusterConfig());
-    return clusterConfig != null ? UserConfig.from(clusterConfig) : null;
+    return clusterConfig != null ? clusterConfig.getUserConfig() : null;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/api/accessor/ParticipantAccessor.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/api/accessor/ParticipantAccessor.java b/helix-core/src/main/java/org/apache/helix/api/accessor/ParticipantAccessor.java
index 83dd53e..07fec9e 100644
--- a/helix-core/src/main/java/org/apache/helix/api/accessor/ParticipantAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/api/accessor/ParticipantAccessor.java
@@ -403,7 +403,7 @@ public class ParticipantAccessor {
   public UserConfig readUserConfig(ParticipantId participantId) {
     InstanceConfig instanceConfig =
         _accessor.getProperty(_keyBuilder.instanceConfig(participantId.stringify()));
-    return instanceConfig != null ? UserConfig.from(instanceConfig) : null;
+    return instanceConfig != null ? instanceConfig.getUserConfig() : null;
   }
 
   /**
@@ -528,8 +528,8 @@ public class ParticipantAccessor {
     RunningInstance runningInstance = null;
     if (liveInstance != null) {
       runningInstance =
-          new RunningInstance(liveInstance.getTypedSessionId(), liveInstance.getTypedHelixVersion(),
-              liveInstance.getProcessId());
+          new RunningInstance(liveInstance.getTypedSessionId(),
+              liveInstance.getTypedHelixVersion(), liveInstance.getProcessId());
     }
 
     Map<MessageId, Message> msgMap = new HashMap<MessageId, Message>();
@@ -568,7 +568,7 @@ public class ParticipantAccessor {
       return null;
     }
 
-    UserConfig userConfig = UserConfig.from(instanceConfig);
+    UserConfig userConfig = instanceConfig.getUserConfig();
     LiveInstance liveInstance = _accessor.getProperty(_keyBuilder.liveInstance(participantName));
 
     Map<String, Message> instanceMsgMap = Collections.emptyMap();

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/api/accessor/ResourceAccessor.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/api/accessor/ResourceAccessor.java b/helix-core/src/main/java/org/apache/helix/api/accessor/ResourceAccessor.java
index 517c8c4..670d167 100644
--- a/helix-core/src/main/java/org/apache/helix/api/accessor/ResourceAccessor.java
+++ b/helix-core/src/main/java/org/apache/helix/api/accessor/ResourceAccessor.java
@@ -416,20 +416,28 @@ public class ResourceAccessor {
       ResourceConfiguration resourceConfiguration, IdealState idealState,
       ExternalView externalView, ResourceAssignment resourceAssignment) {
     UserConfig userConfig;
+    RebalancerContext rebalancerContext = null;
     ResourceType type = ResourceType.DATA;
     if (resourceConfiguration != null) {
-      userConfig = UserConfig.from(resourceConfiguration);
+      userConfig = resourceConfiguration.getUserConfig();
       type = resourceConfiguration.getType();
     } else {
       userConfig = new UserConfig(Scope.resource(resourceId));
     }
     int bucketSize = 0;
     boolean batchMessageMode = false;
-    RebalancerContext rebalancerContext = null;
     if (idealState != null) {
-      rebalancerContext = PartitionedRebalancerContext.from(idealState);
+      if (resourceConfiguration != null) {
+        rebalancerContext =
+            resourceConfiguration.getRebalancerContext(PartitionedRebalancerContext.class);
+      }
+      if (rebalancerContext == null) {
+        // fallback: get rebalancer context from ideal state
+        rebalancerContext = PartitionedRebalancerContext.from(idealState);
+      }
       bucketSize = idealState.getBucketSize();
       batchMessageMode = idealState.getBatchMessageMode();
+      idealState.updateUserConfig(userConfig);
     } else if (resourceConfiguration != null) {
       bucketSize = resourceConfiguration.getBucketSize();
       batchMessageMode = resourceConfiguration.getBatchMessageMode();

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/api/config/NamespacedConfig.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/api/config/NamespacedConfig.java b/helix-core/src/main/java/org/apache/helix/api/config/NamespacedConfig.java
index c6f9d19..4ea0ace 100644
--- a/helix-core/src/main/java/org/apache/helix/api/config/NamespacedConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/api/config/NamespacedConfig.java
@@ -35,7 +35,7 @@ import com.google.common.collect.Maps;
  * Generic configuration of Helix components prefixed with a namespace
  */
 public class NamespacedConfig extends ZNRecord {
-  private static final char PREFIX_CHAR = '!';
+  public static final char PREFIX_CHAR = '!';
   private final String _prefix;
 
   /**
@@ -176,7 +176,7 @@ public class NamespacedConfig extends ZNRecord {
    * Get all map fields with prefixed keys
    * @return prefixed map fields
    */
-  private Map<String, Map<String, String>> getPrefixedMapFields() {
+  public Map<String, Map<String, String>> getPrefixedMapFields() {
     return super.getMapFields();
   }
 
@@ -184,7 +184,7 @@ public class NamespacedConfig extends ZNRecord {
    * Get all list fields with prefixed keys
    * @return prefixed list fields
    */
-  private Map<String, List<String>> getPrefixedListFields() {
+  public Map<String, List<String>> getPrefixedListFields() {
     return super.getListFields();
   }
 
@@ -192,7 +192,7 @@ public class NamespacedConfig extends ZNRecord {
    * Get all simple fields with prefixed keys
    * @return prefixed simple fields
    */
-  private Map<String, String> getPrefixedSimpleFields() {
+  public Map<String, String> getPrefixedSimpleFields() {
     return super.getSimpleFields();
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/controller/rebalancer/context/RebalancerConfig.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/context/RebalancerConfig.java b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/context/RebalancerConfig.java
index 64f70c3..aa872c4 100644
--- a/helix-core/src/main/java/org/apache/helix/controller/rebalancer/context/RebalancerConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/controller/rebalancer/context/RebalancerConfig.java
@@ -100,15 +100,17 @@ public final class RebalancerConfig {
 
   private RebalancerContext getContext() {
     String className = _config.getSimpleField(Fields.REBALANCER_CONTEXT_CLASS.toString());
-    try {
-      Class<? extends RebalancerContext> contextClass =
-          HelixUtil.loadClass(getClass(), className).asSubclass(RebalancerContext.class);
-      String serialized = _config.getSimpleField(Fields.REBALANCER_CONTEXT.toString());
-      return _serializer.deserialize(contextClass, serialized);
-    } catch (ClassNotFoundException e) {
-      LOG.error(className + " is not a valid class");
-    } catch (ClassCastException e) {
-      LOG.error(className + " does not implement RebalancerContext");
+    if (className != null) {
+      try {
+        Class<? extends RebalancerContext> contextClass =
+            HelixUtil.loadClass(getClass(), className).asSubclass(RebalancerContext.class);
+        String serialized = _config.getSimpleField(Fields.REBALANCER_CONTEXT.toString());
+        return _serializer.deserialize(contextClass, serialized);
+      } catch (ClassNotFoundException e) {
+        LOG.error(className + " is not a valid class");
+      } catch (ClassCastException e) {
+        LOG.error(className + " does not implement RebalancerContext");
+      }
     }
     return null;
   }
@@ -134,10 +136,12 @@ public final class RebalancerConfig {
    * @return RebalancerContext subclass instance, or null if conversion is not possible
    */
   public <T extends RebalancerContext> T getRebalancerContext(Class<T> contextClass) {
-    try {
-      return contextClass.cast(_context);
-    } catch (ClassCastException e) {
-      LOG.warn(contextClass + " is incompatible with context class: " + _context.getClass());
+    if (_context != null) {
+      try {
+        return contextClass.cast(_context);
+      } catch (ClassCastException e) {
+        LOG.warn(contextClass + " is incompatible with context class: " + _context.getClass());
+      }
     }
     return null;
   }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/model/ClusterConfiguration.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/ClusterConfiguration.java b/helix-core/src/main/java/org/apache/helix/model/ClusterConfiguration.java
index d733a5c..8386d6c 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ClusterConfiguration.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ClusterConfiguration.java
@@ -1,10 +1,11 @@
 package org.apache.helix.model;
 
-import org.apache.helix.HelixManager;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
+import org.apache.helix.api.config.NamespacedConfig;
 import org.apache.helix.api.config.UserConfig;
 import org.apache.helix.api.id.ClusterId;
+import org.apache.helix.manager.zk.ZKHelixManager;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -46,11 +47,19 @@ public class ClusterConfiguration extends HelixProperty {
   }
 
   /**
+   * Get a typed cluster id
+   * @return ClusterId
+   */
+  public ClusterId getClusterId() {
+    return ClusterId.from(getId());
+  }
+
+  /**
    * Determine if participants can automatically join the cluster
    * @return true if allowed, false if disallowed
    */
   public boolean autoJoinAllowed() {
-    return _record.getBooleanField(HelixManager.ALLOW_PARTICIPANT_AUTO_JOIN, false);
+    return _record.getBooleanField(ZKHelixManager.ALLOW_PARTICIPANT_AUTO_JOIN, false);
   }
 
   /**
@@ -58,7 +67,32 @@ public class ClusterConfiguration extends HelixProperty {
    * @param autoJoinAllowed true if allowed, false if disallowed
    */
   public void setAutoJoinAllowed(boolean autoJoinAllowed) {
-    _record.setBooleanField(HelixManager.ALLOW_PARTICIPANT_AUTO_JOIN, autoJoinAllowed);
+    _record.setBooleanField(ZKHelixManager.ALLOW_PARTICIPANT_AUTO_JOIN, autoJoinAllowed);
+  }
+
+  /**
+   * Get a backward-compatible cluster user config
+   * @return UserConfig
+   */
+  public UserConfig getUserConfig() {
+    UserConfig userConfig = UserConfig.from(this);
+    for (String simpleField : _record.getSimpleFields().keySet()) {
+      if (!simpleField.contains(NamespacedConfig.PREFIX_CHAR + "")
+          && !simpleField.equals(ZKHelixManager.ALLOW_PARTICIPANT_AUTO_JOIN)) {
+        userConfig.setSimpleField(simpleField, _record.getSimpleField(simpleField));
+      }
+    }
+    for (String listField : _record.getListFields().keySet()) {
+      if (!listField.contains(NamespacedConfig.PREFIX_CHAR + "")) {
+        userConfig.setListField(listField, _record.getListField(listField));
+      }
+    }
+    for (String mapField : _record.getMapFields().keySet()) {
+      if (!mapField.contains(NamespacedConfig.PREFIX_CHAR + "")) {
+        userConfig.setMapField(mapField, _record.getMapField(mapField));
+      }
+    }
+    return userConfig;
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/model/IdealState.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/IdealState.java b/helix-core/src/main/java/org/apache/helix/model/IdealState.java
index 09aed50..173e251 100644
--- a/helix-core/src/main/java/org/apache/helix/model/IdealState.java
+++ b/helix-core/src/main/java/org/apache/helix/model/IdealState.java
@@ -33,6 +33,8 @@ import org.apache.helix.HelixDefinedState;
 import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
 import org.apache.helix.api.State;
+import org.apache.helix.api.config.NamespacedConfig;
+import org.apache.helix.api.config.UserConfig;
 import org.apache.helix.api.id.ParticipantId;
 import org.apache.helix.api.id.PartitionId;
 import org.apache.helix.api.id.ResourceId;
@@ -42,7 +44,9 @@ import org.apache.helix.controller.rebalancer.HelixRebalancer;
 import org.apache.helix.controller.rebalancer.RebalancerRef;
 import org.apache.log4j.Logger;
 
+import com.google.common.base.Enums;
 import com.google.common.base.Function;
+import com.google.common.base.Optional;
 import com.google.common.collect.ArrayListMultimap;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
@@ -752,6 +756,20 @@ public class IdealState extends HelixProperty {
   }
 
   /**
+   * Get the non-Helix simple fields from this property and add them to a UserConfig
+   * @param userConfig the user config to update
+   */
+  public void updateUserConfig(UserConfig userConfig) {
+    for (String simpleField : _record.getSimpleFields().keySet()) {
+      Optional<IdealStateProperty> enumField =
+          Enums.getIfPresent(IdealStateProperty.class, simpleField);
+      if (!simpleField.contains(NamespacedConfig.PREFIX_CHAR + "") && !enumField.isPresent()) {
+        userConfig.setSimpleField(simpleField, _record.getSimpleField(simpleField));
+      }
+    }
+  }
+
+  /**
    * Convert a preference list of strings into a preference list of participants
    * @param rawPreferenceList the list of strings representing participant names
    * @return converted list

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
index 8f776a0..35b4bd4 100644
--- a/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
+++ b/helix-core/src/main/java/org/apache/helix/model/InstanceConfig.java
@@ -27,9 +27,13 @@ import java.util.Set;
 
 import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
+import org.apache.helix.api.config.NamespacedConfig;
+import org.apache.helix.api.config.UserConfig;
 import org.apache.helix.api.id.ParticipantId;
 import org.apache.helix.api.id.PartitionId;
-import org.apache.log4j.Logger;
+
+import com.google.common.base.Enums;
+import com.google.common.base.Optional;
 
 /**
  * Instance configurations
@@ -46,8 +50,6 @@ public class InstanceConfig extends HelixProperty {
     TAG_LIST
   }
 
-  private static final Logger _logger = Logger.getLogger(InstanceConfig.class.getName());
-
   /**
    * Instantiate for a specific instance
    * @param instanceId the instance identifier
@@ -265,6 +267,36 @@ public class InstanceConfig extends HelixProperty {
     return ParticipantId.from(getInstanceName());
   }
 
+  /**
+   * Get a backward-compatible participant user config
+   * @return UserConfig
+   */
+  public UserConfig getUserConfig() {
+    UserConfig userConfig = UserConfig.from(this);
+    for (String simpleField : _record.getSimpleFields().keySet()) {
+      Optional<InstanceConfigProperty> enumField =
+          Enums.getIfPresent(InstanceConfigProperty.class, simpleField);
+      if (!simpleField.contains(NamespacedConfig.PREFIX_CHAR + "") && !enumField.isPresent()) {
+        userConfig.setSimpleField(simpleField, _record.getSimpleField(simpleField));
+      }
+    }
+    for (String listField : _record.getListFields().keySet()) {
+      Optional<InstanceConfigProperty> enumField =
+          Enums.getIfPresent(InstanceConfigProperty.class, listField);
+      if (!listField.contains(NamespacedConfig.PREFIX_CHAR + "") && !enumField.isPresent()) {
+        userConfig.setListField(listField, _record.getListField(listField));
+      }
+    }
+    for (String mapField : _record.getMapFields().keySet()) {
+      Optional<InstanceConfigProperty> enumField =
+          Enums.getIfPresent(InstanceConfigProperty.class, mapField);
+      if (!mapField.contains(NamespacedConfig.PREFIX_CHAR + "") && !enumField.isPresent()) {
+        userConfig.setMapField(mapField, _record.getMapField(mapField));
+      }
+    }
+    return userConfig;
+  }
+
   @Override
   public boolean isValid() {
     // HELIX-65: remove check for hostname/port existence

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/main/java/org/apache/helix/model/ResourceConfiguration.java
----------------------------------------------------------------------
diff --git a/helix-core/src/main/java/org/apache/helix/model/ResourceConfiguration.java b/helix-core/src/main/java/org/apache/helix/model/ResourceConfiguration.java
index 230a78b..f32649f 100644
--- a/helix-core/src/main/java/org/apache/helix/model/ResourceConfiguration.java
+++ b/helix-core/src/main/java/org/apache/helix/model/ResourceConfiguration.java
@@ -2,8 +2,15 @@ package org.apache.helix.model;
 
 import org.apache.helix.HelixProperty;
 import org.apache.helix.ZNRecord;
+import org.apache.helix.api.config.NamespacedConfig;
 import org.apache.helix.api.config.ResourceConfig.ResourceType;
+import org.apache.helix.api.config.UserConfig;
 import org.apache.helix.api.id.ResourceId;
+import org.apache.helix.controller.rebalancer.context.RebalancerConfig;
+import org.apache.helix.controller.rebalancer.context.RebalancerContext;
+
+import com.google.common.base.Enums;
+import com.google.common.base.Optional;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -71,4 +78,38 @@ public class ResourceConfiguration extends HelixProperty {
   public ResourceType getType() {
     return _record.getEnumField(Fields.TYPE.toString(), ResourceType.class, ResourceType.DATA);
   }
+
+  /**
+   * Get a backward-compatible resource user config
+   * @return UserConfig
+   */
+  public UserConfig getUserConfig() {
+    UserConfig userConfig = UserConfig.from(this);
+    for (String simpleField : _record.getSimpleFields().keySet()) {
+      Optional<Fields> enumField = Enums.getIfPresent(Fields.class, simpleField);
+      if (!simpleField.contains(NamespacedConfig.PREFIX_CHAR + "") && !enumField.isPresent()) {
+        userConfig.setSimpleField(simpleField, _record.getSimpleField(simpleField));
+      }
+    }
+    for (String listField : _record.getListFields().keySet()) {
+      if (!listField.contains(NamespacedConfig.PREFIX_CHAR + "")) {
+        userConfig.setListField(listField, _record.getListField(listField));
+      }
+    }
+    for (String mapField : _record.getMapFields().keySet()) {
+      if (!mapField.contains(NamespacedConfig.PREFIX_CHAR + "")) {
+        userConfig.setMapField(mapField, _record.getMapField(mapField));
+      }
+    }
+    return userConfig;
+  }
+
+  /**
+   * Get a RebalancerContext if available
+   * @return RebalancerContext, or null
+   */
+  public RebalancerContext getRebalancerContext(Class<? extends RebalancerContext> clazz) {
+    RebalancerConfig config = new RebalancerConfig(this);
+    return config.getRebalancerContext(clazz);
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-helix/blob/26a9413f/helix-core/src/test/java/org/apache/helix/api/TestNamespacedConfig.java
----------------------------------------------------------------------
diff --git a/helix-core/src/test/java/org/apache/helix/api/TestNamespacedConfig.java b/helix-core/src/test/java/org/apache/helix/api/TestNamespacedConfig.java
index 5a14287..761ffe2 100644
--- a/helix-core/src/test/java/org/apache/helix/api/TestNamespacedConfig.java
+++ b/helix-core/src/test/java/org/apache/helix/api/TestNamespacedConfig.java
@@ -3,10 +3,18 @@ package org.apache.helix.api;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.helix.api.config.ResourceConfig.ResourceType;
 import org.apache.helix.api.config.UserConfig;
+import org.apache.helix.api.id.ClusterId;
 import org.apache.helix.api.id.ParticipantId;
+import org.apache.helix.api.id.ResourceId;
+import org.apache.helix.manager.zk.ZKHelixManager;
+import org.apache.helix.model.ClusterConfiguration;
+import org.apache.helix.model.IdealState;
+import org.apache.helix.model.IdealState.RebalanceMode;
 import org.apache.helix.model.InstanceConfig;
 import org.apache.helix.model.InstanceConfig.InstanceConfigProperty;
+import org.apache.helix.model.ResourceConfiguration;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
@@ -85,4 +93,85 @@ public class TestNamespacedConfig {
     Assert.assertEquals(instanceConfig.getRecord().getListField(prefixedKey), testListValue);
     Assert.assertEquals(instanceConfig.getRecord().getMapField(prefixedKey), testMapValue);
   }
+
+  @Test
+  public void testResourceUserConfigCompatibility() {
+    final String KEY1 = "key1";
+    final String VALUE1 = "value1";
+    final String KEY2 = "key2";
+    final String VALUE2 = "value2";
+    final String KEY3 = "key3";
+    final String VALUE3 = "value3";
+
+    // add key1 through user config, key2 through resource config, key3 through ideal state,
+    // resource type through resource config, rebalance mode through ideal state
+    ResourceId resourceId = ResourceId.from("resourceId");
+    UserConfig userConfig = new UserConfig(Scope.resource(resourceId));
+    userConfig.setSimpleField(KEY1, VALUE1);
+    ResourceConfiguration resourceConfig = new ResourceConfiguration(resourceId);
+    resourceConfig.setType(ResourceType.DATA);
+    resourceConfig.addNamespacedConfig(userConfig);
+    resourceConfig.getRecord().setSimpleField(KEY2, VALUE2);
+    IdealState idealState = new IdealState(resourceId);
+    idealState.setRebalanceMode(RebalanceMode.USER_DEFINED);
+    idealState.getRecord().setSimpleField(KEY3, VALUE3);
+
+    // should have key1, key2, and key3, not type or rebalance mode
+    UserConfig result = resourceConfig.getUserConfig();
+    idealState.updateUserConfig(result);
+    Assert.assertEquals(result.getSimpleField(KEY1), VALUE1);
+    Assert.assertEquals(result.getSimpleField(KEY2), VALUE2);
+    Assert.assertEquals(result.getSimpleField(KEY3), VALUE3);
+    Assert.assertNull(result.getSimpleField(ResourceConfiguration.Fields.TYPE.toString()));
+    Assert
+        .assertNull(result.getSimpleField(IdealState.IdealStateProperty.REBALANCE_MODE.toString()));
+  }
+
+  @Test
+  public void testParticipantUserConfigCompatibility() {
+    final String KEY1 = "key1";
+    final String VALUE1 = "value1";
+    final String KEY2 = "key2";
+    final String VALUE2 = "value2";
+
+    // add key1 through user config, key2 through instance config, hostname through user config
+    ParticipantId participantId = ParticipantId.from("participantId");
+    UserConfig userConfig = new UserConfig(Scope.participant(participantId));
+    userConfig.setSimpleField(KEY1, VALUE1);
+    InstanceConfig instanceConfig = new InstanceConfig(participantId);
+    instanceConfig.setHostName("localhost");
+    instanceConfig.addNamespacedConfig(userConfig);
+    instanceConfig.getRecord().setSimpleField(KEY2, VALUE2);
+
+    // should have key1 and key2, not hostname
+    UserConfig result = instanceConfig.getUserConfig();
+    Assert.assertEquals(result.getSimpleField(KEY1), VALUE1);
+    Assert.assertEquals(result.getSimpleField(KEY2), VALUE2);
+    Assert.assertNull(result.getSimpleField(InstanceConfig.InstanceConfigProperty.HELIX_HOST
+        .toString()));
+  }
+
+  @Test
+  public void testClusterUserConfigCompatibility() {
+    final String KEY1 = "key1";
+    final String VALUE1 = "value1";
+    final String KEY2 = "key2";
+    final String VALUE2 = "value2";
+
+    // add the following: key1 straight to user config, key2 to cluster configuration,
+    // allow auto join to cluster configuration
+    ClusterId clusterId = ClusterId.from("clusterId");
+    UserConfig userConfig = new UserConfig(Scope.cluster(clusterId));
+    userConfig.setSimpleField(KEY1, VALUE1);
+    ClusterConfiguration clusterConfiguration = new ClusterConfiguration(clusterId);
+    clusterConfiguration.addNamespacedConfig(userConfig);
+    clusterConfiguration.getRecord().setSimpleField(KEY2, VALUE2);
+    clusterConfiguration.setAutoJoinAllowed(true);
+
+    // there should be key1 and key2, but not auto join
+    UserConfig result = clusterConfiguration.getUserConfig();
+    Assert.assertEquals(result.getSimpleField(KEY1), VALUE1);
+    Assert.assertEquals(result.getSimpleField(KEY2), VALUE2);
+    Assert.assertNull(result.getSimpleField(ZKHelixManager.ALLOW_PARTICIPANT_AUTO_JOIN));
+  }
 }