diff --git a/hugegraph-pd/hg-pd-cli/pom.xml b/hugegraph-pd/hg-pd-cli/pom.xml
new file mode 100644
index 0000000000..d570072a0f
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+
+ 4.0.0
+
+ org.apache.hugegraph
+ hugegraph
+ 1.5.0
+ ../../pom.xml
+
+
+ hg-pd-cli
+
+
+ 11
+ 11
+ UTF-8
+
+
+
+ org.projectlombok
+ lombok
+
+
+ org.apache.hugegraph
+ hg-pd-common
+ 1.5.0
+ compile
+
+
+ org.apache.commons
+ commons-lang3
+ 3.13.0
+ compile
+
+
+ com.alipay.sofa
+ jraft-core
+ 1.3.13
+ compile
+
+
+ org.apache.hugegraph
+ hg-store-client
+ 1.5.0
+ compile
+
+
+ org.apache.hugegraph
+ hg-pd-client
+ ${revision}
+
+
+
+
diff --git a/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/CliApplication.java b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/CliApplication.java
new file mode 100644
index 0000000000..be50764dc6
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/CliApplication.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 org.apache.hugegraph.pd.cli;
+
+import org.apache.hugegraph.pd.cli.cmd.ChangeRaft;
+import org.apache.hugegraph.pd.cli.cmd.CheckPeers;
+import org.apache.hugegraph.pd.cli.cmd.Command;
+import org.apache.hugegraph.pd.cli.cmd.Config;
+import org.apache.hugegraph.pd.cli.cmd.Parameter;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class CliApplication {
+
+ public static void main(String[] args) {
+ try {
+ Parameter parameter = Command.toParameter(args);
+ Command command;
+ switch (parameter.getCmd()) {
+ case "config":
+ command = new Config(parameter.getPd());
+ break;
+ case "change_raft":
+ command = new ChangeRaft(parameter.getPd());
+ break;
+ case "check_peers":
+ command = new CheckPeers(parameter.getPd());
+ break;
+ default:
+ log.error("无效的指令");
+ return;
+ }
+ command.action(parameter.getParams());
+ } catch (Exception e) {
+ log.error("main thread error:", e);
+ System.exit(0);
+ } finally {
+
+ }
+
+ }
+}
diff --git a/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/ChangeRaft.java b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/ChangeRaft.java
new file mode 100644
index 0000000000..991ebdae79
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/ChangeRaft.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 org.apache.hugegraph.pd.cli.cmd;
+
+import org.apache.hugegraph.pd.common.PDException;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/10/17
+ **/
+public class ChangeRaft extends Command {
+
+ public ChangeRaft(String pd) {
+ super(pd);
+ }
+
+ @Override
+ public void action(String[] params) throws PDException {
+ pdClient.updatePdRaft(params[0]);
+ }
+}
diff --git a/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/CheckPeers.java b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/CheckPeers.java
new file mode 100644
index 0000000000..73f293225f
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/CheckPeers.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.pd.cli.cmd;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hugegraph.pd.client.MetaClient;
+import org.apache.hugegraph.pd.common.PDException;
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.pd.grpc.Metapb.Store;
+import org.apache.hugegraph.pd.grpc.ShardGroups;
+import org.apache.hugegraph.store.client.grpc.GrpcStoreStateClient;
+
+import com.alipay.sofa.jraft.entity.PeerId;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @date 2023/10/17
+ **/
+@Slf4j
+public class CheckPeers extends Command {
+
+ private MetaClient metaClient;
+
+ public CheckPeers(String pd) {
+ super(pd);
+ metaClient = new MetaClient(config);
+ }
+
+ @Override
+ public void action(String[] params) throws PDException {
+ GrpcStoreStateClient stateClient = new GrpcStoreStateClient(config);
+ try {
+ ConcurrentHashMap> result = new ConcurrentHashMap<>();
+ List stores = pdClient.getActiveStores();
+ ShardGroups shardGroups = metaClient.getShardGroups();
+ stores.parallelStream().forEach(s -> {
+ for (Metapb.ShardGroup sg : shardGroups.getDataList()) {
+ String groupId = "hg_" + sg.getId();
+ PeerId leader = new PeerId();
+ result.computeIfAbsent(groupId, (key) -> new ConcurrentHashMap<>());
+ try {
+ String peers = stateClient.getPeers(s.getAddress(), sg.getId());
+ if (StringUtils.isEmpty(peers)){
+ continue;
+ }
+ Map nodePeers = result.get(groupId);
+ nodePeers.put(s.getRaftAddress(), peers.split(","));
+ } catch (Exception e) {
+ if (e.getMessage() != null &&
+ (e.getMessage().contains("Fail to get leader of group") ||
+ e.getMessage().contains("Fail to find node"))) {
+ continue;
+ }
+ log.error(String.format("got %s: %s with error:", groupId, leader), e);
+ }
+ }
+ });
+ result.entrySet().parallelStream().forEach(entry -> {
+ String[] record = null;
+ String groupId = entry.getKey();
+ Map nodePeers = entry.getValue();
+ for (Map.Entry e : nodePeers.entrySet()) {
+ if (record == null) {
+ record = e.getValue();
+ continue;
+ }
+ if (!Arrays.equals(record, e.getValue())) {
+ log.error("group: {} ,got error peers: {}", groupId, nodePeers);
+ break;
+ }
+
+ }
+ });
+ log.info("got all node info:{}", result);
+ } catch (Exception e) {
+ log.error("check peers with error:", e);
+ throw e;
+ } finally {
+ stateClient.close();
+ }
+ }
+}
diff --git a/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Command.java b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Command.java
new file mode 100644
index 0000000000..f218f6416b
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Command.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 org.apache.hugegraph.pd.cli.cmd;
+
+import org.apache.hugegraph.pd.client.PDClient;
+import org.apache.hugegraph.pd.client.PDConfig;
+import org.apache.hugegraph.pd.common.PDException;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/10/17
+ **/
+public abstract class Command {
+
+ protected static String error = "启动参数: 命令, pd地址, 命令参数, 参数分隔符(非必须)";
+ protected PDClient pdClient;
+ protected PDConfig config;
+
+ public Command(String pd) {
+ config = PDConfig.of(pd).setAuthority("store", "");
+ pdClient = PDClient.create(config);
+ }
+
+ public static Parameter toParameter(String[] args) throws PDException {
+ if (args.length < 3) {
+ throw new PDException(-1, error);
+ }
+ Parameter parameter = new Parameter();
+ parameter.setCmd(args[0]);
+ parameter.setPd(args[1]);
+ if (args.length == 3) {
+ parameter.setParams(new String[]{args[2]});
+ } else {
+ String t = args[3];
+ if (t != null && t.length() > 0) {
+ parameter.setParams(args[2].split(t));
+ parameter.setSeparator(t);
+ }
+ }
+ return parameter;
+ }
+
+ public abstract void action(String[] params) throws Exception;
+}
diff --git a/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Config.java b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Config.java
new file mode 100644
index 0000000000..8e3f65df03
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Config.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 org.apache.hugegraph.pd.cli.cmd;
+
+import org.apache.hugegraph.pd.common.PDException;
+import org.apache.hugegraph.pd.grpc.Metapb;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/10/17
+ **/
+public class Config extends Command {
+
+ public Config(String pd) {
+ super(pd);
+ }
+
+ @Override
+ public void action(String[] params) throws PDException {
+ String param = params[0];
+ String[] pair = param.split("=");
+ String key = pair[0].trim();
+ Object value = null;
+ if (pair.length > 1) {
+ value = pair[1].trim();
+ }
+ if (value == null) {
+ Metapb.PDConfig pdConfig = pdClient.getPDConfig();
+ switch (key) {
+ case "enableBatchLoad":
+ // value = pdConfig.getEnableBatchLoad();
+ break;
+ case "shardCount":
+ value = pdConfig.getShardCount();
+ break;
+ }
+
+ System.out.println("Get config " + key + "=" + value);
+ } else {
+ Metapb.PDConfig.Builder builder = Metapb.PDConfig.newBuilder();
+ switch (key) {
+ case "enableBatchLoad":
+ // builder.setEnableBatchLoad(Boolean.valueOf((String)value));
+ case "shardCount":
+ builder.setShardCount(Integer.valueOf((String) value));
+ }
+ pdClient.setPDConfig(builder.build());
+ System.out.println("Set config " + key + "=" + value);
+ }
+ }
+}
diff --git a/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Parameter.java b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Parameter.java
new file mode 100644
index 0000000000..170dd83c88
--- /dev/null
+++ b/hugegraph-pd/hg-pd-cli/src/main/java/org/apache/hugegraph/pd/cli/cmd/Parameter.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 org.apache.hugegraph.pd.cli.cmd;
+
+import lombok.Data;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/10/20
+ **/
+@Data
+public class Parameter {
+ String cmd;
+ String pd;
+ String[] params;
+ String separator;
+}
diff --git a/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/ClientCache.java b/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/ClientCache.java
index 9e584583a9..c04e194242 100644
--- a/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/ClientCache.java
+++ b/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/ClientCache.java
@@ -328,4 +328,8 @@ public void updateLeader(int partitionId, Shard leader) {
}
}
}
+
+ public List getLeaderStoreAddresses() {
+ return null;
+ }
}
diff --git a/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDClient.java b/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDClient.java
index 200a35ee87..787dc6ae49 100644
--- a/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDClient.java
+++ b/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDClient.java
@@ -1284,6 +1284,21 @@ public void updatePdRaft(String raftConfig) throws PDException {
handleResponseError(response.getHeader());
}
+ public void forceReconnect() {
+ //todo soya
+ }
+
+ public PDPulse getPulse() {
+ //todo soya
+ return null;
+ }
+
+ public ShardGroup getShardGroupDirect(int partId) throws PDException {
+ //todo soya
+ return null;
+ //return this.pdApi.getShardGroupDirect(partId);
+ }
+
public interface PDEventListener {
void onStoreChanged(NodeEvent event);
diff --git a/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDConfig.java b/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDConfig.java
index 822eda3d5a..82d9827bdc 100644
--- a/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDConfig.java
+++ b/hugegraph-pd/hg-pd-client/src/main/java/org/apache/hugegraph/pd/client/PDConfig.java
@@ -1,34 +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 org.apache.hugegraph.pd.client;
-public final class PDConfig {
+import static java.nio.charset.StandardCharsets.UTF_8;
- // TODO: multi-server
- private String serverHost = "localhost:9000";
+import java.util.Base64;
+import org.apache.commons.lang3.StringUtils;
- // The timeout period for grpc call is 10 seconds
- private long grpcTimeOut = 60000;
+import com.baidu.hugegraph.pd.client.interceptor.AuthenticationException;
- // Whether to receive asynchronous PD notifications
- private boolean enablePDNotify = false;
+import lombok.Getter;
+import lombok.Setter;
+public final class PDConfig {
+ //TODO multi-server
+ private String serverHost = "localhost:9000";
+ private long grpcTimeOut = 60000; // grpc调用超时时间 10秒
+ private boolean enablePDNotify = false; // 是否接收PD异步通知
private boolean enableCache = false;
+ private String authority;
+ private String userName = "";
+ private static final int GRPC_DEFAULT_MAX_INBOUND_MESSAGE_SIZE = 1024 * 1024 * 1024;
+ private static final int GRPC_DEFAULT_MAX_OUTBOUND_MESSAGE_SIZE = 1024 * 1024 * 1024;
+ private static int inboundMessageSize = GRPC_DEFAULT_MAX_INBOUND_MESSAGE_SIZE;
+ private static int outboundMessageSize = GRPC_DEFAULT_MAX_OUTBOUND_MESSAGE_SIZE;
+ @Getter
+ @Setter
+ private boolean autoGetPdServers = false;
private PDConfig() {
}
@@ -61,6 +57,7 @@ public long getGrpcTimeOut() {
@Deprecated
public PDConfig setEnablePDNotify(boolean enablePDNotify) {
this.enablePDNotify = enablePDNotify;
+ // TODO 临时代码,hugegraph修改完后删除
this.enableCache = enablePDNotify;
return this;
}
@@ -76,8 +73,39 @@ public PDConfig setEnableCache(boolean enableCache) {
@Override
public String toString() {
- return "PDConfig{" +
- "serverHost='" + serverHost + '\'' +
- '}';
+ return "PDConfig{ serverHost='" + serverHost + '\'' + '}';
+ }
+
+ public PDConfig setAuthority(String userName, String pwd) {
+ this.userName = userName;
+ String auth = userName + ':' + pwd;
+ this.authority = new String(Base64.getEncoder().encode(auth.getBytes(UTF_8)));
+ return this;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public String getAuthority() {
+ if (StringUtils.isEmpty(this.authority)){
+ throw new AuthenticationException("invalid basic authentication info");
+ }
+ return authority;
+ }
+ public static int getInboundMessageSize() {
+ return inboundMessageSize;
+ }
+
+ public static void setInboundMessageSize(int inboundMessageSize) {
+ PDConfig.inboundMessageSize = inboundMessageSize;
+ }
+
+ public static int getOutboundMessageSize() {
+ return outboundMessageSize;
+ }
+
+ public static void setOutboundMessageSize(int outboundMessageSize) {
+ PDConfig.outboundMessageSize = outboundMessageSize;
}
}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_common.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/common.proto
similarity index 95%
rename from hugegraph-pd/hg-pd-grpc/src/main/proto/pd_common.proto
rename to hugegraph-pd/hg-pd-grpc/src/main/proto/common.proto
index c2b55c2787..b9361065b8 100644
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_common.proto
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/common.proto
@@ -22,7 +22,9 @@ option java_package = "org.apache.hugegraph.pd.grpc.common";
option java_outer_classname = "HgPdCommonProto";
message RequestHeader {
+ // cluster ID
uint64 cluster_id = 1;
+ // sender ID
uint64 sender_id = 2;
}
@@ -49,3 +51,7 @@ message Error {
ErrorType type = 1;
string message = 2;
}
+
+message NoArg{
+ RequestHeader header = 1;
+}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/kv.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/kv.proto
index 22007cda31..80faebe6e6 100644
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/kv.proto
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/kv.proto
@@ -18,7 +18,6 @@
syntax = "proto3";
package kv;
import "pdpb.proto";
-import "metapb.proto";
option java_package = "org.apache.hugegraph.pd.grpc.kv";
option java_multiple_files = true;
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/meta.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/meta.proto
new file mode 100644
index 0000000000..12d91b56d9
--- /dev/null
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/meta.proto
@@ -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.
+ */
+
+syntax = "proto3";
+package meta;
+option java_package = "org.apache.hugegraph.pd.grpc";
+import "google/protobuf/any.proto";
+import "metapb.proto";
+import "common.proto";
+import "pdpb.proto";
+option java_multiple_files = true;
+
+service MetaService{
+ rpc getStores(NoArg) returns(Stores);
+ rpc getPartitions(NoArg) returns(Partitions);
+ rpc getShardGroups(NoArg) returns(ShardGroups);
+ rpc getGraphSpaces(NoArg) returns(GraphSpaces);
+ rpc getGraphs(NoArg) returns(Graphs);
+ rpc updateStore(metapb.Store) returns(VoidResponse);
+ rpc updatePartition(metapb.Partition) returns(VoidResponse);
+ rpc updateShardGroup(metapb.ShardGroup) returns(VoidResponse);
+ rpc updateGraphSpace(metapb.GraphSpace) returns(VoidResponse);
+ rpc updateGraph(metapb.Graph) returns(VoidResponse);
+}
+message Stores{
+ pdpb.ResponseHeader header = 1;
+ repeated metapb.Store data = 2;
+}
+message Partitions{
+ pdpb.ResponseHeader header = 1;
+ repeated metapb.Partition data = 2;
+}
+message ShardGroups{
+ pdpb.ResponseHeader header = 1;
+ repeated metapb.ShardGroup data = 2;
+}
+message Shards{
+ pdpb.ResponseHeader header = 1;
+ repeated metapb.Shard data = 2;
+}
+message GraphSpaces{
+ pdpb.ResponseHeader header = 1;
+ repeated metapb.GraphSpace data = 2;
+}
+message Graphs{
+ pdpb.ResponseHeader header = 1;
+ repeated metapb.Graph data = 2;
+}
+
+message DefaultResponse{
+ pdpb.ResponseHeader header = 1;
+ repeated google.protobuf.Any data = 2;
+}
+
+message VoidResponse{
+ pdpb.ResponseHeader header = 1;
+}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/metaTask.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/metaTask.proto
index 65ab26a688..aaf2a4e2d5 100644
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/metaTask.proto
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/metaTask.proto
@@ -18,7 +18,7 @@
syntax = "proto3";
package metaTask;
import "metapb.proto";
-import "pd_pulse.proto";
+import "pulse.proto";
option java_package = "org.apache.hugegraph.pd.grpc";
enum TaskType {
@@ -28,6 +28,7 @@ enum TaskType {
Move_Partition = 3;
Clean_Partition = 4;
Change_KeyRange = 5;
+ Build_Index = 6;
}
message Task {
@@ -43,6 +44,7 @@ message Task {
MovePartition movePartition = 11;
CleanPartition cleanPartition = 12;
PartitionKeyRange partitionKeyRange = 13;
+ metapb.BuildIndex buildIndex = 14;
}
enum TaskState{
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/metapb.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/metapb.proto
index 2d361de662..ae4d335a7a 100644
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/metapb.proto
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/metapb.proto
@@ -306,6 +306,7 @@ message StoreStats {
int32 cores = 24;
// system metrics
repeated RecordPair system_metrics = 25;
+ bool executing_task = 26;
}
// Partition query criteria
@@ -390,3 +391,22 @@ enum GraphModeReason{
Initiative = 1; // Active status settings
Quota = 2; // The limit condition is reached
}
+
+message BuildIndex {
+ uint64 taskId = 1;
+ uint32 partition_id = 2;
+ BuildIndexParam param = 11;
+}
+
+message BuildIndexParam {
+ string graph = 1;
+ bytes label_id = 2;
+ bool is_vertex_label = 3;
+ bytes prefix = 4; // query prefix
+
+ oneof request_param_union {
+ bytes index_label = 11; // label id
+ bool all_index = 12; // rebuild all index
+ bool label_index = 13; // ??
+ }
+}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_pulse.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_pulse.proto
deleted file mode 100644
index afb6d6287d..0000000000
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_pulse.proto
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto3";
-
-import "metapb.proto";
-import "pd_common.proto";
-
-option java_multiple_files = true;
-option java_package = "org.apache.hugegraph.pd.grpc.pulse";
-option java_outer_classname = "HgPdPulseProto";
-
-service HgPdPulse {
- rpc Pulse(stream PulseRequest) returns (stream PulseResponse);
-}
-
-/* requests */
-message PulseRequest {
- PulseCreateRequest create_request = 1;
- PulseCancelRequest cancel_request = 2;
- PulseNoticeRequest notice_request = 3;
- PulseAckRequest ack_request = 4;
-}
-
-message PulseCreateRequest {
- PulseType pulse_type = 1;
-}
-
-message PulseCancelRequest {
- int64 observer_id = 1;
-}
-
-message PulseNoticeRequest {
- int64 observer_id = 1;
- oneof request_union {
- PartitionHeartbeatRequest partition_heartbeat_request = 10;
- }
-}
-
-message PulseAckRequest {
- int64 observer_id = 1;
- int64 notice_id = 2;
-}
-
-// When an event such as a partition heartbeat occurs such as the increase or decrease of peers in a partition or the change of leader, the leader sends a heartbeat.
-// At the same time, the pd adds or decreases shards to the partition and sends the response to the leader
-message PartitionHeartbeatRequest {
- RequestHeader header = 1;
- // Leader Peer sending the heartbeat
- metapb.PartitionStats states = 4;
-}
-
-/* responses */
-message PulseResponse {
- PulseType pulse_type = 1;
- int64 observer_id = 2;
- int32 status = 3; //0=ok,1=fail
- int64 notice_id = 4;
- oneof response_union {
- PartitionHeartbeatResponse partition_heartbeat_response = 10;
- PdInstructionResponse instruction_response = 11;
- }
-}
-
-message PartitionHeartbeatResponse {
- ResponseHeader header = 1;
- uint64 id = 3;
- metapb.Partition partition = 2;
- ChangeShard change_shard = 4;
-
- TransferLeader transfer_leader = 5;
- // Split into multiple partitions, with the first SplitPartition being the original partition and the second starting being the new partition
- SplitPartition split_partition = 6;
- // rocksdb compaction specifies the table, null is for all
- DbCompaction db_compaction = 7;
- // Migrate data from the partition to the target
- MovePartition move_partition = 8;
- // Clean up the data for the partition of the graph
- CleanPartition clean_partition = 9;
- // partition key range variation
- PartitionKeyRange key_range = 10;
-}
-
-/* Date model */
-message ChangeShard {
- repeated metapb.Shard shard = 1;
- ConfChangeType change_type = 2;
-}
-
-message TransferLeader {
- metapb.Shard shard = 1;
-}
-
-message SplitPartition {
- repeated metapb.Partition new_partition = 1;
-}
-
-message DbCompaction {
- string table_name = 3;
-}
-
-message MovePartition {
- // The new range after migration
- metapb.Partition target_partition = 1;
- // partition's key start and key end,
- // will migrate to target partition
- uint64 key_start = 2;
- uint64 key_end = 3;
-}
-
-message CleanPartition {
- uint64 key_start = 1;
- uint64 key_end = 2;
- CleanType clean_type = 3;
- bool delete_partition = 4; // Whether to delete the partition
-}
-
-message PartitionKeyRange{
- uint32 partition_id = 1;
- uint64 key_start = 2;
- uint64 key_end = 3;
-}
-
-message PdInstructionResponse {
- PdInstructionType instruction_type = 1;
- string leader_ip = 2;
-}
-
-/* enums */
-enum PulseType {
- PULSE_TYPE_UNKNOWN = 0;
- PULSE_TYPE_PARTITION_HEARTBEAT = 1;
- PULSE_TYPE_PD_INSTRUCTION = 2;
-}
-
-enum PulseChangeType {
- PULSE_CHANGE_TYPE_UNKNOWN = 0;
- PULSE_CHANGE_TYPE_ADD = 1;
- PULSE_CHANGE_TYPE_ALTER = 2;
- PULSE_CHANGE_TYPE_DEL = 3;
-}
-
-enum ConfChangeType {
- CONF_CHANGE_TYPE_UNKNOWN = 0;
- CONF_CHANGE_TYPE_ADD_NODE = 1;
- CONF_CHANGE_TYPE_REMOVE_NODE = 2;
- CONF_CHANGE_TYPE_ADD_LEARNER_NODE = 3;
- CONF_CHANGE_TYPE_ADJUST = 4; // Adjust the shard, and the leader dynamically increases or decreases according to the new configuration.
-}
-
-enum CleanType {
- CLEAN_TYPE_KEEP_RANGE = 0; // Only this range remains
- CLEAN_TYPE_EXCLUDE_RANGE = 1; // Delete this range
-}
-
-enum PdInstructionType {
- CHANGE_TO_FOLLOWER = 0;
-}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_watch.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_watch.proto
deleted file mode 100644
index 6d0c016c2c..0000000000
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/pd_watch.proto
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.
- */
-
-syntax = "proto3";
-
-import "metapb.proto";
-
-option java_multiple_files = true;
-option java_package = "org.apache.hugegraph.pd.grpc.watch";
-option java_outer_classname = "HgPdWatchProto";
-
-service HgPdWatch {
- rpc Watch(stream WatchRequest) returns (stream WatchResponse);
-}
-
-message WatchRequest {
- WatchCreateRequest create_request = 1;
- WatchCancelRequest cancel_request = 2;
-}
-
-message WatchCreateRequest {
- WatchType watch_type = 1;
-}
-
-message WatchCancelRequest {
- int64 watcher_id = 1;
-}
-
-message WatchResponse {
- WatchType watch_type = 1;
- int64 watcher_id = 2;
- int32 status = 3; //0=ok,1=fail
- int64 notice_id = 4;
- string msg = 5;
- oneof response_union {
- WatchPartitionResponse partition_response = 10;
- WatchNodeResponse node_response = 11;
- WatchGraphResponse graph_response = 12;
- WatchShardGroupResponse shard_group_response = 13;
- }
-}
-
-message WatchPartitionResponse {
- string graph = 1;
- int32 partition_id = 2;
- WatchChangeType change_type = 3;
-}
-
-message WatchNodeResponse {
- string graph = 1;
- uint64 node_id = 2;
- NodeEventType node_event_type = 3;
-}
-
-message WatchGraphResponse {
- metapb.Graph graph = 1;
- WatchType type = 2;
-}
-
-message WatchShardGroupResponse {
- metapb.ShardGroup shard_group = 1;
- WatchChangeType type = 2;
- int32 shard_group_id = 3;
-}
-
-enum WatchType {
- WATCH_TYPE_UNKNOWN = 0;
- WATCH_TYPE_PARTITION_CHANGE = 1;
- WATCH_TYPE_STORE_NODE_CHANGE = 2;
- WATCH_TYPE_GRAPH_CHANGE = 3;
- WATCH_TYPE_SHARD_GROUP_CHANGE = 4;
-}
-
-enum WatchChangeType {
- WATCH_CHANGE_TYPE_UNKNOWN = 0;
- WATCH_CHANGE_TYPE_ADD = 1;
- WATCH_CHANGE_TYPE_ALTER = 2;
- WATCH_CHANGE_TYPE_DEL = 3;
- WATCH_CHANGE_TYPE_SPECIAL1 = 4;
-}
-
-enum NodeEventType {
- NODE_EVENT_TYPE_UNKNOWN = 0;
- NODE_EVENT_TYPE_NODE_ONLINE = 1;
- NODE_EVENT_TYPE_NODE_OFFLINE = 2;
- NODE_EVENT_TYPE_NODE_RAFT_CHANGE = 3;
- NODE_EVENT_TYPE_PD_LEADER_CHANGE = 4;
-}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/pdpb.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/pdpb.proto
index f7754824ec..d03fd08355 100644
--- a/hugegraph-pd/hg-pd-grpc/src/main/proto/pdpb.proto
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/pdpb.proto
@@ -105,6 +105,15 @@ service PD {
rpc getCache(GetGraphRequest) returns (CacheResponse) {}
rpc getPartitions(GetGraphRequest) returns (CachePartitionResponse) {}
+
+ // submit rebuild index task
+ rpc submitTask(IndexTaskCreateRequest) returns (IndexTaskCreateResponse) {}
+ // query task state
+ rpc queryTaskState(IndexTaskQueryRequest) returns (IndexTaskQueryResponse) {}
+ // retry index task
+ rpc retryIndexTask(IndexTaskQueryRequest) returns (IndexTaskQueryResponse){}
+ rpc getGraphStats(GetGraphRequest) returns (GraphStatsResponse) {}
+ rpc GetMembersAndClusterState(GetMembersRequest) returns (MembersAndClusterState) {}
}
message RequestHeader {
@@ -372,6 +381,13 @@ message GetMembersResponse{
metapb.Member leader = 3;
}
+message MembersAndClusterState{
+ ResponseHeader header = 1;
+ repeated metapb.Member members = 2;
+ metapb.Member leader = 3;
+ metapb.ClusterState state = 4;
+}
+
message GetPDConfigRequest{
RequestHeader header = 1;
uint64 version = 2 ;
@@ -602,3 +618,29 @@ message CachePartitionResponse {
ResponseHeader header = 1;
repeated metapb.Partition partitions = 2;
}
+
+message IndexTaskCreateRequest {
+ RequestHeader header = 1;
+ metapb.BuildIndexParam param = 2;
+}
+
+message IndexTaskCreateResponse {
+ ResponseHeader header = 1;
+ uint64 task_id = 2;
+}
+
+message IndexTaskQueryRequest {
+ RequestHeader header = 1;
+ uint64 task_id = 2;
+}
+
+message IndexTaskQueryResponse{
+ ResponseHeader header = 1;
+ metaTask.TaskState state = 2;
+ string message = 3;
+}
+
+message GraphStatsResponse {
+ ResponseHeader header = 1;
+ metapb.GraphStats stats = 2;
+}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/pulse.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/pulse.proto
new file mode 100644
index 0000000000..05a50445ed
--- /dev/null
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/pulse.proto
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+import "metapb.proto";
+import "common.proto";
+
+option java_multiple_files = true;
+option java_package = "org.apache.hugegraph.pd.grpc.pulse";
+option java_outer_classname = "HgPdPulseProto";
+
+service HgPdPulse {
+ rpc Pulse(stream PulseRequest) returns (stream PulseResponse);
+}
+
+/* requests */
+message PulseRequest {
+ PulseCreateRequest create_request = 1;
+ PulseCancelRequest cancel_request = 2;
+ PulseNoticeRequest notice_request = 3;
+ PulseAckRequest ack_request = 4;
+}
+
+message PulseCreateRequest {
+ PulseType pulse_type = 1;
+}
+
+message PulseCancelRequest {
+ int64 observer_id = 1;
+}
+
+message PulseNoticeRequest {
+ int64 observer_id = 1;
+ oneof request_union {
+ PartitionHeartbeatRequest partition_heartbeat_request = 10;
+ }
+}
+
+message PulseAckRequest {
+ int64 observer_id = 1;
+ int64 notice_id = 2;
+}
+
+// 分区心跳,分区的peer增减、leader改变等事件发生时,由leader发送心跳。
+// 同时pd对分区进行shard增减通过Response发送给leader
+message PartitionHeartbeatRequest {
+ RequestHeader header = 1;
+ // Leader Peer sending the heartbeat
+ metapb.PartitionStats states = 4;
+}
+
+/* responses */
+message PulseResponse {
+ PulseType pulse_type = 1;
+ int64 observer_id = 2;
+ int32 status = 3; //0=ok,1=fail
+ int64 notice_id=4;
+ oneof response_union {
+ PartitionHeartbeatResponse partition_heartbeat_response = 10;
+ PdInstructionResponse instruction_response = 11;
+ }
+}
+
+message PartitionHeartbeatResponse {
+ ResponseHeader header = 1;
+ uint64 id = 3;
+ metapb.Partition partition = 2;
+ ChangeShard change_shard = 4;
+
+ TransferLeader transfer_leader = 5;
+ // 拆分成多个分区,第一个SplitPartition是原分区,从第二开始是新分区
+ SplitPartition split_partition = 6;
+ // rocksdb compaction 指定的表,null是针对所有
+ DbCompaction db_compaction = 7;
+ // 将partition的数据,迁移到 target
+ MovePartition move_partition = 8;
+ // 清理partition的graph的数据
+ CleanPartition clean_partition = 9;
+ // partition key range 变化
+ PartitionKeyRange key_range = 10;
+ // 创建索引的任务
+ metapb.BuildIndex build_index = 11;
+}
+
+/* Date model */
+message ChangeShard {
+ repeated metapb.Shard shard = 1;
+ ConfChangeType change_type = 2;
+}
+
+message TransferLeader {
+ metapb.Shard shard = 1;
+}
+
+message SplitPartition {
+ repeated metapb.Partition new_partition = 1;
+}
+
+message DbCompaction {
+ string table_name = 3;
+}
+
+message MovePartition{
+ // target partition的key range为,迁移后的新range
+ metapb.Partition target_partition = 1;
+ // partition 的 key start 和 key end的所有数据,
+ // 会迁移到 target partition 上
+ uint64 key_start = 2;
+ uint64 key_end = 3;
+}
+
+message CleanPartition {
+ uint64 key_start = 1;
+ uint64 key_end = 2;
+ CleanType clean_type = 3;
+ bool delete_partition = 4; //是否删除分区
+}
+
+message PartitionKeyRange{
+ uint32 partition_id = 1;
+ uint64 key_start = 2;
+ uint64 key_end = 3;
+}
+
+message PdInstructionResponse {
+ PdInstructionType instruction_type = 1;
+ string leader_ip = 2;
+}
+
+/* enums */
+enum PulseType {
+ PULSE_TYPE_UNKNOWN = 0;
+ PULSE_TYPE_PARTITION_HEARTBEAT = 1;
+ PULSE_TYPE_PD_INSTRUCTION = 2;
+}
+
+enum PulseChangeType {
+ PULSE_CHANGE_TYPE_UNKNOWN = 0;
+ PULSE_CHANGE_TYPE_ADD = 1;
+ PULSE_CHANGE_TYPE_ALTER = 2;
+ PULSE_CHANGE_TYPE_DEL = 3;
+}
+
+enum ConfChangeType {
+ CONF_CHANGE_TYPE_UNKNOWN = 0;
+ CONF_CHANGE_TYPE_ADD_NODE = 1;
+ CONF_CHANGE_TYPE_REMOVE_NODE = 2;
+ CONF_CHANGE_TYPE_ADD_LEARNER_NODE = 3;
+ CONF_CHANGE_TYPE_ADJUST = 4; // 调整shard,leader根据新的配置动态增减。
+}
+
+enum CleanType {
+ CLEAN_TYPE_KEEP_RANGE = 0; // 仅保留这个range
+ CLEAN_TYPE_EXCLUDE_RANGE = 1; // 删除这个range
+}
+
+enum PdInstructionType {
+ CHANGE_TO_FOLLOWER = 0;
+}
diff --git a/hugegraph-pd/hg-pd-grpc/src/main/proto/watch.proto b/hugegraph-pd/hg-pd-grpc/src/main/proto/watch.proto
new file mode 100644
index 0000000000..c9063f30d9
--- /dev/null
+++ b/hugegraph-pd/hg-pd-grpc/src/main/proto/watch.proto
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+import "metapb.proto";
+
+option java_multiple_files = true;
+option java_package = "org.apache.hugegraph.pd.grpc.watch";
+option java_outer_classname = "HgPdWatchProto";
+
+service HgPdWatch {
+ rpc Watch(stream WatchRequest) returns (stream WatchResponse);
+}
+
+message WatchRequest {
+ WatchCreateRequest create_request = 1;
+ WatchCancelRequest cancel_request = 2;
+}
+
+message WatchCreateRequest {
+ WatchType watch_type = 1;
+}
+
+message WatchCancelRequest {
+ int64 watcher_id = 1;
+}
+
+message WatchResponse {
+ WatchType watch_type = 1;
+ int64 watcher_id = 2;
+ int32 status = 3; //0=ok,1=fail
+ int64 notice_id = 4;
+ string msg = 5;
+ oneof response_union {
+ WatchPartitionResponse partition_response = 10;
+ WatchNodeResponse node_response = 11;
+ WatchGraphResponse graph_response = 12;
+ WatchShardGroupResponse shard_group_response = 13;
+ }
+}
+
+message WatchPartitionResponse {
+ string graph = 1;
+ int32 partition_id = 2;
+ WatchChangeType change_type = 3;
+}
+
+message WatchNodeResponse {
+ string graph = 1;
+ uint64 node_id = 2;
+ NodeEventType node_event_type = 3;
+}
+
+message WatchGraphResponse {
+ metapb.Graph graph = 1;
+ WatchType type = 2;
+}
+
+message WatchShardGroupResponse {
+ metapb.ShardGroup shard_group = 1;
+ WatchChangeType type = 2;
+ int32 shard_group_id = 3;
+}
+
+enum WatchType {
+ WATCH_TYPE_UNKNOWN = 0;
+ WATCH_TYPE_PARTITION_CHANGE = 1;
+ WATCH_TYPE_STORE_NODE_CHANGE = 2;
+ WATCH_TYPE_GRAPH_CHANGE = 3;
+ WATCH_TYPE_SHARD_GROUP_CHANGE = 4;
+}
+
+enum WatchChangeType {
+ WATCH_CHANGE_TYPE_UNKNOWN = 0;
+ WATCH_CHANGE_TYPE_ADD = 1;
+ WATCH_CHANGE_TYPE_ALTER = 2;
+ WATCH_CHANGE_TYPE_DEL = 3;
+ WATCH_CHANGE_TYPE_SPECIAL1 = 4;
+}
+
+enum NodeEventType {
+ NODE_EVENT_TYPE_UNKNOWN = 0;
+ NODE_EVENT_TYPE_NODE_ONLINE = 1;
+ NODE_EVENT_TYPE_NODE_OFFLINE = 2;
+ NODE_EVENT_TYPE_NODE_RAFT_CHANGE = 3;
+ // pd leader 变更
+ NODE_EVENT_TYPE_PD_LEADER_CHANGE = 4;
+}
diff --git a/hugegraph-pd/pom.xml b/hugegraph-pd/pom.xml
index b2547a7dc4..d1135c991b 100644
--- a/hugegraph-pd/pom.xml
+++ b/hugegraph-pd/pom.xml
@@ -39,6 +39,7 @@
hg-pd-corehg-pd-servicehg-pd-dist
+ hg-pd-cli
diff --git a/hugegraph-store/hg-store-cli/pom.xml b/hugegraph-store/hg-store-cli/pom.xml
index 84de815696..ab9999e56c 100644
--- a/hugegraph-store/hg-store-cli/pom.xml
+++ b/hugegraph-store/hg-store-cli/pom.xml
@@ -56,6 +56,11 @@
hg-pd-client${revision}
+
+ org.apache.hugegraph
+ hg-pd-cli
+ ${revision}
+ org.projectlomboklombok
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/CliApplication.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/CliApplication.java
new file mode 100644
index 0000000000..4e5587b445
--- /dev/null
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/CliApplication.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 org.apache.hugegraph.store.cli;
+
+import org.apache.hugegraph.store.cli.cmd.Load;
+import org.apache.hugegraph.store.cli.cmd.MultiQuery;
+import org.apache.hugegraph.store.cli.cmd.ScanShard;
+import org.apache.hugegraph.store.cli.cmd.ScanSingleShard;
+import org.apache.hugegraph.store.cli.cmd.ScanTable;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+import org.apache.hugegraph.pd.cli.cmd.ChangeRaft;
+import org.apache.hugegraph.pd.cli.cmd.CheckPeers;
+import org.apache.hugegraph.pd.cli.cmd.Command;
+import org.apache.hugegraph.pd.cli.cmd.Parameter;
+import lombok.extern.slf4j.Slf4j;
+
+;
+
+/**
+ * 2022/2/14
+ */
+@SpringBootApplication
+@Slf4j
+public class CliApplication {
+
+ public static void main(String[] args) {
+ Parameter parameter;
+ try {
+ parameter = Command.toParameter(args);
+ Command command;
+ switch (parameter.getCmd()) {
+ case "load":
+ command = new Load(parameter.getPd());
+ break;
+ case "change_raft":
+ command = new ChangeRaft(parameter.getPd());
+ break;
+ case "check_peers":
+ command = new CheckPeers(parameter.getPd());
+ break;
+ case "query":
+ command = new MultiQuery(parameter.getPd());
+ break;
+ case "scan":
+ command = new ScanTable(parameter.getPd());
+ break;
+ case "shard":
+ command = new ScanShard(parameter.getPd());
+ break;
+ case "shard-single":
+ command = new ScanSingleShard(parameter.getPd());
+ break;
+ default:
+ log.error("无效的指令");
+ return;
+ }
+ command.action(parameter.getParams());
+ } catch (Exception e) {
+ log.error("run cli command with error:", e);
+ }
+ System.exit(0);
+
+ }
+}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/StoreConsoleApplication.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/StoreConsoleApplication.java
deleted file mode 100644
index 51e3c09b7e..0000000000
--- a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/StoreConsoleApplication.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hugegraph.store.cli;
-
-import java.io.IOException;
-
-import org.apache.hugegraph.pd.client.PDConfig;
-import org.apache.hugegraph.pd.common.PDException;
-import org.apache.hugegraph.store.HgStoreClient;
-import org.apache.hugegraph.store.cli.loader.HgThread2DB;
-import org.apache.hugegraph.store.cli.scan.GrpcShardScanner;
-import org.apache.hugegraph.store.cli.scan.HgStoreScanner;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 2022/2/14
- */
-@SpringBootApplication
-@Slf4j
-public class StoreConsoleApplication implements CommandLineRunner {
-
- // TODO: this package seems to have many useless class and code, need to be updated.
- @Autowired
- private AppConfig appConfig;
-
- public static void main(String[] args) {
- log.info("Starting StoreConsoleApplication");
- SpringApplication.run(StoreConsoleApplication.class, args);
- log.info("StoreConsoleApplication finished.");
- }
-
- @Override
- public void run(String... args) throws IOException, InterruptedException, PDException {
- if (args.length <= 0) {
- log.warn("Parameter type cmd[-load, -query, -scan]");
- } else {
- switch (args[0]) {
- case "-load":
- HgThread2DB hgThread2DB = new HgThread2DB(args[1]);
- if (!args[3].isEmpty()) {
- hgThread2DB.setGraphName(args[3]);
- }
- try {
- if ("order".equals(args[2])) {
- hgThread2DB.testOrder(args[4]);
- } else {
- hgThread2DB.startMultiprocessInsert(args[2]);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- break;
- case "-query":
- HgThread2DB hgDB = new HgThread2DB(args[1]);
- try {
- hgDB.startMultiprocessQuery("12", args[2]);
- } catch (IOException e) {
- e.printStackTrace();
- }
- break;
- case "-scan":
- if (args.length < 4) {
- log.warn("Parameter type -scan pd graphName tableName");
- } else {
- doScan(args[1], args[2], args[3]);
- }
- break;
- case "-shard":
- GrpcShardScanner scanner = new GrpcShardScanner();
- scanner.getData();
- break;
- case "-shard-single":
- scanner = new GrpcShardScanner();
- scanner.getDataSingle();
- break;
- default:
- log.warn("Parameter type error, no program executed");
- }
- }
- }
-
- private void doScan(String pd, String graphName, String tableName) throws PDException {
- HgStoreClient storeClient = HgStoreClient.create(PDConfig.of(pd)
- .setEnableCache(true));
-
- HgStoreScanner storeScanner = HgStoreScanner.of(storeClient, graphName);
- storeScanner.scanTable2(tableName);
- }
-}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/Load.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/Load.java
new file mode 100644
index 0000000000..04f88a5c97
--- /dev/null
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/Load.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cli.cmd;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.hugegraph.pd.cli.cmd.Command;
+import org.apache.hugegraph.store.HgOwnerKey;
+import org.apache.hugegraph.store.HgStoreClient;
+import org.apache.hugegraph.store.HgStoreSession;
+import org.apache.hugegraph.store.cli.util.HgCliUtil;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @date 2023/10/18
+ **/
+@Slf4j
+public class Load extends Command {
+
+ private static int batchSize = 100000;
+ private static int readerSize = 5;
+ private static long printSize = 10000000;
+ private static long printCount = printSize * 1000;
+ private int pc = Runtime.getRuntime().availableProcessors();
+ private int size = pc * 2;
+ private Semaphore semaphore = new Semaphore(size);
+ private ThreadPoolExecutor executor = new ThreadPoolExecutor(size, size,
+ 0L, TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue());
+ private LinkedBlockingQueue> queue = new LinkedBlockingQueue<>(size * 2);
+ private HgStoreClient storeClient;
+ private AtomicLong insertCount = new AtomicLong();
+ private AtomicLong startTime = new AtomicLong();
+ private String table = "unknown";
+ private AtomicBoolean completed = new AtomicBoolean(false);
+ private String graph;
+ protected Runnable r = () -> {
+ long start = System.currentTimeMillis();
+ try {
+ while (!completed.get() || queue.peek() != null) {
+ semaphore.acquire();
+ List data = queue.take();
+ Runnable task = () -> {
+ try {
+ put(table, data);
+ } catch (Exception e) {
+ log.error("put data with error:", e);
+ } finally {
+ semaphore.release();
+ }
+ };
+ executor.submit(task);
+ }
+ semaphore.acquire(size);
+ semaphore.release(size);
+ log.info("*************************************************");
+ long all = insertCount.get();
+ long end = System.currentTimeMillis();
+ log.info("Load data: {}s,total: {} entries,average:{} entries/s", (end - start) / 1000,
+ all, all * 1000 / (end - start));
+ log.info("*************************************************");
+ } catch (Exception e) {
+ log.error("submit task with error:", e);
+ } finally {
+ try {
+ executor.shutdownNow();
+ } catch (Exception e) {
+
+ } finally {
+
+ }
+ }
+ };
+
+ public Load(String pd) {
+ super(pd);
+ storeClient = HgStoreClient.create(config);
+ }
+
+ @Override
+ public void action(String[] params) throws InterruptedException {
+ graph = params[0];
+ new Thread(r, "load").start();
+ String path = params[1];
+ String[] split = path.split(",");
+ readerSize = split.length;
+ CountDownLatch latch = new CountDownLatch(readerSize);
+ log.info("--- start data loading---");
+ for (int i = 0; i < readerSize; i++) {
+ int fi = i;
+ new Thread(() -> {
+ try {
+ InputStreamReader isr =
+ new InputStreamReader(new FileInputStream(split[fi]), "UTF-8");
+ BufferedReader reader = new BufferedReader(isr);
+ long count = 0;
+ String line;
+ try {
+ List keys = new ArrayList<>(batchSize);
+ while ((line = reader.readLine()) != null) {
+ keys.add(line);
+ count++;
+ if (count % batchSize == 0) {
+ List data = keys;
+ if (!data.isEmpty()) {
+ queue.put(keys);
+ keys = new ArrayList<>(batchSize);
+ }
+ continue;
+ }
+ }
+ if (count % batchSize != 0) {
+ queue.put(keys);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ } finally {
+ isr.close();
+ reader.close();
+ }
+ } catch (Exception e) {
+ log.error("send data with error:", e);
+ } finally {
+ latch.countDown();
+ }
+ }).start();
+ }
+ latch.await();
+ completed.set(true);
+ }
+
+ public boolean put(String table, List keys) {
+ HgStoreSession session = storeClient.openSession(graph);
+ session.beginTx();
+ keys.forEach((key) -> {
+ int j = key.indexOf("\t");
+ String owner = key.substring(0, j);
+ HgOwnerKey hgKey = HgCliUtil.toOwnerKey(owner, owner);
+ byte[] value = HgCliUtil.toBytes(key.substring(j + 1));
+ session.put(table, hgKey, value);
+ });
+ if (!keys.isEmpty()) {
+ if (session.isTx()) {
+ session.commit();
+ } else {
+ session.rollback();
+ }
+ }
+ long sum;
+ if ((sum = insertCount.addAndGet(keys.size())) % printSize == 0) {
+ long c = System.currentTimeMillis();
+ long start = startTime.getAndSet(c);
+ if (c > start) {
+ log.info("count: {}, tps: {}, worker: {},task queue: {}", sum,
+ printCount / (c - start),
+ executor.getActiveCount(), queue.size());
+ }
+ }
+ return true;
+ }
+
+}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/MultiQuery.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/MultiQuery.java
new file mode 100644
index 0000000000..8bf2001c8d
--- /dev/null
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/MultiQuery.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cli.cmd;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.hugegraph.pd.cli.cmd.Command;
+import org.apache.hugegraph.store.HgKvEntry;
+import org.apache.hugegraph.store.HgKvIterator;
+import org.apache.hugegraph.store.HgOwnerKey;
+import org.apache.hugegraph.store.HgScanQuery;
+import org.apache.hugegraph.store.HgStoreClient;
+import org.apache.hugegraph.store.HgStoreSession;
+import org.apache.hugegraph.store.cli.util.HgCliUtil;
+import org.apache.hugegraph.store.client.grpc.KvCloseableIterator;
+import org.apache.hugegraph.store.client.util.MetricX;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Multi-thread query
+ * point 起始查询点,后续根据这个点查询到的value做为下一次的查询条件进行迭代
+ * scanCount 允许启动的线程数量
+ *
+ * @date 2023/10/20
+ **/
+@Slf4j
+public class MultiQuery extends Command {
+
+ private static AtomicLong total = new AtomicLong();
+ private static int batchLimit = 100;
+ private final HgStoreClient storeClient;
+ public String graphName = "hugegraphtest";
+ volatile long startTime = System.currentTimeMillis();
+
+ public MultiQuery(String pd) {
+ super(pd);
+ storeClient = HgStoreClient.create(config);
+ }
+
+ @Override
+ public void action(String[] params) throws Exception {
+ String point = params[0];
+ String scanCount = params[1];
+ log.info("--- start startMultiprocessQuery---");
+ startTime = System.currentTimeMillis();
+ MetricX metrics = MetricX.ofStart();
+ batchLimit = Integer.parseInt(scanCount);
+ CountDownLatch latch = new CountDownLatch(batchLimit);
+ HgStoreSession session = storeClient.openSession(graphName);
+ final AtomicLong[] counter = {new AtomicLong()};
+ final long[] start = {System.currentTimeMillis()};
+ LinkedBlockingQueue[] queue = new LinkedBlockingQueue[batchLimit];
+ for (int i = 0; i < batchLimit; i++) {
+ queue[i] = new LinkedBlockingQueue();
+ }
+ List strKey =
+ Arrays.asList(new String[]{"20727483", "50329304", "26199460", "1177521",
+ "27960125",
+ "30440025", "15833920", "15015183", "33153097",
+ "21250581"});
+ strKey.forEach(key -> {
+ log.info("newkey:{}", key);
+ HgOwnerKey hgKey = HgCliUtil.toOwnerKey(key, key);
+ queue[0].add(hgKey);
+ });
+
+ for (int i = 0; i < batchLimit; i++) {
+ int finalI = i;
+ KvCloseableIterator> iterators =
+ session.scanBatch2(
+ HgScanQuery.prefixIteratorOf(HgCliUtil.TABLE_NAME, new Iterator<>() {
+ HgOwnerKey current = null;
+
+ @Override
+ public boolean hasNext() {
+ while (current == null) {
+ try {
+ current = (HgOwnerKey) queue[finalI].poll(1,
+ TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ //
+ }
+ }
+ if (current == null) {
+ log.info("===== current is null ==========");
+ }
+ return current != null;
+ }
+
+ @Override
+ public HgOwnerKey next() {
+ return current;
+ }
+ })
+ );
+
+ new Thread(() -> {
+ while (iterators.hasNext()) {
+ HgKvIterator iterator = iterators.next();
+ long c = 0;
+ while (iterator.hasNext()) {
+ String newPoint = HgCliUtil.toStr(iterator.next().value());
+ HgOwnerKey newHgKey = HgCliUtil.toOwnerKey(newPoint, newPoint);
+ if (queue[(int) (c % batchLimit)].size() < 1000000) {
+ queue[(int) (c % batchLimit)].add(newHgKey);
+ }
+ c++;
+ }
+ if (counter[0].addAndGet(c) > 1000000) {
+ synchronized (counter) {
+ if (counter[0].get() > 10000000) {
+ log.info("count {}, qps {}", counter[0].get(),
+ counter[0].get() * 1000 /
+ (System.currentTimeMillis() - start[0]));
+ start[0] = System.currentTimeMillis();
+ counter[0].set(0);
+ }
+ }
+ }
+ }
+ }, "client query thread:" + i).start();
+ log.info("===== read thread exit ==========");
+ }
+ latch.await();
+
+ metrics.end();
+ log.info("*************************************************");
+ log.info(" 主进程执行时间:" + metrics.past() / 1000 + "秒; 查询:" + total.get()
+ + "次,qps:" + total.get() * 1000 / metrics.past());
+ log.info("*************************************************");
+ System.out.println("-----主进程执行结束---------");
+ }
+}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/GrpcShardScanner.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/Scan.java
similarity index 54%
rename from hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/GrpcShardScanner.java
rename to hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/Scan.java
index e9e10829f0..4d7cb36153 100644
--- a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/GrpcShardScanner.java
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/Scan.java
@@ -15,87 +15,48 @@
* limitations under the License.
*/
-package org.apache.hugegraph.store.cli.scan;
+package org.apache.hugegraph.store.cli.cmd;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.hugegraph.store.grpc.GraphStoreGrpc;
import org.apache.hugegraph.store.grpc.GraphStoreGrpc.GraphStoreStub;
import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest;
import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest.Reply;
+import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest.ScanType;
import org.apache.hugegraph.store.grpc.Graphpb.ScanResponse;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
-import lombok.extern.slf4j.Slf4j;
-@Slf4j
-public class GrpcShardScanner {
+/**
+ * @date 2023/10/20
+ **/
- private final boolean closed = false;
- private final AtomicInteger sum = new AtomicInteger();
- private final ConcurrentHashMap>
- observers = new ConcurrentHashMap<>();
+public interface Scan {
- public void getData() {
- ExecutorService service = new ThreadPoolExecutor(500, Integer.MAX_VALUE,
- 0L,
- TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<>());
- long start = System.currentTimeMillis();
+ AtomicInteger sum = new AtomicInteger();
+ ConcurrentHashMap> observers =
+ new ConcurrentHashMap<>();
- String[] addresses = new String[]{"10.14.139.71:8500",
- "10.14.139.70:8500",
- "10.14.139.69:8500"};
- int pSize = 72;
- int size = pSize * addresses.length;
- CountDownLatch latch = new CountDownLatch(size);
- for (int j = 0; j < pSize; j++) {
- for (int i = 0; i < addresses.length; i++) {
- String address = addresses[i];
- int finalJ = j;
- service.execute(() -> getData(finalJ, latch, address));
- }
- }
- try {
- latch.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- long end = System.currentTimeMillis();
- long cost = end - start;
- log.info("all rows are: {}, cost: {},avg: {}", sum.get(),
- cost, sum.get() / cost * 1000);
- }
-
- public void getData(int pId, CountDownLatch latch, String address) {
+ default void getData(int pId, CountDownLatch latch, String address) {
try {
- ScanPartitionRequest.Builder builder =
- ScanPartitionRequest.newBuilder();
- ScanPartitionRequest.Request.Builder srb =
- ScanPartitionRequest.Request.newBuilder();
+ ScanPartitionRequest.Builder builder = ScanPartitionRequest.newBuilder();
+ ScanPartitionRequest.Request.Builder srb = ScanPartitionRequest.Request.newBuilder();
ScanPartitionRequest.Request request =
- srb.setGraphName("DEFAULT/hugegraph2/g")
- .setScanType(
- ScanPartitionRequest.ScanType.SCAN_EDGE)
+ srb.setGraphName("DEFAULT/hugegraph2/g").setScanType(
+ ScanType.SCAN_EDGE)
.setTable("g+oe").setBoundary(0x10)
.setPartitionId(pId).build();
- ManagedChannel c =
- ManagedChannelBuilder.forTarget(address)
- .usePlaintext().build();
+ ManagedChannel c = ManagedChannelBuilder.forTarget(address)
+ .usePlaintext().build();
int maxSize = 1024 * 1024 * 1024;
GraphStoreStub stub;
- stub = GraphStoreGrpc.newStub(c)
- .withMaxInboundMessageSize(maxSize)
+ stub = GraphStoreGrpc.newStub(c).withMaxInboundMessageSize(maxSize)
.withMaxOutboundMessageSize(maxSize);
-
AtomicInteger count = new AtomicInteger();
long start = System.currentTimeMillis();
long id = Thread.currentThread().getId();
@@ -107,7 +68,7 @@ public void onNext(ScanResponse value) {
int edgeSize = value.getEdgeCount();
int vertexSize = value.getVertexCount();
if (request.getScanType().equals(
- ScanPartitionRequest.ScanType.SCAN_VERTEX)) {
+ ScanType.SCAN_VERTEX)) {
count.getAndAdd(vertexSize);
} else {
count.getAndAdd(edgeSize);
@@ -130,38 +91,24 @@ public void onNext(ScanResponse value) {
@Override
public void onError(Throwable t) {
- log.warn("Calling grpc interface encountered an error", t);
latch.countDown();
}
@Override
public void onCompleted() {
long time = System.currentTimeMillis() - start;
- log.info("scan id : {}, complete: {} ,time:{}",
- pId, count.get(), time);
sum.addAndGet(count.get());
latch.countDown();
}
};
- StreamObserver observer =
- stub.scanPartition(ro);
+ StreamObserver observer = stub.scanPartition(ro);
observers.put(id, observer);
builder.setScanRequest(request);
observer.onNext(builder.build());
} catch (Exception e) {
e.printStackTrace();
- }
- }
+ } finally {
- public void getDataSingle() {
- CountDownLatch latch = new CountDownLatch(1);
- new Thread(() -> getData(58, latch, "10.14.139.71:8500")).start();
- try {
- latch.await();
- } catch (InterruptedException e) {
- e.printStackTrace();
}
- log.info("all rows are: {}", sum.get());
}
-
}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanShard.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanShard.java
new file mode 100644
index 0000000000..be362f1a5f
--- /dev/null
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanShard.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cli.cmd;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.hugegraph.pd.cli.cmd.Command;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/10/18
+ **/
+@Slf4j
+public class ScanShard extends Command implements Scan {
+
+ private AtomicInteger sum = new AtomicInteger();
+
+ public ScanShard(String pd) {
+ super(pd);
+ }
+
+ @Override
+ public void action(String[] params) {
+ ExecutorService service = new ThreadPoolExecutor(500, Integer.MAX_VALUE,
+ 0L,
+ TimeUnit.MILLISECONDS,
+ new LinkedBlockingQueue<>());
+ long start = System.currentTimeMillis();
+ String[] addresses = new String[]{"10.14.139.71:8500",
+ "10.14.139.70:8500",
+ "10.14.139.69:8500"};
+ int pSize = 72;
+ int size = pSize * addresses.length;
+ CountDownLatch latch = new CountDownLatch(size);
+ for (int j = 0; j < pSize; j++) {
+ for (int i = 0; i < addresses.length; i++) {
+ String address = addresses[i];
+ int finalJ = j;
+ service.execute(() -> getData(finalJ, latch, address));
+ }
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ long end = System.currentTimeMillis();
+ long cost = end - start;
+ log.info("all rows are: {}, cost: {},avg: {}", sum.get(),
+ cost, sum.get() / cost * 1000);
+ service.shutdownNow();
+ }
+}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanSingleShard.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanSingleShard.java
new file mode 100644
index 0000000000..5eee161a6f
--- /dev/null
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanSingleShard.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 org.apache.hugegraph.store.cli.cmd;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.hugegraph.pd.cli.cmd.Command;
+import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest;
+
+import io.grpc.stub.StreamObserver;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @date 2023/10/18
+ **/
+@Slf4j
+public class ScanSingleShard extends Command implements Scan {
+
+ private volatile boolean closed = false;
+ private AtomicInteger sum = new AtomicInteger();
+ private ConcurrentHashMap>
+ observers = new ConcurrentHashMap<>();
+
+ public ScanSingleShard(String pd) {
+ super(pd);
+ }
+
+ @Override
+ public void action(String[] params) {
+ CountDownLatch latch = new CountDownLatch(1);
+ new Thread(() -> getData(58, latch, "10.14.139.71:8500")).start();
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ log.info("all rows are: {}", sum.get());
+ }
+}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanTable.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanTable.java
new file mode 100644
index 0000000000..0bea59d9ae
--- /dev/null
+++ b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/cmd/ScanTable.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cli.cmd;
+
+import java.util.List;
+
+import org.apache.hugegraph.pd.cli.cmd.Command;
+import org.apache.hugegraph.pd.client.PDClient;
+import org.apache.hugegraph.pd.common.PDException;
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.HgKvEntry;
+import org.apache.hugegraph.store.HgKvIterator;
+import org.apache.hugegraph.store.HgKvStore;
+import org.apache.hugegraph.store.HgStoreClient;
+import org.apache.hugegraph.store.HgStoreSession;
+import org.apache.hugegraph.store.cli.util.HgMetricX;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @date 2023/10/18
+ **/
+@Slf4j
+public class ScanTable extends Command {
+
+ public static final byte[] EMPTY_BYTES = new byte[0];
+ private HgStoreClient storeClient;
+
+ public ScanTable(String pd) {
+ super(pd);
+ storeClient = HgStoreClient.create(config);
+ }
+
+ @Override
+ public void action(String[] params) throws PDException {
+ String graphName = params[0];
+ String tableName = params[1];
+ PDClient pdClient = storeClient.getPdClient();
+ List partitions = pdClient.getPartitions(0, graphName);
+ HgStoreSession session = storeClient.openSession(graphName);
+ int count = 0;
+ byte[] position = null;
+ HgMetricX metricX = HgMetricX.ofStart();
+ for (Metapb.Partition partition : partitions) {
+ while (true) {
+ try (HgKvIterator iterator = session.scanIterator(tableName,
+ (int) (partition.getStartKey()),
+ (int) (partition.getEndKey()),
+ HgKvStore.SCAN_HASHCODE,
+ EMPTY_BYTES)) {
+ if (position != null) {
+ iterator.seek(position);
+ }
+ while (iterator.hasNext()) {
+ iterator.next();
+ count++;
+ if (count % 3000 == 0) {
+ if (iterator.hasNext()) {
+ iterator.next();
+ position = iterator.position();
+ System.out.println("count is " + count);
+ } else {
+ position = null;
+ }
+ break;
+ }
+ }
+ if (!iterator.hasNext()) {
+ position = null;
+ break;
+ }
+ }
+ }
+ }
+ metricX.end();
+ log.info("*************************************************");
+ log.info("************* Scanning Completed **************");
+ log.info("Graph: {}", graphName);
+ log.info("Table: {}", tableName);
+ log.info("Keys: {}", count);
+ log.info("Total: {} seconds.", metricX.past() / 1000);
+ log.info("*************************************************");
+ }
+
+}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/loader/HgThread2DB.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/loader/HgThread2DB.java
deleted file mode 100644
index eab9c195fa..0000000000
--- a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/loader/HgThread2DB.java
+++ /dev/null
@@ -1,568 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hugegraph.store.cli.loader;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.apache.hugegraph.pd.client.PDClient;
-import org.apache.hugegraph.pd.client.PDConfig;
-import org.apache.hugegraph.store.HgKvEntry;
-import org.apache.hugegraph.store.HgKvIterator;
-import org.apache.hugegraph.store.HgOwnerKey;
-import org.apache.hugegraph.store.HgScanQuery;
-import org.apache.hugegraph.store.HgStoreClient;
-import org.apache.hugegraph.store.HgStoreSession;
-import org.apache.hugegraph.store.cli.util.HgCliUtil;
-import org.apache.hugegraph.store.client.grpc.KvCloseableIterator;
-import org.apache.hugegraph.store.client.util.MetricX;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * Use pd, support raft
- * Read files and perform multi-threaded storage processing.
- */
-@Slf4j
-public class HgThread2DB {
-
- /* Total number of tasks in progress and in queue */
- private static final AtomicInteger taskTotal = new AtomicInteger(0);
- private static final AtomicInteger queryTaskTotal = new AtomicInteger(0);
- private static final AtomicLong insertDataCount = new AtomicLong();
- private static final AtomicLong queryCount = new AtomicLong();
- private static final AtomicLong totalQueryCount = new AtomicLong();
- private static final AtomicLong longId = new AtomicLong();
- private static final CountDownLatch countDownLatch = null;
- private static PDClient pdClient;
- private static ThreadPoolExecutor threadPool = null;
- private static ThreadPoolExecutor queryThreadPool = null;
- private static int limitScanBatchCount = 100;
- private static ArrayBlockingQueue listQueue = null;
- private final HgStoreClient storeClient;
- public String graphName = "hugegraphtest";
- volatile long startTime = System.currentTimeMillis();
-
- public HgThread2DB(String pdAddr) {
- int threadCount = Runtime.getRuntime().availableProcessors();
-
- listQueue = new ArrayBlockingQueue>(100000000);
- queryThreadPool = new ThreadPoolExecutor(500, 1000,
- 200, TimeUnit.SECONDS,
- new ArrayBlockingQueue<>(1000));
- threadPool = new ThreadPoolExecutor(threadCount * 2, threadCount * 3,
- 200, TimeUnit.SECONDS,
- new ArrayBlockingQueue<>(threadCount + 100));
- storeClient = HgStoreClient.create(PDConfig.of(pdAddr)
- .setEnableCache(true));
- pdClient = storeClient.getPdClient();
- }
-
- public void setGraphName(String graphName) {
- this.graphName = graphName;
- log.info("setGraphName {}", graphName);
- }
-
- public boolean singlePut(String tableName
- , List keys) throws InterruptedException {
- HgStoreSession session = storeClient.openSession(graphName);
- session.beginTx();
-
- keys.forEach((strKey) -> {
- insertDataCount.getAndIncrement();
- int j = strKey.indexOf("\t");
-// byte[] key = HgCliUtil.toBytes(strKey.substring(0, j));
- HgOwnerKey hgKey = HgCliUtil.toOwnerKey(strKey.substring(0, j), strKey);
- byte[] value = HgCliUtil.toBytes(strKey.substring(j + 1));
- session.put(tableName, hgKey, value);
-
- });
- if (insertDataCount.get() > 10000000) {
- synchronized (insertDataCount) {
- long count = insertDataCount.get();
- insertDataCount.set(0);
- if (count > 10000000) {
- log.info("count : " + count + " qps : " +
- count * 1000 / (System.currentTimeMillis() - startTime)
- + " threadCount : " + taskTotal);
- startTime = System.currentTimeMillis();
- }
- }
- }
- if (!keys.isEmpty()) {
- if (session.isTx()) {
- session.commit();
- } else {
- session.rollback();
- }
- }
-
- return true;
- }
-
- public boolean singlePut(String tableName) throws InterruptedException {
- HgStoreSession session = storeClient.openSession(graphName);
- session.beginTx();
-
- int maxlist = 100;
-
- for (int y = 0; y < maxlist; y++) {
- insertDataCount.getAndIncrement();
- String strLine = getLong() + getLong() + getLong() + getLong();
- HgOwnerKey hgKey = HgCliUtil.toOwnerKey(strLine, strLine);
- byte[] value = HgCliUtil.toBytes(strLine);
- session.put(tableName, hgKey, value);
- }
-
- if (insertDataCount.get() > 10000000) {
- synchronized (insertDataCount) {
- long count = insertDataCount.get();
- insertDataCount.set(0);
- if (count > 10000000) {
- log.info("count : " + count + " qps : " +
- count * 1000 / (System.currentTimeMillis() - startTime)
- + " threadCount : " + taskTotal);
- startTime = System.currentTimeMillis();
- }
- }
- }
-
- if (session.isTx()) {
- session.commit();
- } else {
- session.rollback();
- }
-
- return true;
- }
-
- public boolean testOrder(String input) {
- String tableName = "hugegraph02";
- HgStoreSession session = storeClient.openSession(graphName);
- session.beginTx();
- int loop = Integer.parseInt(input);
- if (loop == 0) {
- loop = 2000;
- }
- for (int i = 0; i < loop; i++) {
- long startTime = System.currentTimeMillis();
- HgOwnerKey hgOwnerKey =
- HgCliUtil.toOwnerKey(startTime + "owner:" + i, startTime + "k:" + i);
- session.put(tableName, hgOwnerKey, HgCliUtil.toBytes(i));
- }
-
- if (session.isTx()) {
- session.commit();
- } else {
- session.rollback();
- }
-
- try {
- HgKvIterator iterable = session.scanIterator(tableName);
- int x = 0;
- while (iterable.hasNext()) {
- HgKvEntry entry = iterable.next();
- x++;
- }
- log.info("x={}", x);
- } catch (Exception e) {
- log.error("query error, message: {}", e.getMessage());
- }
-
- return true;
- }
-
- /**
- * Multithreaded file reading and storage into database
- *
- * @throws IOException
- * @throws InterruptedException
- */
- public void startMultiprocessInsert(String filepath) throws IOException {
- log.info("--- start startMultiprocessInsert---");
- startTime = System.currentTimeMillis();
- File readfile = new File(filepath);
- MetricX metrics = null;
- long dataCount = 0;
- if (readfile.exists()) {
- // Read file
- InputStreamReader isr = new InputStreamReader(new FileInputStream(readfile),
- StandardCharsets.UTF_8);
- BufferedReader reader = new BufferedReader(isr);
-
- String strLine = null;
- String tableName = HgCliUtil.TABLE_NAME;
- // Accumulate to how many threads before executing thread storage, 100,000
- int maxlist = 100000;
- List keys = new ArrayList<>(maxlist);
- metrics = MetricX.ofStart();
- try {
- while ((strLine = reader.readLine()) != null) {
- keys.add(strLine);
- dataCount++;
-
- // Read 10000 pieces of data from the file, start a thread for data storage.
- if (dataCount % maxlist == 0) {
- List finalKeys = keys;
- Runnable task = () -> {
- try {
- if (!finalKeys.isEmpty()) {
- boolean ret = singlePut(tableName, finalKeys);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- taskTotal.decrementAndGet();
- synchronized (taskTotal) {
- taskTotal.notifyAll();
- }
- };
- taskTotal.getAndIncrement();
- threadPool.execute(task);
-
- while (taskTotal.get() > 100) {
- synchronized (taskTotal) {
- taskTotal.wait();
- }
- }
- // keys.remove(0);
- keys = new ArrayList<>(maxlist);
- }
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
-
- isr.close();
- reader.close();
- // Move the remaining items into storage
- if (!keys.isEmpty()) {
- List finalKeys1 = keys;
- Runnable task = () -> {
- try {
- boolean ret = singlePut(tableName, finalKeys1);
- } catch (Exception e) {
- e.printStackTrace();
- }
- taskTotal.decrementAndGet();
- synchronized (taskTotal) {
- taskTotal.notifyAll();
- }
- };
- threadPool.execute(task);
- taskTotal.getAndIncrement();
- }
- while (taskTotal.get() > 0) {
- synchronized (taskTotal) {
- try {
- taskTotal.wait(1000);
- if (taskTotal.get() > 0) {
- System.out.println("wait thread exit " + taskTotal.get());
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- threadPool.shutdown();
-
- } else {
- System.out.println("Sample file does not exist: " + filepath);
- }
- metrics.end();
- log.info("*************************************************");
- log.info(" Main process execution time: " + metrics.past() / 1000 + " seconds, total executed: " + dataCount + " items");
- log.info("*************************************************");
- System.out.println(" Main process execution time " + metrics.past() / 1000 + " seconds");
- System.out.println("-----Main process execution ends---------");
- }
-
- /**
- * Multithreaded file reading and storage into database
- *
- * @throws IOException
- * @throws InterruptedException
- */
- public void autoMultiprocessInsert() throws IOException {
- log.info("--- start autoMultiprocessInsert---");
- startTime = System.currentTimeMillis();
-
- MetricX metrics = null;
- long dataCount = 0;
-
- String strLine = null;
- String tableName = HgCliUtil.TABLE_NAME;
- // Accumulate to how many to execute thread storage, 100,000
- int maxlist = 100000;
- List keys = new ArrayList<>(maxlist);
- for (int x = 0; x < 10000000; x++) {
- metrics = MetricX.ofStart();
- try {
- Runnable task = () -> {
- try {
- boolean ret = singlePut(tableName);
- } catch (Exception e) {
- e.printStackTrace();
- }
- taskTotal.decrementAndGet();
- synchronized (taskTotal) {
- taskTotal.notifyAll();
- }
- };
- taskTotal.getAndIncrement();
- threadPool.execute(task);
-
- while (taskTotal.get() > 100) {
- synchronized (taskTotal) {
- taskTotal.wait();
- }
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
-
- while (taskTotal.get() > 0) {
- synchronized (taskTotal) {
- try {
- taskTotal.wait(1000);
- if (taskTotal.get() > 0) {
- System.out.println("wait thread exit " + taskTotal.get());
- }
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
- threadPool.shutdown();
-
- metrics.end();
- log.info("*************************************************");
- log.info(" Main process execution time: " + metrics.past() / 1000 + " seconds, total executed: " + dataCount + " items");
- log.info("*************************************************");
- System.out.println(" Main process execution time " + metrics.past() / 1000 + " seconds");
- System.out.println("-----Main process ends---------");
- }
-
- public String getLong() {
- // If needed longer or more redundant space, just use time * 10^n
- //Currently guaranteed to generate 10000 unique items in 1 millisecond.
- return String.format("%019x", longId.getAndIncrement());
- }
-
- /**
- * Execute the query, and put the results of the query into the queue as the point for the next iteration.
- */
- private void queryAnd2Queue() {
- try {
- HgStoreSession session = storeClient.openSession(graphName);
- HashSet hashSet = new HashSet<>();
- while (!listQueue.isEmpty()) {
-
- log.info(" ====== start scanBatch2 count:{} list:{}=============",
- queryThreadPool.getActiveCount(), listQueue.size());
- List keys = (List) listQueue.take();
- List newQueryList = new ArrayList<>();
-
- KvCloseableIterator> iterators =
- session.scanBatch2(
- HgScanQuery.prefixIteratorOf(HgCliUtil.TABLE_NAME, keys.iterator())
- );
-
- while (iterators.hasNext()) {
- HgKvIterator iterator = iterators.next();
- int insertQueueCount = 0;
- while (iterator.hasNext()) {
- HgKvEntry entry = iterator.next();
- String newPoint = HgCliUtil.toStr(entry.value());
-// log.info("query_key =" + newPoint);
- // Statistical query times
- if (!newPoint.isEmpty() && hashSet.add(newPoint)) {
- queryCount.getAndIncrement();
- totalQueryCount.getAndIncrement();
-
- HgOwnerKey hgKey = HgCliUtil.toOwnerKey(newPoint, newPoint);
- newQueryList.add(hgKey);
-
- if (queryCount.get() > 1000000) {
- synchronized (queryCount) {
- long count = queryCount.get();
- queryCount.set(0);
- if (count > 1000000) {
- log.info("count : " + count + " qps : " + count * 1000 /
- (System.currentTimeMillis() -
- startTime)
- + " threadCount : " +
- queryThreadPool.getActiveCount() + " queueSize:"
- + listQueue.size());
- startTime = System.currentTimeMillis();
- }
- }
- }
- // After reaching 10,000 points, query once.
- if (newQueryList.size() > 10000 && listQueue.size() < 10000) {
- listQueue.put(newQueryList);
- insertQueueCount++;
- newQueryList = new ArrayList<>();
- if (insertQueueCount > 2) {
- break;
- }
- }
- }
- }
- }
- // If a query is less than 10,000, submit a separate query to ensure that all results can execute the query.
- if (!newQueryList.isEmpty() && listQueue.size() < 1000) {
- listQueue.put(newQueryList);
- }
-
- iterators.close();
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("============= thread done ==============");
- countDownLatch.countDown();
- }
-
- /**
- * Multithreaded query
- *
- * @param point Starting query point, subsequent queries will use the value obtained from this point as the next query condition for iteration.
- * @param scanCount The number of threads allowed to start
- * @throws IOException
- * @throws InterruptedException
- */
- public void startMultiprocessQuery(String point, String scanCount) throws IOException,
- InterruptedException {
- log.info("--- start startMultiprocessQuery---");
- startTime = System.currentTimeMillis();
- MetricX metrics = MetricX.ofStart();
- limitScanBatchCount = Integer.parseInt(scanCount);
-
- CountDownLatch latch = new CountDownLatch(limitScanBatchCount);
- HgStoreSession session = storeClient.openSession(graphName);
-
- final AtomicLong[] counter = {new AtomicLong()};
- final long[] start = {System.currentTimeMillis()};
-
- LinkedBlockingQueue[] queue = new LinkedBlockingQueue[limitScanBatchCount];
- for (int i = 0; i < limitScanBatchCount; i++) {
- queue[i] = new LinkedBlockingQueue();
- }
- List strKey = Arrays.asList(
- "20727483", "50329304", "26199460", "1177521", "27960125",
- "30440025", "15833920", "15015183", "33153097", "21250581");
- strKey.forEach(key -> {
- log.info("newkey:{}", key);
- HgOwnerKey hgKey = HgCliUtil.toOwnerKey(key, key);
- queue[0].add(hgKey);
- });
-
- for (int i = 0; i < limitScanBatchCount; i++) {
- int finalI = i;
- KvCloseableIterator> iterators =
- session.scanBatch2(
- HgScanQuery.prefixIteratorOf(HgCliUtil.TABLE_NAME,
- new Iterator() {
- HgOwnerKey current = null;
-
- @Override
- public boolean hasNext() {
- while (current == null) {
- try {
- current =
- (HgOwnerKey) queue[finalI].poll(
- 1,
- TimeUnit.SECONDS);
- } catch (
- InterruptedException e) {
- //
- }
- }
- if (current == null) {
- log.warn(
- "===== current is " +
- "null ==========");
- }
- return current != null;
- }
-
- @Override
- public HgOwnerKey next() {
- return current;
- }
- })
- );
-
- new Thread(() -> {
- while (iterators.hasNext()) {
- HgKvIterator iterator = iterators.next();
- long c = 0;
- while (iterator.hasNext()) {
- String newPoint = HgCliUtil.toStr(iterator.next().value());
- HgOwnerKey newHgKey = HgCliUtil.toOwnerKey(newPoint, newPoint);
- if (queue[(int) (c % limitScanBatchCount)].size() < 1000000) {
- queue[(int) (c % limitScanBatchCount)].add(newHgKey);
- }
- c++;
- }
- if (counter[0].addAndGet(c) > 1000000) {
- synchronized (counter) {
- if (counter[0].get() > 10000000) {
- log.info("count {}, qps {}", counter[0].get(),
- counter[0].get() * 1000 /
- (System.currentTimeMillis() - start[0]));
- start[0] = System.currentTimeMillis();
- counter[0].set(0);
- }
- }
- }
- }
- }, "client query thread:" + i).start();
- log.info("===== read thread exit ==========");
- }
- latch.await();
-
- metrics.end();
- log.info("*************************************************");
- log.info(" Main process execution time: " + metrics.past() / 1000 + " seconds; Queries: " + totalQueryCount.get()
- + "times, qps:" + totalQueryCount.get() * 1000 / metrics.past());
- log.info("*************************************************");
- System.out.println("-----Main process ends---------");
- }
-
-}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/HgStoreCommitter.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/HgStoreCommitter.java
deleted file mode 100644
index cf31e779f9..0000000000
--- a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/HgStoreCommitter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hugegraph.store.cli.scan;
-
-import org.apache.hugegraph.store.HgOwnerKey;
-import org.apache.hugegraph.store.HgSessionManager;
-import org.apache.hugegraph.store.HgStoreSession;
-import org.apache.hugegraph.store.cli.util.HgCliUtil;
-import org.apache.hugegraph.store.client.HgStoreNodeManager;
-
-/**
- * 2022/2/28
- */
-public class HgStoreCommitter {
-
- protected final static HgStoreNodeManager nodeManager = HgStoreNodeManager.getInstance();
-
- private final String graph;
-
- private HgStoreCommitter(String graph) {
- this.graph = graph;
- }
-
- public static HgStoreCommitter of(String graph) {
- return new HgStoreCommitter(graph);
- }
-
- protected HgStoreSession getStoreSession() {
- return HgSessionManager.getInstance().openSession(this.graph);
- }
-
- protected HgStoreSession getStoreSession(String graphName) {
- return HgSessionManager.getInstance().openSession(graphName);
- }
-
- public void put(String tableName, int amount) {
- //*************** Put Benchmark **************//*
- String keyPrefix = "PUT-BENCHMARK";
- HgStoreSession session = getStoreSession();
-
- int length = String.valueOf(amount).length();
-
- session.beginTx();
-
- long start = System.currentTimeMillis();
- for (int i = 0; i < amount; i++) {
- HgOwnerKey key = HgCliUtil.toOwnerKey(
- keyPrefix + "-" + HgCliUtil.padLeftZeros(String.valueOf(i), length));
- byte[] value = HgCliUtil.toBytes(keyPrefix + "-V-" + i);
-
- session.put(tableName, key, value);
-
- if ((i + 1) % 100_000 == 0) {
- HgCliUtil.println("---------- " + (i + 1) + " --------");
- HgCliUtil.println(
- "Preparing took: " + (System.currentTimeMillis() - start) + " ms.");
- session.commit();
- HgCliUtil.println(
- "Committing took: " + (System.currentTimeMillis() - start) + " ms.");
- start = System.currentTimeMillis();
- session.beginTx();
- }
- }
-
- if (session.isTx()) {
- session.commit();
- }
-
- }
-}
diff --git a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/HgStoreScanner.java b/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/HgStoreScanner.java
deleted file mode 100644
index bbc40ca867..0000000000
--- a/hugegraph-store/hg-store-cli/src/main/java/org/apache/hugegraph/store/cli/scan/HgStoreScanner.java
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hugegraph.store.cli.scan;
-
-import java.util.Arrays;
-import java.util.List;
-
-import org.apache.hugegraph.pd.client.PDClient;
-import org.apache.hugegraph.pd.common.PDException;
-import org.apache.hugegraph.pd.grpc.Metapb;
-import org.apache.hugegraph.store.HgKvEntry;
-import org.apache.hugegraph.store.HgKvIterator;
-import org.apache.hugegraph.store.HgKvStore;
-import org.apache.hugegraph.store.HgScanQuery;
-import org.apache.hugegraph.store.HgSessionManager;
-import org.apache.hugegraph.store.HgStoreClient;
-import org.apache.hugegraph.store.HgStoreSession;
-import org.apache.hugegraph.store.cli.util.HgCliUtil;
-import org.apache.hugegraph.store.cli.util.HgMetricX;
-import org.apache.hugegraph.store.client.grpc.KvCloseableIterator;
-import org.apache.hugegraph.store.client.util.HgStoreClientConfig;
-import org.apache.hugegraph.store.client.util.MetricX;
-
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * 2022/2/14
- */
-@Slf4j
-public class HgStoreScanner {
-
- public static final byte[] EMPTY_BYTES = new byte[0];
- private final HgStoreClient storeClient;
- private final String graphName;
- private long modNumber = 1_000_000;
- private int max = 10_000_000;
-
- private HgStoreScanner(HgStoreClient storeClient, String graph) {
- this.storeClient = storeClient;
- this.graphName = graph;
- }
-
- public static HgStoreScanner of(HgStoreClient storeClient, String graph) {
- return new HgStoreScanner(storeClient, graph);
- }
-
- public long getModNumber() {
- return modNumber;
- }
-
- public void setModNumber(int modNumber) {
- if (modNumber <= 0) {
- return;
- }
- this.modNumber = modNumber;
- }
-
- public int getMax() {
- return max;
- }
-
- public void setMax(int max) {
- if (modNumber <= 0) {
- return;
- }
- this.max = max;
- }
-
- protected HgStoreSession getStoreSession() {
- return HgSessionManager.getInstance().openSession(this.graphName);
- }
-
- protected HgStoreSession getStoreSession(String graphName) {
- return HgSessionManager.getInstance().openSession(graphName);
- }
-
- public void scanTable(String tableName) {
- log.info("Starting scan table [{}] of graph [{}] ...", tableName, graphName);
- HgMetricX hgMetricX = HgMetricX.ofStart();
- HgStoreSession session = getStoreSession();
- int count = 0;
- KvCloseableIterator> iterator =
- session.scanBatch2(HgScanQuery.tableOf(tableName));
-
- long start = System.currentTimeMillis();
- while (iterator.hasNext()) {
- HgKvIterator iterator2 = iterator.next();
- while (iterator2.hasNext()) {
-
- count++;
- iterator2.next();
- if (count % (modNumber) == 0) {
- log.info("Scanning keys: " + count + " time is " + modNumber * 1000
- /
- (System.currentTimeMillis() -
- start));
- start = System.currentTimeMillis();
- }
- if (count == max) {
- break;
- }
-
- }
- }
- iterator.close();
-
- hgMetricX.end();
- log.info("*************************************************");
- log.info("************* Scanning Completed **************");
- log.info("Graph: {}", graphName);
- log.info("Table: {}", tableName);
- log.info("Keys: {}", count);
- log.info("Max: {}", max);
- log.info("Waiting: {} seconds.", MetricX.getIteratorWait() / 1000);
- log.info("Total: {} seconds.", hgMetricX.past() / 1000);
- log.info("Iterator: [{}]", iterator.getClass().getSimpleName());
- log.info("Page: {}", HgStoreClientConfig.of().getNetKvScannerPageSize());
- log.info("*************************************************");
- }
-
- public void scanHash() {
-
- String tableName = "g+i";
- HgMetricX hgMetricX = HgMetricX.ofStart();
- String graphName = "/DEFAULT/graphs/hugegraph1/";
- HgStoreSession session = getStoreSession(graphName);
- int count = 0;
- String query =
- "{\"conditions\":[{\"cls\":\"S\",\"el\":{\"key\":\"ID\",\"relation\":\"SCAN\"," +
- "\"value\"" +
- ":{\"start\":\"61180\",\"end\":\"63365\",\"length\":0}}}]," +
- "\"optimizedType\":\"NONE\",\"ids\":[]," +
- "\"mustSortByInput\":true,\"resultType\":\"EDGE\",\"offset\":0," +
- "\"actualOffset\":0,\"actualStoreOffset\":" +
- "0,\"limit\":9223372036854775807,\"capacity\":-1,\"showHidden\":false," +
- "\"showDeleting\":false," +
- "\"showExpired\":false,\"olap\":false,\"withProperties\":false,\"olapPks\":[]}";
- //HgKvIterator iterator = session.scanIterator(tableName,0,715827883,
- // HgKvStore.SCAN_ANY,null);
-
- //HgKvIterator iterator = session.scanIterator(tableName,61180,63365, 348, null);
- //HgKvIterator iterator = session.scanIterator(tableName,0,65535, 348, null);
- HgKvIterator iterator = session.scanIterator(tableName);
- while (iterator.hasNext()) {
-
- count++;
- //iterator.next();
- // if (count % (modNumber) == 0) {
- // log.info("Scanning keys: " + count);
- HgCliUtil.println(Arrays.toString(iterator.next().key()));
- // }
- if (count == max) {
- break;
- }
-
- }
-
- hgMetricX.end();
- log.info("*************************************************");
- log.info("************* Scanning Completed **************");
- log.info("Graph: {}", this.graphName);
- log.info("Table: {}", tableName);
- log.info("Keys: {}", count);
- log.info("Max: {}", max);
- log.info("Waiting: {} seconds.", MetricX.getIteratorWait() / 1000);
- log.info("Total: {} seconds.", hgMetricX.past() / 1000);
- log.info("Iterator: [{}]", iterator.getClass().getSimpleName());
- log.info("Page: {}", HgStoreClientConfig.of().getNetKvScannerPageSize());
- log.info("*************************************************");
- }
-
- public void scanTable2(String tableName) throws PDException {
- // java -jar hg-store-cli-3.6.0-SNAPSHOT.jar -scan 10.45.30.212:8989 "DEFAULT/case_112/g"
- // g+ie
- PDClient pdClient = storeClient.getPdClient();
- List partitions = pdClient.getPartitions(0, graphName);
- HgStoreSession session = storeClient.openSession(graphName);
- int count = 0;
- byte[] position = null;
- HgMetricX hgMetricX = HgMetricX.ofStart();
- for (Metapb.Partition partition : partitions) {
- while (true) {
- try (HgKvIterator iterator = session.scanIterator(tableName,
- (int) (partition.getStartKey()),
- (int) (partition.getEndKey()),
- HgKvStore.SCAN_HASHCODE,
- EMPTY_BYTES)) {
- if (position != null) {
- iterator.seek(position);
- }
- while (iterator.hasNext()) {
- iterator.next();
- count++;
- if (count % 3000 == 0) {
- if (iterator.hasNext()) {
- iterator.next();
- position = iterator.position();
- System.out.println("count is " + count);
- } else {
- position = null;
- }
- break;
- }
- }
- if (!iterator.hasNext()) {
- position = null;
- break;
- }
- }
- }
- }
- hgMetricX.end();
- log.info("*************************************************");
- log.info("************* Scanning Completed **************");
- log.info("Graph: {}", graphName);
- log.info("Table: {}", tableName);
- log.info("Keys: {}", count);
- log.info("Total: {} seconds.", hgMetricX.past() / 1000);
- log.info("*************************************************");
- }
-
-}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvIterator.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvIterator.java
index 38c8b0039b..b78d154cb7 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvIterator.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvIterator.java
@@ -25,11 +25,16 @@
*/
public interface HgKvIterator extends Iterator, HgSeekAble, Closeable {
- byte[] key();
+ default byte[] key() {
+ return new byte[0];
+ }
- byte[] value();
+ default byte[] value() {
+ return new byte[0];
+ }
@Override
- void close();
+ default void close() {
+ }
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvStore.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvStore.java
index f04e743f32..dcce95ba1e 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvStore.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgKvStore.java
@@ -19,8 +19,12 @@
import java.util.List;
+import org.apache.hugegraph.HugeGraphSupplier;
+import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.store.client.grpc.KvCloseableIterator;
import org.apache.hugegraph.store.grpc.stream.ScanStreamReq;
+import org.apache.hugegraph.store.query.StoreQueryParam;
+import org.apache.hugegraph.structure.BaseElement;
/**
* @version 0.2.0
@@ -98,7 +102,8 @@ HgKvIterator scanIterator(String table, int codeFrom, int codeTo, int
HgKvIterator scanIterator(ScanStreamReq.Builder scanReqBuilder);
- long count(String table);
+ List> query(StoreQueryParam query, HugeGraphSupplier supplier) throws
+ PDException;
boolean truncate();
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSeekAble.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSeekAble.java
index fe6a580a1c..b7b2ddd985 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSeekAble.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSeekAble.java
@@ -22,7 +22,11 @@
*/
public interface HgSeekAble {
- byte[] position();
+ default byte[] position() {
+ return null;
+ }
+
+ default void seek(byte[] position) {
+ }
- void seek(byte[] position);
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionConfig.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionConfig.java
new file mode 100644
index 0000000000..fe779ff45e
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionConfig.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store;
+
+
+import lombok.Data;
+
+@Data
+public class HgSessionConfig {
+ private long queryPushDownTimeout = 1800_000;
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionProvider.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionProvider.java
index 7049c27b01..8e97aab7b2 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionProvider.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgSessionProvider.java
@@ -26,4 +26,6 @@
public interface HgSessionProvider {
HgStoreSession createSession(String graphName);
+
+ HgStoreSession createSession(String graphName, HgSessionConfig sessionConfig);
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgStoreClient.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgStoreClient.java
index 0f8ebb929f..17ab82c888 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgStoreClient.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/HgStoreClient.java
@@ -37,6 +37,8 @@ public final class HgStoreClient {
private final HgSessionProvider sessionProvider;
private PDClient pdClient;
+ private HgSessionConfig sessionConfig;
+
public HgStoreClient() {
this.sessionProvider = new HgStoreSessionProvider();
}
@@ -69,6 +71,10 @@ public void setPDConfig(PDConfig config) {
setPdClient(pdClient);
}
+ public void setSessionConfig(HgSessionConfig sessionConfig) {
+ this.sessionConfig = sessionConfig;
+ }
+
/**
* Retrieve or create a HgStoreSession.
*
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgNodePartitionerBuilder.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgNodePartitionerBuilder.java
index 4bb0705b74..4b29abfeef 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgNodePartitionerBuilder.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgNodePartitionerBuilder.java
@@ -67,4 +67,5 @@ public void setPartitions(Set partitions) {
this.partitions = partitions;
}
+
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitioner.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitioner.java
index d540f68aa7..13ff874fbb 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitioner.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitioner.java
@@ -17,6 +17,9 @@
package org.apache.hugegraph.store.client;
+import java.util.List;
+
+import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.store.client.util.HgStoreClientConst;
/**
@@ -63,4 +66,17 @@ default int partition(HgNodePartitionerBuilder builder, String graphName, int pa
, HgStoreClientConst.ALL_PARTITION_OWNER
, HgStoreClientConst.ALL_PARTITION_OWNER);
}
+
+ default String partition(String graphName, byte[] startKey) throws PDException {
+ return null;
+ }
+
+ default String partition(String graphName, int code) throws PDException {
+ return null;
+ }
+
+ default List getStores(String graphName) throws PDException {
+ return null;
+ }
+
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitionerImpl.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitionerImpl.java
index 606b279e8d..f34acf54de 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitionerImpl.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreNodePartitionerImpl.java
@@ -20,6 +20,7 @@
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
+import java.util.stream.Collectors;
import org.apache.hugegraph.pd.client.PDClient;
import org.apache.hugegraph.pd.common.KVPair;
@@ -140,6 +141,18 @@ public int partition(HgNodePartitionerBuilder builder, String graphName,
return 0;
}
+ @Override
+ public String partition(String graphName, byte[] startKey) throws PDException {
+ var shard = pdClient.getPartition(graphName, startKey).getValue();
+ return pdClient.getStore(shard.getStoreId()).getAddress();
+ }
+
+ @Override
+ public String partition(String graphName, int code) throws PDException {
+ var shard = pdClient.getPartitionByCode(graphName, code).getValue();
+ return pdClient.getStore(shard.getStoreId()).getAddress();
+ }
+
/**
* Query hgstore information
*
@@ -196,4 +209,17 @@ public Metapb.Graph delGraph(String graphName) {
public void setNodeManager(HgStoreNodeManager nodeManager) {
this.nodeManager = nodeManager;
}
+
+ @Override
+ public List getStores(String graphName) throws PDException {
+ var list = pdClient.getCache().getLeaderStoreAddresses();
+ if (list.isEmpty()) {
+ // 缓存正在被清空的case,获取原始的
+ return pdClient.getActiveStores(graphName).stream()
+ .map(Metapb.Store::getAddress)
+ .collect(Collectors.toList());
+ }
+ return list;
+ }
}
+
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreSessionProvider.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreSessionProvider.java
index 37fa51cb4a..c4dcc70675 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreSessionProvider.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/HgStoreSessionProvider.java
@@ -19,6 +19,7 @@
import javax.annotation.concurrent.ThreadSafe;
+import org.apache.hugegraph.store.HgSessionConfig;
import org.apache.hugegraph.store.HgSessionProvider;
import org.apache.hugegraph.store.HgStoreSession;
@@ -34,4 +35,9 @@ public class HgStoreSessionProvider implements HgSessionProvider {
public HgStoreSession createSession(String graphName) {
return this.sessionFactory.createStoreSession(graphName);
}
+
+ @Override
+ public HgStoreSession createSession(String graphName, HgSessionConfig sessionConfig) {
+ return this.sessionFactory.createStoreSession(graphName, sessionConfig);
+ }
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/MultiNodeSessionFactory.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/MultiNodeSessionFactory.java
index ff7cde0db8..e1e41214a3 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/MultiNodeSessionFactory.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/MultiNodeSessionFactory.java
@@ -19,6 +19,7 @@
import javax.annotation.concurrent.ThreadSafe;
+import org.apache.hugegraph.store.HgSessionConfig;
import org.apache.hugegraph.store.HgStoreSession;
/**
@@ -42,13 +43,21 @@ static MultiNodeSessionFactory getInstance() {
}
HgStoreSession createStoreSession(String graphName) {
- return buildProxy(graphName);
+ return buildProxy(graphName, null);
}
- private HgStoreSession buildProxy(String graphName) {
+ HgStoreSession createStoreSession(String graphName, HgSessionConfig config) {
+ return buildProxy(graphName, config);
+ }
+
+ private HgStoreSession buildProxy(String graphName, HgSessionConfig config) {
//return new MultiNodeSessionProxy(graphName, nodeManager, storeNodeDispatcher);
//return new NodePartitionSessionProxy(graphName,nodeManager);
//return new NodeRetrySessionProxy(graphName,nodeManager);
- return new NodeTxSessionProxy(graphName, nodeManager);
+ if (config == null) {
+ return new NodeTxSessionProxy(graphName, nodeManager);
+ }
+
+ return new NodeTxSessionProxy(graphName, nodeManager, config);
}
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxSessionProxy.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxSessionProxy.java
index c5a6e5c4a4..30b7617e57 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxSessionProxy.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxSessionProxy.java
@@ -39,20 +39,26 @@
import javax.annotation.concurrent.NotThreadSafe;
+import org.apache.hugegraph.HugeGraphSupplier;
+import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.store.HgKvEntry;
import org.apache.hugegraph.store.HgKvIterator;
import org.apache.hugegraph.store.HgKvOrderedIterator;
import org.apache.hugegraph.store.HgOwnerKey;
import org.apache.hugegraph.store.HgScanQuery;
+import org.apache.hugegraph.store.HgSessionConfig;
import org.apache.hugegraph.store.HgStoreSession;
import org.apache.hugegraph.store.client.grpc.KvBatchScanner;
import org.apache.hugegraph.store.client.grpc.KvCloseableIterator;
+import org.apache.hugegraph.store.client.query.QueryExecutor;
import org.apache.hugegraph.store.client.util.HgAssert;
import org.apache.hugegraph.store.client.util.HgStoreClientConst;
import org.apache.hugegraph.store.client.util.HgStoreClientUtil;
import org.apache.hugegraph.store.grpc.stream.ScanStreamReq.Builder;
+import org.apache.hugegraph.store.query.StoreQueryParam;
import org.apache.hugegraph.store.term.HgPair;
import org.apache.hugegraph.store.term.HgTriple;
+import org.apache.hugegraph.structure.BaseElement;
import lombok.extern.slf4j.Slf4j;
@@ -65,12 +71,13 @@
@NotThreadSafe
class NodeTxSessionProxy implements HgStoreSession {
+ private final HgSessionConfig sessionConfig;
private final HgStoreNodeManager nodeManager;
private final HgStoreNodePartitioner nodePartitioner;
private final String graphName;
private final NodeTxExecutor txExecutor;
- NodeTxSessionProxy(String graphName, HgStoreNodeManager nodeManager) {
+ public NodeTxSessionProxy(String graphName, HgStoreNodeManager nodeManager) {
this.nodeManager = nodeManager;
this.graphName = graphName;
this.nodePartitioner = this.nodeManager.getNodePartitioner();
@@ -78,6 +85,19 @@ class NodeTxSessionProxy implements HgStoreSession {
isFalse(this.nodePartitioner == null,
"Failed to retrieve the node-partitioner from node-manager.");
+ sessionConfig = new HgSessionConfig();
+ }
+
+ public NodeTxSessionProxy(String graphName, HgStoreNodeManager nodeManager,
+ HgSessionConfig config) {
+ this.nodeManager = nodeManager;
+ this.graphName = graphName;
+ this.nodePartitioner = this.nodeManager.getNodePartitioner();
+ this.txExecutor = NodeTxExecutor.graphOf(this.graphName, this);
+
+ isFalse(this.nodePartitioner == null,
+ "Failed to retrieve the node-partitioner from node-manager.");
+ sessionConfig = config;
}
@Override
@@ -503,17 +523,6 @@ public HgKvIterator scanIterator(Builder scanReqBuilder) {
return this.toHgKvIteratorProxy(iterators, scanReqBuilder.getLimit());
}
- @Override
- public long count(String table) {
- return this.toNodeTkvList(table)
- .parallelStream()
- .map(
- e -> this.getStoreNode(e.getNodeId()).openSession(this.graphName)
- .count(e.getTable())
- )
- .collect(Collectors.summingLong(l -> l));
- }
-
@Override
public List> scanBatch(HgScanQuery scanQuery) {
HgAssert.isArgumentNotNull(scanQuery, "scanQuery");
@@ -884,4 +893,19 @@ private List> nodeTkv2Node(Collection node
return hgPairs;
}
+ @Override
+ public List> query(StoreQueryParam query,
+ HugeGraphSupplier supplier) throws
+ PDException {
+ long current = System.nanoTime();
+ QueryExecutor planner = new QueryExecutor(this.nodePartitioner, supplier,
+ this.sessionConfig.getQueryPushDownTimeout());
+ query.checkQuery();
+ var iteratorList = planner.getIterators(query);
+ log.debug("[time_stat] query id: {}, size {}, get Iterator cost: {} ms",
+ query.getQueryId(),
+ iteratorList.size(),
+ (System.nanoTime() - current) * 1.0 / 1000_000);
+ return iteratorList;
+ }
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/SequencedIterator.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/SequencedIterator.java
index aca7bb70b3..aae66b61d7 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/SequencedIterator.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/SequencedIterator.java
@@ -51,7 +51,7 @@ public class SequencedIterator implements HgKvIterator {
SequencedIterator(List iterators, long limit) {
Collections.sort(iterators);
this.queue = new LinkedList(iterators);
- this.limit = limit <= 0 ? Integer.MAX_VALUE : limit;
+ this.limit = limit <= 0 ? Long.MAX_VALUE : limit;
}
private HgKvOrderedIterator getIterator() {
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/AbstractGrpcClient.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/AbstractGrpcClient.java
index 20aa54b39a..92229079f2 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/AbstractGrpcClient.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/AbstractGrpcClient.java
@@ -37,9 +37,9 @@
public abstract class AbstractGrpcClient {
- private static Map channels = new ConcurrentHashMap<>();
+ protected static Map channels = new ConcurrentHashMap<>();
private static int n = 5;
- private static int concurrency = 1 << n;
+ protected static int concurrency = 1 << n;
private static AtomicLong counter = new AtomicLong(0);
private static long limit = Long.MAX_VALUE >> 1;
private static HgStoreClientConfig config = HgStoreClientConfig.of();
@@ -69,7 +69,7 @@ public ManagedChannel[] getChannels(String target) {
int fi = i;
executor.execute(() -> {
try {
- value[fi] = getManagedChannel(target);
+ value[fi] = createChannel(target);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
@@ -162,14 +162,14 @@ public AbstractAsyncStub getAsyncStub(String target) {
}
- private AbstractStub setStubOption(AbstractStub value) {
+ protected AbstractStub setStubOption(AbstractStub value) {
return value.withMaxInboundMessageSize(
config.getGrpcMaxInboundMessageSize())
.withMaxOutboundMessageSize(
config.getGrpcMaxOutboundMessageSize());
}
- private ManagedChannel getManagedChannel(String target) {
+ protected ManagedChannel createChannel(String target) {
return ManagedChannelBuilder.forTarget(target).usePlaintext().build();
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreNodeSessionImpl.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreNodeSessionImpl.java
index 73e95515c7..18f7464b88 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreNodeSessionImpl.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreNodeSessionImpl.java
@@ -20,12 +20,13 @@
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.concurrent.NotThreadSafe;
+import org.apache.hugegraph.HugeGraphSupplier;
+import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.store.HgKvEntry;
import org.apache.hugegraph.store.HgKvIterator;
import org.apache.hugegraph.store.HgKvStore;
@@ -39,6 +40,7 @@
import org.apache.hugegraph.store.client.util.HgStoreClientConst;
import org.apache.hugegraph.store.client.util.HgStoreClientUtil;
import org.apache.hugegraph.store.client.util.HgUuid;
+import org.apache.hugegraph.store.constant.HugeServerTables;
import org.apache.hugegraph.store.grpc.common.GraphMethod;
import org.apache.hugegraph.store.grpc.common.Key;
import org.apache.hugegraph.store.grpc.common.OpType;
@@ -46,6 +48,8 @@
import org.apache.hugegraph.store.grpc.session.BatchEntry;
import org.apache.hugegraph.store.grpc.stream.HgStoreStreamGrpc.HgStoreStreamStub;
import org.apache.hugegraph.store.grpc.stream.ScanStreamReq;
+import org.apache.hugegraph.store.query.StoreQueryParam;
+import org.apache.hugegraph.structure.BaseElement;
import com.google.protobuf.ByteString;
import com.google.protobuf.UnsafeByteOperations;
@@ -61,26 +65,16 @@
@NotThreadSafe
class GrpcStoreNodeSessionImpl implements HgStoreNodeSession {
- private static final HgStoreClientConfig hgStoreClientConfig = HgStoreClientConfig.of();
- private static final ConcurrentHashMap tables = new ConcurrentHashMap<>() {{
- put("unknown", 0);
- put("g+v", 1);
- put("g+oe", 2);
- put("g+ie", 3);
- put("g+index", 4);
- put("g+task", 5);
- put("g+olap", 6);
- put("g+server", 7);
- }};
- private final HgStoreNode storeNode;
- private final String graphName;
- private final GrpcStoreSessionClient storeSessionClient;
- private final GrpcStoreStreamClient storeStreamClient;
- private final HgStoreNodeManager nodeManager;
- private final NotifyingExecutor notifier;
- private final SwitchingExecutor switcher;
- private final BatchEntry.Builder batchEntryBuilder = BatchEntry.newBuilder();
- private final Key.Builder builder = Key.newBuilder();
+ private static HgStoreClientConfig hgStoreClientConfig = HgStoreClientConfig.of();
+ private HgStoreNode storeNode;
+ private String graphName;
+ private GrpcStoreSessionClient storeSessionClient;
+ private GrpcStoreStreamClient storeStreamClient;
+ private HgStoreNodeManager nodeManager;
+ private NotifyingExecutor notifier;
+ private SwitchingExecutor switcher;
+ private BatchEntry.Builder batchEntryBuilder = BatchEntry.newBuilder();
+ private Key.Builder builder = Key.newBuilder();
private boolean isAutoCommit = true;
private String batchId;
private LinkedList batchEntries = new LinkedList<>();
@@ -220,10 +214,7 @@ public boolean merge(String table, HgOwnerKey key, byte[] value) {
private boolean prepareBatchEntry(OpType opType, String table
, HgOwnerKey startKey, HgOwnerKey endKey, byte[] value) {
this.batchEntryBuilder.clear().setOpType(opType);
- Integer tableCode = tables.get(table);
- if (tableCode != null) {
- this.batchEntryBuilder.setTable(tableCode);
- }
+ this.batchEntryBuilder.setTable(HugeServerTables.TABLES_MAP.get(table));
if (startKey != null) {
this.batchEntryBuilder.setStartKey(toKey(startKey));
}
@@ -371,11 +362,6 @@ public HgKvIterator scanIterator(ScanStreamReq.Builder builder) {
return GrpcKvIteratorImpl.of(this, scanner);
}
- @Override
- public long count(String table) {
- return this.storeSessionClient.count(this, table);
- }
-
@Override
public HgKvIterator scanIterator(String table, byte[] query) {
return GrpcKvIteratorImpl.of(this, this.storeStreamClient.doScan(this, table, 0, query));
@@ -545,4 +531,10 @@ private Supplier getSwitcherSupplier(long limit) {
public String toString() {
return "storeNodeSession: {" + storeNode + ", graphName: \"" + graphName + "\"}";
}
+
+ @Override
+ public List> query(StoreQueryParam query,
+ HugeGraphSupplier supplier) throws PDException {
+ return null;
+ }
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreSessionClient.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreSessionClient.java
index 794a7c1286..bd91d3147e 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreSessionClient.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/GrpcStoreSessionClient.java
@@ -17,19 +17,17 @@
package org.apache.hugegraph.store.client.grpc;
-import static org.apache.hugegraph.store.client.grpc.KvBatchUtil.getHeader;
+import static org.apache.hugegraph.store.client.grpc.GrpcUtil.getHeader;
+import static org.apache.hugegraph.store.client.grpc.GrpcUtil.toTk;
import java.util.List;
-import java.util.concurrent.TimeUnit;
import javax.annotation.concurrent.ThreadSafe;
import org.apache.hugegraph.store.HgOwnerKey;
import org.apache.hugegraph.store.client.HgStoreNodeSession;
import org.apache.hugegraph.store.grpc.common.GraphMethod;
-import org.apache.hugegraph.store.grpc.common.ScanMethod;
import org.apache.hugegraph.store.grpc.common.TableMethod;
-import org.apache.hugegraph.store.grpc.session.Agg;
import org.apache.hugegraph.store.grpc.session.BatchEntry;
import org.apache.hugegraph.store.grpc.session.BatchGetReq;
import org.apache.hugegraph.store.grpc.session.BatchReq;
@@ -41,9 +39,7 @@
import org.apache.hugegraph.store.grpc.session.HgStoreSessionGrpc;
import org.apache.hugegraph.store.grpc.session.HgStoreSessionGrpc.HgStoreSessionBlockingStub;
import org.apache.hugegraph.store.grpc.session.TableReq;
-import org.apache.hugegraph.store.grpc.stream.ScanStreamReq;
-import io.grpc.Deadline;
import io.grpc.ManagedChannel;
import lombok.extern.slf4j.Slf4j;
@@ -58,41 +54,25 @@ class GrpcStoreSessionClient extends AbstractGrpcClient {
@Override
public HgStoreSessionBlockingStub getBlockingStub(ManagedChannel channel) {
- HgStoreSessionBlockingStub stub;
- stub = HgStoreSessionGrpc.newBlockingStub(channel);
- return stub;
+ return HgStoreSessionGrpc.newBlockingStub(channel);
}
private HgStoreSessionBlockingStub getBlockingStub(HgStoreNodeSession nodeSession) {
- HgStoreSessionBlockingStub stub =
- (HgStoreSessionBlockingStub) getBlockingStub(
- nodeSession.getStoreNode().getAddress());
- return stub;
+ return (HgStoreSessionBlockingStub) getBlockingStub(
+ nodeSession.getStoreNode().getAddress());
}
FeedbackRes doGet(HgStoreNodeSession nodeSession, String table, HgOwnerKey ownerKey) {
- if (log.isDebugEnabled()) {
- log.debug("doGet: {}-{}-{}-{}", nodeSession, table, ownerKey, GetReq.newBuilder()
- .setHeader(
- GrpcUtil.getHeader(
- nodeSession))
- .setTk(GrpcUtil.toTk(
- table,
- ownerKey))
- .build());
- }
return this.getBlockingStub(nodeSession)
- .get2(GetReq.newBuilder()
- .setHeader(GrpcUtil.getHeader(nodeSession))
- .setTk(GrpcUtil.toTk(table, ownerKey))
- .build()
- );
+ .get2(GetReq.newBuilder().setHeader(getHeader(nodeSession))
+ .setTk(toTk(table, ownerKey))
+ .build());
}
FeedbackRes doClean(HgStoreNodeSession nodeSession, int partId) {
return this.getBlockingStub(nodeSession)
.clean(CleanReq.newBuilder()
- .setHeader(GrpcUtil.getHeader(nodeSession))
+ .setHeader(getHeader(nodeSession))
.setPartition(partId)
.build()
);
@@ -100,7 +80,7 @@ FeedbackRes doClean(HgStoreNodeSession nodeSession, int partId) {
FeedbackRes doBatchGet(HgStoreNodeSession nodeSession, String table, List keyList) {
BatchGetReq.Builder builder = BatchGetReq.newBuilder();
- builder.setHeader(GrpcUtil.getHeader(nodeSession)).setTable(table);
+ builder.setHeader(getHeader(nodeSession)).setTable(table);
for (HgOwnerKey key : keyList) {
builder.addKey(GrpcUtil.toKey(key));
@@ -118,7 +98,7 @@ FeedbackRes doBatch(HgStoreNodeSession nodeSession, String batchId, List channels = new ConcurrentHashMap<>();
private final PDConfig pdConfig;
private final PDClient pdClient;
@@ -63,13 +69,43 @@ public Set getScanState() throws Exception {
} catch (Exception e) {
throw e;
}
+ }
+ public String getPeers(String address, int partitionId) {
+ ManagedChannel channel = channels.get(address);
+ try {
+ if (channel == null) {
+ synchronized (channels) {
+ if ((channel = channels.get(address)) == null) {
+ channel = createChannel(address);
+ channels.put(address, channel);
+ }
+ }
+ }
+ HgStoreStateBlockingStub stub = (HgStoreStateBlockingStub) getBlockingStub(channel);
+ PeersResponse peers =
+ stub.getPeers(PartitionRequest.newBuilder().setId(partitionId).build());
+ return peers.getPeers();
+ } catch (Exception e) {
+ throw e;
+ } finally {
+ }
}
@Override
public AbstractBlockingStub getBlockingStub(ManagedChannel channel) {
- HgStoreStateBlockingStub stub;
- stub = HgStoreStateGrpc.newBlockingStub(channel);
- return stub;
+ return HgStoreStateGrpc.newBlockingStub(channel);
+ }
+
+ @Override
+ public synchronized void close() {
+ for (ManagedChannel c : channels.values()) {
+ try {
+ c.shutdown();
+ } catch (Exception e) {
+
+ }
+ }
+ channels.clear();
}
}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScanner5.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScanner5.java
index 9f95eeb510..2ee91f62b7 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScanner5.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScanner5.java
@@ -35,7 +35,7 @@
import org.apache.hugegraph.store.HgScanQuery;
import org.apache.hugegraph.store.client.HgStoreNodeSession;
import org.apache.hugegraph.store.client.type.HgStoreClientException;
-import org.apache.hugegraph.store.util.Base58Encoder;
+import org.apache.hugegraph.store.client.util.Base58;
import org.apache.hugegraph.store.client.util.HgStoreClientConfig;
import org.apache.hugegraph.store.grpc.common.Kv;
import org.apache.hugegraph.store.grpc.stream.HgStoreStreamGrpc;
@@ -107,7 +107,7 @@ private static class OrderBroker {
if (log.isDebugEnabled()) {
if (scanQuery.getPrefixList() != null && scanQuery.getPrefixList().size() > 0) {
- brokerId = Base58Encoder.convertToBase58(scanQuery.getPrefixList().get(0).getKey());
+ brokerId = Base58.encode(scanQuery.getPrefixList().get(0).getKey());
log.debug(
"[ANALYSIS START] [{}] firstKey: {}, keyLength: {}, table: {}, node: {}"
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScannerMerger.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScannerMerger.java
index 4f89a275c6..a47c37ab42 100644
--- a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScannerMerger.java
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/grpc/KvBatchScannerMerger.java
@@ -233,7 +233,7 @@ public SortedScannerMerger(KvBatchScanner.TaskSplitter splitter) {
super(splitter);
queue.add(() -> {
// Perform merge sort on the store's return result
- return new HgKvIterator<>() {
+ return new HgKvIterator() {
private ScannerDataQueue iterator;
private int currentSN = 0;
private HgKvEntry entry;
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/CommonKvStreamObserver.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/CommonKvStreamObserver.java
new file mode 100644
index 0000000000..a7d8025d29
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/CommonKvStreamObserver.java
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.client.query;
+
+import io.grpc.stub.StreamObserver;
+import lombok.Data;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Iterator;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.apache.hugegraph.store.client.type.HgStoreClientException;
+
+@Slf4j
+public class CommonKvStreamObserver implements StreamObserver {
+
+ /**
+ * 结果保存的队列
+ */
+ private final BlockingQueue> queue;
+
+ /**
+ * 向 server 发送请求的函数
+ */
+ @Setter
+ private Consumer requestSender;
+
+ /**
+ * 当 server 没有结果时候,所做的处理,关闭双向流
+ */
+ @Setter
+ private Consumer transferComplete;
+
+ /**
+ * 解析 server 返回数据的函数
+ */
+ private final Function> valueExtractor;
+ /**
+ * 解析 server 返回数据的状态
+ */
+ private final Function stateWatcher;
+
+ /**
+ * 是否结束,可以由 client 发送,即不接收多余的数据了
+ */
+ private final AtomicBoolean closed = new AtomicBoolean(false);
+
+ @Setter
+ private long timeout = 1800 * 1000;
+
+ /**
+ * 监控内部的状态
+ */
+ private final ResultStateWatcher watcher = new ResultStateWatcher();
+
+ public CommonKvStreamObserver(Function> valueExtractor,
+ Function stateWatcher) {
+ this.queue = new LinkedBlockingQueue<>();
+ this.valueExtractor = valueExtractor;
+ this.stateWatcher = stateWatcher;
+ }
+
+ /**
+ * 发送请求
+ */
+ public void sendRequest() {
+ if (!isServerFinished() && !closed.get()) {
+ this.requestSender.accept(true);
+ this.watcher.setState(ResultState.WAITING);
+ }
+ }
+
+ public boolean isServerFinished() {
+ return this.watcher.getState() == ResultState.FINISHED
+ || this.watcher.getState() == ResultState.ERROR;
+ }
+
+ @Override
+ public void onNext(R value) {
+ watcher.setState(ResultState.INNER_BUSY);
+ try {
+ var state = stateWatcher.apply(value);
+ log.debug("observer state: {}", state);
+
+ switch (state) {
+ case IDLE:
+ case FINISHED:
+ if (! this.closed.get()) {
+ queue.offer(this.valueExtractor.apply(value));
+ }
+ // this.stop();
+ break;
+ default:
+ queue.offer(new ErrorMessageIterator<>(state.getMessage()));
+ break;
+ }
+ watcher.setState(state);
+ // sendRequest();
+ } catch (Exception e) {
+ log.error("handling server data, got error: ", e);
+ queue.offer(new ErrorMessageIterator<>(e.getMessage()));
+ }
+ }
+
+ public Iterator consume() {
+ try {
+ while (!Thread.currentThread().isInterrupted() && (!this.queue.isEmpty() ||
+ !isServerFinished())) {
+ var iterator = this.queue.poll(200, TimeUnit.MILLISECONDS);
+ if (iterator != null) {
+ sendRequest();
+ return iterator;
+ }
+
+ if (System.currentTimeMillis() - watcher.current > this.timeout) {
+ throw new HgStoreClientException("iterator timeout");
+ }
+
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+
+ return null;
+ }
+
+ /**
+ * 发送 onComplete,停止接收数据
+ */
+ public void clear() {
+ if (! this.closed.get()) {
+ this.closed.set(true);
+ this.transferComplete.accept(true);
+ }
+ this.queue.clear();
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ log.error("StreamObserver got error:", t);
+ this.queue.offer(new ErrorMessageIterator<>(t.getMessage()));
+ this.watcher.setState(ResultState.ERROR);
+ }
+
+ @Override
+ public void onCompleted() {
+ if (watcher.getState() != ResultState.ERROR) {
+ watcher.setState(ResultState.FINISHED);
+ }
+ }
+
+ public void setWatcherQueryId(String queryId) {
+ this.watcher.setQueryId(queryId);
+ }
+
+ @Data
+ private static class ResultStateWatcher{
+ private long current = System.nanoTime();
+ private volatile ResultState state = ResultState.IDLE;
+
+ private String queryId;
+
+ public void setState(ResultState state) {
+ log.debug("query Id: {}, COST_STAT: {} -> {}, cost {} ms", this.queryId, this.state, state,
+ + (System.nanoTime() - current) * 1.0 / 1000000);
+ this.state = state;
+ this.current = System.nanoTime();
+ }
+ }
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/ErrorMessageIterator.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/ErrorMessageIterator.java
new file mode 100644
index 0000000000..97cf7e3ade
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/ErrorMessageIterator.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 org.apache.hugegraph.store.client.query;
+
+import org.apache.hugegraph.store.client.type.HgStoreClientException;
+
+import java.util.Iterator;
+
+public class ErrorMessageIterator implements Iterator {
+
+ private String message;
+
+ public ErrorMessageIterator(String message) {
+ this.message = message;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return true;
+ }
+
+ @Override
+ public E next() {
+ throw new HgStoreClientException(message);
+ }
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/MultiStreamIterator.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/MultiStreamIterator.java
new file mode 100644
index 0000000000..1414551c10
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/MultiStreamIterator.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.client.query;
+
+import org.apache.hugegraph.store.HgKvIterator;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+public class MultiStreamIterator implements HgKvIterator {
+ private HgKvIterator currentIterator = null;
+
+ private final Iterator> listIterator;
+
+ public MultiStreamIterator(List> iterators) {
+ this.listIterator = iterators.iterator();
+ }
+
+
+ @Override
+ public byte[] key() {
+ return currentIterator.key();
+ }
+
+ @Override
+ public byte[] value() {
+ return currentIterator.value();
+ }
+
+ @Override
+ public void close() {
+ if (currentIterator != null && currentIterator.hasNext()) {
+ currentIterator.close();
+ }
+ }
+
+ @Override
+ public byte[] position() {
+ return currentIterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ // todo: ??
+ this.currentIterator.seek(position);
+ }
+
+ private void getNextIterator() {
+ if (currentIterator != null && currentIterator.hasNext()) {
+ return;
+ }
+
+ while (listIterator.hasNext()) {
+ currentIterator = listIterator.next();
+ if (currentIterator.hasNext()) {
+ break;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ getNextIterator();
+ return currentIterator != null && currentIterator.hasNext();
+ }
+
+ @Override
+ public E next() {
+ if (currentIterator == null || ! currentIterator.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return currentIterator.next();
+ }
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/QueryExecutor.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/QueryExecutor.java
new file mode 100644
index 0000000000..09ae6461b3
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/QueryExecutor.java
@@ -0,0 +1,550 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.client.query;
+
+import org.apache.hugegraph.HugeGraphSupplier;
+import org.apache.hugegraph.backend.BackendColumn;
+import org.apache.hugegraph.id.Id;
+import org.apache.hugegraph.pd.common.PDException;
+import org.apache.hugegraph.pd.common.PartitionUtils;
+import org.apache.hugegraph.serializer.BinaryElementSerializer;
+import org.apache.hugegraph.serializer.BytesBuffer;
+import org.apache.hugegraph.store.HgKvIterator;
+import org.apache.hugegraph.store.client.HgStoreNodePartitioner;
+import org.apache.hugegraph.store.grpc.common.Kv;
+import org.apache.hugegraph.store.grpc.query.AggregateFunc;
+import org.apache.hugegraph.store.grpc.query.AggregationType;
+import org.apache.hugegraph.store.grpc.query.DeDupOption;
+import org.apache.hugegraph.store.grpc.query.Index;
+import org.apache.hugegraph.store.grpc.query.QueryRequest;
+import org.apache.hugegraph.store.grpc.query.QueryResponse;
+import org.apache.hugegraph.store.grpc.query.ScanType;
+import org.apache.hugegraph.store.grpc.query.ScanTypeParam;
+import org.apache.hugegraph.store.query.BaseElementComparator;
+import org.apache.hugegraph.store.query.KvSerializer;
+import org.apache.hugegraph.store.query.StoreQueryType;
+import org.apache.hugegraph.store.query.Tuple2;
+import org.apache.hugegraph.store.query.func.AggregationFunctionParam;
+import org.apache.hugegraph.store.query.util.KeyUtil;
+import org.apache.hugegraph.store.query.QueryTypeParam;
+import org.apache.hugegraph.store.query.StoreQueryParam;
+import org.apache.hugegraph.structure.BaseElement;
+import org.apache.hugegraph.structure.KvElement;
+import com.google.protobuf.ByteString;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static org.apache.hugegraph.store.constant.HugeServerTables.IN_EDGE_TABLE;
+import static org.apache.hugegraph.store.constant.HugeServerTables.OUT_EDGE_TABLE;
+
+@Slf4j
+public class QueryExecutor {
+
+ private final HgStoreNodePartitioner nodePartitioner;
+
+ private static QueryV2Client client = new QueryV2Client();
+
+ private static BinaryElementSerializer serializer = new BinaryElementSerializer();
+
+ private final HugeGraphSupplier supplier;
+
+ private long timeout = 1800_000;
+
+ /**
+ * 用于调试单台机器
+ */
+ public static String filterStore = null;
+
+ public QueryExecutor(HgStoreNodePartitioner nodePartitioner, HugeGraphSupplier supplier, Long timeout) {
+ this.nodePartitioner = nodePartitioner;
+ this.supplier = supplier;
+ this.timeout = timeout;
+ }
+
+ /**
+ * 根据查询条件获取迭代器列表。
+ *
+ * @param query 查询参数
+ * @return 迭代器列表
+ * @throws PDException 如果发生任何错误则抛出此异常。
+ */
+ public List> getIterators(StoreQueryParam query) throws PDException {
+ if (isSimpleQuery(query)) {
+ return getSimpleIterator(query);
+ }
+
+ List> iterators;
+
+ if (isSimpleCountQuery(query)) {
+ iterators = getCountIterator(query);
+ } else {
+ // 获取所有 node 节点的 iterator
+ iterators = getNodeTasks(query)
+ .parallelStream()
+ .map(tuple -> getIterator(tuple.getV1(), tuple.getV2().build()))
+ .collect(Collectors.toList());
+ }
+
+
+ if (isEmpty(query.getFuncList()) && isEmpty(query.getOrderBy()) && query.getLimit() == 0) {
+ return iterators;
+ }
+
+ HgKvIterator iterator;
+
+ if (! isEmpty(query.getFuncList())) {
+ // agg: 先排序,后计算
+ iterator = new StreamSortedIterator<>(iterators, (o1, o2) -> {
+ if (o1 == null && o2 == null) {
+ return 0;
+ }
+
+ if (o1 != null) {
+ return ((KvElement) o1).compareTo((KvElement) o2);
+ }
+
+ return 0;
+ });
+
+ iterator = new StreamFinalAggregationIterator<>(iterator, query.getFuncList());
+ if (query.getSampleFactor() != 1) {
+ // 抽样不下沉,在最后做
+ iterator = new StreamSampleIterator<>(iterator, query.getSampleFactor());
+ }
+ } else if (!isEmpty(query.getOrderBy())) {
+ // 有排序
+ if (query.getSortOrder() != StoreQueryParam.SORT_ORDER.STRICT_ORDER) {
+ iterator = new StreamSortedIterator<>(iterators, new BaseElementComparator(query.getOrderBy(),
+ query.getSortOrder() == StoreQueryParam.SORT_ORDER.ASC));
+ } else {
+ iterator = new StreamStrictOrderIterator<>(query.getQueryParam(), iterators,
+ query.getGraph(), this.nodePartitioner);
+ }
+ } else {
+ // 后面还有 limit
+ iterator = new MultiStreamIterator<>(iterators);
+ }
+
+ if (query.getLimit() > 0) {
+ iterator = new StreamLimitIterator<>(iterator, query.getLimit());
+ }
+
+ return List.of(iterator);
+ }
+
+ /**
+ * 使用 StreamKvIterator 封装返回的结果
+ * @param address store node addr
+ * @param request initial request
+ * @return iterator result
+ */
+ private StreamKvIterator getIterator(String address, QueryRequest request) {
+ var stub = client.getQueryServiceStub(address);
+ var hasAgg = ! isEmpty(request.getFunctionsList());
+
+ var observer = new CommonKvStreamObserver(
+ response -> new Iterator<>() {
+ final Iterator itr = response.getDataList().iterator();
+
+ BaseElement element = null;
+
+ @Override
+ public boolean hasNext() {
+ if (element == null) {
+ while (itr.hasNext()) {
+ element = fromKv(request.getTable(), itr.next(), hasAgg);
+ if (element != null) {
+ break;
+ }
+ }
+ }
+ return element != null;
+ }
+
+ @Override
+ public BaseElement next() {
+ try {
+ return element;
+ } finally {
+ element = null;
+ }
+ // return fromKv(request.getTable(), itr.next(), hasAgg);
+ }
+ },
+
+ response -> {
+ // is OK | is finished
+ // T | T -> finished
+ // T | F -> has more result
+ // F | T -> busy
+ // F | F -> error
+ var ok = response.getIsOk();
+ var finished = response.getIsFinished();
+
+ if (ok & finished) {
+ return ResultState.FINISHED;
+ }
+
+ if (ok & !finished) {
+ return ResultState.IDLE;
+ }
+
+ if (finished) {
+ return ResultState.ERROR.setMessage("server is busy");
+ }
+ return ResultState.ERROR.setMessage(response.getMessage());
+ }
+
+ );
+
+ var reqStream = stub.query(observer);
+ observer.setWatcherQueryId(request.getQueryId() + '-' + address);
+ observer.setRequestSender(r -> reqStream.onNext(request));
+ observer.setTransferComplete(r -> reqStream.onCompleted());
+ observer.setTimeout(this.timeout);
+
+ var itr = new StreamKvIterator<>(b -> observer.clear(), observer::consume);
+ observer.sendRequest();
+ return itr;
+ }
+
+ private static boolean isEmpty(Collection c) {
+ return c == null || c.isEmpty();
+ }
+
+ // 返回 node addr -> query proto
+ // 只对 id scan 进行拆分,剩下的发送给所有的 store
+ private List> getNodeTasks(StoreQueryParam query) throws PDException {
+ var graph = query.getGraph();
+ var stores = this.nodePartitioner.getStores(graph);
+
+ if (stores.isEmpty()) {
+ log.warn("no stores found, query: {}", query);
+ }
+
+ Map tasks = new HashMap<>();
+
+ if (query.getQueryType() == StoreQueryType.PRIMARY_SCAN) {
+ // primary 拆分 query param,主要是 id scan
+ for (var param : query.getQueryParam()) {
+ if (param.getCode() != -1) {
+ var addr = this.nodePartitioner.partition(graph, param.getCode());
+ if (! tasks.containsKey(addr)) {
+ tasks.put(addr, fromQuery(query)) ;
+ }
+ tasks.get(addr).addScanTypeParam(fromParam(query.getTable(), param));
+ } else {
+ for (String addr : stores) {
+ if (! tasks.containsKey(addr)) {
+ tasks.put(addr, fromQuery(query)) ;
+ }
+ tasks.get(addr).addScanTypeParam(fromParam(query.getTable(), param));
+ }
+ }
+ }
+ } else {
+ for (String addr : stores) {
+ tasks.computeIfAbsent(addr, t -> fromQuery(query));
+ }
+ }
+
+ if (filterStore != null) {
+ return tasks.containsKey(filterStore) ? List.of(Tuple2.of(filterStore, tasks.get(filterStore))) : List.of();
+ }
+
+ return tasks.entrySet().stream()
+ .map(entry -> Tuple2.of(entry.getKey(), entry.getValue()))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 从查询参数构建 QueryRequest.Builder 对象。
+ *
+ * @param query 查询参数对象
+ * @return 返回构建好的 QueryRequest.Builder 对象
+ */
+ private static QueryRequest.Builder fromQuery(StoreQueryParam query) {
+ var builder = QueryRequest.newBuilder()
+ .setQueryId(query.getQueryId())
+ .setGraph(query.getGraph())
+ .setTable(query.getTable())
+ .addAllFunctions(getAggregationProto(query.getFuncList()))
+ .addAllProperty(idToBytes(query.getProperties().getPropertyIds()))
+ .setNullProperty(query.getProperties().isEmptyId())
+ .addAllGroupBy(idToBytes(query.getGroupBy()))
+ .addAllOrderBy(idToBytes(query.getOrderBy()))
+ .addAllHaving(getOrCreate(query.getHaving()))
+ .setScanType(ScanType.forNumber(query.getQueryType().ordinal()))
+ // .addAllScanTypeParam(fromParams(query.getTable(), query.getQueryParam()))
+ .setDedupOption(DeDupOption.forNumber(query.getDedupOption().ordinal()))
+ .setOffset(query.getOffset())
+ .addAllOlapProperty(idToBytes(query.getOlapProperties()))
+ .setLoadPropertyFromIndex(query.isLoadPropertyFromIndex())
+ .setGroupBySchemaLabel(query.isGroupBySchemaLabel());
+
+ if (query.getSortOrder() != StoreQueryParam.SORT_ORDER.STRICT_ORDER) {
+ builder.setSortOrder(query.getSortOrder() == StoreQueryParam.SORT_ORDER.ASC);
+ }
+
+ // count 不需要反查的时候 (不需要去重,不需要取并集),改为 NO_SCAN, 每个 size 都是 1(都是 index scan)
+ if (query.getQueryType() == StoreQueryType.INDEX_SCAN
+ && query.getDedupOption() == StoreQueryParam.DEDUP_OPTION.NONE) {
+ if (! isEmpty(query.getFuncList()) && query.getFuncList().stream().allMatch(f -> f.getFunctionType() ==
+ AggregationFunctionParam.AggregationFunctionType.COUNT) && query.getConditionQuery() == null){
+ if (query.getIndexes().stream().allMatch(i -> i.size() == 1 && i.get(0).isIndexScan())) {
+ log.info("trans query id {} from INDEX_SCAN to NO_SCAN", query.getQueryId());
+ builder.setScanType(ScanType.NO_SCAN);
+ }
+ }
+ }
+
+ if (query.getConditionQuery() != null) {
+ builder.setCondition(ByteString.copyFrom(query.getConditionQuery().bytes()));
+ }
+
+ if (query.getPosition() != null) {
+ builder.setPosition(ByteString.copyFrom(query.getPosition()));
+ }
+
+ // sample and limit are set to default when has function list.
+ builder.setSampleFactor(isEmpty(query.getFuncList()) ? query.getSampleFactor() : 1.0);
+ builder.setLimit(isEmpty(query.getFuncList()) ? query.getLimit() : 0);
+
+ if (query.getIndexes() != null) {
+ builder.addAllIndexes(fromIndex(query.getIndexes()));
+ }
+
+ builder.setCheckTtl(query.isCheckTTL());
+
+ return builder;
+ }
+
+ private static ScanTypeParam fromParam(String table, QueryTypeParam param) {
+ var builder = ScanTypeParam.newBuilder()
+ .setKeyStart(ByteString.copyFrom(param.getStart()))
+ .setScanBoundary(param.getBoundary())
+ .setIsPrefix(param.isPrefix())
+ .setIsSecondaryIndex(param.isSecondaryIndex());
+
+ if (param.getEnd() != null) {
+ builder.setKeyEnd(ByteString.copyFrom(param.getEnd()));
+ }
+
+ if (param.isIdScan() && param.getCode() == -1) {
+ builder.setCode(PartitionUtils.calcHashcode(KeyUtil.getOwnerKey(table, param.getStart())));
+ } else {
+ builder.setCode(param.getCode());
+ }
+
+ if (param.getIdPrefix() != null) {
+ builder.setIdPrefix(ByteString.copyFrom(param.getIdPrefix()));
+ }
+ return builder.build();
+ }
+
+ private static List fromParams(String table, List params) {
+ if (isEmpty(params)) {
+ return new ArrayList<>();
+ }
+ return params.stream().map(p -> fromParam(table, p)).collect(Collectors.toList());
+ }
+
+ private static List fromIndex(List> indexes) {
+ return indexes.stream()
+ .map(x -> Index.newBuilder().addAllParams(fromParams("", x)).build())
+ .collect(Collectors.toList());
+ }
+
+ private static List getAggregationProto(List aggParams) {
+ if (isEmpty(aggParams)) {
+ return new ArrayList<>();
+ }
+ return aggParams.stream().map(param -> {
+
+ var builder = AggregateFunc.newBuilder();
+
+ builder.setFuncType(AggregationType.forNumber(param.getFunctionType().ordinal()));
+
+ if (param.getField() != null) {
+ builder.setField(idToBytes(param.getField()));
+ }
+
+ if (param.getFiledType() != null) {
+ builder.setType(param.getFiledType().getGenericType());
+ }
+ return builder.build();
+ }).collect(Collectors.toList());
+ }
+
+ private static List idToBytes(List ids) {
+ if (isEmpty(ids)) {
+ return new ArrayList<>();
+ }
+
+ return ids.stream().map(QueryExecutor::idToBytes).collect(Collectors.toList());
+ }
+
+ public static ByteString idToBytes(Id id) {
+ BytesBuffer buffer = BytesBuffer.allocate(2);
+ buffer.writeId(id);
+ return ByteString.copyFrom(buffer.bytes());
+ }
+
+ private static List getOrCreate(List list){
+ if (list != null) {
+ return list;
+ }
+ return new ArrayList<>();
+ }
+
+ private BaseElement fromKv(String table, Kv kv, boolean isAgg) {
+ if (isAgg) {
+ return KvElement.of(KvSerializer.fromBytes(kv.getKey().toByteArray()),
+ KvSerializer.fromObjectBytes(kv.getValue().toByteArray()));
+ }
+
+ var backendColumn = BackendColumn.of(kv.getKey().toByteArray(), kv.getValue().toByteArray());
+ try {
+ if (IN_EDGE_TABLE.equals(table) || OUT_EDGE_TABLE.equals(table)) {
+ return serializer.parseEdge(this.supplier, backendColumn, null, true);
+ }
+ return serializer.parseVertex(this.supplier, backendColumn, null);
+ } catch (Exception e) {
+ log.error("parse element error,", e);
+ return null;
+ }
+ }
+
+ /**
+ * execute plan 为空,只是简单的查询
+ * @param param query param
+ * @return true if id simple scan query
+ */
+ private boolean isSimpleQuery(StoreQueryParam param) {
+ if (param.getQueryType() == StoreQueryType.PRIMARY_SCAN && ! param.isCheckTTL() && param.getLimit() == 0) {
+ // all id scan:
+ if (param.getQueryParam().stream().allMatch(QueryTypeParam::isIdScan)) {
+ return isEmpty(param.getFuncList()) &&
+ isEmpty(param.getOrderBy()) &&
+ isEmpty(param.getGroupBy()) &&
+ isEmpty(param.getOlapProperties()) &&
+ ! param.getProperties().needSerialize() &&
+ param.getConditionQuery() == null &&
+ param.getSampleFactor() == 1.0;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 判断是否为简单的聚合查询
+ *
+ * @param param 存储参数对象
+ * @return 如果是简单聚合查询,则返回 true,否则返回 false
+ */
+ private boolean isSimpleCountQuery(StoreQueryParam param) {
+ if (param.getQueryType() == StoreQueryType.TABLE_SCAN && (!isEmpty(param.getFuncList()))) {
+ return param.getFuncList()
+ .stream()
+ .allMatch(f -> f.getFunctionType() == AggregationFunctionParam.AggregationFunctionType.COUNT)
+ && isEmpty(param.getGroupBy())
+ && isEmpty(param.getOrderBy())
+ && param.getConditionQuery() == null
+ && ! param.isGroupBySchemaLabel()
+ && ! param.isCheckTTL();
+ }
+
+ return false;
+ }
+
+ /**
+ * 从查询参数中获取简单迭代器。
+ *
+ * @param query 查询参数
+ * @return 包含 BaseElement 元素的列表
+ * @throws PDException 如果查询失败
+ */
+ private List> getSimpleIterator(StoreQueryParam query) throws PDException {
+
+ return getNodeTasks(query).parallelStream()
+ .map( entry -> {
+ var stub = client.getQueryServiceBlockingStub(entry.getV1());
+ var response = stub.query0(entry.getV2().build());
+ if (! response.getIsOk()) {
+ throw new RuntimeException(response.getMessage());
+ }
+
+ var data = response.getDataList().iterator();
+ return new HgKvIterator() {
+ @Override
+ public boolean hasNext() {
+ return data.hasNext();
+ }
+
+ @Override
+ public BaseElement next() {
+ return fromKv(query.getTable(), data.next(), false);
+ }
+ };
+ })
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * 获取查询结果的元素数量迭代器
+ *
+ * @param query 查询参数
+ * @return 包含元素数量迭代器的列表
+ * @throws PDException 当发生异常时抛出
+ */
+ private List> getCountIterator(StoreQueryParam query) throws PDException {
+
+ return getNodeTasks(query).parallelStream()
+ .map( entry -> {
+ var stub = client.getQueryServiceBlockingStub(entry.getV1())
+ .withDeadlineAfter(3600, TimeUnit.SECONDS);
+ var response = stub.count(entry.getV2().build());
+ if (! response.getIsOk()) {
+ throw new RuntimeException(response.getMessage());
+ }
+
+ var data = response.getDataList().iterator();
+ return new HgKvIterator() {
+ @Override
+ public boolean hasNext() {
+ return data.hasNext();
+ }
+
+ @Override
+ public BaseElement next() {
+ return fromKv(query.getTable(), data.next(), true);
+ }
+ };
+ })
+ .collect(Collectors.toList());
+ }
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/QueryV2Client.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/QueryV2Client.java
new file mode 100644
index 0000000000..7c26a80903
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/QueryV2Client.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.client.query;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.hugegraph.store.client.grpc.AbstractGrpcClient;
+import org.apache.hugegraph.store.grpc.query.QueryServiceGrpc;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+import io.grpc.stub.AbstractAsyncStub;
+import io.grpc.stub.AbstractBlockingStub;
+
+public class QueryV2Client extends AbstractGrpcClient {
+
+ private static ManagedChannel channel = null;
+
+ private volatile AtomicInteger seq = new AtomicInteger(0);
+
+ @Override
+ public AbstractBlockingStub> getBlockingStub(ManagedChannel channel) {
+ return QueryServiceGrpc.newBlockingStub(channel);
+ }
+
+ @Override
+ public AbstractAsyncStub> getAsyncStub(ManagedChannel channel) {
+ return QueryServiceGrpc.newStub(channel);
+ }
+
+ public QueryServiceGrpc.QueryServiceBlockingStub getQueryServiceBlockingStub(String target) {
+ return (QueryServiceGrpc.QueryServiceBlockingStub) getBlockingStub(target);
+ }
+
+ public QueryServiceGrpc.QueryServiceStub getQueryServiceStub(String target) {
+ return (QueryServiceGrpc.QueryServiceStub) setStubOption(QueryServiceGrpc.newStub(getManagedChannel(target)));
+ // return (QueryServiceGrpc.QueryServiceStub) getAsyncStub(target);
+ }
+
+ private ManagedChannel getManagedChannel(String target) {
+ return getChannels(target)[Math.abs(seq.getAndIncrement() % concurrency)];
+ }
+
+ public static void setTestChannel(ManagedChannel directChannel) {
+ channels.clear();
+ channel = directChannel;
+ }
+
+ @Override
+ protected ManagedChannel createChannel(String target) {
+ return channel == null ? ManagedChannelBuilder.forTarget(target).usePlaintext().build() : channel;
+ }
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/ResultState.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/ResultState.java
new file mode 100644
index 0000000000..6aaa066d51
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/ResultState.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 org.apache.hugegraph.store.client.query;
+
+/**
+ * |---(has more result) --> IDLE
+ * |
+ * IDLE --(send req)--> WAITING --(onNext)--> INNER_BUSY |---(onCompleted)--> FINISHED
+ * |
+ * |---(error)----> ERROR
+ * |
+ * |---(processing)-> BUSY (ERROR)
+ */
+public enum ResultState {
+ // 初始化的状态,可以读取新数据
+ IDLE,
+ // 发送数据到等待server返回中间的状态
+ WAITING,
+ // 开始读取数据,后的状态
+ INNER_BUSY,
+ // 没更多数据了
+ FINISHED,
+ // 写入数据的状态
+ // 错误
+ ERROR;
+
+ private String message;
+
+ public String getMessage() {
+ return message;
+ }
+
+ public ResultState setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+}
diff --git a/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/StreamFinalAggregationIterator.java b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/StreamFinalAggregationIterator.java
new file mode 100644
index 0000000000..89bb5bb562
--- /dev/null
+++ b/hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/query/StreamFinalAggregationIterator.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 org.apache.hugegraph.store.client.query;
+
+import org.apache.hugegraph.store.HgKvIterator;
+import org.apache.hugegraph.store.query.func.AggregationFunction;
+import org.apache.hugegraph.store.query.func.AggregationFunctionParam;
+import org.apache.hugegraph.store.query.func.AggregationFunctions;
+import org.apache.hugegraph.structure.KvElement;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+public class StreamFinalAggregationIterator implements HgKvIterator {
+
+ private HgKvIterator iterator;
+
+ private List aggregationParams;
+
+ private List functions;
+
+ private KvElement prev = null;
+
+ private KvElement data = null;
+
+ public StreamFinalAggregationIterator(HgKvIterator iterator, List aggregations) {
+ this.iterator = iterator;
+ this.aggregationParams = aggregations;
+ }
+
+ @Override
+ public byte[] key() {
+ return this.iterator.key();
+ }
+
+ @Override
+ public byte[] value() {
+ return this.iterator.value();
+ }
+
+ @Override
+ public void close() {
+ this.iterator.close();
+ }
+
+ @Override
+ public byte[] position() {
+ return this.iterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ this.iterator.seek(position);
+ }
+
+ @Override
+ public boolean hasNext() {
+
+ while (iterator.hasNext()) {
+ var next = (KvElement) iterator.next();
+ if (prev == null) {
+ // first element, initial
+ prev = next;
+ functions = getAggregationList();
+ merge(prev.getValues());
+ continue;
+ }
+
+ if (keyEquals(next.getKeys(), prev.getKeys())) {
+ merge(next.getValues());
+ } else {
+ // 产生结果
+ data = KvElement.of(prev.getKeys(), term());
+ prev = next;
+ functions = getAggregationList();
+ merge(prev.getValues());
+ break;
+ }
+ }
+
+ // 消费完最后一个prev
+ if (! iterator.hasNext() && prev != null && data == null) {
+ data = KvElement.of(prev.getKeys(), term());
+ prev = null;
+ }
+
+ return data != null;
+ }
+
+ private void merge(List
+
+ org.roaringbitmap
+ RoaringBitmap
+ 0.9.38
+
@@ -168,6 +173,10 @@
hg-store-clienttest
+
+ org.apache.hugegraph
+ hugegraph-struct
+
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HeartbeatService.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HeartbeatService.java
index b8fe84ba91..1eb7d0b096 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HeartbeatService.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HeartbeatService.java
@@ -17,24 +17,28 @@
package org.apache.hugegraph.store;
+import static org.apache.hugegraph.pd.grpc.Pdpb.ErrorType.PD_UNREACHABLE_VALUE;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.hugegraph.pd.common.PDException;
+import org.apache.hugegraph.pd.common.PDRuntimeException;
import org.apache.hugegraph.pd.grpc.Metapb;
-import org.apache.hugegraph.pd.grpc.Pdpb;
+import org.apache.hugegraph.pd.grpc.Pdpb.ErrorType;
+import org.apache.hugegraph.store.consts.PoolNames;
+import org.apache.hugegraph.store.listener.PartitionStateListener;
+import org.apache.hugegraph.store.listener.StoreStateListener;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.meta.PartitionRole;
import org.apache.hugegraph.store.meta.Store;
import org.apache.hugegraph.store.meta.StoreMetadata;
import org.apache.hugegraph.store.options.HgStoreEngineOptions;
-import org.apache.hugegraph.store.options.RaftRocksdbOptions;
import org.apache.hugegraph.store.pd.PdProvider;
import org.apache.hugegraph.store.util.IpUtil;
import org.apache.hugegraph.store.util.Lifecycle;
-import org.rocksdb.MemoryUsageType;
import com.alipay.sofa.jraft.entity.PeerId;
import com.alipay.sofa.jraft.util.Utils;
@@ -47,23 +51,23 @@
@Slf4j
public class HeartbeatService implements Lifecycle, PartitionStateListener {
- private static final int MAX_HEARTBEAT_RETRY_COUNT = 5; // Heartbeat retry count
- private static final int REGISTER_RETRY_INTERVAL = 1; // Registration retry interval, in seconds
+ private static final int MAX_HEARTBEAT_RETRY_COUNT = 5;
+ private static final int REGISTER_RETRY_INTERVAL = 1;
+ private static int processors = Runtime.getRuntime().availableProcessors();
private final HgStoreEngine storeEngine;
- private final List stateListeners;
- private final Object partitionThreadLock = new Object();
- private final Object storeThreadLock = new Object();
private HgStoreEngineOptions options;
private PdProvider pdProvider;
private Store storeInfo;
private Metapb.ClusterStats clusterStats;
private StoreMetadata storeMetadata;
- // Heartbeat failure count
+ private List stateListeners;
+ private Object partitionThreadLock = new Object();
+ private Object storeThreadLock = new Object();
private int heartbeatFailCount = 0;
private int reportErrCount = 0;
// Thread sleep time
private volatile int timerNextDelay = 1000;
- private boolean terminated = false;
+ private volatile boolean terminated = false;
public HeartbeatService(HgStoreEngine storeEngine) {
this.storeEngine = storeEngine;
@@ -82,28 +86,16 @@ public boolean init(HgStoreEngineOptions opts) {
storeInfo.setRaftAddress(options.getRaftAddress());
storeInfo.setState(Metapb.StoreState.Unknown);
storeInfo.setLabels(options.getLabels());
- storeInfo.setCores(Runtime.getRuntime().availableProcessors());
+ storeInfo.setCores(processors);
storeInfo.setDeployPath(HeartbeatService.class.getResource("/").getPath());
storeInfo.setDataPath(options.getDataPath());
this.pdProvider = options.getPdProvider();
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- doStoreHeartbeat();
- }
- }, "heartbeat").start();
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- doPartitionHeartbeat();
- }
- }, " partition-hb").start();
+ new Thread(() -> doStoreHeartbeat(), PoolNames.HEARTBEAT).start();
+ new Thread(() -> doPartitionHeartbeat(), PoolNames.P_HEARTBEAT).start();
return true;
}
- public HeartbeatService addStateListener(HgStoreStateListener stateListener) {
+ public HeartbeatService addStateListener(StoreStateListener stateListener) {
stateListeners.add(stateListener);
return this;
}
@@ -118,7 +110,10 @@ public void setStoreMetadata(StoreMetadata storeMetadata) {
// Whether the cluster is ready
public boolean isClusterReady() {
- return clusterStats.getState() == Metapb.ClusterState.Cluster_OK;
+ if (clusterStats == null) {
+ clusterStats = pdProvider.getClusterStats();
+ }
+ return clusterStats != null && clusterStats.getState() == Metapb.ClusterState.Cluster_OK;
}
/**
@@ -145,7 +140,22 @@ protected void doStoreHeartbeat() {
storeThreadLock.wait(timerNextDelay);
}
} catch (Throwable e) {
- log.error("heartbeat error: ", e);
+ if (e instanceof PDRuntimeException &&
+ ((PDRuntimeException) e).getErrorCode() == PD_UNREACHABLE_VALUE) {
+ log.error("store heartbeat error: PD UNREACHABLE");
+ synchronized (storeThreadLock) {
+ try {
+ if (timerNextDelay < 10000) {
+ storeThreadLock.wait(timerNextDelay);
+ } else {
+ storeThreadLock.wait(timerNextDelay / 2);
+ }
+ } catch (Exception ie) {
+ }
+ }
+ } else {
+ log.error("heartbeat error: ", e);
+ }
}
}
}
@@ -170,7 +180,8 @@ protected void doPartitionHeartbeat() {
protected void registerStore() {
try {
- // Register store, initial registration of PD generates id, automatically assigns value to storeinfo
+ // Register store, initial registration of PD generates id, automatically assigns
+ // value to storeinfo
this.storeInfo.setStoreAddress(IpUtil.getNearestAddress(options.getGrpcAddress()));
this.storeInfo.setRaftAddress(IpUtil.getNearestAddress(options.getRaftAddress()));
@@ -200,22 +211,17 @@ protected void registerStore() {
}
} catch (PDException e) {
int exceptCode = e.getErrorCode();
- if (exceptCode == Pdpb.ErrorType.STORE_ID_NOT_EXIST_VALUE) {
- log.error(
- "The store ID {} does not match the PD. Check that the correct PD is " +
- "connected, " +
- "and then delete the store ID!!!",
- storeInfo.getId());
+ if (exceptCode == ErrorType.STORE_ID_NOT_EXIST_VALUE) {
+ log.error("The store ID {} does not match the PD. Check that the correct PD is " +
+ "connected, " + "and then delete the store ID!!!", storeInfo.getId());
System.exit(-1);
- } else if (exceptCode == Pdpb.ErrorType.STORE_HAS_BEEN_REMOVED_VALUE) {
+ } else if (exceptCode == ErrorType.STORE_HAS_BEEN_REMOVED_VALUE) {
log.error("The store ID {} has been removed, please delete all data and restart!",
storeInfo.getId());
System.exit(-1);
- } else if (exceptCode == Pdpb.ErrorType.STORE_PROHIBIT_DUPLICATE_VALUE) {
- log.error(
- "The store ID {} maybe duplicated, please check out store raft address " +
- "and restart later!",
- storeInfo.getId());
+ } else if (exceptCode == ErrorType.STORE_PROHIBIT_DUPLICATE_VALUE) {
+ log.error("The store ID {} maybe duplicated, please check out store raft address " +
+ "and restart later!", storeInfo.getId());
System.exit(-1);
}
}
@@ -230,16 +236,19 @@ protected void storeHeartbeat() {
clusterStats = pdProvider.storeHeartbeat(this.storeInfo);
} catch (PDException e) {
int exceptCode = e.getErrorCode();
- if (exceptCode == Pdpb.ErrorType.STORE_ID_NOT_EXIST_VALUE) {
+ if (exceptCode == ErrorType.STORE_ID_NOT_EXIST_VALUE) {
log.error("The store ID {} does not match the PD. Check that the correct PD is " +
"connected, and then delete the store ID!!!", storeInfo.getId());
System.exit(-1);
- } else if (exceptCode == Pdpb.ErrorType.STORE_HAS_BEEN_REMOVED_VALUE) {
+ } else if (exceptCode == ErrorType.STORE_HAS_BEEN_REMOVED_VALUE) {
log.error("The store ID {} has been removed, please delete all data and restart!",
storeInfo.getId());
System.exit(-1);
}
}
+ if (clusterStats == null || clusterStats.getState() == null) {
+ throw new PDRuntimeException(PD_UNREACHABLE_VALUE);
+ }
if (clusterStats.getState().getNumber() >= Metapb.ClusterState.Cluster_Fault.getNumber()) {
if (reportErrCount == 0) {
log.info("The cluster is abnormal, {}", clusterStats);
@@ -286,9 +295,9 @@ protected void partitionHeartbeat() {
final List statsList = new ArrayList<>(partitions.size());
Metapb.Shard localLeader = Metapb.Shard.newBuilder()
- .setStoreId(
- storeEngine.getPartitionManager().getStore()
- .getId())
+ .setStoreId(storeEngine
+ .getPartitionManager()
+ .getStore().getId())
.setRole(Metapb.ShardRole.Leader)
.build();
// Get information for each shard.
@@ -300,6 +309,16 @@ protected void partitionHeartbeat() {
stats.setConfVer(partition.getShardGroup().getConfVersion());
stats.setLeader(localLeader);
+ Metapb.PartitionState partitionState = Metapb.PartitionState.PState_Normal;
+ for (var entry : storeEngine.getPartitionManager().getPartitions(partition.getGroupId())
+ .entrySet()) {
+ if (entry.getValue().getWorkState() == Metapb.PartitionState.PState_Offline) {
+ partitionState = Metapb.PartitionState.PState_Offline;
+ }
+ }
+ // pd 不会处理 (3.7.2+)
+ stats.setState(partitionState);
+
stats.addAllShard(partition.getShardGroup().getMetaPbShard());
// shard status
@@ -331,20 +350,20 @@ protected void partitionHeartbeat() {
public void monitorMemory() {
- try {
- Map mems =
- storeEngine.getBusinessHandler().getApproximateMemoryUsageByType(null);
-
- if (mems.get(MemoryUsageType.kCacheTotal) >
- RaftRocksdbOptions.getWriteCacheCapacity() * 0.9 &&
- mems.get(MemoryUsageType.kMemTableUnFlushed) >
- RaftRocksdbOptions.getWriteCacheCapacity() * 0.1) {
- // storeEngine.getBusinessHandler().flushAll();
- log.warn("Less memory, start flush dbs, {}", mems);
- }
- } catch (Exception e) {
- log.error("MonitorMemory exception {}", e);
- }
+ // try {
+ // Map mems =
+ // storeEngine.getBusinessHandler().getApproximateMemoryUsageByType(null);
+ //
+ // if (mems.get(MemoryUsageType.kCacheTotal) > RaftRocksdbOptions
+ // .getWriteCacheCapacity() * 0.9 &&
+ // mems.get(MemoryUsageType.kMemTableUnFlushed) > RaftRocksdbOptions
+ // .getWriteCacheCapacity() * 0.1) {
+ // // storeEngine.getBusinessHandler().flushAll();
+ // // log.warn("Less memory, start flush dbs, {}", mems);
+ // }
+ // } catch (Exception e) {
+ // log.error("MonitorMemory exception {}", e);
+ // }
}
@Override
@@ -381,4 +400,18 @@ private void wakeupHeartbeatThread() {
storeThreadLock.notifyAll();
}
}
+
+ /**
+ * reconnect pulse
+ */
+ public void connectNewPulse() {
+ pdProvider.getPDClient().forceReconnect();
+// pdProvider.startHeartbeatStream(error->{
+// onStateChanged(Metapb.StoreState.Offline);
+// timerNextDelay = REGISTER_RETRY_INTERVAL * 1000;
+// wakeupHeartbeatThread();
+// log.error("Connection closed. The store state changes to {}", Metapb.StoreState
+// .Offline);
+// });
+ }
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreEngine.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreEngine.java
index b76e7a45c9..440d706ae4 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreEngine.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreEngine.java
@@ -24,33 +24,43 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.pd.grpc.Metapb;
import org.apache.hugegraph.rocksdb.access.RocksDBFactory;
import org.apache.hugegraph.store.business.BusinessHandler;
import org.apache.hugegraph.store.business.BusinessHandlerImpl;
-import org.apache.hugegraph.store.business.DataMover;
+import org.apache.hugegraph.store.business.DataManager;
import org.apache.hugegraph.store.cmd.HgCmdClient;
import org.apache.hugegraph.store.cmd.HgCmdProcessor;
-import org.apache.hugegraph.store.cmd.UpdatePartitionRequest;
-import org.apache.hugegraph.store.cmd.UpdatePartitionResponse;
+import org.apache.hugegraph.store.cmd.request.UpdatePartitionRequest;
+import org.apache.hugegraph.store.cmd.response.UpdatePartitionResponse;
+import org.apache.hugegraph.store.consts.PoolNames;
+import org.apache.hugegraph.store.listener.PartitionChangedListener;
+import org.apache.hugegraph.store.listener.StoreStateListener;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.meta.PartitionManager;
import org.apache.hugegraph.store.meta.ShardGroup;
import org.apache.hugegraph.store.meta.Store;
import org.apache.hugegraph.store.metric.HgMetricService;
import org.apache.hugegraph.store.options.HgStoreEngineOptions;
+import org.apache.hugegraph.store.options.JobOptions;
import org.apache.hugegraph.store.options.PartitionEngineOptions;
import org.apache.hugegraph.store.pd.DefaultPdProvider;
import org.apache.hugegraph.store.pd.FakePdServiceProvider;
import org.apache.hugegraph.store.pd.PdProvider;
+import org.apache.hugegraph.store.processor.Processors;
import org.apache.hugegraph.store.raft.RaftClosure;
import org.apache.hugegraph.store.raft.RaftOperation;
+import org.apache.hugegraph.store.util.ExecutorUtil;
import org.apache.hugegraph.store.util.HgRaftError;
import org.apache.hugegraph.store.util.Lifecycle;
import com.alipay.sofa.jraft.JRaftUtils;
+import com.alipay.sofa.jraft.Node;
import com.alipay.sofa.jraft.Status;
import com.alipay.sofa.jraft.conf.Configuration;
import com.alipay.sofa.jraft.core.NodeMetrics;
@@ -66,11 +76,13 @@
* The core class of the storage engine, initializing PD client and raft client
*/
@Slf4j
-public class HgStoreEngine implements Lifecycle, HgStoreStateListener {
+public class HgStoreEngine implements Lifecycle, StoreStateListener,
+ PartitionChangedListener {
- private final static HgStoreEngine instance = new HgStoreEngine();
- private static ConcurrentHashMap engineLocks = new ConcurrentHashMap<>();
- // Partition raft engine, key is GraphName_PartitionID
+ private static final HgStoreEngine INSTANCE = new HgStoreEngine();
+ private static final ConcurrentHashMap engineLocks = new ConcurrentHashMap<>();
+ private static ThreadPoolExecutor uninterruptibleJobs;
+ // 分区raft引擎,key为GraphName_PartitionID
private final Map partitionEngines = new ConcurrentHashMap<>();
private RpcServer rpcServer;
private HgStoreEngineOptions options;
@@ -80,14 +92,24 @@ public class HgStoreEngine implements Lifecycle, HgStoreSt
private HeartbeatService heartbeatService;
private BusinessHandler businessHandler;
private HgMetricService metricService;
- private DataMover dataMover;
+ private DataManager dataManager;
+ private final AtomicBoolean closing = new AtomicBoolean(false);
+
+ private HgStoreEngine() {
+
+ }
public static HgStoreEngine getInstance() {
- return instance;
+ return INSTANCE;
+ }
+
+ public static ThreadPoolExecutor getUninterruptibleJobs() {
+ return uninterruptibleJobs;
}
/**
- * 1. Read StoreId, register with PD, the StoreId is generated by PD for the first registration and stored locally.
+ * 1. Read StoreId, register with PD, the StoreId is generated by PD for the first
+ * registration and stored locally.
* 2. Registration successful, start the raft service.
* 3. Timely send Store heartbeats and Partition heartbeats to maintain contact with PD.
*
@@ -102,6 +124,15 @@ public synchronized boolean init(final HgStoreEngineOptions opts) {
}
this.options = opts;
+ // 放到最前面
+ if (uninterruptibleJobs == null) {
+ JobOptions jobConfig = options.getJobConfig();
+ uninterruptibleJobs = ExecutorUtil.createExecutor(PoolNames.U_JOB,
+ jobConfig.getUninterruptibleCore(),
+ jobConfig.getUninterruptibleMax(),
+ jobConfig.getUninterruptibleQueueSize(),
+ false);
+ }
BusinessHandlerImpl.initRocksdb(opts.getRocksdbConfig(), getRocksdbListener());
@@ -109,16 +140,17 @@ public synchronized boolean init(final HgStoreEngineOptions opts) {
pdProvider = new FakePdServiceProvider(opts.getFakePdOptions());
} else {
pdProvider = new DefaultPdProvider(opts.getPdAddress());
- pdProvider.addPartitionInstructionListener(new PartitionInstructionProcessor(this));
+ pdProvider.setCommandProcessors(new Processors(this));
}
options.setPdProvider(pdProvider);
partitionManager = new PartitionManager(pdProvider, opts);
- partitionManager.addPartitionChangedListener(new PartitionChangedListener());
-
+ partitionManager.addPartitionChangedListener(this);
businessHandler = new BusinessHandlerImpl(partitionManager);
- // Need businessHandler initialization afterwards
+ BusinessHandlerImpl.setIndexDataSize(
+ this.options.getQueryPushDownOption().getIndexSizeLimitCount());
+ // 需要businessHandler 初始化后
partitionManager.load();
rpcServer = createRaftRpcServer(opts.getRaftAddress());
@@ -128,7 +160,7 @@ public synchronized boolean init(final HgStoreEngineOptions opts) {
// When splitting, it has not been reported to pd in time.
if (getPartitionEngine(ptId) != null) {
return getPartitionEngine(ptId).waitForLeader(
- options.getWaitLeaderTimeout() * 1000);
+ options.getWaitLeaderTimeout() * 1000L);
} else {
// May occur cross-partition migration
Metapb.Shard shard = pdProvider.getPartitionLeader(graphName, ptId);
@@ -143,11 +175,12 @@ public synchronized boolean init(final HgStoreEngineOptions opts) {
metricService = HgMetricService.getInstance();
metricService.setHgStoreEngine(this).init(null);
-
- dataMover = opts.getDataTransfer();
- if (dataMover != null) {
- this.dataMover.setBusinessHandler(this.businessHandler);
- this.dataMover.setCmdClient(hgCmdClient);
+ partitionManager.setCmdClient(hgCmdClient);
+ dataManager = opts.getDataTransfer();
+ if (dataManager != null) {
+ dataManager.setBusinessHandler(this.businessHandler);
+ dataManager.setMetaManager(partitionManager);
+ dataManager.setCmdClient(hgCmdClient);
}
return true;
}
@@ -157,6 +190,7 @@ public synchronized boolean init(final HgStoreEngineOptions opts) {
*/
private RpcServer createRaftRpcServer(String raftAddr) {
Endpoint endpoint = JRaftUtils.getEndPoint(raftAddr);
+ //todo soya is this right?
RpcServer rpcServer = RaftRpcServerFactory.createRaftRpcServer(endpoint,
JRaftUtils.createExecutor(
"RAFT-RPC-",
@@ -172,16 +206,30 @@ public void shutdown() {
if (rpcServer == null) {
return;
}
- partitionEngines.forEach((k, v) -> {
- v.shutdown();
- });
+ closing.set(true);
+ heartbeatService.shutdown();
+ metricService.shutdown();
+ partitionEngines.values().stream().parallel().map(pe -> {
+ try {
+ Node raftNode = pe.getRaftNode();
+ if (raftNode.isLeader(false)) {
+ Status status = raftNode.transferLeadershipTo(PeerId.ANY_PEER);
+ if (!status.isOk()) {
+ log.warn("transfer leader error: {}", status);
+ }
+ }
+ } catch (Exception e) {
+ log.error("transfer leader error: ", e);
+ }
+ pe.shutdown();
+ businessHandler.closeDB(pe.getGroupId());
+ return true;
+ }).collect(Collectors.toList());
partitionEngines.clear();
rpcServer.shutdown();
// HgStoreEngine.init function check rpcServer whether is null, skipped if the instance
// exists even shut down.
rpcServer = null;
- heartbeatService.shutdown();
- metricService.shutdown();
// close all db session
RocksDBFactory.getInstance().releaseAllGraphDB();
}
@@ -246,17 +294,7 @@ public void rebuildRaftGroup(long storeId) {
if (partitions.size() > 0) {
var shards = pdProvider.getShardGroup(partId).getShardsList();
if (shards.stream().anyMatch(s -> s.getStoreId() == storeId)) {
- var peers = partitionManager.shards2Peers(shards);
- Configuration initConf = engine.getOptions().getConf();
- if (initConf == null) {
- engine.getOptions().setPeerList(peers);
- } else {
- peers.stream()
- .forEach(peer -> initConf.addPeer(JRaftUtils.getPeerId(peer)));
- }
-
- // engine.getOptions().getConf().setPeers();
- engine.restartRaftNode();
+ restartPartitionEngine(engine, shards);
}
}
} catch (PDException e) {
@@ -265,6 +303,41 @@ public void rebuildRaftGroup(long storeId) {
});
}
+ public void handleShardGroupOp(int groupId, List shards) {
+ log.info("handleShardGroupOp, groupId: {}, shards: {}", groupId, shards);
+
+ var engine = getPartitionEngine(groupId);
+
+ if (engine != null) {
+ if (shards.stream()
+ .anyMatch(s -> s.getStoreId() == partitionManager.getStore().getId())) {
+ restartPartitionEngine(engine, shards);
+ } else {
+ destroyPartitionEngine(groupId, List.copyOf(engine.getPartitions().keySet()));
+ engine.getPartitions().forEach((g, p) -> engine.removePartition(g));
+ engine.shutdown();
+ }
+ }
+ }
+
+ /**
+ * 使用新的configure 启动 partition engine,一般用来处理 raft addr变更,或者要手动调整某些分区
+ *
+ * @param engine partition engine
+ * @param shards shard list
+ */
+ private void restartPartitionEngine(PartitionEngine engine, List shards) {
+ var peers = partitionManager.shards2Peers(shards);
+ Configuration initConf = engine.getOptions().getConf();
+ if (initConf == null) {
+ engine.getOptions().setPeerList(peers);
+ } else {
+ peers.stream().forEach(peer -> initConf.addPeer(JRaftUtils.getPeerId(peer)));
+ }
+ // engine.getOptions().getConf().setPeers();
+ engine.restartRaftNode();
+ }
+
/**
* Create raft Node
*
@@ -282,22 +355,23 @@ public PartitionEngine createPartitionEngine(Partition partition, Configuration
return createPartitionEngine(partition.getId(), shardGroup, conf);
}
- private PartitionEngine createPartitionEngine(int groupId, ShardGroup shardGroup,
- Configuration conf) {
+ public PartitionEngine createPartitionEngine(int groupId, ShardGroup shardGroup,
+ Configuration conf) {
PartitionEngine engine;
if ((engine = partitionEngines.get(groupId)) == null) {
engineLocks.computeIfAbsent(groupId, k -> new Object());
synchronized (engineLocks.get(groupId)) {
- // Special cases during partition splitting (different number of graph partitions in the cluster) can cause the splitting partition not to be on this machine.
+ // Special cases during partition splitting (different number of graph partitions
+ // in the cluster) can cause the splitting partition not to be on this machine.
if (conf != null) {
var list = conf.listPeers();
list.addAll(conf.listLearners());
- if (!list.stream().anyMatch(
- p -> p.getEndpoint().toString().equals(options.getRaftAddress()))) {
- log.info(
- "raft {}, conf {} does not contains raft address:{}, skipped " +
- "create partition engine",
- groupId, conf, options.getRaftAddress());
+ if (!list.stream()
+ .anyMatch(p -> p.getEndpoint().toString()
+ .equals(options.getRaftAddress()))) {
+ log.info("raft {}, conf {} does not contains raft address:{}, skipped " +
+ "create partition engine", groupId, conf,
+ options.getRaftAddress());
return null;
}
} else {
@@ -341,7 +415,8 @@ private PartitionEngine createPartitionEngine(int groupId, ShardGroup shardGroup
}
/**
- * Create raft group, in addition to creating the local raft node, also need to notify other peers to create raft nodes.
+ * Create raft group, in addition to creating the local raft node, also need to notify other
+ * peers to create raft nodes.
* 1. Traverse partition.shards
* 2. Retrieve Store information based on storeId
* 3. Establish Raft RPC to other stores, send StartRaft messages.
@@ -365,14 +440,14 @@ public PartitionEngine createPartitionGroups(Partition partition) {
if (store == null || partitionManager.isLocalStore(store)) {
return;
}
- // Send messages to other peers, create raft groups. This is an asynchronous send.
+ // Send messages to other peers, create raft groups. This is an asynchronous
+ // send.
hgCmdClient.createRaftNode(store.getRaftAddress(), List.of(partition),
status -> {
- log.info(
- "send to {} createRaftNode rpc call " +
- "result {} partitionId {}",
- store.getRaftAddress(), status,
- partition.getId());
+ log.info("send to {} createRaftNode rpc call " +
+ "result {} partitionId {}",
+ store.getRaftAddress(), status,
+ partition.getId());
});
});
}
@@ -393,14 +468,10 @@ public void destroyPartitionGroups(Partition partition) {
}
// Send messages to other peers, create raft groups. This is an asynchronous send.
hgCmdClient.destroyRaftNode(store.getRaftAddress(),
- Arrays.asList(new Partition[]{partition}),
- status -> {
- log.info(
- "send to {} - {} DestroyRaftNode rpc call" +
- " result {}",
- store.getRaftAddress(), partition.getId(),
- status);
- });
+ Arrays.asList(new Partition[]{partition}), status -> {
+ log.info("send to {} - {} DestroyRaftNode rpc call" + " result {}",
+ store.getRaftAddress(), partition.getId(), status);
+ });
});
}
}
@@ -425,6 +496,8 @@ public synchronized void destroyPartitionEngine(Integer groupId, List gr
partitionEngines.remove(groupId);
// Delete the corresponding db folder
businessHandler.destroyGraphDB(graphNames.get(0), groupId);
+ // 删除 partition db location信息
+ getPartitionManager().getStoreMetadata().removePartitionStore(groupId);
} else {
graphNames.forEach(graphName -> {
businessHandler.dbCompaction(graphName, groupId);
@@ -517,8 +590,8 @@ public void setPartitionManager(PartitionManager ptm) {
this.partitionManager = ptm;
}
- public DataMover getDataMover() {
- return dataMover;
+ public DataManager getDataManager() {
+ return dataManager;
}
public PdProvider getPdProvider() {
@@ -569,9 +642,10 @@ public void addRaftTask(String graphName, Integer partId, RaftOperation operatio
Partition partition = partitionManager.findPartition(graphName, partId);
if (partition != null) {
engine = this.createPartitionGroups(partition);
- // May migrate, should not create, put in synchronize block, avoid subsequent ones.
+ // May migrate, should not create, put in synchronize block, avoid
+ // subsequent ones.
if (engine != null) {
- engine.waitForLeader(options.getWaitLeaderTimeout() * 1000);
+ engine.waitForLeader(options.getWaitLeaderTimeout() * 1000L);
}
}
}
@@ -580,7 +654,7 @@ public void addRaftTask(String graphName, Integer partId, RaftOperation operatio
if (engine != null) {
// Waiting for Leader
- Endpoint leader = engine.waitForLeader(options.getWaitLeaderTimeout() * 1000);
+ Endpoint leader = engine.waitForLeader(options.getWaitLeaderTimeout() * 1000L);
if (engine.isLeader()) {
engine.addRaftTask(operation, closure);
} else if (leader != null) {
@@ -588,7 +662,8 @@ public void addRaftTask(String graphName, Integer partId, RaftOperation operatio
Store store = partitionManager.getStoreByRaftEndpoint(engine.getShardGroup(),
leader.toString());
if (store.getId() == 0) {
- // Local store information for the Leader was not found, possibly the Partition has not been synchronized yet, reacquire from the Leader.
+ // Local store information for the Leader was not found, possibly the
+ // Partition has not been synchronized yet, reacquire from the Leader.
Store leaderStore = hgCmdClient.getStoreInfo(leader.toString());
store = leaderStore != null ? leaderStore : store;
log.error("getStoreByRaftEndpoint error store:{}, shard: {}, leader is {}",
@@ -670,57 +745,59 @@ public void onCompacted(String dbName) {
};
}
- class PartitionChangedListener implements PartitionManager.PartitionChangedListener {
+ public HgStoreEngineOptions getOption() {
+ return this.options;
+ }
- /**
- * Partition object changes, leader notifies other followers.
- */
- @Override
- public void onChanged(Partition partition) {
- PartitionEngine engine = getPartitionEngine(partition.getId());
+ /**
+ * Partition object changes, leader notifies other followers.
+ */
+ @Override
+ public void onChanged(Partition partition) {
+ PartitionEngine engine = getPartitionEngine(partition.getId());
- if (engine != null && engine.isLeader()) {
- try {
- engine.addRaftTask(RaftOperation.create(RaftOperation.SYNC_PARTITION,
- partition.getProtoObj()),
- new RaftClosure() {
- @Override
- public void run(Status status) {
- log.info(
- "Partition {}-{}-{} sync partition status " +
- "is {}",
- partition.getGraphName(), partition.getId(),
- partition.getWorkState(),
- status);
- }
- });
- } catch (IOException e) {
- log.error("Partition {}-{} sync partition exception {}",
- partition.getGraphName(), partition.getId(), e);
- }
+ if (engine != null && engine.isLeader()) {
+ try {
+ engine.addRaftTask(
+ RaftOperation.create(RaftOperation.SYNC_PARTITION, partition.getProtoObj()),
+ new RaftClosure() {
+ @Override
+ public void run(Status status) {
+ log.info("Partition {}-{}-{} sync partition status is {}",
+ partition.getGraphName(), partition.getId(),
+ partition.getWorkState(), status);
+ }
+ });
+ } catch (IOException e) {
+ log.error("Partition {}-{} sync partition exception {}", partition.getGraphName(),
+ partition.getId(), e);
}
}
+ }
- /**
- * Partition object key range, status changes, notify other followers by actively finding the leader.
- */
- @Override
- public UpdatePartitionResponse rangeOrStateChanged(UpdatePartitionRequest request) {
- UpdatePartitionResponse response = null;
- try {
- response = hgCmdClient.raftUpdatePartition(request);
-
- log.info("not leader request threadId:{} pId:{} range:{}-{} state:{} response:{}",
- Thread.currentThread().getId(), request.getPartitionId(),
- request.getStartKey(),
- request.getEndKey(), request.getWorkState(), response.getStatus());
+ /**
+ * Partition object key range, status changes, notify other followers by actively finding the
+ * leader.
+ */
+ @Override
+ public UpdatePartitionResponse rangeOrStateChanged(UpdatePartitionRequest request) {
+ UpdatePartitionResponse response = null;
+ try {
+ response = hgCmdClient.raftUpdatePartition(request);
- } catch (Exception e) {
- e.printStackTrace();
- }
+ log.info("not leader request threadId:{} pId:{} range:{}-{} state:{} response:{}",
+ Thread.currentThread().getId(), request.getPartitionId(),
+ request.getStartKey(), request.getEndKey(), request.getWorkState(),
+ response.getStatus());
- return response;
+ } catch (Exception e) {
+ e.printStackTrace();
}
+ return response;
+ }
+
+ public AtomicBoolean isClosing() {
+ return closing;
}
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreStateListener.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreStateListener.java
index cf8ce3904e..9b31dff712 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreStateListener.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/HgStoreStateListener.java
@@ -20,6 +20,7 @@
import org.apache.hugegraph.pd.grpc.Metapb;
import org.apache.hugegraph.store.meta.Store;
+@Deprecated
public interface HgStoreStateListener {
void stateChanged(Store store, Metapb.StoreState oldState, Metapb.StoreState newState);
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionEngine.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionEngine.java
index ee65162f7c..6f6cdb2184 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionEngine.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionEngine.java
@@ -29,34 +29,43 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.collections.ListUtils;
+import org.apache.commons.collections.SetUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.pd.grpc.MetaTask;
import org.apache.hugegraph.pd.grpc.Metapb;
-import org.apache.hugegraph.store.cmd.BatchPutRequest;
-import org.apache.hugegraph.store.cmd.CleanDataRequest;
-import org.apache.hugegraph.store.cmd.DbCompactionRequest;
+import org.apache.hugegraph.store.business.BusinessHandler;
+import org.apache.hugegraph.store.business.BusinessHandlerImpl;
import org.apache.hugegraph.store.cmd.HgCmdClient;
-import org.apache.hugegraph.store.cmd.UpdatePartitionRequest;
+import org.apache.hugegraph.store.cmd.request.BatchPutRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.cmd.request.DbCompactionRequest;
+import org.apache.hugegraph.store.cmd.request.UpdatePartitionRequest;
+import org.apache.hugegraph.store.listener.PartitionStateListener;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.meta.PartitionManager;
import org.apache.hugegraph.store.meta.Shard;
import org.apache.hugegraph.store.meta.ShardGroup;
import org.apache.hugegraph.store.meta.Store;
import org.apache.hugegraph.store.meta.TaskManager;
+import org.apache.hugegraph.store.options.HgStoreEngineOptions;
import org.apache.hugegraph.store.options.PartitionEngineOptions;
-import org.apache.hugegraph.store.raft.HgStoreStateMachine;
+import org.apache.hugegraph.store.raft.DefaultRaftClosure;
+import org.apache.hugegraph.store.raft.PartitionStateMachine;
import org.apache.hugegraph.store.raft.RaftClosure;
import org.apache.hugegraph.store.raft.RaftOperation;
import org.apache.hugegraph.store.raft.RaftStateListener;
import org.apache.hugegraph.store.raft.RaftTaskHandler;
import org.apache.hugegraph.store.raft.util.RaftUtils;
-import org.apache.hugegraph.store.snapshot.HgSnapshotHandler;
+import org.apache.hugegraph.store.snapshot.SnapshotHandler;
import org.apache.hugegraph.store.util.FutureClosure;
import org.apache.hugegraph.store.util.HgRaftError;
import org.apache.hugegraph.store.util.HgStoreException;
@@ -81,12 +90,12 @@
import com.alipay.sofa.jraft.storage.impl.RocksDBLogStorage;
import com.alipay.sofa.jraft.storage.log.RocksDBSegmentLogStorage;
import com.alipay.sofa.jraft.util.Endpoint;
-import com.alipay.sofa.jraft.util.SystemPropertyUtil;
import com.alipay.sofa.jraft.util.ThreadId;
import com.alipay.sofa.jraft.util.Utils;
import com.alipay.sofa.jraft.util.internal.ThrowUtil;
import com.google.protobuf.CodedInputStream;
+import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
/**
@@ -105,25 +114,15 @@ public class PartitionEngine implements Lifecycle, RaftS
private final AtomicBoolean changingPeer;
private final AtomicBoolean snapshotFlag;
private final Object leaderChangedEvent = "leaderChangedEvent";
- /**
- * Default value size threshold to decide whether it will be stored in segments or rocksdb,
- * default is 4K.
- * When the value size is less than 4K, it will be stored in rocksdb directly.
- */
- private final int DEFAULT_VALUE_SIZE_THRESHOLD = SystemPropertyUtil.getInt(
- "jraft.log_storage.segment.value.threshold.bytes", 4 * 1024);
- /**
- * Default checkpoint interval in milliseconds.
- */
- private final int DEFAULT_CHECKPOINT_INTERVAL_MS = SystemPropertyUtil.getInt(
- "jraft.log_storage.segment.checkpoint.interval.ms", 5000);
private PartitionEngineOptions options;
- private HgStoreStateMachine stateMachine;
+ private PartitionStateMachine stateMachine;
+ @Getter
private RaftGroupService raftGroupService;
private TaskManager taskManager;
+ private SnapshotHandler snapshotHandler;
private Node raftNode;
- private boolean started;
+ private volatile boolean started;
public PartitionEngine(HgStoreEngine storeEngine, ShardGroup shardGroup) {
this.storeEngine = storeEngine;
@@ -182,8 +181,8 @@ public synchronized boolean init(PartitionEngineOptions opts) {
log.info("PartitionEngine starting: {}", this);
this.taskManager = new TaskManager(storeEngine.getBusinessHandler(), opts.getGroupId());
- HgSnapshotHandler snapshotHandler = new HgSnapshotHandler(this);
- this.stateMachine = new HgStoreStateMachine(opts.getGroupId(), snapshotHandler);
+ this.snapshotHandler = new SnapshotHandler(this);
+ this.stateMachine = new PartitionStateMachine(opts.getGroupId(), snapshotHandler);
// probably null in test case
if (opts.getTaskHandler() != null) {
this.stateMachine.addTaskHandler(opts.getTaskHandler());
@@ -219,6 +218,7 @@ public synchronized boolean init(PartitionEngineOptions opts) {
nodeOptions.setSharedVoteTimer(true);
nodeOptions.setFilterBeforeCopyRemote(true);
+ HgStoreEngineOptions.RaftOptions raft = options.getRaftOptions();
nodeOptions.setServiceFactory(new DefaultJRaftServiceFactory() {
@Override
public LogStorage createLogStorage(final String uri, final RaftOptions raftOptions) {
@@ -231,27 +231,25 @@ public LogStorage createLogStorage(final String uri, final RaftOptions raftOptio
});
// Initial cluster
nodeOptions.setInitialConf(initConf);
- // Snapshot interval
- nodeOptions.setSnapshotIntervalSecs(options.getRaftOptions().getSnapshotIntervalSecs());
+ // 快照时间间隔
+ nodeOptions.setSnapshotIntervalSecs(raft.getSnapshotIntervalSecs());
+ //todo soya fix
+ //nodeOptions.setSnapShotDownloadingThreads(raft.getSnapshotDownloadingThreads());
//nodeOptions.setSnapshotLogIndexMargin(options.getRaftOptions()
// .getSnapshotLogIndexMargin());
- nodeOptions.setRpcConnectTimeoutMs(options.getRaftOptions().getRpcConnectTimeoutMs());
- nodeOptions.setRpcDefaultTimeout(options.getRaftOptions().getRpcDefaultTimeout());
- nodeOptions.setRpcInstallSnapshotTimeout(
- options.getRaftOptions().getRpcInstallSnapshotTimeout());
- nodeOptions.setElectionTimeoutMs(options.getRaftOptions().getElectionTimeoutMs());
+ nodeOptions.setRpcConnectTimeoutMs(raft.getRpcConnectTimeoutMs());
+ nodeOptions.setRpcDefaultTimeout(raft.getRpcDefaultTimeout());
+ nodeOptions.setRpcInstallSnapshotTimeout(raft.getRpcInstallSnapshotTimeout());
+ nodeOptions.setElectionTimeoutMs(raft.getElectionTimeoutMs());
// Set raft configuration
RaftOptions raftOptions = nodeOptions.getRaftOptions();
- raftOptions.setDisruptorBufferSize(options.getRaftOptions().getDisruptorBufferSize());
- raftOptions.setMaxEntriesSize(options.getRaftOptions().getMaxEntriesSize());
- raftOptions.setMaxReplicatorInflightMsgs(
- options.getRaftOptions().getMaxReplicatorInflightMsgs());
+ raftOptions.setDisruptorBufferSize(raft.getDisruptorBufferSize());
+ raftOptions.setMaxEntriesSize(raft.getMaxEntriesSize());
+ raftOptions.setMaxReplicatorInflightMsgs(raft.getMaxReplicatorInflightMsgs());
raftOptions.setMaxByteCountPerRpc(1024 * 1024);
- raftOptions.setMaxBodySize(options.getRaftOptions().getMaxBodySize());
nodeOptions.setEnableMetrics(true);
-
final PeerId serverId = JRaftUtils.getPeerId(options.getRaftAddress());
// Build raft group and start raft
@@ -310,125 +308,133 @@ public Status changePeers(List peers, final Closure done) {
// Check the peer that needs to be added.
List addPeers = ListUtils.removeAll(peers, oldPeers);
// learner to be deleted. Possible peer change.
- List removedPeers = ListUtils.removeAll(RaftUtils.getLearnerEndpoints(raftNode),
- peers);
+ List removedPeers = ListUtils.removeAll(oldPeers, peers);
HgCmdClient rpcClient = storeEngine.getHgCmdClient();
// Generate a new Configuration object
+
Configuration oldConf = getCurrentConf();
Configuration conf = oldConf.copy();
- if (!addPeers.isEmpty()) {
- addPeers.forEach(peer -> {
- conf.addLearner(JRaftUtils.getPeerId(peer));
- });
- doSnapshot((RaftClosure) status -> {
- log.info("Raft {} snapshot before add learner, result:{}", getGroupId(), status);
- });
+ FutureClosure closure;
- FutureClosure closure = new FutureClosure(addPeers.size());
- addPeers.forEach(peer -> Utils.runInThread(() -> {
- // 1. Create a new peer's raft object
+ if (!addPeers.isEmpty()) {
+ addPeers.forEach(peer -> conf.addLearner(JRaftUtils.getPeerId(peer)));
+ doSnapshot(status -> log.info("Raft {} snapshot before add learner, result:{}",
+ getGroupId(), status));
+ // 2.1 learner 加入 raft group
+ for (var peer : addPeers) {
+ closure = new FutureClosure();
rpcClient.createRaftNode(peer, partitionManager.getPartitionList(getGroupId()),
- conf, status -> {
- closure.run(status);
- if (!status.isOk()) {
- log.error("Raft {} add node {} error {}",
- options.getGroupId(), peer, status);
- }
- });
- }));
- closure.get();
- } else {
- // 3. Check if learner has completed snapshot synchronization
- boolean snapshotOk = true;
- for (PeerId peerId : raftNode.listLearners()) {
- Replicator.State state = getReplicatorState(peerId);
- if (state == null || state != Replicator.State.Replicate) {
- snapshotOk = false;
- break;
+ conf, closure);
+ var status = closure.get();
+ if (!status.isOk()) {
+ log.info("Raft {} createRaftNode, peer:{}, reason:{}", getGroupId(), peer,
+ status.getErrorMsg());
+ return status;
}
- log.info("Raft {} {} getReplicatorState {}", getGroupId(), peerId, state);
}
- if (snapshotOk && !conf.listLearners().isEmpty()) {
- // 4. Delete learner, rejoin as peer
- FutureClosure closure = new FutureClosure();
- raftNode.removeLearners(conf.listLearners(), closure);
- if (closure.get().isOk()) {
- conf.listLearners().forEach(peerId -> {
- conf.addPeer(peerId);
- conf.removeLearner(peerId);
- });
- result = Status.OK();
- } else {
- // Failed, retrying
- result = HgRaftError.TASK_ERROR.toStatus();
- }
- } else if (snapshotOk) {
- result = Status.OK(); // No learner, indicating only delete operations are performed.
+
+ closure = new FutureClosure();
+ raftNode.changePeers(conf, closure);
+ var status = closure.get();
+ if (!status.isOk()) {
+ log.info("Raft {} changePeers failed, reason:{}", getGroupId(),
+ status.getErrorMsg());
+ return status;
}
- }
- if (result.isOk()) {
- // Sync completed, delete old peer
- removedPeers.addAll(ListUtils.removeAll(oldPeers, peers));
- // Check if leader is deleted, if so, perform leader migration first.
- if (removedPeers.contains(
- this.getRaftNode().getNodeId().getPeerId().getEndpoint().toString())) {
-
- log.info("Raft {} leader is removed, needs to transfer leader {}, conf: {}",
- getGroupId(), peers, conf);
- // only one (that's leader self), should add peer first
- if (raftNode.listPeers().size() == 1) {
- FutureClosure closure = new FutureClosure();
- raftNode.changePeers(conf, closure);
- log.info("Raft {} change peer result:{}", getGroupId(), closure.get());
+
+ // 2.2 等待 learner 完成快照同步 (check added learner)
+ boolean allLearnerSnapshotOk = false;
+ long current = System.currentTimeMillis();
+ while (!allLearnerSnapshotOk) {
+ boolean snapshotOk = true;
+ for (var peerId : addPeers) {
+ var state = getReplicatorState(JRaftUtils.getPeerId(peerId));
+ log.info("Raft {}, peer:{}, replicate state:{}", getGroupId(), peerId, state);
+ if (state != Replicator.State.Replicate) {
+ snapshotOk = false;
+ }
+ }
+ allLearnerSnapshotOk = snapshotOk;
+
+ if (!allLearnerSnapshotOk) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ log.warn("Raft {} sleep when check learner snapshot", getGroupId());
+ }
}
+ if (System.currentTimeMillis() - current > 600 * 1000) {
+ return HgRaftError.TASK_CONTINUE.toStatus();
+ }
+ }
+
+ log.info("Raft {} replicate status is OK", getGroupId());
- var status = this.raftNode.transferLeadershipTo(PeerId.ANY_PEER);
- log.info("Raft {} transfer leader status : {}", getGroupId(), status);
- // Need to resend the command to the new leader
+ closure = new FutureClosure();
+ // 2.3 change learner to follower (first remove, then add follower)
+ raftNode.removeLearners(conf.listLearners(), closure);
+ if (!closure.get().isOk()) {
+ log.error("Raft {} remove learner error, result:{}", getGroupId(), status);
+ return HgRaftError.TASK_ERROR.toStatus();
+ }
+
+ addPeers.forEach(peer -> {
+ conf.removeLearner(JRaftUtils.getPeerId(peer));
+ conf.addPeer(JRaftUtils.getPeerId(peer));
+ });
+
+ // add follower
+ closure = new FutureClosure();
+ raftNode.changePeers(conf, closure);
+ if (!closure.get().isOk()) {
+ log.error("Raft {} changePeers error, result:{}", getGroupId(), status);
return HgRaftError.TASK_ERROR.toStatus();
}
}
+ boolean removeSelf = false;
+ // case 3:
if (!removedPeers.isEmpty()) {
- removedPeers.forEach(peer -> {
+ var self = this.getRaftNode().getNodeId().getPeerId().getEndpoint().toString();
+ removeSelf = removedPeers.contains(self);
+ // 3.1 remove peers
+ List toDestroy = new ArrayList<>();
+ for (var peer : removedPeers) {
+ if (Objects.equals(peer, self)) {
+ continue;
+ }
conf.removeLearner(JRaftUtils.getPeerId(peer));
conf.removePeer(JRaftUtils.getPeerId(peer));
- });
- }
+ toDestroy.add(peer);
+ }
- if (!RaftUtils.configurationEquals(oldConf, conf)) {
- // 2. The new peer joins as a learner.
- // 5. peer switching, add new peer, delete old peer
- FutureClosure closure = new FutureClosure();
+ closure = new FutureClosure();
raftNode.changePeers(conf, closure);
- if (closure.get().isOk()) {
- if (!removedPeers.isEmpty()) {
- removedPeers.forEach(peer -> Utils.runInThread(() -> {
- // 6. Stop the deleted peer
- rpcClient.destroyRaftNode(peer,
- partitionManager.getPartitionList(getGroupId()),
- status -> {
- if (!status.isOk()) {
- // TODO: What if it fails?
- log.error("Raft {} destroy node {}" +
- " error {}",
- options.getGroupId(), peer,
- status);
- }
- });
- }));
- }
+ var status = closure.get();
+
+ if (!status.isOk()) {
+ log.error("Raft {} changePeers error after destroy, result:{}", getGroupId(),
+ status);
+ return HgRaftError.TASK_ERROR.toStatus();
} else {
- // Failed, retrying
- result = HgRaftError.TASK_ERROR.toStatus();
+ for (var peer : toDestroy) {
+ closure = new FutureClosure();
+ rpcClient.destroyRaftNode(peer, partitionManager.getPartitionList(getGroupId()),
+ closure);
+ log.info("Raft {} destroy raft node {}, result:{}", peer, getGroupId(),
+ closure.get());
+ }
+ }
+
+ // transfer leadership to any peer
+ if (removeSelf) {
+ raftNode.transferLeadershipTo(PeerId.ANY_PEER);
}
- log.info("Raft {} changePeers result {}, conf is {}",
- getRaftNode().getGroupId(), closure.get(), conf);
}
- log.info("Raft {} changePeers end. {}, result is {}", getGroupId(), peers, result);
- return result;
+
+ return removeSelf ? HgRaftError.TASK_CONTINUE.toStatus() : HgRaftError.OK.toStatus();
}
public void addRaftTask(RaftOperation operation, RaftClosure closure) {
@@ -438,7 +444,7 @@ public void addRaftTask(RaftOperation operation, RaftClosure closure) {
}
final Task task = new Task();
task.setData(ByteBuffer.wrap(operation.getValues()));
- task.setDone(new HgStoreStateMachine.RaftClosureAdapter(operation, closure));
+ task.setDone(new DefaultRaftClosure(operation, closure));
this.raftNode.apply(task);
}
@@ -447,9 +453,6 @@ public void shutdown() {
if (!this.started) {
return;
}
-
- partitionManager.updateShardGroup(shardGroup);
-
if (this.raftGroupService != null) {
this.raftGroupService.shutdown();
try {
@@ -521,8 +524,8 @@ public void addStateListener(PartitionStateListener listener) {
public Map getAlivePeers() {
Map peers = new HashMap<>();
raftNode.listAlivePeers().forEach(peerId -> {
- Shard shard = partitionManager.getShardByRaftEndpoint(shardGroup,
- peerId.getEndpoint().toString());
+ Shard shard = partitionManager.getShardByEndpoint(shardGroup,
+ peerId.getEndpoint().toString());
if (shard != null) {
peers.put(shard.getStoreId(), peerId);
}
@@ -629,7 +632,9 @@ public void onStartFollowing(final PeerId newLeaderId, final long newTerm) {
*/
@Override
public void onConfigurationCommitted(Configuration conf) {
-
+ if (storeEngine.isClosing().get()) {
+ return;
+ }
try {
// Update shardlist
log.info("Raft {} onConfigurationCommitted, conf is {}", getGroupId(), conf.toString());
@@ -661,10 +666,19 @@ public void onConfigurationCommitted(Configuration conf) {
// partitionManager.changeShards(partition, shardGroup.getMetaPbShard());
// });
try {
- var pdGroup = storeEngine.getPdProvider().getShardGroup(getGroupId());
+ var pdGroup = storeEngine.getPdProvider().getShardGroupDirect(getGroupId());
List peers = partitionManager.shards2Peers(pdGroup.getShardsList());
- if (!ListUtils.isEqualList(peers, RaftUtils.getPeerEndpoints(raftNode))) {
+ Long leaderStoreId = null;
+ for (var shard : pdGroup.getShardsList()) {
+ if (shard.getRole() == Metapb.ShardRole.Leader) {
+ leaderStoreId = shard.getStoreId();
+ }
+ }
+ // leader 不同,peers 不同,learner 不同,都要更新 pd 信息
+ if (!SetUtils.isEqualSet(peers, RaftUtils.getPeerEndpoints(raftNode)) ||
+ !SetUtils.isEqualSet(learners, RaftUtils.getLearnerEndpoints(raftNode)) ||
+ !Objects.equals(leaderStoreId, partitionManager.getStore().getId())) {
partitionManager.getPdProvider().updateShardGroup(shardGroup.getProtoObj());
}
@@ -738,99 +752,46 @@ public Status transferLeader(String graphName, Metapb.Shard shard) {
* 4. After the snapshot synchronization is completed, call changePeers, change the learner to follower, and delete the old peer.
*/
public void doChangeShard(final MetaTask.Task task, Closure done) {
- if (!isLeader()) {
- return;
- }
+ try {
+ if (!isLeader() || !changingPeer.compareAndSet(false, true)) {
+ return;
+ }
- log.info("Raft {} doChangeShard task is {}", getGroupId(), task);
- // If the same partition has the same task executing, ignore task execution.
- if (taskManager.partitionTaskRepeat(task.getPartition().getId(),
- task.getPartition().getGraphName(),
- task.getType().name())) {
- log.error("Raft {} doChangeShard task repeat, type:{}", getGroupId(), task.getType());
- return;
- }
- // Task not completed, repeat execution.
- if (task.getState().getNumber() < MetaTask.TaskState.Task_Stop_VALUE && isLeader()) {
+ log.info("Raft {} doChangeShard task is {}", getGroupId(), task);
Utils.runInThread(() -> {
+ List peers =
+ partitionManager.shards2Peers(task.getChangeShard().getShardList());
+ HashSet hashSet = new HashSet<>(peers);
+
try {
- // cannot changePeers in the state machine
- List peers =
- partitionManager.shards2Peers(task.getChangeShard().getShardList());
- HashSet hashSet = new HashSet<>(peers);
- // Task has the same peers, indicating there is an error in the task itself, task ignored
+ // 任务中有相同的 peers,说明任务本身有错误,任务忽略
if (peers.size() != hashSet.size()) {
- log.info("Raft {} doChangeShard peer is repeat, peers: {}", getGroupId(),
+ log.info("Raft {} doChangeShard peer is repeat, peers:{}", getGroupId(),
peers);
+ return;
}
- Status result;
- if (changingPeer.compareAndSet(false, true)) {
- result = this.changePeers(peers, done);
- } else {
- result = HgRaftError.TASK_ERROR.toStatus();
- }
-
- if (result.getCode() != HgRaftError.TASK_CONTINUE.getNumber()) {
- log.info("Raft {} doChangeShard is finished, status is {}", getGroupId(),
- result);
- // Task completed, synchronize task status
- MetaTask.Task newTask;
- if (result.isOk()) {
- newTask = task.toBuilder().setState(MetaTask.TaskState.Task_Success)
- .build();
- } else {
- log.warn(
- "Raft {} doChangeShard is failure, need to retry, status is {}",
- getGroupId(), result);
- try {
- // Reduce send times
- Thread.sleep(1000);
- } catch (Exception e) {
- log.error("wait 1s to resend retry task. got error:{}",
- e.getMessage());
- }
- newTask = task.toBuilder().setState(MetaTask.TaskState.Task_Ready)
- .build();
- }
- try {
- // During the waiting process, it may have already shut down.
- if (isLeader()) {
- storeEngine.addRaftTask(newTask.getPartition().getGraphName(),
- newTask.getPartition().getId(),
- RaftOperation.create(
- RaftOperation.SYNC_PARTITION_TASK,
- newTask),
- status -> {
- if (!status.isOk()) {
- log.error(
- "Raft {} addRaftTask " +
- "error, status is {}",
- newTask.getPartition()
- .getId(), status);
- }
- }
- );
- }
- } catch (Exception e) {
- log.error("Partition {}-{} update task state exception {}",
- task.getPartition().getGraphName(),
- task.getPartition().getId(), e);
- }
- // db might have been destroyed, do not update anymore
- if (this.started) {
- taskManager.updateTask(newTask);
- }
- } else {
- log.info("Raft {} doChangeShard not finished", getGroupId());
+ Status result = changePeers(peers, null);
+
+ if (result.getCode() == HgRaftError.TASK_CONTINUE.getNumber()) {
+ // 需要重新发送一个 request
+ storeEngine.addRaftTask(task.getPartition().getGraphName(),
+ task.getPartition().getId(), RaftOperation.create(
+ RaftOperation.SYNC_PARTITION_TASK, task), status -> {
+ if (!status.isOk()) {
+ log.error(
+ "Raft {} addRaftTask error, " + "status " + "is {}",
+ task.getPartition().getId(), status);
+ }
+ });
}
+ log.info("Raft {} doChangeShard result is {}", getGroupId(), result);
} catch (Exception e) {
log.error("Raft {} doChangeShard exception {}", getGroupId(), e);
} finally {
changingPeer.set(false);
}
});
- } else {
- // Whether the message has been processed
+ } finally {
if (done != null) {
done.run(Status.OK());
}
@@ -917,7 +878,7 @@ private Status handleSplitTask(MetaTask.Task task) {
storeEngine.createPartitionGroups(new Partition(newPartitions.get(i)));
}
// Copy data from the source machine to the target machine
- status = storeEngine.getDataMover().moveData(task.getPartition(), newPartitions);
+ status = storeEngine.getDataManager().move(task.getPartition(), newPartitions);
if (status.isOk()) {
var source = Metapb.Partition.newBuilder(targets.get(0))
@@ -925,7 +886,7 @@ private Status handleSplitTask(MetaTask.Task task) {
.build();
// Update local key range, and synchronize follower
partitionManager.updatePartition(source, true);
- storeEngine.getDataMover().updatePartitionRange(source,
+ partitionManager.updateRange(source,
(int) source.getStartKey(),
(int) source.getEndKey());
}
@@ -955,7 +916,7 @@ private Status handleMoveTask(MetaTask.Task task) {
task.getPartition().getGraphName(),
task.getPartition().getId(),
task.getMovePartition().getTargetPartition().getId());
- status = storeEngine.getDataMover().moveData(task.getPartition(),
+ status = storeEngine.getDataManager().move(task.getPartition(),
task.getMovePartition()
.getTargetPartition());
} catch (Exception e) {
@@ -1051,7 +1012,7 @@ private void handleCleanOp(CleanDataRequest request) {
partitionManager.getPartition(request.getGraphName(), request.getPartitionId());
if (partition != null) {
- storeEngine.getDataMover().doCleanData(request);
+ storeEngine.getDataManager().clean(request);
storeEngine.getBusinessHandler()
.dbCompaction(partition.getGraphName(), partition.getId());
@@ -1087,6 +1048,99 @@ private void handleCleanOp(CleanDataRequest request) {
}
}
+ public void buildIndex(MetaTask.Task task) {
+
+ var state = MetaTask.TaskState.Task_Failure;
+ String message = "SUCCESS";
+ try {
+ var status = storeEngine.getDataManager().doBuildIndex(task.getBuildIndex().getParam(),
+ task.getPartition());
+ if (status.isOk()) {
+ state = MetaTask.TaskState.Task_Success;
+ } else {
+ message = status.getErrorMsg();
+ }
+
+ } catch (Exception e) {
+ message = e.getMessage() == null ? "UNKNOWN" : e.getMessage();
+ log.error("build index error:", e);
+ }
+
+ try {
+ partitionManager.reportTask(
+ task.toBuilder().setState(state).setMessage(message).build());
+ } catch (Exception e) {
+ log.error("report task failed: error :", e);
+ }
+
+ }
+
+ public void doSnapshotSync(Closure done) {
+ long lastIndex = raftNode.getLastAppliedLogIndex();
+ BusinessHandler handler = storeEngine.getBusinessHandler();
+ Integer groupId = getGroupId();
+ String lockPath = handler.getLockPath(groupId);
+ AtomicInteger state = handler.getState(groupId);
+ if (state != null && state.get() == BusinessHandler.compactionDone) {
+ log.info("Partition {},path:{} prepare to doSnapshotSync", this.getGroupId(), lockPath);
+ BusinessHandlerImpl.getCompactionPool().execute(() -> {
+ try {
+ long start = System.currentTimeMillis();
+ while ((System.currentTimeMillis() - start) < 5000 &&
+ raftNode.getLastAppliedLogIndex() == lastIndex) {
+ synchronized (state) {
+ state.wait(200);
+ }
+ }
+ log.info("Partition {},path:{} begin to doSnapshotSync", this.getGroupId(),
+ lockPath);
+ //todo soya may have problem
+ //raftNode.getRaftOptions().setTruncateLog(true);
+ CountDownLatch latch = new CountDownLatch(1);
+ AtomicReference result = new AtomicReference<>();
+ raftNode.snapshot(status -> {
+ result.set(status);
+ try {
+ //todo soya may have problem
+ //raftNode.getRaftOptions().setTruncateLog(false);
+ latch.countDown();
+ log.info("Partition {},path: {} doSnapshotSync result : {}. ", groupId,
+ lockPath, status);
+ } catch (Exception e) {
+ log.error("wait doSnapshotSync with error:", e);
+ } finally {
+ handler.setAndNotifyState(groupId, BusinessHandler.compactionCanStart);
+ handler.unlock(lockPath);
+ log.info("Partition {},path: {} release dbCompaction lock", groupId,
+ lockPath);
+ }
+ });
+ latch.await();
+ } catch (Exception e) {
+ log.error("doSnapshotSync with error:", e);
+ handler.setAndNotifyState(groupId, BusinessHandler.compactionCanStart);
+ handler.unlock(lockPath);
+ }
+ });
+ }
+ if (done != null) {
+ done.run(Status.OK());
+ }
+ }
+
+ public void doBlankTaskSync(Closure done) {
+ try {
+ doSnapshotSync(done);
+ } catch (Exception e) {
+ Integer groupId = getGroupId();
+ // String msg = String.format("Partition %s blank task done with error:", groupId);
+ //log.error(msg, e);
+ if (done != null) {
+ done.run(new Status(-1, e.getMessage()));
+ }
+ }
+ }
+
public Configuration getCurrentConf() {
return new Configuration(this.raftNode.listPeers(), this.raftNode.listLearners());
}
@@ -1192,7 +1246,9 @@ public boolean invoke(final int groupId, byte[] request,
invoke(groupId, methodId, Metapb.Partition.parseFrom(input), response);
break;
case RaftOperation.DO_SNAPSHOT:
+ case RaftOperation.DO_SYNC_SNAPSHOT:
case RaftOperation.BLANK_TASK:
+ case RaftOperation.SYNC_BLANK_TASK:
invoke(groupId, methodId, null, response);
break;
case RaftOperation.IN_WRITE_OP:
@@ -1236,7 +1292,7 @@ public boolean invoke(final int groupId, byte methodId, Object req,
doSnapshot(response);
break;
case RaftOperation.IN_WRITE_OP:
- storeEngine.getDataMover().doWriteData((BatchPutRequest) (req));
+ storeEngine.getDataManager().write((BatchPutRequest) (req));
break;
case RaftOperation.IN_CLEAN_OP:
handleCleanOp((CleanDataRequest) req);
@@ -1253,6 +1309,12 @@ public boolean invoke(final int groupId, byte methodId, Object req,
dbCompactionRequest.getPartitionId(),
dbCompactionRequest.getTableName());
break;
+ case RaftOperation.DO_SYNC_SNAPSHOT:
+ doSnapshotSync(response);
+ break;
+ case RaftOperation.SYNC_BLANK_TASK:
+ doBlankTaskSync(response);
+ break;
default:
return false;
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionInstructionProcessor.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionInstructionProcessor.java
index 65830b7ba8..a57fadea84 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionInstructionProcessor.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionInstructionProcessor.java
@@ -17,7 +17,6 @@
package org.apache.hugegraph.store;
-import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
@@ -36,8 +35,8 @@
import org.apache.hugegraph.pd.grpc.pulse.PartitionKeyRange;
import org.apache.hugegraph.pd.grpc.pulse.SplitPartition;
import org.apache.hugegraph.pd.grpc.pulse.TransferLeader;
-import org.apache.hugegraph.store.cmd.CleanDataRequest;
-import org.apache.hugegraph.store.cmd.DbCompactionRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.cmd.request.DbCompactionRequest;
import org.apache.hugegraph.store.meta.MetadataKeyHelper;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.pd.PartitionInstructionListener;
@@ -53,6 +52,7 @@
/**
* PD sends partition instruction processor to Store
*/
+@Deprecated
public class PartitionInstructionProcessor implements PartitionInstructionListener {
private static final Logger LOG = Log.logger(PartitionInstructionProcessor.class);
@@ -309,9 +309,6 @@ public void onPartitionKeyRangeChanged(long taskId, Partition partition,
});
LOG.info("onPartitionKeyRangeChanged: {}, update to pd", newPartition);
partitionManager.updatePartitionToPD(List.of(newPartition));
- } catch (IOException e) {
- LOG.error("Partition {}-{} onPartitionKeyRangeChanged exception {}",
- newPartition.getGraphName(), newPartition.getId(), e);
} catch (PDException e) {
throw new RuntimeException(e);
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionStateListener.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionStateListener.java
index ad73f95e8a..349ddc3812 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionStateListener.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/PartitionStateListener.java
@@ -22,7 +22,7 @@
import org.apache.hugegraph.pd.grpc.Metapb;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.meta.PartitionRole;
-
+@Deprecated
public interface PartitionStateListener {
// Partition role changed
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/AbstractSelectIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/AbstractSelectIterator.java
index 88c71dc9a9..353464f8c6 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/AbstractSelectIterator.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/AbstractSelectIterator.java
@@ -17,14 +17,10 @@
package org.apache.hugegraph.store.business;
-import org.apache.hugegraph.backend.serializer.AbstractSerializer;
-import org.apache.hugegraph.backend.serializer.BinarySerializer;
-import org.apache.hugegraph.backend.store.BackendEntry;
-import org.apache.hugegraph.iterator.CIter;
+import org.apache.hugegraph.backend.BackendColumn;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
-import org.apache.hugegraph.structure.HugeElement;
-import org.apache.hugegraph.util.Bytes;
-import org.apache.tinkerpop.gremlin.structure.Edge;
+import org.apache.hugegraph.serializer.BinaryElementSerializer;
+import org.apache.hugegraph.structure.BaseElement;
import lombok.extern.slf4j.Slf4j;
@@ -32,36 +28,17 @@
public abstract class AbstractSelectIterator implements ScanIterator {
protected ScanIterator iterator;
- protected AbstractSerializer serializer;
+ protected BinaryElementSerializer serializer;
public AbstractSelectIterator() {
- this.serializer = new BinarySerializer();
+ this.serializer = new BinaryElementSerializer();
}
- public boolean belongToMe(BackendEntry entry,
- BackendEntry.BackendColumn column) {
- return Bytes.prefixWith(column.name, entry.id().asBytes());
- }
-
- public HugeElement parseEntry(BackendEntry entry, boolean isVertex) {
- try {
- if (isVertex) {
- return this.serializer.readVertex(null, entry);
- } else {
- CIter itr =
- this.serializer.readEdges(null, entry);
-
- // Iterator itr = this.serializer.readEdges(
- // null, entry, true, false).iterator();
- HugeElement el = null;
- if (itr.hasNext()) {
- el = (HugeElement) itr.next();
- }
- return el;
- }
- } catch (Exception e) {
- log.error("Failed to parse entry: {}", entry, e);
- throw e;
+ public BaseElement parseEntry(BackendColumn column, boolean isVertex) {
+ if (isVertex) {
+ return serializer.parseVertex(null, column, null);
+ } else {
+ return serializer.parseEdge(null, column, null, true);
}
}
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandler.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandler.java
index 824d4ada77..31b336b735 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandler.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandler.java
@@ -19,6 +19,9 @@
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -26,13 +29,16 @@
import org.apache.hugegraph.pd.grpc.pulse.CleanType;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.store.constant.HugeServerTables;
import org.apache.hugegraph.store.grpc.Graphpb;
import org.apache.hugegraph.store.grpc.common.Key;
import org.apache.hugegraph.store.grpc.common.OpType;
+import org.apache.hugegraph.store.grpc.query.DeDupOption;
import org.apache.hugegraph.store.grpc.session.BatchEntry;
import org.apache.hugegraph.store.meta.base.DBSessionBuilder;
import org.apache.hugegraph.store.metric.HgStoreMetric;
-import org.apache.hugegraph.store.raft.HgStoreStateMachine;
+import org.apache.hugegraph.store.query.QueryTypeParam;
+import org.apache.hugegraph.store.raft.PartitionStateMachine;
import org.apache.hugegraph.store.term.HgPair;
import org.apache.hugegraph.store.util.HgStoreException;
import org.rocksdb.Cache;
@@ -40,23 +46,16 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public interface BusinessHandler extends DBSessionBuilder {
+import com.google.protobuf.ByteString;
- Logger log = LoggerFactory.getLogger(HgStoreStateMachine.class);
- String tableUnknown = "unknown";
- String tableVertex = "g+v";
- String tableOutEdge = "g+oe";
- String tableInEdge = "g+ie";
- String tableIndex = "g+index";
- String tableTask = "g+task";
- String tableOlap = "g+olap";
- String tableServer = "g+server";
+public interface BusinessHandler extends DBSessionBuilder {
- String[] tables = new String[]{tableUnknown, tableVertex, tableOutEdge, tableInEdge, tableIndex,
- tableTask, tableOlap, tableServer};
+ Logger log = LoggerFactory.getLogger(PartitionStateMachine.class);
+ int compactionCanStart = 0;
+ int compactionDone = 1;
+ int doing = -1;
- void doPut(String graph, int code, String table, byte[] key, byte[] value) throws
- HgStoreException;
+ void doPut(String graph, int code, String table, byte[] key, byte[] value) throws HgStoreException;
byte[] doGet(String graph, int code, String table, byte[] key) throws HgStoreException;
@@ -66,8 +65,15 @@ void doPut(String graph, int code, String table, byte[] key, byte[] value) throw
ScanIterator scan(String graph, String table, int codeFrom, int codeTo) throws HgStoreException;
- ScanIterator scan(String graph, int code, String table, byte[] start, byte[] end,
- int scanType) throws HgStoreException;
+ ScanIterator scan(String graph, int code, String table, byte[] start,
+ byte[] end, int scanType) throws HgStoreException;
+
+ /**
+ * primary index scan
+ */
+ ScanIterator scan(String graph, String table, List params,
+ DeDupOption dedupOption)
+ throws HgStoreException;
ScanIterator scan(String graph, int code, String table, byte[] start, byte[] end, int scanType,
byte[] conditionQuery) throws HgStoreException;
@@ -76,11 +82,17 @@ ScanIterator scan(String graph, int code, String table, byte[] start, byte[] end
ScanIterator scanOriginal(Graphpb.ScanPartitionRequest request);
- ScanIterator scanPrefix(String graph, int code, String table, byte[] prefix,
- int scanType) throws HgStoreException;
+ ScanIterator scanPrefix(String graph, int code, String table, byte[] prefix, int scanType) throws HgStoreException;
+
+ ScanIterator scanPrefix(String graph, int code, String table, byte[] prefix) throws HgStoreException;
- ScanIterator scanPrefix(String graph, int code, String table, byte[] prefix) throws
- HgStoreException;
+ ScanIterator scanIndex(String graph, List> param,
+ DeDupOption dedupOption, boolean transElement, boolean filterTTL) throws HgStoreException;
+
+ ScanIterator scanIndex(String graph, String table, List> params,
+ DeDupOption dedupOption, boolean lookupBack, boolean transKey,
+ boolean filterTTL, int limit)
+ throws HgStoreException;
HgStoreMetric.Partition getPartitionMetric(String graph, int partId,
boolean accurateCount) throws HgStoreException;
@@ -92,6 +104,8 @@ void batchGet(String graph, String table, Supplier> s,
void flushAll();
+ void closeDB(int partId);
+
void closeAll();
//
@@ -99,6 +113,8 @@ void batchGet(String graph, String table, Supplier> s,
List getLeaderPartitionIds(String graph);
+ Set getLeaderPartitionIdSet();
+
HgStoreMetric.Graph getGraphMetric(String graph, int partId);
void saveSnapshot(String snapshotPath, String graph, int partId) throws HgStoreException;
@@ -129,12 +145,14 @@ boolean cleanPartition(String graph, int partId, long startKey, long endKey,
TxBuilder txBuilder(String graph, int partId);
+ boolean cleanTtl(String graph, int partId, String table, List ids);
+
default void doBatch(String graph, int partId, List entryList) {
BusinessHandler.TxBuilder builder = txBuilder(graph, partId);
try {
for (BatchEntry b : entryList) {
Key start = b.getStartKey();
- String table = tables[b.getTable()];
+ String table = HugeServerTables.TABLES[b.getTable()];
byte[] startKey = start.getKey().toByteArray();
int number = b.getOpType().getNumber();
if (number == OpType.OP_TYPE_PUT_VALUE) {
@@ -186,10 +204,26 @@ default void doBatch(String graph, int partId, List entryList) {
boolean dbCompaction(String graphName, int partitionId, String tableName);
+ boolean blockingCompact(String graphName, int partitionId);
+
void destroyGraphDB(String graphName, int partId) throws HgStoreException;
long count(String graphName, String table);
+ void lock(String path) throws InterruptedException,
+ TimeoutException;
+ void unlock(String path);
+
+ void awaitAndSetLock(int id, int expectedValue, int value) throws InterruptedException,
+ TimeoutException;
+ void setAndNotifyState(int id, int state);
+
+ AtomicInteger getState(int id);
+
+ String getLockPath(int partitionId);
+
+ List getPartitionIds(String graph);
+
@NotThreadSafe
interface TxBuilder {
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandlerImpl.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandlerImpl.java
index 6421082cf1..3f63bb79a1 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandlerImpl.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/BusinessHandlerImpl.java
@@ -17,28 +17,48 @@
package org.apache.hugegraph.store.business;
-import static org.apache.hugegraph.store.util.HgStoreConst.EMPTY_BYTES;
+import static org.apache.hugegraph.store.business.MultiPartitionIterator.EMPTY_BYTES;
+import static org.apache.hugegraph.store.constant.HugeServerTables.INDEX_TABLE;
+import static org.apache.hugegraph.store.constant.HugeServerTables.IN_EDGE_TABLE;
+import static org.apache.hugegraph.store.constant.HugeServerTables.OUT_EDGE_TABLE;
+import static org.apache.hugegraph.store.constant.HugeServerTables.VERTEX_TABLE;
import static org.apache.hugegraph.store.util.HgStoreConst.SCAN_ALL_PARTITIONS_ID;
+import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
+import java.util.function.ToLongFunction;
import java.util.stream.Collectors;
import javax.annotation.concurrent.NotThreadSafe;
-import org.apache.commons.configuration2.MapConfiguration;
+import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
+import org.apache.hugegraph.HugeGraphSupplier;
+import org.apache.hugegraph.SchemaGraph;
+import org.apache.hugegraph.backend.BackendColumn;
import org.apache.hugegraph.config.HugeConfig;
import org.apache.hugegraph.config.OptionSpace;
+import org.apache.hugegraph.id.EdgeId;
+import org.apache.hugegraph.id.Id;
+import org.apache.hugegraph.pd.client.PDConfig;
+import org.apache.hugegraph.pd.common.PartitionUtils;
import org.apache.hugegraph.pd.grpc.pulse.CleanType;
import org.apache.hugegraph.rocksdb.access.DBStoreException;
import org.apache.hugegraph.rocksdb.access.RocksDBFactory;
@@ -47,52 +67,83 @@
import org.apache.hugegraph.rocksdb.access.RocksDBSession;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
import org.apache.hugegraph.rocksdb.access.SessionOperator;
+import org.apache.hugegraph.serializer.BinaryElementSerializer;
+import org.apache.hugegraph.serializer.BytesBuffer;
+import org.apache.hugegraph.serializer.DirectBinarySerializer;
import org.apache.hugegraph.store.HgStoreEngine;
-import org.apache.hugegraph.store.cmd.CleanDataRequest;
+import org.apache.hugegraph.store.PartitionEngine;
+import org.apache.hugegraph.store.business.itrv2.BatchGetIterator;
+import org.apache.hugegraph.store.business.itrv2.InAccurateIntersectionIterator;
+import org.apache.hugegraph.store.business.itrv2.InAccurateUnionFilterIterator;
+import org.apache.hugegraph.store.business.itrv2.IntersectionFilterIterator;
+import org.apache.hugegraph.store.business.itrv2.IntersectionWrapper;
+import org.apache.hugegraph.store.business.itrv2.MapJoinIterator;
+import org.apache.hugegraph.store.business.itrv2.MapLimitIterator;
+import org.apache.hugegraph.store.business.itrv2.MapUnionIterator;
+import org.apache.hugegraph.store.business.itrv2.MultiListIterator;
+import org.apache.hugegraph.store.business.itrv2.TypeTransIterator;
+import org.apache.hugegraph.store.business.itrv2.UnionFilterIterator;
+import org.apache.hugegraph.store.business.itrv2.io.SortShuffleSerializer;
+import org.apache.hugegraph.store.cmd.HgCmdClient;
+import org.apache.hugegraph.store.cmd.request.BlankTaskRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.consts.PoolNames;
import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest;
import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest.Request;
import org.apache.hugegraph.store.grpc.Graphpb.ScanPartitionRequest.ScanType;
+import org.apache.hugegraph.store.grpc.query.DeDupOption;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.meta.PartitionManager;
import org.apache.hugegraph.store.meta.asynctask.AsyncTaskState;
import org.apache.hugegraph.store.meta.asynctask.CleanTask;
import org.apache.hugegraph.store.metric.HgStoreMetric;
+import org.apache.hugegraph.store.pd.DefaultPdProvider;
import org.apache.hugegraph.store.pd.PdProvider;
+import org.apache.hugegraph.store.query.QueryTypeParam;
+import org.apache.hugegraph.store.raft.RaftClosure;
+import org.apache.hugegraph.store.raft.RaftOperation;
import org.apache.hugegraph.store.term.Bits;
import org.apache.hugegraph.store.term.HgPair;
+import org.apache.hugegraph.store.util.ExecutorUtil;
import org.apache.hugegraph.store.util.HgStoreException;
+import org.apache.hugegraph.structure.BaseElement;
+import org.apache.hugegraph.util.Bytes;
import org.rocksdb.Cache;
import org.rocksdb.MemoryUsageType;
import com.alipay.sofa.jraft.util.Utils;
+import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class BusinessHandlerImpl implements BusinessHandler {
+ private static final Map GRAPH_SUPPLIER_CACHE =
+ new ConcurrentHashMap<>();
private static final int batchSize = 10000;
+ private static Long indexDataSize = 50 * 1024L;
private static final RocksDBFactory factory = RocksDBFactory.getInstance();
private static final HashMap tableMapping = new HashMap<>() {{
- put(ScanType.SCAN_VERTEX, tableVertex);
- put(ScanType.SCAN_EDGE, tableOutEdge);
+ put(ScanType.SCAN_VERTEX, VERTEX_TABLE);
+ put(ScanType.SCAN_EDGE, OUT_EDGE_TABLE);
}};
private static final Map dbNames = new ConcurrentHashMap<>();
-
- static {
- int code = tableUnknown.hashCode();
- code = tableVertex.hashCode();
- code = tableOutEdge.hashCode();
- code = tableInEdge.hashCode();
- code = tableIndex.hashCode();
- code = tableTask.hashCode();
- code = tableTask.hashCode();
- log.debug("init table code:{}", code);
- }
-
+ private static HugeGraphSupplier mockGraphSupplier = null;
+ private static final int compactionThreadCount = 64;
+ private static final ConcurrentMap pathLock = new ConcurrentHashMap<>();
+ private static final ConcurrentMap compactionState =
+ new ConcurrentHashMap<>();
+ private static final ThreadPoolExecutor compactionPool =
+ ExecutorUtil.createExecutor(PoolNames.COMPACT, compactionThreadCount,
+ compactionThreadCount * 4, Integer.MAX_VALUE);
+ private static final int timeoutMillis = 6 * 3600 * 1000;
+ private final BinaryElementSerializer serializer = BinaryElementSerializer.getInstance();
+ private final DirectBinarySerializer directBinarySerializer = new DirectBinarySerializer();
private final PartitionManager partitionManager;
private final PdProvider provider;
private final InnerKeyCreator keyCreator;
+ private final Semaphore semaphore = new Semaphore(1);
public BusinessHandlerImpl(PartitionManager partitionManager) {
this.partitionManager = partitionManager;
@@ -122,7 +173,7 @@ public static HugeConfig initRocksdb(Map rocksdbConfig,
// Register rocksdb configuration
OptionSpace.register("rocksdb", "org.apache.hugegraph.rocksdb.access.RocksDBOptions");
RocksDBOptions.instance();
- HugeConfig hConfig = new HugeConfig(new MapConfiguration(rocksdbConfig));
+ HugeConfig hConfig = new HugeConfig(rocksdbConfig);
factory.setHugeConfig(hConfig);
if (listener != null) {
factory.addRocksdbChangedListener(listener);
@@ -130,6 +181,27 @@ public static HugeConfig initRocksdb(Map rocksdbConfig,
return hConfig;
}
+ public static void setIndexDataSize(long dataSize) {
+ if (dataSize > 0) {
+ indexDataSize = dataSize;
+ }
+ }
+
+ /**
+ * FNV hash method
+ *
+ * @param key hash input
+ * @return a long hash value
+ */
+ public static Long fnvHash(byte[] key) {
+ long rv = 0xcbf29ce484222325L;
+ for (var b : key) {
+ rv ^= b;
+ rv *= 0x100000001b3L;
+ }
+ return rv;
+ }
+
public static String getDbName(int partId) {
String dbName = dbNames.get(partId);
if (dbName == null) {
@@ -140,6 +212,40 @@ public static String getDbName(int partId) {
return dbName;
}
+ public static ThreadPoolExecutor getCompactionPool() {
+ return compactionPool;
+ }
+
+ /**
+ * used for testing, setting fake graph supplier
+ *
+ * @param supplier
+ */
+ public static void setMockGraphSupplier(HugeGraphSupplier supplier) {
+ mockGraphSupplier = supplier;
+ }
+
+ public static HugeGraphSupplier getGraphSupplier(String graph) {
+ if (mockGraphSupplier != null) {
+ return mockGraphSupplier;
+ }
+
+ if (GRAPH_SUPPLIER_CACHE.get(graph) == null) {
+ synchronized (BusinessHandlerImpl.class) {
+ if (GRAPH_SUPPLIER_CACHE.get(graph) == null) {
+ var config =
+ PDConfig.of(HgStoreEngine.getInstance().getOption().getPdAddress());
+ config.setAuthority(DefaultPdProvider.name, DefaultPdProvider.authority);
+ String[] parts = graph.split("/");
+ assert (parts.length > 1);
+ GRAPH_SUPPLIER_CACHE.put(graph, new SchemaGraph(parts[0], parts[1], config));
+ }
+ }
+ }
+
+ return GRAPH_SUPPLIER_CACHE.get(graph);
+ }
+
@Override
public void doPut(String graph, int code, String table, byte[] key, byte[] value) throws
HgStoreException {
@@ -149,7 +255,7 @@ public void doPut(String graph, int code, String table, byte[] key, byte[] value
SessionOperator op = dbSession.sessionOp();
try {
op.prepare();
- byte[] targetKey = keyCreator.getKey(partId, graph, code, key);
+ byte[] targetKey = keyCreator.getKeyOrCreate(partId, graph, code, key);
op.put(table, targetKey, value);
op.commit();
} catch (Exception e) {
@@ -163,6 +269,9 @@ public void doPut(String graph, int code, String table, byte[] key, byte[] value
@Override
public byte[] doGet(String graph, int code, String table, byte[] key) throws HgStoreException {
int partId = provider.getPartitionByCode(graph, code).getId();
+ if (!partitionManager.hasPartition(graph, partId)) {
+ return null;
+ }
try (RocksDBSession dbSession = getSession(graph, table, partId)) {
byte[] targetKey = keyCreator.getKey(partId, graph, code, key);
@@ -231,6 +340,76 @@ public ScanIterator scan(String graph, int code, String table, byte[] start, byt
return MultiPartitionIterator.of(ids, function);
}
+ /**
+ * 将 id scan 合并成一个 list,其他的调用 scan 函数
+ *
+ * @param graph graph
+ * @param table table
+ * @param params primary scan params
+ * @param dedupOption de-duplicate option, 0: none, 1: none-exactly 2: exactly
+ * @return an iterator
+ * @throws HgStoreException when get db session fail
+ */
+ @Override
+ public ScanIterator scan(String graph, String table, List params,
+ DeDupOption dedupOption) throws HgStoreException {
+
+ var iterator = scan(graph, table, params);
+
+ if (!(iterator instanceof MultiListIterator)) {
+ return iterator;
+ }
+
+ switch (dedupOption) {
+ case NONE:
+ return iterator;
+ case DEDUP:
+ return new InAccurateUnionFilterIterator<>(iterator,
+ BusinessHandlerImpl::getColumnByteHash);
+ case LIMIT_DEDUP:
+ return new MapLimitIterator<>(iterator);
+ case PRECISE_DEDUP:
+ // todo: 优化?
+ var wrapper =
+ new IntersectionWrapper<>(iterator, BusinessHandlerImpl::getColumnByteHash);
+ wrapper.proc();
+ // 再次扫描一遍
+ return new UnionFilterIterator<>(scan(graph, table, params), wrapper,
+ (o1, o2) -> Arrays.compare(o1.name, o2.name),
+ SortShuffleSerializer.ofBackendColumnSerializer());
+ default:
+ return null;
+ }
+ }
+
+ private ScanIterator scan(String graph, String table, List params) throws
+ HgStoreException {
+ // id scan 单独放到一个列表中
+ var idList = params.stream().filter(QueryTypeParam::isIdScan).collect(Collectors.toList());
+
+ var itr = new MultiListIterator();
+ for (var param : params) {
+ if (param.isPrefixScan()) {
+ // prefix scan
+ itr.addIterator(scanPrefix(graph, param.getCode(), table, param.getStart(),
+ param.getBoundary()));
+ } else if (param.isRangeScan()) {
+ // ranged scan
+ itr.addIterator(
+ scan(graph, param.getCode(), table, param.getStart(), param.getEnd(),
+ param.getBoundary()));
+ }
+ }
+
+ if (!idList.isEmpty()) {
+ itr.addIterator(new BatchGetIterator(idList.iterator(),
+ idParam -> doGet(graph, idParam.getCode(), table,
+ idParam.getStart())));
+ }
+
+ return itr.getIterators().size() == 1 ? itr.getIterators().get(0) : itr;
+ }
+
/**
* According to keyCode range return data, left closed right open.
*
@@ -283,6 +462,395 @@ public GraphStoreIterator scan(ScanPartitionRequest spr) throws HgStoreException
return new GraphStoreIterator(scanOriginal(spr), spr);
}
+ private ToLongFunction getBaseElementHashFunction() {
+ return value -> fnvHash(value.id().asBytes());
+ }
+
+ @Override
+ public ScanIterator scanIndex(String graph, String table, List> params,
+ DeDupOption dedupOption, boolean lookupBack, boolean transKey,
+ boolean filterTTL, int limit) throws HgStoreException {
+
+ ScanIterator result;
+
+ boolean onlyPrimary =
+ params.stream().allMatch(sub -> sub.size() == 1 && !sub.get(0).isIndexScan());
+
+ boolean needLookup = lookupBack && !onlyPrimary;
+
+ if (params.size() == 1) {
+ // no union operation
+ result = indexIntersection(graph, table, params.get(0), dedupOption, onlyPrimary,
+ filterTTL, needLookup, limit);
+ } else {
+ // 多个索引
+ var sub = params.stream()
+ .map(p2 -> indexIntersection(graph, table, p2, dedupOption, onlyPrimary,
+ filterTTL, needLookup, limit))
+ .collect(Collectors.toList());
+
+ switch (dedupOption) {
+ case NONE:
+ result = new MultiListIterator(sub);
+ break;
+ case DEDUP:
+ result = new InAccurateUnionFilterIterator<>(new MultiListIterator(sub),
+ BusinessHandlerImpl::getColumnByteHash);
+ break;
+ case LIMIT_DEDUP:
+ result = new MapLimitIterator<>(new MultiListIterator(sub));
+ break;
+ case PRECISE_DEDUP:
+ if (limit > 0) {
+ // map limit 去重
+ result = new MapLimitIterator(
+ new MultiListIterator(sub));
+ } else {
+ // union operation
+ var fileSize = getQueryFileSize(graph, table, getLeaderPartitionIds(graph),
+ params);
+ if (fileSize < indexDataSize * params.size()) {
+ // using map
+ result = new MapUnionIterator(sub,
+ col -> Arrays.toString(
+ col.name));
+ } else {
+ result = new MultiListIterator(sub);
+ var wrapper = new IntersectionWrapper<>(result,
+ BusinessHandlerImpl::getColumnByteHash);
+ wrapper.proc();
+
+ var round2 = new MultiListIterator();
+ for (int i = 0; i < params.size(); i++) {
+ var itr = sub.get(i);
+ if (itr instanceof MapJoinIterator) {
+ // 放到内存的,可以不用重新算了
+ ((MapJoinIterator, ?>) itr).reset();
+ round2.addIterator(itr);
+ } else {
+ round2.addIterator(
+ indexIntersection(graph, table, params.get(i),
+ dedupOption, onlyPrimary, filterTTL,
+ needLookup, limit));
+ }
+ }
+ result = new UnionFilterIterator<>(round2, wrapper,
+ (o1, o2) -> Arrays.compare(o1.name,
+ o2.name),
+ SortShuffleSerializer.ofBackendColumnSerializer());
+ }
+ }
+ break;
+ default:
+ throw new HgStoreException("deduplication option not supported");
+ }
+ }
+
+ if (needLookup) {
+ // 回查原表
+ result =
+ new TypeTransIterator(
+ result, column -> {
+ if (column != null && column.name != null) {
+ // var id = KeyUtil.getOwnerKey(table, backendColumn.name);
+ var value =
+ doGet(graph, PartitionUtils.calcHashcode(column.value), table,
+ column.name);
+ if (value != null && value.length > 0) {
+ return RocksDBSession.BackendColumn.of(column.name, value);
+ }
+ }
+ return null;
+ }, "lookup-back-table");
+ }
+ return result;
+ }
+
+ /**
+ * for no scan:
+ * case 1: count case, multi param + no dedup + no transElement
+ * case 2: transElement, one param + dedup + transElement
+ */
+ @Override
+ public ScanIterator scanIndex(String graph, List> params,
+ DeDupOption dedupOption, boolean transElement,
+ boolean filterTTL) throws HgStoreException {
+ // case 1
+ if (!transElement) {
+ if (params.size() == 1) {
+ var param = params.get(0).get(0);
+ if (param.isRangeIndexScan()) {
+ return scan(graph, param.getCode(), "g+index", param.getStart(), param.getEnd(),
+ param.getBoundary());
+ } else {
+ return scanPrefix(graph, param.getCode(), "g+index", param.getStart(),
+ param.getBoundary());
+ }
+ } else {
+ // todo: change multiListIterator of MultiPartition to ? ,
+ // combine multi id?
+ var result = new MultiListIterator();
+ params.forEach(sub -> {
+ var param = sub.get(0);
+ if (param.isRangeIndexScan()) {
+ result.addIterator(scan(graph, param.getCode(), "g+index", param.getStart(),
+ param.getEnd(), param.getBoundary()));
+ } else {
+ result.addIterator(
+ scanPrefix(graph, param.getCode(), "g+index", param.getStart(),
+ param.getBoundary()));
+ }
+ });
+ return result;
+ }
+ }
+
+ // case 2
+ var param = params.get(0).get(0);
+ var result = scanIndexToBaseElement(graph, param, filterTTL);
+
+ switch (dedupOption) {
+ case NONE:
+ return result;
+ case DEDUP:
+ return new InAccurateUnionFilterIterator<>(result, getBaseElementHashFunction());
+ case LIMIT_DEDUP:
+ return new MapLimitIterator<>(result);
+ case PRECISE_DEDUP:
+ var wrapper = new IntersectionWrapper<>(result, getBaseElementHashFunction());
+ wrapper.proc();
+ return new UnionFilterIterator<>(scanIndexToBaseElement(graph, param, filterTTL),
+ wrapper,
+ (o1, o2) -> Arrays.compare(o1.id().asBytes(),
+ o2.id().asBytes()),
+ SortShuffleSerializer.ofBaseElementSerializer());
+ default:
+ return null;
+ }
+ }
+
+ public ScanIterator indexIntersection(String graph, String table, List params,
+ DeDupOption dedupOption, boolean onlyPrimary,
+ boolean filterTTL, boolean lookup, int limit) throws
+ HgStoreException {
+
+ // 主键查询,不需要去重,只支持一个主键。如果有其他的 index 查询,要按照 BackendColumn 消重,抹除掉 value
+ if (params.size() == 1 && !params.get(0).isIndexScan()) {
+ var iterator = scan(graph, table, params);
+ // 需要删除掉 value,和 index 去重
+ return onlyPrimary ? iterator : new TypeTransIterator<>(iterator,
+ (Function) column -> {
+ // todo: from key
+ // to owner key
+ BaseElement element;
+ try {
+ if (IN_EDGE_TABLE.equals(
+ table) ||
+ OUT_EDGE_TABLE.equals(
+ table)) {
+ element =
+ serializer.parseEdge(
+ getGraphSupplier(
+ graph),
+ BackendColumn.of(
+ column.name,
+ column.value),
+ null,
+ false);
+ } else {
+ element =
+ serializer.parseVertex(
+ getGraphSupplier(
+ graph),
+ BackendColumn.of(
+ column.name,
+ column.value),
+ null);
+ }
+ } catch (Exception e) {
+ log.error("parse " +
+ "element " +
+ "error, " +
+ "graph" +
+ " " +
+ "{}, table," +
+ " {}", graph,
+ table, e);
+ return null;
+ }
+ // column.value =
+ // KeyUtil
+ // .idToBytes
+ // (BinaryElementSerializer.ownerId
+ // (element));
+ column.value =
+ BinaryElementSerializer.ownerId(
+ element)
+ .asBytes();
+ return column;
+ }, "replace-pk");
+ }
+
+ var iterators =
+ params.stream().map(param -> scanIndexToElementId(graph, param, filterTTL, lookup))
+ .collect(Collectors.toList());
+
+ // 减少 iterator 层次结构
+ ScanIterator result =
+ params.size() == 1 ? iterators.get(0) : new MultiListIterator(iterators);
+
+ if (dedupOption == DeDupOption.NONE) {
+ return result;
+ } else if (dedupOption == DeDupOption.DEDUP) {
+ return params.size() == 1 ? new InAccurateUnionFilterIterator<>(result,
+ BusinessHandlerImpl::getColumnByteHash) :
+ new InAccurateIntersectionIterator<>(result,
+ BusinessHandlerImpl::getColumnByteHash);
+ } else if (dedupOption == DeDupOption.PRECISE_DEDUP && limit > 0 ||
+ dedupOption == DeDupOption.LIMIT_DEDUP) {
+ // 精确去重+limit 使用 map 去重
+ return new MapLimitIterator(result);
+ } else {
+ // todo: single index need not to deduplication
+ var ids = this.getLeaderPartitionIds(graph);
+ var sizes = params.stream().map(param -> getQueryFileSize(graph, "g+v", ids, param))
+ .collect(Collectors.toList());
+
+ log.debug("queries: {} ,sizes : {}", params, sizes);
+ Long minSize = Long.MAX_VALUE;
+ int loc = -1;
+ for (int i = 0; i < sizes.size(); i++) {
+ if (sizes.get(i) < minSize) {
+ minSize = sizes.get(i);
+ loc = i;
+ }
+ }
+
+ if (minSize < indexDataSize) {
+ return new MapJoinIterator(iterators, loc,
+ col -> Arrays.toString(
+ col.name));
+ } else {
+ // 只能扫描 2 遍了
+ var wrapper =
+ new IntersectionWrapper<>(result, BusinessHandlerImpl::getColumnByteHash,
+ true);
+ wrapper.proc();
+
+ var r2 = multiIndexIterator(graph, params, filterTTL, lookup);
+ return params.size() == 1 ? new UnionFilterIterator<>(r2, wrapper,
+ (o1, o2) -> Arrays.compare(
+ o1.name, o2.name),
+ SortShuffleSerializer.ofBackendColumnSerializer()) :
+ new IntersectionFilterIterator(r2, wrapper, params.size());
+ }
+ }
+ }
+
+ private long getQueryFileSize(String graph, String table, List partitions,
+ List> params) {
+ long total = 0;
+ for (var sub : params) {
+ var size = sub.stream().map(param -> getQueryFileSize(graph,
+ param.isIndexScan() ? "g+index" :
+ table, partitions, param))
+ .min(Long::compareTo);
+ total += size.get();
+ }
+ return total;
+ }
+
+ private long getQueryFileSize(String graph, String table, List partitions,
+ QueryTypeParam param) {
+ long total = 0;
+ for (int partId : partitions) {
+ try (RocksDBSession dbSession = getSession(graph, partId)) {
+ total += dbSession.getApproximateDataSize(table, param.getStart(), param.getEnd());
+ }
+ }
+ return total;
+ }
+
+ private ScanIterator multiIndexIterator(String graph, List params,
+ boolean filterTTL, boolean lookup) {
+ var iterators =
+ params.stream().map(param -> scanIndexToElementId(graph, param, filterTTL, lookup))
+ .collect(Collectors.toList());
+ return params.size() == 1 ? iterators.get(0) : new MultiListIterator(iterators);
+ }
+
+ private ScanIterator scanIndexToElementId(String graph, QueryTypeParam param, boolean filterTTL,
+ boolean lookup) {
+ long now = System.currentTimeMillis();
+ return new TypeTransIterator(
+ param.isRangeIndexScan() ?
+ scan(graph, param.getCode(), INDEX_TABLE, param.getStart(), param.getEnd(),
+ param.getBoundary()) :
+ scanPrefix(graph, param.getCode(), INDEX_TABLE, param.getStart(),
+ param.getBoundary()), column -> {
+ if (filterTTL && isIndexExpire(column, now)) {
+ return null;
+ }
+
+ // todo : 后面使用 parseIndex(BackendColumn indexCol)
+ var index = serializer.parseIndex(getGraphSupplier(graph),
+ BackendColumn.of(column.name, column.value), null);
+
+ if (param.getIdPrefix() != null &&
+ !Bytes.prefixWith(index.elementId().asBytes(), param.getIdPrefix())) {
+ return null;
+ }
+
+ Id elementId = index.elementId();
+ if (elementId instanceof EdgeId) {
+ column.name = new BytesBuffer().writeEdgeId(elementId).bytes();
+ } else {
+ column.name = new BytesBuffer().writeId(elementId).bytes();
+ }
+
+ if (lookup) {
+ // 存放的 owner key
+ column.value = BinaryElementSerializer.ownerId(index).asBytes();
+ // column.value = KeyUtil.idToBytes(BinaryElementSerializer.ownerId(index));
+ }
+ return column;
+ }, "trans-index-to-element-id");
+ }
+
+ private ScanIterator scanIndexToBaseElement(String graph, QueryTypeParam param,
+ boolean filterTTL) {
+
+ long now = System.currentTimeMillis();
+ return new TypeTransIterator(
+ param.isRangeIndexScan() ?
+ scan(graph, param.getCode(), INDEX_TABLE, param.getStart(), param.getEnd(),
+ param.getBoundary()) :
+ scanPrefix(graph, param.getCode(), INDEX_TABLE, param.getStart(),
+ param.getBoundary()), column -> {
+ if (filterTTL && isIndexExpire(column, now)) {
+ return null;
+ }
+
+ var e = serializer.index2Element(getGraphSupplier(graph),
+ BackendColumn.of(column.name, column.value));
+
+ if (param.getIdPrefix() != null &&
+ !Bytes.prefixWith(e.id().asBytes(), param.getIdPrefix())) {
+ return null;
+ }
+
+ return e;
+ // return new BaseVertex(IdUtil.readLong(String.valueOf(random.nextLong())),
+ // VertexLabel.GENERAL);
+ }, "trans-index-to-base-element");
+ }
+
+ private boolean isIndexExpire(RocksDBSession.BackendColumn column, long now) {
+ var e = directBinarySerializer.parseIndex(column.name, column.value);
+ return e.expiredTime() > 0 && e.expiredTime() < now;
+ }
+
@Override
public ScanIterator scanOriginal(ScanPartitionRequest spr) throws HgStoreException {
Request request = spr.getScanRequest();
@@ -439,7 +1007,8 @@ public void batchGet(String graph, String table, Supplier getLeaderPartitionIds(String graph) {
return partitionManager.getLeaderPartitionIds(graph);
}
+ @Override
+ public Set getLeaderPartitionIdSet() {
+ return partitionManager.getLeaderPartitionIdSet();
+ }
+
@Override
public void saveSnapshot(String snapshotPath, String graph, int partId) throws
HgStoreException {
@@ -574,7 +1153,8 @@ public boolean cleanPartition(String graph, int partId, long startKey, long endK
/**
* Clean up partition data, delete data not belonging to this partition.
- * Traverse all keys of partId, read code, if code >= splitKey generate a new key, write to newPartId
+ * Traverse all keys of partId, read code, if code >= splitKey generate a new key, write to
+ * newPartId
*/
private boolean cleanPartition(Partition partition,
Function belongsFunction) {
@@ -671,8 +1251,15 @@ private RocksDBSession getSession(String graphName, int partId) throws HgStoreEx
*/
@Override
public RocksDBSession getSession(int partId) throws HgStoreException {
- // Each partition corresponds to a rocksdb instance, so the rocksdb instance name is rocksdb + partId
+ // Each partition corresponds to a rocksdb instance, so the rocksdb instance name is
+ // rocksdb + partId
String dbName = getDbName(partId);
+ if (HgStoreEngine.getInstance().isClosing().get()) {
+ HgStoreException closeException =
+ new HgStoreException(HgStoreException.EC_CLOSE, "store is closing", dbName);
+ log.error("get session with error:", closeException);
+ throw closeException;
+ }
RocksDBSession dbSession = factory.queryGraphDB(dbName);
if (dbSession == null) {
long version = HgStoreEngine.getInstance().getCommittedIndex(partId);
@@ -693,15 +1280,32 @@ private void deleteGraphDatabase(String graph, int partId) throws IOException {
truncate(graph, partId);
}
- private PartitionManager getPartManager() {
- return this.partitionManager;
- }
-
@Override
public TxBuilder txBuilder(String graph, int partId) throws HgStoreException {
return new TxBuilderImpl(graph, partId, getSession(graph, partId));
}
+ @Override
+ public boolean cleanTtl(String graph, int partId, String table, List ids) {
+
+ try (RocksDBSession dbSession = getSession(graph, table, partId)) {
+ SessionOperator op = dbSession.sessionOp();
+ try {
+ op.prepare();
+ for (ByteString bs : ids) {
+ byte[] targetKey = keyCreator.getKey(partId, graph, bs.toByteArray());
+ op.delete(table, targetKey);
+ }
+ op.commit();
+ } catch (Exception e) {
+ log.error("Graph: " + graph + " cleanTTL exception", e);
+ op.rollback();
+ throw new HgStoreException(HgStoreException.EC_RKDB_DODEL_FAIL, e.toString());
+ }
+ }
+ return true;
+ }
+
@Override
public boolean existsTable(String graph, int partId, String table) {
try (RocksDBSession session = getSession(graph, partId)) {
@@ -746,17 +1350,149 @@ public boolean dbCompaction(String graphName, int partitionId) {
* Perform compaction on RocksDB
*/
@Override
- public boolean dbCompaction(String graphName, int partitionId, String tableName) {
- try (RocksDBSession session = getSession(graphName, partitionId)) {
- SessionOperator op = session.sessionOp();
- if (tableName.isEmpty()) {
- op.compactRange();
- } else {
- op.compactRange(tableName);
+ public boolean dbCompaction(String graphName, int id, String tableName) {
+ try {
+ compactionPool.submit(() -> {
+ try {
+ String path = getLockPath(id);
+ try (RocksDBSession session = getSession(graphName, id)) {
+ SessionOperator op = session.sessionOp();
+ pathLock.putIfAbsent(path, new AtomicInteger(compactionCanStart));
+ compactionState.putIfAbsent(id, new AtomicInteger(0));
+ log.info("Partition {} dbCompaction started", id);
+ if (tableName.isEmpty()) {
+ lock(path);
+ setState(id, doing);
+ log.info("Partition {}-{} got lock, dbCompaction start", id, path);
+ op.compactRange();
+ setState(id, compactionDone);
+ log.info("Partition {} dbCompaction end and start to do snapshot", id);
+ PartitionEngine pe = HgStoreEngine.getInstance().getPartitionEngine(id);
+ // 执行完成后找 leader 发送 blankTask
+ if (pe.isLeader()) {
+ RaftClosure bc = (closure) -> {
+ };
+ pe.addRaftTask(RaftOperation.create(RaftOperation.SYNC_BLANK_TASK),
+ bc);
+ } else {
+ HgCmdClient client = HgStoreEngine.getInstance().getHgCmdClient();
+ BlankTaskRequest request = new BlankTaskRequest();
+ request.setGraphName("");
+ request.setPartitionId(id);
+ client.tryInternalCallSyncWithRpc(request);
+ }
+ setAndNotifyState(id, compactionDone);
+ } else {
+ op.compactRange(tableName);
+ }
+ }
+ log.info("Partition {}-{} dbCompaction end", id, path);
+ } catch (Exception e) {
+ log.error("do dbCompaction with error: ", e);
+ } finally {
+ try {
+ semaphore.release();
+ } catch (Exception e) {
+
+ }
+ }
+ });
+ } catch (Exception e) {
+
+ }
+ return true;
+ }
+
+ @Override
+ public void lock(String path) throws InterruptedException, TimeoutException {
+ long start = System.currentTimeMillis();
+ while (!compareAndSetLock(path)) {
+ AtomicInteger lock = pathLock.get(path);
+ synchronized (lock) {
+ lock.wait(1000);
+ if (System.currentTimeMillis() - start > timeoutMillis) {
+ throw new TimeoutException("wait compaction start timeout");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void unlock(String path) {
+ AtomicInteger l = pathLock.get(path);
+ l.set(compactionCanStart);
+ synchronized (l) {
+ l.notifyAll();
+ }
+ }
+
+ private boolean compareAndSetLock(String path) {
+ AtomicInteger l = pathLock.get(path);
+ return l.compareAndSet(compactionCanStart, doing);
+ }
+
+ @Override
+ public void awaitAndSetLock(int id, int expectedValue, int value) throws InterruptedException,
+ TimeoutException {
+ long start = System.currentTimeMillis();
+ while (!compareAndSetState(id, expectedValue, value)) {
+ AtomicInteger state = compactionState.get(id);
+ synchronized (state) {
+ state.wait(500);
+ if (System.currentTimeMillis() - start > timeoutMillis) {
+ throw new TimeoutException("wait compaction start timeout");
+ }
}
}
+ }
- log.info("Partition {}-{} dbCompaction end", graphName, partitionId);
+ @Override
+ public void setAndNotifyState(int id, int state) {
+ AtomicInteger l = compactionState.get(id);
+ l.set(state);
+ synchronized (l) {
+ l.notifyAll();
+ }
+ }
+
+ @Override
+ public AtomicInteger getState(int id) {
+ AtomicInteger l = compactionState.get(id);
+ return l;
+ }
+
+ private AtomicInteger setState(int id, int state) {
+ AtomicInteger l = compactionState.get(id);
+ l.set(state);
+ return l;
+ }
+
+ private boolean compareAndSetState(int id, int expectedState, int newState) {
+ AtomicInteger l = compactionState.get(id);
+ return l.compareAndSet(expectedState, newState);
+ }
+
+ @Override
+ public String getLockPath(int partitionId) {
+ String dataPath = partitionManager.getDbDataPath(partitionId);
+ File file = FileUtils.getFile(dataPath);
+ File pf = file.getParentFile();
+ return pf.getAbsolutePath();
+ }
+
+ @Override
+ public List getPartitionIds(String graph) {
+ return partitionManager.getPartitionIds(graph);
+ }
+
+ @Override
+ public boolean blockingCompact(String graphName, int partitionId) {
+ boolean locked = semaphore.tryAcquire();
+ if (locked) {
+ dbCompaction(graphName, partitionId, "");
+ } else {
+ return false;
+ }
return true;
}
@@ -768,7 +1504,8 @@ public boolean dbCompaction(String graphName, int partitionId, String tableName)
*/
@Override
public void destroyGraphDB(String graphName, int partId) throws HgStoreException {
- // Each graph each partition corresponds to a rocksdb instance, so the rocksdb instance name is rocksdb + partId
+ // Each graph each partition corresponds to a rocksdb instance, so the rocksdb instance
+ // name is rocksdb + partId
String dbName = getDbName(partId);
factory.destroyGraphDB(dbName);
@@ -806,6 +1543,14 @@ public long count(String graph, String table) {
return all;
}
+ public InnerKeyCreator getKeyCreator() {
+ return keyCreator;
+ }
+
+ public static Long getColumnByteHash(RocksDBSession.BackendColumn column) {
+ return fnvHash(column.name);
+ }
+
@NotThreadSafe
private class TxBuilderImpl implements TxBuilder {
@@ -904,7 +1649,8 @@ public Tx build() {
return new Tx() {
@Override
public void commit() throws HgStoreException {
- op.commit(); // After an exception occurs in commit, rollback must be called, otherwise it will cause the lock not to be released.
+ op.commit(); // After an exception occurs in commit, rollback must be
+ // called, otherwise it will cause the lock not to be released.
dbSession.close();
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataManager.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataManager.java
new file mode 100644
index 0000000000..b890f4a3fd
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataManager.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business;
+
+import java.util.List;
+
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.cmd.HgCmdClient;
+import org.apache.hugegraph.store.cmd.request.BatchPutRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.meta.PartitionManager;
+
+import com.alipay.sofa.jraft.Status;
+
+/**
+ * 数据管理接口,实现分区数据管理,分裂和合并,支持跨机器转移数据
+ */
+public interface DataManager {
+
+ void setBusinessHandler(BusinessHandler handler);
+
+ void setMetaManager(PartitionManager metaManager);
+
+ void setCmdClient(HgCmdClient cmdClient);
+
+ /**
+ * 拷贝分区source内的数据到其他分区targets
+ * 一个分区,迁移到多个分区
+ *
+ * @param source source partition
+ * @param targets target partitions
+ * @return execution status
+ * @throws Exception execution exception
+ */
+ Status move(Metapb.Partition source, List targets) throws Exception;
+
+ /**
+ * 将source target的数据全部拷贝到target上
+ * 从一个分区迁移到另外一个分区
+ *
+ * @param source source partition
+ * @param target target partition
+ * @return execution result
+ * @throws Exception execution exception
+ */
+ Status move(Metapb.Partition source, Metapb.Partition target) throws Exception;
+
+ //// 同步副本之间的分区状态
+ //UpdatePartitionResponse updatePartitionState(Metapb.Partition partition, Metapb
+ // .PartitionState state);
+ //
+
+ /// / 同步副本之间分区的范围
+ //UpdatePartitionResponse updatePartitionRange(Metapb.Partition partition, int startKey, int
+ // endKey);
+
+ // 清理分区partition内的无效数据
+ void cleanData(Metapb.Partition partition);
+
+ // 写入数据
+ void write(BatchPutRequest request);
+
+ void clean(CleanDataRequest request);
+
+ Status doBuildIndex(Metapb.BuildIndexParam param, Metapb.Partition partition) throws Exception;
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataManagerImpl.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataManagerImpl.java
new file mode 100644
index 0000000000..520c0ba894
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataManagerImpl.java
@@ -0,0 +1,430 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business;
+
+import static org.apache.hugegraph.store.constant.HugeServerTables.INDEX_TABLE;
+import static org.apache.hugegraph.store.constant.HugeServerTables.OUT_EDGE_TABLE;
+import static org.apache.hugegraph.store.constant.HugeServerTables.VERTEX_TABLE;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.BiFunction;
+
+import org.apache.hugegraph.backend.BackendColumn;
+import org.apache.hugegraph.id.IdUtil;
+import org.apache.hugegraph.pd.common.PartitionUtils;
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.pd.grpc.Metapb.PartitionState;
+import org.apache.hugegraph.pd.grpc.pulse.CleanType;
+import org.apache.hugegraph.rocksdb.access.RocksDBSession;
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.schema.IndexLabel;
+import org.apache.hugegraph.serializer.BinaryElementSerializer;
+import org.apache.hugegraph.store.HgStoreEngine;
+import org.apache.hugegraph.store.cmd.HgCmdClient;
+import org.apache.hugegraph.store.cmd.request.BatchPutRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.cmd.response.BatchPutResponse;
+import org.apache.hugegraph.store.cmd.response.UpdatePartitionResponse;
+import org.apache.hugegraph.store.meta.PartitionManager;
+import org.apache.hugegraph.store.query.util.KeyUtil;
+import org.apache.hugegraph.store.raft.RaftClosure;
+import org.apache.hugegraph.store.raft.RaftOperation;
+import org.apache.hugegraph.store.term.Bits;
+import org.apache.hugegraph.structure.BaseEdge;
+import org.apache.hugegraph.structure.BaseElement;
+import org.apache.hugegraph.structure.BaseVertex;
+import org.apache.hugegraph.structure.Index;
+import org.apache.hugegraph.structure.builder.IndexBuilder;
+
+import com.alipay.sofa.jraft.Status;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class DataManagerImpl implements DataManager {
+
+ public static final int BATCH_PUT_SIZE = 2000;
+ private BusinessHandler businessHandler;
+ private PartitionManager metaManager;
+ private HgCmdClient client;
+
+ private static Metapb.Partition findPartition(List partitions, int code) {
+ for (Metapb.Partition partition : partitions) {
+ if (code >= partition.getStartKey() && code < partition.getEndKey()) {
+ return partition;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setBusinessHandler(BusinessHandler handler) {
+ this.businessHandler = handler;
+ }
+
+ @Override
+ public void setMetaManager(PartitionManager metaManager) {
+ this.metaManager = metaManager;
+ }
+
+ @Override
+ public void setCmdClient(HgCmdClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public Status move(Metapb.Partition source, List targets) throws Exception {
+ Status status = Status.OK();
+ // 开始移动数据之前,先把分区下线
+ UpdatePartitionResponse response =
+ metaManager.updateState(source, PartitionState.PState_Offline);
+ if (response.getStatus().isOK()) {
+ status = move(source, targets, DataManagerImpl::findPartition);
+
+ // 数据迁移成功后,设置新分区范围和上线新分区
+ for (var target : targets) {
+ if (status.isOk()) {
+ if (!(metaManager.updateRange(target, (int) target.getStartKey(),
+ (int) target.getEndKey())
+ .getStatus().isOK()
+ &&
+ metaManager.updateState(target, PartitionState.PState_Normal).getStatus()
+ .isOK())) {
+ status.setError(-3, "new partition online fail");
+ }
+ }
+ }
+ } else {
+ status.setError(-1, "source partition offline fail");
+ }
+
+ metaManager.updateState(source, PartitionState.PState_Normal);
+
+ return status;
+ }
+
+ @Override
+ public Status move(Metapb.Partition source, Metapb.Partition target) throws Exception {
+ // 只写入 target
+ return move(source, Collections.singletonList(target), (partitions, integer) -> target);
+ }
+
+ /**
+ * move data from partition to targets
+ *
+ * @param source source partition
+ * @param targets target partitions
+ * @param partitionSelector the key of source partition belongs which target
+ * @return execution result
+ * @throws Exception exception when put data
+ */
+
+ private Status move(Metapb.Partition source, List targets,
+ BiFunction, Integer, Metapb.Partition> partitionSelector)
+ throws Exception {
+
+ Status status = Status.OK();
+ String graphName = source.getGraphName();
+ List tables = businessHandler.getTableNames(graphName, source.getId());
+
+ log.info("moveData, graph:{}, partition id:{} tables:{}, {}-{}", source.getGraphName(),
+ source.getId(), tables,
+ source.getStartKey(), source.getEndKey());
+ WriteBatch batch = new WriteBatch(graphName);
+ // target partition : count
+ Map moveCount = new HashMap<>();
+
+ for (String table : tables) {
+ int total = 0;
+ moveCount.clear();
+
+ try (ScanIterator iterator =
+ businessHandler.scan(graphName, table, (int) source.getStartKey(),
+ (int) source.getEndKey())) {
+ int count = 0;
+ while (iterator.hasNext() && status.isOk()) {
+ total += 1;
+ RocksDBSession.BackendColumn entry = iterator.next();
+ byte[] innerKey = entry.name;
+ byte[] key = Arrays.copyOfRange(innerKey, 0, innerKey.length - Short.BYTES);
+ int code = Bits.getShort(innerKey, innerKey.length - Short.BYTES);
+ Metapb.Partition partition = partitionSelector.apply(targets, code);
+ if (partition != null) {
+ moveCount.put(partition.getId(),
+ moveCount.getOrDefault(partition.getId(), 0L) + 1);
+ batch.add(partition.getId(),
+ BatchPutRequest.KV.of(table, code, key, entry.value));
+ if (++count >= BATCH_PUT_SIZE) {
+ if (!batch.sync()) {
+ status.setError(-2, "move data fail");
+ }
+ count = 0;
+ }
+ }
+ }
+ if (count > 0) {
+ if (!batch.sync()) {
+ status.setError(-2, "move data fail");
+ }
+ }
+
+ for (var pair : moveCount.entrySet()) {
+ log.info("{}-{}, table: {}, move to partition id {}, count:{}, total:{}",
+ source.getGraphName(), source.getId(), table, pair.getKey(),
+ pair.getValue(),
+ total);
+ }
+ }
+ }
+
+ return status;
+ }
+
+ @Override
+ public void cleanData(Metapb.Partition partition) {
+ String graphName = partition.getGraphName();
+ CleanDataRequest request = new CleanDataRequest();
+ request.setGraphName(graphName);
+ request.setPartitionId(partition.getId());
+ request.setCleanType(CleanType.CLEAN_TYPE_KEEP_RANGE);
+ request.setKeyStart(partition.getStartKey());
+ request.setKeyEnd(partition.getEndKey());
+ request.setDeletePartition(false);
+
+ try {
+ client.cleanData(request);
+ } catch (Exception e) {
+ log.error("exception ", e);
+ }
+ }
+
+ @Override
+ public void write(BatchPutRequest request) {
+ BusinessHandler.TxBuilder tx =
+ businessHandler.txBuilder(request.getGraphName(), request.getPartitionId());
+ for (BatchPutRequest.KV kv : request.getEntries()) {
+ tx.put(kv.getCode(), kv.getTable(), kv.getKey(), kv.getValue());
+ }
+ tx.build().commit();
+ }
+
+ @Override
+ public void clean(CleanDataRequest request) {
+ // raft 执行真实数据的清理
+ businessHandler.cleanPartition(request.getGraphName(), request.getPartitionId(),
+ request.getKeyStart(), request.getKeyEnd(),
+ request.getCleanType());
+ }
+
+ @Override
+ public Status doBuildIndex(Metapb.BuildIndexParam param, Metapb.Partition source) throws
+ Exception {
+
+ var partitionId = source.getId();
+ var graphName = param.getGraph();
+ log.info("doBuildIndex begin, partition id :{}, with param: {}", partitionId, param);
+
+ Status status = Status.OK();
+ var graphSupplier = BusinessHandlerImpl.getGraphSupplier(graphName);
+
+ var labelId = IdUtil.fromBytes(param.getLabelId().toByteArray());
+ IndexLabel indexLabel = null;
+ if (param.hasIndexLabel()) {
+ indexLabel =
+ graphSupplier.indexLabel(IdUtil.fromBytes(param.getIndexLabel().toByteArray()));
+ }
+
+ WriteBatch batch = new WriteBatch(param.getGraph());
+ IndexBuilder builder = new IndexBuilder(graphSupplier);
+ BinaryElementSerializer serializer = new BinaryElementSerializer();
+
+ long countTotal = 0;
+ long start = System.currentTimeMillis();
+ long countRecord = 0;
+
+ // todo : table scan or prefix scan
+ try (var itr = businessHandler.scan(graphName,
+ param.getIsVertexLabel() ? VERTEX_TABLE :
+ OUT_EDGE_TABLE,
+ (int) source.getStartKey(), (int) source.getEndKey())) {
+
+ int count = 0;
+ while (itr.hasNext()) {
+ RocksDBSession.BackendColumn entry = itr.next();
+
+ byte[] innerKey = entry.name;
+ byte[] key = Arrays.copyOfRange(innerKey, 0, innerKey.length - Short.BYTES);
+ var column = BackendColumn.of(key, entry.value);
+
+ BaseElement element = null;
+
+ try {
+ if (param.getIsVertexLabel()) {
+ element = serializer.parseVertex(graphSupplier, column, null);
+ } else {
+ element = serializer.parseEdge(graphSupplier, column, null, true);
+ }
+ } catch (Exception e) {
+ log.error("parse element failed, graph:{}, key:{}", graphName, e);
+ continue;
+ }
+
+ // filter by label id
+ if (!element.schemaLabel().id().equals(labelId)) {
+ continue;
+ }
+
+ countRecord += 1;
+
+ List array;
+ if (indexLabel != null) {
+ // label id
+ array = builder.buildIndex(element, indexLabel);
+ } else if (param.hasLabelIndex() && param.getLabelIndex()) {
+ // element type index
+ array = builder.buildLabelIndex(element);
+ } else {
+ // rebuild all index
+ if (param.getIsVertexLabel()) {
+ assert element instanceof BaseVertex;
+ array = builder.buildVertexIndex((BaseVertex) element);
+ } else {
+ assert element instanceof BaseEdge;
+ array = builder.buildEdgeIndex((BaseEdge) element);
+ }
+ }
+
+ for (var index : array) {
+ var col = serializer.writeIndex(index);
+ int code = PartitionUtils.calcHashcode(KeyUtil.getOwnerId(index.elementId()));
+ // same partition id with element
+ batch.add(partitionId, BatchPutRequest.KV.of(INDEX_TABLE, code, col.name,
+ col.value == null ? new byte[0] :
+ col.value));
+
+ if (++count >= BATCH_PUT_SIZE) {
+ if (!batch.sync()) {
+ status.setError(-2, "sync index failed");
+ break;
+ }
+ count = 0;
+ }
+ countTotal++;
+ }
+
+ if (!status.isOk()) {
+ break;
+ }
+ }
+
+ if (status.isOk()) {
+ if (count > 0) {
+ if (!batch.sync()) {
+ status.setError(-2, "sync index failed");
+ }
+ }
+ }
+
+ log.info("doBuildIndex end, partition id: {}, records: {}, total index: {}, cost: {}s",
+ source.getId(),
+ countRecord, countTotal, (System.currentTimeMillis() - start) / 1000);
+ }
+
+ return status;
+ }
+
+ class WriteBatch {
+
+ private final Map> data = new HashMap<>();
+ private final String graphName;
+
+ public WriteBatch(String graphName) {
+ this.graphName = graphName;
+ }
+
+ public WriteBatch add(int partition, BatchPutRequest.KV kv) {
+ if (!data.containsKey(partition)) {
+ data.put(partition, new LinkedList<>());
+ }
+ data.get(partition).add(kv);
+ return this;
+ }
+
+ public Boolean sync() {
+ boolean ret = true;
+ for (Map.Entry> entry : data.entrySet()) {
+ ret = ret && sendData(entry.getKey(), entry.getValue());
+ }
+ for (List list : data.values()) {
+ list.clear();
+ }
+
+ return ret;
+ }
+
+ public Boolean sendData(Integer partId, List kvs) {
+ BatchPutRequest request = new BatchPutRequest();
+ request.setGraphName(graphName);
+ request.setPartitionId(partId);
+ request.setEntries(kvs);
+
+ var engine = HgStoreEngine.getInstance().getPartitionEngine(partId);
+
+ if (engine != null && engine.isLeader()) {
+ try {
+ CountDownLatch latch = new CountDownLatch(1);
+
+ final Boolean[] ret = {Boolean.FALSE};
+ engine.addRaftTask(RaftOperation.create(RaftOperation.IN_WRITE_OP, request),
+ new RaftClosure() {
+ @Override
+ public void run(Status status) {
+ if (status.isOk()) {
+ ret[0] = Boolean.TRUE;
+ }
+ latch.countDown();
+ }
+ });
+ latch.await();
+
+ if (ret[0]) {
+ return true;
+ }
+ } catch (Exception e) {
+ // using send data by client when exception occurs
+ log.warn("send data by raft: pid: {}, error: ", partId, e);
+ }
+ }
+
+ BatchPutResponse response = client.batchPut(request);
+ if (response == null || !response.getStatus().isOK()) {
+ log.error("sendData error, pId:{} status:{}", partId,
+ response != null ? response.getStatus() : "EMPTY_RESPONSE");
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataMover.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataMover.java
index a348f561c7..603448e4e7 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataMover.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DataMover.java
@@ -30,6 +30,7 @@
/**
* Data transfer interface, implementing partition splitting and merging, supporting cross-machine data transfer.
*/
+@Deprecated
public interface DataMover {
void setBusinessHandler(BusinessHandler handler);
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DefaultDataMover.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DefaultDataMover.java
index aeca3a3cae..26138b334e 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DefaultDataMover.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/DefaultDataMover.java
@@ -42,6 +42,7 @@
import lombok.extern.slf4j.Slf4j;
@Slf4j
+@Deprecated
public class DefaultDataMover implements DataMover {
public static int Batch_Put_Size = 2000;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/FilterIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/FilterIterator.java
index e3c1380b93..093d6e793a 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/FilterIterator.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/FilterIterator.java
@@ -17,20 +17,17 @@
package org.apache.hugegraph.store.business;
-import java.util.Arrays;
-
import org.apache.commons.lang3.ArrayUtils;
-import org.apache.hugegraph.backend.query.ConditionQuery;
-import org.apache.hugegraph.backend.serializer.BinaryBackendEntry;
-import org.apache.hugegraph.backend.store.BackendEntry;
-import org.apache.hugegraph.rocksdb.access.RocksDBSession.BackendColumn;
+import org.apache.hugegraph.backend.BackendColumn;
+import org.apache.hugegraph.query.ConditionQuery;
+import org.apache.hugegraph.rocksdb.access.RocksDBSession;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
-import org.apache.hugegraph.structure.HugeElement;
+import org.apache.hugegraph.structure.BaseElement;
import lombok.extern.slf4j.Slf4j;
@Slf4j
-public class FilterIterator extends
+public class FilterIterator extends
AbstractSelectIterator
implements ScanIterator {
@@ -58,27 +55,20 @@ public boolean hasNext() {
boolean match = false;
if (this.query.resultType().isVertex() ||
this.query.resultType().isEdge()) {
- BackendEntry entry = null;
+
while (iterator.hasNext()) {
current = iterator.next();
- BackendEntry.BackendColumn column =
- BackendEntry.BackendColumn.of(
- current.name, current.value);
- BackendEntry.BackendColumn[] columns =
- new BackendEntry.BackendColumn[]{column};
- if (entry == null || !belongToMe(entry, column) ||
- this.query.resultType().isEdge()) {
- entry = new BinaryBackendEntry(query.resultType(),
- current.name);
- entry.columns(Arrays.asList(columns));
+ BaseElement element;
+ if (this.query.resultType().isVertex()) {
+ element = serializer.parseVertex(null,
+ BackendColumn.of(current.name, current.value),
+ null);
} else {
- // There may be cases that contain multiple columns
- entry.columns(Arrays.asList(columns));
- continue;
+ element = serializer.parseEdge(null,
+ BackendColumn.of(current.name, current.value),
+ null, true);
}
- HugeElement element = this.parseEntry(entry,
- this.query.resultType()
- .isVertex());
+
match = query.test(element);
if (match) {
break;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/GraphStoreIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/GraphStoreIterator.java
index 0e8aa50706..b90d1ec704 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/GraphStoreIterator.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/GraphStoreIterator.java
@@ -18,7 +18,6 @@
package org.apache.hugegraph.store.business;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
@@ -31,10 +30,9 @@
import javax.script.ScriptException;
import org.apache.commons.lang.StringUtils;
-import org.apache.hugegraph.backend.id.Id;
-import org.apache.hugegraph.backend.serializer.BinaryBackendEntry;
-import org.apache.hugegraph.backend.store.BackendEntry;
-import org.apache.hugegraph.rocksdb.access.RocksDBSession.BackendColumn;
+import org.apache.hugegraph.backend.BackendColumn;
+import org.apache.hugegraph.id.Id;
+import org.apache.hugegraph.rocksdb.access.RocksDBSession;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
import org.apache.hugegraph.schema.EdgeLabel;
import org.apache.hugegraph.schema.PropertyKey;
@@ -47,19 +45,18 @@
import org.apache.hugegraph.store.grpc.Graphpb.Variant.Builder;
import org.apache.hugegraph.store.grpc.Graphpb.VariantType;
import org.apache.hugegraph.store.grpc.Graphpb.Vertex;
-import org.apache.hugegraph.structure.HugeEdge;
-import org.apache.hugegraph.structure.HugeElement;
-import org.apache.hugegraph.structure.HugeProperty;
-import org.apache.hugegraph.structure.HugeVertex;
+import org.apache.hugegraph.structure.BaseEdge;
+import org.apache.hugegraph.structure.BaseElement;
+import org.apache.hugegraph.structure.BaseProperty;
+import org.apache.hugegraph.structure.BaseVertex;
import org.apache.hugegraph.type.HugeType;
import org.apache.hugegraph.util.Blob;
-import org.apache.tinkerpop.gremlin.structure.Property;
-import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors;
+import groovy.lang.MissingMethodException;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -78,10 +75,11 @@ public class GraphStoreIterator extends AbstractSelectIterator
private final Set properties;
private Vertex.Builder vertex;
private Edge.Builder edge;
- private ArrayList data;
+ private ArrayList data;
private GroovyScriptEngineImpl engine;
private CompiledScript script;
- private HugeElement current;
+ private BaseElement current;
+ private Exception stopCause;
public GraphStoreIterator(ScanIterator iterator,
ScanPartitionRequest scanRequest) {
@@ -117,40 +115,27 @@ public GraphStoreIterator(ScanIterator iterator,
}
}
- private HugeElement getElement(BackendColumn next) {
- BackendEntry entry = null;
- BackendEntry.BackendColumn column = BackendEntry.BackendColumn.of(
- next.name, next.value);
- if (entry == null || !belongToMe(entry, column) || !isVertex) {
- try {
- entry = new BinaryBackendEntry(type, next.name);
- } catch (Exception e) {
- log.error("using core to new entry with error:", e);
- }
- }
- BackendEntry.BackendColumn[] columns =
- new BackendEntry.BackendColumn[]{column};
- entry.columns(Arrays.asList(columns));
- return this.parseEntry(entry, isVertex);
+ private BaseElement getElement(RocksDBSession.BackendColumn next) {
+ return this.parseEntry(BackendColumn.of(next.name, next.value), isVertex);
}
@Override
public boolean hasNext() {
if (current == null) {
while (iter.hasNext()) {
- BackendColumn next = this.iter.next();
- HugeElement element = getElement(next);
+ RocksDBSession.BackendColumn next = this.iter.next();
+ BaseElement element = getElement(next);
try {
boolean evalResult = true;
if (isVertex) {
- HugeVertex el = (HugeVertex) element;
+ BaseVertex el = (BaseVertex) element;
if (engine != null) {
Bindings bindings = engine.createBindings();
bindings.put("element", el);
evalResult = (boolean) script.eval(bindings);
}
} else {
- HugeEdge el = (HugeEdge) element;
+ BaseEdge el = (BaseEdge) element;
if (engine != null) {
Bindings bindings = engine.createBindings();
bindings.put("element", el);
@@ -162,6 +147,10 @@ public boolean hasNext() {
}
current = element;
return true;
+ } catch (ScriptException | MissingMethodException se) {
+ stopCause = se;
+ log.error("get next with error which cause to stop:", se);
+ return false;
} catch (Exception e) {
log.error("get next with error:", e);
}
@@ -189,8 +178,8 @@ public T next() {
return next;
}
- public T select(BackendColumn current) {
- HugeElement element = getElement(current);
+ public T select(RocksDBSession.BackendColumn current) {
+ BaseElement element = getElement(current);
if (isVertex) {
return (T) parseVertex(element);
} else {
@@ -199,6 +188,9 @@ public T select(BackendColumn current) {
}
public ArrayList convert() {
+ if (data == null || data.isEmpty()) {
+ return new ArrayList<>(0);
+ }
ArrayList result = new ArrayList(data.size());
for (int i = 0; i < data.size(); i++) {
result.add(select(data.get(i)));
@@ -206,7 +198,7 @@ public ArrayList convert() {
return result;
}
- private
> List buildProperties(
+ private
> List buildProperties(
Builder variant,
int size,
Iterator
eps) {
@@ -215,7 +207,7 @@ private
> List buildProperties(
pSize : size);
Graphpb.Property.Builder pb = Graphpb.Property.newBuilder();
while (eps.hasNext()) {
- HugeProperty> property = (HugeProperty>) eps.next();
+ BaseProperty> property = eps.next();
PropertyKey key = property.propertyKey();
long pkId = key.id().asLong();
if (pSize > 0 && !properties.contains(pkId)) {
@@ -309,8 +301,8 @@ private void buildId(Builder variant, Id id) {
}
}
- private Edge parseEdge(HugeElement element) {
- HugeEdge e = (HugeEdge) element;
+ private Edge parseEdge(BaseElement element) {
+ BaseEdge e = (BaseEdge) element;
edge.clear();
EdgeLabel label = e.schemaLabel();
edge.setLabel(label.longId());
@@ -323,14 +315,14 @@ private Edge parseEdge(HugeElement element) {
buildId(variant, e.targetVertex().id());
edge.setTargetId(variant.build());
int size = e.sizeOfProperties();
- Iterator> eps = e.properties();
+ Iterator> eps = e.properties().iterator();
List props = buildProperties(variant, size, eps);
edge.setField(propertiesDesEdge, props);
return edge.build();
}
- private Vertex parseVertex(HugeElement element) {
- HugeVertex v = (HugeVertex) element;
+ private Vertex parseVertex(BaseElement element) {
+ BaseVertex v = (BaseVertex) element;
vertex.clear();
VertexLabel label = v.schemaLabel();
vertex.setLabel(label.longId());
@@ -338,7 +330,7 @@ private Vertex parseVertex(HugeElement element) {
buildId(variant, v.id());
vertex.setId(variant.build());
int size = v.sizeOfProperties();
- Iterator> vps = v.properties();
+ Iterator> vps = v.properties().iterator();
List props = buildProperties(variant, size, vps);
vertex.setField(propertiesDesVertex, props);
return vertex.build();
@@ -348,4 +340,8 @@ private Vertex parseVertex(HugeElement element) {
public void close() {
iter.close();
}
+
+ public Exception getStopCause() {
+ return stopCause;
+ }
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyCreator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyCreator.java
index 072d09cc4a..43db542f6b 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyCreator.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyCreator.java
@@ -30,7 +30,7 @@
public class InnerKeyCreator {
final BusinessHandler businessHandler;
- private final Map graphIdCache = new ConcurrentHashMap<>();
+ private volatile Map graphIdCache = new ConcurrentHashMap<>();
public InnerKeyCreator(BusinessHandler businessHandler) {
this.businessHandler = businessHandler;
@@ -49,6 +49,26 @@ public int getGraphId(Integer partId, String graphName) throws HgStoreException
}
}
+ /**
+ *
+ * @param partId partition id
+ * @param graphName graph name
+ * @return 65535 如果不存在
+ * @throws HgStoreException
+ */
+ public int getGraphIdOrCreate(Integer partId, String graphName) throws HgStoreException {
+ try {
+ GraphIdManager manager;
+ if ((manager = graphIdCache.get(partId)) == null) {
+ manager = new GraphIdManager(businessHandler, partId);
+ graphIdCache.put(partId, manager);
+ }
+ return (int) manager.getGraphIdOrCreate(graphName);
+ } catch (Exception e) {
+ throw new HgStoreException(HgStoreException.EC_RKDB_PD_FAIL, e.getMessage());
+ }
+ }
+
public void delGraphId(Integer partId, String graphName) {
if (graphIdCache.containsKey(partId)) {
graphIdCache.get(partId).releaseGraphId(graphName);
@@ -68,6 +88,15 @@ public int parseKeyCode(byte[] innerKey) {
return Bits.getShort(innerKey, innerKey.length - Short.BYTES);
}
+ public byte[] getKeyOrCreate(Integer partId, String graph, int code, byte[] key) {
+ int graphId = getGraphIdOrCreate(partId, graph);
+ byte[] buf = new byte[Short.BYTES + key.length + Short.BYTES];
+ Bits.putShort(buf, 0, graphId);
+ Bits.put(buf, Short.BYTES, key);
+ Bits.putShort(buf, key.length + Short.BYTES, code);
+ return buf;
+ }
+
public byte[] getKey(Integer partId, String graph, int code, byte[] key) {
int graphId = getGraphId(partId, graph);
byte[] buf = new byte[Short.BYTES + key.length + Short.BYTES];
@@ -77,6 +106,21 @@ public byte[] getKey(Integer partId, String graph, int code, byte[] key) {
return buf;
}
+ /**
+ *
+ * @param partId
+ * @param graph
+ * @param key
+ * @return
+ */
+ public byte[] getKey(Integer partId, String graph, byte[] key) {
+ int graphId = getGraphId(partId, graph);
+ byte[] buf = new byte[Short.BYTES + key.length];
+ Bits.putShort(buf, 0, graphId);
+ Bits.put(buf, Short.BYTES, key);
+ return buf;
+ }
+
public byte[] getStartKey(Integer partId, String graph) {
int graphId = getGraphId(partId, graph);
byte[] buf = new byte[Short.BYTES];
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyFilter.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyFilter.java
index 34dc46063b..368032f2ce 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyFilter.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/InnerKeyFilter.java
@@ -40,6 +40,14 @@ public InnerKeyFilter(ScanIterator iterator) {
moveNext();
}
+ public InnerKeyFilter(ScanIterator iterator, boolean codeFilter) {
+ this.iterator = iterator;
+ this.codeFrom = Integer.MIN_VALUE;
+ this.codeTo = Integer.MAX_VALUE;
+ this.codeFilter = codeFilter;
+ moveNext();
+ }
+
public InnerKeyFilter(ScanIterator iterator, int codeFrom, int codeTo) {
this.iterator = iterator;
this.codeFrom = codeFrom;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/MultiPartitionIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/MultiPartitionIterator.java
index 44d77935d5..72cc472b21 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/MultiPartitionIterator.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/MultiPartitionIterator.java
@@ -24,6 +24,7 @@
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.function.BiFunction;
+import java.util.stream.Collectors;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
@@ -198,4 +199,16 @@ private byte[] getPositionKey(int partitionId) {
}
+ /**
+ * obtain iteration list of all partitions
+ *
+ * @return iteration list
+ */
+ public List getIterators() {
+ return this.partitions.stream()
+ .map(id -> supplier.apply(id, getPositionKey(id)))
+ .filter(ScanIterator::hasNext)
+ .collect(Collectors.toList());
+ }
+
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/SelectIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/SelectIterator.java
index 41a47efccf..2b51e98778 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/SelectIterator.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/SelectIterator.java
@@ -21,10 +21,10 @@
import java.util.List;
import java.util.Set;
-import org.apache.hugegraph.backend.id.Id;
-import org.apache.hugegraph.backend.serializer.BytesBuffer;
+import org.apache.hugegraph.id.Id;
import org.apache.hugegraph.rocksdb.access.RocksDBSession.BackendColumn;
import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.serializer.BytesBuffer;
import org.apache.hugegraph.type.define.DataType;
import org.apache.hugegraph.type.define.SerialEnum;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/BatchGetIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/BatchGetIterator.java
new file mode 100644
index 0000000000..948a46aa89
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/BatchGetIterator.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 org.apache.hugegraph.store.business.itrv2;
+
+import java.util.Iterator;
+import java.util.function.Function;
+
+import org.apache.hugegraph.rocksdb.access.RocksDBSession;
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.store.query.QueryTypeParam;
+
+/**
+ * 传入多个id,根据id去查询数据,返回iterator
+ * ID 查询
+ */
+public class BatchGetIterator implements ScanIterator {
+
+ private final Iterator iterator;
+
+ private final Function retriveFunction;
+
+ private byte[] pos;
+
+ public BatchGetIterator(Iterator iterator,
+ Function retriveFunction) {
+ this.iterator = iterator;
+ this.retriveFunction = retriveFunction;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.iterator.hasNext();
+ }
+
+ @Override
+ public boolean isValid() {
+ return this.iterator.hasNext();
+ }
+
+ @Override
+ public RocksDBSession.BackendColumn next() {
+ var param = iterator.next();
+ byte[] key = param.getStart();
+ this.pos = key;
+ var value = retriveFunction.apply(param);
+ return value == null ? null : RocksDBSession.BackendColumn.of(key, value);
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public byte[] position() {
+ return this.pos;
+ }
+
+ @Override
+ public long count() {
+ long count = 0L;
+ while (this.iterator.hasNext()) {
+ this.iterator.next();
+ count += 1;
+ }
+ return count;
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ // not supported
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/FileObjectIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/FileObjectIterator.java
new file mode 100644
index 0000000000..23fd7fbb55
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/FileObjectIterator.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 org.apache.hugegraph.store.business.itrv2;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import org.apache.hugegraph.store.business.itrv2.io.SortShuffleSerializer;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class FileObjectIterator implements Iterator {
+
+ private FileInputStream fis = null;
+ private T current;
+ private String fn;
+ private SortShuffleSerializer serializer;
+
+ public FileObjectIterator(String filePath, SortShuffleSerializer serializer) {
+ this.fn = filePath;
+ this.serializer = serializer;
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ if (fis == null) {
+ fis = new FileInputStream(this.fn);
+ }
+ current = readObject(fis);
+
+ if (current != null) {
+ return true;
+ } else {
+ String parent = new File(this.fn).getParent();
+ new File(parent).delete();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+
+ @Override
+ public T next() {
+ return current;
+ }
+
+ public T readObject(InputStream input) throws IOException {
+ return serializer.read(input);
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/InAccurateIntersectionIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/InAccurateIntersectionIterator.java
new file mode 100644
index 0000000000..69d1699df6
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/InAccurateIntersectionIterator.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.NoSuchElementException;
+import java.util.function.ToLongFunction;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.roaringbitmap.longlong.Roaring64Bitmap;
+
+/**
+ * 不适用于单个的iterator, 单个的要使用union的版本(只是做去重)
+ *
+ * @param
+ */
+public class InAccurateIntersectionIterator implements ScanIterator {
+
+ private final Roaring64Bitmap workBitmap;
+
+ private final ToLongFunction toLongFunction;
+
+ private final ScanIterator iterator;
+
+ private T current;
+
+ public InAccurateIntersectionIterator(ScanIterator iterator, ToLongFunction toLongFunction) {
+ assert (iterator instanceof MultiListIterator &&
+ ((MultiListIterator) iterator).getIterators().size() > 0);
+ this.iterator = iterator;
+ this.workBitmap = new Roaring64Bitmap();
+ this.toLongFunction = toLongFunction;
+ }
+
+ @Override
+ public boolean hasNext() {
+ current = null;
+ while (iterator.hasNext()) {
+ var element = (T) iterator.next();
+ if (element == null) {
+ continue;
+ }
+
+ var key = toLongFunction.applyAsLong(element);
+ if (workBitmap.contains(key)) {
+ current = element;
+ return true;
+ } else {
+ workBitmap.add(key);
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isValid() {
+ return iterator.isValid();
+ }
+
+ @Override
+ public E next() {
+ if (current == null) {
+ throw new NoSuchElementException();
+ }
+ return (E) current;
+ }
+
+ @Override
+ public long count() {
+ return iterator.count();
+ }
+
+ @Override
+ public byte[] position() {
+ return iterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ iterator.seek(position);
+ }
+
+ @Override
+ public void close() {
+ iterator.close();
+ this.workBitmap.clear();
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/InAccurateUnionFilterIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/InAccurateUnionFilterIterator.java
new file mode 100644
index 0000000000..8e818c54f6
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/InAccurateUnionFilterIterator.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.NoSuchElementException;
+import java.util.function.ToLongFunction;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.roaringbitmap.longlong.Roaring64Bitmap;
+
+/**
+ * 不精确的去重,直接使用位图
+ *
+ * @param
+ */
+public class InAccurateUnionFilterIterator implements ScanIterator {
+
+ private final Roaring64Bitmap workBitmap;
+
+ private final ToLongFunction toLongFunction;
+
+ private final ScanIterator iterator;
+
+ private T current;
+
+ public InAccurateUnionFilterIterator(ScanIterator iterator, ToLongFunction toLongFunction) {
+ this.iterator = iterator;
+ this.workBitmap = new Roaring64Bitmap();
+ this.toLongFunction = toLongFunction;
+ }
+
+ @Override
+ public boolean hasNext() {
+ current = null;
+ while (iterator.hasNext()) {
+ var element = (T) iterator.next();
+ if (element == null) {
+ continue;
+ }
+
+ var key = toLongFunction.applyAsLong(element);
+ if (!workBitmap.contains(key)) {
+ current = element;
+ workBitmap.add(key);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isValid() {
+ return iterator.isValid();
+ }
+
+ @Override
+ public E next() {
+ if (current == null) {
+ throw new NoSuchElementException();
+ }
+ return (E) current;
+ }
+
+ @Override
+ public long count() {
+ return iterator.count();
+ }
+
+ @Override
+ public byte[] position() {
+ return iterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ iterator.seek(position);
+ }
+
+ @Override
+ public void close() {
+ iterator.close();
+ this.workBitmap.clear();
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/IntersectionFilterIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/IntersectionFilterIterator.java
new file mode 100644
index 0000000000..8651b96f7d
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/IntersectionFilterIterator.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.hugegraph.rocksdb.access.RocksDBSession;
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.store.business.itrv2.io.SortShuffleSerializer;
+import org.apache.hugegraph.store.util.SortShuffle;
+
+/**
+ * 目前应用:2个及以上的iterator去重 (大数据量)
+ * 痛点: iterator 内部可能有重复的, 怎么处理 ??
+ */
+public class IntersectionFilterIterator implements ScanIterator {
+
+ private static final Integer MAX_SIZE = 100000;
+ protected Map map;
+ private ScanIterator iterator;
+ private IntersectionWrapper wrapper;
+ private boolean processed = false;
+ private Iterator innerIterator;
+ private SortShuffle sortShuffle;
+
+ private int size = -1;
+
+ @Deprecated
+ public IntersectionFilterIterator(ScanIterator iterator, IntersectionWrapper> wrapper) {
+ this.iterator = iterator;
+ this.wrapper = wrapper;
+ this.map = new HashMap<>();
+ }
+
+ /**
+ * iterator中取交集(可以是multi list iterator
+ * 问题:对于multi list iterator,无法做到每个都存在,需要外部去重。但是保证总数
+ *
+ * @param iterator 待遍历的iterator
+ * @param wrapper bitmap, 不在bitmap中的,丢弃
+ * @param size the element count in the iterator by filtering
+ */
+ public IntersectionFilterIterator(ScanIterator iterator, IntersectionWrapper> wrapper,
+ int size) {
+ this(iterator, wrapper);
+ this.size = size;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!processed) {
+ try {
+ dedup();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ processed = true;
+ }
+
+ return innerIterator.hasNext();
+ }
+
+ // TODO: 优化序列化器
+ private void saveElements() throws IOException, ClassNotFoundException {
+ for (var entry : this.map.entrySet()) {
+ for (int i = 0; i < entry.getValue(); i++) {
+ sortShuffle.append((RocksDBSession.BackendColumn) entry.getKey());
+ }
+ }
+
+ this.map.clear();
+ }
+
+ /**
+ * todo: 如果一个iterator中存在重复的,目前是无解。 去重的代价太高了。
+ *
+ * @throws IOException
+ * @throws ClassNotFoundException
+ */
+ protected void dedup() throws IOException, ClassNotFoundException {
+ while (this.iterator.hasNext()) {
+ var object = this.iterator.next();
+ if (wrapper.contains(object)) {
+ this.map.put(object, map.getOrDefault(object, 0) + 1);
+ if (this.map.size() >= MAX_SIZE) {
+ if (this.sortShuffle == null) {
+ this.sortShuffle =
+ new SortShuffle<>((o1, o2) -> Arrays.compare(o1.name, o2.name),
+ SortShuffleSerializer.ofBackendColumnSerializer());
+ }
+ saveElements();
+ }
+ }
+ }
+
+ // last batch
+ if (this.sortShuffle != null) {
+ saveElements();
+ this.sortShuffle.finish();
+ }
+
+ if (this.sortShuffle == null) {
+ // map 没填满
+ this.innerIterator =
+ new MapValueFilterIterator<>(this.map, x -> x == size || size == -1 && x > 1);
+ } else {
+ // 需要读取文件
+ var fileIterator =
+ (Iterator) this.sortShuffle.getIterator();
+ this.innerIterator = new ReduceIterator<>(fileIterator,
+ (o1, o2) -> Arrays.compare(o1.name, o2.name),
+ this.size);
+ }
+ }
+
+ @Override
+ public boolean isValid() {
+ if (this.processed) {
+ return false;
+ }
+ return iterator.isValid();
+ }
+
+ @Override
+ public T next() {
+ return (T) this.innerIterator.next();
+ }
+
+ @Override
+ public void close() {
+ this.iterator.close();
+ this.map.clear();
+ }
+
+ @Override
+ public long count() {
+ return this.iterator.count();
+ }
+
+ @Override
+ public byte[] position() {
+ return this.iterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ this.iterator.seek(position);
+ }
+
+ /**
+ * 只保留有重复元素的
+ *
+ * @param
+ */
+ public static class ReduceIterator implements Iterator {
+
+ private E prev = null;
+
+ private E current = null;
+
+ private E data = null;
+
+ private int count = 0;
+
+ private Iterator iterator;
+
+ private Comparator comparator;
+
+ private int adjacent;
+
+ public ReduceIterator(Iterator iterator, Comparator comparator, int adjacent) {
+ this.count = 0;
+ this.iterator = iterator;
+ this.comparator = comparator;
+ this.adjacent = adjacent;
+ }
+
+ /**
+ * 连续重复结果消除. 当prev == current的时候,记录data
+ * 当不等的时候,放回之前的data.
+ * 注意最后的结果,可能重复
+ */
+ @Override
+ public boolean hasNext() {
+ while (iterator.hasNext()) {
+ if (prev == null) {
+ prev = iterator.next();
+ continue;
+ }
+
+ current = iterator.next();
+ if (comparator.compare(prev, current) == 0) {
+ data = current;
+ count += 1;
+ } else {
+ // count starts from 0, so the size is count + 1
+ if (count > 0 && this.adjacent == -1 || count + 1 == this.adjacent) {
+ count = 0;
+ prev = current;
+ return true;
+ } else {
+ count = 0;
+ prev = current;
+ }
+ }
+ }
+
+ // 最后一个结果
+ if (count > 0) {
+ count = 0;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public E next() {
+ return data;
+ }
+ }
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/IntersectionWrapper.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/IntersectionWrapper.java
new file mode 100644
index 0000000000..e6dd9f398b
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/IntersectionWrapper.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.List;
+import java.util.function.ToLongFunction;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.roaringbitmap.longlong.Roaring64Bitmap;
+
+public class IntersectionWrapper {
+
+ private Roaring64Bitmap workBitmap;
+ private Roaring64Bitmap resultBitmap;
+ private ScanIterator iterator;
+ private ToLongFunction hashFunction;
+ private boolean matchAll;
+
+ public IntersectionWrapper(ScanIterator iterator, ToLongFunction hashFunction) {
+ this.iterator = iterator;
+ this.hashFunction = hashFunction;
+ this.workBitmap = new Roaring64Bitmap();
+ this.resultBitmap = new Roaring64Bitmap();
+ this.matchAll = false;
+ }
+
+ /**
+ * 记录iterator中hash值相同的部分.
+ *
+ * @param iterator iterator
+ * @param hashFunction mapping the element to a long value
+ * @param matchAllIterator a value that all exists in the iterator( MultiListIterator)
+ */
+ public IntersectionWrapper(ScanIterator iterator, ToLongFunction hashFunction,
+ boolean matchAllIterator) {
+ this(iterator, hashFunction);
+ this.matchAll = matchAllIterator;
+ }
+
+ public void proc() {
+ if (matchAll && iterator instanceof MultiListIterator) {
+ var mIterators = ((MultiListIterator) iterator).getIterators();
+ if (mIterators.size() > 1) {
+ procMulti(mIterators);
+ }
+ return;
+ }
+
+ procSingle(this.iterator, false);
+ }
+
+ /**
+ * 对于multi list iterator 取所有 iterator的交集
+ *
+ * @param iterators iterators
+ */
+ private void procMulti(List iterators) {
+ var itr = iterators.get(0);
+ procSingle(itr, true);
+
+ for (int i = 1; i < iterators.size(); i++) {
+ // change last round result to the work map
+ workBitmap = resultBitmap.clone();
+ resultBitmap.clear();
+ procSingle(iterators.get(i), false);
+ }
+ }
+
+ private void procSingle(ScanIterator itr, boolean firstRound) {
+ while (itr.hasNext()) {
+ var n = itr.next();
+ if (n == null) {
+ continue;
+ }
+ var key = hashFunction.applyAsLong((T) n);
+
+ if (firstRound) {
+ resultBitmap.add(key);
+ } else {
+ if (workBitmap.contains(key)) {
+ resultBitmap.add(key);
+ } else {
+ workBitmap.add(key);
+ }
+ }
+ }
+ workBitmap.clear();
+ }
+
+ /**
+ * return contains
+ *
+ * @param o input element
+ * @return true: 如果是hash的,可能存在,false则肯定不存在
+ */
+ public boolean contains(T o) {
+ return resultBitmap.contains(hashFunction.applyAsLong(o));
+ }
+}
+
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapJoinIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapJoinIterator.java
new file mode 100644
index 0000000000..1da9b90d4a
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapJoinIterator.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+
+public class MapJoinIterator implements ScanIterator {
+
+ private final List iteratorList;
+
+ private final Function keyFunction;
+
+ private final Map map = new HashMap<>();
+
+ private Iterator iterator;
+
+ private int loc = -1;
+
+ private boolean flag;
+
+ /**
+ * 多个iterator取交集。
+ *
+ * @param iteratorList iterator list
+ * @param loc the location of the iterator having smallest size
+ * @param keyFunction key mapping mapping
+ */
+ public MapJoinIterator(List iteratorList, int loc, Function keyFunction) {
+ assert (iteratorList != null);
+ assert (loc >= 0 && loc < iteratorList.size());
+ this.iteratorList = iteratorList;
+ this.keyFunction = keyFunction;
+ this.loc = loc;
+ this.flag = false;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!flag) {
+ proc();
+ }
+ return this.iterator.hasNext();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public T next() {
+ return (T) this.iterator.next();
+ }
+
+ @Override
+ public void close() {
+ iteratorList.forEach(ScanIterator::close);
+ this.map.clear();
+ }
+
+ public void reset() {
+ this.iterator = this.map.values().iterator();
+ }
+
+ private void proc() {
+ var itr = iteratorList.get(loc);
+ while (itr.hasNext()) {
+ var tmp = (T) itr.next();
+ if (tmp != null) {
+ map.put(keyFunction.apply(tmp), tmp);
+ }
+ }
+
+ for (int i = 0; i < iteratorList.size(); i++) {
+
+ if (i == loc) {
+ continue;
+ }
+
+ var workMap = new HashMap();
+
+ itr = iteratorList.get(i);
+ while (itr.hasNext()) {
+ var tmp = (T) itr.next();
+ if (tmp != null) {
+ var key = keyFunction.apply(tmp);
+ if (map.containsKey(key)) {
+ workMap.put(key, tmp);
+ }
+ }
+ }
+
+ map.clear();
+ map.putAll(workMap);
+ }
+
+ this.iterator = this.map.values().iterator();
+
+ this.flag = true;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapLimitIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapLimitIterator.java
new file mode 100644
index 0000000000..354fc9bf3e
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapLimitIterator.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.Set;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+
+import com.alipay.sofa.jraft.util.concurrent.ConcurrentHashSet;
+
+/**
+ * 针对一个iterator做去重,前SET_MAX_SIZE做精确去重,后面的直接返回
+ *
+ * @param
+ */
+public class MapLimitIterator implements ScanIterator {
+
+ private static final Integer SET_MAX_SIZE = 100000;
+ private ScanIterator iterator;
+ private Set set;
+ private T current = null;
+
+ public MapLimitIterator(ScanIterator iterator) {
+ this.iterator = iterator;
+ set = new ConcurrentHashSet<>();
+ }
+
+ /**
+ * {@inheritDoc}
+ * 返回下一个元素是否存在。
+ * 检查集合中是否还有下一个可用的元素,如果有则返回true,否则返回false。
+ * 如果当前元素为空或者已在集合中,将跳过该元素继续检查下一个元素。
+ * 在检查完所有符合条件的元素后,再次调用hasNext方法会重新检查一遍元素,
+ * 如果满足条件(即不为null且未包含在集合中),则将当前元素添加到集合中并返回true。
+ * 当集合中已经包含SET_MAX_SIZE个元素时,将不会再添加任何新元素,并且返回false。
+ *
+ * @return 下一个元素是否存在
+ */
+ @Override
+ public boolean hasNext() {
+ current = null;
+ while (iterator.hasNext()) {
+ var tmp = (T) iterator.next();
+ if (tmp != null && !set.contains(tmp)) {
+ current = tmp;
+ break;
+ }
+ }
+
+ // 控制set的大小
+ if (current != null && set.size() <= SET_MAX_SIZE) {
+ set.add(current);
+ }
+
+ return current != null;
+ }
+
+ /**
+ * {@inheritDoc}
+ * 返回当前对象。
+ *
+ * @return 当前对象的类型为T1的引用。
+ */
+ @Override
+ public T1 next() {
+ return (T1) current;
+ }
+
+ /**
+ * 返回当前迭代器是否有效。
+ *
+ * @return 当前迭代器是否有效,即是否还有下一个元素。
+ */
+ @Override
+ public boolean isValid() {
+ return iterator.isValid();
+ }
+
+ /**
+ * 返回集合的数量。
+ *
+ * @return 集合的数量。
+ */
+ @Override
+ public long count() {
+ return iterator.count();
+ }
+
+ /**
+ * 返回当前迭代器的位置。
+ *
+ * @return 当前迭代器的位置。
+ */
+ @Override
+ public byte[] position() {
+ return iterator.position();
+ }
+
+ /**
+ * {@inheritDoc}
+ * 将文件指针移动到指定的位置。
+ *
+ * @param position 指定的字节数组,表示要移动到的位置。
+ */
+ @Override
+ public void seek(byte[] position) {
+ iterator.seek(position);
+ }
+
+ /**
+ * 关闭迭代器。
+ */
+ @Override
+ public void close() {
+ iterator.close();
+ this.set.clear();
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapUnionIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapUnionIterator.java
new file mode 100644
index 0000000000..8fc7ecee8f
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapUnionIterator.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+
+public class MapUnionIterator implements ScanIterator {
+
+ private final List iteratorList;
+
+ private final Function keyFunction;
+
+ private final Map map = new HashMap<>();
+
+ private Iterator iterator;
+
+ private boolean flag = false;
+
+ public MapUnionIterator(List iteratorList, Function keyFunction) {
+ this.iteratorList = iteratorList;
+ this.keyFunction = keyFunction;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (!this.flag) {
+ this.proc();
+ }
+ return this.iterator.hasNext();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public T next() {
+ return (T) this.iterator.next();
+ }
+
+ @Override
+ public void close() {
+ iteratorList.forEach(ScanIterator::close);
+ this.map.clear();
+ }
+
+ private void proc() {
+ for (ScanIterator itr : this.iteratorList) {
+ while (itr.hasNext()) {
+ var item = (T) itr.next();
+ if (item != null) {
+ map.put(keyFunction.apply(item), item);
+ }
+ }
+ }
+
+ this.iterator = map.values().iterator();
+ this.flag = true;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapValueFilterIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapValueFilterIterator.java
new file mode 100644
index 0000000000..94d9402bbc
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MapValueFilterIterator.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 org.apache.hugegraph.store.business.itrv2;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.function.IntPredicate;
+
+public class MapValueFilterIterator implements Iterator {
+
+ Iterator> mapIterator;
+ private IntPredicate filter;
+ private K value;
+
+ public MapValueFilterIterator(Map map, IntPredicate filter) {
+ this.mapIterator = map.entrySet().iterator();
+ this.filter = filter;
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (mapIterator.hasNext()) {
+ Map.Entry entry = mapIterator.next();
+ if (filter.test(entry.getValue())) {
+ value = entry.getKey();
+ return true;
+ }
+ }
+ this.value = null;
+ return false;
+ }
+
+ @Override
+ public K next() {
+ if (value == null) {
+ throw new NoSuchElementException();
+ }
+
+ return value;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MultiListIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MultiListIterator.java
new file mode 100644
index 0000000000..909f1fa873
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/MultiListIterator.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.store.business.MultiPartitionIterator;
+
+/**
+ * 一组的同类型iterator,依次按照iterator输出
+ */
+public class MultiListIterator implements ScanIterator {
+
+ /**
+ * iterator 容器
+ */
+ private List iterators;
+
+ /**
+ * iterator 容器的迭代器
+ */
+ private Iterator innerListIterator;
+
+ /**
+ * 当前的element
+ */
+ private ScanIterator innerIterator;
+
+ public MultiListIterator() {
+ this.iterators = new CopyOnWriteArrayList<>();
+ }
+
+ public MultiListIterator(List iterators) {
+ this.iterators = new CopyOnWriteArrayList<>(iterators);
+ }
+
+ /**
+ * 将迭代器添加到扫描迭代器列表中。
+ *
+ * @param iterator 要添加的扫描迭代器。
+ */
+ public void addIterator(ScanIterator iterator) {
+ this.iterators.add(iterator);
+ }
+
+ public List getIterators() {
+ return iterators;
+ }
+
+ /**
+ * 获取内部迭代器
+ */
+ private void getInnerIterator() {
+ if (this.innerIterator != null && this.innerIterator.hasNext()) {
+ return;
+ }
+
+ // close prev one
+ if (this.innerIterator != null) {
+ this.innerIterator.close();
+ }
+
+ if (this.innerListIterator == null) {
+ this.innerListIterator = this.iterators.iterator();
+ }
+
+ while (this.innerListIterator.hasNext()) {
+ this.innerIterator = this.innerListIterator.next();
+ if (this.innerIterator.hasNext()) {
+ return;
+ } else {
+ // whole empty
+ this.innerIterator.close();
+ }
+ }
+
+ this.innerIterator = null;
+ }
+
+ @Override
+ public boolean hasNext() {
+ getInnerIterator();
+ return this.innerIterator != null;
+ }
+
+ @Override
+ public boolean isValid() {
+ getInnerIterator();
+ if (this.innerIterator != null) {
+ return this.innerIterator.isValid();
+ }
+ return true;
+ }
+
+ /**
+ * 关闭迭代器。
+ */
+ @Override
+ public void close() {
+ if (this.innerIterator != null) {
+ this.innerIterator.close();
+ }
+ if (this.innerListIterator != null) {
+ while (this.innerListIterator.hasNext()) {
+ this.innerListIterator.next().close();
+ }
+ }
+ this.iterators.clear();
+ }
+
+ @Override
+ public T next() {
+ return (T) this.innerIterator.next();
+ }
+
+ @Override
+ public long count() {
+ long count = 0;
+ while (hasNext()) {
+ next();
+ count += 1;
+ }
+ return count;
+ }
+
+ @Override
+ public byte[] position() {
+ return this.innerIterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ if (this.iterators.size() == 1) {
+ // range scan or prefix scan
+ if (this.innerIterator instanceof MultiPartitionIterator) {
+ this.innerIterator.seek(position);
+ }
+ }
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/TypeTransIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/TypeTransIterator.java
new file mode 100644
index 0000000000..610d495bcc
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/TypeTransIterator.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+
+/**
+ * 封装 iterator, 通过 function 做type的转化,
+ * 最后发送一个supplier.get的指令
+ *
+ * @param 原来的类型
+ * @param 转换后的类型
+ */
+public class TypeTransIterator implements ScanIterator {
+
+ private final Iterator iterator;
+ private final Function function;
+ private String name = "";
+ private ScanIterator originalIterator;
+ private Supplier additionSupplier;
+
+ /**
+ * is used once. return supper. apply and set to true.
+ */
+ private boolean flag = false;
+
+ private E data;
+
+ public TypeTransIterator(ScanIterator scanIterator, Function function) {
+ this.originalIterator = scanIterator;
+ this.iterator = new Iterator() {
+ @Override
+ public boolean hasNext() {
+ return scanIterator.hasNext();
+ }
+
+ @Override
+ public F next() {
+ return scanIterator.next();
+ }
+ };
+ this.function = function;
+ }
+
+ public TypeTransIterator(ScanIterator scanIterator, Function function, String name) {
+ this(scanIterator, function);
+ this.name = name;
+ }
+
+ public TypeTransIterator(Iterator iterator, Function function) {
+ this.iterator = iterator;
+ this.function = function;
+ }
+
+ public TypeTransIterator(Iterator iterator, Function function, Supplier supplier) {
+ this.iterator = iterator;
+ this.function = function;
+ this.additionSupplier = supplier;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (this.data != null) {
+ return true;
+ }
+
+ while (this.iterator.hasNext()) {
+ var n = this.iterator.next();
+ if (n != null && (data = this.function.apply(n)) != null) {
+ return true;
+ }
+ }
+
+ // look up for the default supplier
+ if (this.additionSupplier != null && !this.flag) {
+ data = this.additionSupplier.get();
+ this.flag = true;
+ }
+
+ return data != null;
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public T next() {
+ if (this.data == null) {
+ throw new NoSuchElementException();
+ }
+ try {
+ return (T) this.data;
+ } finally {
+ // 取出去之后,将data置空
+ this.data = null;
+ }
+ }
+
+ @Override
+ public void close() {
+ if (this.originalIterator != null) {
+ this.originalIterator.close();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "TypeTransIterator{" +
+ "name='" + name + '\'' +
+ ", function=" + function +
+ ", additionSupplier=" + additionSupplier +
+ ", flag=" + flag +
+ ", iterator=" + (originalIterator == null ? iterator : originalIterator) +
+ '}';
+ }
+
+ /**
+ * to java.util.Iterator
+ *
+ * @return iterator
+ */
+ public Iterator toIterator() {
+ return new InnerIterator(this);
+ }
+
+ private class InnerIterator implements Iterator, ScanIterator {
+
+ private final TypeTransIterator iterator;
+
+ public InnerIterator(TypeTransIterator iterator) {
+ this.iterator = iterator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return this.iterator.hasNext();
+ }
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public E next() {
+ return this.iterator.next();
+ }
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/UnionFilterIterator.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/UnionFilterIterator.java
new file mode 100644
index 0000000000..d08bc7dd73
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/UnionFilterIterator.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.hugegraph.pd.common.HgAssert;
+import org.apache.hugegraph.rocksdb.access.ScanIterator;
+import org.apache.hugegraph.store.business.itrv2.io.SortShuffleSerializer;
+import org.apache.hugegraph.store.util.SortShuffle;
+
+public class UnionFilterIterator implements ScanIterator {
+
+ private static final Integer MAP_SIZE = 10000;
+
+ private final ScanIterator iterator;
+
+ private final IntersectionWrapper wrapper;
+ private final Comparator comparator;
+ protected Map map;
+ private Iterator innerIterator;
+ private SortShuffle sortShuffle;
+ private SortShuffleSerializer serializer;
+ private Object current;
+ private boolean isProcessed = false;
+
+ public UnionFilterIterator(ScanIterator iterator, IntersectionWrapper wrapper,
+ Comparator comparator, SortShuffleSerializer serializer) {
+ HgAssert.isNotNull(wrapper, "wrapper is null");
+ this.iterator = iterator;
+ this.wrapper = wrapper;
+ this.map = new HashMap<>();
+ this.comparator = comparator;
+ this.serializer = serializer;
+ }
+
+ /**
+ * 将当前元素保存到sortShuffle中。
+ */
+ private void saveElement() {
+ for (var entry : this.map.entrySet()) {
+ try {
+ sortShuffle.append(entry.getKey());
+ if (entry.getValue() > 1) {
+ sortShuffle.append(entry.getKey());
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ this.map.clear();
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (this.iterator.hasNext()) {
+ var obj = (T) this.iterator.next();
+ // batch get or index lookup may generate null
+ if (obj == null) {
+ continue;
+ }
+
+ // 肯定是唯一的
+ if (!wrapper.contains(obj)) {
+ this.current = obj;
+ return true;
+ } else {
+ // System.out.println("----->" + Arrays.toString(((RocksDBSession.BackendColumn)
+ // obj).name));
+ this.map.put(obj, map.getOrDefault(obj, 0) + 1);
+ if (this.map.size() > MAP_SIZE) {
+ if (this.sortShuffle == null) {
+ sortShuffle = new SortShuffle<>(this.comparator, this.serializer);
+ }
+ saveElement();
+ }
+ }
+ }
+
+ if (!isProcessed) {
+ if (sortShuffle != null) {
+ try {
+ saveElement();
+ sortShuffle.finish();
+
+ var fileIterator = sortShuffle.getIterator();
+ this.innerIterator = new NoRepeatValueIterator<>(fileIterator, this.comparator);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ this.innerIterator = new MapValueFilterIterator<>(this.map, x -> x > 0);
+ }
+
+ isProcessed = true;
+ }
+
+ var ret = this.innerIterator.hasNext();
+ if (ret) {
+ this.current = this.innerIterator.next();
+ return true;
+ }
+
+ if (sortShuffle != null) {
+ sortShuffle.close();
+ sortShuffle = null;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean isValid() {
+ // todo: check logic
+ return this.iterator.isValid() || hasNext();
+ }
+
+ @Override
+ public X next() {
+ if (current == null) {
+ throw new NoSuchElementException();
+ }
+
+ return (X) current;
+ }
+
+ @Override
+ public void close() {
+ this.iterator.close();
+ if (this.sortShuffle != null) {
+ this.sortShuffle.close();
+ }
+ }
+
+ @Override
+ public long count() {
+ return this.iterator.count();
+ }
+
+ @Override
+ public byte[] position() {
+ return this.iterator.position();
+ }
+
+ @Override
+ public void seek(byte[] position) {
+ this.iterator.seek(position);
+ }
+
+ private static class NoRepeatValueIterator implements Iterator {
+
+ private final Iterator iterator;
+ private final Comparator comparator;
+ private E prev = null;
+ private E data = null;
+ private int count = 0;
+
+ public NoRepeatValueIterator(Iterator iterator, Comparator comparator) {
+ this.count = 0;
+ this.iterator = iterator;
+ this.comparator = comparator;
+ }
+
+ @Override
+ public boolean hasNext() {
+ while (iterator.hasNext()) {
+ var n = iterator.next();
+ if (prev == null) {
+ // prev = iterator.next();
+ prev = n;
+ continue;
+ }
+
+ // E current = iterator.next();
+ E current = n;
+
+ if (comparator.compare(prev, current) == 0) {
+ count += 1;
+ } else {
+ if (count > 0) {
+ // --- pre is dup
+ prev = current;
+ } else {
+ data = prev;
+ prev = current;
+ return true;
+ }
+ count = 0;
+ }
+ }
+
+ // 最后一个结果
+ if (count == 0) {
+ data = prev;
+ count = 1;
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public E next() {
+ return data;
+ }
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/io/SortShuffleSerializer.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/io/SortShuffleSerializer.java
new file mode 100644
index 0000000000..c321c33a14
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/business/itrv2/io/SortShuffleSerializer.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.business.itrv2.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+import org.apache.hugegraph.backend.BackendColumn;
+import org.apache.hugegraph.rocksdb.access.RocksDBSession;
+import org.apache.hugegraph.serializer.BinaryElementSerializer;
+import org.apache.hugegraph.store.query.KvSerializer;
+import org.apache.hugegraph.store.query.Tuple2;
+import org.apache.hugegraph.store.util.MultiKv;
+import org.apache.hugegraph.structure.BaseEdge;
+import org.apache.hugegraph.structure.BaseElement;
+import org.apache.hugegraph.structure.BaseVertex;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * support backend column, Multi kv, BaseElement
+ * format : object | object | object
+ * todo: need write object type header ?
+ *
+ * @param object type
+ */
+@Slf4j
+public class SortShuffleSerializer {
+
+ private static final byte TYPE_HEADER_MULTI_KV = 1;
+ private static final byte TYPE_HEADER_BACKEND_COLUMN = 2;
+ private static final byte TYPE_HEADER_BASE_ELEMENT = 3;
+
+ private static SortShuffleSerializer backendSerializer =
+ new SortShuffleSerializer<>(new BackendColumnSerializer());
+
+ private static SortShuffleSerializer mkv =
+ new SortShuffleSerializer<>(new MultiKvSerializer());
+
+ private static SortShuffleSerializer element =
+ new SortShuffleSerializer<>(new BaseElementSerializer());
+
+ private final ObjectSerializer serializer;
+
+ private SortShuffleSerializer(ObjectSerializer serializer) {
+ this.serializer = serializer;
+ }
+
+ public static SortShuffleSerializer ofBackendColumnSerializer() {
+ return backendSerializer;
+ }
+
+ public static SortShuffleSerializer ofMultiKvSerializer() {
+ return mkv;
+ }
+
+ public static SortShuffleSerializer ofBaseElementSerializer() {
+ return element;
+ }
+
+ public static byte[] toByte(int i) {
+ byte[] result = new byte[4];
+ result[0] = (byte) ((i >> 24) & 0xff);
+ result[1] = (byte) ((i >> 16) & 0xff);
+ result[2] = (byte) ((i >> 8) & 0xff);
+ result[3] = (byte) (i & 0xff);
+ return result;
+ }
+
+ public static int toInt(byte[] b) {
+ assert b.length == 4;
+ int value = 0;
+ for (int i = 0; i < 4; i++) {
+ int shift = (3 - i) * 8;
+ value += (b[i] & 0xff) << shift;
+ }
+ return value;
+ }
+
+ private static byte[] kvBytesToByte(byte[] key, byte[] value) {
+
+ int len = (key == null ? 0 : key.length) + (value == null ? 0 : value.length) + 8;
+ ByteBuffer buffer = ByteBuffer.allocate(len);
+ buffer.putInt(key == null ? 0 : key.length);
+ if (key != null) {
+ buffer.put(key);
+ }
+ buffer.putInt(value == null ? 0 : value.length);
+ if (value != null) {
+ buffer.put(value);
+ }
+ return buffer.array();
+ }
+
+ private static Tuple2 fromKvBytes(byte[] bytes) {
+ assert bytes != null;
+ ByteBuffer buffer = ByteBuffer.wrap(bytes);
+
+ int nameLen = buffer.getInt();
+ byte[] name = null;
+ if (nameLen != 0) {
+ name = new byte[nameLen];
+ buffer.get(name);
+ }
+
+ int valueLen = buffer.getInt();
+ byte[] value = null;
+ if (valueLen != 0) {
+ value = new byte[valueLen];
+ buffer.get(value);
+ }
+
+ return Tuple2.of(name, value);
+ }
+
+ public void write(OutputStream output, T data) throws IOException {
+ // input.write(serializer.getTypeHeader());
+ var b = serializer.getBytes(data);
+ output.write(toByte(b.length));
+ output.write(b);
+ }
+
+ public T read(InputStream input) {
+ try {
+ var bytes = input.readNBytes(4);
+
+ if (bytes.length == 0) {
+ return null;
+ }
+
+ int sz = toInt(bytes);
+ return serializer.fromBytes(input.readNBytes(sz));
+ } catch (IOException e) {
+ log.debug("error: {}", e.getMessage());
+ return null;
+ }
+ }
+
+ private abstract static class ObjectSerializer {
+
+ public abstract T fromBytes(byte[] bytes);
+
+ public abstract byte[] getBytes(T t);
+
+ public abstract byte getTypeHeader();
+ }
+
+ /**
+ * format :
+ * key bytes len| key | value bytes len | value bytes
+ */
+
+ private static class MultiKvSerializer extends ObjectSerializer {
+
+ @Override
+ public MultiKv fromBytes(byte[] bytes) {
+ var tuple = fromKvBytes(bytes);
+ return MultiKv.of(KvSerializer.fromObjectBytes(tuple.getV1()),
+ KvSerializer.fromObjectBytes(tuple.getV2()));
+ }
+
+ @Override
+ public byte[] getBytes(MultiKv multiKv) {
+ return kvBytesToByte(KvSerializer.toBytes(multiKv.getKeys()),
+ KvSerializer.toBytes(multiKv.getValues()));
+ }
+
+ @Override
+ public byte getTypeHeader() {
+ return TYPE_HEADER_MULTI_KV;
+ }
+ }
+
+ /**
+ * format:
+ * name.len | name | value.len | value
+ */
+ private static class BackendColumnSerializer extends
+ ObjectSerializer {
+
+ @Override
+ public RocksDBSession.BackendColumn fromBytes(byte[] bytes) {
+ var tuple = fromKvBytes(bytes);
+ return RocksDBSession.BackendColumn.of(tuple.getV1(), tuple.getV2());
+ }
+
+ @Override
+ public byte[] getBytes(RocksDBSession.BackendColumn column) {
+ return kvBytesToByte(column.name, column.value);
+ }
+
+ @Override
+ public byte getTypeHeader() {
+ return TYPE_HEADER_BACKEND_COLUMN;
+ }
+ }
+
+ /**
+ * format:
+ * vertex/edge | name.len | name | value.len | value
+ */
+ private static class BaseElementSerializer extends ObjectSerializer {
+
+ private final BinaryElementSerializer serializer = new BinaryElementSerializer();
+
+ @Override
+ public BaseElement fromBytes(byte[] bytes) {
+ ByteBuffer buffer = ByteBuffer.wrap(bytes);
+
+ boolean isVertex = buffer.get() == 0;
+
+ int nameLen = buffer.getInt();
+ byte[] name = new byte[nameLen];
+ buffer.get(name);
+ int valueLen = buffer.getInt();
+ byte[] value = new byte[valueLen];
+ buffer.get(value);
+
+ if (isVertex) {
+ return serializer.parseVertex(null, BackendColumn.of(name, value), null);
+ }
+ return serializer.parseEdge(null, BackendColumn.of(name, value), null, true);
+ }
+
+ @Override
+ public byte[] getBytes(BaseElement element) {
+ assert element != null;
+
+ BackendColumn column;
+ boolean isVertex = false;
+ if (element instanceof BaseVertex) {
+ column = serializer.writeVertex((BaseVertex) element);
+ isVertex = true;
+ } else {
+ column = serializer.writeEdge((BaseEdge) element);
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(column.name.length + column.value.length + 9);
+ if (isVertex) {
+ buffer.put((byte) 0);
+ } else {
+ buffer.put((byte) 1);
+ }
+
+ buffer.putInt(column.name.length);
+ buffer.put(column.name);
+ buffer.putInt(column.value.length);
+ buffer.put(column.value);
+ return buffer.array();
+ }
+
+ @Override
+ public byte getTypeHeader() {
+ return TYPE_HEADER_BASE_ELEMENT;
+ }
+ }
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutRequest.java
index a776e6d4e1..ba51f82759 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutRequest.java
@@ -23,6 +23,7 @@
import lombok.Data;
@Data
+@Deprecated
public class BatchPutRequest extends HgCmdBase.BaseRequest {
private List entries;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutResponse.java
index 98a72f5655..ad2bd4b638 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/BatchPutResponse.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.store.cmd;
+@Deprecated
public class BatchPutResponse extends HgCmdBase.BaseResponse {
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataResponse.java
index f7773075de..ce4f5fa98a 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataResponse.java
@@ -16,7 +16,7 @@
*/
package org.apache.hugegraph.store.cmd;
-
+@Deprecated
public class CleanDataResponse extends HgCmdBase.BaseResponse {
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftRequest.java
index be5c384205..ad3cb063b4 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftRequest.java
@@ -28,6 +28,7 @@
import lombok.extern.slf4j.Slf4j;
@Slf4j
+@Deprecated
public class CreateRaftRequest extends HgCmdBase.BaseRequest {
List values = new ArrayList<>();
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftResponse.java
index 9e14ffc97d..8bfdf9c551 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CreateRaftResponse.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.store.cmd;
+@Deprecated
public class CreateRaftResponse extends HgCmdBase.BaseResponse {
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionResponse.java
index 228aae1078..ae589f212f 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionResponse.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.store.cmd;
+@Deprecated
public class DbCompactionResponse extends HgCmdBase.BaseResponse {
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftResponse.java
index cb24b2fc49..8d015f2f94 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftResponse.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.store.cmd;
+@Deprecated
public class DestroyRaftResponse extends HgCmdBase.BaseResponse {
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/FutureClosureAdapter.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/FutureClosureAdapter.java
index 8579a7d4f1..b7633160d4 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/FutureClosureAdapter.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/FutureClosureAdapter.java
@@ -22,6 +22,7 @@
import com.alipay.sofa.jraft.Closure;
import com.alipay.sofa.jraft.Status;
+@Deprecated
public class FutureClosureAdapter implements Closure {
public final CompletableFuture future = new CompletableFuture<>();
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoResponse.java
index df32cd99fe..7ed2e3d054 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoResponse.java
@@ -25,6 +25,7 @@
import lombok.extern.slf4j.Slf4j;
@Slf4j
+@Deprecated
public class GetStoreInfoResponse extends HgCmdBase.BaseResponse {
private byte[] store;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdBase.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdBase.java
index b612f3fc44..0f7923e210 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdBase.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdBase.java
@@ -32,6 +32,10 @@ public class HgCmdBase {
public static final byte ROCKSDB_COMPACTION = 0x05;
public static final byte CREATE_RAFT = 0x06;
public static final byte DESTROY_RAFT = 0x07;
+ public static final byte TTL_CLEAN = 0x08;
+ public static final byte BLANK_TASK = 0x09;
+
+ public static final byte REDIRECT_RAFT_TASK = 0x10;
@Data
public abstract static class BaseRequest implements Serializable {
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdClient.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdClient.java
index 6a73639e67..890dfe095f 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdClient.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdClient.java
@@ -23,6 +23,18 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import org.apache.hugegraph.store.cmd.request.BatchPutRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.cmd.request.CreateRaftRequest;
+import org.apache.hugegraph.store.cmd.request.DestroyRaftRequest;
+import org.apache.hugegraph.store.cmd.request.GetStoreInfoRequest;
+import org.apache.hugegraph.store.cmd.request.RedirectRaftTaskRequest;
+import org.apache.hugegraph.store.cmd.request.UpdatePartitionRequest;
+import org.apache.hugegraph.store.cmd.response.BatchPutResponse;
+import org.apache.hugegraph.store.cmd.response.CleanDataResponse;
+import org.apache.hugegraph.store.cmd.response.GetStoreInfoResponse;
+import org.apache.hugegraph.store.cmd.response.RedirectRaftTaskResponse;
+import org.apache.hugegraph.store.cmd.response.UpdatePartitionResponse;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.meta.Store;
@@ -137,6 +149,10 @@ public UpdatePartitionResponse raftUpdatePartition(UpdatePartitionRequest reques
return (UpdatePartitionResponse) tryInternalCallSyncWithRpc(request);
}
+ public RedirectRaftTaskResponse redirectRaftTask(RedirectRaftTaskRequest request) {
+ return (RedirectRaftTaskResponse) tryInternalCallSyncWithRpc(request);
+ }
+
/**
* Find Leader, retry on error, handle Leader redirection
*
@@ -165,6 +181,7 @@ public HgCmdBase.BaseResponse tryInternalCallSyncWithRpc(HgCmdBase.BaseRequest r
&& response.partitionLeaders != null
) {
// When returning leader drift, and partitionLeaders is not empty, need to reset the leader.
+ Thread.sleep(i * 1000L);
} else {
log.error(
"HgCmdClient tryInternalCallSyncWithRpc error msg {} leaders is {}",
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdProcessor.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdProcessor.java
index e0710ef97e..56bc7918bc 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdProcessor.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/HgCmdProcessor.java
@@ -22,6 +22,23 @@
import java.util.concurrent.TimeUnit;
import org.apache.hugegraph.store.HgStoreEngine;
+import org.apache.hugegraph.store.PartitionEngine;
+import org.apache.hugegraph.store.cmd.request.BatchPutRequest;
+import org.apache.hugegraph.store.cmd.request.BlankTaskRequest;
+import org.apache.hugegraph.store.cmd.request.CleanDataRequest;
+import org.apache.hugegraph.store.cmd.request.CreateRaftRequest;
+import org.apache.hugegraph.store.cmd.request.DestroyRaftRequest;
+import org.apache.hugegraph.store.cmd.request.GetStoreInfoRequest;
+import org.apache.hugegraph.store.cmd.request.RedirectRaftTaskRequest;
+import org.apache.hugegraph.store.cmd.request.UpdatePartitionRequest;
+import org.apache.hugegraph.store.cmd.response.BatchPutResponse;
+import org.apache.hugegraph.store.cmd.response.CleanDataResponse;
+import org.apache.hugegraph.store.cmd.response.CreateRaftResponse;
+import org.apache.hugegraph.store.cmd.response.DefaultResponse;
+import org.apache.hugegraph.store.cmd.response.DestroyRaftResponse;
+import org.apache.hugegraph.store.cmd.response.GetStoreInfoResponse;
+import org.apache.hugegraph.store.cmd.response.RedirectRaftTaskResponse;
+import org.apache.hugegraph.store.cmd.response.UpdatePartitionResponse;
import org.apache.hugegraph.store.meta.Partition;
import org.apache.hugegraph.store.raft.RaftClosure;
import org.apache.hugegraph.store.raft.RaftOperation;
@@ -56,6 +73,8 @@ public static void registerProcessor(final RpcServer rpcServer, final HgStoreEng
rpcServer.registerProcessor(new HgCmdProcessor<>(UpdatePartitionRequest.class, engine));
rpcServer.registerProcessor(new HgCmdProcessor<>(CreateRaftRequest.class, engine));
rpcServer.registerProcessor(new HgCmdProcessor<>(DestroyRaftRequest.class, engine));
+ rpcServer.registerProcessor(new HgCmdProcessor<>(BlankTaskRequest.class, engine));
+ rpcServer.registerProcessor(new HgCmdProcessor<>(ProcessBuilder.Redirect.class, engine));
}
@Override
@@ -93,6 +112,17 @@ public void handleRequest(RpcContext rpcCtx, T request) {
handleDestroyRaft((DestroyRaftRequest) request, (DestroyRaftResponse) response);
break;
}
+ case HgCmdBase.BLANK_TASK: {
+ response = new DefaultResponse();
+ addBlankTask((BlankTaskRequest) request, (DefaultResponse) response);
+ break;
+ }
+ case HgCmdBase.REDIRECT_RAFT_TASK: {
+ response = new RedirectRaftTaskResponse();
+ handleRedirectRaftTask((RedirectRaftTaskRequest) request,
+ (RedirectRaftTaskResponse) response);
+ break;
+ }
default: {
log.warn("HgCmdProcessor magic {} is not recognized ", request.magic());
}
@@ -138,6 +168,39 @@ public void handleDestroyRaft(DestroyRaftRequest request, DestroyRaftResponse re
response.setStatus(Status.OK);
}
+ public void handleRedirectRaftTask(RedirectRaftTaskRequest request,
+ RedirectRaftTaskResponse response) {
+ log.info("RedirectRaftTaskNode rpc call received, {}", request.getPartitionId());
+ raftSyncTask(request.getGraphName(), request.getPartitionId(), request.getRaftOp(),
+ request.getData(), response);
+ response.setStatus(Status.OK);
+ }
+
+ public void addBlankTask(BlankTaskRequest request, DefaultResponse response) {
+ try {
+ int partitionId = request.getPartitionId();
+ PartitionEngine pe = engine.getPartitionEngine(partitionId);
+ if (pe.isLeader()) {
+ CountDownLatch latch = new CountDownLatch(1);
+ RaftClosure closure = s -> {
+ if (s.isOk()) {
+ response.setStatus(Status.OK);
+ } else {
+ log.error("doBlankTask in cmd with error: {}", s.getErrorMsg());
+ response.setStatus(Status.EXCEPTION);
+ }
+ latch.countDown();
+ };
+ pe.addRaftTask(RaftOperation.create(RaftOperation.SYNC_BLANK_TASK), closure);
+ latch.await();
+ } else {
+ response.setStatus(Status.LEADER_REDIRECT);
+ }
+ } catch (Exception e) {
+ response.setStatus(Status.EXCEPTION);
+ }
+ }
+
/**
* raft notify replica synchronization execution
*
@@ -147,9 +210,14 @@ public void handleDestroyRaft(DestroyRaftRequest request, DestroyRaftResponse re
*/
private void raftSyncTask(HgCmdBase.BaseRequest request, HgCmdBase.BaseResponse response,
final byte op) {
+ raftSyncTask(request.getGraphName(), request.getPartitionId(), op, request, response);
+ }
+
+ private void raftSyncTask(String graph, int partId, byte op, Object raftReq,
+ HgCmdBase.BaseResponse response) {
CountDownLatch latch = new CountDownLatch(1);
- engine.addRaftTask(request.getGraphName(), request.getPartitionId(),
- RaftOperation.create(op, request), new RaftClosure() {
+ engine.addRaftTask(graph, partId,
+ RaftOperation.create(op, raftReq), new RaftClosure() {
@Override
public void run(com.alipay.sofa.jraft.Status status) {
Status responseStatus = Status.UNKNOWN;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionRequest.java
index 016b162870..12dbc372ce 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionRequest.java
@@ -22,6 +22,7 @@
import lombok.Data;
@Data
+@Deprecated
public class UpdatePartitionRequest extends HgCmdBase.BaseRequest {
private int startKey;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionResponse.java
index 49bb1c7cb5..5cec121442 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionResponse.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/UpdatePartitionResponse.java
@@ -17,6 +17,7 @@
package org.apache.hugegraph.store.cmd;
+@Deprecated
public class UpdatePartitionResponse extends HgCmdBase.BaseResponse {
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/BatchPutRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/BatchPutRequest.java
new file mode 100644
index 0000000000..1e09424da1
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/BatchPutRequest.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 org.apache.hugegraph.store.cmd.request;
+
+import java.io.Serializable;
+import java.util.List;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+import lombok.Data;
+
+@Data
+public class BatchPutRequest extends HgCmdBase.BaseRequest {
+
+ private List entries;
+
+ @Override
+ public byte magic() {
+ return HgCmdBase.BATCH_PUT;
+ }
+
+ @Data
+ public static class KV implements Serializable {
+
+ private String table;
+ private int code;
+ private byte[] key;
+ private byte[] value;
+
+ public static KV of(String table, int code, byte[] key, byte[] value) {
+ KV kv = new KV();
+ kv.table = table;
+ kv.code = code;
+ kv.key = key;
+ kv.value = value;
+ return kv;
+ }
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/BlankTaskRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/BlankTaskRequest.java
new file mode 100644
index 0000000000..08abc59a21
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/BlankTaskRequest.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 org.apache.hugegraph.store.cmd.request;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/8/21
+ **/
+public class BlankTaskRequest extends HgCmdBase.BaseRequest {
+
+ @Override
+ public byte magic() {
+ return HgCmdBase.BLANK_TASK;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/CleanDataRequest.java
similarity index 96%
rename from hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataRequest.java
rename to hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/CleanDataRequest.java
index 35540687bf..1fbfb5656d 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/CleanDataRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/CleanDataRequest.java
@@ -15,10 +15,11 @@
* limitations under the License.
*/
-package org.apache.hugegraph.store.cmd;
+package org.apache.hugegraph.store.cmd.request;
import org.apache.hugegraph.pd.grpc.pulse.CleanPartition;
import org.apache.hugegraph.pd.grpc.pulse.CleanType;
+import org.apache.hugegraph.store.cmd.HgCmdBase;
import org.apache.hugegraph.store.meta.Partition;
import lombok.Data;
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/CreateRaftRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/CreateRaftRequest.java
new file mode 100644
index 0000000000..1897c850c8
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/CreateRaftRequest.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 org.apache.hugegraph.store.cmd.request;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+import com.alipay.sofa.jraft.conf.Configuration;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class CreateRaftRequest extends HgCmdBase.BaseRequest {
+
+ List values = new ArrayList<>();
+ String peers;
+
+ public List getPartitions() {
+ try {
+ List partitions = new ArrayList<>();
+ for (byte[] partition : values) {
+ partitions.add(Metapb.Partition.parseFrom(partition));
+ }
+ return partitions;
+ } catch (InvalidProtocolBufferException e) {
+ log.error("CreateRaftNodeProcessor parse partition exception }", e);
+ }
+ return new ArrayList<>();
+ }
+
+ public void addPartition(Metapb.Partition partition) {
+ values.add(partition.toByteArray());
+ }
+
+ public Configuration getConf() {
+ Configuration conf = null;
+ if (peers != null) {
+ conf = new Configuration();
+ conf.parse(this.peers);
+ }
+ return conf;
+ }
+
+ public void setConf(Configuration conf) {
+ if (conf != null) {
+ this.peers = conf.toString();
+ }
+ }
+
+ @Override
+ public byte magic() {
+ return HgCmdBase.CREATE_RAFT;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/DbCompactionRequest.java
similarity index 91%
rename from hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionRequest.java
rename to hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/DbCompactionRequest.java
index 7952f170d1..5da60f0bf0 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DbCompactionRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/DbCompactionRequest.java
@@ -15,7 +15,9 @@
* limitations under the License.
*/
-package org.apache.hugegraph.store.cmd;
+package org.apache.hugegraph.store.cmd.request;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
import lombok.Data;
@@ -29,3 +31,4 @@ public byte magic() {
return HgCmdBase.ROCKSDB_COMPACTION;
}
}
+
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/DestroyRaftRequest.java
similarity index 87%
rename from hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftRequest.java
rename to hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/DestroyRaftRequest.java
index 10bf1c30b7..ecd7e7cf0e 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/DestroyRaftRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/DestroyRaftRequest.java
@@ -15,17 +15,19 @@
* limitations under the License.
*/
-package org.apache.hugegraph.store.cmd;
+package org.apache.hugegraph.store.cmd.request;
import java.util.ArrayList;
import java.util.List;
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
import lombok.Data;
@Data
public class DestroyRaftRequest extends HgCmdBase.BaseRequest {
- private final List graphNames = new ArrayList<>();
+ private List graphNames = new ArrayList<>();
public void addGraphName(String graphName) {
graphNames.add(graphName);
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/GetStoreInfoRequest.java
similarity index 90%
rename from hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoRequest.java
rename to hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/GetStoreInfoRequest.java
index 68f0d7f329..0b194a5051 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/GetStoreInfoRequest.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/GetStoreInfoRequest.java
@@ -15,7 +15,9 @@
* limitations under the License.
*/
-package org.apache.hugegraph.store.cmd;
+package org.apache.hugegraph.store.cmd.request;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
public class GetStoreInfoRequest extends HgCmdBase.BaseRequest {
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/RedirectRaftTaskRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/RedirectRaftTaskRequest.java
new file mode 100644
index 0000000000..efb430a696
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/RedirectRaftTaskRequest.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.request;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+import lombok.Data;
+
+@Data
+public class RedirectRaftTaskRequest extends HgCmdBase.BaseRequest {
+
+ final byte raftOp;
+
+ private Object data;
+
+ public RedirectRaftTaskRequest(String graph, Integer partitionId, byte raftOp, Object data) {
+ this.raftOp = raftOp;
+ this.data = data;
+ setGraphName(graph);
+ setPartitionId(partitionId);
+ }
+
+ @Override
+ public byte magic() {
+ return HgCmdBase.REDIRECT_RAFT_TASK;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/UpdatePartitionRequest.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/UpdatePartitionRequest.java
new file mode 100644
index 0000000000..430756178a
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/request/UpdatePartitionRequest.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.request;
+
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+import lombok.Data;
+
+@Data
+public class UpdatePartitionRequest extends HgCmdBase.BaseRequest {
+
+ private int startKey;
+ private int endKey;
+
+ private Metapb.PartitionState workState;
+
+ @Override
+ public byte magic() {
+ return HgCmdBase.RAFT_UPDATE_PARTITION;
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/BatchPutResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/BatchPutResponse.java
new file mode 100644
index 0000000000..c687a1c8ea
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/BatchPutResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class BatchPutResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/CleanDataResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/CleanDataResponse.java
new file mode 100644
index 0000000000..cfa9454166
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/CleanDataResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class CleanDataResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/CreateRaftResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/CreateRaftResponse.java
new file mode 100644
index 0000000000..c58dddfe1c
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/CreateRaftResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class CreateRaftResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DbCompactionResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DbCompactionResponse.java
new file mode 100644
index 0000000000..5c81833aa2
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DbCompactionResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class DbCompactionResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DefaultResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DefaultResponse.java
new file mode 100644
index 0000000000..bfa27cbe49
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DefaultResponse.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase.BaseResponse;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/8/21
+ **/
+public class DefaultResponse extends BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DestroyRaftResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DestroyRaftResponse.java
new file mode 100644
index 0000000000..0e037e0435
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/DestroyRaftResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class DestroyRaftResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/GetStoreInfoResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/GetStoreInfoResponse.java
new file mode 100644
index 0000000000..779c2785de
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/GetStoreInfoResponse.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 org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+import org.apache.hugegraph.store.meta.Store;
+
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class GetStoreInfoResponse extends HgCmdBase.BaseResponse {
+
+ private byte[] store;
+
+ public Store getStore() {
+ try {
+ return new Store(Metapb.Store.parseFrom(this.store));
+ } catch (InvalidProtocolBufferException e) {
+ log.error("GetStoreResponse parse exception {}", e);
+ }
+ return null;
+ }
+
+ public void setStore(Store store) {
+ this.store = store.getProtoObj().toByteArray();
+ }
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/RedirectRaftTaskResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/RedirectRaftTaskResponse.java
new file mode 100644
index 0000000000..9ee7ca45c5
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/RedirectRaftTaskResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class RedirectRaftTaskResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/UpdatePartitionResponse.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/UpdatePartitionResponse.java
new file mode 100644
index 0000000000..9901ab5428
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/cmd/response/UpdatePartitionResponse.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.cmd.response;
+
+import org.apache.hugegraph.store.cmd.HgCmdBase;
+
+public class UpdatePartitionResponse extends HgCmdBase.BaseResponse {
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/consts/PoolNames.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/consts/PoolNames.java
new file mode 100644
index 0000000000..b3d2ed586a
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/consts/PoolNames.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 org.apache.hugegraph.store.consts;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/10/30
+ **/
+public class PoolNames {
+
+ public static final String GRPC = "hg-grpc";
+ public static final String SCAN = "hg-scan";
+ public static final String SCAN_V2 = "hg-scan-v2";
+ public static final String I_JOB = "hg-i-job";
+ public static final String U_JOB = "hg-u-job";
+ public static final String COMPACT = "hg-compact";
+ public static final String HEARTBEAT = "hg-heartbeat";
+ public static final String P_HEARTBEAT = "hg-p-heartbeat";
+
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/PartitionChangedListener.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/PartitionChangedListener.java
new file mode 100644
index 0000000000..5b4e5f80f4
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/PartitionChangedListener.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.listener;
+
+import org.apache.hugegraph.store.cmd.request.UpdatePartitionRequest;
+import org.apache.hugegraph.store.cmd.response.UpdatePartitionResponse;
+import org.apache.hugegraph.store.meta.Partition;
+
+/**
+ * @author zhangyingjie
+ * @date 2023/9/11
+ * Partition对象被修改消息
+ **/
+public interface PartitionChangedListener {
+
+ void onChanged(Partition partition);
+
+ UpdatePartitionResponse rangeOrStateChanged(UpdatePartitionRequest request);
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/PartitionStateListener.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/PartitionStateListener.java
new file mode 100644
index 0000000000..06b9918005
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/PartitionStateListener.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.listener;
+
+import java.util.List;
+
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.meta.Partition;
+import org.apache.hugegraph.store.meta.PartitionRole;
+
+public interface PartitionStateListener {
+
+ // 分区角色发生改变
+ void partitionRoleChanged(Partition partition, PartitionRole newRole);
+
+ // 分区发生改变
+ void partitionShardChanged(Partition partition, List oldShards,
+ List newShards);
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/StoreStateListener.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/StoreStateListener.java
new file mode 100644
index 0000000000..11c607338b
--- /dev/null
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/listener/StoreStateListener.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hugegraph.store.listener;
+
+import org.apache.hugegraph.pd.grpc.Metapb;
+import org.apache.hugegraph.store.meta.Store;
+
+public interface StoreStateListener {
+
+ void stateChanged(Store store, Metapb.StoreState oldState, Metapb.StoreState newState);
+}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/GraphIdManager.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/GraphIdManager.java
index c98b03935d..92a8440ac3 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/GraphIdManager.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/GraphIdManager.java
@@ -18,19 +18,25 @@
package org.apache.hugegraph.store.meta;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
import org.apache.hugegraph.store.meta.base.DBSessionBuilder;
import org.apache.hugegraph.store.meta.base.PartitionMetaStore;
+import org.apache.hugegraph.store.term.Bits;
import org.apache.hugegraph.store.util.HgStoreException;
import com.google.protobuf.Int64Value;
+import lombok.extern.slf4j.Slf4j;
+
/**
* GraphId Manager, maintains a self-incrementing circular ID, responsible for managing the mapping between GraphName and GraphId.
*/
+@Slf4j
public class GraphIdManager extends PartitionMetaStore {
protected static final String GRAPH_ID_PREFIX = "@GRAPH_ID@";
@@ -39,27 +45,6 @@ public class GraphIdManager extends PartitionMetaStore {
static Object cidLock = new Object();
final DBSessionBuilder sessionBuilder;
final int partitionId;
- // public long getGraphId(String graphName) {
- // if (!graphIdCache.containsKey(graphName)) {
- // synchronized (graphIdLock) {
- // if (!graphIdCache.containsKey(graphName)) {
- // byte[] key = MetadataKeyHelper.getGraphIDKey(graphName);
- // Int64Value id = get(Int64Value.parser(), key);
- // if (id == null) {
- // id = Int64Value.of(getCId(GRAPH_ID_PREFIX, maxGraphID));
- // if (id.getValue() == -1) {
- // throw new HgStoreException(HgStoreException.EC_FAIL,
- // "The number of graphs exceeds the maximum 65535");
- // }
- // put(key, id);
- // flush();
- // }
- // graphIdCache.put(graphName, id.getValue());
- // }
- // }
- // }
- // return graphIdCache.get(graphName);
- // }
private final Map graphIdCache = new ConcurrentHashMap<>();
public GraphIdManager(DBSessionBuilder sessionBuilder, int partitionId) {
@@ -79,12 +64,34 @@ public long getGraphId(String graphName) {
byte[] key = MetadataKeyHelper.getGraphIDKey(graphName);
Int64Value id = get(Int64Value.parser(), key);
if (id == null) {
- id = Int64Value.of(getCId(GRAPH_ID_PREFIX, maxGraphID));
+ id = Int64Value.of(maxGraphID);
+ }
+ l = id.getValue();
+ graphIdCache.put(graphName, l);
+ }
+ }
+ }
+ return l;
+ }
+
+ public long getGraphIdOrCreate(String graphName) {
+
+ Long l = graphIdCache.get(graphName);
+ if (l == null || l == maxGraphID) {
+ synchronized (graphIdLock) {
+ if ((l = graphIdCache.get(graphName)) == null || l == maxGraphID) {
+ byte[] key = MetadataKeyHelper.getGraphIDKey(graphName);
+ Int64Value id = get(Int64Value.parser(), key);
+ if (id == null) {
+ id = Int64Value.of(getCId(GRAPH_ID_PREFIX, maxGraphID - 1));
if (id.getValue() == -1) {
throw new HgStoreException(HgStoreException.EC_FAIL,
"The number of graphs exceeds the maximum " +
"65535");
}
+ log.info("partition: {}, Graph ID {} is allocated for graph {}, stack: {}",
+ this.partitionId, id.getValue(), graphName,
+ Arrays.toString(Thread.currentThread().getStackTrace()));
put(key, id);
flush();
}
@@ -112,7 +119,20 @@ public long releaseGraphId(String graphName) {
}
/**
- * Get auto-increment non-repetitive id, start from 0 after reaching the limit.
+ * 为了兼容受影响的 graph,保证 g+v 表中没有数据
+ *
+ * @return 有数据返回 false,没有返回 true
+ */
+ private boolean checkCount(long l) {
+ var start = new byte[2];
+ Bits.putShort(start, 0, (short) l);
+ try (var itr = sessionBuilder.getSession(partitionId).sessionOp().scan("g+v", start)) {
+ return itr == null || !itr.hasNext();
+ }
+ }
+
+ /**
+ * 获取自增循环不重复 id, 达到上限后从 0 开始自增
*
* @param key key
* @param max max id limit, after reaching this value, it will reset to 0 and start incrementing again.
@@ -127,24 +147,19 @@ protected long getCId(String key, long max) {
// Find an unused cid
List ids =
scan(Int64Value.parser(), genCIDSlotKey(key, current), genCIDSlotKey(key, max));
- for (Int64Value id : ids) {
- if (current == id.getValue()) {
+ var idSet = ids.stream().map(Int64Value::getValue).collect(Collectors.toSet());
+
+ while (idSet.contains(current) || !checkCount(current)) {
current++;
- } else {
- break;
- }
}
- if (current == max) {
+ if (current == max - 1) {
current = 0;
ids = scan(Int64Value.parser(), genCIDSlotKey(key, current),
genCIDSlotKey(key, last));
- for (Int64Value id : ids) {
- if (current == id.getValue()) {
+ idSet = ids.stream().map(Int64Value::getValue).collect(Collectors.toSet());
+ while (idSet.contains(current) || !checkCount(current)) {
current++;
- } else {
- break;
- }
}
}
@@ -162,7 +177,7 @@ protected long getCId(String key, long max) {
/**
* Return key with used Cid
*/
- private byte[] genCIDSlotKey(String key, long value) {
+ public byte[] genCIDSlotKey(String key, long value) {
byte[] keySlot = MetadataKeyHelper.getCidSlotKeyPrefix(key);
ByteBuffer buf = ByteBuffer.allocate(keySlot.length + Long.SIZE);
buf.put(keySlot);
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/PartitionManager.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/PartitionManager.java
index ffd1349a91..ac98d39282 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/PartitionManager.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/PartitionManager.java
@@ -26,6 +26,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
@@ -37,8 +38,10 @@
import org.apache.hugegraph.pd.grpc.Metapb;
import org.apache.hugegraph.store.HgStoreEngine;
import org.apache.hugegraph.store.business.BusinessHandlerImpl;
-import org.apache.hugegraph.store.cmd.UpdatePartitionRequest;
-import org.apache.hugegraph.store.cmd.UpdatePartitionResponse;
+import org.apache.hugegraph.store.cmd.HgCmdClient;
+import org.apache.hugegraph.store.cmd.request.UpdatePartitionRequest;
+import org.apache.hugegraph.store.cmd.response.UpdatePartitionResponse;
+import org.apache.hugegraph.store.listener.PartitionChangedListener;
import org.apache.hugegraph.store.meta.base.GlobalMetaStore;
import org.apache.hugegraph.store.options.HgStoreEngineOptions;
import org.apache.hugegraph.store.options.MetadataOptions;
@@ -72,6 +75,7 @@ public class PartitionManager extends GlobalMetaStore {
// Record all partition information of this machine, consistent with rocksdb storage.
private Map> partitions;
+ private HgCmdClient cmdClient;
public PartitionManager(PdProvider pdProvider, HgStoreEngineOptions options) {
super(new MetadataOptions() {{
@@ -225,7 +229,7 @@ private void loadPartitions() {
var partIds = new HashSet();
for (String path : this.options.getDataPath().split(",")) {
File[] dirs = new File(path + "/" + HgStoreEngineOptions.DB_Path_Prefix).listFiles();
- if (dirs == null) {
+ if (dirs == null || dirs.length == 0) {
continue;
}
@@ -241,6 +245,8 @@ private void loadPartitions() {
}
}
+ Set normalPartitions = new HashSet<>();
+
// Once according to the partition read
for (int partId : partIds) {
if (!resetPartitionPath(partId)) {
@@ -249,18 +255,23 @@ private void loadPartitions() {
continue;
}
- for (var metaPart : wrapper.scan(partId, Metapb.Partition.parser(), key)) {
+ var metaParts = wrapper.scan(partId, Metapb.Partition.parser(), key);
+ int countOfPartition = 0;
+
+ var shards = pdProvider.getShardGroup(partId).getShardsList();
+
+ for (var metaPart : metaParts) {
var graph = metaPart.getGraphName();
var pdPartition = pdProvider.getPartitionByID(graph, metaPart.getId());
boolean isLegeal = false;
- var shards = pdProvider.getShardGroup(metaPart.getId()).getShardsList();
-
if (pdPartition != null) {
// Check if it contains this store id
if (shards.stream().anyMatch(s -> s.getStoreId() == storeId)) {
isLegeal = true;
}
+ } else {
+ continue;
}
if (isLegeal) {
@@ -268,6 +279,8 @@ private void loadPartitions() {
partitions.put(graph, new ConcurrentHashMap<>());
}
+ countOfPartition += 1;
+
Partition partition = new Partition(metaPart);
partition.setWorkState(Metapb.PartitionState.PState_Normal); // Start recovery work state
partitions.get(graph).put(partition.getId(), partition);
@@ -284,6 +297,19 @@ private void loadPartitions() {
System.exit(0);
}
}
+
+ if (countOfPartition > 0) {
+ // 分区数据正常
+ normalPartitions.add(partId);
+ }
+ wrapper.close(partId);
+ }
+
+ // 删掉多余的分区存储路径,被迁移走的分区,有可能还会迁回来
+ for (var location : storeMetadata.getPartitionStores()) {
+ if (!normalPartitions.contains(location.getPartitionId())) {
+ storeMetadata.removePartitionStore(location.getPartitionId());
+ }
}
}
@@ -611,7 +637,7 @@ public ShardGroup getShardGroup(int partitionId) {
Metapb.ShardGroup.parser());
if (shardGroup == null) {
- shardGroup = pdProvider.getShardGroup(partitionId);
+ shardGroup = pdProvider.getShardGroupDirect(partitionId);
if (shardGroup != null) {
// local not found, write back to db from pd
@@ -726,6 +752,18 @@ public List getLeaderPartitionIds(String graph) {
return ids;
}
+ public Set getLeaderPartitionIdSet() {
+ Set ids = new HashSet<>();
+ partitions.forEach((key, value) -> {
+ value.forEach((k, v) -> {
+ if (!useRaft || v.isLeader()) {
+ ids.add(k);
+ }
+ });
+ });
+ return ids;
+ }
+
/**
* Generate partition peer string, containing priority information *
*
@@ -833,15 +871,15 @@ public Store getStoreByRaftEndpoint(ShardGroup group, String endpoint) {
return result[0];
}
- public Shard getShardByRaftEndpoint(ShardGroup group, String endpoint) {
- final Shard[] result = {new Shard()};
- group.getShards().forEach((shard) -> {
+ public Shard getShardByEndpoint(ShardGroup group, String endpoint) {
+ List shards = group.getShards();
+ for (Shard shard : shards) {
Store store = getStore(shard.getStoreId());
if (store != null && store.getRaftAddress().equalsIgnoreCase(endpoint)) {
- result[0] = shard;
+ return shard;
}
- });
- return result[0];
+ }
+ return new Shard();
}
/**
@@ -885,6 +923,16 @@ public String getDbDataPath(int partitionId, String dbName) {
return location;
}
+ /**
+ * db 存储路径
+ *
+ * @return location/db
+ */
+ public String getDbDataPath(int partitionId) {
+ String dbName = BusinessHandlerImpl.getDbName(partitionId);
+ return getDbDataPath(partitionId, dbName);
+ }
+
public void reportTask(MetaTask.Task task) {
try {
pdProvider.reportTask(task);
@@ -908,14 +956,39 @@ public PartitionMetaStoreWrapper getWrapper() {
return wrapper;
}
- /**
- * Partition object is modified message
- */
- public interface PartitionChangedListener {
+ public void setCmdClient(HgCmdClient client) {
+ this.cmdClient = client;
+ }
- void onChanged(Partition partition);
+ public UpdatePartitionResponse updateState(Metapb.Partition partition,
+ Metapb.PartitionState state) {
+ // 分区分裂时,主动需要查找 leader 进行同步信息
+ UpdatePartitionRequest request = new UpdatePartitionRequest();
+ request.setWorkState(state);
+ request.setPartitionId(partition.getId());
+ request.setGraphName(partition.getGraphName());
+ return cmdClient.raftUpdatePartition(request);
+ }
- UpdatePartitionResponse rangeOrStateChanged(UpdatePartitionRequest request);
+ public UpdatePartitionResponse updateRange(Metapb.Partition partition, int startKey,
+ int endKey) {
+ // 分区分裂时,主动需要查找 leader 进行同步信息
+ UpdatePartitionRequest request = new UpdatePartitionRequest();
+ request.setStartKey(startKey);
+ request.setEndKey(endKey);
+ request.setPartitionId(partition.getId());
+ request.setGraphName(partition.getGraphName());
+ return cmdClient.raftUpdatePartition(request);
+ }
+
+ public List getPartitionIds(String graph) {
+ List ids = new ArrayList<>();
+ if (partitions.containsKey(graph)) {
+ partitions.get(graph).forEach((k, v) -> {
+ ids.add(k);
+ });
+ }
+ return ids;
}
}
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/ShardGroup.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/ShardGroup.java
index 892af940b3..4b3a5a618f 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/ShardGroup.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/ShardGroup.java
@@ -52,11 +52,17 @@ public static ShardGroup from(Metapb.ShardGroup meta) {
shardGroup.setId(meta.getId());
shardGroup.setVersion(meta.getVersion());
shardGroup.setConfVersion(meta.getConfVer());
- shardGroup.setShards(meta.getShardsList().stream().map(Shard::fromMetaPbShard)
- .collect(Collectors.toList()));
+ shardGroup.setShards(new CopyOnWriteArrayList<>(
+ meta.getShardsList().stream().map(Shard::fromMetaPbShard)
+ .collect(Collectors.toList())));
return shardGroup;
}
+ public ShardGroup addShard(Shard shard) {
+ this.shards.add(shard);
+ return this;
+ }
+
public synchronized ShardGroup changeLeader(long storeId) {
shards.forEach(shard -> {
shard.setRole(shard.getStoreId() == storeId ? Metapb.ShardRole.Leader :
diff --git a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/StoreMetadata.java b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/StoreMetadata.java
index 662b6521f1..6fb54710cc 100644
--- a/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/StoreMetadata.java
+++ b/hugegraph-store/hg-store-core/src/main/java/org/apache/hugegraph/store/meta/StoreMetadata.java
@@ -24,6 +24,7 @@
import java.util.List;
import java.util.Map;
+import org.apache.commons.io.FileUtils;
import org.apache.hugegraph.pd.grpc.Metapb;
import org.apache.hugegraph.store.meta.base.GlobalMetaStore;
import org.apache.hugegraph.store.options.MetadataOptions;
@@ -115,6 +116,16 @@ public Metapb.PartitionStore getPartitionStore(int partitionId) {
return get(Metapb.PartitionStore.parser(), key);
}
+ /**
+ * 删除指定分区对应的存储元数据。
+ *
+ * @param partitionId 分区ID。
+ */
+ public void removePartitionStore(int partitionId) {
+ byte[] key = MetadataKeyHelper.getPartitionStoreKey(partitionId);
+ delete(key);
+ }
+
public List getPartitionStores() {
byte[] key = MetadataKeyHelper.getPartitionStorePrefix();
return scan(Metapb.PartitionStore.parser(), key);
@@ -141,16 +152,14 @@ public void savePartitionRaft(Metapb.PartitionRaft partitionRaft) {
}
private String getMinDataLocation() {
- Map counter = new HashMap<>();
- dataLocations.forEach(l -> {
- counter.put(l, Integer.valueOf(0));
- });
- getPartitionStores().forEach(ptStore -> {
- if (counter.containsKey(ptStore.getStoreLocation())) {
- counter.put(ptStore.getStoreLocation(),
- counter.get(ptStore.getStoreLocation()) + 1);
+ var counter = stateLocByFreeSpace();
+ if (counter.isEmpty()) {
+ counter = stateLocByPartitionCount();
+ log.info("allocate db path using partition count: db count stats: {}", counter);
+ } else {
+ log.info("allocate db path using free space: db size stats: {}", counter);
}
- });
+
int min = Integer.MAX_VALUE;
String location = "";
for (String k : counter.keySet()) {
@@ -162,6 +171,91 @@ private String getMinDataLocation() {
return location;
}
+ /**
+ * get location count by allocated db count
+ *
+ * @return loc -> db count
+ */
+ private Map stateLocByPartitionCount() {
+ Map