forked from TrueCloudLab/frostfs-node
If context has already been canceled, then there is no need to check other shards. At the same time, it is necessary to avoid handling context cancellation in each handler. Therefore, the context check has been moved to the shard iteration method, which now returns an error. Change-Id: I70030ace36593ce7d2b8376bee39fe82e9dbf88f Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
196 lines
5.5 KiB
Go
196 lines
5.5 KiB
Go
package engine
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/shard"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-observability/tracing"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
|
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"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
"go.opentelemetry.io/otel/trace"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// DeletePrm groups the parameters of Delete operation.
|
|
type DeletePrm struct {
|
|
addr oid.Address
|
|
|
|
forceRemoval bool
|
|
}
|
|
|
|
// WithAddress is a Delete option to set the addresses of the objects to delete.
|
|
//
|
|
// Option is required.
|
|
func (p *DeletePrm) WithAddress(addr oid.Address) {
|
|
p.addr = addr
|
|
}
|
|
|
|
// WithForceRemoval is a Delete option to remove an object despite any
|
|
// restrictions imposed on deleting that object. Expected to be used
|
|
// only in control service.
|
|
func (p *DeletePrm) WithForceRemoval() {
|
|
p.forceRemoval = true
|
|
}
|
|
|
|
// Delete marks the objects to be removed.
|
|
//
|
|
// Returns an error if executions are blocked (see BlockExecution).
|
|
//
|
|
// Returns apistatus.ObjectLocked if at least one object is locked.
|
|
// In this case no object from the list is marked to be deleted.
|
|
//
|
|
// NOTE: Marks any object to be deleted (despite any prohibitions
|
|
// on operations with that object) if WithForceRemoval option has
|
|
// been provided.
|
|
func (e *StorageEngine) Delete(ctx context.Context, prm DeletePrm) error {
|
|
ctx, span := tracing.StartSpanFromContext(ctx, "StorageEngine.Delete",
|
|
trace.WithAttributes(
|
|
attribute.String("address", prm.addr.EncodeToString()),
|
|
attribute.Bool("force_removal", prm.forceRemoval),
|
|
))
|
|
defer span.End()
|
|
defer elapsed("Delete", e.metrics.AddMethodDuration)()
|
|
|
|
return e.execIfNotBlocked(func() error {
|
|
return e.delete(ctx, prm)
|
|
})
|
|
}
|
|
|
|
func (e *StorageEngine) delete(ctx context.Context, prm DeletePrm) error {
|
|
var locked struct {
|
|
is bool
|
|
}
|
|
var splitInfo *objectSDK.SplitInfo
|
|
var ecInfo *objectSDK.ECInfo
|
|
|
|
// Removal of a big object is done in multiple stages:
|
|
// 1. Remove the parent object. If it is locked or already removed, return immediately.
|
|
// 2. Otherwise, search for all objects with a particular SplitID and delete them too.
|
|
if err := e.iterateOverSortedShards(ctx, prm.addr, func(_ int, sh hashedShard) (stop bool) {
|
|
var existsPrm shard.ExistsPrm
|
|
existsPrm.Address = prm.addr
|
|
|
|
resExists, err := sh.Exists(ctx, existsPrm)
|
|
if err != nil {
|
|
if client.IsErrObjectAlreadyRemoved(err) || shard.IsErrObjectExpired(err) {
|
|
return true
|
|
}
|
|
|
|
var splitErr *objectSDK.SplitInfoError
|
|
var ecErr *objectSDK.ECInfoError
|
|
if errors.As(err, &splitErr) {
|
|
splitInfo = splitErr.SplitInfo()
|
|
} else if errors.As(err, &ecErr) {
|
|
e.deleteChunks(ctx, sh, ecInfo, prm)
|
|
return false
|
|
} else {
|
|
if !client.IsErrObjectNotFound(err) {
|
|
e.reportShardError(ctx, sh, "could not check object existence", err, zap.Stringer("address", prm.addr))
|
|
}
|
|
return false
|
|
}
|
|
} else if !resExists.Exists() {
|
|
return false
|
|
}
|
|
|
|
var shPrm shard.InhumePrm
|
|
shPrm.MarkAsGarbage(prm.addr)
|
|
if prm.forceRemoval {
|
|
shPrm.ForceRemoval()
|
|
}
|
|
|
|
_, err = sh.Inhume(ctx, shPrm)
|
|
if err != nil {
|
|
e.reportShardError(ctx, sh, "could not inhume object in shard", err, zap.Stringer("address", prm.addr))
|
|
|
|
var target *apistatus.ObjectLocked
|
|
locked.is = errors.As(err, &target)
|
|
|
|
return locked.is
|
|
}
|
|
|
|
// If a parent object is removed we should set GC mark on each shard.
|
|
return splitInfo == nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if locked.is {
|
|
return new(apistatus.ObjectLocked)
|
|
}
|
|
|
|
if splitInfo != nil {
|
|
return e.deleteChildren(ctx, prm.addr, prm.forceRemoval, splitInfo.SplitID())
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (e *StorageEngine) deleteChildren(ctx context.Context, addr oid.Address, force bool, splitID *objectSDK.SplitID) error {
|
|
var fs objectSDK.SearchFilters
|
|
fs.AddSplitIDFilter(objectSDK.MatchStringEqual, splitID)
|
|
|
|
var selectPrm shard.SelectPrm
|
|
selectPrm.SetFilters(fs)
|
|
selectPrm.SetContainerID(addr.Container(), false) // doesn't matter for search by splitID
|
|
|
|
var inhumePrm shard.InhumePrm
|
|
if force {
|
|
inhumePrm.ForceRemoval()
|
|
}
|
|
|
|
return e.iterateOverSortedShards(ctx, addr, func(_ int, sh hashedShard) (stop bool) {
|
|
res, err := sh.Select(ctx, selectPrm)
|
|
if err != nil {
|
|
e.log.Warn(ctx, logs.EngineErrorDuringSearchingForObjectChildren,
|
|
zap.Stringer("addr", addr),
|
|
zap.Error(err))
|
|
return false
|
|
}
|
|
|
|
for _, addr := range res.AddressList() {
|
|
inhumePrm.MarkAsGarbage(addr)
|
|
|
|
_, err = sh.Inhume(ctx, inhumePrm)
|
|
if err != nil {
|
|
e.log.Debug(ctx, logs.EngineCouldNotInhumeObjectInShard,
|
|
zap.Stringer("addr", addr),
|
|
zap.Error(err))
|
|
continue
|
|
}
|
|
}
|
|
return false
|
|
})
|
|
}
|
|
|
|
func (e *StorageEngine) deleteChunks(
|
|
ctx context.Context, sh hashedShard, ecInfo *objectSDK.ECInfo, prm DeletePrm,
|
|
) {
|
|
var inhumePrm shard.InhumePrm
|
|
if prm.forceRemoval {
|
|
inhumePrm.ForceRemoval()
|
|
}
|
|
for _, chunk := range ecInfo.Chunks {
|
|
var addr oid.Address
|
|
addr.SetContainer(prm.addr.Container())
|
|
var objID oid.ID
|
|
err := objID.ReadFromV2(chunk.ID)
|
|
if err != nil {
|
|
e.reportShardError(ctx, sh, "could not delete EC chunk", err, zap.Stringer("address", prm.addr))
|
|
}
|
|
addr.SetObject(objID)
|
|
inhumePrm.MarkAsGarbage(addr)
|
|
_, err = sh.Inhume(ctx, inhumePrm)
|
|
if err != nil {
|
|
e.log.Debug(ctx, logs.EngineCouldNotInhumeObjectInShard,
|
|
zap.Stringer("addr", addr),
|
|
zap.Error(err))
|
|
continue
|
|
}
|
|
}
|
|
}
|