You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by ma...@apache.org on 2016/11/19 14:21:14 UTC

[04/12] kylin git commit: KYLIN-2198 Add a framework to allow major changes in DimensionEncoding

KYLIN-2198 Add a framework to allow major changes in DimensionEncoding


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

Branch: refs/heads/yang21-cdh5.7
Commit: e1acc4192a982f897489f52d1bbc836a5c207da6
Parents: c0c0814
Author: Hongbin Ma <ma...@apache.org>
Authored: Wed Nov 16 14:47:53 2016 +0800
Committer: Hongbin Ma <ma...@apache.org>
Committed: Thu Nov 17 11:11:34 2016 +0800

----------------------------------------------------------------------
 .../apache/kylin/common/util/JacksonBean.java   | 55 +++++++++++
 .../apache/kylin/common/util/JacksonTest.java   | 39 ++++++++
 .../org/apache/kylin/cube/kv/CubeDimEncMap.java |  2 +-
 .../apache/kylin/cube/model/RowKeyColDesc.java  | 13 ++-
 .../dimension/DimensionEncodingFactory.java     | 97 +++++++++++++++-----
 .../kylin/measure/topn/TopNMeasureType.java     | 12 ++-
 .../kylin/rest/controller/CubeController.java   | 12 +--
 7 files changed, 198 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/core-common/src/test/java/org/apache/kylin/common/util/JacksonBean.java
----------------------------------------------------------------------
diff --git a/core-common/src/test/java/org/apache/kylin/common/util/JacksonBean.java b/core-common/src/test/java/org/apache/kylin/common/util/JacksonBean.java
new file mode 100644
index 0000000..42357f2
--- /dev/null
+++ b/core-common/src/test/java/org/apache/kylin/common/util/JacksonBean.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.common.util;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonAutoDetect(fieldVisibility = Visibility.NONE, getterVisibility = Visibility.NONE, isGetterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
+public class JacksonBean {
+
+    @JsonProperty("a")
+    private String a;
+    @JsonProperty("b")
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    private int b;
+
+    public String getA() {
+        return a;
+    }
+
+    public void setA(String a) {
+        this.a = a;
+    }
+
+    public int getB() {
+        return b;
+    }
+
+    public void setB(int b) {
+        this.b = b;
+    }
+
+    @Override
+    public String toString() {
+        return "JacksonBean{" + "a='" + a + '\'' + ", b=" + b + '}';
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/core-common/src/test/java/org/apache/kylin/common/util/JacksonTest.java
----------------------------------------------------------------------
diff --git a/core-common/src/test/java/org/apache/kylin/common/util/JacksonTest.java b/core-common/src/test/java/org/apache/kylin/common/util/JacksonTest.java
new file mode 100644
index 0000000..81be7eb
--- /dev/null
+++ b/core-common/src/test/java/org/apache/kylin/common/util/JacksonTest.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *  
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *  
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.common.util;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+public class JacksonTest {
+    @Test
+    public void foo() throws IOException {
+        JacksonBean bean = new JacksonBean();
+        bean.setA("valuea");
+
+        String s = JsonUtil.writeValueAsString(bean);
+        System.out.println(s);
+
+        JacksonBean desBean = (JacksonBean) JsonUtil.readValue("{\"a\":\"valuea\"}", JacksonBean.class);
+        String x2 = JsonUtil.writeValueAsString(desBean);
+        System.out.println(desBean);
+        System.out.println(x2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/core-cube/src/main/java/org/apache/kylin/cube/kv/CubeDimEncMap.java
----------------------------------------------------------------------
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/kv/CubeDimEncMap.java b/core-cube/src/main/java/org/apache/kylin/cube/kv/CubeDimEncMap.java
index f588986..a4d2d6f 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/kv/CubeDimEncMap.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/kv/CubeDimEncMap.java
@@ -72,7 +72,7 @@ public class CubeDimEncMap implements IDimensionEncodingMap {
                 }
             } else {
                 // normal case
-                result = DimensionEncodingFactory.create(colDesc.getEncodingName(), colDesc.getEncodingArgs());
+                result = DimensionEncodingFactory.create(colDesc.getEncodingName(), colDesc.getEncodingArgs(), colDesc.getEncodingVersion());
             }
             encMap.put(col, result);
         }

http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/core-cube/src/main/java/org/apache/kylin/cube/model/RowKeyColDesc.java
----------------------------------------------------------------------
diff --git a/core-cube/src/main/java/org/apache/kylin/cube/model/RowKeyColDesc.java b/core-cube/src/main/java/org/apache/kylin/cube/model/RowKeyColDesc.java
index 12c4dfc..9b32d8c 100644
--- a/core-cube/src/main/java/org/apache/kylin/cube/model/RowKeyColDesc.java
+++ b/core-cube/src/main/java/org/apache/kylin/cube/model/RowKeyColDesc.java
@@ -47,6 +47,9 @@ public class RowKeyColDesc {
     private String column;
     @JsonProperty("encoding")
     private String encoding;
+    @JsonProperty("encoding_version")
+    @JsonInclude(JsonInclude.Include.NON_DEFAULT)
+    private int encodingVersion = 1;
     @JsonProperty("isShardBy")
     private boolean isShardBy;//usually it is ultra high cardinality column, shard by such column can reduce the agg cache for each shard
     @JsonProperty("index")
@@ -72,7 +75,7 @@ public class RowKeyColDesc {
         encodingName = (String) encodingConf[0];
         encodingArgs = (String[]) encodingConf[1];
 
-        if (!DimensionEncodingFactory.isVaildEncoding(this.encodingName))
+        if (!DimensionEncodingFactory.isValidEncoding(this.encodingName))
             throw new IllegalArgumentException("Not supported row key col encoding: '" + this.encoding + "'");
 
         // convert date/time dictionary on date/time column to DimensionEncoding implicitly
@@ -144,6 +147,14 @@ public class RowKeyColDesc {
         this.index = index;
     }
 
+    public int getEncodingVersion() {
+        return encodingVersion;
+    }
+
+    public void setEncodingVersion(int encodingVersion) {
+        this.encodingVersion = encodingVersion;
+    }
+
     @Override
     public String toString() {
         return Objects.toStringHelper(this).add("column", column).add("encoding", encoding).toString();

http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java b/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java
index 27bebd7..242e003 100644
--- a/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java
+++ b/core-metadata/src/main/java/org/apache/kylin/dimension/DimensionEncodingFactory.java
@@ -18,30 +18,47 @@
 
 package org.apache.kylin.dimension;
 
+import java.util.Arrays;
 import java.util.Map;
-import java.util.Set;
-import java.util.TreeSet;
+
+import javax.annotation.Nullable;
 
 import org.apache.kylin.common.KylinConfig;
 import org.apache.kylin.common.util.ClassUtil;
+import org.apache.kylin.common.util.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 
 public abstract class DimensionEncodingFactory {
 
     private static final Logger logger = LoggerFactory.getLogger(DimensionEncodingFactory.class);
 
-    private static Map<String, DimensionEncodingFactory> factoryMap;
+    private static Map<Pair<String, Integer>, DimensionEncodingFactory> factoryMap;
+
+    /**
+     * If a bug found in a DimEnc will cause different cube outputs,
+     * we'll have to increase the version number of DimEnc, in order
+     * to distinguish current version with prior version.
+     * <p>
+     * The default version applys to all existing legacy DimEncs
+     */
+    protected int getCurrentVersion() {
+        return 1;
+    }
 
-    /** Create a DimensionEncoding instance, with inputs corresponding to RowKeyColDesc.encodingName and RowKeyColDesc.encodingArgs. */
-    public static DimensionEncoding create(String encodingName, String[] args) {
+    /**
+     * Create a DimensionEncoding instance, with inputs corresponding to RowKeyColDesc.encodingName and RowKeyColDesc.encodingArgs.
+     */
+    public static DimensionEncoding create(String encodingName, String[] args, int version) {
+        logger.debug("Encoding Name : {}, args : {}, version {}", encodingName, Arrays.toString(args), version);
         if (factoryMap == null)
             initFactoryMap();
 
-        DimensionEncodingFactory factory = factoryMap.get(encodingName);
+        DimensionEncodingFactory factory = factoryMap.get(Pair.newPair(encodingName, version));
         if (factory == null) {
             throw new IllegalArgumentException("Unknown dimension encoding name " + encodingName //
                     + " (note '" + DictionaryDimEnc.ENCODING_NAME + "' is not handled by factory)");
@@ -50,42 +67,72 @@ public abstract class DimensionEncodingFactory {
         return factory.createDimensionEncoding(encodingName, args);
     }
 
-    public static Set<String> getValidEncodings() {
+    public static Map<String, Integer> getValidEncodings() {
         if (factoryMap == null)
             initFactoryMap();
 
-        TreeSet<String> result = Sets.newTreeSet();
-        result.addAll(factoryMap.keySet());
-        result.add(DictionaryDimEnc.ENCODING_NAME);
+        Map<String, Integer> result = Maps.newHashMap();
+        for (Pair<String, Integer> p : factoryMap.keySet()) {
+            result.put(p.getFirst(), p.getSecond());
+        }
+        result.put(DictionaryDimEnc.ENCODING_NAME, 1);
         return result;
     }
 
-    public static boolean isVaildEncoding(String encodingName) {
+    public static boolean isValidEncoding(final String encodingName) {
         if (factoryMap == null)
             initFactoryMap();
 
         // note dictionary is a special case
-        return DictionaryDimEnc.ENCODING_NAME.equals(encodingName) || factoryMap.containsKey(encodingName);
+        return DictionaryDimEnc.ENCODING_NAME.equals(encodingName) || //
+                Iterables.any(factoryMap.keySet(), new Predicate<Pair<String, Integer>>() {
+                    @Override
+                    public boolean apply(@Nullable Pair<String, Integer> input) {
+                        return input.getFirst().equals(encodingName);
+                    }
+                });
     }
 
     private synchronized static void initFactoryMap() {
         if (factoryMap == null) {
-            Map<String, DimensionEncodingFactory> map = Maps.newConcurrentMap();
+            Map<Pair<String, Integer>, DimensionEncodingFactory> map = Maps.newConcurrentMap();
 
             // built-in encodings, note dictionary is a special case
-            map.put(FixedLenDimEnc.ENCODING_NAME, new FixedLenDimEnc.Factory());
-            map.put(IntDimEnc.ENCODING_NAME, new IntDimEnc.Factory());
-            map.put(IntegerDimEnc.ENCODING_NAME, new IntegerDimEnc.Factory());
-            map.put(FixedLenHexDimEnc.ENCODING_NAME, new FixedLenHexDimEnc.Factory());
-            map.put(DateDimEnc.ENCODING_NAME, new DateDimEnc.Factory());
-            map.put(TimeDimEnc.ENCODING_NAME, new TimeDimEnc.Factory());
+            {
+                FixedLenDimEnc.Factory value = new FixedLenDimEnc.Factory();
+                map.put(Pair.newPair(FixedLenDimEnc.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
+            {
+                IntDimEnc.Factory value = new IntDimEnc.Factory();
+                map.put(Pair.newPair(IntDimEnc.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
+            {
+                IntegerDimEnc.Factory value = new IntegerDimEnc.Factory();
+                map.put(Pair.newPair(IntegerDimEnc.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
+            {
+                IntegerDimEncV2.Factory value = new IntegerDimEncV2.Factory();
+                map.put(Pair.newPair(IntegerDimEncV2.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
+            {
+                FixedLenHexDimEnc.Factory value = new FixedLenHexDimEnc.Factory();
+                map.put(Pair.newPair(FixedLenHexDimEnc.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
+            {
+                DateDimEnc.Factory value = new DateDimEnc.Factory();
+                map.put(Pair.newPair(DateDimEnc.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
+            {
+                TimeDimEnc.Factory value = new TimeDimEnc.Factory();
+                map.put(Pair.newPair(TimeDimEnc.ENCODING_NAME, value.getCurrentVersion()), value);
+            }
 
             // custom encodings
             String[] clsNames = KylinConfig.getInstanceFromEnv().getCubeDimensionCustomEncodingFactories();
             for (String clsName : clsNames) {
                 try {
                     DimensionEncodingFactory factory = (DimensionEncodingFactory) ClassUtil.newInstance(clsName);
-                    map.put(factory.getSupportedEncodingName(), factory);
+                    map.put(Pair.newPair(factory.getSupportedEncodingName(), factory.getCurrentVersion()), factory);
                 } catch (Exception ex) {
                     logger.error("Failed to init dimension encoding factory " + clsName, ex);
                 }
@@ -95,9 +142,13 @@ public abstract class DimensionEncodingFactory {
         }
     }
 
-    /** Return the supported encoding name, corresponds to RowKeyColDesc.encodingName */
+    /**
+     * Return the supported encoding name, corresponds to RowKeyColDesc.encodingName
+     */
     abstract public String getSupportedEncodingName();
 
-    /** Create a DimensionEncoding instance, with inputs corresponding to RowKeyColDesc.encodingName and RowKeyColDesc.encodingArgs */
+    /**
+     * Create a DimensionEncoding instance, with inputs corresponding to RowKeyColDesc.encodingName and RowKeyColDesc.encodingArgs
+     */
     abstract public DimensionEncoding createDimensionEncoding(String encodingName, String[] args);
 }

http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/core-metadata/src/main/java/org/apache/kylin/measure/topn/TopNMeasureType.java
----------------------------------------------------------------------
diff --git a/core-metadata/src/main/java/org/apache/kylin/measure/topn/TopNMeasureType.java b/core-metadata/src/main/java/org/apache/kylin/measure/topn/TopNMeasureType.java
index 1e2d6dd..88e9533 100644
--- a/core-metadata/src/main/java/org/apache/kylin/measure/topn/TopNMeasureType.java
+++ b/core-metadata/src/main/java/org/apache/kylin/measure/topn/TopNMeasureType.java
@@ -57,6 +57,7 @@ public class TopNMeasureType extends MeasureType<TopNCounter<ByteArray>> {
     public static final String DATATYPE_TOPN = "topn";
 
     public static final String CONFIG_ENCODING_PREFIX = "topn.encoding.";
+    public static final String CONFIG_ENCODING_VERSION_PREFIX = "topn.encoding_version.";
     public static final String CONFIG_AGG = "topn.aggregation";
     public static final String CONFIG_ORDER = "topn.order";
 
@@ -418,11 +419,20 @@ public class TopNMeasureType extends MeasureType<TopNCounter<ByteArray>> {
         for (int i = 0; i < literalCols.size(); i++) {
             TblColRef colRef = literalCols.get(i);
             String encoding = function.getConfiguration().get(TopNMeasureType.CONFIG_ENCODING_PREFIX + colRef.getName());
+            String encodingVersionStr = function.getConfiguration().get(TopNMeasureType.CONFIG_ENCODING_VERSION_PREFIX + colRef.getName());
             if (StringUtils.isEmpty(encoding) || DictionaryDimEnc.ENCODING_NAME.equals(encoding)) {
                 dimensionEncodings[i] = new DictionaryDimEnc(dictionaryMap.get(colRef));
             } else {
+                int encodingVersion = 1;
+                if (!StringUtils.isEmpty(encodingVersionStr)) {
+                    try {
+                        encodingVersion = Integer.parseInt(encodingVersionStr);
+                    } catch (NumberFormatException e) {
+                        throw new RuntimeException(TopNMeasureType.CONFIG_ENCODING_VERSION_PREFIX + colRef.getName() + " has to be an integer");
+                    }
+                }
                 Object[] encodingConf = DimensionEncoding.parseEncodingConf(encoding);
-                dimensionEncodings[i] = DimensionEncodingFactory.create((String) encodingConf[0], (String[]) encodingConf[1]);
+                dimensionEncodings[i] = DimensionEncodingFactory.create((String) encodingConf[0], (String[]) encodingConf[1], encodingVersion);
             }
         }
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/e1acc419/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java
----------------------------------------------------------------------
diff --git a/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java
index 64fde81..891248f 100644
--- a/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java
+++ b/server-base/src/main/java/org/apache/kylin/rest/controller/CubeController.java
@@ -24,7 +24,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.UUID;
 
 import org.apache.commons.lang.StringUtils;
@@ -76,7 +75,7 @@ import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.google.common.base.Joiner;
-import com.google.common.collect.Sets;
+import com.google.common.collect.Maps;
 
 /**
  * CubeController is defined as Restful API entrance for UI.
@@ -120,12 +119,13 @@ public class CubeController extends BasicController {
 
     @RequestMapping(value = "validEncodings", method = { RequestMethod.GET })
     @ResponseBody
-    public Set<String> getValidEncodings() {
-        Set<String> encodings;
+    public Map<String, Integer> getValidEncodings() {
+        Map<String, Integer> encodings;
         try {
             encodings = DimensionEncodingFactory.getValidEncodings();
         } catch (Exception e) {
-            return Sets.newTreeSet();
+            logger.error("Error when getting valid encodings", e);
+            return Maps.newHashMap();
         }
         return encodings;
     }
@@ -360,7 +360,7 @@ public class CubeController extends BasicController {
 
         CubeDesc cubeDesc = cube.getDescriptor();
         CubeDesc newCubeDesc = CubeDesc.getCopyOf(cubeDesc);
-        
+
         KylinConfig config = cubeService.getConfig();
         newCubeDesc.setName(newCubeName);
         newCubeDesc.setEngineType(config.getDefaultCubeEngine());