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 }