github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/operator/status_tracker.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Portions of this file are additionally subject to the following
    15  // copyright.
    16  //
    17  // Copyright (C) 2021 Matrix Origin.
    18  //
    19  // Modified the behavior of the status tracker.
    20  
    21  package operator
    22  
    23  import (
    24  	"sync"
    25  	"time"
    26  )
    27  
    28  // Only record non-end status and one end status.
    29  type statusTimes [firstEndStatus + 1]time.Time
    30  
    31  // OpStatusTracker represents the status of an operator.
    32  type OpStatusTracker struct {
    33  	rw         sync.RWMutex
    34  	current    OpStatus    // Current status
    35  	reachTimes statusTimes // Time when reach the current status
    36  }
    37  
    38  // NewOpStatusTracker creates an OpStatus.
    39  func NewOpStatusTracker() OpStatusTracker {
    40  	return OpStatusTracker{
    41  		current:    STARTED,
    42  		reachTimes: statusTimes{STARTED: time.Now()},
    43  	}
    44  }
    45  
    46  // Status returns current status.
    47  func (trk *OpStatusTracker) Status() OpStatus {
    48  	trk.rw.RLock()
    49  	defer trk.rw.RUnlock()
    50  	return trk.current
    51  }
    52  
    53  // SetStatus only used for tests.
    54  func (trk *OpStatusTracker) setStatus(status OpStatus) {
    55  	trk.rw.Lock()
    56  	defer trk.rw.Unlock()
    57  	trk.current = status
    58  }
    59  
    60  // ReachTime returns the reach time of current status.
    61  func (trk *OpStatusTracker) ReachTime() time.Time {
    62  	trk.rw.RLock()
    63  	defer trk.rw.RUnlock()
    64  	return trk.getTime(trk.current)
    65  }
    66  
    67  // ReachTimeOf returns the time when reached given status. If didn't reached the given status, return zero.
    68  func (trk *OpStatusTracker) ReachTimeOf(s OpStatus) time.Time {
    69  	trk.rw.RLock()
    70  	defer trk.rw.RUnlock()
    71  	return trk.getTime(s)
    72  }
    73  
    74  func (trk *OpStatusTracker) getTime(s OpStatus) time.Time {
    75  	if s < firstEndStatus {
    76  		return trk.reachTimes[s]
    77  	} else if trk.current == s {
    78  		return trk.reachTimes[firstEndStatus]
    79  	} else {
    80  		return time.Time{}
    81  	}
    82  }
    83  
    84  // To transfer the current status to dst if this transition is valid,
    85  // returns whether transferred.
    86  func (trk *OpStatusTracker) To(dst OpStatus) bool {
    87  	trk.rw.Lock()
    88  	defer trk.rw.Unlock()
    89  	return trk.toLocked(dst)
    90  }
    91  
    92  func (trk *OpStatusTracker) toLocked(dst OpStatus) bool {
    93  	if dst < statusCount && validTrans[trk.current][dst] {
    94  		trk.current = dst
    95  		trk.setTime(trk.current, time.Now())
    96  		return true
    97  	}
    98  	return false
    99  }
   100  
   101  func (trk *OpStatusTracker) setTime(st OpStatus, t time.Time) {
   102  	if st < firstEndStatus {
   103  		trk.reachTimes[st] = t
   104  	} else {
   105  		trk.reachTimes[firstEndStatus] = t
   106  	}
   107  }
   108  
   109  // IsEnd checks whether the current status is an end status.
   110  func (trk *OpStatusTracker) IsEnd() bool {
   111  	trk.rw.RLock()
   112  	defer trk.rw.RUnlock()
   113  	return IsEndStatus(trk.current)
   114  }
   115  
   116  // CheckExpired checks if expired, and update the current status.
   117  func (trk *OpStatusTracker) CheckExpired(exp time.Duration) bool {
   118  	trk.rw.Lock()
   119  	defer trk.rw.Unlock()
   120  	switch trk.current {
   121  	case STARTED:
   122  		if time.Since(trk.reachTimes[STARTED]) < exp {
   123  			return false
   124  		}
   125  		_ = trk.toLocked(EXPIRED)
   126  		return true
   127  	}
   128  	return trk.current == EXPIRED
   129  }
   130  
   131  // String implements fmt.Stringer.
   132  func (trk *OpStatusTracker) String() string {
   133  	trk.rw.RLock()
   134  	defer trk.rw.RUnlock()
   135  	return OpStatusToString(trk.current)
   136  }