storj/satellite/metainfo/metabase/update.go
Michał Niewrzał 27ae0d1f15 satellite/metainfo/metabase: add NewRedundancy parameter for UpdateSegmentPieces method
At some point we might try to change original segment RS values and set Pieces according to the new values. This change adds add NewRedundancy parameter for UpdateSegmentPieces method to give ability to do that. As a part of change NewPieces are validated against NewRedundancy.

Change-Id: I8ea531c9060b5cd283d3bf4f6e4c320099dd5576
2021-03-22 08:12:56 +00:00

102 lines
2.6 KiB
Go

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package metabase
import (
"context"
"database/sql"
"errors"
"github.com/zeebo/errs"
"storj.io/common/storj"
"storj.io/common/uuid"
"storj.io/storj/storage"
)
// UpdateSegmentPieces contains arguments necessary for updating segment pieces.
type UpdateSegmentPieces struct {
StreamID uuid.UUID
Position SegmentPosition
OldPieces Pieces
NewRedundancy storj.RedundancyScheme
NewPieces Pieces
}
// UpdateSegmentPieces updates pieces for specified segment. If provided old pieces
// won't match current database state update will fail.
func (db *DB) UpdateSegmentPieces(ctx context.Context, opts UpdateSegmentPieces) (err error) {
defer mon.Task()(&ctx)(&err)
if opts.StreamID.IsZero() {
return ErrInvalidRequest.New("StreamID missing")
}
if err := opts.OldPieces.Verify(); err != nil {
if ErrInvalidRequest.Has(err) {
return ErrInvalidRequest.New("OldPieces: %v", errs.Unwrap(err))
}
return err
}
if opts.NewRedundancy.IsZero() {
return ErrInvalidRequest.New("NewRedundancy zero")
}
// its possible that in this method we will have less pieces
// than optimal shares (e.g. after repair)
if len(opts.NewPieces) < int(opts.NewRedundancy.RepairShares) {
return ErrInvalidRequest.New("number of new pieces is less than new redundancy repair shares value")
}
if err := opts.NewPieces.Verify(); err != nil {
if ErrInvalidRequest.Has(err) {
return ErrInvalidRequest.New("NewPieces: %v", errs.Unwrap(err))
}
return err
}
oldPieces, err := db.aliasCache.ConvertPiecesToAliases(ctx, opts.OldPieces)
if err != nil {
return Error.New("unable to convert pieces to aliases: %w", err)
}
newPieces, err := db.aliasCache.ConvertPiecesToAliases(ctx, opts.NewPieces)
if err != nil {
return Error.New("unable to convert pieces to aliases: %w", err)
}
var resultPieces AliasPieces
err = db.db.QueryRow(ctx, `
UPDATE segments SET
remote_alias_pieces = CASE
WHEN remote_alias_pieces = $3 THEN $4
ELSE remote_alias_pieces
END,
redundancy = CASE
WHEN remote_alias_pieces = $3 THEN $5
ELSE redundancy
END
WHERE
stream_id = $1 AND
position = $2
RETURNING remote_alias_pieces
`, opts.StreamID, opts.Position, oldPieces, newPieces, redundancyScheme{&opts.NewRedundancy}).
Scan(&resultPieces)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return ErrSegmentNotFound.New("segment missing")
}
return Error.New("unable to update segment pieces: %w", err)
}
if !EqualAliasPieces(newPieces, resultPieces) {
return storage.ErrValueChanged.New("segment remote_alias_pieces field was changed")
}
return nil
}