go.temporal.io/server@v1.23.0/common/archiver/history_iterator.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 //go:generate mockgen -copyright_file ../../LICENSE -package $GOPACKAGE -source $GOFILE -destination history_iterator_mock.go 26 27 package archiver 28 29 import ( 30 "context" 31 "encoding/json" 32 "errors" 33 34 historypb "go.temporal.io/api/history/v1" 35 "go.temporal.io/api/serviceerror" 36 "google.golang.org/protobuf/encoding/protojson" 37 "google.golang.org/protobuf/proto" 38 39 archiverspb "go.temporal.io/server/api/archiver/v1" 40 "go.temporal.io/server/common" 41 "go.temporal.io/server/common/persistence" 42 ) 43 44 const ( 45 historyPageSize = 250 46 ) 47 48 type ( 49 // HistoryIterator is used to get history batches 50 HistoryIterator interface { 51 Next(context.Context) (*archiverspb.HistoryBlob, error) 52 HasNext() bool 53 GetState() ([]byte, error) 54 } 55 56 historyIteratorState struct { 57 NextEventID int64 58 FinishedIteration bool 59 } 60 61 historyIterator struct { 62 historyIteratorState 63 64 request *ArchiveHistoryRequest 65 executionManager persistence.ExecutionManager 66 sizeEstimator SizeEstimator 67 historyPageSize int 68 targetHistoryBlobSize int 69 } 70 ) 71 72 var ( 73 errIteratorDepleted = errors.New("iterator is depleted") 74 ) 75 76 // NewHistoryIterator returns a new HistoryIterator 77 func NewHistoryIterator( 78 request *ArchiveHistoryRequest, 79 executionManager persistence.ExecutionManager, 80 targetHistoryBlobSize int, 81 ) HistoryIterator { 82 return newHistoryIterator(request, executionManager, targetHistoryBlobSize) 83 } 84 85 // NewHistoryIteratorFromState returns a new HistoryIterator with specified state 86 func NewHistoryIteratorFromState( 87 request *ArchiveHistoryRequest, 88 executionManager persistence.ExecutionManager, 89 targetHistoryBlobSize int, 90 initialState []byte, 91 ) (HistoryIterator, error) { 92 it := newHistoryIterator(request, executionManager, targetHistoryBlobSize) 93 if initialState == nil { 94 return it, nil 95 } 96 if err := it.reset(initialState); err != nil { 97 return nil, err 98 } 99 return it, nil 100 } 101 102 func newHistoryIterator( 103 request *ArchiveHistoryRequest, 104 executionManager persistence.ExecutionManager, 105 targetHistoryBlobSize int, 106 ) *historyIterator { 107 return &historyIterator{ 108 historyIteratorState: historyIteratorState{ 109 NextEventID: common.FirstEventID, 110 FinishedIteration: false, 111 }, 112 request: request, 113 executionManager: executionManager, 114 historyPageSize: historyPageSize, 115 targetHistoryBlobSize: targetHistoryBlobSize, 116 sizeEstimator: NewJSONSizeEstimator(), 117 } 118 } 119 120 func (i *historyIterator) Next( 121 ctx context.Context, 122 ) (*archiverspb.HistoryBlob, error) { 123 if !i.HasNext() { 124 return nil, errIteratorDepleted 125 } 126 127 historyBatches, newIterState, err := i.readHistoryBatches(ctx, i.NextEventID) 128 if err != nil { 129 return nil, err 130 } 131 132 i.historyIteratorState = newIterState 133 firstEvent := historyBatches[0].Events[0] 134 lastBatch := historyBatches[len(historyBatches)-1] 135 lastEvent := lastBatch.Events[len(lastBatch.Events)-1] 136 eventCount := int64(0) 137 for _, batch := range historyBatches { 138 eventCount += int64(len(batch.Events)) 139 } 140 header := &archiverspb.HistoryBlobHeader{ 141 Namespace: i.request.Namespace, 142 NamespaceId: i.request.NamespaceID, 143 WorkflowId: i.request.WorkflowID, 144 RunId: i.request.RunID, 145 IsLast: i.FinishedIteration, 146 FirstFailoverVersion: firstEvent.Version, 147 LastFailoverVersion: lastEvent.Version, 148 FirstEventId: firstEvent.EventId, 149 LastEventId: lastEvent.EventId, 150 EventCount: eventCount, 151 } 152 153 return &archiverspb.HistoryBlob{ 154 Header: header, 155 Body: historyBatches, 156 }, nil 157 } 158 159 // HasNext returns true if there are more items to iterate over. 160 func (i *historyIterator) HasNext() bool { 161 return !i.FinishedIteration 162 } 163 164 // GetState returns the encoded iterator state 165 func (i *historyIterator) GetState() ([]byte, error) { 166 return json.Marshal(i.historyIteratorState) 167 } 168 169 func (i *historyIterator) readHistoryBatches( 170 ctx context.Context, 171 firstEventID int64, 172 ) ([]*historypb.History, historyIteratorState, error) { 173 size := 0 174 targetSize := i.targetHistoryBlobSize 175 var historyBatches []*historypb.History 176 newIterState := historyIteratorState{} 177 for size < targetSize { 178 currHistoryBatches, err := i.readHistory(ctx, firstEventID) 179 if _, isNotFound := err.(*serviceerror.NotFound); isNotFound && firstEventID != common.FirstEventID { 180 newIterState.FinishedIteration = true 181 return historyBatches, newIterState, nil 182 } 183 if err != nil { 184 return nil, newIterState, err 185 } 186 for idx, batch := range currHistoryBatches { 187 historyBatchSize, err := i.sizeEstimator.EstimateSize(batch) 188 if err != nil { 189 return nil, newIterState, err 190 } 191 size += historyBatchSize 192 historyBatches = append(historyBatches, batch) 193 firstEventID = batch.Events[len(batch.Events)-1].EventId + 1 194 195 // In case targetSize is satisfied before reaching the end of current set of batches, return immediately. 196 // Otherwise, we need to look ahead to see if there's more history batches. 197 if size >= targetSize && idx != len(currHistoryBatches)-1 { 198 newIterState.FinishedIteration = false 199 newIterState.NextEventID = firstEventID 200 return historyBatches, newIterState, nil 201 } 202 } 203 } 204 205 // If you are here, it means the target size is met after adding the last batch of read history. 206 // We need to check if there's more history batches. 207 _, err := i.readHistory(ctx, firstEventID) 208 if _, isNotFound := err.(*serviceerror.NotFound); isNotFound && firstEventID != common.FirstEventID { 209 newIterState.FinishedIteration = true 210 return historyBatches, newIterState, nil 211 } 212 if err != nil { 213 return nil, newIterState, err 214 } 215 newIterState.FinishedIteration = false 216 newIterState.NextEventID = firstEventID 217 return historyBatches, newIterState, nil 218 } 219 220 func (i *historyIterator) readHistory(ctx context.Context, firstEventID int64) ([]*historypb.History, error) { 221 req := &persistence.ReadHistoryBranchRequest{ 222 BranchToken: i.request.BranchToken, 223 MinEventID: firstEventID, 224 MaxEventID: common.EndEventID, 225 PageSize: i.historyPageSize, 226 ShardID: i.request.ShardID, 227 } 228 historyBatches, _, _, err := persistence.ReadFullPageEventsByBatch(ctx, i.executionManager, req) 229 return historyBatches, err 230 } 231 232 // reset resets iterator to a certain state given its encoded representation 233 // if it returns an error, the operation will have no effect on the iterator 234 func (i *historyIterator) reset(stateToken []byte) error { 235 var iteratorState historyIteratorState 236 if err := json.Unmarshal(stateToken, &iteratorState); err != nil { 237 return err 238 } 239 i.historyIteratorState = iteratorState 240 return nil 241 } 242 243 type ( 244 // SizeEstimator is used to estimate the size of any object 245 SizeEstimator interface { 246 EstimateSize(v interface{}) (int, error) 247 } 248 249 jsonSizeEstimator struct { 250 } 251 ) 252 253 func (e *jsonSizeEstimator) EstimateSize(v interface{}) (int, error) { 254 // protojson must be used for proto structs. 255 if protoMessage, ok := v.(proto.Message); ok { 256 bs, err := protojson.Marshal(protoMessage) 257 return len(bs), err 258 } 259 260 data, err := json.Marshal(v) 261 if err != nil { 262 return 0, err 263 } 264 return len(data), nil 265 } 266 267 // NewJSONSizeEstimator returns a new SizeEstimator which uses json encoding to estimate size 268 func NewJSONSizeEstimator() SizeEstimator { 269 return &jsonSizeEstimator{} 270 }