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  }