From fb8f45da83c494e49362ea7d5284ec0105a47af1 Mon Sep 17 00:00:00 2001 From: Pawan Rawal <pawan0201@gmail.com> Date: Tue, 19 Apr 2016 14:33:39 +0530 Subject: [PATCH] Adding DGraph service to proto file and communicating using gRpc --- client/go/main.go | 36 ++++------- query/pb/graphresponse.pb.go | 121 ++++++++++++++++++++++++++++++----- query/pb/graphresponse.proto | 8 +++ query/query.go | 13 +--- query/query_test.go | 11 +--- server/main.go | 56 +++++++--------- 6 files changed, 151 insertions(+), 94 deletions(-) diff --git a/client/go/main.go b/client/go/main.go index 780ea537..ca2d662f 100644 --- a/client/go/main.go +++ b/client/go/main.go @@ -17,18 +17,19 @@ package main import ( - "bytes" "flag" "fmt" - "net" - "github.com/dgraph-io/dgraph/query/protocolbuffer" + "golang.org/x/net/context" + + "google.golang.org/grpc" + + "github.com/dgraph-io/dgraph/query/pb" "github.com/dgraph-io/dgraph/x" - "github.com/golang/protobuf/proto" ) var glog = x.Log("client") -var port = flag.String("port", "8090", "Port to communicate with server") +var port = flag.String("port", "9090", "Port to communicate with server") func main() { // TODO(pawan) - Remove hardcoded query. Give helper methods to user for building query. @@ -42,33 +43,20 @@ func main() { }` // TODO(pawan): Pick address for server from config - conn, err := net.Dial("tcp", "127.0.0.1:"+*port) + conn, err := grpc.Dial("127.0.0.1:"+*port, grpc.WithInsecure()) if err != nil { x.Err(glog, err).Fatal("DialTCPConnection") } + defer conn.Close() - _, err = conn.Write([]byte(q0)) - if err != nil { - x.Err(glog, err).Fatal("Error in writing to server") - } + c := pb.NewDGraphClient(conn) - // TODO(pawan): Discuss and implement a better way of doing this. - reply := make([]byte, 32768) - _, err = conn.Read(reply) + r, err := c.GetGraphResponse(context.Background(), &pb.GraphRequest{Query: q0}) if err != nil { - x.Err(glog, err).Fatal("Error in reading response from server") - } - - // Trimming null bytes - reply = bytes.Trim(reply, "\000") - - usg := &protocolbuffer.SubGraph{} - if err := proto.Unmarshal(reply, usg); err != nil { - x.Err(glog, err).Fatal("Error in umarshalling protocol buffer") + x.Err(glog, err).Fatal("Error in getting response from server") } // TODO(pawan): Remove this later - fmt.Printf("Subgraph %+v", usg) + fmt.Printf("Subgraph %+v", r) - conn.Close() } diff --git a/query/pb/graphresponse.pb.go b/query/pb/graphresponse.pb.go index 343fbedb..83c48b38 100644 --- a/query/pb/graphresponse.pb.go +++ b/query/pb/graphresponse.pb.go @@ -11,6 +11,7 @@ It is generated from these files: It has these top-level messages: UidList Result + GraphRequest GraphResponse */ package pb @@ -19,6 +20,11 @@ import proto "github.com/golang/protobuf/proto" import fmt "fmt" import math "math" +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal var _ = fmt.Errorf @@ -54,6 +60,15 @@ func (m *Result) GetUidmatrix() []*UidList { return nil } +type GraphRequest struct { + Query string `protobuf:"bytes,1,opt,name=query" json:"query,omitempty"` +} + +func (m *GraphRequest) Reset() { *m = GraphRequest{} } +func (m *GraphRequest) String() string { return proto.CompactTextString(m) } +func (*GraphRequest) ProtoMessage() {} +func (*GraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + type GraphResponse struct { Attribute string `protobuf:"bytes,1,opt,name=attribute" json:"attribute,omitempty"` Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` @@ -63,7 +78,7 @@ type GraphResponse struct { func (m *GraphResponse) Reset() { *m = GraphResponse{} } func (m *GraphResponse) String() string { return proto.CompactTextString(m) } func (*GraphResponse) ProtoMessage() {} -func (*GraphResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*GraphResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } func (m *GraphResponse) GetResult() *Result { if m != nil { @@ -82,23 +97,97 @@ func (m *GraphResponse) GetChildren() []*GraphResponse { func init() { proto.RegisterType((*UidList)(nil), "pb.UidList") proto.RegisterType((*Result)(nil), "pb.Result") + proto.RegisterType((*GraphRequest)(nil), "pb.GraphRequest") proto.RegisterType((*GraphResponse)(nil), "pb.GraphResponse") } +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion2 + +// Client API for DGraph service + +type DGraphClient interface { + GetResponse(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error) +} + +type dGraphClient struct { + cc *grpc.ClientConn +} + +func NewDGraphClient(cc *grpc.ClientConn) DGraphClient { + return &dGraphClient{cc} +} + +func (c *dGraphClient) GetResponse(ctx context.Context, in *GraphRequest, opts ...grpc.CallOption) (*GraphResponse, error) { + out := new(GraphResponse) + err := grpc.Invoke(ctx, "/pb.DGraph/GetResponse", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for DGraph service + +type DGraphServer interface { + GetResponse(context.Context, *GraphRequest) (*GraphResponse, error) +} + +func RegisterDGraphServer(s *grpc.Server, srv DGraphServer) { + s.RegisterService(&_DGraph_serviceDesc, srv) +} + +func _DGraph_GetResponse_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GraphRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DGraphServer).GetResponse(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/pb.DGraph/GetResponse", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DGraphServer).GetResponse(ctx, req.(*GraphRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _DGraph_serviceDesc = grpc.ServiceDesc{ + ServiceName: "pb.DGraph", + HandlerType: (*DGraphServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "GetResponse", + Handler: _DGraph_GetResponse_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, +} + var fileDescriptor0 = []byte{ - // 209 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x54, 0x8f, 0xbf, 0x4a, 0xc5, 0x30, - 0x14, 0xc6, 0xc9, 0xbd, 0x25, 0xda, 0x53, 0x1d, 0x3c, 0x82, 0x74, 0x50, 0x90, 0x4c, 0x75, 0xb0, - 0x83, 0x3e, 0x84, 0x83, 0x4e, 0x07, 0x7c, 0x80, 0xc4, 0x06, 0x1b, 0xa8, 0x6d, 0xc8, 0x1f, 0x71, - 0xf4, 0xd1, 0x4d, 0xd3, 0x60, 0xb9, 0x5b, 0x92, 0xdf, 0xc7, 0xef, 0xfb, 0x02, 0xd7, 0x9f, 0x4e, - 0xda, 0xd1, 0x69, 0x6f, 0x97, 0xd9, 0xeb, 0xde, 0xba, 0x25, 0x2c, 0x78, 0xb0, 0x4a, 0xdc, 0xc1, - 0xd9, 0xbb, 0x19, 0xde, 0x8c, 0x0f, 0x88, 0x50, 0x45, 0x33, 0xf8, 0x96, 0xdd, 0x1f, 0xbb, 0x8a, - 0xf2, 0x59, 0xbc, 0x02, 0x27, 0xed, 0xe3, 0x14, 0xf0, 0x06, 0xf8, 0xb7, 0x9c, 0xa2, 0xde, 0xf8, - 0x05, 0x95, 0x1b, 0x3e, 0x40, 0x9d, 0x92, 0x5f, 0x32, 0x38, 0xf3, 0xd3, 0x1e, 0x12, 0x6a, 0x9e, - 0x9a, 0xde, 0xaa, 0xbe, 0x58, 0x69, 0xa7, 0xe2, 0x97, 0xc1, 0xe5, 0xcb, 0xba, 0x83, 0xca, 0x0e, - 0xbc, 0x85, 0x5a, 0x86, 0xc4, 0x54, 0x0c, 0x3a, 0x79, 0x59, 0x57, 0xd3, 0xfe, 0x80, 0x02, 0xb8, - 0xcb, 0xe5, 0xc9, 0xcb, 0x92, 0x17, 0x56, 0xef, 0x36, 0x87, 0x0a, 0xc1, 0x47, 0x38, 0xff, 0x18, - 0xcd, 0x34, 0x38, 0x3d, 0xb7, 0xc7, 0xdc, 0x7e, 0xb5, 0xa6, 0x4e, 0x6a, 0xe8, 0x3f, 0xa2, 0x78, - 0xfe, 0xf9, 0xf3, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc1, 0x1f, 0x18, 0x20, 0x10, 0x01, 0x00, - 0x00, + // 256 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0x41, 0x4b, 0xc4, 0x30, + 0x10, 0x85, 0xed, 0xee, 0x1a, 0xed, 0x74, 0x05, 0x1d, 0x45, 0x8a, 0x28, 0x48, 0xf0, 0xb0, 0x1e, + 0xec, 0x61, 0xf5, 0xec, 0x49, 0xd8, 0x83, 0x9e, 0x02, 0xfe, 0x80, 0xd6, 0x06, 0x37, 0x50, 0xb7, + 0x31, 0x99, 0x8a, 0xde, 0xfc, 0xe9, 0x4e, 0xd3, 0xb0, 0x55, 0xbc, 0xcd, 0xf4, 0xbd, 0x79, 0xef, + 0x6b, 0xe0, 0xf8, 0xd5, 0x95, 0x76, 0xed, 0xb4, 0xb7, 0xed, 0xc6, 0xeb, 0xc2, 0xba, 0x96, 0x5a, + 0x9c, 0xd8, 0x4a, 0x5e, 0xc0, 0xde, 0xb3, 0xa9, 0x9f, 0x8c, 0x27, 0x44, 0x98, 0x75, 0xa6, 0xf6, + 0x79, 0x72, 0x39, 0x5d, 0xcc, 0x54, 0x98, 0xe5, 0x23, 0x08, 0xa5, 0x7d, 0xd7, 0x10, 0x9e, 0x82, + 0xf8, 0x28, 0x9b, 0x4e, 0x0f, 0xfa, 0x5c, 0xc5, 0x0d, 0xaf, 0x21, 0x65, 0xe7, 0x5b, 0x49, 0xce, + 0x7c, 0xe6, 0x13, 0x96, 0xb2, 0x65, 0x56, 0xd8, 0xaa, 0x88, 0xa9, 0x6a, 0x54, 0xe5, 0x15, 0xcc, + 0x57, 0x3d, 0x86, 0xd2, 0xef, 0x7c, 0x49, 0x78, 0x02, 0xbb, 0x3c, 0xb8, 0x2f, 0x4e, 0x4c, 0x16, + 0xa9, 0x1a, 0x16, 0xf9, 0x9d, 0xc0, 0x41, 0xb4, 0x0d, 0xb4, 0x78, 0x0e, 0x69, 0x49, 0x9c, 0x50, + 0x75, 0xa4, 0xa3, 0x77, 0xfc, 0x80, 0x12, 0x84, 0x0b, 0x88, 0xdc, 0x9e, 0x70, 0x3b, 0xf4, 0xed, + 0x03, 0xb4, 0x8a, 0x0a, 0xde, 0xc0, 0xfe, 0xcb, 0xda, 0x34, 0xb5, 0xd3, 0x9b, 0x7c, 0x1a, 0x18, + 0x8f, 0x7a, 0xd7, 0x9f, 0x1a, 0xb5, 0xb5, 0x2c, 0xef, 0x41, 0x3c, 0x04, 0x0d, 0xef, 0x20, 0x5b, + 0x69, 0xda, 0x92, 0x1c, 0xfe, 0xba, 0x0a, 0xff, 0x70, 0xf6, 0x3f, 0x47, 0xee, 0x54, 0x22, 0xbc, + 0xef, 0xed, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xab, 0xec, 0x88, 0x76, 0x01, 0x00, 0x00, } diff --git a/query/pb/graphresponse.proto b/query/pb/graphresponse.proto index 9fad4eb9..cd04f3fb 100644 --- a/query/pb/graphresponse.proto +++ b/query/pb/graphresponse.proto @@ -1,6 +1,10 @@ syntax="proto3"; package pb; +service DGraph { + rpc GetResponse (GraphRequest) returns (GraphResponse) {} +} + message UidList { repeated uint64 uids = 1; } @@ -10,6 +14,10 @@ message Result { repeated UidList uidmatrix = 2; } +message GraphRequest { + string query = 1; +} + message GraphResponse { string attribute = 1; Result result = 2; diff --git a/query/query.go b/query/query.go index 27b71f73..c94c94f6 100644 --- a/query/query.go +++ b/query/query.go @@ -30,7 +30,6 @@ import ( "github.com/dgraph-io/dgraph/task" "github.com/dgraph-io/dgraph/worker" "github.com/dgraph-io/dgraph/x" - "github.com/golang/protobuf/proto" "github.com/google/flatbuffers/go" ) @@ -310,20 +309,14 @@ func preTraverse(g *SubGraph) (gr *pb.GraphResponse, rerr error) { return gr, nil } -func (g *SubGraph) ToProtocolBuffer() (pbuffer []byte, rerr error) { +func (g *SubGraph) ToProtocolBuffer() (gr *pb.GraphResponse, rerr error) { gr, err := preTraverse(g) if err != nil { x.Err(glog, err).Error("Error while traversal") - return pbuffer, err + return gr, err } - pbuffer, err = proto.Marshal(gr) - if err != nil { - x.Err(glog, err).Error("Error while marshalling to protocol buffer") - return pbuffer, err - } - - return pbuffer, nil + return gr, nil } func treeCopy(gq *gql.GraphQuery, sg *SubGraph) { diff --git a/query/query_test.go b/query/query_test.go index 07818652..8e89be87 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -26,12 +26,10 @@ import ( "github.com/dgraph-io/dgraph/commit" "github.com/dgraph-io/dgraph/gql" "github.com/dgraph-io/dgraph/posting" - "github.com/dgraph-io/dgraph/query/pb" "github.com/dgraph-io/dgraph/store" "github.com/dgraph-io/dgraph/task" "github.com/dgraph-io/dgraph/worker" "github.com/dgraph-io/dgraph/x" - "github.com/golang/protobuf/proto" "github.com/google/flatbuffers/go" ) @@ -355,14 +353,7 @@ func TestToProtocolBuffer(t *testing.T) { t.Error(err) } - pbuffer, err := sg.ToProtocolBuffer() - if err != nil { - t.Error(err) - } - - // Unmarshalling to a protocol buffer graph response for testing - ugr := &pb.GraphResponse{} - err = proto.Unmarshal(pbuffer, ugr) + ugr, err := sg.ToProtocolBuffer() if err != nil { t.Error(err) } diff --git a/server/main.go b/server/main.go index 76cd6471..8a0df9b9 100644 --- a/server/main.go +++ b/server/main.go @@ -24,15 +24,19 @@ import ( "net" "net/http" "runtime" - "strconv" "strings" "time" + "golang.org/x/net/context" + + "google.golang.org/grpc" + "github.com/Sirupsen/logrus" "github.com/dgraph-io/dgraph/commit" "github.com/dgraph-io/dgraph/gql" "github.com/dgraph-io/dgraph/posting" "github.com/dgraph-io/dgraph/query" + "github.com/dgraph-io/dgraph/query/pb" "github.com/dgraph-io/dgraph/rdf" "github.com/dgraph-io/dgraph/store" "github.com/dgraph-io/dgraph/uid" @@ -46,6 +50,7 @@ var postingDir = flag.String("postings", "", "Directory to store posting lists") var uidDir = flag.String("uids", "", "XID UID posting lists directory") var mutationDir = flag.String("mutations", "", "Directory to store mutations") var port = flag.String("port", "8080", "Port to run server on.") +var clientPort = flag.String("port", "9090", "Port used to communicate with client on tcp") var numcpu = flag.Int("numCpu", runtime.NumCPU(), "Number of cores to be used by the process") var instanceIdx = flag.Uint64("instanceIdx", 0, @@ -197,10 +202,14 @@ func queryHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, string(js)) } -func pbQueryHandler(q []byte) (pb []byte, rerr error) { +// server is used to implement pb.DGraphServer +type server struct{} + +func (s *server) GetResponse(ctx context.Context, r *pb.GraphRequest) (gr *pb.GraphResponse, rerr error) { + q := []byte(r.Query) if len(q) == 0 { glog.Error("While reading query") - return pb, fmt.Errorf("Empty query") + return gr, fmt.Errorf("Empty query") } // TODO(pawan): Refactor query parsing and graph processing code to a common @@ -209,13 +218,13 @@ func pbQueryHandler(q []byte) (pb []byte, rerr error) { gq, _, err := gql.Parse(string(q)) if err != nil { x.Err(glog, err).Error("While parsing query") - return pb, err + return gr, err } sg, err := query.ToSubGraph(gq) if err != nil { x.Err(glog, err).Error("While conversion to internal format") - return pb, err + return gr, err } glog.WithField("q", string(q)).Debug("Query parsed.") @@ -224,17 +233,17 @@ func pbQueryHandler(q []byte) (pb []byte, rerr error) { err = <-rch if err != nil { x.Err(glog, err).Error("While executing query") - return pb, err + return gr, err } glog.WithField("q", string(q)).Debug("Graph processed.") - pb, err = sg.ToProtocolBuffer() + gr, err = sg.ToProtocolBuffer() if err != nil { x.Err(glog, err).Error("While converting to Json.") - return pb, err + return gr, err } - return pb, err + return gr, err } func runServerForClient(address string) error { @@ -245,28 +254,9 @@ func runServerForClient(address string) error { } glog.WithField("address", ln.Addr()).Info("Client Worker listening") - go func() { - for { - cxn, err := ln.Accept() - if err != nil { - glog.Fatalf("listen(%q): %s\n", address, err) - return - } - glog.WithField("local", cxn.LocalAddr()). - WithField("remote", cxn.RemoteAddr()). - Debug("Client Worker accepted connection") - - // TODO(pawan) - Find a better way to do this, byte slice shouldn't be of fixed size - q := make([]byte, 4096) - // TODO(pawan) - Move to separate function - go func(c net.Conn) { - c.Read(q) - r, _ := pbQueryHandler(q) - c.Write(r) - }(cxn) - } - }() - return nil + s := grpc.NewServer() + pb.RegisterDGraphServer(s, &server{}) + s.Serve(ln) } func main() { @@ -313,9 +303,7 @@ func main() { worker.Connect(addrs) - // TODO(pawan): Have a better way to do this, pick port for client from a flag - clientPort, _ := strconv.Atoi(*port) - runServerForClient(":" + strconv.Itoa(clientPort+10)) + runServerForClient(":" + *clientPort) http.HandleFunc("/query", queryHandler) glog.WithField("port", *port).Info("Listening for requests...") -- GitLab