2020-12-09 12:24:37 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package metabase_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2021-07-28 16:16:01 +01:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
2020-12-09 12:24:37 +00:00
|
|
|
"storj.io/common/storj"
|
|
|
|
"storj.io/common/testcontext"
|
|
|
|
"storj.io/common/uuid"
|
2021-04-21 13:42:57 +01:00
|
|
|
"storj.io/storj/satellite/metabase"
|
2021-04-26 10:35:44 +01:00
|
|
|
"storj.io/storj/satellite/metabase/metabasetest"
|
2020-12-09 12:24:37 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestDeleteBucketObjects(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
|
|
|
obj1 := metabasetest.RandObjectStream()
|
|
|
|
obj2 := metabasetest.RandObjectStream()
|
|
|
|
obj3 := metabasetest.RandObjectStream()
|
|
|
|
objX := metabasetest.RandObjectStream()
|
|
|
|
objY := metabasetest.RandObjectStream()
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
obj2.ProjectID, obj2.BucketName = obj1.ProjectID, obj1.BucketName
|
|
|
|
obj3.ProjectID, obj3.BucketName = obj1.ProjectID, obj1.BucketName
|
|
|
|
objX.ProjectID = obj1.ProjectID
|
|
|
|
objY.BucketName = obj1.BucketName
|
|
|
|
|
|
|
|
t.Run("invalid options", func(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: metabase.BucketLocation{
|
|
|
|
ProjectID: uuid.UUID{},
|
|
|
|
BucketName: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
|
|
ErrText: "ProjectID missing",
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: metabase.BucketLocation{
|
|
|
|
ProjectID: uuid.UUID{1},
|
|
|
|
BucketName: "",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ErrClass: &metabase.ErrInvalidRequest,
|
|
|
|
ErrText: "BucketName missing",
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("empty bucket", func(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
return errors.New("shouldn't be called")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 0,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("one object", func(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.CreateObject(ctx, t, db, obj1, 2)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
if len(segments) != 2 {
|
|
|
|
return errors.New("expected 2 segments")
|
|
|
|
}
|
|
|
|
for _, s := range segments {
|
|
|
|
if len(s.Pieces) != 1 {
|
|
|
|
return errors.New("expected 1 piece per segment")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 1,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("empty object", func(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.CreateObject(ctx, t, db, obj1, 0)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
return errors.New("expected no segments")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// TODO: fix the count for objects without segments
|
|
|
|
// this should be 1.
|
|
|
|
Deleted: 0,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("three objects", func(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.CreateObject(ctx, t, db, obj1, 2)
|
|
|
|
metabasetest.CreateObject(ctx, t, db, obj2, 2)
|
|
|
|
metabasetest.CreateObject(ctx, t, db, obj3, 2)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
BatchSize: 2,
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
if len(segments) != 2 && len(segments) != 4 {
|
|
|
|
return errors.New("expected 2 or 4 segments")
|
|
|
|
}
|
|
|
|
for _, s := range segments {
|
|
|
|
if len(s.Pieces) != 1 {
|
|
|
|
return errors.New("expected 1 piece per segment")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 3,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("don't delete non-exact match", func(t *testing.T) {
|
2021-04-26 10:35:44 +01:00
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.CreateObject(ctx, t, db, obj1, 1)
|
|
|
|
metabasetest.CreateObject(ctx, t, db, objX, 1)
|
|
|
|
metabasetest.CreateObject(ctx, t, db, objY, 1)
|
2020-12-09 12:24:37 +00:00
|
|
|
now := time.Now()
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.DeleteBucketObjects{
|
2020-12-09 12:24:37 +00:00
|
|
|
Opts: metabase.DeleteBucketObjects{
|
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
},
|
|
|
|
Deleted: 1,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
metabasetest.Verify{
|
2020-12-09 12:24:37 +00:00
|
|
|
Objects: []metabase.RawObject{
|
|
|
|
{
|
|
|
|
ObjectStream: objX,
|
|
|
|
CreatedAt: now,
|
|
|
|
Status: metabase.Committed,
|
|
|
|
SegmentCount: 1,
|
|
|
|
|
|
|
|
TotalPlainSize: 512,
|
|
|
|
TotalEncryptedSize: 1024,
|
2021-01-14 11:47:29 +00:00
|
|
|
FixedSegmentSize: 512,
|
2021-04-26 10:35:44 +01:00
|
|
|
Encryption: metabasetest.DefaultEncryption,
|
2020-12-09 12:24:37 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
ObjectStream: objY,
|
|
|
|
CreatedAt: now,
|
|
|
|
Status: metabase.Committed,
|
|
|
|
SegmentCount: 1,
|
|
|
|
|
|
|
|
TotalPlainSize: 512,
|
|
|
|
TotalEncryptedSize: 1024,
|
2021-01-14 11:47:29 +00:00
|
|
|
FixedSegmentSize: 512,
|
2021-04-26 10:35:44 +01:00
|
|
|
Encryption: metabasetest.DefaultEncryption,
|
2020-12-09 12:24:37 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Segments: []metabase.RawSegment{
|
|
|
|
{
|
2021-03-12 17:43:30 +00:00
|
|
|
StreamID: objX.StreamID,
|
|
|
|
Position: metabase.SegmentPosition{Part: 0, Index: 0},
|
2021-08-05 00:56:50 +01:00
|
|
|
CreatedAt: now,
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
RootPieceID: storj.PieceID{1},
|
|
|
|
Pieces: metabase.Pieces{{Number: 0, StorageNode: storj.NodeID{2}}},
|
|
|
|
EncryptedKey: []byte{3},
|
|
|
|
EncryptedKeyNonce: []byte{4},
|
2021-03-25 07:53:10 +00:00
|
|
|
EncryptedETag: []byte{5},
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
EncryptedSize: 1024,
|
|
|
|
PlainSize: 512,
|
|
|
|
PlainOffset: 0,
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
Redundancy: metabasetest.DefaultRedundancy,
|
2020-12-09 12:24:37 +00:00
|
|
|
},
|
|
|
|
{
|
2021-03-12 17:43:30 +00:00
|
|
|
StreamID: objY.StreamID,
|
|
|
|
Position: metabase.SegmentPosition{Part: 0, Index: 0},
|
2021-08-05 00:56:50 +01:00
|
|
|
CreatedAt: now,
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
RootPieceID: storj.PieceID{1},
|
|
|
|
Pieces: metabase.Pieces{{Number: 0, StorageNode: storj.NodeID{2}}},
|
|
|
|
EncryptedKey: []byte{3},
|
|
|
|
EncryptedKeyNonce: []byte{4},
|
2021-03-25 07:53:10 +00:00
|
|
|
EncryptedETag: []byte{5},
|
2020-12-09 12:24:37 +00:00
|
|
|
|
|
|
|
EncryptedSize: 1024,
|
|
|
|
PlainSize: 512,
|
|
|
|
PlainOffset: 0,
|
|
|
|
|
2021-04-26 10:35:44 +01:00
|
|
|
Redundancy: metabasetest.DefaultRedundancy,
|
2020-12-09 12:24:37 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
})
|
2021-06-24 16:14:58 +01:00
|
|
|
|
|
|
|
t.Run("object with multiple segments", func(t *testing.T) {
|
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
2021-08-05 20:44:04 +01:00
|
|
|
metabasetest.CreateObject(ctx, t, db, obj1, 37)
|
2021-06-24 16:14:58 +01:00
|
|
|
|
|
|
|
metabasetest.DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
2021-08-05 20:44:04 +01:00
|
|
|
Bucket: obj1.Location().Bucket(),
|
|
|
|
BatchSize: 2,
|
2021-06-24 16:14:58 +01:00
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
2021-08-05 20:44:04 +01:00
|
|
|
if len(segments) != 37 {
|
|
|
|
return errors.New("expected 37 segments")
|
2021-06-24 16:14:58 +01:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 1,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
2021-07-28 16:16:01 +01:00
|
|
|
|
|
|
|
t.Run("multiple objects", func(t *testing.T) {
|
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
root := metabasetest.RandObjectStream()
|
|
|
|
for i := 0; i < 5; i++ {
|
|
|
|
obj := metabasetest.RandObjectStream()
|
|
|
|
obj.ProjectID = root.ProjectID
|
|
|
|
obj.BucketName = root.BucketName
|
|
|
|
metabasetest.CreateObject(ctx, t, db, obj, 5)
|
|
|
|
}
|
|
|
|
|
|
|
|
segmentsDeleted := 0
|
|
|
|
metabasetest.DeleteBucketObjects{
|
|
|
|
Opts: metabase.DeleteBucketObjects{
|
2021-08-05 20:44:04 +01:00
|
|
|
Bucket: root.Location().Bucket(),
|
|
|
|
BatchSize: 1,
|
2021-07-28 16:16:01 +01:00
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
segmentsDeleted += len(segments)
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Deleted: 5,
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
require.Equal(t, 25, segmentsDeleted)
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDeleteBucketObjectsParallel(t *testing.T) {
|
|
|
|
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
root := metabasetest.RandObjectStream()
|
|
|
|
for i := 0; i < 5; i++ {
|
|
|
|
obj := metabasetest.RandObjectStream()
|
|
|
|
obj.ProjectID = root.ProjectID
|
|
|
|
obj.BucketName = root.BucketName
|
|
|
|
metabasetest.CreateObject(ctx, t, db, obj, 50)
|
|
|
|
}
|
|
|
|
|
|
|
|
objects, err := db.TestingAllObjects(ctx)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, 5, len(objects))
|
|
|
|
|
|
|
|
for i := 0; i < 3; i++ {
|
|
|
|
ctx.Go(func() error {
|
|
|
|
_, err := db.DeleteBucketObjects(ctx, metabase.DeleteBucketObjects{
|
2021-08-05 20:44:04 +01:00
|
|
|
Bucket: root.Location().Bucket(),
|
|
|
|
BatchSize: 2,
|
2021-07-28 16:16:01 +01:00
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
return err
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.Wait()
|
|
|
|
|
|
|
|
metabasetest.Verify{}.Check(ctx, t, db)
|
2020-12-09 12:24:37 +00:00
|
|
|
})
|
|
|
|
}
|
2021-08-05 20:44:04 +01:00
|
|
|
|
|
|
|
func TestDeleteBucketObjectsCancel(t *testing.T) {
|
|
|
|
metabasetest.Run(t, func(ctx *testcontext.Context, t *testing.T, db *metabase.DB) {
|
|
|
|
defer metabasetest.DeleteAll{}.Check(ctx, t, db)
|
|
|
|
|
|
|
|
object := metabasetest.CreateObject(ctx, t, db, metabasetest.RandObjectStream(), 1)
|
|
|
|
|
|
|
|
testCtx, cancel := context.WithCancel(ctx)
|
|
|
|
cancel()
|
|
|
|
_, err := db.DeleteBucketObjects(testCtx, metabase.DeleteBucketObjects{
|
|
|
|
Bucket: object.Location().Bucket(),
|
|
|
|
BatchSize: 2,
|
|
|
|
DeletePieces: func(ctx context.Context, segments []metabase.DeletedSegmentInfo) error {
|
|
|
|
return nil
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.Error(t, err)
|
|
|
|
|
|
|
|
metabasetest.Verify{
|
|
|
|
Objects: []metabase.RawObject{metabase.RawObject(object)},
|
|
|
|
Segments: []metabase.RawSegment{
|
|
|
|
metabasetest.DefaultRawSegment(object.ObjectStream, metabase.SegmentPosition{}),
|
|
|
|
},
|
|
|
|
}.Check(ctx, t, db)
|
|
|
|
})
|
|
|
|
}
|