You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@druid.apache.org by ab...@apache.org on 2021/11/10 04:53:55 UTC
[druid] branch master updated: Ensure backward compatibility of
multi dimension partitioning (#11889)
This is an automated email from the ASF dual-hosted git repository.
abhishek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new d3914c1 Ensure backward compatibility of multi dimension partitioning (#11889)
d3914c1 is described below
commit d3914c1a78ac4460665c910056b8976a31115483
Author: Kashif Faraz <ka...@gmail.com>
AuthorDate: Wed Nov 10 10:23:34 2021 +0530
Ensure backward compatibility of multi dimension partitioning (#11889)
This PR has changes to ensure backward compatibility of multi dimension partitioning
such that if some middle managers are upgraded to a newer version, the cluster still
functions normally for single_dim use cases.
---
.../org/apache/druid/data/input/StringTuple.java | 9 ++
.../partition/BuildingDimensionRangeShardSpec.java | 18 +--
.../BuildingSingleDimensionShardSpec.java | 64 ++++-----
.../partition/DimensionRangeBucketShardSpec.java | 20 ++-
.../timeline/partition/PartitionBoundaries.java | 61 ++++++++-
.../BuildingDimensionRangeShardSpecTest.java | 8 +-
.../BuildingSingleDimensionShardSpecTest.java | 92 +++++++++++--
.../DimensionRangeBucketShardSpecTest.java | 15 +++
.../partition/PartitionBoundariesTest.java | 145 ++++++++++++++++++++-
.../partition/SingleDimensionShardSpecTest.java | 20 +++
10 files changed, 379 insertions(+), 73 deletions(-)
diff --git a/core/src/main/java/org/apache/druid/data/input/StringTuple.java b/core/src/main/java/org/apache/druid/data/input/StringTuple.java
index 1362ed7..39d25e3 100644
--- a/core/src/main/java/org/apache/druid/data/input/StringTuple.java
+++ b/core/src/main/java/org/apache/druid/data/input/StringTuple.java
@@ -39,6 +39,15 @@ public class StringTuple implements Comparable<StringTuple>
return new StringTuple(values);
}
+ /**
+ * Gets the first String from the given StringTuple if the tuple is non-null
+ * and non-empty, null otherwise.
+ */
+ public static String firstOrNull(StringTuple tuple)
+ {
+ return tuple == null || tuple.size() < 1 ? null : tuple.get(0);
+ }
+
@JsonCreator
public StringTuple(String[] values)
{
diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpec.java
index 88f5e85..166c7b8 100644
--- a/core/src/main/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpec.java
+++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpec.java
@@ -29,7 +29,12 @@ import java.util.Objects;
/**
* See {@link BuildingShardSpec} for how this class is used.
+ * <p>
+ * Calling {@link #convert(int)} on an instance of this class creates a
+ * {@link SingleDimensionShardSpec} if there is a single dimension or a
+ * {@link DimensionRangeShardSpec} if there are multiple dimensions.
*
+ * @see SingleDimensionShardSpec
* @see DimensionRangeShardSpec
*/
public class BuildingDimensionRangeShardSpec implements BuildingShardSpec<DimensionRangeShardSpec>
@@ -68,14 +73,14 @@ public class BuildingDimensionRangeShardSpec implements BuildingShardSpec<Dimens
@Nullable
@JsonProperty("start")
- public StringTuple getStart()
+ public StringTuple getStartTuple()
{
return start;
}
@Nullable
@JsonProperty("end")
- public StringTuple getEnd()
+ public StringTuple getEndTuple()
{
return end;
}
@@ -100,8 +105,8 @@ public class BuildingDimensionRangeShardSpec implements BuildingShardSpec<Dimens
return dimensions != null && dimensions.size() == 1
? new SingleDimensionShardSpec(
dimensions.get(0),
- firstOrNull(start),
- firstOrNull(end),
+ StringTuple.firstOrNull(start),
+ StringTuple.firstOrNull(end),
partitionId,
numCorePartitions
) : new DimensionRangeShardSpec(
@@ -113,11 +118,6 @@ public class BuildingDimensionRangeShardSpec implements BuildingShardSpec<Dimens
);
}
- private String firstOrNull(StringTuple tuple)
- {
- return tuple == null || tuple.size() < 1 ? null : tuple.get(0);
- }
-
@Override
public <T> PartitionChunk<T> createChunk(T obj)
{
diff --git a/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java
index 6dd0992..1b9c613 100644
--- a/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java
+++ b/core/src/main/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpec.java
@@ -21,8 +21,13 @@ package org.apache.druid.timeline.partition;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonValue;
+import org.apache.druid.data.input.StringTuple;
import javax.annotation.Nullable;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Objects;
/**
@@ -30,17 +35,17 @@ import java.util.Objects;
*
* @see SingleDimensionShardSpec
*/
-public class BuildingSingleDimensionShardSpec implements BuildingShardSpec<SingleDimensionShardSpec>
+public class BuildingSingleDimensionShardSpec extends BuildingDimensionRangeShardSpec
{
public static final String TYPE = "building_single_dim";
- private final int bucketId;
private final String dimension;
+
@Nullable
private final String start;
+
@Nullable
private final String end;
- private final int partitionId;
@JsonCreator
public BuildingSingleDimensionShardSpec(
@@ -51,57 +56,58 @@ public class BuildingSingleDimensionShardSpec implements BuildingShardSpec<Singl
@JsonProperty("partitionNum") int partitionNum
)
{
- this.bucketId = bucketId;
+ super(
+ bucketId,
+ dimension == null ? Collections.emptyList() : Collections.singletonList(dimension),
+ start == null ? null : StringTuple.create(start),
+ end == null ? null : StringTuple.create(end),
+ partitionNum
+ );
this.dimension = dimension;
this.start = start;
this.end = end;
- this.partitionId = partitionNum;
}
- @JsonProperty("dimension")
+ @JsonValue
+ public Map<String, Object> getSerializableObject()
+ {
+ Map<String, Object> jsonMap = new HashMap<>();
+ jsonMap.put("dimension", dimension);
+ jsonMap.put("start", start);
+ jsonMap.put("end", end);
+ jsonMap.put("bucketId", getBucketId());
+ jsonMap.put("partitionNum", getPartitionNum());
+
+ return jsonMap;
+ }
+
public String getDimension()
{
return dimension;
}
@Nullable
- @JsonProperty("start")
public String getStart()
{
return start;
}
@Nullable
- @JsonProperty("end")
public String getEnd()
{
return end;
}
@Override
- @JsonProperty("partitionNum")
- public int getPartitionNum()
- {
- return partitionId;
- }
-
- @Override
- @JsonProperty("bucketId")
- public int getBucketId()
- {
- return bucketId;
- }
-
- @Override
public SingleDimensionShardSpec convert(int numCorePartitions)
{
- return new SingleDimensionShardSpec(dimension, start, end, partitionId, numCorePartitions);
+ return new SingleDimensionShardSpec(dimension, start, end, getPartitionNum(), numCorePartitions);
}
@Override
public <T> PartitionChunk<T> createChunk(T obj)
{
- return new NumberedPartitionChunk<>(partitionId, 0, obj);
+ return new NumberedPartitionChunk<>(getPartitionNum(), 0, obj);
}
@Override
@@ -114,8 +120,8 @@ public class BuildingSingleDimensionShardSpec implements BuildingShardSpec<Singl
return false;
}
BuildingSingleDimensionShardSpec that = (BuildingSingleDimensionShardSpec) o;
- return bucketId == that.bucketId &&
- partitionId == that.partitionId &&
+ return getBucketId() == that.getBucketId() &&
+ getPartitionNum() == that.getPartitionNum() &&
Objects.equals(dimension, that.dimension) &&
Objects.equals(start, that.start) &&
Objects.equals(end, that.end);
@@ -124,18 +130,18 @@ public class BuildingSingleDimensionShardSpec implements BuildingShardSpec<Singl
@Override
public int hashCode()
{
- return Objects.hash(bucketId, dimension, start, end, partitionId);
+ return Objects.hash(getBucketId(), dimension, start, end, getPartitionNum());
}
@Override
public String toString()
{
return "BuildingSingleDimensionShardSpec{" +
- "bucketId=" + bucketId +
+ "bucketId=" + getBucketId() +
", dimension='" + dimension + '\'' +
", start='" + start + '\'' +
", end='" + end + '\'' +
- ", partitionNum=" + partitionId +
+ ", partitionNum=" + getPartitionNum() +
'}';
}
}
diff --git a/core/src/main/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpec.java b/core/src/main/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpec.java
index 1cdedb2..17491a6 100644
--- a/core/src/main/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpec.java
+++ b/core/src/main/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpec.java
@@ -32,7 +32,12 @@ import java.util.Objects;
/**
* See {@link BucketNumberedShardSpec} for how this class is used.
+ * <p>
+ * Calling {@link #convert(int)} on an instance of this class creates a
+ * {@link BuildingSingleDimensionShardSpec} if there is a single dimension
+ * or {@link BuildingDimensionRangeShardSpec} if there are multiple dimensions.
*
+ * @see BuildingSingleDimensionShardSpec
* @see BuildingDimensionRangeShardSpec
*/
public class DimensionRangeBucketShardSpec implements BucketNumberedShardSpec<BuildingDimensionRangeShardSpec>
@@ -100,7 +105,20 @@ public class DimensionRangeBucketShardSpec implements BucketNumberedShardSpec<Bu
@Override
public BuildingDimensionRangeShardSpec convert(int partitionId)
{
- return new BuildingDimensionRangeShardSpec(bucketId, dimensions, start, end, partitionId);
+ return dimensions != null && dimensions.size() == 1
+ ? new BuildingSingleDimensionShardSpec(
+ bucketId,
+ dimensions.get(0),
+ StringTuple.firstOrNull(start),
+ StringTuple.firstOrNull(end),
+ partitionId
+ ) : new BuildingDimensionRangeShardSpec(
+ bucketId,
+ dimensions,
+ start,
+ end,
+ partitionId
+ );
}
@Override
diff --git a/core/src/main/java/org/apache/druid/timeline/partition/PartitionBoundaries.java b/core/src/main/java/org/apache/druid/timeline/partition/PartitionBoundaries.java
index 2247a91..7f96f75 100644
--- a/core/src/main/java/org/apache/druid/timeline/partition/PartitionBoundaries.java
+++ b/core/src/main/java/org/apache/druid/timeline/partition/PartitionBoundaries.java
@@ -19,8 +19,11 @@
package org.apache.druid.timeline.partition;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
import com.google.common.collect.ForwardingList;
import org.apache.druid.data.input.StringTuple;
+import org.apache.druid.java.util.common.IAE;
import java.util.ArrayList;
import java.util.Arrays;
@@ -36,13 +39,6 @@ public class PartitionBoundaries extends ForwardingList<StringTuple> implements
{
private final List<StringTuple> delegate;
- // For jackson
- @SuppressWarnings("unused")
- private PartitionBoundaries()
- {
- delegate = new ArrayList<>();
- }
-
/**
* @param partitions Elements corresponding to evenly-spaced fractional ranks of the distribution
*/
@@ -71,6 +67,57 @@ public class PartitionBoundaries extends ForwardingList<StringTuple> implements
delegate = Collections.unmodifiableList(partitionBoundaries);
}
+ /**
+ * This constructor supports an array of Objects and not just an array of
+ * StringTuples for backward compatibility. Older versions of this class
+ * are serialized as a String array.
+ *
+ * @param partitions array of StringTuples or array of String
+ */
+ @JsonCreator
+ private PartitionBoundaries(Object[] partitions)
+ {
+ delegate = Arrays.stream(partitions)
+ .map(this::toStringTuple)
+ .collect(Collectors.toList());
+ }
+
+ @JsonValue
+ public Object getSerializableObject()
+ {
+ boolean isSingleDim = true;
+ for (StringTuple tuple : delegate) {
+ if (tuple != null && tuple.size() != 1) {
+ isSingleDim = false;
+ break;
+ }
+ }
+
+ if (isSingleDim) {
+ return delegate.stream().map(StringTuple::firstOrNull).collect(Collectors.toList());
+ } else {
+ return delegate;
+ }
+ }
+
+ /**
+ * Converts the given item to a StringTuple.
+ */
+ private StringTuple toStringTuple(Object item)
+ {
+ if (item == null || item instanceof StringTuple) {
+ return (StringTuple) item;
+ } else if (item instanceof String) {
+ return StringTuple.create((String) item);
+ } else if (item instanceof String[]) {
+ return StringTuple.create((String[]) item);
+ } else if (item instanceof List) {
+ return StringTuple.create((String[]) ((List) item).toArray(new String[0]));
+ } else {
+ throw new IAE("Item must either be a String or StringTuple");
+ }
+ }
+
@Override
protected List<StringTuple> delegate()
{
diff --git a/core/src/test/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpecTest.java
index 9817f8d..4c8045a 100644
--- a/core/src/test/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpecTest.java
+++ b/core/src/test/java/org/apache/druid/timeline/partition/BuildingDimensionRangeShardSpecTest.java
@@ -58,13 +58,7 @@ public class BuildingDimensionRangeShardSpecTest
public void testConvert_withSingleDimension()
{
Assert.assertEquals(
- new SingleDimensionShardSpec(
- "dim",
- "start",
- "end",
- 5,
- 10
- ),
+ new SingleDimensionShardSpec("dim", "start", "end", 5, 10),
new BuildingDimensionRangeShardSpec(
1,
Collections.singletonList("dim"),
diff --git a/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java
index d70a42f..84ff681 100644
--- a/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java
+++ b/core/src/test/java/org/apache/druid/timeline/partition/BuildingSingleDimensionShardSpecTest.java
@@ -19,16 +19,19 @@
package org.apache.druid.timeline.partition;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.InjectableValues.Std;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.NamedType;
-import nl.jqno.equalsverifier.EqualsVerifier;
+import org.apache.druid.java.util.common.ISE;
import org.junit.Assert;
import org.junit.Test;
+import java.util.Map;
+
public class BuildingSingleDimensionShardSpecTest
{
+ private static final ObjectMapper OBJECT_MAPPER = setupObjectMapper();
+
@Test
public void testConvert()
{
@@ -48,25 +51,88 @@ public class BuildingSingleDimensionShardSpecTest
}
@Test
- public void testSerde() throws JsonProcessingException
+ public void testSerde()
+ {
+ final BuildingSingleDimensionShardSpec original =
+ new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5);
+ final String json = serialize(original);
+ final BuildingSingleDimensionShardSpec fromJson =
+ (BuildingSingleDimensionShardSpec) deserialize(json, ShardSpec.class);
+ Assert.assertEquals(original, fromJson);
+ }
+
+ @Test
+ public void testGetSerializableObject()
+ {
+ BuildingSingleDimensionShardSpec shardSpec =
+ new BuildingSingleDimensionShardSpec(1, "dim", "abc", "xyz", 5);
+
+ // Verify the fields of the serializable object
+ Map<String, Object> jsonMap = shardSpec.getSerializableObject();
+ Assert.assertEquals(5, jsonMap.size());
+ Assert.assertEquals(1, jsonMap.get("bucketId"));
+ Assert.assertEquals("dim", jsonMap.get("dimension"));
+ Assert.assertEquals("abc", jsonMap.get("start"));
+ Assert.assertEquals("xyz", jsonMap.get("end"));
+ Assert.assertEquals(5, jsonMap.get("partitionNum"));
+ }
+
+ @Test
+ public void testDeserializeFromMap()
+ {
+ final String json = "{\"type\": \"" + BuildingSingleDimensionShardSpec.TYPE + "\","
+ + " \"bucketId\":1,"
+ + " \"dimension\": \"dim\","
+ + " \"start\": \"abc\","
+ + " \"end\": \"xyz\","
+ + " \"partitionNum\": 5}";
+
+ BuildingSingleDimensionShardSpec shardSpec =
+ (BuildingSingleDimensionShardSpec) deserialize(json, ShardSpec.class);
+ Assert.assertEquals(
+ new BuildingSingleDimensionShardSpec(1, "dim", "abc", "xyz", 5),
+ shardSpec
+ );
+ }
+
+ @Test
+ public void testEquals()
+ {
+ Assert.assertEquals(
+ new BuildingSingleDimensionShardSpec(10, "dim", "start", "end", 4),
+ new BuildingSingleDimensionShardSpec(10, "dim", "start", "end", 4)
+ );
+ }
+
+ private static ObjectMapper setupObjectMapper()
{
final ObjectMapper mapper = ShardSpecTestUtils.initObjectMapper();
mapper.registerSubtypes(
new NamedType(BuildingSingleDimensionShardSpec.class, BuildingSingleDimensionShardSpec.TYPE)
);
mapper.setInjectableValues(new Std().addValue(ObjectMapper.class, mapper));
- final BuildingSingleDimensionShardSpec original = new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5);
- final String json = mapper.writeValueAsString(original);
- final BuildingSingleDimensionShardSpec fromJson = (BuildingSingleDimensionShardSpec) mapper.readValue(
- json,
- ShardSpec.class
- );
- Assert.assertEquals(original, fromJson);
+
+ return mapper;
}
- @Test
- public void testEquals()
+ private String serialize(Object object)
{
- EqualsVerifier.forClass(BuildingSingleDimensionShardSpec.class).usingGetClass().verify();
+ try {
+ return OBJECT_MAPPER.writeValueAsString(object);
+ }
+ catch (Exception e) {
+ throw new ISE("Error while serializing");
+ }
}
+
+ private <T> T deserialize(String json, Class<T> clazz)
+ {
+ try {
+ return OBJECT_MAPPER.readValue(json, clazz);
+ }
+ catch (Exception e) {
+ throw new ISE(e, "Error while deserializing");
+ }
+ }
+
}
diff --git a/core/src/test/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpecTest.java
index 3285c15..16d34dd 100644
--- a/core/src/test/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpecTest.java
+++ b/core/src/test/java/org/apache/druid/timeline/partition/DimensionRangeBucketShardSpecTest.java
@@ -35,6 +35,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
public class DimensionRangeBucketShardSpecTest
@@ -66,6 +67,20 @@ public class DimensionRangeBucketShardSpecTest
}
@Test
+ public void testConvert_withSingleDimension()
+ {
+ Assert.assertEquals(
+ new BuildingSingleDimensionShardSpec(1, "dim", "start", "end", 5),
+ new DimensionRangeBucketShardSpec(
+ 1,
+ Collections.singletonList("dim"),
+ StringTuple.create("start"),
+ StringTuple.create("end")
+ ).convert(5)
+ );
+ }
+
+ @Test
public void testCreateChunk()
{
Assert.assertEquals(
diff --git a/core/src/test/java/org/apache/druid/timeline/partition/PartitionBoundariesTest.java b/core/src/test/java/org/apache/druid/timeline/partition/PartitionBoundariesTest.java
index e87818c..96ac623 100644
--- a/core/src/test/java/org/apache/druid/timeline/partition/PartitionBoundariesTest.java
+++ b/core/src/test/java/org/apache/druid/timeline/partition/PartitionBoundariesTest.java
@@ -19,10 +19,10 @@
package org.apache.druid.timeline.partition;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.apache.druid.data.input.StringTuple;
+import org.apache.druid.java.util.common.ISE;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -34,6 +34,8 @@ import java.util.List;
public class PartitionBoundariesTest
{
+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
private PartitionBoundaries target;
private StringTuple[] values;
private List<StringTuple> expected;
@@ -79,16 +81,108 @@ public class PartitionBoundariesTest
@Test
public void handlesRepeatedValue()
{
- Assert.assertEquals(Arrays.asList(null, null), new PartitionBoundaries(StringTuple.create("a"), StringTuple.create("a"), StringTuple.create("a")));
+ Assert.assertEquals(
+ Arrays.asList(null, null),
+ new PartitionBoundaries(
+ StringTuple.create("a"),
+ StringTuple.create("a"),
+ StringTuple.create("a")
+ )
+ );
+ }
+
+ @Test
+ public void serializesDeserializes()
+ {
+ String serialized = serialize(target);
+ Object deserialized = deserialize(serialized, target.getClass());
+ Assert.assertEquals(serialized, serialize(deserialized));
+ }
+
+ @Test
+ public void testSerdeWithMultiDimensions()
+ {
+ PartitionBoundaries original = new PartitionBoundaries(
+ StringTuple.create("a", "10"),
+ StringTuple.create("b", "7"),
+ StringTuple.create("c", "4")
+ );
+
+ String json = serialize(original);
+ PartitionBoundaries deserialized = deserialize(json, PartitionBoundaries.class);
+ Assert.assertEquals(original, deserialized);
}
@Test
- public void serializesDeserializes() throws JsonProcessingException
+ public void testGetSerializableObject_withMultiDimensions()
{
- final ObjectMapper objectMapper = new ObjectMapper();
- String serialized = objectMapper.writeValueAsString(target);
- Object deserialized = objectMapper.readValue(serialized, target.getClass());
- Assert.assertEquals(serialized, objectMapper.writeValueAsString(deserialized));
+ // Create a PartitionBoundaries for multiple dimensions
+ PartitionBoundaries multiDimBoundaries = new PartitionBoundaries(
+ StringTuple.create("a", "10"),
+ StringTuple.create("b", "7"),
+ StringTuple.create("c", "4")
+ );
+
+ // Verify that the serializable object is a List<StringTuple>
+ Object serializableObject = multiDimBoundaries.getSerializableObject();
+ Assert.assertTrue(serializableObject instanceof List);
+ assertThatItemsAreNullOr(StringTuple.class, (List<?>) serializableObject);
+
+ // Verify the output of getSerializableObject can be serialized/deserialized
+ String json = serialize(serializableObject);
+ PartitionBoundaries deserialized = deserialize(json, PartitionBoundaries.class);
+ Assert.assertEquals(multiDimBoundaries, deserialized);
+ }
+
+ @Test
+ public void testGetSerializableObject_withSingleDimension()
+ {
+ // Create a PartitionBoundaries for a single dimension
+ PartitionBoundaries singleDimBoundaries = new PartitionBoundaries(
+ StringTuple.create("a"),
+ StringTuple.create("b"),
+ StringTuple.create("c")
+ );
+
+ // Verify that the serializable object is a List<String>
+ Object serializableObject = singleDimBoundaries.getSerializableObject();
+ Assert.assertTrue(serializableObject instanceof List);
+ assertThatItemsAreNullOr(String.class, (List<?>) serializableObject);
+
+ // Verify the output of getSerializableObject can be serialized/deserialized
+ String json = serialize(serializableObject);
+ PartitionBoundaries deserialized = deserialize(json, PartitionBoundaries.class);
+ Assert.assertEquals(singleDimBoundaries, deserialized);
+ }
+
+ @Test
+ public void testDeserializeArrayOfString()
+ {
+ String json = "[null, \"a\", null]";
+ PartitionBoundaries deserialized = deserialize(json, PartitionBoundaries.class);
+ Assert.assertEquals(
+ new PartitionBoundaries(
+ null,
+ StringTuple.create("a"),
+ StringTuple.create("b")
+ ),
+ deserialized
+ );
+ }
+
+ @Test
+ public void testDeserializeArrayOfTuples()
+ {
+ String json = "[null, [\"a\",\"10\"], null]";
+ PartitionBoundaries deserialized = deserialize(json, PartitionBoundaries.class);
+ Assert.assertEquals(
+ new PartitionBoundaries(
+ null,
+ StringTuple.create("a", "10"),
+ StringTuple.create("a", "20")
+ ),
+ deserialized
+ );
}
@Test
@@ -102,4 +196,41 @@ public class PartitionBoundariesTest
{
EqualsVerifier.forClass(PartitionBoundaries.class).withNonnullFields("delegate").usingGetClass().verify();
}
+
+ /**
+ * Asserts that all the items in the given list are either null or of the
+ * specified class.
+ */
+ private <T> void assertThatItemsAreNullOr(Class<T> clazz, List<?> list)
+ {
+ if (list == null || list.isEmpty()) {
+ return;
+ }
+
+ for (Object item : list) {
+ if (item != null) {
+ Assert.assertSame(clazz, item.getClass());
+ }
+ }
+ }
+
+ private String serialize(Object object)
+ {
+ try {
+ return OBJECT_MAPPER.writeValueAsString(object);
+ }
+ catch (Exception e) {
+ throw new ISE("Error while serializing");
+ }
+ }
+
+ private <T> T deserialize(String json, Class<T> clazz)
+ {
+ try {
+ return OBJECT_MAPPER.readValue(json, clazz);
+ }
+ catch (Exception e) {
+ throw new ISE(e, "Error while deserializing");
+ }
+ }
}
diff --git a/core/src/test/java/org/apache/druid/timeline/partition/SingleDimensionShardSpecTest.java b/core/src/test/java/org/apache/druid/timeline/partition/SingleDimensionShardSpecTest.java
index 1a05f12..219c0ea 100644
--- a/core/src/test/java/org/apache/druid/timeline/partition/SingleDimensionShardSpecTest.java
+++ b/core/src/test/java/org/apache/druid/timeline/partition/SingleDimensionShardSpecTest.java
@@ -19,6 +19,7 @@
package org.apache.druid.timeline.partition;
+import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -163,6 +164,25 @@ public class SingleDimensionShardSpecTest
testSerde(new SingleDimensionShardSpec("dim", "abc", "xyz", 10, null));
}
+ @Test
+ public void testDeserialize() throws JsonProcessingException
+ {
+ final String json = "{\"type\": \"single\","
+ + " \"dimension\": \"dim\","
+ + " \"start\": \"abc\","
+ + "\"end\": \"xyz\","
+ + "\"partitionNum\": 5,"
+ + "\"numCorePartitions\": 10}";
+ ShardSpec shardSpec = OBJECT_MAPPER.readValue(json, ShardSpec.class);
+ Assert.assertTrue(shardSpec instanceof SingleDimensionShardSpec);
+
+ SingleDimensionShardSpec singleDimShardSpec = (SingleDimensionShardSpec) shardSpec;
+ Assert.assertEquals(
+ new SingleDimensionShardSpec("dim", "abc", "xyz", 5, 10),
+ singleDimShardSpec
+ );
+ }
+
private void testSerde(SingleDimensionShardSpec shardSpec) throws IOException
{
String json = OBJECT_MAPPER.writeValueAsString(shardSpec);
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@druid.apache.org
For additional commands, e-mail: commits-help@druid.apache.org