dubbo.apache.org/dubbo-go/v3@v3.1.1/metrics/util/aggregate/sliding_window.go (about)

     1  /*
     2   * Licensed to the Apache Software Foundation (ASF) under one or more
     3   * contributor license agreements.  See the NOTICE file distributed with
     4   * this work for additional information regarding copyright ownership.
     5   * The ASF licenses this file to You under the Apache License, Version 2.0
     6   * (the "License"); you may not use this file except in compliance with
     7   * the License.  You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   */
    17  
    18  package aggregate
    19  
    20  // SlidingWindow adopts sliding window algorithm for statistics.
    21  //
    22  // It is NOT concurrent-safe.
    23  // A window contains paneCount panes.
    24  // intervalInMs = paneCount * paneIntervalInMs.
    25  type slidingWindow struct {
    26  	paneCount        int
    27  	intervalInMs     int64
    28  	paneIntervalInMs int64
    29  	paneSlice        []*pane
    30  }
    31  
    32  func newSlidingWindow(paneCount int, intervalInMs int64) *slidingWindow {
    33  	return &slidingWindow{
    34  		paneCount:        paneCount,
    35  		intervalInMs:     intervalInMs,
    36  		paneIntervalInMs: intervalInMs / int64(paneCount),
    37  		paneSlice:        make([]*pane, paneCount),
    38  	}
    39  }
    40  
    41  // values get all values from the slidingWindow's paneSlice.
    42  func (s *slidingWindow) values(timeMillis int64) []interface{} {
    43  	if timeMillis < 0 {
    44  		return make([]interface{}, 0)
    45  	}
    46  
    47  	res := make([]interface{}, 0, s.paneCount)
    48  
    49  	for _, p := range s.paneSlice {
    50  		if p == nil || s.isPaneDeprecated(p, timeMillis) {
    51  			continue
    52  		}
    53  		res = append(res, p.value)
    54  	}
    55  
    56  	return res
    57  }
    58  
    59  // isPaneDeprecated checks if the specified pane is deprecated at the specified timeMillis
    60  func (s *slidingWindow) isPaneDeprecated(pane *pane, timeMillis int64) bool {
    61  	return timeMillis-pane.startInMs > s.intervalInMs
    62  }
    63  
    64  // currentPane get the pane at the specified timestamp or create a new one if the pane is deprecated.
    65  func (s *slidingWindow) currentPane(timeMillis int64, newEmptyValue func() interface{}) *pane {
    66  	if timeMillis < 0 {
    67  		return nil
    68  	}
    69  	paneIdx := s.calcPaneIdx(timeMillis)
    70  	paneStart := s.calcPaneStart(timeMillis)
    71  
    72  	if s.paneSlice[paneIdx] == nil {
    73  		p := newPane(s.paneIntervalInMs, paneStart, newEmptyValue())
    74  		s.paneSlice[paneIdx] = p
    75  		return p
    76  	} else {
    77  		p := s.paneSlice[paneIdx]
    78  		if paneStart == p.startInMs {
    79  			return p
    80  		} else if paneStart > p.startInMs {
    81  			// The pane has deprecated. To avoid the overhead of creating a new instance, reset the original pane directly.
    82  			p.resetTo(paneStart, newEmptyValue())
    83  			return p
    84  		} else {
    85  			// The specified timestamp has passed.
    86  			return newPane(s.paneIntervalInMs, paneStart, newEmptyValue())
    87  		}
    88  	}
    89  }
    90  
    91  func (s *slidingWindow) calcPaneIdx(timeMillis int64) int {
    92  	return int(timeMillis/s.paneIntervalInMs) % s.paneCount
    93  }
    94  
    95  func (s *slidingWindow) calcPaneStart(timeMillis int64) int64 {
    96  	return timeMillis - timeMillis%s.paneIntervalInMs
    97  }