vitess.io/vitess@v0.16.2/go/history/history.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreedto in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package history implements a circular buffer with adjacent-item deduplication. 18 package history 19 20 import ( 21 "sync" 22 ) 23 24 // Deduplicable is an interface that records should implement if the 25 // history should perform their deduplication. An example would be 26 // deduplicating records whose only difference is their timestamp. 27 type Deduplicable interface { 28 // IsDuplicate returns true if other is considered to be a 29 // duplicate of the calling instance. 30 IsDuplicate(any) bool 31 } 32 33 // History is a data structure that allows you to keep some number of 34 // records. 35 type History struct { 36 mu sync.Mutex 37 records []any 38 lastAdded any 39 latest any 40 next int 41 length int 42 } 43 44 // New returns a History with the specified maximum length. 45 func New(length int) *History { 46 return &History{records: make([]any, length)} 47 } 48 49 // Add a new record in a threadsafe manner. If record implements 50 // Deduplicable, and IsDuplicate returns true when called on the last 51 // previously added record, it will not be added. 52 func (history *History) Add(record any) { 53 history.mu.Lock() 54 defer history.mu.Unlock() 55 56 history.latest = record 57 58 if equiv, ok := record.(Deduplicable); ok && history.length > 0 { 59 if equiv.IsDuplicate(history.lastAdded) { 60 return 61 } 62 } 63 64 history.records[history.next] = record 65 history.lastAdded = record 66 67 if history.length < len(history.records) { 68 history.length++ 69 } 70 71 history.next = (history.next + 1) % len(history.records) 72 } 73 74 // Records returns the kept records in reverse chronological order in a 75 // threadsafe manner. 76 func (history *History) Records() []any { 77 history.mu.Lock() 78 defer history.mu.Unlock() 79 80 records := make([]any, 0, history.length) 81 records = append(records, history.records[history.next:history.length]...) 82 records = append(records, history.records[:history.next]...) 83 84 // In place reverse. 85 for i := 0; i < history.length/2; i++ { 86 records[i], records[history.length-i-1] = records[history.length-i-1], records[i] 87 } 88 89 return records 90 } 91 92 // Latest returns the record most recently passed to Add(), 93 // regardless of whether it was actually added or dropped as a duplicate. 94 func (history *History) Latest() any { 95 history.mu.Lock() 96 defer history.mu.Unlock() 97 return history.latest 98 }