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