istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/test/loadbalancersim/mesh/node.go (about)

     1  //  Copyright Istio Authors
     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  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //  See the License for the specific language governing permissions and
    13  //  limitations under the License.
    14  
    15  package mesh
    16  
    17  import (
    18  	"math"
    19  	"time"
    20  
    21  	"istio.io/istio/pkg/test/loadbalancersim/locality"
    22  	"istio.io/istio/pkg/test/loadbalancersim/network"
    23  	"istio.io/istio/pkg/test/loadbalancersim/timer"
    24  	"istio.io/istio/pkg/test/loadbalancersim/timeseries"
    25  )
    26  
    27  var _ network.Connection = &Node{}
    28  
    29  const maxQLatency = 30 * time.Second
    30  
    31  type Node struct {
    32  	locality        locality.Instance
    33  	helper          *network.ConnectionHelper
    34  	q               *timer.Queue
    35  	serviceTime     time.Duration
    36  	qLatencyEnabled bool
    37  	qLength         timeseries.Instance
    38  	qLatency        timeseries.Instance
    39  }
    40  
    41  func newNode(name string, serviceTime time.Duration, enableQueueLatency bool, l locality.Instance) *Node {
    42  	return &Node{
    43  		locality:        l,
    44  		helper:          network.NewConnectionHelper(name),
    45  		q:               timer.NewQueue(),
    46  		serviceTime:     serviceTime,
    47  		qLatencyEnabled: enableQueueLatency,
    48  	}
    49  }
    50  
    51  func (n *Node) Name() string {
    52  	return n.helper.Name()
    53  }
    54  
    55  func (n *Node) QueueLength() *timeseries.Instance {
    56  	return &n.qLength
    57  }
    58  
    59  func (n *Node) QueueLatency() *timeseries.Instance {
    60  	return &n.qLatency
    61  }
    62  
    63  func (n *Node) calcRequestDuration() time.Duration {
    64  	// Get the current queue length.
    65  	qLen := n.q.Len()
    66  	qLatency := n.calcQLatency(qLen)
    67  
    68  	// Add the observations
    69  	tnow := time.Now()
    70  	n.qLength.AddObservation(float64(qLen), tnow)
    71  	n.qLatency.AddObservation(qLatency.Seconds(), tnow)
    72  
    73  	return n.serviceTime + qLatency
    74  }
    75  
    76  func (n *Node) calcQLatency(qlen int) time.Duration {
    77  	if !n.qLatencyEnabled {
    78  		return 0
    79  	}
    80  
    81  	// Compute the queue latency in milliseconds.
    82  	latency := math.Pow(1.2, float64(qlen+1))
    83  
    84  	// Clip the latency at the maximum value.
    85  	clippedLatency := math.Min(latency, float64(maxQLatency.Milliseconds()))
    86  
    87  	// Return the latency as milliseconds.
    88  	return time.Duration(clippedLatency) * time.Millisecond
    89  }
    90  
    91  func (n *Node) TotalRequests() uint64 {
    92  	return n.helper.TotalRequests()
    93  }
    94  
    95  func (n *Node) ActiveRequests() uint64 {
    96  	return n.helper.ActiveRequests()
    97  }
    98  
    99  func (n *Node) Latency() *timeseries.Instance {
   100  	return n.helper.Latency()
   101  }
   102  
   103  func (n *Node) Request(onDone func()) {
   104  	n.helper.Request(func(wrappedOnDone func()) {
   105  		deadline := time.Now().Add(n.calcRequestDuration())
   106  
   107  		// Schedule the done function to be called after the deadline.
   108  		n.q.Schedule(wrappedOnDone, deadline)
   109  	}, onDone)
   110  }
   111  
   112  func (n *Node) Locality() locality.Instance {
   113  	return n.locality
   114  }
   115  
   116  func (n *Node) ShutDown() {
   117  	n.q.ShutDown()
   118  }
   119  
   120  type Nodes []*Node
   121  
   122  func (nodes Nodes) Select(match locality.Match) Nodes {
   123  	var out Nodes
   124  	for _, n := range nodes {
   125  		if match(n.locality) {
   126  			out = append(out, n)
   127  		}
   128  	}
   129  	return out
   130  }
   131  
   132  func (nodes Nodes) Latency() *timeseries.Instance {
   133  	var out timeseries.Instance
   134  	for _, n := range nodes {
   135  		out.AddAll(n.Latency())
   136  	}
   137  	return &out
   138  }
   139  
   140  func (nodes Nodes) QueueLatency() *timeseries.Instance {
   141  	var out timeseries.Instance
   142  	for _, n := range nodes {
   143  		out.AddAll(n.QueueLatency())
   144  	}
   145  	return &out
   146  }
   147  
   148  func (nodes Nodes) TotalRequests() uint64 {
   149  	var out uint64
   150  	for _, n := range nodes {
   151  		out += n.TotalRequests()
   152  	}
   153  	return out
   154  }
   155  
   156  func (nodes Nodes) ShutDown() {
   157  	for _, n := range nodes {
   158  		n.ShutDown()
   159  	}
   160  }