From b4e72a2dfdede508443e301d12bf619987f428ba Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Tue, 11 Jul 2023 11:39:17 +0300 Subject: [PATCH] [#335] treesvc: Sort nodes by Filename in GetSubTree Signed-off-by: Dmitrii Stepanov --- pkg/local_object_storage/engine/tree.go | 4 +- pkg/local_object_storage/pilorama/boltdb.go | 19 +++++-- pkg/local_object_storage/pilorama/forest.go | 12 +++-- .../pilorama/interface.go | 2 +- pkg/local_object_storage/pilorama/types.go | 6 +++ pkg/local_object_storage/shard/tree.go | 2 +- pkg/services/tree/service.go | 49 ++++++++++++++---- pkg/services/tree/service.pb.go | Bin 118702 -> 124265 bytes pkg/services/tree/service.proto | 9 ++++ pkg/services/tree/service_frostfs.pb.go | Bin 57237 -> 58250 bytes pkg/services/tree/service_grpc.pb.go | Bin 18231 -> 19139 bytes pkg/services/tree/types.pb.go | Bin 9911 -> 9911 bytes 12 files changed, 81 insertions(+), 22 deletions(-) diff --git a/pkg/local_object_storage/engine/tree.go b/pkg/local_object_storage/engine/tree.go index 08c6d26b0..df3e919ec 100644 --- a/pkg/local_object_storage/engine/tree.go +++ b/pkg/local_object_storage/engine/tree.go @@ -172,7 +172,7 @@ func (e *StorageEngine) TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID s } // TreeGetChildren implements the pilorama.Forest interface. -func (e *StorageEngine) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]uint64, error) { +func (e *StorageEngine) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]pilorama.NodeInfo, error) { ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.TreeGetChildren", trace.WithAttributes( attribute.String("container_id", cid.EncodeToString()), @@ -183,7 +183,7 @@ func (e *StorageEngine) TreeGetChildren(ctx context.Context, cid cidSDK.ID, tree defer span.End() var err error - var nodes []uint64 + var nodes []pilorama.NodeInfo for _, sh := range e.sortShardsByWeight(cid) { nodes, err = sh.TreeGetChildren(ctx, cid, treeID, nodeID) if err != nil { diff --git a/pkg/local_object_storage/pilorama/boltdb.go b/pkg/local_object_storage/pilorama/boltdb.go index 53a52433d..a729e2a22 100644 --- a/pkg/local_object_storage/pilorama/boltdb.go +++ b/pkg/local_object_storage/pilorama/boltdb.go @@ -927,7 +927,7 @@ func (t *boltForest) TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID stri } // TreeGetChildren implements the Forest interface. -func (t *boltForest) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) { +func (t *boltForest) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]NodeInfo, error) { var ( startedAt = time.Now() success = false @@ -956,7 +956,7 @@ func (t *boltForest) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID key[0] = 'c' binary.LittleEndian.PutUint64(key[1:], nodeID) - var children []uint64 + var result []NodeInfo err := t.db.View(func(tx *bbolt.Tx) error { treeRoot := tx.Bucket(bucketName(cid, treeID)) @@ -967,12 +967,23 @@ func (t *boltForest) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID b := treeRoot.Bucket(dataBucket) c := b.Cursor() for k, _ := c.Seek(key); len(k) == childrenKeySize && binary.LittleEndian.Uint64(k[1:]) == nodeID; k, _ = c.Next() { - children = append(children, binary.LittleEndian.Uint64(k[9:])) + childID := binary.LittleEndian.Uint64(k[9:]) + childInfo := NodeInfo{ + ID: childID, + } + parentID, _, metaBytes, found := t.getState(b, stateKey(key, childID)) + if found { + childInfo.ParentID = parentID + if err := childInfo.Meta.FromBytes(metaBytes); err != nil { + return err + } + } + result = append(result, childInfo) } return nil }) success = err == nil - return children, metaerr.Wrap(err) + return result, metaerr.Wrap(err) } // TreeList implements the Forest interface. diff --git a/pkg/local_object_storage/pilorama/forest.go b/pkg/local_object_storage/pilorama/forest.go index 76220c1db..8fb519128 100644 --- a/pkg/local_object_storage/pilorama/forest.go +++ b/pkg/local_object_storage/pilorama/forest.go @@ -148,7 +148,7 @@ func (f *memoryForest) TreeGetMeta(_ context.Context, cid cid.ID, treeID string, } // TreeGetChildren implements the Forest interface. -func (f *memoryForest) TreeGetChildren(_ context.Context, cid cid.ID, treeID string, nodeID Node) ([]uint64, error) { +func (f *memoryForest) TreeGetChildren(_ context.Context, cid cid.ID, treeID string, nodeID Node) ([]NodeInfo, error) { fullID := cid.String() + "/" + treeID s, ok := f.treeMap[fullID] if !ok { @@ -156,8 +156,14 @@ func (f *memoryForest) TreeGetChildren(_ context.Context, cid cid.ID, treeID str } children := s.tree.getChildren(nodeID) - res := make([]Node, len(children)) - copy(res, children) + res := make([]NodeInfo, 0, len(children)) + for _, childID := range children { + res = append(res, NodeInfo{ + ID: childID, + Meta: s.infoMap[childID].Meta, + ParentID: s.infoMap[childID].Parent, + }) + } return res, nil } diff --git a/pkg/local_object_storage/pilorama/interface.go b/pkg/local_object_storage/pilorama/interface.go index 89c752627..ea171a479 100644 --- a/pkg/local_object_storage/pilorama/interface.go +++ b/pkg/local_object_storage/pilorama/interface.go @@ -32,7 +32,7 @@ type Forest interface { TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) (Meta, Node, error) // TreeGetChildren returns children of the node with the specified ID. The order is arbitrary. // Should return ErrTreeNotFound if the tree is not found, and empty result if the node is not in the tree. - TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]uint64, error) + TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID Node) ([]NodeInfo, error) // TreeGetOpLog returns first log operation stored at or above the height. // In case no such operation is found, empty Move and nil error should be returned. TreeGetOpLog(ctx context.Context, cid cidSDK.ID, treeID string, height uint64) (Move, error) diff --git a/pkg/local_object_storage/pilorama/types.go b/pkg/local_object_storage/pilorama/types.go index 99918683d..8d8616364 100644 --- a/pkg/local_object_storage/pilorama/types.go +++ b/pkg/local_object_storage/pilorama/types.go @@ -55,3 +55,9 @@ var ( func isAttributeInternal(key string) bool { return key == AttributeFilename } + +type NodeInfo struct { + ID Node + Meta Meta + ParentID Node +} diff --git a/pkg/local_object_storage/shard/tree.go b/pkg/local_object_storage/shard/tree.go index 81477325a..7e2c80152 100644 --- a/pkg/local_object_storage/shard/tree.go +++ b/pkg/local_object_storage/shard/tree.go @@ -159,7 +159,7 @@ func (s *Shard) TreeGetMeta(ctx context.Context, cid cidSDK.ID, treeID string, n } // TreeGetChildren implements the pilorama.Forest interface. -func (s *Shard) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]uint64, error) { +func (s *Shard) TreeGetChildren(ctx context.Context, cid cidSDK.ID, treeID string, nodeID pilorama.Node) ([]pilorama.NodeInfo, error) { ctx, span := tracing.StartSpanFromContext(ctx, "Shard.TreeGetChildren", trace.WithAttributes( attribute.String("shard_id", s.ID().String()), diff --git a/pkg/services/tree/service.go b/pkg/services/tree/service.go index 12d970c42..57767f87e 100644 --- a/pkg/services/tree/service.go +++ b/pkg/services/tree/service.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "sort" "sync" "sync/atomic" @@ -440,7 +441,15 @@ func (s *Service) GetSubTree(req *GetSubTreeRequest, srv TreeService_GetSubTreeS func getSubTree(ctx context.Context, srv TreeService_GetSubTreeServer, cid cidSDK.ID, b *GetSubTreeRequest_Body, forest pilorama.Forest) error { // Traverse the tree in a DFS manner. Because we need to support arbitrary depth, // recursive implementation is not suitable here, so we maintain explicit stack. - stack := [][]uint64{{b.GetRootId()}} + m, p, err := forest.TreeGetMeta(ctx, cid, b.GetTreeId(), b.GetRootId()) + if err != nil { + return err + } + stack := [][]pilorama.NodeInfo{{{ + ID: b.GetRootId(), + Meta: m, + ParentID: p, + }}} for { if len(stack) == 0 { @@ -450,19 +459,15 @@ func getSubTree(ctx context.Context, srv TreeService_GetSubTreeServer, cid cidSD continue } - nodeID := stack[len(stack)-1][0] + node := stack[len(stack)-1][0] stack[len(stack)-1] = stack[len(stack)-1][1:] - m, p, err := forest.TreeGetMeta(ctx, cid, b.GetTreeId(), nodeID) - if err != nil { - return err - } err = srv.Send(&GetSubTreeResponse{ Body: &GetSubTreeResponse_Body{ - NodeId: nodeID, - ParentId: p, - Timestamp: m.Time, - Meta: metaToProto(m.Items), + NodeId: node.ID, + ParentId: node.ParentID, + Timestamp: node.Meta.Time, + Meta: metaToProto(node.Meta.Items), }, }) if err != nil { @@ -470,7 +475,11 @@ func getSubTree(ctx context.Context, srv TreeService_GetSubTreeServer, cid cidSD } if b.GetDepth() == 0 || uint32(len(stack)) < b.GetDepth() { - children, err := forest.TreeGetChildren(ctx, cid, b.GetTreeId(), nodeID) + children, err := forest.TreeGetChildren(ctx, cid, b.GetTreeId(), node.ID) + if err != nil { + return err + } + children, err = sortByFilename(children, b.GetOrderBy().GetDirection()) if err != nil { return err } @@ -482,6 +491,24 @@ func getSubTree(ctx context.Context, srv TreeService_GetSubTreeServer, cid cidSD return nil } +func sortByFilename(nodes []pilorama.NodeInfo, d GetSubTreeRequest_Body_Order_Direction) ([]pilorama.NodeInfo, error) { + switch d { + case GetSubTreeRequest_Body_Order_None: + return nodes, nil + case GetSubTreeRequest_Body_Order_Asc: + if len(nodes) == 0 { + return nodes, nil + } + less := func(i, j int) bool { + return bytes.Compare(nodes[i].Meta.GetAttr(pilorama.AttributeFilename), nodes[j].Meta.GetAttr(pilorama.AttributeFilename)) < 0 + } + sort.Slice(nodes, less) + return nodes, nil + default: + return nil, fmt.Errorf("unsupported order direction: %s", d.String()) + } +} + // Apply locally applies operation from the remote node to the tree. func (s *Service) Apply(_ context.Context, req *ApplyRequest) (*ApplyResponse, error) { err := verifyMessage(req) diff --git a/pkg/services/tree/service.pb.go b/pkg/services/tree/service.pb.go index 08664a6d050d1d1618603d7179be6cb5c0331368..63f3e714a2578d648309f46e53a12d3eda9f61f0 100644 GIT binary patch delta 7220 zcmai333QZImOeM3R+11RWC0V3AW)Trkk8{96d zwIWbm-(a&_Ey6hzYYl4M9;g->L-DbrHs#qgU2|-3sj$AnCbXfr^dC@lS{+tvMuu%( zb>n*5eZF;#e%pFqO}(uuP$#z}T)QRRdY`W@>;1Mo39(%HzOufdZcX() z4_Yl2E!UO|pZ&>ccyQtYZpqIry0dqT!qUCb!E~6F3R8mVNVzsmf+?h2o6V`v5KmQO zb%o8|Bm;=Q7S^FcwL8S|)A5PdF4}-yW6RC6Rr~L^OR`5%=CE2)Y7k+Q)l%ngXsoNY z)mU3bSd6|FR5)kaizL5`6&EE{_NMf?{`!i#H8l-^I=dsyR#O+iCx6vCq&;0~-rl(W zZhxJ<$>F#HF5St!7TaOF8fi4pl~wVXQJdxm_MOc&{`%$G$`L+{G?P8rjND~+j5Z+U zk~N(S(P5YwceRl#xGEcWjs^~f`UBAV$`$a@#&r$x+3G~$8McqTOX*-Q%^P+pNSUF=ZzQbrwn4p$IJRZd^yD+ zjwM;du|g>vHm28T$lU3qM5FG7cxvecF<5C9kwq4fH)IWIlrDS7GCVqD?T;p8-0zZN zD?`&I=x3VDR~}z(qgd(Es38=JV|ZUMkl2vjlUWnROLi{U!`{{U zZWfnkkI-85>#3r9j^%2t_xG<7N8b4S>ab4#Jx%l$jM(UmsW3QY+C=`FiE?`|!w%bg zf5R{Z;O_@X0Fx7jmDKzR7B93zBU274K}C~A$$X>O`B<~)HXFp*Hf#T{UTY5%|9!?U zG72Mv_Ex0md;L?9^zKJuRdEcI4dTN04pB1{(LeO(3*q7q=K|vL0)xnS=aR60aH&7? z?61SbM<4rzRvIlX-(e8Fug?4*hSBm_S{ae3SS!opo_Zmg{*CyC17CY{VRG&|MX;2+B0CciJ9_`9|L`mZa( zxFDPi{1#EQ=v!|TmjZg=Yw9CK$#+NP>K9^mjaulx@cnrrr|9^$R+QFS#qv+)i8b{x z;#{MFPlVB4EcufEA}%#3(Exnr8_joDp%$7n_rN>(^g@`qB3nJW__&3p@X_fsmS40` z8rN89Dj!QF<3P$BGKbG}x^Q4wS6`(CX ziVkzRcu^ENxU81UJm@0}f0s`ad0QP7aKQ{pYIFIO0nZ3H1)3>;>IO0uu$HCd|5DQv zbU|u5mAhGPWkAz?SD;rJyBW@3YYj1Dw@2Tg|IAT3x~BRZ3|zr^r3F4(Nt5^=6*MKn zFO}U2#KnaP5dW=?mT`>+3Ys%0Rs}u}flDj@GMb={M@8WM0mFQxlCGZ-r04%}l2
  • 3$ha9BQ=MzPK8SwdrYXEDeRZH)*+zIvl5lfO>{SYHN3_l%>cfzMXZMB@A@ z6vY!(Q8K@6qa1F(g^YY@4w`d&0mXBl13Su7&??8Qz(w0KBrDLnOLX)C z7r9uo!M!(10P!mujA^&g4Q#8TX_nxya85i0K%)XACbggQWHuaHZ=W zxDX0Df&&fkagqh~TXPpyztw0OA1i{Di#LFSh0Dnjp~_t!0##hN0-4qdK}gAJ(8UWu z7s_uwDsRN#PvRZRK|#}E@E0n-c>Un{Y8jRCsYQU81RnOIRu6M7QRIhza6NGRu&;loTd-23H7#t4dy1LqQGDREl` zrAg1LmtfMUB;$CPD61frKU@O-Yobto$h1vrogPOMck8OlrI6|xG)0p_g3qBqgJCT(1I>$c?ro}&5DshW~m_csq1v{u_h#ILjWo5&H!5Z zYRcw56N(-aQ=zG%9Bd-@0Gec@0!lI9D1$fe-3zOA#c1T~*6MK;tc4`YgoNL-7BgZ) z6dqLvrq_bEGiz1L^^8YYkFKRiH6m0Hx2*#UOV*-Vz8;U!v1%S%IlPW$B4%hiI68*B zYFs*fd^runacu%Hz8g>Rk`?E8?3Uw3VLiK^ruNLB*fH{i>Zzvr{9#~(8yl+i&ex^^ zxh)xaEGVVxqbd;+hIa5MzI-rj%(I^`VhalzL{K;RPXE8Ky~NvR>+%& zoEQB2W}2_28!jb~xhmP0g1Sh5fO4cSy#{p3e;H}wz@Y~)lg7xGLVoulN{E<;Pq$Q8 z1=4Iz2lqWhcZRvt?-bTrNWEWA8ueCh*YK?dzE#rL*-qE>>>_Jix_Ws{zv{)yo>Ev@ z$*0>;wvTtB5+7*+RjnkM~Jd{dK*}Nrxo^o)<$un zEYrX(ZLoK=1qnajhLP~#K2-D5QYL-iw04R|j0QHgL(~hA0Y)*B5PUw*?nH(5w&Uh> zZa+pxS3BxgMicJz>bEP-)5kU?Es35#;RzLP+mT{8+ErpuIP9jz$@JY z?d?*g<7MY^;bDo_0lX8^v+NCo?;L839J3+l89%fEftflHz)z%1qM3D;Tu$!TfMZdD z$#=P)tt}S4>a=c3iztwl&+~df9eL!@`d3sHpNC2;+_IG#7%&J7^wdv2TZZ z`q>UD!aO*?gNm`HbQLeC0Hge64M$X+ynOLmZ zNedCg6FaFCPf@$*W;57M&*-3P9CHjvxo(nzCC(Qned|X7gja;ft<_ zYF0HdZV%Zpk+S#D!qAFauZ`7Pak21d{PG^U9ga>Gur9C32*m9Tl|xQwQ&vb~;*Ne0MgalO$z8BHD)3|R7)|5my<>6y{k$w-qzZXt>*uD=n;o*Dt z(E^Jb@3%0FB2qp0_CCt7xPzsUIow$0^x*fQd~PSDjMv)?#}@ifZuSl zN%?uoL1fjwALmToL9~uMY{IP;yiCi*y-W@Z-t{GJrkgt$YHu-34@U*^@%?D(N&6`~ zSaq3v`Vgvm{eE%*JnaCO{Ox{9H|dUKvhOff!w-<%3`7Z-tpgSxpjj4|PG)9~F57hg zHH~kZ4#J7E2WYwl;mSQ;jBM!yympe8&&c(42PxHp3!GXHI@x~^h2qq>{Sdr>k5N44 z5Sq=!FG&Gh9LqmBglhC~^kE1N9wH5J%jM^f4pE93aMD`P!<56M1)I)$#lZ;v-~b}( zJwTE2$Qfs~c=aHbd$rswjXyn1iIW124Kg!p~dzzl*>Ce)gBIO;t zuf9xs`P8GhhTr^0Xa%07w|UKTw1|g#aq=AfBfZGmp2znR{m;>#`NRu!C!c+uhWPZ0 zkbU1rR&momaoqhUJWFom8R1F0ix}|k^lez delta 4686 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 F`VaT$w>$s< diff --git a/pkg/services/tree/service.proto b/pkg/services/tree/service.proto index 182d8adb2..ec63d88ec 100644 --- a/pkg/services/tree/service.proto +++ b/pkg/services/tree/service.proto @@ -238,6 +238,13 @@ message GetNodeByPathResponse { message GetSubTreeRequest { message Body { + message Order { + enum Direction { + None = 0; + Asc = 1; + } + Direction direction = 1; + } // Container ID in V2 format. bytes container_id = 1; // The name of the tree. @@ -249,6 +256,8 @@ message GetSubTreeRequest { uint32 depth = 4; // Bearer token in V2 format. bytes bearer_token = 5; + // Result ordering. + Order order_by = 6; } // Request body. diff --git a/pkg/services/tree/service_frostfs.pb.go b/pkg/services/tree/service_frostfs.pb.go index 42b7ba3fc7a175eec8fe18428984e102ad0cc92f..b272e4389907a89b64a5ca73e02db3dce21559f2 100644 GIT binary patch delta 197 zcmbQbpSkNe^M?8|ws`-dl+>chg0k9ju6d=o!I@R58iqOwnRz9~Mj91*E}2EC$t9Wj zd77IOHKs65uCEp3j(5sWsf6llbX&{<(l=S~j-(K#rpbcYb0!y z4N5#_ItmqfU~8Q!;idv*c{e*4nlMiGtZJCdqM$x`N}Mn|R27mj?3-syLV0FMhJtgDONc^AQEI9l z7uSS1M&pU`ax6xMdX^g-7cm+q=jRodC}?nTh5!{njfi(lNr`tW&B^giEy>7F@k`82 zRX_mS$qgbRY(OPST%40XIE#s3Q3+PV161Qw8IV|#0aBT$FD#BlCD2^3;>jNj*mY1{ z;hSHUiqjcjP3#~gAj2QHNC}`B4pt!Go?7CUpOT7j#zu275iEv6+~6z7jphbWsQ6?S zmtfHXakwm+2FHSeoJy>!;XYj_%7&&1=t}<;FDtn z#R#VU+{rZp`z9x;it(fQbaDf?(dI6}?@Y?bhT{l)up8M$rzjw+#TM>Bg}IXz#db`d zWG8~n#>riRB9qrjaBp@Kzs3X#T1*%FZSIt679+m2rC?<3yb}Mx%+{ax8{MdL|p!E@Iql$ZE?uS&&(n#W5vCY4SsXtjXW_mVg8% z-(und^ECu!0Qpit_Aih)t8ac;Dnw<9&=#=D4a|ICm8*mgg8BcLxi?P`(PRd3I75n3 zQ++avOCVa@B&L8w*&GWBaw?&wNbUwJbWbhuFYw7vSDHLQ#BK5~Y3|LFrLHl7qLW+ni!|608>HY>@aZd+yC=93Fx_iRwU* JVeYOqtN^?_ehUBq diff --git a/pkg/services/tree/types.pb.go b/pkg/services/tree/types.pb.go index 45d889177d01f3b6c8ed8b3959d5e7dcd24c4ed9..b4d6981ef92d8434e0d3ffa6370d8cb860db9ce4 100644 GIT binary patch delta 15 Wcmdn)yWMxf5*8yvJ