blob: 0c25d78c38f85a1b93a7e4ce31adf3477046f1db [file] [log] [blame]
/*
* 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.rest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.hugegraph.pd.common.PDException;
import org.apache.hugegraph.pd.grpc.Metapb;
import org.apache.hugegraph.pd.grpc.Pdpb;
import org.apache.hugegraph.pd.model.GraphRestRequest;
import org.apache.hugegraph.pd.model.RestApiResponse;
import org.apache.hugegraph.pd.service.PDRestService;
import org.apache.hugegraph.pd.service.PDService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
@RestController
@Slf4j
@RequestMapping("/v1")
public class GraphAPI extends API {
@Autowired
PDRestService pdRestService;
@Autowired
PDService pdService;
@GetMapping(value = "/graph/partitionSizeRange", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public RestApiResponse getPartitionSizeRange() {
try {
int minPartitionSize = 1;
int maxPartitionSize = pdService.getStoreNodeService().getShardGroups().size();
Map<String, Integer> dataMap = new HashMap<>();
dataMap.put("minPartitionSize", minPartitionSize);
dataMap.put("maxPartitionSize", maxPartitionSize);
return new RestApiResponse(dataMap, Pdpb.ErrorType.OK, Pdpb.ErrorType.OK.name());
} catch (PDException e) {
log.error("PDException:", e);
return new RestApiResponse(null, e.getErrorCode(), e.getMessage());
}
}
@GetMapping(value = "/graphs", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public RestApiResponse getGraphs() {
RestApiResponse response = new RestApiResponse();
try {
List<Metapb.Graph> graphs = pdRestService.getGraphs();
List<GraphStatistics> resultGraphs = new ArrayList<>();
for (Metapb.Graph graph : graphs) {
if ((graph.getGraphName() != null) && (graph.getGraphName().endsWith("/g"))) {
resultGraphs.add(new GraphStatistics(graph));
}
}
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put("graphs", resultGraphs);
response.setData(dataMap);
response.setStatus(Pdpb.ErrorType.OK.getNumber());
response.setMessage(Pdpb.ErrorType.OK.name());
} catch (PDException e) {
log.error("PDException: ", e);
response.setData(new HashMap<String, Object>());
response.setStatus(e.getErrorCode());
response.setMessage(e.getMessage());
}
return response;
}
@PostMapping(value = "/graph/**", consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public String setGraph(@RequestBody GraphRestRequest body, HttpServletRequest request) {
try {
String requestURL = request.getRequestURL().toString();
final String prefix = "/graph/";
final int limit = 2;
String graphName = requestURL.split(prefix, limit)[1];
graphName = URLDecoder.decode(graphName, StandardCharsets.UTF_8);
Metapb.Graph curGraph = pdRestService.getGraph(graphName);
Metapb.Graph.Builder builder = Metapb.Graph.newBuilder(
curGraph == null ? Metapb.Graph.getDefaultInstance() : curGraph);
builder.setGraphName(graphName);
if (body.getPartitionCount() > 0) {
builder.setPartitionCount(body.getPartitionCount());
}
Metapb.Graph newGraph = pdRestService.updateGraph(builder.build());
return toJSON(newGraph, "graph");
} catch (PDException exception) {
return toJSON(exception);
} catch (Exception e) {
return toJSON(e);
}
}
@GetMapping(value = "/graph/**", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public RestApiResponse getGraph(HttpServletRequest request) throws
UnsupportedEncodingException {
RestApiResponse response = new RestApiResponse();
GraphStatistics statistics = null;
String requestURL = request.getRequestURL().toString();
final String prefix = "/graph/";
final int limit = 2;
String graphName = requestURL.split(prefix, limit)[1];
graphName = URLDecoder.decode(graphName, StandardCharsets.UTF_8);
try {
Metapb.Graph graph = pdRestService.getGraph(graphName);
if (graph != null) {
statistics = new GraphStatistics(graph);
response.setData(statistics);
} else {
response.setData(new HashMap<String, Object>());
}
response.setStatus(Pdpb.ErrorType.OK.getNumber());
response.setMessage(Pdpb.ErrorType.OK.name());
} catch (PDException e) {
log.error(e.getMessage());
response.setData(new HashMap<String, Object>());
response.setStatus(Pdpb.ErrorType.UNKNOWN.getNumber());
response.setMessage(e.getMessage());
}
return response;
}
@Data
class Shard {
long partitionId;
long storeId;
String state;
String role;
int progress;
public Shard(Metapb.ShardStats shardStats, long partitionId) {
this.role = String.valueOf(shardStats.getRole());
this.storeId = shardStats.getStoreId();
this.state = String.valueOf(shardStats.getState());
this.partitionId = partitionId;
this.progress = shardStats.getProgress();
}
public Shard(Metapb.Shard shard, long partitionId) {
this.role = String.valueOf(shard.getRole());
this.storeId = shard.getStoreId();
this.state = Metapb.ShardState.SState_Normal.name();
this.progress = 0;
this.partitionId = partitionId;
}
}
@Data
class Partition {
int partitionId;
String graphName;
String workState;
long startKey;
long endKey;
List<Shard> shards;
long dataSize;
public Partition(Metapb.Partition pt, Metapb.PartitionStats partitionStats) {
if (pt != null) {
partitionId = pt.getId();
startKey = pt.getStartKey();
endKey = pt.getEndKey();
workState = String.valueOf(pt.getState());
graphName = pt.getGraphName();
final int postfixLength = 2;
graphName = graphName.substring(0, graphName.length() - postfixLength);
if (partitionStats != null) {
List<Metapb.ShardStats> shardStatsList = partitionStats.getShardStatsList();
List<Shard> shardsList = new ArrayList<>();
for (Metapb.ShardStats shardStats : shardStatsList) {
Shard shard = new Shard(shardStats, partitionId);
shardsList.add(shard);
}
this.shards = shardsList;
} else {
List<Shard> shardsList = new ArrayList<>();
try {
var shardGroup = pdService.getStoreNodeService().getShardGroup(pt.getId());
if (shardGroup != null) {
for (Metapb.Shard shard1 : shardGroup.getShardsList()) {
shardsList.add(new Shard(shard1, partitionId));
}
} else {
log.error("GraphAPI.Partition(), get shard group: {} returns null",
pt.getId());
}
} catch (PDException e) {
log.error("Partition init failed, error: {}", e.getMessage());
}
this.shards = shardsList;
}
}
}
}
@Data
class GraphStatistics {
// Graph statistics
String graphName;
long partitionCount;
String state;
List<Partition> partitions;
long dataSize;
//todo
int nodeCount;
int edgeCount;
long keyCount;
public GraphStatistics(Metapb.Graph graph) throws PDException {
if (graph == null) {
return;
}
Map<Integer, Long> partition2DataSize = new HashMap<>();
graphName = graph.getGraphName();
partitionCount = graph.getPartitionCount();
state = String.valueOf(graph.getState());
// The amount of data and the number of keys
List<Metapb.Store> stores = pdRestService.getStores(graphName);
for (Metapb.Store store : stores) {
List<Metapb.GraphStats> graphStatsList = store.getStats().getGraphStatsList();
for (Metapb.GraphStats graphStats : graphStatsList) {
if ((graphName.equals(graphStats.getGraphName()))
&& (Metapb.ShardRole.Leader.equals(graphStats.getRole()))) {
keyCount += graphStats.getApproximateKeys();
dataSize += graphStats.getApproximateSize();
partition2DataSize.put(graphStats.getPartitionId(),
graphStats.getApproximateSize());
}
}
}
List<Partition> resultPartitionList = new ArrayList<>();
List<Metapb.Partition> tmpPartitions = pdRestService.getPartitions(graphName);
if ((tmpPartitions != null) && (!tmpPartitions.isEmpty())) {
// The partition information to be returned
for (Metapb.Partition partition : tmpPartitions) {
Metapb.PartitionStats partitionStats = pdRestService
.getPartitionStats(graphName, partition.getId());
Partition pt = new Partition(partition, partitionStats);
pt.dataSize = partition2DataSize.getOrDefault(partition.getId(), 0L);
resultPartitionList.add(pt);
}
}
partitions = resultPartitionList;
// Hide /g /m /s after the title of the graph
final int postfixLength = 2;
graphName = graphName.substring(0, graphName.length() - postfixLength);
}
}
}