mirror of
https://github.com/daeuniverse/dae.git
synced 2025-01-10 23:38:14 +07:00
155 lines
4.2 KiB
Go
155 lines
4.2 KiB
Go
|
/*
|
||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||
|
* Copyright (c) since 2023, mzz2017 (mzz@tuta.io). All rights reserved.
|
||
|
*/
|
||
|
|
||
|
package dialer
|
||
|
|
||
|
import (
|
||
|
"foo/common/consts"
|
||
|
"github.com/mzz2017/softwind/pkg/fastrand"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type minLatency struct {
|
||
|
latency time.Duration
|
||
|
dialer *Dialer
|
||
|
}
|
||
|
|
||
|
// AliveDialerSet assumes mapping between index and dialer MUST remain unchanged.
|
||
|
//
|
||
|
// It is thread-safe.
|
||
|
type AliveDialerSet struct {
|
||
|
log *logrus.Logger
|
||
|
|
||
|
mu sync.Mutex
|
||
|
dialerToIndex map[*Dialer]int // *Dialer -> index of inorderedAliveDialerSet
|
||
|
dialerToLatency map[*Dialer]time.Duration
|
||
|
inorderedAliveDialerSet []*Dialer
|
||
|
|
||
|
selectionPolicy consts.DialerSelectionPolicy
|
||
|
minLatency minLatency
|
||
|
}
|
||
|
|
||
|
func NewAliveDialerSet(
|
||
|
log *logrus.Logger,
|
||
|
selectionPolicy consts.DialerSelectionPolicy,
|
||
|
dialers []*Dialer,
|
||
|
setAlive bool,
|
||
|
) *AliveDialerSet {
|
||
|
a := &AliveDialerSet{
|
||
|
log: log,
|
||
|
dialerToIndex: make(map[*Dialer]int),
|
||
|
dialerToLatency: make(map[*Dialer]time.Duration),
|
||
|
inorderedAliveDialerSet: make([]*Dialer, 0, len(dialers)),
|
||
|
selectionPolicy: selectionPolicy,
|
||
|
minLatency: minLatency{
|
||
|
// Initiate the latency with a very big value.
|
||
|
latency: time.Hour,
|
||
|
},
|
||
|
}
|
||
|
if setAlive && len(dialers) > 0 {
|
||
|
// Use first dialer if no dialer has alive state.
|
||
|
a.minLatency.dialer = dialers[0]
|
||
|
}
|
||
|
for _, d := range dialers {
|
||
|
a.dialerToIndex[d] = -1
|
||
|
}
|
||
|
for _, d := range dialers {
|
||
|
a.SetAlive(d, setAlive)
|
||
|
}
|
||
|
return a
|
||
|
}
|
||
|
|
||
|
func (a *AliveDialerSet) GetRand() *Dialer {
|
||
|
a.mu.Lock()
|
||
|
defer a.mu.Unlock()
|
||
|
if len(a.inorderedAliveDialerSet) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
ind := fastrand.Intn(len(a.inorderedAliveDialerSet))
|
||
|
return a.inorderedAliveDialerSet[ind]
|
||
|
}
|
||
|
|
||
|
// GetMinLatency acquires correct selectionPolicy.
|
||
|
func (a *AliveDialerSet) GetMinLatency() *Dialer {
|
||
|
return a.minLatency.dialer
|
||
|
}
|
||
|
|
||
|
// SetAlive should be invoked when dialer every time latency and alive state changes.
|
||
|
func (a *AliveDialerSet) SetAlive(dialer *Dialer, alive bool) {
|
||
|
a.mu.Lock()
|
||
|
defer a.mu.Unlock()
|
||
|
var (
|
||
|
latency time.Duration
|
||
|
hasLatency bool
|
||
|
)
|
||
|
|
||
|
switch a.selectionPolicy {
|
||
|
case consts.DialerSelectionPolicy_MinLastLatency:
|
||
|
latency, hasLatency = dialer.Latencies10.LastLatency()
|
||
|
case consts.DialerSelectionPolicy_MinAverage10Latencies:
|
||
|
latency, hasLatency = dialer.Latencies10.AvgLatency()
|
||
|
}
|
||
|
|
||
|
if alive {
|
||
|
index := a.dialerToIndex[dialer]
|
||
|
if index >= 0 {
|
||
|
// This dialer is already alive.
|
||
|
} else {
|
||
|
// Not alive -> alive.
|
||
|
a.dialerToIndex[dialer] = len(a.inorderedAliveDialerSet)
|
||
|
a.inorderedAliveDialerSet = append(a.inorderedAliveDialerSet, dialer)
|
||
|
}
|
||
|
} else {
|
||
|
index := a.dialerToIndex[dialer]
|
||
|
if index >= 0 {
|
||
|
// Alive -> not alive.
|
||
|
// Remove the dialer from inorderedAliveDialerSet.
|
||
|
if index >= len(a.inorderedAliveDialerSet) {
|
||
|
a.log.Panicf("index:%v >= len(a.inorderedAliveDialerSet):%v", index, len(a.inorderedAliveDialerSet))
|
||
|
}
|
||
|
a.dialerToIndex[dialer] = -1
|
||
|
if index < len(a.inorderedAliveDialerSet)-1 {
|
||
|
// Swap this element with the last element.
|
||
|
dialerToSwap := a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1]
|
||
|
if dialer == dialerToSwap {
|
||
|
a.log.Panicf("dialer[%p] == dialerToSwap[%p]", dialer, dialerToSwap)
|
||
|
}
|
||
|
|
||
|
a.dialerToIndex[dialerToSwap] = index
|
||
|
a.inorderedAliveDialerSet[index], a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1] =
|
||
|
a.inorderedAliveDialerSet[len(a.inorderedAliveDialerSet)-1], a.inorderedAliveDialerSet[index]
|
||
|
}
|
||
|
// Pop the last element.
|
||
|
a.inorderedAliveDialerSet = a.inorderedAliveDialerSet[:len(a.inorderedAliveDialerSet)-1]
|
||
|
} else {
|
||
|
// This dialer is already not alive.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if hasLatency {
|
||
|
a.dialerToLatency[dialer] = latency
|
||
|
if latency < a.minLatency.latency {
|
||
|
a.minLatency.latency = latency
|
||
|
a.minLatency.dialer = dialer
|
||
|
} else if a.minLatency.dialer == dialer {
|
||
|
a.minLatency.latency = time.Hour
|
||
|
a.minLatency.dialer = nil
|
||
|
a.calcMinLatency()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (a *AliveDialerSet) calcMinLatency() {
|
||
|
for _, d := range a.inorderedAliveDialerSet {
|
||
|
latency := a.dialerToLatency[d]
|
||
|
if latency < a.minLatency.latency {
|
||
|
a.minLatency.latency = latency
|
||
|
a.minLatency.dialer = d
|
||
|
}
|
||
|
}
|
||
|
}
|