vitess.io/vitess@v0.16.2/go/vt/throttler/result.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 agreed to 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 throttler
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"sync"
    23  	"text/template"
    24  	"time"
    25  
    26  	"vitess.io/vitess/go/vt/topo/topoproto"
    27  )
    28  
    29  type rateChange string
    30  
    31  const (
    32  	increasedRate rateChange = "increased"
    33  	decreasedRate rateChange = "decreased"
    34  	unchangedRate rateChange = "not changed"
    35  )
    36  
    37  type goodOrBadRate string
    38  
    39  const (
    40  	goodRate    = "good"
    41  	badRate     = "bad"
    42  	ignoredRate = "ignored"
    43  )
    44  
    45  var resultStringTemplate = template.Must(template.New("result.String()").Parse(
    46  	`rate was: {{.RateChange}} from: {{.OldRate}} to: {{.NewRate}}
    47  alias: {{.Alias}} lag: {{.LagRecordNow.Stats.ReplicationLagSeconds}}s
    48  last change: {{.TimeSinceLastRateChange}} rate: {{.CurrentRate}} good/bad? {{.GoodOrBad}} skipped b/c: {{.MemorySkipReason}} good/bad: {{.HighestGood}}/{{.LowestBad}}
    49  state (old/tested/new): {{.OldState}}/{{.TestedState}}/{{.NewState}} 
    50  lag before: {{.LagBefore}} ({{.AgeOfBeforeLag}} ago) rates (primary/replica): {{.PrimaryRate}}/{{.GuessedReplicationRate}} backlog (old/new): {{.GuessedReplicationBacklogOld}}/{{.GuessedReplicationBacklogNew}}
    51  reason: {{.Reason}}`))
    52  
    53  // result is generated by the MaxReplicationLag module for each processed
    54  // "replicationLagRecord".
    55  // It captures the details and the decision of the processing.
    56  type result struct {
    57  	Now            time.Time
    58  	RateChange     rateChange
    59  	lastRateChange time.Time
    60  
    61  	OldState    state
    62  	TestedState state
    63  	NewState    state
    64  
    65  	OldRate int64
    66  	NewRate int64
    67  	Reason  string
    68  
    69  	CurrentRate      int64
    70  	GoodOrBad        goodOrBadRate
    71  	MemorySkipReason string
    72  	HighestGood      int64
    73  	LowestBad        int64
    74  
    75  	LagRecordNow                 replicationLagRecord
    76  	LagRecordBefore              replicationLagRecord
    77  	PrimaryRate                  int64
    78  	GuessedReplicationRate       int64
    79  	GuessedReplicationBacklogOld int
    80  	GuessedReplicationBacklogNew int
    81  }
    82  
    83  func (r result) String() string {
    84  	var b bytes.Buffer
    85  	if err := resultStringTemplate.Execute(&b, r); err != nil {
    86  		panic(fmt.Sprintf("failed to Execute() template: %v", err))
    87  	}
    88  	return b.String()
    89  }
    90  
    91  func (r result) Alias() string {
    92  	return topoproto.TabletAliasString(r.LagRecordNow.Tablet.Alias)
    93  }
    94  
    95  func (r result) TimeSinceLastRateChange() string {
    96  	if r.lastRateChange.IsZero() {
    97  		return "n/a"
    98  	}
    99  	return fmt.Sprintf("%.1fs", r.Now.Sub(r.lastRateChange).Seconds())
   100  }
   101  
   102  func (r result) LagBefore() string {
   103  	if r.LagRecordBefore.isZero() {
   104  		return "n/a"
   105  	}
   106  	return fmt.Sprintf("%ds", r.LagRecordBefore.Stats.ReplicationLagSeconds)
   107  }
   108  
   109  func (r result) AgeOfBeforeLag() string {
   110  	if r.LagRecordBefore.isZero() {
   111  		return "n/a"
   112  	}
   113  	return fmt.Sprintf("%.1fs", r.LagRecordNow.time.Sub(r.LagRecordBefore.time).Seconds())
   114  }
   115  
   116  // resultRing implements a ring buffer for "result" instances.
   117  type resultRing struct {
   118  	// mu guards the fields below.
   119  	mu sync.Mutex
   120  	// position holds the index of the *next* result in the ring.
   121  	position int
   122  	// wrapped becomes true when the ring buffer "wrapped" at least once and we
   123  	// started reusing entries.
   124  	wrapped bool
   125  	// values is the underlying ring buffer.
   126  	values []result
   127  }
   128  
   129  // newResultRing creates a new resultRing.
   130  func newResultRing(capacity int) *resultRing {
   131  	return &resultRing{
   132  		values: make([]result, capacity),
   133  	}
   134  }
   135  
   136  // add inserts a new result into the ring buffer.
   137  func (rr *resultRing) add(r result) {
   138  	rr.mu.Lock()
   139  	defer rr.mu.Unlock()
   140  
   141  	rr.values[rr.position] = r
   142  	rr.position++
   143  	if rr.position == len(rr.values) {
   144  		rr.position = 0
   145  		rr.wrapped = true
   146  	}
   147  }
   148  
   149  // latestValues returns all values of the buffer. Entries are sorted in reverse
   150  // chronological order i.e. newer items come first.
   151  func (rr *resultRing) latestValues() []result {
   152  	rr.mu.Lock()
   153  	defer rr.mu.Unlock()
   154  
   155  	start := rr.position - 1
   156  	if start == -1 {
   157  		// Current position is at the end.
   158  		start = len(rr.values) - 1
   159  	}
   160  	count := len(rr.values)
   161  	if !rr.wrapped {
   162  		count = rr.position
   163  	}
   164  
   165  	results := make([]result, count)
   166  	for i := 0; i < count; i++ {
   167  		pos := start - i
   168  		if pos < 0 {
   169  			// We started in the middle of the array and need to wrap around at the
   170  			// beginning of it.
   171  			pos += count
   172  		}
   173  		results[i] = rr.values[pos%count]
   174  	}
   175  	return results
   176  }