From aea855e8f3823a7ed8d6b5b4daa3fbd475cdf3ee Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Fri, 29 Apr 2022 13:06:10 +0300 Subject: [PATCH] [#1326] services/tree: Implement GetSubTree RPC Signed-off-by: Evgenii Stratonikov --- pkg/local_object_storage/engine/tree.go | 14 + pkg/local_object_storage/pilorama/boltdb.go | 25 ++ pkg/local_object_storage/pilorama/forest.go | 18 + .../pilorama/forest_test.go | 56 +++ .../pilorama/interface.go | 2 + pkg/local_object_storage/shard/tree.go | 5 + pkg/services/tree/service.go | 99 ++++- pkg/services/tree/service.pb.go | 367 ++++++++---------- pkg/services/tree/service.proto | 16 +- pkg/services/tree/service_grpc.pb.go | 84 ++-- 10 files changed, 431 insertions(+), 255 deletions(-) diff --git a/pkg/local_object_storage/engine/tree.go b/pkg/local_object_storage/engine/tree.go index 986bea15e..04362b8c6 100644 --- a/pkg/local_object_storage/engine/tree.go +++ b/pkg/local_object_storage/engine/tree.go @@ -76,3 +76,17 @@ func (e *StorageEngine) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID piloram } return pilorama.Meta{}, err } + +// TreeGetChildren implements the pilorama.Forest interface. +func (e *StorageEngine) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]uint64, error) { + var err error + var nodes []uint64 + for _, sh := range e.sortShardsByWeight(cid) { + nodes, err = sh.TreeGetChildren(cid, treeID, nodeID) + if err != nil { + continue + } + return nodes, nil + } + return nil, err +} diff --git a/pkg/local_object_storage/pilorama/boltdb.go b/pkg/local_object_storage/pilorama/boltdb.go index 6eb12a6c6..808ab18e6 100644 --- a/pkg/local_object_storage/pilorama/boltdb.go +++ b/pkg/local_object_storage/pilorama/boltdb.go @@ -416,6 +416,31 @@ func (t *boltForest) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID Node) (Met return m, err } +// TreeGetChildren implements the Forest interface. +func (t *boltForest) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) { + key := make([]byte, 9) + key[0] = 'c' + binary.LittleEndian.PutUint64(key[1:], nodeID) + + var children []uint64 + + err := t.db.View(func(tx *bbolt.Tx) error { + treeRoot := tx.Bucket(bucketName(cid, treeID)) + if treeRoot == nil { + return ErrTreeNotFound + } + + b := treeRoot.Bucket(dataBucket) + c := b.Cursor() + for k, _ := c.Seek(key); len(k) == 17 && binary.LittleEndian.Uint64(k[1:]) == nodeID; k, _ = c.Next() { + children = append(children, binary.LittleEndian.Uint64(k[9:])) + } + return nil + }) + + return children, err +} + func (t *boltForest) getPathPrefix(bTree *bbolt.Bucket, attr string, path []string) (int, Node, error) { var key [9]byte diff --git a/pkg/local_object_storage/pilorama/forest.go b/pkg/local_object_storage/pilorama/forest.go index 53d044866..f6d79413d 100644 --- a/pkg/local_object_storage/pilorama/forest.go +++ b/pkg/local_object_storage/pilorama/forest.go @@ -112,3 +112,21 @@ func (f *memoryForest) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID Node) (M return s.getMeta(nodeID), nil } + +// TreeGetChildren implements the Forest interface. +func (f *memoryForest) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) { + fullID := cid.String() + "/" + treeID + s, ok := f.treeMap[fullID] + if !ok { + return nil, ErrTreeNotFound + } + + children, ok := s.childMap[nodeID] + if !ok { + return nil, nil + } + + res := make([]Node, len(children)) + copy(res, children) + return res, nil +} diff --git a/pkg/local_object_storage/pilorama/forest_test.go b/pkg/local_object_storage/pilorama/forest_test.go index f3a498fe5..d3503bf3b 100644 --- a/pkg/local_object_storage/pilorama/forest_test.go +++ b/pkg/local_object_storage/pilorama/forest_test.go @@ -98,6 +98,62 @@ func testForestTreeMove(t *testing.T, s Forest) { }) } +func TestMemoryForest_TreeGetChildren(t *testing.T) { + for i := range providers { + t.Run(providers[i].name, func(t *testing.T) { + testForestTreeGetChildren(t, providers[i].construct(t)) + }) + } +} + +func testForestTreeGetChildren(t *testing.T, s Forest) { + cid := cidtest.ID() + treeID := "version" + + treeAdd := func(t *testing.T, child, parent Node) { + _, err := s.TreeMove(cid, treeID, &Move{ + Parent: parent, + Child: child, + }) + require.NoError(t, err) + } + + // 0 + // |- 10 + // | |- 3 + // | |- 6 + // | |- 11 + // |- 2 + // |- 7 + treeAdd(t, 10, 0) + treeAdd(t, 3, 10) + treeAdd(t, 6, 10) + treeAdd(t, 11, 6) + treeAdd(t, 2, 0) + treeAdd(t, 7, 0) + + testGetChildren := func(t *testing.T, nodeID Node, expected []Node) { + actual, err := s.TreeGetChildren(cid, treeID, nodeID) + require.NoError(t, err) + require.ElementsMatch(t, expected, actual) + } + + testGetChildren(t, 0, []uint64{10, 2, 7}) + testGetChildren(t, 10, []uint64{3, 6}) + testGetChildren(t, 3, nil) + testGetChildren(t, 6, []uint64{11}) + testGetChildren(t, 11, nil) + testGetChildren(t, 2, nil) + testGetChildren(t, 7, nil) + t.Run("missing node", func(t *testing.T) { + testGetChildren(t, 42, nil) + }) + t.Run("missing tree", func(t *testing.T) { + _, err := s.TreeGetChildren(cid, treeID+"123", 0) + require.ErrorIs(t, err, ErrTreeNotFound) + }) +} + func TestForest_TreeAdd(t *testing.T) { for i := range providers { t.Run(providers[i].name, func(t *testing.T) { diff --git a/pkg/local_object_storage/pilorama/interface.go b/pkg/local_object_storage/pilorama/interface.go index 005ce5ce5..9ffa529a8 100644 --- a/pkg/local_object_storage/pilorama/interface.go +++ b/pkg/local_object_storage/pilorama/interface.go @@ -20,6 +20,8 @@ type Forest interface { TreeGetByPath(cid cidSDK.ID, treeID string, attr string, path []string, latest bool) ([]Node, error) // TreeGetMeta returns meta information of the node with the specified ID. TreeGetMeta(cid cidSDK.ID, treeID string, nodeID Node) (Meta, error) + // TreeGetChildren returns children of the node with the specified ID. The order is arbitrary. + TreeGetChildren(cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) } type ForestStorage interface { diff --git a/pkg/local_object_storage/shard/tree.go b/pkg/local_object_storage/shard/tree.go index 2a4120a12..13cd8fcf0 100644 --- a/pkg/local_object_storage/shard/tree.go +++ b/pkg/local_object_storage/shard/tree.go @@ -31,3 +31,8 @@ func (s *Shard) TreeGetByPath(cid cidSDK.ID, treeID string, attr string, path [] func (s *Shard) TreeGetMeta(cid cidSDK.ID, treeID string, nodeID pilorama.Node) (pilorama.Meta, error) { return s.pilorama.TreeGetMeta(cid, treeID, nodeID) } + +// TreeGetChildren implements the pilorama.Forest interface. +func (s *Shard) TreeGetChildren(cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]uint64, error) { + return s.pilorama.TreeGetChildren(cid, treeID, nodeID) +} diff --git a/pkg/services/tree/service.go b/pkg/services/tree/service.go index 00de22b8a..8cf6236cd 100644 --- a/pkg/services/tree/service.go +++ b/pkg/services/tree/service.go @@ -21,6 +21,9 @@ type Service struct { closeCh chan struct{} } +// MaxGetSubTreeDepth represents maximum allowed traversal depth in GetSubTree RPC. +const MaxGetSubTreeDepth = 10 + var _ TreeServiceServer = (*Service)(nil) // New creates new tree service instance. @@ -66,7 +69,7 @@ func (s *Service) Add(_ context.Context, req *AddRequest) (*AddResponse, error) log, err := s.forest.TreeMove(cid, b.GetTreeId(), &pilorama.Move{ Parent: b.GetParentId(), Child: pilorama.RootID, - Meta: pilorama.Meta{Items: constructMeta(b.GetMeta())}, + Meta: pilorama.Meta{Items: protoToMeta(b.GetMeta())}, }) if err != nil { return nil, err @@ -93,7 +96,7 @@ func (s *Service) AddByPath(_ context.Context, req *AddByPathRequest) (*AddByPat return nil, err } - meta := constructMeta(b.GetMeta()) + meta := protoToMeta(b.GetMeta()) attr := b.GetPathAttribute() if len(attr) == 0 { @@ -173,7 +176,7 @@ func (s *Service) Move(_ context.Context, req *MoveRequest) (*MoveResponse, erro log, err := s.forest.TreeMove(cid, b.GetTreeId(), &pilorama.Move{ Parent: b.GetParentId(), Child: b.GetNodeId(), - Meta: pilorama.Meta{Items: constructMeta(b.GetMeta())}, + Meta: pilorama.Meta{Items: protoToMeta(b.GetMeta())}, }) if err != nil { return nil, err @@ -211,23 +214,20 @@ func (s *Service) GetNodeByPath(_ context.Context, req *GetNodeByPathRequest) (* var x GetNodeByPathResponse_Info x.NodeId = node x.Timestamp = m.Time - for _, kv := range m.Items { - needAttr := b.AllAttributes - if !needAttr { + if b.AllAttributes { + x.Meta = metaToProto(m.Items) + } else { + for _, kv := range m.Items { for _, attr := range b.GetAttributes() { if kv.Key == attr { - needAttr = true + x.Meta = append(x.Meta, &KeyValue{ + Key: kv.Key, + Value: kv.Value, + }) break } } } - - if needAttr { - x.Meta = append(x.Meta, &KeyValue{ - Key: kv.Key, - Value: kv.Value, - }) - } } info = append(info, &x) } @@ -239,8 +239,56 @@ func (s *Service) GetNodeByPath(_ context.Context, req *GetNodeByPathRequest) (* }, nil } -func (s *Service) GetSubTree(_ context.Context, req *GetSubTreeRequest) (*GetSubTreeResponse, error) { - return nil, errors.New("GetSubTree is unimplemented") +type nodeDepthPair struct { + nodes []uint64 + depth uint32 +} + +func (s *Service) GetSubTree(req *GetSubTreeRequest, srv TreeService_GetSubTreeServer) error { + b := req.GetBody() + if b.GetDepth() > MaxGetSubTreeDepth { + return fmt.Errorf("too big depth: max=%d, got=%d", MaxGetSubTreeDepth, b.GetDepth()) + } + + var cid cidSDK.ID + if err := cid.Decode(b.GetContainerId()); err != nil { + return err + } + + queue := []nodeDepthPair{{[]uint64{b.GetRootId()}, 0}} + + for len(queue) != 0 { + for _, nodeID := range queue[0].nodes { + m, err := s.forest.TreeGetMeta(cid, b.GetTreeId(), nodeID) + if err != nil { + return err + } + err = srv.Send(&GetSubTreeResponse{ + Body: &GetSubTreeResponse_Body{ + NodeId: b.GetRootId(), + ParentId: b.GetRootId(), + Timestamp: m.Time, + Meta: metaToProto(m.Items), + }, + }) + if err != nil { + return err + } + } + + if queue[0].depth < b.GetDepth() { + for _, nodeID := range queue[0].nodes { + children, err := s.forest.TreeGetChildren(cid, b.GetTreeId(), nodeID) + if err != nil { + return err + } + queue = append(queue, nodeDepthPair{children, queue[0].depth + 1}) + } + } + + queue = queue[1:] + } + return nil } // Apply locally applies operation from the remote node to the tree. @@ -284,11 +332,24 @@ loop: }) } -func constructMeta(arr []*KeyValue) []pilorama.KeyValue { +func protoToMeta(arr []*KeyValue) []pilorama.KeyValue { meta := make([]pilorama.KeyValue, len(arr)) for i, kv := range arr { - meta[i].Key = kv.Key - meta[i].Value = kv.Value + if kv != nil { + meta[i].Key = kv.Key + meta[i].Value = kv.Value + } + } + return meta +} + +func metaToProto(arr []pilorama.KeyValue) []*KeyValue { + meta := make([]*KeyValue, len(arr)) + for i, kv := range arr { + meta[i] = &KeyValue{ + Key: kv.Key, + Value: kv.Value, + } } return meta } diff --git a/pkg/services/tree/service.pb.go b/pkg/services/tree/service.pb.go index bf1f26904..5f0afdb9a 100644 --- a/pkg/services/tree/service.pb.go +++ b/pkg/services/tree/service.pb.go @@ -1471,9 +1471,10 @@ type GetSubTreeRequest_Body struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - ContainerId []byte `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` - TreeId string `protobuf:"bytes,2,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` - Nodes []uint64 `protobuf:"varint,3,rep,packed,name=nodes,proto3" json:"nodes,omitempty"` + ContainerId []byte `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + TreeId string `protobuf:"bytes,2,opt,name=tree_id,json=treeId,proto3" json:"tree_id,omitempty"` + RootId uint64 `protobuf:"varint,3,opt,name=root_id,json=rootId,proto3" json:"root_id,omitempty"` + Depth uint32 `protobuf:"varint,4,opt,name=depth,proto3" json:"depth,omitempty"` } func (x *GetSubTreeRequest_Body) Reset() { @@ -1522,66 +1523,18 @@ func (x *GetSubTreeRequest_Body) GetTreeId() string { return "" } -func (x *GetSubTreeRequest_Body) GetNodes() []uint64 { +func (x *GetSubTreeRequest_Body) GetRootId() uint64 { if x != nil { - return x.Nodes - } - return nil -} - -type GetSubTreeResponse_Info struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - NodeId uint64 `protobuf:"varint,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - Meta []*KeyValue `protobuf:"bytes,2,rep,name=meta,proto3" json:"meta,omitempty"` -} - -func (x *GetSubTreeResponse_Info) Reset() { - *x = GetSubTreeResponse_Info{} - if protoimpl.UnsafeEnabled { - mi := &file_pkg_services_tree_service_proto_msgTypes[26] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetSubTreeResponse_Info) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetSubTreeResponse_Info) ProtoMessage() {} - -func (x *GetSubTreeResponse_Info) ProtoReflect() protoreflect.Message { - mi := &file_pkg_services_tree_service_proto_msgTypes[26] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use GetSubTreeResponse_Info.ProtoReflect.Descriptor instead. -func (*GetSubTreeResponse_Info) Descriptor() ([]byte, []int) { - return file_pkg_services_tree_service_proto_rawDescGZIP(), []int{11, 0} -} - -func (x *GetSubTreeResponse_Info) GetNodeId() uint64 { - if x != nil { - return x.NodeId + return x.RootId } return 0 } -func (x *GetSubTreeResponse_Info) GetMeta() []*KeyValue { +func (x *GetSubTreeRequest_Body) GetDepth() uint32 { if x != nil { - return x.Meta + return x.Depth } - return nil + return 0 } type GetSubTreeResponse_Body struct { @@ -1589,13 +1542,16 @@ type GetSubTreeResponse_Body struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Info []*GetSubTreeResponse_Info `protobuf:"bytes,1,rep,name=info,proto3" json:"info,omitempty"` + NodeId uint64 `protobuf:"varint,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + ParentId uint64 `protobuf:"varint,2,opt,name=parent_id,json=parentId,proto3" json:"parent_id,omitempty"` + Timestamp uint64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Meta []*KeyValue `protobuf:"bytes,4,rep,name=meta,proto3" json:"meta,omitempty"` } func (x *GetSubTreeResponse_Body) Reset() { *x = GetSubTreeResponse_Body{} if protoimpl.UnsafeEnabled { - mi := &file_pkg_services_tree_service_proto_msgTypes[27] + mi := &file_pkg_services_tree_service_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1608,7 +1564,7 @@ func (x *GetSubTreeResponse_Body) String() string { func (*GetSubTreeResponse_Body) ProtoMessage() {} func (x *GetSubTreeResponse_Body) ProtoReflect() protoreflect.Message { - mi := &file_pkg_services_tree_service_proto_msgTypes[27] + mi := &file_pkg_services_tree_service_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1621,12 +1577,33 @@ func (x *GetSubTreeResponse_Body) ProtoReflect() protoreflect.Message { // Deprecated: Use GetSubTreeResponse_Body.ProtoReflect.Descriptor instead. func (*GetSubTreeResponse_Body) Descriptor() ([]byte, []int) { - return file_pkg_services_tree_service_proto_rawDescGZIP(), []int{11, 1} + return file_pkg_services_tree_service_proto_rawDescGZIP(), []int{11, 0} } -func (x *GetSubTreeResponse_Body) GetInfo() []*GetSubTreeResponse_Info { +func (x *GetSubTreeResponse_Body) GetNodeId() uint64 { if x != nil { - return x.Info + return x.NodeId + } + return 0 +} + +func (x *GetSubTreeResponse_Body) GetParentId() uint64 { + if x != nil { + return x.ParentId + } + return 0 +} + +func (x *GetSubTreeResponse_Body) GetTimestamp() uint64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +func (x *GetSubTreeResponse_Body) GetMeta() []*KeyValue { + if x != nil { + return x.Meta } return nil } @@ -1644,7 +1621,7 @@ type ApplyRequest_Body struct { func (x *ApplyRequest_Body) Reset() { *x = ApplyRequest_Body{} if protoimpl.UnsafeEnabled { - mi := &file_pkg_services_tree_service_proto_msgTypes[28] + mi := &file_pkg_services_tree_service_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1657,7 +1634,7 @@ func (x *ApplyRequest_Body) String() string { func (*ApplyRequest_Body) ProtoMessage() {} func (x *ApplyRequest_Body) ProtoReflect() protoreflect.Message { - mi := &file_pkg_services_tree_service_proto_msgTypes[28] + mi := &file_pkg_services_tree_service_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1703,7 +1680,7 @@ type ApplyResponse_Body struct { func (x *ApplyResponse_Body) Reset() { *x = ApplyResponse_Body{} if protoimpl.UnsafeEnabled { - mi := &file_pkg_services_tree_service_proto_msgTypes[29] + mi := &file_pkg_services_tree_service_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1716,7 +1693,7 @@ func (x *ApplyResponse_Body) String() string { func (*ApplyResponse_Body) ProtoMessage() {} func (x *ApplyResponse_Body) ProtoReflect() protoreflect.Message { - mi := &file_pkg_services_tree_service_proto_msgTypes[29] + mi := &file_pkg_services_tree_service_proto_msgTypes[28] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1876,86 +1853,88 @@ var file_pkg_services_tree_service_proto_rawDesc = []byte{ 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, - 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0xce, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, + 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0xe7, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x30, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, 0x58, 0x0a, 0x04, + 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, 0x71, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, - 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, - 0x05, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0xf6, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x75, - 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x72, + 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x65, 0x70, + 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x64, 0x65, 0x70, 0x74, 0x68, 0x22, + 0xf6, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, + 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, + 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, + 0x72, 0x65, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, 0x7e, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, + 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, + 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x12, 0x22, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x22, 0xdb, 0x01, 0x0a, 0x0c, 0x41, 0x70, 0x70, + 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, + 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x42, 0x6f, 0x64, 0x79, + 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, + 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x65, 0x65, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, 0x6f, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x21, 0x0a, + 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, + 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, 0x12, 0x2b, 0x0a, 0x09, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x74, + 0x72, 0x65, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x6f, 0x76, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x74, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x70, 0x70, + 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x1a, 0x06, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x32, 0x9a, 0x03, 0x0a, + 0x0b, 0x54, 0x72, 0x65, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2a, 0x0a, 0x03, + 0x41, 0x64, 0x64, 0x12, 0x10, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x42, + 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, + 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, + 0x12, 0x13, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x4d, + 0x6f, 0x76, 0x65, 0x12, 0x11, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x4d, 0x6f, + 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0d, 0x47, 0x65, + 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x74, 0x72, + 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, + 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, + 0x65, 0x65, 0x12, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, + 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, - 0x12, 0x2d, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, - 0x43, 0x0a, 0x04, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x17, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, - 0x12, 0x22, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, - 0x6d, 0x65, 0x74, 0x61, 0x1a, 0x39, 0x0a, 0x04, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x31, 0x0a, 0x04, - 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x72, 0x65, - 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x04, 0x69, 0x6e, 0x66, 0x6f, 0x22, - 0xdb, 0x01, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x2b, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, - 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, 0x6f, 0x0a, 0x04, - 0x42, 0x6f, 0x64, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, - 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x74, 0x72, 0x65, 0x65, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x72, 0x65, 0x65, 0x49, 0x64, - 0x12, 0x2b, 0x0a, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x4d, 0x6f, - 0x76, 0x65, 0x52, 0x09, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x74, 0x0a, - 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, - 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x74, - 0x72, 0x65, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x2e, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x12, 0x2d, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x1a, 0x06, 0x0a, 0x04, 0x42, - 0x6f, 0x64, 0x79, 0x32, 0x98, 0x03, 0x0a, 0x0b, 0x54, 0x72, 0x65, 0x65, 0x53, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x12, 0x2a, 0x0a, 0x03, 0x41, 0x64, 0x64, 0x12, 0x10, 0x2e, 0x74, 0x72, 0x65, - 0x65, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, - 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x3c, 0x0a, 0x09, 0x41, 0x64, 0x64, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x12, 0x16, 0x2e, 0x74, - 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x64, 0x64, 0x42, - 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, - 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x12, 0x13, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x52, - 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x74, - 0x72, 0x65, 0x65, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x04, 0x4d, 0x6f, 0x76, 0x65, 0x12, 0x11, 0x2e, 0x74, 0x72, 0x65, - 0x65, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x12, 0x2e, - 0x74, 0x72, 0x65, 0x65, 0x2e, 0x4d, 0x6f, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x48, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x50, 0x61, - 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, - 0x65, 0x42, 0x79, 0x50, 0x61, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, - 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x42, 0x79, 0x50, - 0x61, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0a, 0x47, - 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x12, 0x17, 0x2e, 0x74, 0x72, 0x65, 0x65, - 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x75, 0x62, - 0x54, 0x72, 0x65, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x05, - 0x41, 0x70, 0x70, 0x6c, 0x79, 0x12, 0x12, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x70, 0x70, - 0x6c, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x72, 0x65, 0x65, - 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, - 0x5a, 0x31, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x73, 0x70, - 0x63, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x2d, 0x6e, 0x6f, 0x64, - 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, - 0x72, 0x65, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x30, 0x0a, 0x05, 0x41, 0x70, 0x70, 0x6c, 0x79, + 0x12, 0x12, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x74, 0x72, 0x65, 0x65, 0x2e, 0x41, 0x70, 0x70, 0x6c, + 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x33, 0x5a, 0x31, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x73, 0x70, 0x63, 0x63, 0x2d, 0x64, 0x65, + 0x76, 0x2f, 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x2d, 0x6e, 0x6f, 0x64, 0x65, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x74, 0x72, 0x65, 0x65, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1970,7 +1949,7 @@ func file_pkg_services_tree_service_proto_rawDescGZIP() []byte { return file_pkg_services_tree_service_proto_rawDescData } -var file_pkg_services_tree_service_proto_msgTypes = make([]protoimpl.MessageInfo, 30) +var file_pkg_services_tree_service_proto_msgTypes = make([]protoimpl.MessageInfo, 29) var file_pkg_services_tree_service_proto_goTypes = []interface{}{ (*AddRequest)(nil), // 0: tree.AddRequest (*AddResponse)(nil), // 1: tree.AddResponse @@ -1998,70 +1977,68 @@ var file_pkg_services_tree_service_proto_goTypes = []interface{}{ (*GetNodeByPathResponse_Info)(nil), // 23: tree.GetNodeByPathResponse.Info (*GetNodeByPathResponse_Body)(nil), // 24: tree.GetNodeByPathResponse.Body (*GetSubTreeRequest_Body)(nil), // 25: tree.GetSubTreeRequest.Body - (*GetSubTreeResponse_Info)(nil), // 26: tree.GetSubTreeResponse.Info - (*GetSubTreeResponse_Body)(nil), // 27: tree.GetSubTreeResponse.Body - (*ApplyRequest_Body)(nil), // 28: tree.ApplyRequest.Body - (*ApplyResponse_Body)(nil), // 29: tree.ApplyResponse.Body - (*Signature)(nil), // 30: tree.Signature - (*KeyValue)(nil), // 31: tree.KeyValue - (*LogMove)(nil), // 32: tree.LogMove + (*GetSubTreeResponse_Body)(nil), // 26: tree.GetSubTreeResponse.Body + (*ApplyRequest_Body)(nil), // 27: tree.ApplyRequest.Body + (*ApplyResponse_Body)(nil), // 28: tree.ApplyResponse.Body + (*Signature)(nil), // 29: tree.Signature + (*KeyValue)(nil), // 30: tree.KeyValue + (*LogMove)(nil), // 31: tree.LogMove } var file_pkg_services_tree_service_proto_depIdxs = []int32{ 14, // 0: tree.AddRequest.body:type_name -> tree.AddRequest.Body - 30, // 1: tree.AddRequest.signature:type_name -> tree.Signature + 29, // 1: tree.AddRequest.signature:type_name -> tree.Signature 15, // 2: tree.AddResponse.body:type_name -> tree.AddResponse.Body - 30, // 3: tree.AddResponse.signature:type_name -> tree.Signature + 29, // 3: tree.AddResponse.signature:type_name -> tree.Signature 16, // 4: tree.AddByPathRequest.body:type_name -> tree.AddByPathRequest.Body - 30, // 5: tree.AddByPathRequest.signature:type_name -> tree.Signature + 29, // 5: tree.AddByPathRequest.signature:type_name -> tree.Signature 17, // 6: tree.AddByPathResponse.body:type_name -> tree.AddByPathResponse.Body - 30, // 7: tree.AddByPathResponse.signature:type_name -> tree.Signature + 29, // 7: tree.AddByPathResponse.signature:type_name -> tree.Signature 18, // 8: tree.RemoveRequest.body:type_name -> tree.RemoveRequest.Body - 30, // 9: tree.RemoveRequest.signature:type_name -> tree.Signature + 29, // 9: tree.RemoveRequest.signature:type_name -> tree.Signature 19, // 10: tree.RemoveResponse.body:type_name -> tree.RemoveResponse.Body - 30, // 11: tree.RemoveResponse.signature:type_name -> tree.Signature + 29, // 11: tree.RemoveResponse.signature:type_name -> tree.Signature 20, // 12: tree.MoveRequest.body:type_name -> tree.MoveRequest.Body - 30, // 13: tree.MoveRequest.signature:type_name -> tree.Signature + 29, // 13: tree.MoveRequest.signature:type_name -> tree.Signature 21, // 14: tree.MoveResponse.body:type_name -> tree.MoveResponse.Body - 30, // 15: tree.MoveResponse.signature:type_name -> tree.Signature + 29, // 15: tree.MoveResponse.signature:type_name -> tree.Signature 22, // 16: tree.GetNodeByPathRequest.body:type_name -> tree.GetNodeByPathRequest.Body - 30, // 17: tree.GetNodeByPathRequest.signature:type_name -> tree.Signature + 29, // 17: tree.GetNodeByPathRequest.signature:type_name -> tree.Signature 24, // 18: tree.GetNodeByPathResponse.body:type_name -> tree.GetNodeByPathResponse.Body - 30, // 19: tree.GetNodeByPathResponse.signature:type_name -> tree.Signature + 29, // 19: tree.GetNodeByPathResponse.signature:type_name -> tree.Signature 25, // 20: tree.GetSubTreeRequest.body:type_name -> tree.GetSubTreeRequest.Body - 30, // 21: tree.GetSubTreeRequest.signature:type_name -> tree.Signature - 27, // 22: tree.GetSubTreeResponse.body:type_name -> tree.GetSubTreeResponse.Body - 30, // 23: tree.GetSubTreeResponse.signature:type_name -> tree.Signature - 28, // 24: tree.ApplyRequest.body:type_name -> tree.ApplyRequest.Body - 30, // 25: tree.ApplyRequest.signature:type_name -> tree.Signature - 29, // 26: tree.ApplyResponse.body:type_name -> tree.ApplyResponse.Body - 30, // 27: tree.ApplyResponse.signature:type_name -> tree.Signature - 31, // 28: tree.AddRequest.Body.meta:type_name -> tree.KeyValue - 31, // 29: tree.AddByPathRequest.Body.meta:type_name -> tree.KeyValue - 31, // 30: tree.MoveRequest.Body.meta:type_name -> tree.KeyValue - 31, // 31: tree.GetNodeByPathResponse.Info.meta:type_name -> tree.KeyValue + 29, // 21: tree.GetSubTreeRequest.signature:type_name -> tree.Signature + 26, // 22: tree.GetSubTreeResponse.body:type_name -> tree.GetSubTreeResponse.Body + 29, // 23: tree.GetSubTreeResponse.signature:type_name -> tree.Signature + 27, // 24: tree.ApplyRequest.body:type_name -> tree.ApplyRequest.Body + 29, // 25: tree.ApplyRequest.signature:type_name -> tree.Signature + 28, // 26: tree.ApplyResponse.body:type_name -> tree.ApplyResponse.Body + 29, // 27: tree.ApplyResponse.signature:type_name -> tree.Signature + 30, // 28: tree.AddRequest.Body.meta:type_name -> tree.KeyValue + 30, // 29: tree.AddByPathRequest.Body.meta:type_name -> tree.KeyValue + 30, // 30: tree.MoveRequest.Body.meta:type_name -> tree.KeyValue + 30, // 31: tree.GetNodeByPathResponse.Info.meta:type_name -> tree.KeyValue 23, // 32: tree.GetNodeByPathResponse.Body.nodes:type_name -> tree.GetNodeByPathResponse.Info - 31, // 33: tree.GetSubTreeResponse.Info.meta:type_name -> tree.KeyValue - 26, // 34: tree.GetSubTreeResponse.Body.info:type_name -> tree.GetSubTreeResponse.Info - 32, // 35: tree.ApplyRequest.Body.operation:type_name -> tree.LogMove - 0, // 36: tree.TreeService.Add:input_type -> tree.AddRequest - 2, // 37: tree.TreeService.AddByPath:input_type -> tree.AddByPathRequest - 4, // 38: tree.TreeService.Remove:input_type -> tree.RemoveRequest - 6, // 39: tree.TreeService.Move:input_type -> tree.MoveRequest - 8, // 40: tree.TreeService.GetNodeByPath:input_type -> tree.GetNodeByPathRequest - 10, // 41: tree.TreeService.GetSubTree:input_type -> tree.GetSubTreeRequest - 12, // 42: tree.TreeService.Apply:input_type -> tree.ApplyRequest - 1, // 43: tree.TreeService.Add:output_type -> tree.AddResponse - 3, // 44: tree.TreeService.AddByPath:output_type -> tree.AddByPathResponse - 5, // 45: tree.TreeService.Remove:output_type -> tree.RemoveResponse - 7, // 46: tree.TreeService.Move:output_type -> tree.MoveResponse - 9, // 47: tree.TreeService.GetNodeByPath:output_type -> tree.GetNodeByPathResponse - 11, // 48: tree.TreeService.GetSubTree:output_type -> tree.GetSubTreeResponse - 13, // 49: tree.TreeService.Apply:output_type -> tree.ApplyResponse - 43, // [43:50] is the sub-list for method output_type - 36, // [36:43] is the sub-list for method input_type - 36, // [36:36] is the sub-list for extension type_name - 36, // [36:36] is the sub-list for extension extendee - 0, // [0:36] is the sub-list for field type_name + 30, // 33: tree.GetSubTreeResponse.Body.meta:type_name -> tree.KeyValue + 31, // 34: tree.ApplyRequest.Body.operation:type_name -> tree.LogMove + 0, // 35: tree.TreeService.Add:input_type -> tree.AddRequest + 2, // 36: tree.TreeService.AddByPath:input_type -> tree.AddByPathRequest + 4, // 37: tree.TreeService.Remove:input_type -> tree.RemoveRequest + 6, // 38: tree.TreeService.Move:input_type -> tree.MoveRequest + 8, // 39: tree.TreeService.GetNodeByPath:input_type -> tree.GetNodeByPathRequest + 10, // 40: tree.TreeService.GetSubTree:input_type -> tree.GetSubTreeRequest + 12, // 41: tree.TreeService.Apply:input_type -> tree.ApplyRequest + 1, // 42: tree.TreeService.Add:output_type -> tree.AddResponse + 3, // 43: tree.TreeService.AddByPath:output_type -> tree.AddByPathResponse + 5, // 44: tree.TreeService.Remove:output_type -> tree.RemoveResponse + 7, // 45: tree.TreeService.Move:output_type -> tree.MoveResponse + 9, // 46: tree.TreeService.GetNodeByPath:output_type -> tree.GetNodeByPathResponse + 11, // 47: tree.TreeService.GetSubTree:output_type -> tree.GetSubTreeResponse + 13, // 48: tree.TreeService.Apply:output_type -> tree.ApplyResponse + 42, // [42:49] is the sub-list for method output_type + 35, // [35:42] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name } func init() { file_pkg_services_tree_service_proto_init() } @@ -2384,18 +2361,6 @@ func file_pkg_services_tree_service_proto_init() { } } file_pkg_services_tree_service_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetSubTreeResponse_Info); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_pkg_services_tree_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetSubTreeResponse_Body); i { case 0: return &v.state @@ -2407,7 +2372,7 @@ func file_pkg_services_tree_service_proto_init() { return nil } } - file_pkg_services_tree_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_pkg_services_tree_service_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyRequest_Body); i { case 0: return &v.state @@ -2419,7 +2384,7 @@ func file_pkg_services_tree_service_proto_init() { return nil } } - file_pkg_services_tree_service_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + file_pkg_services_tree_service_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*ApplyResponse_Body); i { case 0: return &v.state @@ -2438,7 +2403,7 @@ func file_pkg_services_tree_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_pkg_services_tree_service_proto_rawDesc, NumEnums: 0, - NumMessages: 30, + NumMessages: 29, NumExtensions: 0, NumServices: 1, }, diff --git a/pkg/services/tree/service.proto b/pkg/services/tree/service.proto index f18b835f7..0da34f652 100644 --- a/pkg/services/tree/service.proto +++ b/pkg/services/tree/service.proto @@ -21,7 +21,7 @@ service TreeService { // GetNodeByPath returns list of IDs corresponding to a specific filepath. rpc GetNodeByPath (GetNodeByPathRequest) returns (GetNodeByPathResponse); // GetSubTree returns tree corresponding to a specific node. - rpc GetSubTree (GetSubTreeRequest) returns (GetSubTreeResponse); + rpc GetSubTree (GetSubTreeRequest) returns (stream GetSubTreeResponse); /* Synchronization API */ @@ -153,7 +153,10 @@ message GetSubTreeRequest { message Body { bytes container_id = 1; string tree_id = 2; - repeated uint64 nodes = 3; + uint64 root_id = 3; + // Optional depth of the traversal. Zero means return only root. + // Maximum depth is 10. + uint32 depth = 4; } Body body = 1; @@ -161,12 +164,11 @@ message GetSubTreeRequest { } message GetSubTreeResponse { - message Info { - uint64 node_id = 1; - repeated KeyValue meta = 2; - } message Body { - repeated Info info = 1; + uint64 node_id = 1; + uint64 parent_id = 2; + uint64 timestamp = 3; + repeated KeyValue meta = 4; } Body body = 1; diff --git a/pkg/services/tree/service_grpc.pb.go b/pkg/services/tree/service_grpc.pb.go index 47aac0f78..dc6fe4fd1 100644 --- a/pkg/services/tree/service_grpc.pb.go +++ b/pkg/services/tree/service_grpc.pb.go @@ -33,7 +33,7 @@ type TreeServiceClient interface { // GetNodeByPath returns list of IDs corresponding to a specific filepath. GetNodeByPath(ctx context.Context, in *GetNodeByPathRequest, opts ...grpc.CallOption) (*GetNodeByPathResponse, error) // GetSubTree returns tree corresponding to a specific node. - GetSubTree(ctx context.Context, in *GetSubTreeRequest, opts ...grpc.CallOption) (*GetSubTreeResponse, error) + GetSubTree(ctx context.Context, in *GetSubTreeRequest, opts ...grpc.CallOption) (TreeService_GetSubTreeClient, error) // Apply pushes log operation from another node to the current. // The request must be signed by a container node. Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResponse, error) @@ -92,13 +92,36 @@ func (c *treeServiceClient) GetNodeByPath(ctx context.Context, in *GetNodeByPath return out, nil } -func (c *treeServiceClient) GetSubTree(ctx context.Context, in *GetSubTreeRequest, opts ...grpc.CallOption) (*GetSubTreeResponse, error) { - out := new(GetSubTreeResponse) - err := c.cc.Invoke(ctx, "/tree.TreeService/GetSubTree", in, out, opts...) +func (c *treeServiceClient) GetSubTree(ctx context.Context, in *GetSubTreeRequest, opts ...grpc.CallOption) (TreeService_GetSubTreeClient, error) { + stream, err := c.cc.NewStream(ctx, &TreeService_ServiceDesc.Streams[0], "/tree.TreeService/GetSubTree", opts...) if err != nil { return nil, err } - return out, nil + x := &treeServiceGetSubTreeClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type TreeService_GetSubTreeClient interface { + Recv() (*GetSubTreeResponse, error) + grpc.ClientStream +} + +type treeServiceGetSubTreeClient struct { + grpc.ClientStream +} + +func (x *treeServiceGetSubTreeClient) Recv() (*GetSubTreeResponse, error) { + m := new(GetSubTreeResponse) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil } func (c *treeServiceClient) Apply(ctx context.Context, in *ApplyRequest, opts ...grpc.CallOption) (*ApplyResponse, error) { @@ -125,7 +148,7 @@ type TreeServiceServer interface { // GetNodeByPath returns list of IDs corresponding to a specific filepath. GetNodeByPath(context.Context, *GetNodeByPathRequest) (*GetNodeByPathResponse, error) // GetSubTree returns tree corresponding to a specific node. - GetSubTree(context.Context, *GetSubTreeRequest) (*GetSubTreeResponse, error) + GetSubTree(*GetSubTreeRequest, TreeService_GetSubTreeServer) error // Apply pushes log operation from another node to the current. // The request must be signed by a container node. Apply(context.Context, *ApplyRequest) (*ApplyResponse, error) @@ -150,8 +173,8 @@ func (UnimplementedTreeServiceServer) Move(context.Context, *MoveRequest) (*Move func (UnimplementedTreeServiceServer) GetNodeByPath(context.Context, *GetNodeByPathRequest) (*GetNodeByPathResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetNodeByPath not implemented") } -func (UnimplementedTreeServiceServer) GetSubTree(context.Context, *GetSubTreeRequest) (*GetSubTreeResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method GetSubTree not implemented") +func (UnimplementedTreeServiceServer) GetSubTree(*GetSubTreeRequest, TreeService_GetSubTreeServer) error { + return status.Errorf(codes.Unimplemented, "method GetSubTree not implemented") } func (UnimplementedTreeServiceServer) Apply(context.Context, *ApplyRequest) (*ApplyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method Apply not implemented") @@ -258,22 +281,25 @@ func _TreeService_GetNodeByPath_Handler(srv interface{}, ctx context.Context, de return interceptor(ctx, in, info, handler) } -func _TreeService_GetSubTree_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSubTreeRequest) - if err := dec(in); err != nil { - return nil, err +func _TreeService_GetSubTree_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(GetSubTreeRequest) + if err := stream.RecvMsg(m); err != nil { + return err } - if interceptor == nil { - return srv.(TreeServiceServer).GetSubTree(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/tree.TreeService/GetSubTree", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(TreeServiceServer).GetSubTree(ctx, req.(*GetSubTreeRequest)) - } - return interceptor(ctx, in, info, handler) + return srv.(TreeServiceServer).GetSubTree(m, &treeServiceGetSubTreeServer{stream}) +} + +type TreeService_GetSubTreeServer interface { + Send(*GetSubTreeResponse) error + grpc.ServerStream +} + +type treeServiceGetSubTreeServer struct { + grpc.ServerStream +} + +func (x *treeServiceGetSubTreeServer) Send(m *GetSubTreeResponse) error { + return x.ServerStream.SendMsg(m) } func _TreeService_Apply_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { @@ -321,15 +347,17 @@ var TreeService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetNodeByPath", Handler: _TreeService_GetNodeByPath_Handler, }, - { - MethodName: "GetSubTree", - Handler: _TreeService_GetSubTree_Handler, - }, { MethodName: "Apply", Handler: _TreeService_Apply_Handler, }, }, - Streams: []grpc.StreamDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "GetSubTree", + Handler: _TreeService_GetSubTree_Handler, + ServerStreams: true, + }, + }, Metadata: "pkg/services/tree/service.proto", }