From 1ec5377241cedbe4782ea9d296cfbd8a463a0784 Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Thu, 20 Mar 2025 14:57:24 +0300 Subject: [PATCH] m Signed-off-by: Evgenii Stratonikov --- .../metabase/select_proto_access.go | 90 ++++++++++++++++--- .../metabase/select_proto_access_test.go | 63 +++++++++++++ 2 files changed, 143 insertions(+), 10 deletions(-) diff --git a/pkg/local_object_storage/metabase/select_proto_access.go b/pkg/local_object_storage/metabase/select_proto_access.go index 75d28610a..682b01f47 100644 --- a/pkg/local_object_storage/metabase/select_proto_access.go +++ b/pkg/local_object_storage/metabase/select_proto_access.go @@ -2,11 +2,10 @@ package meta import ( "errors" - "fmt" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" + "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/util/proto" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" - objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id" "github.com/VictoriaMetrics/easyproto" ) @@ -174,18 +173,89 @@ func attributeValueRaw(src []byte, attribute string) (attributeMatchResult, erro } func parentFromChild(src []byte) ([]byte, error) { - child := objectSDK.New() + iter := newProtoIter(src, true) + for iter.next(); !iter.finished(); iter.next() { + if iter.fc.FieldNum == 3 { + return parentFromChildHeader(iter.fc) + } + } + return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) +} - err := child.Unmarshal(src) - if err != nil { - return nil, fmt.Errorf("unmarshal child with parent: %w", err) +func parentFromChildHeader(fc easyproto.FieldContext) ([]byte, error) { + iter := newProtoIter(fc.MessageData()) + for iter.next(); !iter.finished(); iter.next() { + if iter.fc.FieldNum == 11 { + return parentFromSplitHeader(iter.fc) + } + } + return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) +} + +func parentFromSplitHeader(fc easyproto.FieldContext) ([]byte, error) { + iter := newProtoIter(fc.MessageData()) + + var parentID, parentSig, parentHdr []byte + for iter.next(); !iter.finished(); iter.next() { + var ok bool + switch iter.fc.FieldNum { + case 1: // parent id + parentID, ok = iter.fc.MessageData() + case 3: // parent signature + parentSig, ok = iter.fc.MessageData() + case 4: // parent header + parentHdr, ok = iter.fc.MessageData() + default: + continue + } + if !ok { + return nil, errMalformedObject + } } - par := child.Parent() - - if par == nil { // this should never happen though + if parentSig == nil || parentHdr == nil { return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) } - return par.Marshal() + h := &header{ + parentID: parentID, + parentSig: parentSig, + parentHdr: parentHdr, + } + return h.StableMarshal(make([]byte, h.StableSize())), nil +} + +type ( + header struct { + parentID bs + parentSig bs + parentHdr bs + } +) + +func (o *header) StableSize() int { + var size int + size += proto.NestedStructureSize(1, &o.parentID) + size += proto.NestedStructureSize(2, &o.parentSig) + size += proto.NestedStructureSize(3, &o.parentHdr) + return size +} + +func (o *header) StableMarshal(buf []byte) []byte { + var offset int + offset += proto.NestedStructureMarshal(1, buf[offset:], &o.parentID) + offset += proto.NestedStructureMarshal(2, buf[offset:], &o.parentSig) + offset += proto.NestedStructureMarshal(3, buf[offset:], &o.parentHdr) + return buf[:offset] +} + +type bs []byte + +func (b *bs) StableSize() int { + return len(*b) +} + +func (b *bs) StableMarshal(dst []byte) []byte { + copy(dst, *b) + return dst[:len(*b)] } diff --git a/pkg/local_object_storage/metabase/select_proto_access_test.go b/pkg/local_object_storage/metabase/select_proto_access_test.go index c85045ddb..4a7906b06 100644 --- a/pkg/local_object_storage/metabase/select_proto_access_test.go +++ b/pkg/local_object_storage/metabase/select_proto_access_test.go @@ -1,10 +1,16 @@ package meta import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "fmt" "strconv" "testing" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/internal/testutil" + "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr" + apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id/test" objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object" oidtest "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id/test" @@ -36,3 +42,60 @@ func TestAttributeValueRaw_ZeroAlloc(t *testing.T) { } })) } + +func BenchmarkParentFromChild(b *testing.B) { + cnr := cidtest.ID() + parent := testutil.GenerateObjectWithCIDWithPayload(cnr, nil) + parent.SetPayloadSize(1234) + + pk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(b, err) + require.NoError(b, objectSDK.CalculateAndSetSignature(*pk, parent)) + + child := testutil.GenerateObjectWithCID(cnr) + child.SetParent(parent) + + data, err := child.Marshal() + require.NoError(b, err) + + o1, err := parentFromChildUnoptimized(data) + require.NoError(b, err) + o2, err := parentFromChild(data) + require.NoError(b, err) + + require.Equal(b, o1, o2) + + b.Run("unoptimized", func(b *testing.B) { + for range b.N { + _, err := parentFromChildUnoptimized(data) + if err != nil { + b.Fatal(err) + } + } + }) + b.Run("proto access", func(b *testing.B) { + for range b.N { + _, err := parentFromChild(data) + if err != nil { + b.Fatal(err) + } + } + }) +} + +func parentFromChildUnoptimized(src []byte) ([]byte, error) { + child := objectSDK.New() + + err := child.Unmarshal(src) + if err != nil { + return nil, fmt.Errorf("unmarshal child with parent: %w", err) + } + + par := child.Parent() + + if par == nil { // this should never happen though + return nil, logicerr.Wrap(new(apistatus.ObjectNotFound)) + } + + return par.Marshal() +}