satellite/metainfo/metabase: use txutil.WithTx in delete
Change-Id: I75965c4dcf57479ace2367f5d3069d785628e86a
This commit is contained in:
parent
bc460cd62d
commit
8182d8a726
@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"storj.io/common/storj"
|
"storj.io/common/storj"
|
||||||
"storj.io/storj/private/dbutil/pgutil"
|
"storj.io/storj/private/dbutil/pgutil"
|
||||||
|
"storj.io/storj/private/dbutil/txutil"
|
||||||
"storj.io/storj/private/tagsql"
|
"storj.io/storj/private/tagsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -101,63 +102,52 @@ func (db *DB) DeleteObjectExactVersion(ctx context.Context, opts DeleteObjectExa
|
|||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := db.db.BeginTx(ctx, nil)
|
err = txutil.WithTx(ctx, db.db, nil, func(ctx context.Context, tx tagsql.Tx) error {
|
||||||
if err != nil {
|
rows, err := tx.Query(ctx, `
|
||||||
return DeleteObjectResult{}, Error.New("failed BeginTx: %w", err)
|
DELETE FROM objects
|
||||||
}
|
WHERE
|
||||||
committed := false
|
project_id = $1 AND
|
||||||
defer func() {
|
bucket_name = $2 AND
|
||||||
if !committed {
|
object_key = $3 AND
|
||||||
err = errs.Combine(err, Error.Wrap(tx.Rollback()))
|
version = $4 AND
|
||||||
|
status = 1
|
||||||
|
RETURNING
|
||||||
|
version, stream_id,
|
||||||
|
created_at, expires_at,
|
||||||
|
status, segment_count,
|
||||||
|
encrypted_metadata_nonce, encrypted_metadata,
|
||||||
|
total_encrypted_size, fixed_segment_size,
|
||||||
|
encryption;
|
||||||
|
`, opts.ProjectID, opts.BucketName, []byte(opts.ObjectKey), opts.Version)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
||||||
|
}
|
||||||
|
return Error.New("unable to delete object: %w", err)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
rows, err := tx.Query(ctx, `
|
result.Objects, err = scanObjectDeletion(opts.ObjectLocation, rows)
|
||||||
DELETE FROM objects
|
if err != nil {
|
||||||
WHERE
|
return err
|
||||||
project_id = $1 AND
|
|
||||||
bucket_name = $2 AND
|
|
||||||
object_key = $3 AND
|
|
||||||
version = $4 AND
|
|
||||||
status = 1
|
|
||||||
RETURNING
|
|
||||||
version, stream_id,
|
|
||||||
created_at, expires_at,
|
|
||||||
status, segment_count,
|
|
||||||
encrypted_metadata_nonce, encrypted_metadata,
|
|
||||||
total_encrypted_size, fixed_segment_size,
|
|
||||||
encryption;
|
|
||||||
`, opts.ProjectID, opts.BucketName, []byte(opts.ObjectKey), opts.Version)
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
|
||||||
}
|
}
|
||||||
return DeleteObjectResult{}, Error.New("unable to delete object: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Objects, err = scanObjectDeletion(opts.ObjectLocation, rows)
|
if len(result.Objects) == 0 {
|
||||||
|
return storj.ErrObjectNotFound.Wrap(Error.New("no rows deleted"))
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(segmentInfos) != 0 {
|
||||||
|
result.Segments = segmentInfos
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result.Objects) == 0 {
|
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.New("no rows deleted"))
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(segmentInfos) != 0 {
|
|
||||||
result.Segments = segmentInfos
|
|
||||||
}
|
|
||||||
|
|
||||||
err, committed = tx.Commit(), true
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, Error.New("unable to commit tx: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,85 +159,75 @@ func (db *DB) DeleteObjectLatestVersion(ctx context.Context, opts DeleteObjectLa
|
|||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := db.db.BeginTx(ctx, nil)
|
err = txutil.WithTx(ctx, db.db, nil, func(ctx context.Context, tx tagsql.Tx) error {
|
||||||
if err != nil {
|
// TODO different sql for Postgres and CockroachDB
|
||||||
return DeleteObjectResult{}, Error.New("failed BeginTx: %w", err)
|
// version ONLY for cockroachdb
|
||||||
}
|
// Postgres doesn't support ORDER BY and LIMIT in DELETE
|
||||||
committed := false
|
// rows, err = tx.Query(ctx, `
|
||||||
defer func() {
|
// DELETE FROM objects
|
||||||
if !committed {
|
// WHERE
|
||||||
err = errs.Combine(err, Error.Wrap(tx.Rollback()))
|
// project_id = $1 AND
|
||||||
}
|
// bucket_name = $2 AND
|
||||||
}()
|
// object_key = $3 AND
|
||||||
|
// status = 1
|
||||||
|
// ORDER BY version DESC
|
||||||
|
// LIMIT 1
|
||||||
|
// RETURNING stream_id;
|
||||||
|
// `, opts.ProjectID, opts.BucketName, opts.ObjectKey)
|
||||||
|
|
||||||
// TODO different sql for Postgres and CockroachDB
|
// version for Postgres and Cockroachdb (but slow for Cockroachdb)
|
||||||
// version ONLY for cockroachdb
|
rows, err := tx.Query(ctx, `
|
||||||
// Postgres doesn't support ORDER BY and LIMIT in DELETE
|
DELETE FROM objects
|
||||||
// rows, err = tx.Query(ctx, `
|
WHERE
|
||||||
// DELETE FROM objects
|
|
||||||
// WHERE
|
|
||||||
// project_id = $1 AND
|
|
||||||
// bucket_name = $2 AND
|
|
||||||
// object_key = $3 AND
|
|
||||||
// status = 1
|
|
||||||
// ORDER BY version DESC
|
|
||||||
// LIMIT 1
|
|
||||||
// RETURNING stream_id;
|
|
||||||
// `, opts.ProjectID, opts.BucketName, opts.ObjectKey)
|
|
||||||
|
|
||||||
// version for Postgres and Cockroachdb (but slow for Cockroachdb)
|
|
||||||
rows, err := tx.Query(ctx, `
|
|
||||||
DELETE FROM objects
|
|
||||||
WHERE
|
|
||||||
project_id = $1 AND
|
|
||||||
bucket_name = $2 AND
|
|
||||||
object_key = $3 AND
|
|
||||||
version = (SELECT version FROM objects WHERE
|
|
||||||
project_id = $1 AND
|
project_id = $1 AND
|
||||||
bucket_name = $2 AND
|
bucket_name = $2 AND
|
||||||
object_key = $3 AND
|
object_key = $3 AND
|
||||||
|
version = (SELECT version FROM objects WHERE
|
||||||
|
project_id = $1 AND
|
||||||
|
bucket_name = $2 AND
|
||||||
|
object_key = $3 AND
|
||||||
|
status = 1
|
||||||
|
ORDER BY version DESC LIMIT 1
|
||||||
|
) AND
|
||||||
status = 1
|
status = 1
|
||||||
ORDER BY version DESC LIMIT 1
|
RETURNING
|
||||||
) AND
|
version, stream_id,
|
||||||
status = 1
|
created_at, expires_at,
|
||||||
RETURNING
|
status, segment_count,
|
||||||
version, stream_id,
|
encrypted_metadata_nonce, encrypted_metadata,
|
||||||
created_at, expires_at,
|
total_encrypted_size, fixed_segment_size,
|
||||||
status, segment_count,
|
encryption;
|
||||||
encrypted_metadata_nonce, encrypted_metadata,
|
`, opts.ProjectID, opts.BucketName, []byte(opts.ObjectKey))
|
||||||
total_encrypted_size, fixed_segment_size,
|
if err != nil {
|
||||||
encryption;
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
`, opts.ProjectID, opts.BucketName, []byte(opts.ObjectKey))
|
return storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
||||||
if err != nil {
|
}
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
return Error.New("unable to delete object: %w", err)
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
|
||||||
}
|
}
|
||||||
return DeleteObjectResult{}, Error.New("unable to delete object: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Objects, err = scanObjectDeletion(opts.ObjectLocation, rows)
|
result.Objects, err = scanObjectDeletion(opts.ObjectLocation, rows)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result.Objects) == 0 {
|
||||||
|
return storj.ErrObjectNotFound.Wrap(Error.New("no rows deleted"))
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(segmentInfos) != 0 {
|
||||||
|
result.Segments = segmentInfos
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result.Objects) == 0 {
|
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.New("no rows deleted"))
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(segmentInfos) != 0 {
|
|
||||||
result.Segments = segmentInfos
|
|
||||||
}
|
|
||||||
|
|
||||||
err, committed = tx.Commit(), true
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, Error.New("unable to commit tx: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,63 +238,52 @@ func (db *DB) DeleteObjectAllVersions(ctx context.Context, opts DeleteObjectAllV
|
|||||||
if err := opts.Verify(); err != nil {
|
if err := opts.Verify(); err != nil {
|
||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
err = txutil.WithTx(ctx, db.db, nil, func(ctx context.Context, tx tagsql.Tx) error {
|
||||||
tx, err := db.db.BeginTx(ctx, nil)
|
rows, err := tx.Query(ctx, `
|
||||||
if err != nil {
|
DELETE FROM objects
|
||||||
return DeleteObjectResult{}, Error.New("failed BeginTx: %w", err)
|
WHERE
|
||||||
}
|
project_id = $1 AND
|
||||||
committed := false
|
bucket_name = $2 AND
|
||||||
defer func() {
|
object_key = $3 AND
|
||||||
if !committed {
|
status = 1
|
||||||
err = errs.Combine(err, Error.Wrap(tx.Rollback()))
|
RETURNING
|
||||||
|
version, stream_id,
|
||||||
|
created_at, expires_at,
|
||||||
|
status, segment_count,
|
||||||
|
encrypted_metadata_nonce, encrypted_metadata,
|
||||||
|
total_encrypted_size, fixed_segment_size,
|
||||||
|
encryption;
|
||||||
|
`, opts.ProjectID, opts.BucketName, []byte(opts.ObjectKey))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
||||||
|
}
|
||||||
|
return Error.New("unable to delete object: %w", err)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
rows, err := tx.Query(ctx, `
|
result.Objects, err = scanObjectDeletion(opts.ObjectLocation, rows)
|
||||||
DELETE FROM objects
|
if err != nil {
|
||||||
WHERE
|
return err
|
||||||
project_id = $1 AND
|
|
||||||
bucket_name = $2 AND
|
|
||||||
object_key = $3 AND
|
|
||||||
status = 1
|
|
||||||
RETURNING
|
|
||||||
version, stream_id,
|
|
||||||
created_at, expires_at,
|
|
||||||
status, segment_count,
|
|
||||||
encrypted_metadata_nonce, encrypted_metadata,
|
|
||||||
total_encrypted_size, fixed_segment_size,
|
|
||||||
encryption;
|
|
||||||
`, opts.ProjectID, opts.BucketName, []byte(opts.ObjectKey))
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
|
||||||
}
|
}
|
||||||
return DeleteObjectResult{}, Error.New("unable to delete object: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Objects, err = scanObjectDeletion(opts.ObjectLocation, rows)
|
if len(result.Objects) == 0 {
|
||||||
|
return storj.ErrObjectNotFound.Wrap(Error.New("no rows deleted"))
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(segmentInfos) != 0 {
|
||||||
|
result.Segments = segmentInfos
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result.Objects) == 0 {
|
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.New("no rows deleted"))
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(segmentInfos) != 0 {
|
|
||||||
result.Segments = segmentInfos
|
|
||||||
}
|
|
||||||
|
|
||||||
err, committed = tx.Commit(), true
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, Error.New("unable to commit tx: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,79 +300,68 @@ func (db *DB) DeleteObjectsAllVersions(ctx context.Context, opts DeleteObjectsAl
|
|||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := db.db.BeginTx(ctx, nil)
|
err = txutil.WithTx(ctx, db.db, nil, func(ctx context.Context, tx tagsql.Tx) error {
|
||||||
if err != nil {
|
// It is aleady verified that all object locations are in the same bucket
|
||||||
return DeleteObjectResult{}, Error.New("failed BeginTx: %w", err)
|
projectID := opts.Locations[0].ProjectID
|
||||||
}
|
bucketName := opts.Locations[0].BucketName
|
||||||
committed := false
|
|
||||||
defer func() {
|
objectKeys := make([][]byte, len(opts.Locations))
|
||||||
if !committed {
|
for i := range opts.Locations {
|
||||||
err = errs.Combine(err, Error.Wrap(tx.Rollback()))
|
objectKeys[i] = []byte(opts.Locations[i].ObjectKey)
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
// It is aleady verified that all object locations are in the same bucket
|
// Sorting the object keys just in case.
|
||||||
projectID := opts.Locations[0].ProjectID
|
// TODO: Check if this is really necessary for the SQL query.
|
||||||
bucketName := opts.Locations[0].BucketName
|
sort.Slice(objectKeys, func(i, j int) bool {
|
||||||
|
return bytes.Compare(objectKeys[i], objectKeys[j]) < 0
|
||||||
|
})
|
||||||
|
|
||||||
objectKeys := make([][]byte, len(opts.Locations))
|
rows, err := tx.Query(ctx, `
|
||||||
for i := range opts.Locations {
|
DELETE FROM objects
|
||||||
objectKeys[i] = []byte(opts.Locations[i].ObjectKey)
|
WHERE
|
||||||
}
|
project_id = $1 AND
|
||||||
|
bucket_name = $2 AND
|
||||||
|
object_key = ANY ($3) AND
|
||||||
|
status = 1
|
||||||
|
RETURNING
|
||||||
|
project_id, bucket_name,
|
||||||
|
object_key, version, stream_id,
|
||||||
|
created_at, expires_at,
|
||||||
|
status, segment_count,
|
||||||
|
encrypted_metadata_nonce, encrypted_metadata,
|
||||||
|
total_encrypted_size, fixed_segment_size,
|
||||||
|
encryption;
|
||||||
|
`, projectID, bucketName, pgutil.ByteaArray(objectKeys))
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
||||||
|
}
|
||||||
|
return Error.New("unable to delete object: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Sorting the object keys just in case.
|
result.Objects, err = scanMultipleObjectsDeletion(rows)
|
||||||
// TODO: Check if this is really necessary for the SQL query.
|
if err != nil {
|
||||||
sort.Slice(objectKeys, func(i, j int) bool {
|
return err
|
||||||
return bytes.Compare(objectKeys[i], objectKeys[j]) < 0
|
}
|
||||||
|
|
||||||
|
if len(result.Objects) == 0 {
|
||||||
|
// nothing was delete, no error
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(segmentInfos) != 0 {
|
||||||
|
result.Segments = segmentInfos
|
||||||
|
}
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
rows, err := tx.Query(ctx, `
|
|
||||||
DELETE FROM objects
|
|
||||||
WHERE
|
|
||||||
project_id = $1 AND
|
|
||||||
bucket_name = $2 AND
|
|
||||||
object_key = ANY ($3) AND
|
|
||||||
status = 1
|
|
||||||
RETURNING
|
|
||||||
project_id, bucket_name,
|
|
||||||
object_key, version, stream_id,
|
|
||||||
created_at, expires_at,
|
|
||||||
status, segment_count,
|
|
||||||
encrypted_metadata_nonce, encrypted_metadata,
|
|
||||||
total_encrypted_size, fixed_segment_size,
|
|
||||||
encryption;
|
|
||||||
`, projectID, bucketName, pgutil.ByteaArray(objectKeys))
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
|
||||||
return DeleteObjectResult{}, storj.ErrObjectNotFound.Wrap(Error.Wrap(err))
|
|
||||||
}
|
|
||||||
return DeleteObjectResult{}, Error.New("unable to delete object: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Objects, err = scanMultipleObjectsDeletion(rows)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return DeleteObjectResult{}, err
|
return DeleteObjectResult{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result.Objects) == 0 {
|
|
||||||
// nothing was delete, no error
|
|
||||||
return DeleteObjectResult{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
segmentInfos, err := deleteSegments(ctx, tx, result.Objects)
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(segmentInfos) != 0 {
|
|
||||||
result.Segments = segmentInfos
|
|
||||||
}
|
|
||||||
|
|
||||||
err, committed = tx.Commit(), true
|
|
||||||
if err != nil {
|
|
||||||
return DeleteObjectResult{}, Error.New("unable to commit tx: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,6 +418,10 @@ func scanMultipleObjectsDeletion(rows tagsql.Rows) (objects []Object, err error)
|
|||||||
return nil, Error.New("unable to delete object: %w", err)
|
return nil, Error.New("unable to delete object: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(objects) == 0 {
|
||||||
|
objects = nil
|
||||||
|
}
|
||||||
|
|
||||||
return objects, nil
|
return objects, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user