2024-04-22 06:43:42 +00:00
|
|
|
package getsvc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/policy"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/logger"
|
2024-05-02 11:29:59 +00:00
|
|
|
apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status"
|
2024-04-22 06:43:42 +00:00
|
|
|
objectSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/erasurecode"
|
|
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"golang.org/x/sync/errgroup"
|
|
|
|
)
|
|
|
|
|
|
|
|
type assemblerec struct {
|
|
|
|
addr oid.Address
|
|
|
|
ecInfo *objectSDK.ECInfo
|
|
|
|
rng *objectSDK.Range
|
|
|
|
objGetter objectGetter
|
|
|
|
cs container.Source
|
|
|
|
log *logger.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func newAssemblerEC(
|
|
|
|
addr oid.Address,
|
|
|
|
ecInfo *objectSDK.ECInfo,
|
|
|
|
rng *objectSDK.Range,
|
|
|
|
objGetter objectGetter,
|
|
|
|
cs container.Source,
|
|
|
|
log *logger.Logger,
|
|
|
|
) *assemblerec {
|
|
|
|
return &assemblerec{
|
|
|
|
addr: addr,
|
|
|
|
rng: rng,
|
|
|
|
ecInfo: ecInfo,
|
|
|
|
objGetter: objGetter,
|
|
|
|
cs: cs,
|
|
|
|
log: log,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assemble assembles erasure-coded object and writes it's content to ObjectWriter.
|
|
|
|
// It returns parent object.
|
|
|
|
func (a *assemblerec) Assemble(ctx context.Context, writer ObjectWriter, headOnly bool) (*objectSDK.Object, error) {
|
2024-05-02 11:29:59 +00:00
|
|
|
if headOnly {
|
|
|
|
return a.reconstructHeader(ctx, writer)
|
|
|
|
} else if a.rng != nil {
|
|
|
|
return a.reconstructRange(ctx, writer)
|
|
|
|
}
|
|
|
|
return a.reconstructObject(ctx, writer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *assemblerec) getConstructor() (*erasurecode.Constructor, error) {
|
2024-04-22 06:43:42 +00:00
|
|
|
cnt, err := a.cs.Get(a.addr.Container())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-02 11:29:59 +00:00
|
|
|
dataCount := policy.ECDataCount(cnt.Value.PlacementPolicy())
|
|
|
|
parityCount := policy.ECParityCount(cnt.Value.PlacementPolicy())
|
|
|
|
return erasurecode.NewConstructor(dataCount, parityCount)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *assemblerec) reconstructHeader(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
|
|
|
parts := a.retrieveParts(ctx, true)
|
|
|
|
c, err := a.getConstructor()
|
2024-04-22 06:43:42 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2024-05-02 11:29:59 +00:00
|
|
|
obj, err := c.ReconstructHeader(parts)
|
|
|
|
if err == nil {
|
|
|
|
return obj, writer.WriteHeader(ctx, obj)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *assemblerec) reconstructRange(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
|
|
|
parts := a.retrieveParts(ctx, false)
|
|
|
|
c, err := a.getConstructor()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
obj, err := c.Reconstruct(parts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
from := a.rng.GetOffset()
|
|
|
|
to := from + a.rng.GetLength()
|
|
|
|
if pLen := uint64(len(obj.Payload())); to < from || pLen < from || pLen < to {
|
|
|
|
return nil, &apistatus.ObjectOutOfRange{}
|
|
|
|
}
|
|
|
|
err = writer.WriteChunk(ctx, obj.Payload()[from:to])
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return obj, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *assemblerec) reconstructObject(ctx context.Context, writer ObjectWriter) (*objectSDK.Object, error) {
|
|
|
|
parts := a.retrieveParts(ctx, false)
|
|
|
|
c, err := a.getConstructor()
|
|
|
|
if err != nil {
|
2024-04-22 06:43:42 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
obj, err := c.Reconstruct(parts)
|
|
|
|
if err == nil {
|
|
|
|
err = writer.WriteHeader(ctx, obj.CutPayload())
|
|
|
|
if err == nil {
|
|
|
|
err = writer.WriteChunk(ctx, obj.Payload())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *assemblerec) retrieveParts(mainCtx context.Context, headOnly bool) []*objectSDK.Object {
|
|
|
|
parts := make([]*objectSDK.Object, int(a.ecInfo.Chunks[0].Total))
|
|
|
|
errGroup, ctx := errgroup.WithContext(mainCtx)
|
|
|
|
|
|
|
|
for i := range a.ecInfo.Chunks {
|
|
|
|
chunk := a.ecInfo.Chunks[i]
|
|
|
|
errGroup.Go(func() error {
|
|
|
|
objID := new(oid.ID)
|
|
|
|
err := objID.ReadFromV2(chunk.ID)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid object ID: %w", err)
|
|
|
|
}
|
|
|
|
var obj *objectSDK.Object
|
|
|
|
if headOnly {
|
|
|
|
obj, err = a.objGetter.HeadObject(ctx, *objID)
|
|
|
|
if err != nil {
|
|
|
|
a.log.Debug(logs.GetUnableToHeadPartECObject, zap.Stringer("part_id", objID), zap.Error(err))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sow := NewSimpleObjectWriter()
|
|
|
|
obj, err = a.objGetter.GetObjectAndWritePayload(ctx, *objID, nil, sow)
|
|
|
|
if err != nil {
|
|
|
|
a.log.Debug(logs.GetUnableToGetPartECObject, zap.Stringer("part_id", objID), zap.Error(err))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
obj.SetPayload(sow.pld)
|
|
|
|
}
|
|
|
|
parts[chunk.Index] = obj
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := errGroup.Wait(); err != nil {
|
|
|
|
a.log.Debug(logs.GetUnableToGetAllPartsECObject, zap.Error(err))
|
|
|
|
}
|
|
|
|
return parts
|
|
|
|
}
|