github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/nomad/timetable.go (about) 1 package nomad 2 3 import ( 4 "sort" 5 "sync" 6 "time" 7 8 "github.com/ugorji/go/codec" 9 ) 10 11 // TimeTable is used to associate a Raft index with a timestamp. 12 // This is used so that we can quickly go from a timestamp to an 13 // index or visa versa. 14 type TimeTable struct { 15 granularity time.Duration 16 limit time.Duration 17 table []TimeTableEntry 18 l sync.RWMutex 19 } 20 21 // TimeTableEntry is used to track a time and index 22 type TimeTableEntry struct { 23 Index uint64 24 Time time.Time 25 } 26 27 // NewTimeTable creates a new time table which stores entries 28 // at a given granularity for a maximum limit. The storage space 29 // required is (limit/granularity) 30 func NewTimeTable(granularity time.Duration, limit time.Duration) *TimeTable { 31 size := limit / granularity 32 if size < 1 { 33 size = 1 34 } 35 t := &TimeTable{ 36 granularity: granularity, 37 limit: limit, 38 table: make([]TimeTableEntry, 1, size), 39 } 40 return t 41 } 42 43 // Serialize is used to serialize the time table 44 func (t *TimeTable) Serialize(enc *codec.Encoder) error { 45 t.l.RLock() 46 defer t.l.RUnlock() 47 return enc.Encode(t.table) 48 } 49 50 // Deserialize is used to deserialize the time table 51 // and restore the state 52 func (t *TimeTable) Deserialize(dec *codec.Decoder) error { 53 // Decode the table 54 var table []TimeTableEntry 55 if err := dec.Decode(&table); err != nil { 56 return err 57 } 58 59 // Witness from oldest to newest 60 n := len(table) 61 for i := n - 1; i >= 0; i-- { 62 t.Witness(table[i].Index, table[i].Time) 63 } 64 return nil 65 } 66 67 // Witness is used to witness a new index and time. 68 func (t *TimeTable) Witness(index uint64, when time.Time) { 69 t.l.Lock() 70 defer t.l.Unlock() 71 72 // Ensure monotonic indexes 73 if t.table[0].Index > index { 74 return 75 } 76 77 // Skip if we already have a recent enough entry 78 if when.Sub(t.table[0].Time) < t.granularity { 79 return 80 } 81 82 // Grow the table if we haven't reached the size 83 if len(t.table) < cap(t.table) { 84 t.table = append(t.table, TimeTableEntry{}) 85 } 86 87 // Add this entry 88 copy(t.table[1:], t.table[:len(t.table)-1]) 89 t.table[0].Index = index 90 t.table[0].Time = when 91 } 92 93 // NearestIndex returns the nearest index older than the given time 94 func (t *TimeTable) NearestIndex(when time.Time) uint64 { 95 t.l.RLock() 96 defer t.l.RUnlock() 97 98 n := len(t.table) 99 idx := sort.Search(n, func(i int) bool { 100 return !t.table[i].Time.After(when) 101 }) 102 if idx < n && idx >= 0 { 103 return t.table[idx].Index 104 } 105 return 0 106 } 107 108 // NearestTime returns the nearest time older than the given index 109 func (t *TimeTable) NearestTime(index uint64) time.Time { 110 t.l.RLock() 111 defer t.l.RUnlock() 112 113 n := len(t.table) 114 idx := sort.Search(n, func(i int) bool { 115 return t.table[i].Index <= index 116 }) 117 if idx < n && idx >= 0 { 118 return t.table[idx].Time 119 } 120 return time.Time{} 121 }