RPC API
This document mainly introduces the details of calling TuGraph’s RPC API.
1.Introduction
TuGraph provides rich RPC APIs for developers to remotely call the services provided by TuGraph through RPC requests.
RPC (Remote Procedure Call) is a protocol for requesting services from remote computer programs through a network without the need to understand the underlying network technology. Compared with REST, RPC is method-oriented and mainly used for calling function methods. It is suitable for more complex communication scenarios and has higher performance. brpc is an industrial-grade RPC framework written in C++. Based on brpc, TuGraph provides rich RPC APIs. This document describes how to use TuGraph’s RPC API.
2.Request
2.1.Establishing a Connection
When developers send RPC requests to TuGraph services, they must first establish a connection. Taking C++ as an example, developers create a channel with the specified URL and create a specified service stub (LGraphRPCService_Stub) from the channel. Subsequently, they can send requests to the remote server through the stub like calling local methods.
std::shared_ptr<lgraph_rpc::m_channel_options> options = std::make_shared<lgraph_rpc::m_channel_options>();
options->protocol = "baidu_std";
options->connection_type = "";
options->timeout_ms = 60 * 60 * 1000 /*milliseconds*/;
options->max_retry = 3;
std::string load_balancer = "";
std::shared_ptr<lgraph_rpc::m_channel> channel = std::make_shared<lgraph_rpc::m_channel>();
if (channel->Init(url.c_str(), load_balancer, options.get()) != 0)
throw RpcException("Fail to initialize channel");
LGraphRPCService_Stub stub(channel.get());
2.2.Request Types
TuGraph supports 10 types of RPC requests, and each request’s functionality is shown in the following table:
Request |
Functionality |
---|---|
GraphApiRequest |
Vertex-Edge Index |
CypherRequest |
cypher |
PluginRequest |
Stored Procedures |
HARequest |
High Availability |
ImportRequest |
Data Import |
GraphRequest |
Subgraph Operations |
AclRequest |
Access Control |
ConfigRequest |
Configuration |
RestoreRequest |
Backup |
SchemaRequest |
Schema Management |
When a user sends a request, the following parameters need to be passed in:
client_version: an optional parameter, in HA mode, it can prevent outdated responses by comparing
client_version
andserver_version
token: a necessary parameter, the client obtains the token after logging in, and the token is passed in with each request to verify the user’s identity
is_write_op: an optional parameter, indicating whether the request is a write request
user: an optional parameter, set user when synchronizing requests between master and slave in HA mode, and no token verification is required After the service processes the RPC request, it sends back a response.
In addition to containing separate response information for each request, the response message also includes the following parameters:
error_code: a necessary parameter, indicating the processing status of the request
redirect: an optional parameter, when processing fails to send a write request to a follower in HA mode, set redirect as the request forwarding address, that is, the leader address
error: an optional parameter, indicating the error information of the request
server_version: an optional parameter, set
server_version
in the HA mode request response to avoid reverse time travel when client reads data
:warning: Except for CypherRequest, PluginRequest, HARequest and AclRequest, all other RPC interfaces will be gradually deprecated, and their functions will be unified into the CypherRequest interface.
3.Login
The login request message contains the following parameters:
user: a necessary parameter, the username
pass: a necessary parameter, the password
Taking C++ as an example, the user sends a login request using the constructed service stub:
auto* req = request.mutable_acl_request();
auto* auth = req->mutable_auth_request()->mutable_login();
auth->set_user(user);
auth->set_password(pass);
// send data
cntl->Reset();
cntl->request_attachment().append(FLAGS_attachment);
req->set_client_version(server_version);
req->set_token(token);
LGraphRPCService_Stub stub(channel.get());
LGraphResponse res;
stub.HandleRequest(cntl.get(), req, &resp, nullptr);
if (cntl->Failed()) throw RpcConnectionException(cntl->ErrorText());
server_version = std::max(server_version, res.server_version());
if (res.error_code() != LGraphResponse::SUCCESS) throw RpcStatusException(res.error());
token = res.acl_response().auth_response().token();
The login response message contains the following parameters:
token: a necessary parameter. After successful login, a signed token, namely Json Web Token, will be received. The client stores the token and uses it for each subsequent request. If the login fails, an “Authentication failed” error will be received.
4.Query
Users can interact with TuGraph through Cypher queries. The Cypher request message contains the following parameters:
query: a necessary parameter, the Cypher query statement
param_names: an optional parameter, the parameter name
param_values: an optional parameter, the parameter value
result_in_json_format: a necessary parameter, whether to return the query results in JSON format
graph: an optional parameter, the subgraph name for executing the Cypher statement
timeout: an optional parameter, the timeout for executing the Cypher statement
Taking C++ as an example, the user sends a Cypher request as follows:
LGraphResponse res;
cntl->Reset();
cntl->request_attachment().append(FLAGS_attachment);
LGraphRequest req;
req.set_client_version(server_version);
req.set_token(token);
lgraph::CypherRequest* cypher_req = req.mutable_cypher_request();
cypher_req->set_graph(graph);
cypher_req->set_query(query);
cypher_req->set_timeout(timeout);
cypher_req->set_result_in_json_format(true);
LGraphRPCService_Stub stub(channel.get());
stub.HandleRequest(cntl.get(), &req, &res, nullptr);
if (cntl->Failed()) throw RpcConnectionException(cntl->ErrorText());
if (res.error_code() != LGraphResponse::SUCCESS) throw RpcStatusException(res.error());
server_version = std::max(server_version, res.server_version());
CypherResponse cypher_res = res.cypher_response();
The Cypher request response contains one of the following two parameters:
json_result: the Cypher query result in JSON format
binary_result: the Cypher query result in the CypherResult format
5.Stored Procedures
To meet users’ more complex query/update logic, TuGraph supports stored procedures written in C and Python. Users can use RPC requests to perform CRUD operations on stored procedures.
5.1.Load Stored Procedures
The request for loading stored procedures contains the following parameters:
name: a necessary parameter, the stored procedure name
read_only: a necessary parameter, whether it is read-only
code: a necessary parameter, the ByteString generated by reading the stored procedure file
desc: an optional parameter, the stored procedure description
code_type: an optional parameter, the stored procedure code type, which can be PY, SO, CPP, or ZIP
Taking C++ as an example, the user loads the stored procedure as follows:
std::string content;
if (!FieldSpecSerializer::FileReader(source_file, content)) {
std::swap(content, result);
return false;
}
LGraphRequest req;
req.set_is_write_op(true);
lgraph::PluginRequest* pluginRequest = req.mutable_plugin_request();
pluginRequest->set_graph(graph);
pluginRequest->set_type(procedure_type == "CPP" ? lgraph::PluginRequest::CPP
: lgraph::PluginRequest::PYTHON);
pluginRequest->set_version(version);
lgraph::LoadPluginRequest* loadPluginRequest = pluginRequest->mutable_load_plugin_request();
loadPluginRequest->set_code_type([](const std::string& type) {
std::unordered_map<std::string, lgraph::LoadPluginRequest_CodeType> um{
{"SO", lgraph::LoadPluginRequest::SO},
{"PY", lgraph::LoadPluginRequest::PY},
{"ZIP", lgraph::LoadPluginRequest::ZIP},
{"CPP", lgraph::LoadPluginRequest::CPP}};
return um[type];
}(code_type));
loadPluginRequest->set_name(procedure_name);
loadPluginRequest->set_desc(procedure_description);
loadPluginRequest->set_read_only(read_only);
loadPluginRequest->set_code(content);
cntl->Reset();
cntl->request_attachment().append(FLAGS_attachment);
req.set_client_version(server_version);
req.set_token(token);
LGraphRPCService_Stub stub(channel.get());
LGraphResponse res;
stub.HandleRequest(cntl.get(), &req, &res, nullptr);
if (cntl->Failed()) throw RpcConnectionException(cntl->ErrorText());
server_version = std::max(server_version, res.server_version());
if (res.error_code() != LGraphResponse::SUCCESS) throw RpcStatusException(res.error());
The response for loading the stored procedure does not contain parameters, and if the loading fails, a BadInput exception will be thrown.
5.2.Invoke Stored Procedures
The request for invoking stored procedures contains the following parameters:
name: a necessary parameter, the stored procedure name
param: a necessary parameter, the stored procedure parameters
result_in_json_format: an optional parameter, whether to return the invocation result in JSON format
in_process: an optional parameter, to be supported in the future
timeout: an optional parameter, the timeout for invoking the stored procedure
Taking C++ as an example, the user invokes the stored procedure as follows:
LGraphRequest req;
lgraph::PluginRequest* pluginRequest = req.mutable_plugin_request();
pluginRequest->set_graph(graph);
pluginRequest->set_type(procedure_type == "CPP" ? lgraph::PluginRequest::CPP
: lgraph::PluginRequest::PYTHON);
lgraph::CallPluginRequest *cpRequest = pluginRequest->mutable_call_plugin_request();
cpRequest->set_name(procedure_name);
cpRequest->set_in_process(in_process);
cpRequest->set_param(param);
cpRequest->set_timeout(procedure_time_out);
cpRequest->set_result_in_json_format(json_format);
LGraphResponse res;
cntl->Reset();
cntl->request_attachment().append(FLAGS_attachment);
req.set_client_version(server_version);
req.set_token(token);
LGraphRPCService_Stub stub(channel.get());
stub.HandleRequest(cntl.get(), &req, &res, nullptr);
if (cntl->Failed()) throw RpcConnectionException(cntl->ErrorText());
server_version = std::max(server_version, res.server_version());
if (res.error_code() != LGraphResponse::SUCCESS) throw RpcStatusException(res.error());
if (json_format) {
result = res.mutable_plugin_response()->mutable_call_plugin_response()->json_result();
} else {
result = res.mutable_plugin_response()->mutable_call_plugin_response()->reply();
}
The response for invoking the stored procedure contains one of the following two parameters:
reply: the stored procedure invocation result in the ByteString format
json_result: the stored procedure invocation result in JSON format
5.3.Delete Stored Procedures
The request for deleting stored procedures contains the following parameters:
name: a necessary parameter, the stored procedure name
Taking C++ as an example, the user deletes the stored procedure as follows:
LGraphRequest req;
req.set_is_write_op(true);
lgraph::PluginRequest* pluginRequest = req.mutable_plugin_request();
pluginRequest->set_graph(graph);
pluginRequest->set_type(procedure_type == "CPP" ? lgraph::PluginRequest::CPP
: lgraph::PluginRequest::PYTHON);
lgraph::DelPluginRequest* dpRequest = pluginRequest->mutable_del_plugin_request();
dpRequest->set_name(procedure_name);
cntl->Reset();
cntl->request_attachment().append(FLAGS_attachment);
req.set_client_version(server_version);
req.set_token(token);
LGraphRPCService_Stub stub(channel.get());
LGraphResponse res;
stub.HandleRequest(cntl.get(), &req, &res, nullptr);
if (cntl->Failed()) throw RpcConnectionException(cntl->ErrorText());
server_version = std::max(server_version, res.server_version());
if (res.error_code() != LGraphResponse::SUCCESS) throw RpcStatusException(res.error());
The response for deleting the stored procedure does not contain parameters, and if the deletion fails, a BadInput exception will be thrown.
5.4.List Stored Procedures
The request for listing stored procedures does not require parameters. Taking C++ as an example, the user lists the stored procedures as follows:
LGraphRequest req;
req.set_is_write_op(false);
lgraph::PluginRequest* pluginRequest = req.mutable_plugin_request();
pluginRequest->set_graph(graph);
pluginRequest->set_type(procedure_type == "CPP" ? lgraph::PluginRequest::CPP
: lgraph::PluginRequest::PYTHON);
pluginRequest->mutable_list_plugin_request();
cntl->Reset();
cntl->request_attachment().append(FLAGS_attachment);
req.set_client_version(server_version);
req.set_token(token);
LGraphRPCService_Stub stub(channel.get());
LGraphResponse res;
stub.HandleRequest(cntl.get(), &req, &res, nullptr);
if (cntl->Failed()) throw RpcConnectionException(cntl->ErrorText());
server_version = std::max(server_version, res.server_version());
if (res.error_code() != LGraphResponse::SUCCESS) throw RpcStatusException(res.error());
result = res.mutable_plugin_response()->mutable_list_plugin_response()->reply();
The response for listing the stored procedures contains the following parameter:
reply: the procedure list in JSON format