2020-12-09 12:24:37 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package metabase
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
|
2021-04-23 10:52:40 +01:00
|
|
|
"storj.io/private/dbutil"
|
2020-12-09 12:24:37 +00:00
|
|
|
)
|
|
|
|
|
2021-06-25 09:19:32 +01:00
|
|
|
const (
|
2021-08-05 20:44:04 +01:00
|
|
|
deleteBatchSizeLimit = intLimitRange(50)
|
2021-06-25 09:19:32 +01:00
|
|
|
)
|
2021-06-24 16:14:58 +01:00
|
|
|
|
2020-12-09 12:24:37 +00:00
|
|
|
// DeleteBucketObjects contains arguments for deleting a whole bucket.
|
|
|
|
type DeleteBucketObjects struct {
|
|
|
|
Bucket BucketLocation
|
|
|
|
BatchSize int
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBucketObjects deletes all objects in the specified bucket.
|
2021-08-05 20:44:04 +01:00
|
|
|
// Deletion performs in batches, so in case of error while processing,
|
|
|
|
// this method will return the number of objects deleted to the moment
|
|
|
|
// when an error occurs.
|
2020-12-09 12:24:37 +00:00
|
|
|
func (db *DB) DeleteBucketObjects(ctx context.Context, opts DeleteBucketObjects) (deletedObjectCount int64, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
if err := opts.Bucket.Verify(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2021-06-25 09:19:32 +01:00
|
|
|
deleteBatchSizeLimit.Ensure(&opts.BatchSize)
|
2021-06-24 16:14:58 +01:00
|
|
|
|
2023-08-09 10:51:33 +01:00
|
|
|
// TODO we may think about doing pending and committed objects in parallel
|
|
|
|
deletedBatchCount := int64(opts.BatchSize)
|
|
|
|
for deletedBatchCount > 0 {
|
2022-03-04 15:08:46 +00:00
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
return deletedObjectCount, err
|
|
|
|
}
|
|
|
|
|
2023-08-09 10:51:33 +01:00
|
|
|
deletedBatchCount, err = db.deleteBucketObjects(ctx, opts)
|
2022-08-02 03:07:02 +01:00
|
|
|
deletedObjectCount += deletedBatchCount
|
2022-03-04 15:08:46 +00:00
|
|
|
|
2023-08-09 10:51:33 +01:00
|
|
|
if err != nil {
|
2022-08-02 03:07:02 +01:00
|
|
|
return deletedObjectCount, err
|
|
|
|
}
|
|
|
|
}
|
2023-08-09 10:51:33 +01:00
|
|
|
|
|
|
|
deletedBatchCount = int64(opts.BatchSize)
|
|
|
|
for deletedBatchCount > 0 {
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
|
|
return deletedObjectCount, err
|
|
|
|
}
|
|
|
|
|
|
|
|
deletedBatchCount, err = db.deleteBucketPendingObjects(ctx, opts)
|
|
|
|
deletedObjectCount += deletedBatchCount
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return deletedObjectCount, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return deletedObjectCount, nil
|
2022-08-02 03:07:02 +01:00
|
|
|
}
|
2022-03-04 15:08:46 +00:00
|
|
|
|
2023-07-19 09:22:08 +01:00
|
|
|
func (db *DB) deleteBucketObjects(ctx context.Context, opts DeleteBucketObjects) (deletedObjectCount int64, err error) {
|
2022-03-04 15:08:46 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2021-02-19 15:48:33 +00:00
|
|
|
var query string
|
2022-03-04 15:08:46 +00:00
|
|
|
|
2021-05-11 09:49:26 +01:00
|
|
|
switch db.impl {
|
2021-02-19 15:48:33 +00:00
|
|
|
case dbutil.Cockroach:
|
|
|
|
query = `
|
|
|
|
WITH deleted_objects AS (
|
|
|
|
DELETE FROM objects
|
|
|
|
WHERE project_id = $1 AND bucket_name = $2 LIMIT $3
|
2023-07-19 09:22:08 +01:00
|
|
|
RETURNING objects.stream_id, objects.segment_count
|
|
|
|
), deleted_segments AS (
|
|
|
|
DELETE FROM segments
|
|
|
|
WHERE segments.stream_id IN (SELECT deleted_objects.stream_id FROM deleted_objects)
|
|
|
|
RETURNING segments.stream_id
|
2021-02-19 15:48:33 +00:00
|
|
|
)
|
2023-07-19 09:22:08 +01:00
|
|
|
SELECT COUNT(1), COALESCE(SUM(segment_count), 0) FROM deleted_objects
|
2021-02-19 15:48:33 +00:00
|
|
|
`
|
|
|
|
case dbutil.Postgres:
|
|
|
|
query = `
|
|
|
|
WITH deleted_objects AS (
|
|
|
|
DELETE FROM objects
|
|
|
|
WHERE stream_id IN (
|
|
|
|
SELECT stream_id FROM objects
|
|
|
|
WHERE project_id = $1 AND bucket_name = $2
|
|
|
|
LIMIT $3
|
|
|
|
)
|
2023-07-19 09:22:08 +01:00
|
|
|
RETURNING objects.stream_id, objects.segment_count
|
|
|
|
), deleted_segments AS (
|
|
|
|
DELETE FROM segments
|
|
|
|
WHERE segments.stream_id IN (SELECT deleted_objects.stream_id FROM deleted_objects)
|
|
|
|
RETURNING segments.stream_id
|
2021-02-19 15:48:33 +00:00
|
|
|
)
|
2023-07-19 09:22:08 +01:00
|
|
|
SELECT COUNT(1), COALESCE(SUM(segment_count), 0) FROM deleted_objects
|
2021-02-19 15:48:33 +00:00
|
|
|
`
|
|
|
|
default:
|
2021-06-04 14:21:09 +01:00
|
|
|
return 0, Error.New("unhandled database: %v", db.impl)
|
2021-02-19 15:48:33 +00:00
|
|
|
}
|
|
|
|
|
2023-07-19 09:22:08 +01:00
|
|
|
var deletedSegmentCount int64
|
|
|
|
err = db.db.QueryRowContext(ctx, query, opts.Bucket.ProjectID, []byte(opts.Bucket.BucketName), opts.BatchSize).Scan(&deletedObjectCount, &deletedSegmentCount)
|
|
|
|
if err != nil {
|
|
|
|
return 0, Error.Wrap(err)
|
|
|
|
}
|
2021-06-04 14:21:09 +01:00
|
|
|
|
2023-07-19 09:22:08 +01:00
|
|
|
mon.Meter("object_delete").Mark64(deletedObjectCount)
|
|
|
|
mon.Meter("segment_delete").Mark64(deletedSegmentCount)
|
2021-06-04 14:21:09 +01:00
|
|
|
|
2023-07-19 09:22:08 +01:00
|
|
|
return deletedObjectCount, nil
|
2020-12-09 12:24:37 +00:00
|
|
|
}
|
2023-08-09 10:51:33 +01:00
|
|
|
|
|
|
|
func (db *DB) deleteBucketPendingObjects(ctx context.Context, opts DeleteBucketObjects) (deletedObjectCount int64, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
|
|
|
var query string
|
|
|
|
|
|
|
|
// TODO handle number of deleted segments
|
|
|
|
switch db.impl {
|
|
|
|
case dbutil.Cockroach:
|
|
|
|
query = `
|
|
|
|
WITH deleted_objects AS (
|
|
|
|
DELETE FROM pending_objects
|
|
|
|
WHERE project_id = $1 AND bucket_name = $2 LIMIT $3
|
|
|
|
RETURNING pending_objects.stream_id
|
|
|
|
), deleted_segments AS (
|
|
|
|
DELETE FROM segments
|
|
|
|
WHERE segments.stream_id IN (SELECT deleted_objects.stream_id FROM deleted_objects)
|
|
|
|
RETURNING segments.stream_id
|
|
|
|
)
|
|
|
|
SELECT COUNT(1) FROM deleted_objects
|
|
|
|
`
|
|
|
|
case dbutil.Postgres:
|
|
|
|
query = `
|
|
|
|
WITH deleted_objects AS (
|
|
|
|
DELETE FROM pending_objects
|
|
|
|
WHERE stream_id IN (
|
|
|
|
SELECT stream_id FROM pending_objects
|
|
|
|
WHERE project_id = $1 AND bucket_name = $2
|
|
|
|
LIMIT $3
|
|
|
|
)
|
|
|
|
RETURNING pending_objects.stream_id
|
|
|
|
), deleted_segments AS (
|
|
|
|
DELETE FROM segments
|
|
|
|
WHERE segments.stream_id IN (SELECT deleted_objects.stream_id FROM deleted_objects)
|
|
|
|
RETURNING segments.stream_id
|
|
|
|
)
|
|
|
|
SELECT COUNT(1) FROM deleted_objects
|
|
|
|
`
|
|
|
|
default:
|
|
|
|
return 0, Error.New("unhandled database: %v", db.impl)
|
|
|
|
}
|
|
|
|
|
|
|
|
err = db.db.QueryRowContext(ctx, query, opts.Bucket.ProjectID, []byte(opts.Bucket.BucketName), opts.BatchSize).Scan(&deletedObjectCount)
|
|
|
|
if err != nil {
|
|
|
|
return 0, Error.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mon.Meter("object_delete").Mark64(deletedObjectCount)
|
|
|
|
|
|
|
|
return deletedObjectCount, nil
|
|
|
|
}
|