Refactor the way that the that node_ping_controller works

This moves node ping controller to using the new internal lock
API.

The reason for this is twofold:
* The channel approach that was used to notify other
  controllers of changes could only be used once (at startup),
  and couldn't be used in the future to broadcast node
  ping status. The idea idea is here that we could move
  to a sync.Cond style API and only wakeup other controllers
  on change, as opposed to constantly polling each other
* The problem with sync.Cond is that it's not context friendly.
  If we want to do stuff like wait on a sync.cond and use a context
  or a timer or similar, it doesn't work whereas this API allows
  context cancellations on condition change.

The idea is that as we have more controllers that act as centralized
sources of authority, they can broadcast out their state.
This commit is contained in:
Sargun Dhillon
2020-11-05 03:01:46 -08:00
parent d562b71d9a
commit 11c63bca6f
3 changed files with 227 additions and 22 deletions

View File

@@ -2,9 +2,9 @@ package node
import (
"context"
"sync"
"time"
"github.com/virtual-kubelet/virtual-kubelet/internal/lock"
"github.com/virtual-kubelet/virtual-kubelet/log"
"github.com/virtual-kubelet/virtual-kubelet/trace"
"golang.org/x/sync/singleflight"
@@ -12,14 +12,10 @@ import (
)
type nodePingController struct {
nodeProvider NodeProvider
pingInterval time.Duration
firstPingCompleted chan struct{}
pingTimeout *time.Duration
// "Results"
sync.Mutex
result *pingResult
nodeProvider NodeProvider
pingInterval time.Duration
pingTimeout *time.Duration
cond lock.MonitorVariable
}
type pingResult struct {
@@ -37,10 +33,10 @@ func newNodePingController(node NodeProvider, pingInterval time.Duration, timeou
}
return &nodePingController{
nodeProvider: node,
pingInterval: pingInterval,
firstPingCompleted: make(chan struct{}),
pingTimeout: timeout,
nodeProvider: node,
pingInterval: pingInterval,
pingTimeout: timeout,
cond: lock.NewMonitorVariable(),
}
}
@@ -87,28 +83,26 @@ func (npc *nodePingController) run(ctx context.Context) {
pingResult.pingTime = result.Val.(time.Time)
}
npc.Lock()
defer npc.Unlock()
npc.result = &pingResult
npc.cond.Set(&pingResult)
span.SetStatus(pingResult.error)
}
// Run the first check manually
checkFunc(ctx)
close(npc.firstPingCompleted)
wait.UntilWithContext(ctx, checkFunc, npc.pingInterval)
}
// getResult returns the current ping result in a non-blocking fashion except for the first ping. It waits for the
// first ping to be successful before returning. If the context is cancelled while waiting for that value, it will
// return immediately.
func (npc *nodePingController) getResult(ctx context.Context) (*pingResult, error) {
sub := npc.cond.Subscribe()
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-npc.firstPingCompleted:
case <-sub.NewValueReady():
}
npc.Lock()
defer npc.Unlock()
return npc.result, nil
return sub.Value().Value.(*pingResult), nil
}