frostfs-node/pkg/local_object_storage/metabase/select_proto_access.go
Evgenii Stratonikov eb8b0a6999
WIP: metabase: Find attribute without full unmarshal
Signed-off-by: Evgenii Stratonikov <e.stratonikov@yadro.com>
2025-03-20 16:15:12 +03:00

191 lines
4.1 KiB
Go

package meta
import (
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/util/logicerr"
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"
)
var errMalformedObject = errors.New("malformed object")
type protoAttribute struct {
found bool
value string
}
type protoHeader struct {
attribute protoAttribute
ecHeader protoECHeader
}
type protoECHeader struct {
parentID protoOID
attribute protoAttribute
}
type protoOID oid.ID
type protoIter struct {
fc easyproto.FieldContext
data []byte
failed bool
eof bool
}
func newProtoIter(data []byte, ok bool) protoIter {
return protoIter{data: data, failed: !ok}
}
func (p *protoIter) err() error {
if p.failed {
return errMalformedObject
}
return nil
}
func (p *protoIter) finished() bool {
return p.failed || p.eof
}
func (p *protoIter) next() {
if p.failed {
return
}
if len(p.data) == 0 {
p.eof = true
return
}
data, err := p.fc.NextField(p.data)
if err != nil {
p.failed = true
return
}
p.data = data
}
func (p *protoOID) fill(fc easyproto.FieldContext) error {
iter := newProtoIter(fc.MessageData())
for iter.next(); !iter.finished(); iter.next() {
if iter.fc.FieldNum == 1 { // Wire number for `id` field.
rawID, ok := iter.fc.Bytes()
if !ok {
return errMalformedObject
}
return (*oid.ID)(p).Decode(rawID)
}
}
return iter.err()
}
func (p *protoAttribute) fill(fc easyproto.FieldContext, attribute string) error {
var key, value string
iter := newProtoIter(fc.MessageData())
for iter.next(); !iter.finished(); iter.next() {
var ok bool
switch iter.fc.FieldNum {
case 1: // Wire number for `key` field.
key, ok = iter.fc.String()
case 2: // Wire number for `value` field.
value, ok = iter.fc.String()
default:
continue
}
if !ok {
return errMalformedObject
}
}
if key == attribute {
p.found = true
p.value = value
}
return iter.err()
}
func (p *protoECHeader) fill(fc easyproto.FieldContext, attribute string) error {
iter := newProtoIter(fc.MessageData())
for iter.next(); !iter.finished(); iter.next() {
var err error
switch iter.fc.FieldNum {
case 1: // Wire number for `parent` field.
err = p.parentID.fill(iter.fc)
case 8: // Wire number for `parent_attributes` field.
err = p.attribute.fill(iter.fc, attribute)
}
if err != nil {
return err
}
}
return iter.err()
}
func (p *protoHeader) fill(fc easyproto.FieldContext, attribute string) error {
iter := newProtoIter(fc.MessageData())
for iter.next(); !iter.finished(); iter.next() {
var err error
switch iter.fc.FieldNum {
case 10: // Wire number for `attributes` field.
err = p.attribute.fill(iter.fc, attribute)
case 12: // Wire number for `ec` field.
err = p.ecHeader.fill(iter.fc, attribute)
}
if err != nil {
return err
}
}
return iter.err()
}
type attributeMatchResult struct {
attribute protoAttribute
isEC bool
parentID oid.ID
}
func attributeValueRaw(src []byte, attribute string) (attributeMatchResult, error) {
iter := newProtoIter(src, true)
for iter.next(); !iter.finished(); iter.next() {
if iter.fc.FieldNum == 3 { // Wire number for `header` field.
var p protoHeader
if err := p.fill(iter.fc, attribute); err != nil {
return attributeMatchResult{}, err
}
if p.ecHeader.attribute.found {
return attributeMatchResult{
attribute: p.ecHeader.attribute,
isEC: true,
parentID: oid.ID(p.ecHeader.parentID),
}, nil
} else {
return attributeMatchResult{attribute: p.attribute}, nil
}
}
}
return attributeMatchResult{}, iter.err()
}
func parentFromChild(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()
}