github.com/Jeffail/benthos/v3@v3.65.0/lib/util/checkpoint/type.go (about) 1 package checkpoint 2 3 import ( 4 "errors" 5 ) 6 7 // ErrOutOfSync is returned when an offset to be tracked is less than or equal 8 // to the current highest tracked offset. 9 var ErrOutOfSync = errors.New("provided offset was out of sync") 10 11 // ErrResolvedOffsetNotTracked is returned when an offset to be resolved has not 12 // been tracked (or was already resolved). 13 var ErrResolvedOffsetNotTracked = errors.New("resolved offset was not tracked") 14 15 // Type receives an ordered feed of integer based offsets being tracked, and an 16 // unordered feed of integer based offsets that are resolved, and is able to 17 // return the highest offset currently able to be committed such that an 18 // unresolved offset is never committed. 19 // 20 // WARNING: DEPRECATED 21 // TODO: V4 Remove this 22 type Type struct { 23 base int 24 maxResolved int 25 maxUnresolved int 26 27 offset int 28 pending []int 29 buf []int 30 } 31 32 // New returns a new checkpointer from a base offset, which is the value 33 // returned when no checkpoints have yet been resolved. The base can be zero. 34 func New(base int) *Type { 35 buffer := make([]int, 100) 36 return &Type{ 37 base: base, 38 pending: buffer[:0], 39 buf: buffer, 40 } 41 } 42 43 // Highest returns the current highest checkpoint. 44 func (t *Type) Highest() int { 45 return t.base + t.maxResolved 46 } 47 48 // Pending returns the number of pending checkpoints. 49 func (t *Type) Pending() int { 50 return len(t.pending) 51 } 52 53 // Track a new unresolved integer offset. This offset will be cached until it is 54 // marked as resolved. While it is cached no higher valued offset will ever be 55 // committed. If the provided value is lower than an already provided value an 56 // error is returned. 57 func (t *Type) Track(i int) error { 58 if t.offset == cap(t.buf) { 59 if len(t.pending) == cap(t.buf) { 60 // Reached the limit of our buffer, create a new one. 61 t.buf = make([]int, cap(t.buf)*2) 62 } 63 copy(t.buf, t.pending) 64 t.pending = t.buf[0:len(t.pending)] 65 t.offset = len(t.pending) 66 } 67 if len(t.pending) > 0 && t.pending[len(t.pending)-1] >= i { 68 return ErrOutOfSync 69 } 70 t.offset++ 71 t.pending = t.pending[:len(t.pending)+1] 72 t.pending[len(t.pending)-1] = i 73 return nil 74 } 75 76 // MustTrack is the same as Track but panics instead of returning an error. 77 func (t *Type) MustTrack(i int) { 78 if err := t.Track(i); err != nil { 79 panic(err) 80 } 81 } 82 83 // Resolve a tracked offset by allowing it to be committed. The highest possible 84 // offset to be committed is returned, or an error if the provided offset was 85 // not recognised. 86 func (t *Type) Resolve(offset int) (int, error) { 87 var i, v int 88 for i, v = range t.pending { 89 if v >= offset { 90 break 91 } 92 } 93 if v != offset { 94 return 0, ErrResolvedOffsetNotTracked 95 } 96 if v > t.maxUnresolved { 97 t.maxUnresolved = v 98 } 99 if i > 0 { 100 // The offset wasn't the next checkpoint. 101 copy(t.pending[1:], t.pending[:i]) 102 } else if len(t.pending) == 1 { 103 t.maxResolved = t.maxUnresolved 104 } else if t.pending[1] > t.maxUnresolved { 105 t.maxResolved = t.maxUnresolved 106 } else { 107 t.maxResolved = v 108 } 109 t.pending = t.pending[1:] 110 return t.base + t.maxResolved, nil 111 } 112 113 // MustResolve is the same as Resolve but panics instead of returning an error. 114 func (t *Type) MustResolve(offset int) int { 115 i, err := t.Resolve(offset) 116 if err != nil { 117 panic(err) 118 } 119 return i 120 }