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