storj.io/uplink@v1.13.0/private/storage/streams/streambatcher/batcher.go (about) 1 // Copyright (C) 2023 Storj Labs, Inc. 2 // See LICENSE for copying information. 3 4 package streambatcher 5 6 import ( 7 "context" 8 "sync" 9 "time" 10 11 "github.com/zeebo/errs" 12 13 "storj.io/common/pb" 14 "storj.io/common/storj" 15 "storj.io/uplink/private/metaclient" 16 ) 17 18 // Info returns stream information gathered by the Batcher. 19 type Info struct { 20 // CreationDate is the creation date of the stream extracted by the 21 // stream ID that is provided to the Batcher or gathered from the 22 // BeginObject response. 23 CreationDate time.Time 24 25 // PlainSize is the plain-text size of the stream aggregated from all 26 // MakeInlineSegment or CommitSegment batch items. 27 PlainSize int64 28 29 // Version is object version retrieved from CommitObject batch item. 30 Version []byte 31 } 32 33 // Batcher issues batch items related to a single stream. It aggregates 34 // information about the stream required by callers to commit the stream. It 35 // also learns the stream ID (unless already provided for part uploads) and 36 // automatically injects it into batch items that need it. 37 type Batcher struct { 38 miBatcher metaclient.Batcher 39 40 mu sync.Mutex 41 streamID storj.StreamID 42 info Info 43 } 44 45 // New returns a new Batcher that issues batch items for a stream. The streamID 46 // can be nil (in the case of an object upload) or not (in the case of a part 47 // upload). The batcher will discover the streamID in the former case when it 48 // processes a BeginObject. 49 func New(miBatcher metaclient.Batcher, streamID storj.StreamID) *Batcher { 50 return &Batcher{ 51 miBatcher: miBatcher, 52 streamID: streamID, 53 } 54 } 55 56 // Batch issues batch items for a stream. Once the streamID is known, it will 57 // be injected into batch items that need it. If a BeginObject is issued, the 58 // stream ID will be gleaned from it. If a BeginObject needs to be issued, it 59 // must be the first batch item issued by the batcher. 60 func (s *Batcher) Batch(ctx context.Context, batchItems ...metaclient.BatchItem) ([]metaclient.BatchResponse, error) { 61 s.mu.Lock() 62 defer s.mu.Unlock() 63 64 for _, item := range batchItems { 65 switch item := item.(type) { 66 case *metaclient.BeginSegmentParams: 67 item.StreamID = s.streamID 68 case *metaclient.MakeInlineSegmentParams: 69 item.StreamID = s.streamID 70 s.info.PlainSize += item.PlainSize 71 case *metaclient.CommitSegmentParams: 72 s.info.PlainSize += item.PlainSize 73 case *metaclient.CommitObjectParams: 74 item.StreamID = s.streamID 75 } 76 } 77 78 if len(batchItems) == 0 { 79 return nil, errs.New("programmer error: empty batch request") 80 } 81 82 resp, err := s.miBatcher.Batch(ctx, batchItems...) 83 if err != nil { 84 return nil, err 85 } 86 87 if len(resp) == 0 { 88 return nil, errs.New("programmer error: empty batch response") 89 } 90 91 if s.streamID == nil { 92 beginObject, err := resp[0].BeginObject() 93 if err != nil { 94 return nil, errs.New("programmer error: first batch must start with BeginObject: %w", err) 95 } 96 if beginObject.StreamID.IsZero() { 97 return nil, errs.New("stream ID missing from BeginObject response") 98 } 99 s.streamID = beginObject.StreamID 100 } 101 102 for _, response := range resp { 103 if response.IsCommitObject() { 104 commitObject, err := response.CommitObject() 105 if err != nil { 106 return nil, errs.New("programmer error: batch must be CommitObject: %w", err) 107 } 108 109 s.info = Info{ 110 CreationDate: commitObject.Object.Created, 111 PlainSize: commitObject.Object.PlainSize, 112 } 113 if commitObject.Object.Status == int32(pb.Object_COMMITTED_VERSIONED) { 114 s.info.Version = commitObject.Object.Version 115 } 116 } 117 } 118 119 return resp, nil 120 } 121 122 // StreamID returns the stream ID either provided to the Batcher or gleaned 123 // from issuing a BeginObject request. 124 func (s *Batcher) StreamID() storj.StreamID { 125 s.mu.Lock() 126 defer s.mu.Unlock() 127 return s.streamID 128 } 129 130 // Info returns the stream information gathered by the batch items. 131 func (s *Batcher) Info() (Info, error) { 132 s.mu.Lock() 133 defer s.mu.Unlock() 134 135 if s.streamID == nil { 136 return Info{}, errs.New("stream ID is unexpectedly nil") 137 } 138 139 return s.info, nil 140 }