github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/gateway/multipart/tracker.go (about)

     1  package multipart
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	"github.com/treeverse/lakefs/pkg/kv"
    10  	"google.golang.org/protobuf/types/known/timestamppb"
    11  )
    12  
    13  const storePartitionKey = "multiparts"
    14  
    15  type Metadata map[string]string
    16  
    17  type Upload struct {
    18  	// UploadID A unique identifier for the uploaded part
    19  	UploadID string `db:"upload_id"`
    20  	// Path Multipart path in repository
    21  	Path string `db:"path"`
    22  	// CreationDate Creation date of the part
    23  	CreationDate time.Time `db:"creation_date"`
    24  	// PhysicalAddress Physical address of the part in the storage
    25  	PhysicalAddress string `db:"physical_address"`
    26  	// Metadata Additional metadata as required (by storage vendor etc.)
    27  	Metadata Metadata `db:"metadata"`
    28  	// ContentType Original file's content-type
    29  	ContentType string `db:"content_type"`
    30  }
    31  
    32  type Tracker interface {
    33  	Create(ctx context.Context, multipart Upload) error
    34  	Get(ctx context.Context, uploadID string) (*Upload, error)
    35  	Delete(ctx context.Context, uploadID string) error
    36  }
    37  
    38  type tracker struct {
    39  	store kv.Store
    40  }
    41  
    42  var (
    43  	ErrMultipartUploadNotFound = errors.New("multipart upload not found")
    44  	ErrInvalidUploadID         = errors.New("invalid upload id")
    45  )
    46  
    47  func NewTracker(store kv.Store) Tracker {
    48  	return &tracker{
    49  		store: store,
    50  	}
    51  }
    52  
    53  func multipartFromProto(pb *UploadData) *Upload {
    54  	return &Upload{
    55  		UploadID:        pb.UploadId,
    56  		Path:            pb.Path,
    57  		CreationDate:    pb.CreationDate.AsTime(),
    58  		PhysicalAddress: pb.PhysicalAddress,
    59  		Metadata:        pb.Metadata,
    60  		ContentType:     pb.ContentType,
    61  	}
    62  }
    63  
    64  func protoFromMultipart(m *Upload) *UploadData {
    65  	return &UploadData{
    66  		UploadId:        m.UploadID,
    67  		Path:            m.Path,
    68  		CreationDate:    timestamppb.New(m.CreationDate),
    69  		PhysicalAddress: m.PhysicalAddress,
    70  		Metadata:        m.Metadata,
    71  		ContentType:     m.ContentType,
    72  	}
    73  }
    74  
    75  func (m *tracker) Create(ctx context.Context, multipart Upload) error {
    76  	if multipart.UploadID == "" {
    77  		return ErrInvalidUploadID
    78  	}
    79  	return kv.SetMsgIf(ctx, m.store, storePartitionKey, []byte(multipart.UploadID), protoFromMultipart(&multipart), nil)
    80  }
    81  
    82  func (m *tracker) Get(ctx context.Context, uploadID string) (*Upload, error) {
    83  	if uploadID == "" {
    84  		return nil, ErrInvalidUploadID
    85  	}
    86  	data := &UploadData{}
    87  	_, err := kv.GetMsg(ctx, m.store, storePartitionKey, []byte(uploadID), data)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	return multipartFromProto(data), nil
    92  }
    93  
    94  func (m *tracker) Delete(ctx context.Context, uploadID string) error {
    95  	if uploadID == "" {
    96  		return ErrInvalidUploadID
    97  	}
    98  	key := []byte(uploadID)
    99  	if _, err := m.store.Get(ctx, []byte(storePartitionKey), key); err != nil {
   100  		if errors.Is(err, kv.ErrNotFound) {
   101  			return fmt.Errorf("%w uploadID=%s", ErrMultipartUploadNotFound, uploadID)
   102  		}
   103  		return err
   104  	}
   105  
   106  	return m.store.Delete(ctx, []byte(storePartitionKey), key)
   107  }