diff --git a/streaming/java/BUILD.bazel b/streaming/java/BUILD.bazel index a3b37f9dc..ccaefb3b0 100644 --- a/streaming/java/BUILD.bazel +++ b/streaming/java/BUILD.bazel @@ -6,6 +6,7 @@ exports_files([ ]) all_modules = [ + "streaming-state", "streaming-api", "streaming-runtime", ] @@ -35,6 +36,7 @@ define_java_module( define_test_lib = True, test_deps = [ "//java:io_ray_ray_api", + ":io_ray_ray_streaming-state", ":io_ray_ray_streaming-api", "@ray_streaming_maven//:com_google_guava_guava", "@ray_streaming_maven//:org_slf4j_slf4j_api", @@ -43,12 +45,37 @@ define_java_module( ], visibility = ["//visibility:public"], deps = [ + ":io_ray_ray_streaming-state", "@ray_streaming_maven//:com_google_guava_guava", "@ray_streaming_maven//:org_slf4j_slf4j_api", "@ray_streaming_maven//:org_slf4j_slf4j_log4j12", ], ) +define_java_module( + name = "streaming-state", + define_test_lib = True, + test_deps = [ + ":io_ray_ray_streaming-state", + "@ray_streaming_maven//:com_google_guava_guava", + "@ray_streaming_maven//:org_slf4j_slf4j_api", + "@ray_streaming_maven//:org_slf4j_slf4j_log4j12", + "@ray_streaming_maven//:org_testng_testng", + "@ray_streaming_maven//:org_mockito_mockito_all", + "@ray_streaming_maven//:de_ruedigermoeller_fst", + "@ray_streaming_maven//:org_apache_commons_commons_lang3", + ], + visibility = ["//visibility:public"], + deps = [ + "@ray_streaming_maven//:com_google_guava_guava", + "@ray_streaming_maven//:de_ruedigermoeller_fst", + "@ray_streaming_maven//:log4j_log4j", + "@ray_streaming_maven//:org_apache_commons_commons_lang3", + "@ray_streaming_maven//:org_slf4j_slf4j_api", + "@ray_streaming_maven//:org_slf4j_slf4j_log4j12", + ], +) + # `//streaming:streaming_java` will be located in jar `streaming` directory, # but we need it located in jar root path. # resource_strip_prefix = "streaming" will make other resources file located in wrong path. @@ -98,6 +125,7 @@ define_java_module( test_deps = [ "//java:io_ray_ray_api", "//java:io_ray_ray_runtime", + ":io_ray_ray_streaming-state", ":io_ray_ray_streaming-api", ":io_ray_ray_streaming-runtime", "@ray_streaming_maven//:com_google_guava_guava", @@ -114,6 +142,7 @@ define_java_module( visibility = ["//visibility:public"], deps = [ ":io_ray_ray_streaming-api", + ":io_ray_ray_streaming-state", "//java:io_ray_ray_api", "//java:io_ray_ray_runtime", "@ray_streaming_maven//:com_github_davidmoten_flatbuffers_java", @@ -136,6 +165,7 @@ java_binary( ":io_ray_ray_streaming-api_test", ":io_ray_ray_streaming-runtime", ":io_ray_ray_streaming-runtime_test", + ":io_ray_ray_streaming-state", "//java:io_ray_ray_runtime", "@ray_streaming_maven//:com_beust_jcommander", "@ray_streaming_maven//:org_mockito_mockito_all", diff --git a/streaming/java/dependencies.bzl b/streaming/java/dependencies.bzl index 80ed895df..40327336d 100644 --- a/streaming/java/dependencies.bzl +++ b/streaming/java/dependencies.bzl @@ -13,6 +13,10 @@ def gen_streaming_java_deps(): "org.slf4j:slf4j-api:1.7.12", "org.slf4j:slf4j-log4j12:1.7.25", "org.apache.logging.log4j:log4j-core:2.8.2", + "org.testng:testng:6.9.10", + "log4j:log4j:1.2.17", + "org.mockito:mockito-all:1.10.19", + "org.apache.commons:commons-lang3:3.3.2", "org.msgpack:msgpack-core:0.8.20", "org.testng:testng:6.9.10", "org.mockito:mockito-all:1.10.19", diff --git a/streaming/java/pom.xml b/streaming/java/pom.xml index d0fea927c..222093041 100644 --- a/streaming/java/pom.xml +++ b/streaming/java/pom.xml @@ -16,34 +16,41 @@ streaming-api streaming-runtime + streaming-state 1.8 UTF-8 0.1-SNAPSHOT + 1.7.25 + 1.2.17 + 6.9.10 + 1.10.19 + 27.0.1-jre + 2.57 com.google.guava guava - 27.0.1-jre + ${guava.version} org.slf4j slf4j-api - 1.7.25 + ${slf4j.version} org.slf4j slf4j-log4j12 - 1.7.25 + ${slf4j.version} org.testng testng - 6.9.10 + ${testng.version} diff --git a/streaming/java/streaming-api/pom.xml b/streaming/java/streaming-api/pom.xml old mode 100755 new mode 100644 index 9c8ff9764..253f7a3b4 --- a/streaming/java/streaming-api/pom.xml +++ b/streaming/java/streaming-api/pom.xml @@ -1,5 +1,5 @@ - + @@ -23,6 +23,11 @@ ${project.version} + org.ray + streaming-state + ${project.version} + + com.google.guava guava 27.0.1-jre diff --git a/streaming/java/streaming-api/pom_template.xml b/streaming/java/streaming-api/pom_template.xml index 6ea6d8701..7c7171cdc 100644 --- a/streaming/java/streaming-api/pom_template.xml +++ b/streaming/java/streaming-api/pom_template.xml @@ -1,5 +1,5 @@ - {auto_gen_header} + {auto_gen_header} @@ -22,6 +22,11 @@ ray-api ${project.version} + + org.ray + streaming-state + ${project.version} + {generated_bzl_deps} diff --git a/streaming/java/streaming-api/src/main/java/io/ray/streaming/api/context/RuntimeContext.java b/streaming/java/streaming-api/src/main/java/io/ray/streaming/api/context/RuntimeContext.java index fa723c8ae..ce92142fb 100644 --- a/streaming/java/streaming-api/src/main/java/io/ray/streaming/api/context/RuntimeContext.java +++ b/streaming/java/streaming-api/src/main/java/io/ray/streaming/api/context/RuntimeContext.java @@ -1,5 +1,14 @@ package io.ray.streaming.api.context; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.ValueState; +import java.util.Map; + /** * Encapsulate the runtime information of a streaming task. */ @@ -11,8 +20,23 @@ public interface RuntimeContext { int getParallelism(); - Long getBatchId(); + Long getCheckpointId(); + + void setCheckpointId(long checkpointId); Long getMaxBatch(); + Map getConfig(); + + void setCurrentKey(Object key); + + KeyStateBackend getKeyStateBackend(); + + void setKeyStateBackend(KeyStateBackend keyStateBackend); + + ValueState getValueState(ValueStateDescriptor stateDescriptor); + + ListState getListState(ListStateDescriptor stateDescriptor); + + MapState getMapState(MapStateDescriptor stateDescriptor); } diff --git a/streaming/java/streaming-api/src/main/java/io/ray/streaming/operator/impl/SourceOperator.java b/streaming/java/streaming-api/src/main/java/io/ray/streaming/operator/impl/SourceOperator.java index 5f3eaf6c1..f85f27551 100644 --- a/streaming/java/streaming-api/src/main/java/io/ray/streaming/operator/impl/SourceOperator.java +++ b/streaming/java/streaming-api/src/main/java/io/ray/streaming/operator/impl/SourceOperator.java @@ -10,7 +10,6 @@ import io.ray.streaming.operator.StreamOperator; import java.util.List; public class SourceOperator extends StreamOperator> { - private SourceContextImpl sourceContext; public SourceOperator(SourceFunction function) { @@ -38,6 +37,7 @@ public class SourceOperator extends StreamOperator> { } class SourceContextImpl implements SourceContext { + private List collectors; public SourceContextImpl(List collectors) { diff --git a/streaming/java/streaming-runtime/pom.xml b/streaming/java/streaming-runtime/pom.xml old mode 100755 new mode 100644 index a38416401..d2a8577a5 --- a/streaming/java/streaming-runtime/pom.xml +++ b/streaming/java/streaming-runtime/pom.xml @@ -1,5 +1,5 @@ - + @@ -71,6 +71,11 @@ powermock-api-mockito 1.6.6 + + org.powermock + powermock-core + 1.6.6 + org.powermock powermock-module-testng diff --git a/streaming/java/streaming-runtime/pom_template.xml b/streaming/java/streaming-runtime/pom_template.xml index 392948d2e..832fd72bd 100644 --- a/streaming/java/streaming-runtime/pom_template.xml +++ b/streaming/java/streaming-runtime/pom_template.xml @@ -1,5 +1,5 @@ - {auto_gen_header} + {auto_gen_header} diff --git a/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/core/processor/StreamProcessor.java b/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/core/processor/StreamProcessor.java index 1a718093d..bee8b8988 100644 --- a/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/core/processor/StreamProcessor.java +++ b/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/core/processor/StreamProcessor.java @@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory; * @param

Type of the specific operator class. */ public abstract class StreamProcessor implements Processor { + private static final Logger LOGGER = LoggerFactory.getLogger(StreamProcessor.class); protected List collectors; diff --git a/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/context/RayRuntimeContext.java b/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/context/RayRuntimeContext.java index 4cb107cd0..345f67a38 100644 --- a/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/context/RayRuntimeContext.java +++ b/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/context/RayRuntimeContext.java @@ -2,19 +2,39 @@ package io.ray.streaming.runtime.worker.context; import static io.ray.streaming.util.Config.STREAMING_BATCH_MAX_COUNT; +import com.google.common.base.Preconditions; import io.ray.streaming.api.context.RuntimeContext; import io.ray.streaming.runtime.core.graph.ExecutionTask; +import io.ray.streaming.state.backend.AbstractKeyStateBackend; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.OperatorStateBackend; +import io.ray.streaming.state.keystate.desc.AbstractStateDescriptor; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.ValueState; import java.util.Map; /** * Use Ray to implement RuntimeContext. */ public class RayRuntimeContext implements RuntimeContext { + + private final Long maxBatch; + /** + * Backend for keyed state. This might be empty if we're not on a keyed stream. + */ + protected transient KeyStateBackend keyStateBackend; + /** + * Backend for operator state. This might be empty + */ + protected transient OperatorStateBackend operatorStateBackend; private int taskId; private int taskIndex; private int parallelism; - private Long batchId; - private final Long maxBatch; + private Long checkpointId; private Map config; public RayRuntimeContext(ExecutionTask executionTask, Map config, @@ -46,8 +66,19 @@ public class RayRuntimeContext implements RuntimeContext { } @Override - public Long getBatchId() { - return batchId; + public Long getCheckpointId() { + return checkpointId; + } + + @Override + public void setCheckpointId(long checkpointId) { + if (this.keyStateBackend != null) { + this.keyStateBackend.setCheckpointId(checkpointId); + } + if (this.operatorStateBackend != null) { + this.operatorStateBackend.setCheckpointId(checkpointId); + } + this.checkpointId = checkpointId; } @Override @@ -55,7 +86,48 @@ public class RayRuntimeContext implements RuntimeContext { return maxBatch; } - public void setBatchId(Long batchId) { - this.batchId = batchId; + @Override + public Map getConfig() { + return config; + } + + + @Override + public void setCurrentKey(Object key) { + this.keyStateBackend.setCurrentKey(key); + } + + @Override + public KeyStateBackend getKeyStateBackend() { + return keyStateBackend; + } + + @Override + public void setKeyStateBackend(KeyStateBackend keyStateBackend) { + this.keyStateBackend = keyStateBackend; + } + + @Override + public ValueState getValueState(ValueStateDescriptor stateDescriptor) { + stateSanityCheck(stateDescriptor, this.keyStateBackend); + return this.keyStateBackend.getValueState(stateDescriptor); + } + + @Override + public ListState getListState(ListStateDescriptor stateDescriptor) { + stateSanityCheck(stateDescriptor, this.keyStateBackend); + return this.keyStateBackend.getListState(stateDescriptor); + } + + @Override + public MapState getMapState(MapStateDescriptor stateDescriptor) { + stateSanityCheck(stateDescriptor, this.keyStateBackend); + return this.keyStateBackend.getMapState(stateDescriptor); + } + + protected void stateSanityCheck(AbstractStateDescriptor stateDescriptor, + AbstractKeyStateBackend backend) { + Preconditions.checkNotNull(stateDescriptor, "The state properties must not be null"); + Preconditions.checkNotNull(backend, "backend must not be null"); } } diff --git a/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/tasks/StreamTask.java b/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/tasks/StreamTask.java index 7bff5101e..d16cc029d 100644 --- a/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/tasks/StreamTask.java +++ b/streaming/java/streaming-runtime/src/main/java/io/ray/streaming/runtime/worker/tasks/StreamTask.java @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class StreamTask implements Runnable { + private static final Logger LOG = LoggerFactory.getLogger(StreamTask.class); protected int taskId; @@ -41,8 +42,8 @@ public abstract class StreamTask implements Runnable { this.worker = worker; prepareTask(); - this.thread = new Thread(Ray.wrapRunnable(this), this.getClass().getName() - + "-" + System.currentTimeMillis()); + this.thread = new Thread(Ray.wrapRunnable(this), + this.getClass().getName() + "-" + System.currentTimeMillis()); this.thread.setDaemon(true); } @@ -105,8 +106,8 @@ public abstract class StreamTask implements Runnable { reader = new DataReader(channelIDs, inputActors, queueConf); } - RuntimeContext runtimeContext = new RayRuntimeContext( - worker.getExecutionTask(), worker.getConfig(), executionNode.getParallelism()); + RuntimeContext runtimeContext = new RayRuntimeContext(worker.getExecutionTask(), + worker.getConfig(), executionNode.getParallelism()); processor.open(collectors, runtimeContext); diff --git a/streaming/java/streaming-state/pom.xml b/streaming/java/streaming-state/pom.xml new file mode 100644 index 000000000..77e3de57b --- /dev/null +++ b/streaming/java/streaming-state/pom.xml @@ -0,0 +1,95 @@ + + + + ray-streaming + io.ray + 0.1-SNAPSHOT + + 4.0.0 + + org.ray + streaming-state + + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + + + log4j + log4j + ${log4j.version} + + + org.testng + testng + ${testng.version} + test + + + org.mockito + mockito-all + ${mockito.version} + test + + + + com.google.guava + guava + ${guava.version} + + + + + + + org.slf4j + slf4j-api + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + org.testng + testng + + + org.mockito + mockito-all + + + de.ruedigermoeller + fst + ${fst.version} + + + com.google.guava + guava + + + org.apache.commons + commons-lang3 + 3.3.2 + + + com.beust + jcommander + 1.27 + + + + \ No newline at end of file diff --git a/streaming/java/streaming-state/pom_template.xml b/streaming/java/streaming-state/pom_template.xml new file mode 100644 index 000000000..adaf7908e --- /dev/null +++ b/streaming/java/streaming-state/pom_template.xml @@ -0,0 +1,22 @@ + + {auto_gen_header} + + + ray-streaming + io.ray + 0.1-SNAPSHOT + + 4.0.0 + + streaming-state + ray streaming state + ray streaming state + jar + + + {generated_bzl_deps} + + + diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/KeyValueState.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/KeyValueState.java new file mode 100644 index 000000000..b62571e59 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/KeyValueState.java @@ -0,0 +1,35 @@ +/* + * 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 io.ray.streaming.state; + +/** + * Key Value State interface. + */ +public interface KeyValueState { + + /** + * get value from state + */ + V get(K key); + + /** + * put key and value into state + */ + void put(K k, V v); +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/PartitionRecord.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/PartitionRecord.java new file mode 100644 index 000000000..4f58ba7c1 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/PartitionRecord.java @@ -0,0 +1,53 @@ +/* + * 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 io.ray.streaming.state; + +import java.io.Serializable; + +/** + * value record for partition. + */ +public class PartitionRecord implements Serializable { + + /** + * The partition number of the partitioned value. + */ + private int partitionID; + private T value; + + public PartitionRecord() { + } + + public PartitionRecord(int partitionID, T value) { + this.partitionID = partitionID; + this.value = value; + } + + public T getValue() { + return value; + } + + public int getPartitionID() { + return partitionID; + } + + public void setPartitionID(int partitionID) { + this.partitionID = partitionID; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StateException.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StateException.java new file mode 100644 index 000000000..af608eac5 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StateException.java @@ -0,0 +1,33 @@ +/* + * 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 io.ray.streaming.state; + +/** + * RuntimeException wrapper, indicating the exceptions occurs in states. + */ +public class StateException extends RuntimeException { + + public StateException(Throwable t) { + super(t); + } + + public StateException(String msg) { + super(msg); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StateStoreManager.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StateStoreManager.java new file mode 100644 index 000000000..5d55273a8 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StateStoreManager.java @@ -0,0 +1,52 @@ +/* + * 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 io.ray.streaming.state; + +/** + * TransactionState interface. + *

+ * Streaming State should implement transaction in case of failure, + * which in our case is four default method, finish, commit, ackCommit, rollback. + */ +public interface StateStoreManager { + + /** + * The finish method is used when the batched data is all saved in state. + * Normally, serialization job is done here. + */ + void finish(long checkpointId); + + /** + * The commit method is used for persistent, and can be used in another thread to reach async + * state commit. + * Normally, data persistent is done here. + */ + void commit(long checkpointId); + + /** + * The ackCommit method is used for cleaning the last checkpoint, and must be called after commit + * in the same thread. + */ + void ackCommit(long checkpointId, long timeStamp); + + /** + * The rollback method is used for recovering the checkpoint. + */ + void rollBack(long checkpointId); +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StorageRecord.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StorageRecord.java new file mode 100644 index 000000000..03421e679 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/StorageRecord.java @@ -0,0 +1,59 @@ +/* + * 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 io.ray.streaming.state; + +import java.io.Serializable; + +/** + * This Class contains a record with some checkpointId. + */ +public class StorageRecord implements Serializable { + + private long checkpointId; + private T value; + + public StorageRecord() { + } + + public StorageRecord(long checkpointId, T value) { + this.checkpointId = checkpointId; + this.value = value; + } + + public T getValue() { + return value; + } + + public long getCheckpointId() { + return checkpointId; + } + + public void setCheckpointId(long checkpointId) { + this.checkpointId = checkpointId; + } + + @Override + public String toString() { + if (value != null) { + return "checkpointId:" + checkpointId + ", value:" + value; + } else { + return "checkpointId:" + checkpointId + ", value:null"; + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/AbstractKeyStateBackend.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/AbstractKeyStateBackend.java new file mode 100644 index 000000000..6edc312c1 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/AbstractKeyStateBackend.java @@ -0,0 +1,211 @@ +/* + * 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 io.ray.streaming.state.backend; + +import io.ray.streaming.state.StateStoreManager; +import io.ray.streaming.state.keystate.desc.AbstractStateDescriptor; +import io.ray.streaming.state.keystate.desc.AbstractStateDescriptor.StateType; +import io.ray.streaming.state.keystate.state.proxy.ListStateStoreManagerProxy; +import io.ray.streaming.state.keystate.state.proxy.MapStateStoreManagerProxy; +import io.ray.streaming.state.keystate.state.proxy.ValueStateStoreManagerProxy; +import io.ray.streaming.state.store.KeyMapStore; +import io.ray.streaming.state.store.KeyValueStore; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Transaction support primitive operations like finish, commit, ackcommit and rollback. + *

+ * State value modification is not thread safe! By default, every processing thread has its own + * space to handle state. + */ +public abstract class AbstractKeyStateBackend implements StateStoreManager { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractKeyStateBackend.class); + + protected long currentCheckpointId; + protected Object currentKey; + protected int keyGroupIndex = -1; + protected Map valueManagerProxyHashMap = new HashMap<>(); + protected Map listManagerProxyHashMap = new HashMap<>(); + protected Map mapManagerProxyHashMap = new HashMap<>(); + protected Set descNamespace; + + /** + * tablename, KeyValueStore key, checkpointId, content + */ + protected Map>> backStorageCache; + private AbstractStateBackend backend; + + public AbstractKeyStateBackend(AbstractStateBackend backend) { + this.backStorageCache = new HashMap<>(); + this.backend = backend; + this.descNamespace = new HashSet<>(); + } + + public void put(AbstractStateDescriptor descriptor, K key, T value) { + String desc = descriptor.getIdentify(); + if (descriptor.getStateType() == StateType.VALUE) { + if (this.valueManagerProxyHashMap.containsKey(desc)) { + valueManagerProxyHashMap.get(desc).put((String) key, value); + } + } else if (descriptor.getStateType() == StateType.LIST) { + if (this.listManagerProxyHashMap.containsKey(desc)) { + listManagerProxyHashMap.get(desc).put((String) key, value); + } + } else if (descriptor.getStateType() == StateType.MAP) { + if (this.mapManagerProxyHashMap.containsKey(desc)) { + mapManagerProxyHashMap.get(desc).put((String) key, value); + } + } + } + + public T get(AbstractStateDescriptor descriptor, K key) { + String desc = descriptor.getIdentify(); + if (descriptor.getStateType() == StateType.VALUE) { + if (this.valueManagerProxyHashMap.containsKey(desc)) { + return (T) valueManagerProxyHashMap.get(desc).get((String) key); + } + } else if (descriptor.getStateType() == StateType.LIST) { + if (this.listManagerProxyHashMap.containsKey(desc)) { + return (T) listManagerProxyHashMap.get(desc).get((String) key); + } + } else if (descriptor.getStateType() == StateType.MAP) { + if (this.mapManagerProxyHashMap.containsKey(desc)) { + return (T) mapManagerProxyHashMap.get(desc).get((String) key); + } + } + return null; + } + + @Override + public void finish(long checkpointId) { + for (Entry entry : valueManagerProxyHashMap.entrySet()) { + entry.getValue().finish(checkpointId); + } + for (Entry entry : listManagerProxyHashMap.entrySet()) { + entry.getValue().finish(checkpointId); + } + for (Entry entry : mapManagerProxyHashMap.entrySet()) { + entry.getValue().finish(checkpointId); + } + } + + @Override + public void commit(long checkpointId) { + for (Entry entry : valueManagerProxyHashMap.entrySet()) { + entry.getValue().commit(checkpointId); + } + for (Entry entry : listManagerProxyHashMap.entrySet()) { + entry.getValue().commit(checkpointId); + } + for (Entry entry : mapManagerProxyHashMap.entrySet()) { + entry.getValue().commit(checkpointId); + } + } + + @Override + public void ackCommit(long checkpointId, long timeStamp) { + for (Entry entry : valueManagerProxyHashMap.entrySet()) { + entry.getValue().ackCommit(checkpointId, timeStamp); + } + for (Entry entry : listManagerProxyHashMap.entrySet()) { + entry.getValue().ackCommit(checkpointId, timeStamp); + } + for (Entry entry : mapManagerProxyHashMap.entrySet()) { + entry.getValue().ackCommit(checkpointId, timeStamp); + } + } + + @Override + public void rollBack(long checkpointId) { + for (Entry entry : valueManagerProxyHashMap.entrySet()) { + LOG.warn("backend rollback:{},{}", entry.getKey(), checkpointId); + entry.getValue().rollBack(checkpointId); + } + for (Entry entry : listManagerProxyHashMap.entrySet()) { + LOG.warn("backend rollback:{},{}", entry.getKey(), checkpointId); + entry.getValue().rollBack(checkpointId); + } + for (Entry entry : mapManagerProxyHashMap.entrySet()) { + LOG.warn("backend rollback:{},{}", entry.getKey(), checkpointId); + entry.getValue().rollBack(checkpointId); + } + } + + public KeyValueStore> getBackStorage(String tableName) { + if (this.backStorageCache.containsKey(tableName)) { + return this.backStorageCache.get(tableName); + } else { + KeyMapStore ikvStore = this.backend.getKeyMapStore(tableName); + this.backStorageCache.put(tableName, ikvStore); + return ikvStore; + } + } + + public KeyValueStore> getBackStorage( + AbstractStateDescriptor stateDescriptor) { + String tableName = this.backend.getTableName(stateDescriptor); + return getBackStorage(tableName); + } + + public StateStrategy getStateStrategy() { + return this.backend.getStateStrategy(); + } + + public BackendType getBackendType() { + return this.backend.getBackendType(); + } + + public Object getCurrentKey() { + return this.currentKey; + } + + public abstract void setCurrentKey(Object currentKey); + + public long getCheckpointId() { + return this.currentCheckpointId; + } + + public void setCheckpointId(long checkpointId) { + this.currentCheckpointId = checkpointId; + } + + public void setContext(long checkpointId, Object currentKey) { + setCheckpointId(checkpointId); + setCurrentKey(currentKey); + } + + public AbstractStateBackend getBackend() { + return backend; + } + + public int getKeyGroupIndex() { + return this.keyGroupIndex; + } + + public void setKeyGroupIndex(int keyGroupIndex) { + this.keyGroupIndex = keyGroupIndex; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/AbstractStateBackend.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/AbstractStateBackend.java new file mode 100644 index 000000000..d01d7a6b1 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/AbstractStateBackend.java @@ -0,0 +1,74 @@ +/* + * 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 io.ray.streaming.state.backend; + +import static io.ray.streaming.state.config.ConfigKey.DELIMITER; + +import io.ray.streaming.state.config.ConfigKey; +import io.ray.streaming.state.keystate.desc.AbstractStateDescriptor; +import io.ray.streaming.state.serialization.KeyMapStoreSerializer; +import io.ray.streaming.state.store.KeyMapStore; +import io.ray.streaming.state.store.KeyValueStore; +import java.io.Serializable; +import java.util.Map; + +/** + * This class is the abstract class for different kinds of StateBackend. + */ +public abstract class AbstractStateBackend implements Serializable { + + protected final Map config; + protected final StateStrategy stateStrategy; + + protected final BackendType backendType; + protected int keyGroupIndex = -1; + + protected AbstractStateBackend(Map config) { + this.stateStrategy = StateStrategy.getEnum(ConfigKey.getStateStrategyEnum(config)); + this.backendType = BackendType.getEnum(ConfigKey.getBackendType(config)); + this.config = config; + } + + public abstract KeyValueStore getKeyValueStore(String tableName); + + public abstract KeyMapStore getKeyMapStore(String tableName); + + public abstract KeyMapStore getKeyMapStore( + String tableName, KeyMapStoreSerializer keyMapStoreSerializer); + + public BackendType getBackendType() { + return backendType; + } + + public StateStrategy getStateStrategy() { + return stateStrategy; + } + + public String getTableName(AbstractStateDescriptor stateDescriptor) { + return stateDescriptor.getTableName(); + } + + public String getStateKey(String descName, String currentKey) { + return descName + DELIMITER + currentKey; + } + + public void setKeyGroupIndex(int keyGroupIndex) { + this.keyGroupIndex = keyGroupIndex; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/BackendType.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/BackendType.java new file mode 100644 index 000000000..cc616647e --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/BackendType.java @@ -0,0 +1,41 @@ +/* + * 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 io.ray.streaming.state.backend; + +/** + * Backend Types. + */ +public enum BackendType { + /** + * Saving the state values in memory + */ + MEMORY; + + /** + * get the enum from input string value, ignoring the case + */ + public static BackendType getEnum(String value) { + for (BackendType v : values()) { + if (v.name().equalsIgnoreCase(value)) { + return v; + } + } + return MEMORY; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/KeyStateBackend.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/KeyStateBackend.java new file mode 100644 index 000000000..9e73295ec --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/KeyStateBackend.java @@ -0,0 +1,134 @@ +/* + * 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 io.ray.streaming.state.backend; + +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.KeyGroupAssignment; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.ValueState; +import io.ray.streaming.state.keystate.state.proxy.ListStateStoreManagerProxy; +import io.ray.streaming.state.keystate.state.proxy.MapStateStoreManagerProxy; +import io.ray.streaming.state.keystate.state.proxy.ValueStateStoreManagerProxy; + +/** + * key state backend manager, managing different kinds of states in different thread. + * This class is not thread safe. + */ +public class KeyStateBackend extends AbstractKeyStateBackend { + + protected final int numberOfKeyGroups; + protected final KeyGroup keyGroup; + + public KeyStateBackend(int numberOfKeyGroups, KeyGroup keyGroup, + AbstractStateBackend abstractStateBackend) { + super(abstractStateBackend); + this.numberOfKeyGroups = numberOfKeyGroups; + this.keyGroup = keyGroup; + } + + /** + * get new value state proxy + */ + protected ValueStateStoreManagerProxy newValueStateProxy( + ValueStateDescriptor stateDescriptor) { + return new ValueStateStoreManagerProxy<>(this, stateDescriptor); + } + + public ValueState getValueState(ValueStateDescriptor stateDescriptor) { + String desc = stateDescriptor.getIdentify(); + if (valueManagerProxyHashMap.containsKey(desc)) { + return valueManagerProxyHashMap.get(desc).getValueState(); + } else { + ValueStateStoreManagerProxy valueStateProxy = newValueStateProxy(stateDescriptor); + valueManagerProxyHashMap.put(desc, valueStateProxy); + return valueStateProxy.getValueState(); + } + } + + /** + * get new list state proxy + */ + protected ListStateStoreManagerProxy newListStateProxy( + ListStateDescriptor stateDescriptor) { + return new ListStateStoreManagerProxy<>(this, stateDescriptor); + } + + public ListState getListState(ListStateDescriptor stateDescriptor) { + String desc = stateDescriptor.getIdentify(); + if (listManagerProxyHashMap.containsKey(desc)) { + ListStateStoreManagerProxy listStateProxy = listManagerProxyHashMap.get(desc); + return listStateProxy.getListState(); + } else { + ListStateStoreManagerProxy listStateProxy = newListStateProxy(stateDescriptor); + listManagerProxyHashMap.put(desc, listStateProxy); + return listStateProxy.getListState(); + } + } + + /** + * get map state proxy + */ + protected MapStateStoreManagerProxy newMapStateProxy( + MapStateDescriptor stateDescriptor) { + return new MapStateStoreManagerProxy<>(this, stateDescriptor); + } + + public MapState getMapState(MapStateDescriptor stateDescriptor) { + String desc = stateDescriptor.getIdentify(); + if (mapManagerProxyHashMap.containsKey(desc)) { + MapStateStoreManagerProxy mapStateProxy = mapManagerProxyHashMap.get(desc); + return mapStateProxy.getMapState(); + } else { + MapStateStoreManagerProxy mapStateProxy = newMapStateProxy(stateDescriptor); + mapManagerProxyHashMap.put(desc, mapStateProxy); + return mapStateProxy.getMapState(); + } + } + + @Override + public void setCurrentKey(Object currentKey) { + super.keyGroupIndex = KeyGroupAssignment + .assignKeyGroupIndexForKey(currentKey, numberOfKeyGroups); + super.currentKey = currentKey; + } + + public int getNumberOfKeyGroups() { + return numberOfKeyGroups; + } + + public KeyGroup getKeyGroup() { + return keyGroup; + } + + public void close() { + for (ValueStateStoreManagerProxy proxy : valueManagerProxyHashMap.values()) { + proxy.close(); + } + for (ListStateStoreManagerProxy proxy : listManagerProxyHashMap.values()) { + proxy.close(); + } + for (MapStateStoreManagerProxy proxy : mapManagerProxyHashMap.values()) { + proxy.close(); + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/OperatorStateBackend.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/OperatorStateBackend.java new file mode 100644 index 000000000..68bdac581 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/OperatorStateBackend.java @@ -0,0 +1,76 @@ +/* + * 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 io.ray.streaming.state.backend; + +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.impl.OperatorStateImpl; +import io.ray.streaming.state.keystate.state.proxy.ListStateStoreManagerProxy; + +/** + * OperatorState manager for getting split or union list state. + */ +public class OperatorStateBackend extends AbstractKeyStateBackend { + + public OperatorStateBackend(AbstractStateBackend backend) { + super(backend); + } + + @Override + public void setCurrentKey(Object currentKey) { + super.currentKey = currentKey; + } + + protected ListStateStoreManagerProxy newListStateStoreManagerProxy( + ListStateDescriptor stateDescriptor) { + return new ListStateStoreManagerProxy<>(this, stateDescriptor); + } + + /** + * get spitted List for different operator instance. + */ + public ListState getSplitListState(ListStateDescriptor stateDescriptor) { + String desc = stateDescriptor.getIdentify(); + if (listManagerProxyHashMap.containsKey(desc)) { + ListStateStoreManagerProxy listStateProxy = listManagerProxyHashMap.get(desc); + return listStateProxy.getListState(); + } else { + ListStateStoreManagerProxy listStateProxy = newListStateStoreManagerProxy(stateDescriptor); + listManagerProxyHashMap.put(desc, listStateProxy); + ((OperatorStateImpl) (listStateProxy.getListState())).setSplit(true); + return listStateProxy.getListState(); + } + } + + /** + * get a union List for different operator instance. + */ + public ListState getUnionListState(ListStateDescriptor stateDescriptor) { + String desc = stateDescriptor.getIdentify(); + if (listManagerProxyHashMap.containsKey(desc)) { + ListStateStoreManagerProxy listStateProxy = listManagerProxyHashMap.get(desc); + return listStateProxy.getListState(); + } else { + ListStateStoreManagerProxy listStateProxy = newListStateStoreManagerProxy(stateDescriptor); + listManagerProxyHashMap.put(desc, listStateProxy); + ((OperatorStateImpl) (listStateProxy.getListState())).init(); + return listStateProxy.getListState(); + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/StateBackendBuilder.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/StateBackendBuilder.java new file mode 100644 index 000000000..27e849a96 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/StateBackendBuilder.java @@ -0,0 +1,50 @@ +/* + * 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 io.ray.streaming.state.backend; + +import io.ray.streaming.state.backend.impl.MemoryStateBackend; +import io.ray.streaming.state.config.ConfigKey; +import java.util.Map; + +/** + * State Backend Builder. + */ +public class StateBackendBuilder { + + private static AbstractStateBackend getStateBackend(Map config, + BackendType type) { + switch (type) { + case MEMORY: + return new MemoryStateBackend(config); + default: + throw new RuntimeException(type.name() + " not supported"); + } + } + + public static AbstractStateBackend buildStateBackend(Map config) { + BackendType type; + if (config == null) { + type = BackendType.MEMORY; + } else { + type = BackendType.getEnum(config.get(ConfigKey.STATE_BACKEND_TYPE)); + } + + return getStateBackend(config, type); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/StateStrategy.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/StateStrategy.java new file mode 100644 index 000000000..40d231cf8 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/StateStrategy.java @@ -0,0 +1,44 @@ +/* + * 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 io.ray.streaming.state.backend; + +/** + * This class describe State Saving Model. + */ +public enum StateStrategy { + /** + * save two version together in case of rollback. + */ + DUAL_VERSION, + + /** + * for storage supporting mvcc, we save only current version. + */ + SINGLE_VERSION; + + + public static StateStrategy getEnum(String value) { + for (StateStrategy v : values()) { + if (v.name().equalsIgnoreCase(value)) { + return v; + } + } + throw new IllegalArgumentException(value + " strategy is not supported"); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/impl/MemoryStateBackend.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/impl/MemoryStateBackend.java new file mode 100644 index 000000000..67eeebf33 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/backend/impl/MemoryStateBackend.java @@ -0,0 +1,54 @@ +/* + * 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 io.ray.streaming.state.backend.impl; + +import io.ray.streaming.state.backend.AbstractStateBackend; +import io.ray.streaming.state.serialization.KeyMapStoreSerializer; +import io.ray.streaming.state.store.KeyMapStore; +import io.ray.streaming.state.store.KeyValueStore; +import io.ray.streaming.state.store.impl.MemoryKeyMapStore; +import io.ray.streaming.state.store.impl.MemoryKeyValueStore; +import java.util.Map; + +/** + * MemoryStateBackend. Supporting memory store. + */ +public class MemoryStateBackend extends AbstractStateBackend { + + public MemoryStateBackend(Map config) { + super(config); + } + + @Override + public KeyValueStore getKeyValueStore(String tableName) { + return new MemoryKeyValueStore<>(); + } + + @Override + public KeyMapStore getKeyMapStore(String tableName) { + return new MemoryKeyMapStore<>(); + } + + @Override + public KeyMapStore getKeyMapStore( + String tableName, KeyMapStoreSerializer keyMapStoreSerializer) { + return new MemoryKeyMapStore<>(); + } + +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/config/ConfigHelper.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/config/ConfigHelper.java new file mode 100644 index 000000000..0fbf88f3e --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/config/ConfigHelper.java @@ -0,0 +1,45 @@ +/* + * 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 io.ray.streaming.state.config; + +import java.util.Map; + +/** + * Config Helper figure out the config info. + * Todo replace this to config module. + */ +public class ConfigHelper { + + public static int getIntegerOrDefault(Map config, String configKey, int defaultValue) { + if (config.containsKey(configKey)) { + return Integer.valueOf(String.valueOf(config.get(configKey))); + } else { + return defaultValue; + } + } + + public static String getStringOrDefault(Map config, String configKey, String defaultValue) { + if (config.containsKey(configKey)) { + return String.valueOf(config.get(configKey)); + } else { + return defaultValue; + } + } + +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/config/ConfigKey.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/config/ConfigKey.java new file mode 100644 index 000000000..f7d8a42a0 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/config/ConfigKey.java @@ -0,0 +1,58 @@ +/* + * 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 io.ray.streaming.state.config; + +import java.util.Map; + +/** + * state config keys. + * Todo replace this to config module. + */ +public final class ConfigKey { + + /** + * backend + */ + public static final String STATE_BACKEND_TYPE = "state.backend.type"; + public static final String STATE_TABLE_NAME = "state.table.name"; + public static final String STATE_STRATEGY_MODE = "state.strategy.mode"; + public static final String NUMBER_PER_CHECKPOINT = "number.per.checkpoint"; + public static final String JOB_MAX_PARALLEL = "job.max.parallel"; + public static final String DELIMITER = "\u0001\u0008"; // for String delimiter + + private ConfigKey() { + throw new AssertionError(); + } + + public static String getStateStrategyEnum(Map config) { + return ConfigHelper.getStringOrDefault(config, STATE_STRATEGY_MODE, "DUAL_VERSION"); + } + + public static String getBackendType(Map config) { + return ConfigHelper.getStringOrDefault(config, STATE_BACKEND_TYPE, "MEMORY"); + } + + public static int getNumberPerCheckpoint(Map config) { + return ConfigHelper.getIntegerOrDefault(config, NUMBER_PER_CHECKPOINT, 5); + } + + public static String getStateTableName(Map config) { + return ConfigHelper.getStringOrDefault(config, STATE_TABLE_NAME, "table"); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/KeyGroup.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/KeyGroup.java new file mode 100644 index 000000000..fb24c72d1 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/KeyGroup.java @@ -0,0 +1,66 @@ +/* + * 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 io.ray.streaming.state.keystate; + +import com.google.common.base.Preconditions; +import java.io.Serializable; + +/** + * This class defines key-groups. Key-groups is + * the key space in a job, which is partitioned for keyed state processing in state backend. + * The boundaries of the key-group are inclusive. + */ +public class KeyGroup implements Serializable { + + private final int startIndex; + private final int endIndex; + + /** + * Defines the range [startIndex, endIndex] + * + * @param startIndex start of the range (inclusive) + * @param endIndex end of the range (inclusive) + */ + public KeyGroup(int startIndex, int endIndex) { + Preconditions.checkArgument(startIndex >= 0 && startIndex <= endIndex); + this.startIndex = startIndex; + this.endIndex = endIndex; + Preconditions.checkArgument(size() >= 0, "overflow detected."); + } + + /** + * @return The number of key-group in the range + */ + public int size() { + return 1 + endIndex - startIndex; + } + + public int getStartIndex() { + return startIndex; + } + + public int getEndIndex() { + return endIndex; + } + + @Override + public String toString() { + return "KeyGroup{" + "startIndex=" + startIndex + ", endIndex=" + endIndex + '}'; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/KeyGroupAssignment.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/KeyGroupAssignment.java new file mode 100644 index 000000000..77b465f74 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/KeyGroupAssignment.java @@ -0,0 +1,73 @@ +/* + * 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 io.ray.streaming.state.keystate; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class defines key-group assignment algorithm。 + */ +public final class KeyGroupAssignment { + + /** + * Computes the range of key-groups that are assigned for a given operator instance. + * + * @param maxParallelism Maximal parallelism of the job. + * @param parallelism Parallelism for the job. <= maxParallelism. + * @param index index of the operator instance. + */ + public static KeyGroup getKeyGroup(int maxParallelism, int parallelism, int index) { + Preconditions.checkArgument(maxParallelism >= parallelism, + "Maximum parallelism (%s) must not be smaller than parallelism(%s)", maxParallelism, + parallelism); + + int start = index == 0 ? 0 : ((index * maxParallelism - 1) / parallelism) + 1; + int end = ((index + 1) * maxParallelism - 1) / parallelism; + return new KeyGroup(start, end); + } + + /** + * Assigning the key to a key-group index. + * + * @param key the key to assign. + * @param maxParallelism the maximum parallelism. + * @return the key-group index to which the given key is assigned. + */ + public static int assignKeyGroupIndexForKey(Object key, int maxParallelism) { + return Math.abs(key.hashCode() % maxParallelism); + } + + public static Map> computeKeyGroupToTask(int maxParallelism, + List targetTasks) { + Map> keyGroupToTask = new ConcurrentHashMap<>(); + for (int index = 0; index < targetTasks.size(); index++) { + KeyGroup taskKeyGroup = getKeyGroup(maxParallelism, targetTasks.size(), index); + for (int groupId = taskKeyGroup.getStartIndex(); groupId <= taskKeyGroup.getEndIndex(); + groupId++) { + keyGroupToTask.put(groupId, ImmutableList.of(targetTasks.get(index))); + } + } + return keyGroupToTask; + } + +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/AbstractStateDescriptor.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/AbstractStateDescriptor.java new file mode 100644 index 000000000..c85decf95 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/AbstractStateDescriptor.java @@ -0,0 +1,88 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + +import com.google.common.base.Preconditions; +import io.ray.streaming.state.keystate.state.State; + +/** + * This class defines basic data structures of StateDescriptor. + */ +public abstract class AbstractStateDescriptor { + + private final String name; + private String tableName; + private Class type; + + protected AbstractStateDescriptor(String name, Class type) { + this.name = name; + this.type = type; + } + + public String getName() { + return name; + } + + public Class getType() { + return type; + } + + protected Class setType(Class type) { + return type; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public abstract StateType getStateType(); + + public String getIdentify() { + Preconditions.checkArgument(this.tableName != null, "table name must not be null."); + Preconditions.checkArgument(this.name != null, "table name must not be null."); + return this.name; + } + + @Override + public String toString() { + return "AbstractStateDescriptor{" + "tableName='" + tableName + '\'' + ", name='" + name + '\'' + + ", type=" + type + '}'; + } + + public enum StateType { + /** + * value state + */ + VALUE, + + /** + * list state + */ + LIST, + + /** + * map state + */ + MAP + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/ListStateDescriptor.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/ListStateDescriptor.java new file mode 100644 index 000000000..c6a7af6b6 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/ListStateDescriptor.java @@ -0,0 +1,83 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + + +import static io.ray.streaming.state.config.ConfigKey.DELIMITER; + +import io.ray.streaming.state.keystate.state.ListState; + +/** + * ListStateDescriptor. + */ +public class ListStateDescriptor extends AbstractStateDescriptor, T> { + + private final boolean isOperatorList; + private int index; + private int partitionNum; + + private ListStateDescriptor(String name, Class type, boolean isOperatorList) { + super(name, type); + this.isOperatorList = isOperatorList; + } + + public static ListStateDescriptor build(String name, Class type) { + return build(name, type, false); + } + + public static ListStateDescriptor build(String name, Class type, + boolean isOperatorList) { + return new ListStateDescriptor<>(name, type, isOperatorList); + } + + public boolean isOperatorList() { + return isOperatorList; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getPartitionNumber() { + return partitionNum; + } + + public void setPartitionNumber(int number) { + this.partitionNum = number; + } + + @Override + public StateType getStateType() { + return StateType.LIST; + } + + @Override + public String getIdentify() { + if (isOperatorList) { + return String + .format("%s%s%d%s%d", super.getIdentify(), DELIMITER, partitionNum, DELIMITER, index); + } else { + return super.getIdentify(); + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/MapStateDescriptor.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/MapStateDescriptor.java new file mode 100644 index 000000000..15373265e --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/MapStateDescriptor.java @@ -0,0 +1,43 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + +import io.ray.streaming.state.keystate.state.MapState; +import java.util.Map; + +/** + * MapStateDescriptor. + */ +public class MapStateDescriptor extends AbstractStateDescriptor, Map> { + + public MapStateDescriptor(String name, Class keyType, Class valueType) { + super(name, null); + // TODO: use the types to help serde + } + + public static MapStateDescriptor build(String name, Class keyType, + Class valueType) { + return new MapStateDescriptor<>(name, keyType, valueType); + } + + @Override + public StateType getStateType() { + return StateType.MAP; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/ValueStateDescriptor.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/ValueStateDescriptor.java new file mode 100644 index 000000000..e067ba71d --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/desc/ValueStateDescriptor.java @@ -0,0 +1,47 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + +import io.ray.streaming.state.keystate.state.ValueState; + +/** + * ValueStateDescriptor. + */ +public class ValueStateDescriptor extends AbstractStateDescriptor, T> { + + private final T defaultValue; + + public ValueStateDescriptor(String name, Class type, T defaultValue) { + super(name, type); + this.defaultValue = defaultValue; + } + + public static ValueStateDescriptor build(String name, Class type, T defaultValue) { + return new ValueStateDescriptor<>(name, type, defaultValue); + } + + public T getDefaultValue() { + return defaultValue; + } + + @Override + public StateType getStateType() { + return StateType.VALUE; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/ListState.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/ListState.java new file mode 100644 index 000000000..d5efa6670 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/ListState.java @@ -0,0 +1,41 @@ +/* + * 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 io.ray.streaming.state.keystate.state; + +import java.util.List; + +/** + * ListState interface. + */ +public interface ListState extends UnaryState> { + + /** + * add the value to list + * + * @param value the new value + */ + void add(T value); + + /** + * update list state + * + * @param list the new value + */ + void update(List list); +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/MapState.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/MapState.java new file mode 100644 index 000000000..663da8f71 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/MapState.java @@ -0,0 +1,112 @@ +/* + * 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 io.ray.streaming.state.keystate.state; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +/** + * MapState interface. + */ +public interface MapState extends UnaryState> { + + /** + * Returns the current value associated with the given key. + * + * @param key The key of the mapping + * @return The value of the mapping with the given key + */ + V get(K key); + + /** + * Associates a new value with the given key. + * + * @param key The key of the mapping + * @param value The new value of the mapping + */ + void put(K key, V value); + + /** + * Resets the state value. + * + * @param map The mappings for reset in this state + */ + void update(Map map); + + /** + * Copies all of the mappings from the given map into the state. + * + * @param map The mappings to be stored in this state + */ + void putAll(Map map); + + /** + * Deletes the mapping of the given key. + * + * @param key The key of the mapping + */ + void remove(K key); + + /** + * Returns whether there exists the given mapping. + * + * @param key The key of the mapping + * @return True if there exists a mapping whose key equals to the given key + */ + default boolean contains(K key) { + return get().containsKey(key); + } + + /** + * Returns all the mappings in the state + * + * @return An iterable view of all the key-value pairs in the state. + */ + default Iterable> entries() { + return get().entrySet(); + } + + /** + * Returns all the keys in the state + * + * @return An iterable view of all the keys in the state. + */ + default Iterable keys() { + return get().keySet(); + } + + /** + * Returns all the values in the state. + * + * @return An iterable view of all the values in the state. + */ + default Iterable values() { + return get().values(); + } + + /** + * Iterates over all the mappings in the state. + * + * @return An iterator over all the mappings in the state + */ + default Iterator> iterator() { + return get().entrySet().iterator(); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/State.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/State.java new file mode 100644 index 000000000..6b18c62d8 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/State.java @@ -0,0 +1,30 @@ +/* + * 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 io.ray.streaming.state.keystate.state; + +/** + * State interface. + */ +public interface State { + + /** + * set current key of the state + */ + void setCurrentKey(Object currentKey); +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/UnaryState.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/UnaryState.java new file mode 100644 index 000000000..a67196d20 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/UnaryState.java @@ -0,0 +1,32 @@ +/* + * 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 io.ray.streaming.state.keystate.state; + +/** + * one value per state. + */ +public interface UnaryState extends State { + + /** + * get the value in state + * + * @return the value in state + */ + O get(); +} \ No newline at end of file diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/ValueState.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/ValueState.java new file mode 100644 index 000000000..e26b23b8a --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/ValueState.java @@ -0,0 +1,32 @@ +/* + * 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 io.ray.streaming.state.keystate.state; + +/** + * ValueState interface. + */ +public interface ValueState extends UnaryState { + + /** + * update the value + * + * @param value the new value + */ + void update(T value); +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/ListStateImpl.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/ListStateImpl.java new file mode 100644 index 000000000..a56da03ec --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/ListStateImpl.java @@ -0,0 +1,69 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import io.ray.streaming.state.backend.AbstractKeyStateBackend; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import java.util.ArrayList; +import java.util.List; + +/** + * ListState implementation. + */ +public class ListStateImpl implements ListState { + + private final StateHelper> helper; + + public ListStateImpl(ListStateDescriptor descriptor, AbstractKeyStateBackend backend) { + this.helper = new StateHelper<>(backend, descriptor); + } + + @Override + public List get() { + List list = helper.get(); + if (list == null) { + list = new ArrayList<>(); + } + return list; + } + + @Override + public void add(V value) { + List list = helper.get(); + if (list == null) { + list = new ArrayList<>(); + } + list.add(value); + helper.put(list); + } + + @Override + public void update(List list) { + if (list == null) { + list = new ArrayList<>(); + } + helper.put(list); + } + + @Override + public void setCurrentKey(Object currentKey) { + helper.setCurrentKey(currentKey); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/MapStateImpl.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/MapStateImpl.java new file mode 100644 index 000000000..0d0729133 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/MapStateImpl.java @@ -0,0 +1,92 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.state.MapState; +import java.util.HashMap; +import java.util.Map; + +/** + * MapState implementation. + */ +public class MapStateImpl implements MapState { + + private final StateHelper> helper; + + public MapStateImpl(MapStateDescriptor descriptor, KeyStateBackend backend) { + this.helper = new StateHelper<>(backend, descriptor); + } + + @Override + public Map get() { + Map map = helper.get(); + if (map == null) { + map = new HashMap<>(); + } + return map; + } + + @Override + public V get(K key) { + Map map = get(); + return map.get(key); + } + + @Override + public void put(K key, V value) { + Map map = get(); + + map.put(key, value); + helper.put(map); + } + + @Override + public void update(Map map) { + if (map == null) { + map = new HashMap<>(); + } + helper.put(map); + } + + @Override + public void putAll(Map newMap) { + Map map = get(); + + map.putAll(newMap); + helper.put(map); + } + + @Override + public void remove(K key) { + Map map = get(); + + map.remove(key); + helper.put(map); + } + + /** + * set current key of the state + */ + @Override + public void setCurrentKey(Object currentKey) { + helper.setCurrentKey(currentKey); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/OperatorStateImpl.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/OperatorStateImpl.java new file mode 100644 index 000000000..e1b8defd3 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/OperatorStateImpl.java @@ -0,0 +1,150 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import static io.ray.streaming.state.config.ConfigKey.DELIMITER; + +import com.google.common.base.Preconditions; +import io.ray.streaming.state.PartitionRecord; +import io.ray.streaming.state.backend.AbstractKeyStateBackend; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * This class defines the implementation of operator state. + * When the state is initialized, we must scan the whole table. + * And if the state type is splitList, all the records must be spitted. + */ +public class OperatorStateImpl implements ListState { + + private final ListStateDescriptor descriptor; + private StateHelper>> helper; + private List> allList; + + private AtomicBoolean hasInit; + private boolean isSplit; + + public OperatorStateImpl(ListStateDescriptor descriptor, AbstractKeyStateBackend backend) { + this.descriptor = descriptor; + this.helper = new StateHelper<>(backend, descriptor); + this.isSplit = false; + this.hasInit = new AtomicBoolean(false); + this.allList = new ArrayList<>(); + } + + private void splitList() { + // fetch target list and save + List> list = new ArrayList<>(); + int step = descriptor.getPartitionNumber(); + Preconditions.checkArgument(step > 0); + + for (int round = 0; round * step <= allList.size(); round++) { + int m = round * step + descriptor.getIndex(); + if (m < allList.size()) { + PartitionRecord tmp = allList.get(m); + tmp.setPartitionID(descriptor.getPartitionNumber()); + list.add(tmp); + } + } + helper.put(list, getStateKey()); + allList.clear(); + } + + private void scan() { + int partitionNum = -1; + int index = 0; + while (true) { + List> list = helper.getBackend() + .get(descriptor, getKey(descriptor.getIdentify(), index)); + if (list != null && !list.isEmpty()) { + partitionNum = list.get(0).getPartitionID(); + allList.addAll(list); + } + if (++index >= partitionNum) { + break; + } + } + } + + public void init() { + scan(); + + if (isSplit) { + splitList(); + } + } + + private String getKey(String descName, int index) { + String[] stringList = descName.split(DELIMITER); + return String.format("%s%s%s%s%d", stringList[0], DELIMITER, stringList[1], DELIMITER, index); + } + + protected String getStateKey() { + return getKey(this.descriptor.getIdentify(), this.descriptor.getIndex()); + } + + @Override + public void setCurrentKey(Object currentKey) { + throw new UnsupportedOperationException("OperatorState cannot set current key"); + } + + @Override + public List get() { + if (!hasInit.getAndSet(true)) { + init(); + } + List> prList = helper.get(getStateKey()); + List list = new ArrayList<>(); + for (PartitionRecord pr : prList) { + list.add(pr.getValue()); + } + return list; + } + + @Override + public void add(V value) { + if (!hasInit.getAndSet(true)) { + init(); + } + List> list = helper.get(getStateKey()); + if (list == null) { + list = new ArrayList<>(); + } + list.add(new PartitionRecord<>(descriptor.getPartitionNumber(), value)); + helper.put(list, getStateKey()); + } + + @Override + public void update(List list) { + List> prList = new ArrayList<>(); + if (list != null) { + for (V value : list) { + prList.add(new PartitionRecord<>(descriptor.getPartitionNumber(), value)); + } + } + helper.put(prList); + } + + public void setSplit(boolean split) { + this.isSplit = split; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/StateHelper.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/StateHelper.java new file mode 100644 index 000000000..23511081a --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/StateHelper.java @@ -0,0 +1,80 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import com.google.common.base.Preconditions; +import io.ray.streaming.state.backend.AbstractKeyStateBackend; +import io.ray.streaming.state.keystate.desc.AbstractStateDescriptor; + +/** + * State Helper Class. + */ +public class StateHelper { + + private final AbstractKeyStateBackend backend; + private final AbstractStateDescriptor descriptor; + + public StateHelper(AbstractKeyStateBackend backend, AbstractStateDescriptor descriptor) { + this.backend = backend; + this.descriptor = descriptor; + } + + protected String getStateKey(String descName) { + Preconditions.checkNotNull(backend, "KeyedBackend must not be null"); + Preconditions.checkNotNull(backend.getCurrentKey(), "currentKey must not be null"); + return this.backend.getBackend().getStateKey(descName, backend.getCurrentKey().toString()); + } + + public void put(T value, String key) { + backend.put(descriptor, key, value); + } + + public void put(T value) { + put(value, getStateKey(descriptor.getIdentify())); + } + + public T get(String key) { + return backend.get(descriptor, key); + } + + public T get() { + return get(getStateKey(descriptor.getIdentify())); + } + + public void setCurrentKey(Object currentKey) { + Preconditions.checkNotNull(backend, "KeyedBackend must not be null"); + this.backend.setCurrentKey(currentKey); + } + + public void setKeyGroupIndex(int keyGroupIndex) { + this.backend.setKeyGroupIndex(keyGroupIndex); + } + + public void resetKeyGroupIndex() { + this.backend.setKeyGroupIndex(-1); + } + + public AbstractStateDescriptor getDescriptor() { + return descriptor; + } + + public AbstractKeyStateBackend getBackend() { + return backend; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/ValueStateImpl.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/ValueStateImpl.java new file mode 100644 index 000000000..50a72ec65 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/impl/ValueStateImpl.java @@ -0,0 +1,58 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ValueState; + +/** + * ValueState implementation. + */ +public class ValueStateImpl implements ValueState { + + private final StateHelper helper; + + public ValueStateImpl(ValueStateDescriptor descriptor, KeyStateBackend backend) { + this.helper = new StateHelper<>(backend, descriptor); + } + + @Override + public void update(T value) { + helper.put(value); + } + + @Override + public T get() { + T value = helper.get(); + if (null == value) { + return ((ValueStateDescriptor) helper.getDescriptor()).getDefaultValue(); + } else { + return value; + } + } + + /** + * set current key of the state + */ + @Override + public void setCurrentKey(Object currentKey) { + helper.setCurrentKey(currentKey); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/ListStateStoreManagerProxy.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/ListStateStoreManagerProxy.java new file mode 100644 index 000000000..9f25ec4f4 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/ListStateStoreManagerProxy.java @@ -0,0 +1,51 @@ +/* + * 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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.KeyValueState; +import io.ray.streaming.state.backend.AbstractKeyStateBackend; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.impl.ListStateImpl; +import io.ray.streaming.state.keystate.state.impl.OperatorStateImpl; +import io.ray.streaming.state.strategy.StateStoreManagerProxy; +import java.util.List; + +/** + * This class defines ListState Wrapper, connecting state and backend. + */ +public class ListStateStoreManagerProxy extends StateStoreManagerProxy> implements + KeyValueState> { + + private final ListState listState; + + public ListStateStoreManagerProxy(AbstractKeyStateBackend keyStateBackend, + ListStateDescriptor stateDescriptor) { + super(keyStateBackend, stateDescriptor); + if (stateDescriptor.isOperatorList()) { + this.listState = new OperatorStateImpl<>(stateDescriptor, keyStateBackend); + } else { + this.listState = new ListStateImpl<>(stateDescriptor, keyStateBackend); + } + } + + public ListState getListState() { + return this.listState; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/MapStateStoreManagerProxy.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/MapStateStoreManagerProxy.java new file mode 100644 index 000000000..a30e5084a --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/MapStateStoreManagerProxy.java @@ -0,0 +1,46 @@ +/* + * 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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.KeyValueState; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.impl.MapStateImpl; +import io.ray.streaming.state.strategy.StateStoreManagerProxy; +import java.util.Map; + +/** + * This class defines MapState Wrapper, connecting state and backend. + */ +public class MapStateStoreManagerProxy extends StateStoreManagerProxy> implements + KeyValueState> { + + private final MapStateImpl mapState; + + public MapStateStoreManagerProxy(KeyStateBackend keyStateBackend, + MapStateDescriptor stateDescriptor) { + super(keyStateBackend, stateDescriptor); + this.mapState = new MapStateImpl<>(stateDescriptor, keyStateBackend); + } + + public MapState getMapState() { + return this.mapState; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/ValueStateStoreManagerProxy.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/ValueStateStoreManagerProxy.java new file mode 100644 index 000000000..3ab4bd2a1 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/keystate/state/proxy/ValueStateStoreManagerProxy.java @@ -0,0 +1,45 @@ +/* + * 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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.KeyValueState; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ValueState; +import io.ray.streaming.state.keystate.state.impl.ValueStateImpl; +import io.ray.streaming.state.strategy.StateStoreManagerProxy; + +/** + * This class defines ValueState Wrapper, connecting state and backend. + */ +public class ValueStateStoreManagerProxy extends StateStoreManagerProxy implements + KeyValueState { + + private final ValueStateImpl valueState; + + public ValueStateStoreManagerProxy(KeyStateBackend keyStateBackend, + ValueStateDescriptor stateDescriptor) { + super(keyStateBackend, stateDescriptor); + this.valueState = new ValueStateImpl<>(stateDescriptor, keyStateBackend); + } + + public ValueState getValueState() { + return this.valueState; + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/KeyMapStoreSerializer.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/KeyMapStoreSerializer.java new file mode 100644 index 000000000..620913a4c --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/KeyMapStoreSerializer.java @@ -0,0 +1,36 @@ +/* + * 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 io.ray.streaming.state.serialization; + +/** + * Key Map Serialization and Deserialization. + */ +public interface KeyMapStoreSerializer { + + byte[] serializeKey(K key); + + byte[] serializeUKey(S uk); + + S deserializeUKey(byte[] ukArray); + + byte[] serializeUValue(T uv); + + T deserializeUValue(byte[] uvArray); + +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/KeyValueStoreSerialization.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/KeyValueStoreSerialization.java new file mode 100644 index 000000000..dc53ef7ba --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/KeyValueStoreSerialization.java @@ -0,0 +1,31 @@ +/* + * 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 io.ray.streaming.state.serialization; + +/** + * Key Value Serialization and Deserialization. + */ +public interface KeyValueStoreSerialization { + + byte[] serializeKey(K key); + + byte[] serializeValue(V value); + + V deserializeValue(byte[] valueArray); +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/Serializer.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/Serializer.java new file mode 100644 index 000000000..6db0c589f --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/Serializer.java @@ -0,0 +1,38 @@ +/* + * 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 io.ray.streaming.state.serialization; + +import org.nustaq.serialization.FSTConfiguration; + +/** + * fst wrapper. + */ +public class Serializer { + + private static final ThreadLocal conf = ThreadLocal + .withInitial(FSTConfiguration::createDefaultConfiguration); + + public static byte[] object2Bytes(Object value) { + return conf.get().asByteArray(value); + } + + public static Object bytes2Object(byte[] buffer) { + return conf.get().asObject(buffer); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/AbstractSerialization.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/AbstractSerialization.java new file mode 100644 index 000000000..cc8daee6e --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/AbstractSerialization.java @@ -0,0 +1,46 @@ +/* + * 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 io.ray.streaming.state.serialization.impl; + +import com.google.common.hash.Hashing; +import io.ray.streaming.state.StateException; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * AbstractSerialization. Generate row key. + */ +public abstract class AbstractSerialization { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractSerialization.class); + + public String generateRowKeyPrefix(String key) { + if (StringUtils.isNotEmpty(key)) { + String md5 = Hashing.md5().hashUnencodedChars(key).toString(); + if ("".equals(md5)) { + throw new StateException("Invalid value to md5:" + key); + } + return StringUtils.substring(md5, 0, 4) + ":" + key; + } else { + LOG.warn("key is empty"); + return key; + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/DefaultKeyMapStoreSerializer.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/DefaultKeyMapStoreSerializer.java new file mode 100644 index 000000000..99f51764b --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/DefaultKeyMapStoreSerializer.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 io.ray.streaming.state.serialization.impl; + +import io.ray.streaming.state.serialization.KeyMapStoreSerializer; +import io.ray.streaming.state.serialization.Serializer; + +/** + * Default Key Map Serialization and Deserialization. + */ +public class DefaultKeyMapStoreSerializer extends AbstractSerialization implements + KeyMapStoreSerializer { + + @Override + public byte[] serializeKey(K key) { + String keyWithPrefix = generateRowKeyPrefix(key.toString()); + return keyWithPrefix.getBytes(); + } + + @Override + public byte[] serializeUKey(S uk) { + return Serializer.object2Bytes(uk); + } + + @Override + public S deserializeUKey(byte[] ukArray) { + return (S) Serializer.bytes2Object(ukArray); + } + + @Override + public byte[] serializeUValue(T uv) { + return Serializer.object2Bytes(uv); + } + + @Override + public T deserializeUValue(byte[] uvArray) { + return (T) Serializer.bytes2Object(uvArray); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/DefaultKeyValueStoreSerialization.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/DefaultKeyValueStoreSerialization.java new file mode 100644 index 000000000..ff0a843f6 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/serialization/impl/DefaultKeyValueStoreSerialization.java @@ -0,0 +1,45 @@ +/* + * 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 io.ray.streaming.state.serialization.impl; + +import io.ray.streaming.state.serialization.KeyValueStoreSerialization; +import io.ray.streaming.state.serialization.Serializer; + +/** + * KV Store Serialization and Deserialization. + */ +public class DefaultKeyValueStoreSerialization extends AbstractSerialization implements + KeyValueStoreSerialization { + + @Override + public byte[] serializeKey(K key) { + String keyWithPrefix = generateRowKeyPrefix(key.toString()); + return keyWithPrefix.getBytes(); + } + + @Override + public byte[] serializeValue(V value) { + return Serializer.object2Bytes(value); + } + + @Override + public V deserializeValue(byte[] valueArray) { + return (V) Serializer.bytes2Object(valueArray); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/KeyMapStore.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/KeyMapStore.java new file mode 100644 index 000000000..0393ee294 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/KeyMapStore.java @@ -0,0 +1,38 @@ +/* + * 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 io.ray.streaming.state.store; + +import java.io.IOException; +import java.util.Map; + +/** + * Key Map Store interface. + */ +public interface KeyMapStore extends KeyValueStore> { + + /** + * put sub key value into the store incrementally. + */ + void put(K key, S subKey, T value) throws IOException; + + /** + * get subValue from store. + */ + T get(K key, S subKey) throws IOException; +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/KeyValueStore.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/KeyValueStore.java new file mode 100644 index 000000000..506baef34 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/KeyValueStore.java @@ -0,0 +1,58 @@ +/* + * 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 io.ray.streaming.state.store; + +import java.io.IOException; +import java.io.Serializable; + +/** + * Key Value Store interface. + */ +public interface KeyValueStore extends Serializable { + + /** + * put key value into store. + */ + void put(K key, V value) throws IOException; + + /** + * get value from store. + */ + V get(K key) throws IOException; + + /** + * remove key in the store. + */ + void remove(K key) throws IOException; + + /** + * flush to disk. + */ + void flush() throws IOException; + + /** + * clear all cache. + */ + void clearCache(); + + /** + * close the store. + */ + void close() throws IOException; +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/impl/MemoryKeyMapStore.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/impl/MemoryKeyMapStore.java new file mode 100644 index 000000000..aad605c5a --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/impl/MemoryKeyMapStore.java @@ -0,0 +1,89 @@ +/* + * 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 io.ray.streaming.state.store.impl; + +import com.google.common.collect.Maps; +import io.ray.streaming.state.store.KeyMapStore; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** + * Memory Key Map Store. + */ +public class MemoryKeyMapStore implements KeyMapStore { + + private Map> memoryStore; + + public MemoryKeyMapStore() { + this.memoryStore = Maps.newConcurrentMap(); + } + + @Override + public void put(K key, Map value) throws IOException { + this.memoryStore.put(key, value); + } + + @Override + public void put(K key, S subKey, T value) throws IOException { + if (memoryStore.containsKey(key)) { + memoryStore.get(key).put(subKey, value); + } else { + Map map = new HashMap<>(); + map.put(subKey, value); + memoryStore.put(key, map); + } + } + + @Override + public Map get(K key) throws IOException { + return this.memoryStore.get(key); + } + + @Override + public T get(K key, S subKey) throws IOException { + if (memoryStore.containsKey(key)) { + return memoryStore.get(key).get(subKey); + } + return null; + } + + @Override + public void remove(K key) throws IOException { + this.memoryStore.remove(key); + } + + @Override + public void flush() throws IOException { + + } + + @Override + public void clearCache() { + + } + + @Override + public void close() throws IOException { + if (memoryStore != null) { + memoryStore.clear(); + } + } + +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/impl/MemoryKeyValueStore.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/impl/MemoryKeyValueStore.java new file mode 100644 index 000000000..d5882832c --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/store/impl/MemoryKeyValueStore.java @@ -0,0 +1,68 @@ +/* + * 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 io.ray.streaming.state.store.impl; + +import com.google.common.collect.Maps; +import io.ray.streaming.state.store.KeyValueStore; +import java.io.IOException; +import java.util.Map; + +/** + * Memory Key Value Store. + */ +public class MemoryKeyValueStore implements KeyValueStore { + + private Map memoryStore; + + public MemoryKeyValueStore() { + this.memoryStore = Maps.newConcurrentMap(); + } + + @Override + public void put(K key, V value) throws IOException { + this.memoryStore.put(key, value); + } + + @Override + public V get(K key) throws IOException { + return this.memoryStore.get(key); + } + + @Override + public void remove(K key) throws IOException { + this.memoryStore.remove(key); + } + + @Override + public void flush() throws IOException { + + } + + @Override + public void clearCache() { + + } + + @Override + public void close() throws IOException { + if (memoryStore != null) { + memoryStore.clear(); + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/AbstractStateStoreManager.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/AbstractStateStoreManager.java new file mode 100644 index 000000000..0d4e179fe --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/AbstractStateStoreManager.java @@ -0,0 +1,93 @@ +/* + * 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 io.ray.streaming.state.strategy; + +import io.ray.streaming.state.StateException; +import io.ray.streaming.state.StateStoreManager; +import io.ray.streaming.state.StorageRecord; +import io.ray.streaming.state.serialization.Serializer; +import io.ray.streaming.state.store.KeyValueStore; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class defines the StoreManager Abstract class. + * We use three layer to store the state, frontStore, middleStore and keyValueStore(remote). + */ +public abstract class AbstractStateStoreManager implements StateStoreManager { + + /** + * read-write + */ + protected Map> frontStore = new ConcurrentHashMap<>(); + + /** + * remote-storage + */ + protected KeyValueStore> kvStore; + + /** + * read-only + */ + protected Map> middleStore = new ConcurrentHashMap<>(); + protected int keyGroupIndex = -1; + + public AbstractStateStoreManager(KeyValueStore> backStore) { + kvStore = backStore; + } + + public byte[] toBytes(StorageRecord storageRecord) { + return Serializer.object2Bytes(storageRecord); + } + + public StorageRecord toStorageRecord(byte[] data) { + return (StorageRecord) Serializer.bytes2Object(data); + } + + public abstract V get(long checkpointId, String key); + + public void put(long checkpointId, String k, V v) { + frontStore.put(k, new StorageRecord<>(checkpointId, v)); + } + + @Override + public void ackCommit(long checkpointId, long timeStamp) { + ackCommit(checkpointId); + } + + public abstract void ackCommit(long checkpointId); + + public void setKeyGroupIndex(int keyGroupIndex) { + this.keyGroupIndex = keyGroupIndex; + } + + public void close() { + frontStore.clear(); + middleStore.clear(); + if (kvStore != null) { + kvStore.clearCache(); + try { + kvStore.close(); + } catch (IOException e) { + throw new StateException(e); + } + } + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/DualStateStoreManager.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/DualStateStoreManager.java new file mode 100644 index 000000000..f3f31b1b2 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/DualStateStoreManager.java @@ -0,0 +1,167 @@ +/* + * 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 io.ray.streaming.state.strategy; + +import com.google.common.primitives.Longs; +import io.ray.streaming.state.StateException; +import io.ray.streaming.state.StorageRecord; +import io.ray.streaming.state.store.KeyValueStore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class define the checkpoint store strategy, which saves two-version data once. + */ +public class DualStateStoreManager extends AbstractStateStoreManager { + + private static final Logger LOG = LoggerFactory.getLogger(DualStateStoreManager.class); + + public DualStateStoreManager(KeyValueStore> backStore) { + super(backStore); + } + + @Override + public void finish(long checkpointId) { + LOG.info("do finish checkpointId:{}", checkpointId); + Map cpStore = new HashMap<>(); + for (Entry> entry : frontStore.entrySet()) { + String key = entry.getKey(); + StorageRecord value = entry.getValue(); + cpStore.put(key, toBytes(value)); + } + middleStore.put(checkpointId, cpStore); + frontStore.clear(); + } + + @Override + public void commit(long checkpointId) { + try { + LOG.info("do commit checkpointId:{}", checkpointId); + Map cpStore = middleStore.get(checkpointId); + if (cpStore == null) { + throw new StateException("why cp store is null"); + } + for (Entry entry : cpStore.entrySet()) { + String key = entry.getKey(); + byte[] value = entry.getValue(); + + /** + * 2 is specific key in kv store and indicates that new value + * should be stored with this key after overwriting old value in key 1. i.e. + * + * -2 -1 1 2 + * k1 6 5 a b + * k2 9 7 d e + * + * k1's value for checkpoint 5 is a, and b for checkpoint 6. + */ + Map remoteData = super.kvStore.get(key); + if (remoteData == null || remoteData.size() == 0) { + remoteData = new HashMap<>(); + remoteData.put(2L, value); + remoteData.put(-2L, Longs.toByteArray(checkpointId)); + } else { + long oldBatchId = Longs.fromByteArray(remoteData.get(-2L)); + if (oldBatchId < checkpointId) { + //move the old data + remoteData.put(1L, remoteData.get(2L)); + remoteData.put(-1L, remoteData.get(-2L)); + } + + //put the new data here + remoteData.put(2L, value); + remoteData.put(-2L, Longs.toByteArray(checkpointId)); + } + super.kvStore.put(key, remoteData); + } + super.kvStore.flush(); + } catch (Exception e) { + LOG.error(e.getMessage(), e); + throw new StateException(e); + } + } + + @Override + public void rollBack(long checkpointId) { + LOG.info("do rollBack checkpointId:{}", checkpointId); + this.frontStore.clear(); + this.middleStore.clear(); + this.kvStore.clearCache(); + } + + @Override + public V get(long checkpointId, String key) { + // get from current cp cache + StorageRecord storageRecord = frontStore.get(key); + if (storageRecord != null) { + return storageRecord.getValue(); + } + + //get from not commit cp info + List checkpointIds = new ArrayList<>(middleStore.keySet()); + Collections.sort(checkpointIds); + for (int i = checkpointIds.size() - 1; i >= 0; i--) { + Map cpStore = middleStore.get(checkpointIds.get(i)); + if (cpStore != null) { + if (cpStore.containsKey(key)) { + byte[] cpData = cpStore.get(key); + storageRecord = toStorageRecord(cpData); + return storageRecord.getValue(); + } + } + } + + try { + Map remoteData = super.kvStore.get(key); + if (remoteData != null) { + for (Entry entry : remoteData.entrySet()) { + if (entry.getKey() > 0) { + StorageRecord tmp = toStorageRecord(entry.getValue()); + if (tmp.getCheckpointId() < checkpointId) { + if (storageRecord == null) { + storageRecord = tmp; + } else if (storageRecord.getCheckpointId() < tmp.getCheckpointId()) { + storageRecord = tmp; + } + } + } + } + if (storageRecord != null) { + return storageRecord.getValue(); + } + } + } catch (Exception e) { + LOG.error("get checkpointId:" + checkpointId + " key:" + key, e); + throw new StateException(e); + } + return null; + } + + @Override + public void ackCommit(long checkpointId) { + LOG.info("do ackCommit checkpointId:{}", checkpointId); + middleStore.remove(checkpointId); + } +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/MVStateStoreManager.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/MVStateStoreManager.java new file mode 100644 index 000000000..82208c234 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/MVStateStoreManager.java @@ -0,0 +1,157 @@ +/* + * 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 io.ray.streaming.state.strategy; + +import io.ray.streaming.state.StateException; +import io.ray.streaming.state.StorageRecord; +import io.ray.streaming.state.store.KeyValueStore; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +/** + * This class define the multi-version store strategy, which leverages external storage's mvcc. + */ +public class MVStateStoreManager extends AbstractStateStoreManager { + + public MVStateStoreManager(KeyValueStore> kvStore) { + super(kvStore); + } + + @Override + public void finish(long checkpointId) { + Map currentStateRecords = new HashMap<>(); + for (Entry> entry : frontStore.entrySet()) { + currentStateRecords.put(entry.getKey(), toBytes(entry.getValue())); + } + + middleStore.put(checkpointId, currentStateRecords); + frontStore.clear(); + } + + @Override + public void commit(long checkpointId) { + // write to external storage + List checkpointIds = new ArrayList<>(middleStore.keySet()); + Collections.sort(checkpointIds); + + for (int i = checkpointIds.size() - 1; i >= 0; i--) { + long commitBatchId = checkpointIds.get(i); + if (commitBatchId > checkpointId) { + continue; + } + + Map commitRecords = middleStore.get(commitBatchId); + + try { + for (Entry entry : commitRecords.entrySet()) { + + Map remoteData = this.kvStore.get(entry.getKey()); + if (remoteData == null) { + remoteData = new HashMap<>(); + } + + remoteData.put(commitBatchId, entry.getValue()); + + this.kvStore.put(entry.getKey(), remoteData); + } + this.kvStore.flush(); + } catch (Exception e) { + throw new StateException(e); + } + } + } + + @Override + public void rollBack(long checkpointId) { + this.frontStore.clear(); + this.middleStore.clear(); + this.kvStore.clearCache(); + } + + @Override + public V get(long checkpointId, String key) { + StorageRecord valueArray = frontStore.get(key); + if (valueArray != null) { + return valueArray.getValue(); + } else { + List checkpointIds = new ArrayList<>(middleStore.keySet()); + Collections.sort(checkpointIds); + + for (int i = checkpointIds.size() - 1; i >= 0; i--) { + if (checkpointIds.get(i) > checkpointId) { + continue; + } + + Map records = middleStore.get(checkpointIds.get(i)); + if (records != null) { + if (records.containsKey(key)) { + byte[] bytes = records.get(key); + return toStorageRecord(bytes).getValue(); + } + } + } + + // get from external storage + try { + Map remoteData = this.kvStore.get(key); + if (remoteData != null) { + checkpointIds = new ArrayList<>(remoteData.keySet()); + Collections.sort(checkpointIds); + + for (int i = checkpointIds.size() - 1; i >= 0; i--) { + if (checkpointIds.get(i) > checkpointId) { + continue; + } + + byte[] bytes = remoteData.get(checkpointIds.get(i)); + return toStorageRecord(bytes).getValue(); + } + } + } catch (Exception e) { + throw new StateException(e); + } + } + return null; + } + + @Override + public void put(long checkpointId, String k, V v) { + frontStore.put(k, new StorageRecord<>(checkpointId, v)); + } + + @Override + public void ackCommit(long checkpointId) { + List checkpointIds = new ArrayList<>(middleStore.keySet()); + Collections.sort(checkpointIds); + + for (int i = checkpointIds.size() - 1; i >= 0; i--) { + long commitBatchId = checkpointIds.get(i); + if (commitBatchId > checkpointId) { + continue; + } + + this.middleStore.remove(commitBatchId); + } + } + +} diff --git a/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/StateStoreManagerProxy.java b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/StateStoreManagerProxy.java new file mode 100644 index 000000000..a394fa738 --- /dev/null +++ b/streaming/java/streaming-state/src/main/java/io/ray/streaming/state/strategy/StateStoreManagerProxy.java @@ -0,0 +1,102 @@ +/* + * 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 io.ray.streaming.state.strategy; + +import io.ray.streaming.state.StateStoreManager; +import io.ray.streaming.state.backend.AbstractKeyStateBackend; +import io.ray.streaming.state.backend.StateStrategy; +import io.ray.streaming.state.keystate.desc.AbstractStateDescriptor; +import io.ray.streaming.state.store.KeyValueStore; +import java.util.Map; + + +/** + * This class support ITransactionState. + *

+ * Based on the given StorageMode, different implementation instance of the AbstractStateStrategy + * class will be created. All method calls will be delegated to the strategy instance. + */ + +public abstract class StateStoreManagerProxy implements StateStoreManager { + + protected final AbstractStateStoreManager stateStrategy; + private final AbstractKeyStateBackend keyStateBackend; + + public StateStoreManagerProxy(AbstractKeyStateBackend keyStateBackend, + AbstractStateDescriptor stateDescriptor) { + this.keyStateBackend = keyStateBackend; + KeyValueStore> backStorage = keyStateBackend + .getBackStorage(stateDescriptor); + StateStrategy stateStrategy = keyStateBackend.getStateStrategy(); + switch (stateStrategy) { + case DUAL_VERSION: + this.stateStrategy = new DualStateStoreManager<>(backStorage); + break; + case SINGLE_VERSION: + this.stateStrategy = new MVStateStoreManager<>(backStorage); + break; + default: + throw new UnsupportedOperationException("store vertexType not support"); + } + } + + protected void setKeyGroupIndex(int index) { + this.stateStrategy.setKeyGroupIndex(index); + } + + @Override + public void finish(long checkpointId) { + this.stateStrategy.finish(checkpointId); + } + + /** + * The commit can be used in another thread to reach async state commit. + */ + @Override + public void commit(long checkpointId) { + this.stateStrategy.commit(checkpointId); + } + + /** + * The ackCommit must be called after commit in the same thread. + */ + @Override + public void ackCommit(long checkpointId, long timeStamp) { + this.stateStrategy.ackCommit(checkpointId); + } + + @Override + public void rollBack(long checkpointId) { + this.stateStrategy.rollBack(checkpointId); + } + + public void close() { + this.stateStrategy.close(); + } + + public V get(String key) { + this.stateStrategy.setKeyGroupIndex(keyStateBackend.getKeyGroupIndex()); + return this.stateStrategy.get(this.keyStateBackend.getCheckpointId(), key); + } + + public void put(String key, V value) { + this.stateStrategy.setKeyGroupIndex(keyStateBackend.getKeyGroupIndex()); + this.stateStrategy.put(this.keyStateBackend.getCheckpointId(), key, value); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/backend/KeyStateBackendTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/backend/KeyStateBackendTest.java new file mode 100644 index 000000000..91b375c5f --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/backend/KeyStateBackendTest.java @@ -0,0 +1,315 @@ +/* + * 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 io.ray.streaming.state.backend; + +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.ValueState; +import java.util.Arrays; +import java.util.HashMap; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class KeyStateBackendTest { + + private AbstractStateBackend stateBackend; + private KeyStateBackend keyStateBackend; + + public void testGetValueState() { + keyStateBackend.setCheckpointId(1L); + ValueStateDescriptor valueStateDescriptor = ValueStateDescriptor + .build("value", String.class, null); + valueStateDescriptor.setTableName("kepler_hlg_ut"); + ValueState valueState = keyStateBackend.getValueState(valueStateDescriptor); + + valueState.setCurrentKey("1"); + valueState.update("hello"); + Assert.assertEquals(valueState.get(), "hello"); + + valueState.update("hello1"); + Assert.assertEquals(valueState.get(), "hello1"); + + valueState.setCurrentKey("2"); + Assert.assertEquals(valueState.get(), null); + + valueState.update("eagle"); + Assert.assertEquals(valueState.get(), "eagle"); + + keyStateBackend.rollBack(1); + valueState.setCurrentKey("1"); + Assert.assertEquals(valueState.get(), null); + valueState.setCurrentKey("2"); + Assert.assertEquals(valueState.get(), null); + + valueState.setCurrentKey("1"); + valueState.update("eagle"); + keyStateBackend.finish(1); + + keyStateBackend.setCheckpointId(2); + valueState.setCurrentKey("2"); + valueState.update("tim"); + + valueState.setCurrentKey("2-1"); + valueState.update("jim"); + keyStateBackend.finish(2); + + keyStateBackend.setCheckpointId(3); + valueState.setCurrentKey("3"); + valueState.update("lucy"); + keyStateBackend.finish(3); + + keyStateBackend.setCheckpointId(4); + valueState.setCurrentKey("4"); + valueState.update("eric"); + keyStateBackend.finish(4); + + keyStateBackend.setCheckpointId(5); + valueState.setCurrentKey("4"); + valueState.update("eric-1"); + valueState.setCurrentKey("5"); + valueState.update("jack"); + keyStateBackend.finish(5); + keyStateBackend.commit(5); + + keyStateBackend.setCheckpointId(6); + valueState.setCurrentKey("5"); + Assert.assertEquals(valueState.get(), "jack"); + + valueState.setCurrentKey("4"); + Assert.assertEquals(valueState.get(), "eric-1"); + + valueState.setCurrentKey(4); + valueState.update("if-ttt"); + Assert.assertEquals(valueState.get(), "if-ttt"); + + keyStateBackend.setCheckpointId(7); + valueState.setCurrentKey(9); + valueState.update("6666"); + + keyStateBackend.rollBack(5); + keyStateBackend.setCheckpointId(6); + valueState.setCurrentKey("4"); + Assert.assertEquals(valueState.get(), "eric-1"); + valueState.setCurrentKey("5"); + Assert.assertEquals(valueState.get(), "jack"); + valueState.setCurrentKey("9"); + Assert.assertNull(valueState.get()); + + } + + public void testGetListState() { + keyStateBackend.setCheckpointId(1l); + ListStateDescriptor listStateDescriptor = ListStateDescriptor + .build("list", String.class); + listStateDescriptor.setTableName("kepler_hlg_ut"); + ListState listState = keyStateBackend.getListState(listStateDescriptor); + + listState.setCurrentKey("1"); + listState.add("hello1"); + Assert.assertEquals(listState.get(), Arrays.asList("hello1")); + + listState.add("hello2"); + Assert.assertEquals(listState.get(), Arrays.asList("hello1", "hello2")); + + listState.setCurrentKey("2"); + Assert.assertEquals(listState.get(), Arrays.asList()); + + listState.setCurrentKey("2"); + listState.add("eagle"); + listState.setCurrentKey("1"); + Assert.assertEquals(listState.get(), Arrays.asList("hello1", "hello2")); + listState.setCurrentKey("2"); + Assert.assertEquals(listState.get(), Arrays.asList("eagle")); + + keyStateBackend.rollBack(1); + listState.setCurrentKey("1"); + Assert.assertEquals(listState.get(), Arrays.asList()); + listState.setCurrentKey("2"); + Assert.assertEquals(listState.get(), Arrays.asList()); + + listState.setCurrentKey("1"); + listState.add("eagle"); + listState.add("eagle-2"); + keyStateBackend.finish(1); + + keyStateBackend.setCheckpointId(2); + listState.setCurrentKey("2"); + listState.add("tim"); + + listState.setCurrentKey("2-1"); + listState.add("jim"); + keyStateBackend.finish(2); + + keyStateBackend.setCheckpointId(3); + listState.setCurrentKey("3"); + listState.add("lucy"); + keyStateBackend.finish(3); + + keyStateBackend.setCheckpointId(4); + listState.setCurrentKey("4"); + listState.add("eric"); + keyStateBackend.finish(4); + + keyStateBackend.setCheckpointId(5); + listState.setCurrentKey("4"); + listState.add("eric-1"); + Assert.assertEquals(listState.get(), Arrays.asList("eric", "eric-1")); + + listState.setCurrentKey("5"); + listState.add("jack"); + keyStateBackend.finish(5); + keyStateBackend.commit(5); + + keyStateBackend.setCheckpointId(6); + listState.setCurrentKey("5"); + Assert.assertEquals(listState.get(), Arrays.asList("jack")); + + listState.setCurrentKey("4"); + Assert.assertEquals(listState.get(), Arrays.asList("eric", "eric-1")); + + listState.setCurrentKey(4); + listState.add("if-ttt"); + Assert.assertEquals(listState.get(), Arrays.asList("eric", "eric-1", "if-ttt")); + + keyStateBackend.setCheckpointId(7); + listState.setCurrentKey(9); + listState.add("6666"); + + keyStateBackend.rollBack(5); + keyStateBackend.setCheckpointId(6); + listState.setCurrentKey("4"); + Assert.assertEquals(listState.get(), Arrays.asList("eric", "eric-1")); + listState.setCurrentKey("5"); + Assert.assertEquals(listState.get(), Arrays.asList("jack")); + listState.setCurrentKey("9"); + Assert.assertEquals(listState.get(), Arrays.asList()); + } + + public void testGetMapState() { + keyStateBackend.setCheckpointId(1l); + MapStateDescriptor mapStateDescriptor = MapStateDescriptor + .build("map", String.class, String.class); + mapStateDescriptor.setTableName("kepler_hlg_ut"); + MapState mapState = keyStateBackend.getMapState(mapStateDescriptor); + + mapState.setCurrentKey("1"); + mapState.put("hello1", "world1"); + Assert.assertEquals(mapState.get("hello1"), "world1"); + + mapState.put("hello2", "world2"); + Assert.assertEquals(mapState.get("hello2"), "world2"); + Assert.assertEquals(mapState.get("hello1"), "world1"); + Assert.assertEquals(mapState.get("hello3"), null); + + mapState.setCurrentKey("2"); + //Assert.assertEquals(mapState.iterator(), (new HashMap())); + + mapState.setCurrentKey("2"); + mapState.put("eagle", "eagle-1"); + mapState.setCurrentKey("1"); + Assert.assertEquals(mapState.get("hello1"), "world1"); + mapState.setCurrentKey("2"); + Assert.assertEquals(mapState.get("eagle"), "eagle-1"); + Assert.assertEquals(mapState.get("xxx"), null); + + keyStateBackend.rollBack(1); + mapState.setCurrentKey("1"); + Assert.assertEquals(mapState.iterator(), (new HashMap()).entrySet().iterator()); + mapState.setCurrentKey("2"); + Assert.assertEquals(mapState.iterator(), (new HashMap()).entrySet().iterator()); + + mapState.setCurrentKey("1"); + mapState.put("eagle", "eagle-1"); + mapState.put("eagle-2", "eagle-3"); + keyStateBackend.finish(1); + + keyStateBackend.setCheckpointId(2); + mapState.setCurrentKey("2"); + mapState.put("tim", "tina"); + + mapState.setCurrentKey("2-1"); + mapState.put("jim", "tick"); + keyStateBackend.finish(2); + + keyStateBackend.setCheckpointId(3); + mapState.setCurrentKey("3"); + mapState.put("lucy", "ja"); + keyStateBackend.finish(3); + + keyStateBackend.setCheckpointId(4); + mapState.setCurrentKey("4"); + mapState.put("eric", "sam"); + keyStateBackend.finish(4); + + keyStateBackend.setCheckpointId(5); + mapState.setCurrentKey("4"); + mapState.put("eric-1", "zxy"); + Assert.assertEquals(mapState.get("eric-1"), "zxy"); + Assert.assertEquals(mapState.get("eric"), "sam"); + + mapState.setCurrentKey("5"); + mapState.put("jack", "zhang"); + keyStateBackend.finish(5); + keyStateBackend.commit(5); + + keyStateBackend.setCheckpointId(6); + mapState.setCurrentKey("5"); + Assert.assertEquals(mapState.get("jack"), "zhang"); + mapState.put("hlll", "gggg"); + + mapState.setCurrentKey("4"); + Assert.assertEquals(mapState.get("eric-1"), "zxy"); + Assert.assertEquals(mapState.get("eric"), "sam"); + + mapState.setCurrentKey(4); + mapState.put("if-ttt", "if-ggg"); + Assert.assertEquals(mapState.get("if-ttt"), "if-ggg"); + + keyStateBackend.setCheckpointId(7); + mapState.setCurrentKey(9); + mapState.put("6666", "7777"); + + keyStateBackend.rollBack(5); + keyStateBackend.setCheckpointId(6); + mapState.setCurrentKey("4"); + Assert.assertEquals(mapState.get("eric-1"), "zxy"); + Assert.assertEquals(mapState.get("eric"), "sam"); + Assert.assertNull(mapState.get("if-ttt")); + + mapState.setCurrentKey("5"); + Assert.assertNull(mapState.get("hlll")); + mapState.setCurrentKey("9"); + Assert.assertNull(mapState.get("6666")); + } + + + @Test + public void testMem() { + stateBackend = StateBackendBuilder.buildStateBackend(new HashMap<>()); + keyStateBackend = new KeyStateBackend(4, new KeyGroup(2, 3), stateBackend); + testGetValueState(); + testGetListState(); + testGetMapState(); + } + +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/DefaultKeyMapStoreSerializationTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/DefaultKeyMapStoreSerializationTest.java new file mode 100644 index 000000000..94716a107 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/DefaultKeyMapStoreSerializationTest.java @@ -0,0 +1,60 @@ +/* + * 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 io.ray.streaming.state.impl; + +import io.ray.streaming.state.serialization.impl.DefaultKeyMapStoreSerializer; +import java.util.HashMap; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class DefaultKeyMapStoreSerializationTest { + + private DefaultKeyMapStoreSerializer> defaultKMapStoreSerDe; + + @BeforeClass + public void setUp() { + this.defaultKMapStoreSerDe = new DefaultKeyMapStoreSerializer<>(); + } + + @Test + public void testSerKey() { + String key = "hello"; + byte[] result = this.defaultKMapStoreSerDe.serializeKey(key); + String keyWithPrefix = this.defaultKMapStoreSerDe.generateRowKeyPrefix(key.toString()); + Assert.assertEquals(result, keyWithPrefix.getBytes()); + } + + @Test + public void testSerUKey() { + String subKey = "hell1"; + byte[] result = this.defaultKMapStoreSerDe.serializeUKey(subKey); + Assert.assertEquals(subKey, this.defaultKMapStoreSerDe.deserializeUKey(result)); + } + + @Test + public void testSerUValue() { + Map value = new HashMap<>(); + value.put("foo", "bar"); + byte[] result = this.defaultKMapStoreSerDe.serializeUValue(value); + Assert.assertEquals(value, this.defaultKMapStoreSerDe.deserializeUValue(result)); + } + +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/DefaultKeyValueStoreSerializationTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/DefaultKeyValueStoreSerializationTest.java new file mode 100644 index 000000000..2bf546481 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/DefaultKeyValueStoreSerializationTest.java @@ -0,0 +1,43 @@ +/* + * 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 io.ray.streaming.state.impl; + +import io.ray.streaming.state.serialization.impl.DefaultKeyValueStoreSerialization; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class DefaultKeyValueStoreSerializationTest { + + DefaultKeyValueStoreSerialization serDe = new DefaultKeyValueStoreSerialization<>(); + byte[] ret; + + @Test + public void testSerKey() throws Exception { + ret = serDe.serializeKey("key"); + String key = new String(ret); + Assert.assertEquals(key.indexOf("key"), 5); + } + + @Test + public void testSerValue() throws Exception { + ret = serDe.serializeValue(5); + Assert.assertEquals(ret.length, 2); + Assert.assertEquals((int) serDe.deserializeValue(ret), 5); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/MemoryKeyMapStoreTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/MemoryKeyMapStoreTest.java new file mode 100644 index 000000000..4f9e5f20b --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/MemoryKeyMapStoreTest.java @@ -0,0 +1,66 @@ +/* + * 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 io.ray.streaming.state.impl; + +import com.google.common.collect.Maps; +import io.ray.streaming.state.backend.AbstractStateBackend; +import io.ray.streaming.state.backend.StateBackendBuilder; +import io.ray.streaming.state.store.KeyMapStore; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class MemoryKeyMapStoreTest { + + private AbstractStateBackend stateBackend; + private KeyMapStore IKeyMapStore; + + @BeforeClass + public void setUp() { + stateBackend = StateBackendBuilder.buildStateBackend(new HashMap()); + IKeyMapStore = stateBackend.getKeyMapStore("test-table"); + } + + @Test + public void testCase() { + try { + Assert.assertNull(IKeyMapStore.get("hello")); + Map map = Maps.newHashMap(); + map.put("1", "1-1"); + map.put("2", "2-1"); + + IKeyMapStore.put("hello", map); + Assert.assertEquals(IKeyMapStore.get("hello"), map); + + Map map2 = Maps.newHashMap(); + map.put("3", "3-1"); + map.put("4", "4-1"); + IKeyMapStore.put("hello", map2); + Assert.assertNotEquals(IKeyMapStore.get("hello"), map); + Assert.assertEquals(IKeyMapStore.get("hello"), map2); + + + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/MemoryKeyValueStoreTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/MemoryKeyValueStoreTest.java new file mode 100644 index 000000000..84487d535 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/impl/MemoryKeyValueStoreTest.java @@ -0,0 +1,54 @@ +/* + * 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 io.ray.streaming.state.impl; + +import io.ray.streaming.state.backend.AbstractStateBackend; +import io.ray.streaming.state.backend.StateBackendBuilder; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class MemoryKeyValueStoreTest { + + private AbstractStateBackend stateBackend; + private io.ray.streaming.state.store.KeyValueStore KeyValueStore; + + @BeforeClass + public void setUp() { + Map config = new HashMap<>(); + stateBackend = StateBackendBuilder.buildStateBackend(config); + KeyValueStore = stateBackend.getKeyValueStore("kepler_hlg_ut"); + } + + @Test + public void testCase() { + try { + KeyValueStore.put("hello", "world"); + Assert.assertEquals(KeyValueStore.get("hello"), "world"); + KeyValueStore.put("hello", "world1"); + Assert.assertEquals(KeyValueStore.get("hello"), "world1"); + Assert.assertNull(KeyValueStore.get("hello1")); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/KeyGroupAssignmentTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/KeyGroupAssignmentTest.java new file mode 100644 index 000000000..eeb560d09 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/KeyGroupAssignmentTest.java @@ -0,0 +1,56 @@ +/* + * 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 io.ray.streaming.state.keystate; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +public class KeyGroupAssignmentTest { + + + @Test + public void testComputeKeyGroupRangeForOperatorIndex() throws Exception { + KeyGroup keyGroup = KeyGroupAssignment.getKeyGroup(4096, 1, 0); + assertEquals(keyGroup.getStartIndex(), 0); + assertEquals(keyGroup.getEndIndex(), 4095); + assertEquals(keyGroup.size(), 4096); + + KeyGroup keyGroup2 = KeyGroupAssignment.getKeyGroup(4096, 2, 0); + assertEquals(keyGroup2.getStartIndex(), 0); + assertEquals(keyGroup2.getEndIndex(), 2047); + assertEquals(keyGroup2.size(), 2048); + + keyGroup = KeyGroupAssignment.getKeyGroup(4096, 3, 0); + assertEquals(keyGroup.getStartIndex(), 0); + assertEquals(keyGroup.getEndIndex(), 1365); + assertEquals(keyGroup.size(), 1366); + + keyGroup2 = KeyGroupAssignment.getKeyGroup(4096, 3, 1); + assertEquals(keyGroup2.getStartIndex(), 1366); + assertEquals(keyGroup2.getEndIndex(), 2730); + assertEquals(keyGroup2.size(), 1365); + + KeyGroup keyGroup3 = KeyGroupAssignment.getKeyGroup(4096, 3, 2); + assertEquals(keyGroup3.getStartIndex(), 2731); + assertEquals(keyGroup3.getEndIndex(), 4095); + assertEquals(keyGroup3.size(), 1365); + } + +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/ListStateDescriptorTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/ListStateDescriptorTest.java new file mode 100644 index 000000000..7daed7d0b --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/ListStateDescriptorTest.java @@ -0,0 +1,43 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + +import io.ray.streaming.state.config.ConfigKey; +import org.testng.Assert; +import org.testng.annotations.Test; + +public class ListStateDescriptorTest { + + @Test + public void test() { + ListStateDescriptor descriptor = ListStateDescriptor + .build("lsdTest", Integer.class, true); + descriptor.setTableName("table"); + Assert.assertTrue(descriptor.isOperatorList()); + + descriptor.setPartitionNumber(3); + descriptor.setIndex(0); + + Assert.assertEquals(descriptor.getPartitionNumber(), 3); + Assert.assertEquals(descriptor.getIndex(), 0); + + Assert.assertEquals(descriptor.getIdentify(), + "lsdTest" + ConfigKey.DELIMITER + "3" + ConfigKey.DELIMITER + "0"); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/MapStateDescriptorTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/MapStateDescriptorTest.java new file mode 100644 index 000000000..8c62888c5 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/MapStateDescriptorTest.java @@ -0,0 +1,36 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class MapStateDescriptorTest { + + @Test + public void test() { + MapStateDescriptor descriptor = MapStateDescriptor + .build("msdTest", String.class, Integer.class); + + descriptor.setTableName("table"); + Assert.assertEquals(descriptor.getTableName(), "table"); + Assert.assertEquals(descriptor.getName(), "msdTest"); + } + +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/ValueStateDescriptorTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/ValueStateDescriptorTest.java new file mode 100644 index 000000000..9ee939f89 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/desc/ValueStateDescriptorTest.java @@ -0,0 +1,32 @@ +/* + * 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 io.ray.streaming.state.keystate.desc; + +import org.testng.Assert; +import org.testng.annotations.Test; + +public class ValueStateDescriptorTest { + + @Test + public void test() { + ValueStateDescriptor descriptor = ValueStateDescriptor + .build("vsdTest", Integer.class, 0); + Assert.assertEquals(descriptor.getDefaultValue().intValue(), 0); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/ListStateImplTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/ListStateImplTest.java new file mode 100644 index 000000000..049499507 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/ListStateImplTest.java @@ -0,0 +1,92 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.impl.MemoryStateBackend; +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ListStateImplTest { + + ListStateImpl listState; + KeyStateBackend keyStateBackend; + + @BeforeClass + public void setUp() throws Exception { + keyStateBackend = new KeyStateBackend(1, new KeyGroup(1, 2), + new MemoryStateBackend(new HashMap<>())); + ListStateDescriptor descriptor = ListStateDescriptor + .build("ListStateImplTest", Integer.class); + descriptor.setTableName("table"); + + listState = (ListStateImpl) keyStateBackend.getListState(descriptor); + } + + @Test + public void testAddGet() throws Exception { + keyStateBackend.setContext(1L, 1); + List list = listState.get(); + Assert.assertEquals(list.size(), 0); + + listState.add(1); + listState.add(2); + + Assert.assertEquals(listState.get(), Arrays.asList(1, 2)); + + listState.add(3); + Assert.assertEquals(listState.get(), Arrays.asList(1, 2, 3)); + + list = listState.get(); + list.set(1, -1); + listState.add(4); + Assert.assertEquals(listState.get(), Arrays.asList(1, -1, 3, 4)); + + keyStateBackend.setCurrentKey(2); + + listState.add(5); + listState.add(6); + + Assert.assertEquals(listState.get(), Arrays.asList(5, 6)); + } + + + @Test(dependsOnMethods = {"testAddGet"}) + public void testUpdate() throws Exception { + Assert.assertEquals(listState.get(), Arrays.asList(5, 6)); + + listState.update(Arrays.asList(7, 8, 9)); + + List list = listState.get(); + Assert.assertEquals(list, Arrays.asList(7, 8, 9)); + + list.set(1, 10); + listState.update(list); + Assert.assertEquals(list, Arrays.asList(7, 10, 9)); + + listState.update(null); + Assert.assertEquals(listState.get().size(), 0); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/MapStateImplTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/MapStateImplTest.java new file mode 100644 index 000000000..5dd7ad566 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/MapStateImplTest.java @@ -0,0 +1,137 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import com.google.common.collect.ImmutableMap; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.impl.MemoryStateBackend; +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import java.util.HashMap; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class MapStateImplTest { + + MapStateImpl mapState; + KeyStateBackend keyStateBackend; + + @BeforeClass + public void setUp() throws Exception { + keyStateBackend = new KeyStateBackend(1, new KeyGroup(1, 2), + new MemoryStateBackend(new HashMap<>())); + MapStateDescriptor descriptor = MapStateDescriptor + .build("MapStateImplTest", Integer.class, String.class); + descriptor.setTableName("table"); + mapState = (MapStateImpl) keyStateBackend.getMapState(descriptor); + } + + @Test + public void testPuTGet() throws Exception { + keyStateBackend.setContext(1L, 1); + + Assert.assertEquals(mapState.get().size(), 0); + + mapState.put(1, "1"); + mapState.put(2, "2"); + + Assert.assertTrue(mapState.contains(1)); + Assert.assertTrue(mapState.contains(2)); + Assert.assertFalse(mapState.contains(3)); + + Assert.assertEquals("1", mapState.get(1)); + Assert.assertEquals("2", mapState.get(2)); + + mapState.remove(1); + Assert.assertFalse(mapState.contains(1)); + Assert.assertTrue(mapState.contains(2)); + + mapState.remove(2); + mapState.putAll(ImmutableMap.of(1, "1", 2, "2")); + Assert.assertEquals("1", mapState.get(1)); + Assert.assertEquals("2", mapState.get(2)); + } + + @Test(dependsOnMethods = {"testPuTGet"}) + public void testUpdate() throws Exception { + Assert.assertEquals(mapState.get().size(), 2); + + mapState.update(ImmutableMap.of(3, "3", 4, "4")); + Assert.assertEquals(mapState.get(3), "3"); + Assert.assertEquals(mapState.get(4), "4"); + Assert.assertEquals(mapState.get().size(), 2); + + Map map = ImmutableMap.of(5, "5", 6, "6"); + mapState.update(map); + Assert.assertEquals(mapState.get(), map); + + mapState.update(null); + Assert.assertEquals(mapState.get().size(), 0); + } + + + @Test + public void testFailover() throws Exception { + keyStateBackend.setContext(1L, 1); + + Assert.assertEquals(mapState.get().size(), 0); + + mapState.put(1, "1"); + mapState.put(2, "2"); + + Assert.assertTrue(mapState.contains(1)); + Assert.assertTrue(mapState.contains(2)); + Assert.assertFalse(mapState.contains(3)); + + Assert.assertEquals("1", mapState.get(1)); + Assert.assertEquals("2", mapState.get(2)); + + mapState.remove(1); + Assert.assertFalse(mapState.contains(1)); + Assert.assertTrue(mapState.contains(2)); + + mapState.remove(2); + mapState.putAll(ImmutableMap.of(1, "1", 2, "2")); + Assert.assertEquals("1", mapState.get(1)); + Assert.assertEquals("2", mapState.get(2)); + + keyStateBackend.finish(5); + + Assert.assertEquals("1", mapState.get(1)); + Assert.assertEquals("2", mapState.get(2)); + + mapState.put(2, "3"); + Assert.assertEquals("3", mapState.get(2)); + + keyStateBackend.finish(6); + + keyStateBackend.commit(5); + Assert.assertEquals("3", mapState.get(2)); + + keyStateBackend.commit(6); + keyStateBackend.ackCommit(5, 0); + keyStateBackend.ackCommit(6, 1); + + mapState.put(2, "5"); + Assert.assertEquals("5", mapState.get(2)); + mapState.update(null); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/OperatorStateImplTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/OperatorStateImplTest.java new file mode 100644 index 000000000..843a0d0dd --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/OperatorStateImplTest.java @@ -0,0 +1,71 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import io.ray.streaming.state.backend.OperatorStateBackend; +import io.ray.streaming.state.backend.impl.MemoryStateBackend; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class OperatorStateImplTest { + + OperatorStateImpl operatorState; + ListStateDescriptor descriptor; + OperatorStateBackend operatorStateBackend; + + @BeforeClass + public void setUp() { + String table_name = "operatorState"; + Map config = new HashMap<>(); + + operatorStateBackend = new OperatorStateBackend(new MemoryStateBackend(config)); + + descriptor = ListStateDescriptor + .build("OperatorStateImplTest" + System.currentTimeMillis(), Integer.class, true); + descriptor.setPartitionNumber(1); + descriptor.setIndex(0); + descriptor.setTableName(table_name); + + operatorState = (OperatorStateImpl) operatorStateBackend.getSplitListState(descriptor); + } + + @Test + public void testInit() throws Exception { + operatorStateBackend.setCheckpointId(1L); + List list = operatorState.get(); + Assert.assertEquals(list.size(), 0); + + for (int i = 0; i < 100; i++) { + operatorState.add(i); + } + Assert.assertEquals(operatorState.get().size(), 100); + operatorStateBackend.finish(1L); + operatorStateBackend.commit(1L); + operatorStateBackend.ackCommit(1L, 0); + + operatorStateBackend.finish(5L); + operatorStateBackend.commit(5L); + operatorStateBackend.ackCommit(5L, 0); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/ValueStateImplTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/ValueStateImplTest.java new file mode 100644 index 000000000..bbda18e87 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/impl/ValueStateImplTest.java @@ -0,0 +1,57 @@ +/* + * 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 io.ray.streaming.state.keystate.state.impl; + +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.impl.MemoryStateBackend; +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import java.util.HashMap; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ValueStateImplTest { + + ValueStateImpl valueState; + KeyStateBackend keyStateBackend; + + @BeforeClass + public void setUp() throws Exception { + keyStateBackend = new KeyStateBackend(1, new KeyGroup(1, 2), + new MemoryStateBackend(new HashMap<>())); + ValueStateDescriptor descriptor = ValueStateDescriptor + .build("ValueStateImplTest", String.class, "hello"); + descriptor.setTableName("table"); + + valueState = (ValueStateImpl) keyStateBackend.getValueState(descriptor); + } + + @Test + public void testUpdateGet() throws Exception { + keyStateBackend.setContext(1L, 1); + + Assert.assertEquals(valueState.get(), "hello"); + + String str = valueState.get(); + + valueState.update(str + " world"); + Assert.assertEquals(valueState.get(), "hello world"); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/ListStateStoreManagerTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/ListStateStoreManagerTest.java new file mode 100644 index 000000000..1c9c5fe90 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/ListStateStoreManagerTest.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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import java.util.Arrays; +import java.util.List; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ListStateStoreManagerTest extends StateStoreManagerTest { + + ListStateStoreManagerProxy proxy; + + @BeforeClass + public void setUp() { + ListStateDescriptor descriptor = ListStateDescriptor.build("list", Integer.class); + descriptor.setTableName("tableName"); + keyStateBackend.setContext(1L, "key"); + proxy = new ListStateStoreManagerProxy<>(keyStateBackend, descriptor); + } + + @Test + public void test() throws Exception { + Assert.assertEquals(proxy.getListState().get().size(), 0); + + List list = Arrays.asList(1, 2, 3); + proxy.put("key1", list); + Assert.assertEquals(proxy.get("key1"), list); + + proxy.put("key1", Arrays.asList(1, 3)); + + proxy.put("key2", Arrays.asList(4, 5)); + Assert.assertEquals(proxy.get("key2"), Arrays.asList(4, 5)); + + Assert.assertEquals(proxy.get("key1"), Arrays.asList(1, 3)); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/MapStateStoreManagerTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/MapStateStoreManagerTest.java new file mode 100644 index 000000000..8b8ea73d6 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/MapStateStoreManagerTest.java @@ -0,0 +1,56 @@ +/* + * 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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import java.util.HashMap; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class MapStateStoreManagerTest extends StateStoreManagerTest { + + MapStateStoreManagerProxy facade; + + @BeforeClass + public void setUp() { + MapStateDescriptor descriptor = MapStateDescriptor + .build("map", String.class, Integer.class); + descriptor.setTableName("tableName"); + keyStateBackend.setContext(1L, "key"); + facade = new MapStateStoreManagerProxy<>(keyStateBackend, descriptor); + } + + @Test + public void test() throws Exception { + Assert.assertEquals(facade.getMapState().get().size(), 0); + + Map map = new HashMap<>(); + map.put("key1", 1); + map.put("key2", 2); + facade.put("key1", map); + Assert.assertEquals(facade.get("key1"), map); + + map.remove("key1"); + facade.put("key2", map); + Assert.assertEquals(facade.get("key2"), map); + } + +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/StateStoreManagerTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/StateStoreManagerTest.java new file mode 100644 index 000000000..e6a0971fb --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/StateStoreManagerTest.java @@ -0,0 +1,30 @@ +/* + * 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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.impl.MemoryStateBackend; +import io.ray.streaming.state.keystate.KeyGroup; +import java.util.HashMap; + +public class StateStoreManagerTest { + + protected KeyStateBackend keyStateBackend = new KeyStateBackend(1, new KeyGroup(1, 1), + new MemoryStateBackend(new HashMap())); +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/ValueStateStoreManagerTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/ValueStateStoreManagerTest.java new file mode 100644 index 000000000..f3a8584b8 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/keystate/state/proxy/ValueStateStoreManagerTest.java @@ -0,0 +1,57 @@ +/* + * 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 io.ray.streaming.state.keystate.state.proxy; + +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ValueState; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class ValueStateStoreManagerTest extends StateStoreManagerTest { + + ValueStateStoreManagerProxy proxy; + + @BeforeClass + public void setUp() { + ValueStateDescriptor descriptor = ValueStateDescriptor + .build("value", Integer.class, 0); + descriptor.setTableName("tableName"); + keyStateBackend.setContext(1L, "key"); + proxy = new ValueStateStoreManagerProxy<>(keyStateBackend, descriptor); + } + + @Test + public void test() throws Exception { + ValueState state = proxy.getValueState(); + Assert.assertEquals(state.get().intValue(), 0); + + proxy.put("key1", 2); + Assert.assertEquals(proxy.get("key1").intValue(), 2); + + proxy.put("key1", 3); + Assert.assertEquals(proxy.get("key1").intValue(), 3); + + proxy.put("key2", 9); + Assert.assertEquals(proxy.get("key2").intValue(), 9); + + proxy.put("key2", 6); + Assert.assertEquals(proxy.get("key2").intValue(), 6); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/strategy/DualStateStrategyTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/strategy/DualStateStrategyTest.java new file mode 100644 index 000000000..16fa4d29d --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/strategy/DualStateStrategyTest.java @@ -0,0 +1,434 @@ +/* + * 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 io.ray.streaming.state.strategy; + +import com.google.common.collect.Lists; +import io.ray.streaming.state.backend.BackendType; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.StateBackendBuilder; +import io.ray.streaming.state.backend.StateStrategy; +import io.ray.streaming.state.config.ConfigKey; +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.ValueState; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class DualStateStrategyTest { + + private final String table = "kepler_cp_store"; + private final String defaultValue = "default"; + protected KeyStateBackend keyStateBackend; + Map config = new HashMap<>(); + private String currentTime; + + @BeforeClass + public void setUp() { + config.put(ConfigKey.STATE_STRATEGY_MODE, StateStrategy.DUAL_VERSION.name()); + currentTime = Long.toString(System.currentTimeMillis()); + } + + public void caseKV() { + ValueStateDescriptor valueStateDescriptor = ValueStateDescriptor + .build("VALUE-" + currentTime, String.class, defaultValue); + valueStateDescriptor.setTableName(table); + ValueState state = this.keyStateBackend.getValueState(valueStateDescriptor); + + this.keyStateBackend.setCheckpointId(1l); + + state.setCurrentKey("1"); + state.update("hello"); + state.setCurrentKey("2"); + state.update("world"); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "hello"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "world"); + + this.keyStateBackend.finish(1); + + this.keyStateBackend.setCheckpointId(2); + state.setCurrentKey(("3")); + state.update("eagle"); + state.setCurrentKey(("4")); + state.update("alex"); + + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "eagle"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "alex"); + + this.keyStateBackend.commit(1); + this.keyStateBackend.ackCommit(1, 1); + + this.keyStateBackend.finish(2); + this.keyStateBackend.setCheckpointId(3); + + state.setCurrentKey(("1")); + state.update("tim"); + state.setCurrentKey(("4")); + state.update("scala"); + + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4); + + state.setCurrentKey(("3")); + state.update("cook"); + state.setCurrentKey(("2")); + state.update("inf"); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "tim"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "inf"); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "cook"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "scala"); + + this.keyStateBackend.commit(2); + this.keyStateBackend.ackCommit(2, 2); + + //do rollback, all memory data is deleted. + this.keyStateBackend.rollBack(1); + this.keyStateBackend.setCheckpointId(1); + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), defaultValue); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), defaultValue); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), defaultValue); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), defaultValue); + + this.keyStateBackend.setCheckpointId(4); + this.keyStateBackend.setCurrentKey("1"); + state.update("tim"); + this.keyStateBackend.finish(4); + + this.keyStateBackend.setCheckpointId(5); + this.keyStateBackend.setCurrentKey("2"); + state.update("info"); + this.keyStateBackend.finish(5); + + this.keyStateBackend.setCheckpointId(6); + state.update("cook"); + this.keyStateBackend.finish(6); + + this.keyStateBackend.setCheckpointId(7); + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(), "tim"); + + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(), "cook"); + + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(), "tim"); + + this.keyStateBackend.rollBack(6); + } + + public void caseKVGap() { + ValueStateDescriptor valueStateDescriptor = ValueStateDescriptor + .build("value2-" + currentTime, String.class, defaultValue); + valueStateDescriptor.setTableName(table); + ValueState state = this.keyStateBackend.getValueState(valueStateDescriptor); + + this.keyStateBackend.setCheckpointId(5L); + + state.setCurrentKey("1"); + state.update("hello"); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "hello"); + + this.keyStateBackend.setCheckpointId(5); + this.keyStateBackend.setCurrentKey("1"); + state.update("info"); + this.keyStateBackend.finish(5); + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.setCheckpointId(10); + Assert.assertEquals(state.get(), "info"); + this.keyStateBackend.finish(10); + this.keyStateBackend.commit(10); + this.keyStateBackend.ackCommit(10, 10); + + this.keyStateBackend.setCheckpointId(15); + state.update("world"); + this.keyStateBackend.finish(15); + this.keyStateBackend.commit(15); + this.keyStateBackend.ackCommit(15, 15); + + this.keyStateBackend.setCheckpointId(11); + this.keyStateBackend.rollBack(11); + Assert.assertEquals(state.get(), "info"); + + this.keyStateBackend.setCheckpointId(15); + state.update("world2"); + this.keyStateBackend.finish(15); + this.keyStateBackend.commit(15); + this.keyStateBackend.ackCommit(15, 15); + + this.keyStateBackend.setCheckpointId(11); + this.keyStateBackend.rollBack(11); + Assert.assertEquals(state.get(), "info"); + + } + + public void caseKList() { + ListStateDescriptor listStateDescriptor = ListStateDescriptor + .build("LIST-" + currentTime, Integer.class); + listStateDescriptor.setTableName(table); + ListState state = this.keyStateBackend.getListState(listStateDescriptor); + + this.keyStateBackend.setCheckpointId(1l); + + state.setCurrentKey("1"); + state.add(1); + state.setCurrentKey("2"); + state.add(2); + + state.setCurrentKey("1"); + List result = state.get(); + Assert.assertEquals(result, Arrays.asList(1)); + state.setCurrentKey("2"); + Assert.assertEquals(state.get(), Arrays.asList(2)); + + this.keyStateBackend.finish(1); + + this.keyStateBackend.setCheckpointId(2); + state.setCurrentKey(("3")); + state.add(3); + state.setCurrentKey(("4")); + state.add(4); + + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), Arrays.asList(3)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), Arrays.asList(4)); + + this.keyStateBackend.commit(1); + this.keyStateBackend.ackCommit(1, 1); + + this.keyStateBackend.finish(2); + this.keyStateBackend.setCheckpointId(3); + + state.setCurrentKey(("1")); + state.add(2); + state.setCurrentKey(("4")); + state.add(5); + + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4); + + state.setCurrentKey(("3")); + state.add(4); + state.setCurrentKey(("2")); + state.add(3); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), Arrays.asList(1, 2)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), Arrays.asList(2, 3)); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), Arrays.asList(3, 4)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), Arrays.asList(4, 5)); + + this.keyStateBackend.commit(2); + this.keyStateBackend.ackCommit(2, 2); + + //do rollback, all memory data is deleted. + this.keyStateBackend.rollBack(1); + this.keyStateBackend.setCheckpointId(1); + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), Lists.newArrayList()); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), Lists.newArrayList()); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), Lists.newArrayList()); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), Lists.newArrayList()); + + this.keyStateBackend.setCheckpointId(4); + this.keyStateBackend.setCurrentKey("1"); + state.add(1); + this.keyStateBackend.finish(4); + + this.keyStateBackend.setCheckpointId(5); + this.keyStateBackend.setCurrentKey("2"); + state.add(2); + this.keyStateBackend.finish(5); + + this.keyStateBackend.setCheckpointId(6); + state.add(3); + this.keyStateBackend.finish(6); + + this.keyStateBackend.setCheckpointId(7); + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(), Arrays.asList(2, 2, 3)); + + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.rollBack(5); + + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(), Arrays.asList(1)); + } + + public void caseKMap() { + MapStateDescriptor mapStateDescriptor = MapStateDescriptor + .build("MAP-" + currentTime, Integer.class, Integer.class); + mapStateDescriptor.setTableName(table); + MapState state = this.keyStateBackend.getMapState(mapStateDescriptor); + + this.keyStateBackend.setCheckpointId(1l); + + state.setCurrentKey("1"); + state.put(1, 1); + state.setCurrentKey("2"); + state.put(2, 2); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + + this.keyStateBackend.finish(1); + + this.keyStateBackend.setCheckpointId(2); + state.setCurrentKey(("3")); + state.put(3, 3); + state.setCurrentKey(("4")); + state.put(4, 4); + + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(3), Integer.valueOf(3)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(4), Integer.valueOf(4)); + + this.keyStateBackend.commit(1); + this.keyStateBackend.ackCommit(1, 1); + + this.keyStateBackend.finish(2); + this.keyStateBackend.setCheckpointId(3); + + state.setCurrentKey(("1")); + state.put(5, 5); + state.setCurrentKey(("4")); + state.put(6, 6); + + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4); + + state.setCurrentKey(("3")); + state.put(7, 7); + state.setCurrentKey(("2")); + state.put(8, 8); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + Assert.assertEquals(state.get(5), Integer.valueOf(5)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertEquals(state.get(8), Integer.valueOf(8)); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(3), Integer.valueOf(3)); + Assert.assertEquals(state.get(7), Integer.valueOf(7)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(4), Integer.valueOf(4)); + Assert.assertEquals(state.get(6), Integer.valueOf(6)); + + this.keyStateBackend.commit(2); + this.keyStateBackend.ackCommit(2, 2); + + //do rollback, memory data is deleted. + this.keyStateBackend.rollBack(1); + this.keyStateBackend.setCheckpointId(1); + state.setCurrentKey(("1")); + Assert.assertEquals(state.entries(), (new HashMap()).entrySet()); + state.setCurrentKey(("2")); + Assert.assertEquals(state.entries(), (new HashMap()).entrySet()); + state.setCurrentKey(("3")); + Assert.assertEquals(state.entries(), (new HashMap()).entrySet()); + state.setCurrentKey(("4")); + Assert.assertEquals(state.entries(), (new HashMap()).entrySet()); + + this.keyStateBackend.setCheckpointId(4); + this.keyStateBackend.setCurrentKey("1"); + state.put(1, 1); + this.keyStateBackend.finish(4); + + this.keyStateBackend.setCheckpointId(5); + this.keyStateBackend.setCurrentKey("2"); + state.put(2, 2); + this.keyStateBackend.finish(5); + + this.keyStateBackend.setCheckpointId(6); + state.put(3, 3); + this.keyStateBackend.finish(6); + + this.keyStateBackend.setCheckpointId(7); + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertEquals(state.get(3), Integer.valueOf(3)); + + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.rollBack(5); + + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertEquals(state.get(3), null); + } + + @Test + public void testMem() { + config.put(ConfigKey.STATE_BACKEND_TYPE, BackendType.MEMORY.name()); + this.keyStateBackend = new KeyStateBackend(10, new KeyGroup(1, 3), + StateBackendBuilder.buildStateBackend(config)); + caseKV(); + caseKVGap(); + caseKList(); + caseKMap(); + } +} diff --git a/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/strategy/MVStateStrategyTest.java b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/strategy/MVStateStrategyTest.java new file mode 100644 index 000000000..4580e3890 --- /dev/null +++ b/streaming/java/streaming-state/src/test/java/io/ray/streaming/state/strategy/MVStateStrategyTest.java @@ -0,0 +1,405 @@ +/* + * 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 io.ray.streaming.state.strategy; + +import io.ray.streaming.state.backend.BackendType; +import io.ray.streaming.state.backend.KeyStateBackend; +import io.ray.streaming.state.backend.StateBackendBuilder; +import io.ray.streaming.state.backend.StateStrategy; +import io.ray.streaming.state.config.ConfigKey; +import io.ray.streaming.state.keystate.KeyGroup; +import io.ray.streaming.state.keystate.desc.ListStateDescriptor; +import io.ray.streaming.state.keystate.desc.MapStateDescriptor; +import io.ray.streaming.state.keystate.desc.ValueStateDescriptor; +import io.ray.streaming.state.keystate.state.ListState; +import io.ray.streaming.state.keystate.state.MapState; +import io.ray.streaming.state.keystate.state.ValueState; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +public class MVStateStrategyTest { + + private final String table = "kepler_hlg_ut"; + Map config = new HashMap<>(); + private KeyStateBackend keyStateBackend; + private String currentTime; + + @BeforeClass + public void setUp() { + config.put(ConfigKey.STATE_STRATEGY_MODE, StateStrategy.SINGLE_VERSION.name()); + currentTime = Long.toString(System.currentTimeMillis()); + } + + public void caseKV() { + + ValueStateDescriptor valueStateDescriptor = ValueStateDescriptor + .build("mvint-" + currentTime, String.class, ""); + valueStateDescriptor.setTableName(table); + ValueState state = this.keyStateBackend.getValueState(valueStateDescriptor); + + this.keyStateBackend.setCheckpointId(1l); + + state.setCurrentKey("1"); + state.update("hello"); + state.setCurrentKey("2"); + state.update("world"); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "hello"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "world"); + + this.keyStateBackend.finish(1); + + this.keyStateBackend.setCheckpointId(2); + state.setCurrentKey(("3")); + state.update("eagle"); + state.setCurrentKey(("4")); + state.update("alex"); + + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "eagle"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "alex"); + + this.keyStateBackend.commit(1); + this.keyStateBackend.ackCommit(1, 1); + + this.keyStateBackend.finish(2); + this.keyStateBackend.setCheckpointId(3); + + state.setCurrentKey(("1")); + state.update("tim"); + state.setCurrentKey(("4")); + state.update("scala"); + + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4); + + state.setCurrentKey(("3")); + state.update("cook"); + state.setCurrentKey(("2")); + state.update("inf"); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "tim"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "inf"); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "cook"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "scala"); + + this.keyStateBackend.commit(2); + this.keyStateBackend.ackCommit(2, 2); + + this.keyStateBackend.rollBack(2); + this.keyStateBackend.setCheckpointId(3); + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "hello"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "world"); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "eagle"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "alex"); + + state.setCurrentKey(("1")); + state.update("tim"); + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4l); + + state.setCurrentKey(("4")); + state.update("scala"); + this.keyStateBackend.finish(4); + this.keyStateBackend.setCheckpointId(5l); + + state.setCurrentKey(("3")); + state.update("cook"); + this.keyStateBackend.finish(5); + + state.setCurrentKey(("2")); + state.update("info"); + this.keyStateBackend.finish(6); + this.keyStateBackend.setCheckpointId(6); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "tim"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "info"); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "cook"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "scala"); + + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.rollBack(5); + + this.keyStateBackend.setCheckpointId(6); + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), "tim"); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), "world"); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), "cook"); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), "scala"); + } + + public void caseKList() { + ListStateDescriptor listStateDescriptor = ListStateDescriptor + .build("mvlist-" + currentTime, Integer.class); + listStateDescriptor.setTableName(table); + ListState state = this.keyStateBackend.getListState(listStateDescriptor); + + this.keyStateBackend.setCheckpointId(1l); + + state.setCurrentKey("1"); + state.add(1); + state.setCurrentKey("2"); + state.add(2); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), Arrays.asList(1)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), Arrays.asList(2)); + + this.keyStateBackend.finish(1); + + this.keyStateBackend.setCheckpointId(2); + state.setCurrentKey(("3")); + state.add(3); + state.setCurrentKey(("4")); + state.add(4); + + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), Arrays.asList(3)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), Arrays.asList(4)); + + this.keyStateBackend.commit(1); + this.keyStateBackend.ackCommit(1, 1); + + this.keyStateBackend.finish(2); + this.keyStateBackend.setCheckpointId(3); + + state.setCurrentKey(("1")); + state.add(2); + state.setCurrentKey(("4")); + state.add(5); + + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4); + + state.setCurrentKey(("3")); + state.add(4); + state.setCurrentKey(("2")); + state.add(3); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), Arrays.asList(1, 2)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), Arrays.asList(2, 3)); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), Arrays.asList(3, 4)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), Arrays.asList(4, 5)); + + this.keyStateBackend.commit(2); + this.keyStateBackend.ackCommit(2, 2); + + this.keyStateBackend.rollBack(2); + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(), Arrays.asList(1)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(), Arrays.asList(2)); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(), Arrays.asList(3)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(), Arrays.asList(4)); + + this.keyStateBackend.setCheckpointId(4); + this.keyStateBackend.setCurrentKey("1"); + state.add(1); + this.keyStateBackend.finish(4); + + this.keyStateBackend.setCheckpointId(5); + this.keyStateBackend.setCurrentKey("2"); + state.add(2); + this.keyStateBackend.finish(5); + + this.keyStateBackend.setCheckpointId(6); + state.add(3); + this.keyStateBackend.finish(6); + + this.keyStateBackend.setCheckpointId(7); + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(), Arrays.asList(1, 1)); + + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(), Arrays.asList(2, 2, 3)); + + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.rollBack(5); + + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(), Arrays.asList(1, 1)); + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(), Arrays.asList(2, 2)); + } + + public void caseKMap() { + MapStateDescriptor mapStateDescriptor = MapStateDescriptor + .build("mvmap-" + currentTime, Integer.class, Integer.class); + mapStateDescriptor.setTableName(table); + MapState state = this.keyStateBackend.getMapState(mapStateDescriptor); + + this.keyStateBackend.setCheckpointId(1l); + + state.setCurrentKey("1"); + state.put(1, 1); + state.setCurrentKey("2"); + state.put(2, 2); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + + this.keyStateBackend.finish(1); + + this.keyStateBackend.setCheckpointId(2); + state.setCurrentKey(("3")); + state.put(3, 3); + state.setCurrentKey(("4")); + state.put(4, 4); + + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(3), Integer.valueOf(3)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(4), Integer.valueOf(4)); + + this.keyStateBackend.commit(1); + this.keyStateBackend.ackCommit(1, 1); + + this.keyStateBackend.finish(2); + this.keyStateBackend.setCheckpointId(3); + + state.setCurrentKey(("1")); + state.put(5, 5); + state.setCurrentKey(("4")); + state.put(6, 6); + + this.keyStateBackend.finish(3); + this.keyStateBackend.setCheckpointId(4); + + state.setCurrentKey(("3")); + state.put(7, 7); + state.setCurrentKey(("2")); + state.put(8, 8); + + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + Assert.assertEquals(state.get(5), Integer.valueOf(5)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertEquals(state.get(8), Integer.valueOf(8)); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(3), Integer.valueOf(3)); + Assert.assertEquals(state.get(7), Integer.valueOf(7)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(4), Integer.valueOf(4)); + Assert.assertEquals(state.get(6), Integer.valueOf(6)); + + this.keyStateBackend.commit(2); + this.keyStateBackend.ackCommit(2, 2); + + this.keyStateBackend.rollBack(2); + state.setCurrentKey(("1")); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + Assert.assertNull(state.get(5)); + state.setCurrentKey(("2")); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertNull(state.get(8)); + state.setCurrentKey(("3")); + Assert.assertEquals(state.get(3), Integer.valueOf(3)); + Assert.assertNull(state.get(7)); + state.setCurrentKey(("4")); + Assert.assertEquals(state.get(4), Integer.valueOf(4)); + Assert.assertNull(state.get(6)); + + this.keyStateBackend.setCheckpointId(4); + this.keyStateBackend.setCurrentKey("1"); + state.put(5, 5); + this.keyStateBackend.finish(4); + + this.keyStateBackend.setCheckpointId(5); + this.keyStateBackend.setCurrentKey("2"); + state.put(8, 8); + this.keyStateBackend.finish(5); + + this.keyStateBackend.setCheckpointId(6); + state.put(7, 7); + this.keyStateBackend.finish(6); + + this.keyStateBackend.setCheckpointId(7); + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + Assert.assertEquals(state.get(5), Integer.valueOf(5)); + + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertEquals(state.get(8), Integer.valueOf(8)); + Assert.assertEquals(state.get(7), Integer.valueOf(7)); + + this.keyStateBackend.commit(5); + this.keyStateBackend.ackCommit(5, 5); + + this.keyStateBackend.rollBack(5); + + this.keyStateBackend.setCurrentKey("1"); + Assert.assertEquals(state.get(1), Integer.valueOf(1)); + Assert.assertEquals(state.get(5), Integer.valueOf(5)); + + this.keyStateBackend.setCurrentKey("2"); + Assert.assertEquals(state.get(2), Integer.valueOf(2)); + Assert.assertEquals(state.get(8), Integer.valueOf(8)); + Assert.assertNull(state.get(7)); + } + + + @Test + public void testMemMV() { + config.put(ConfigKey.STATE_BACKEND_TYPE, BackendType.MEMORY.name()); + this.keyStateBackend = new KeyStateBackend(10, new KeyGroup(1, 3), + StateBackendBuilder.buildStateBackend(config)); + caseKV(); + caseKList(); + caseKMap(); + } +} +