storj/satellite/buckets/db.go
Michal Niewrzal 95a5cfe647 satellite/buckets: handle bucket exists better
In some rare cases when two entities are trying to create the same
bucket at the same time it's possible that we will return internal
error instead of `bucket already exists`. It's because we are not
handling correctly DB error about constraint error. This change checks
if while inserting bucket into DB we got constraint error and propagate
correct error to metainfo API.

Change-Id: Ie6fd2c943b864b4ea7d71e4a162e74dc3510e386
2023-08-23 14:40:31 +00:00

118 lines
4.1 KiB
Go

// Copyright (C) 2018 Storj Labs, Inc.
// See LICENSE for copying information.
package buckets
import (
"context"
"time"
"github.com/zeebo/errs"
"storj.io/common/macaroon"
"storj.io/common/pb"
"storj.io/common/storj"
"storj.io/common/uuid"
"storj.io/storj/satellite/metabase"
)
var (
// ErrBucket is an error class for general bucket errors.
ErrBucket = errs.Class("bucket")
// ErrNoBucket is an error class for using empty bucket name.
ErrNoBucket = errs.Class("no bucket specified")
// ErrBucketNotFound is an error class for non-existing bucket.
ErrBucketNotFound = errs.Class("bucket not found")
// ErrBucketAlreadyExists is used to indicate that bucket already exists.
ErrBucketAlreadyExists = errs.Class("bucket already exists")
)
// Bucket contains information about a specific bucket.
type Bucket struct {
ID uuid.UUID
Name string
ProjectID uuid.UUID
UserAgent []byte
Created time.Time
PathCipher storj.CipherSuite
DefaultSegmentsSize int64
DefaultRedundancyScheme storj.RedundancyScheme
DefaultEncryptionParameters storj.EncryptionParameters
Placement storj.PlacementConstraint
}
// ListDirection specifies listing direction.
type ListDirection = pb.ListDirection
const (
// DirectionForward lists forwards from cursor, including cursor.
DirectionForward = pb.ListDirection_FORWARD
// DirectionAfter lists forwards from cursor, without cursor.
DirectionAfter = pb.ListDirection_AFTER
)
// MinimalBucket contains minimal bucket fields for metainfo protocol.
type MinimalBucket struct {
Name []byte
CreatedAt time.Time
}
// ListOptions lists objects.
type ListOptions struct {
Cursor string
Direction ListDirection
Limit int
}
// NextPage returns options for listing the next page.
func (opts ListOptions) NextPage(list List) ListOptions {
if !list.More || len(list.Items) == 0 {
return ListOptions{}
}
return ListOptions{
Cursor: list.Items[len(list.Items)-1].Name,
Direction: DirectionAfter,
Limit: opts.Limit,
}
}
// List is a list of buckets.
type List struct {
More bool
Items []Bucket
}
// DB is the interface for the database to interact with buckets.
//
// architecture: Database
type DB interface {
// CreateBucket creates a new bucket
CreateBucket(ctx context.Context, bucket Bucket) (_ Bucket, err error)
// GetBucket returns an existing bucket
GetBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (bucket Bucket, err error)
// GetBucketPlacement returns with the placement constraint identifier.
GetBucketPlacement(ctx context.Context, bucketName []byte, projectID uuid.UUID) (placement storj.PlacementConstraint, err error)
// GetMinimalBucket returns existing bucket with minimal number of fields.
GetMinimalBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (bucket MinimalBucket, err error)
// HasBucket returns if a bucket exists.
HasBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (exists bool, err error)
// GetBucketID returns an existing bucket id.
GetBucketID(ctx context.Context, bucket metabase.BucketLocation) (id uuid.UUID, err error)
// UpdateBucket updates an existing bucket
UpdateBucket(ctx context.Context, bucket Bucket) (_ Bucket, err error)
// UpdateUserAgent updates buckets user agent.
UpdateUserAgent(ctx context.Context, projectID uuid.UUID, bucketName string, userAgent []byte) error
// DeleteBucket deletes a bucket
DeleteBucket(ctx context.Context, bucketName []byte, projectID uuid.UUID) (err error)
// ListBuckets returns all buckets for a project
ListBuckets(ctx context.Context, projectID uuid.UUID, listOpts ListOptions, allowedBuckets macaroon.AllowedBuckets) (bucketList List, err error)
// CountBuckets returns the number of buckets a project currently has
CountBuckets(ctx context.Context, projectID uuid.UUID) (int, error)
// IterateBucketLocations iterates through all buckets from some point with limit.
IterateBucketLocations(ctx context.Context, projectID uuid.UUID, bucketName string, limit int, fn func([]metabase.BucketLocation) error) (more bool, err error)
}