add DeliverLatest as common function for use by Manager and ProxyTracker Open (#19564)

Open
add DeliverLatest as common function for use by Manager and ProxyTracker
pull/19508/head
John Murret 2023-11-07 16:03:37 -07:00 committed by GitHub
parent 8d6545ec43
commit caaff73337
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 46 additions and 55 deletions

View File

@ -5,6 +5,7 @@ package proxycfg
import ( import (
"errors" "errors"
"github.com/hashicorp/consul/lib/channels"
"runtime/debug" "runtime/debug"
"sync" "sync"
@ -259,37 +260,15 @@ func (m *Manager) notify(snap *ConfigSnapshot) {
// it will drain the chan and then re-attempt delivery so that a slow consumer // it will drain the chan and then re-attempt delivery so that a slow consumer
// gets the latest config earlier. This MUST be called from a method where m.mu // gets the latest config earlier. This MUST be called from a method where m.mu
// is held to be safe since it assumes we are the only goroutine sending on ch. // is held to be safe since it assumes we are the only goroutine sending on ch.
func (m *Manager) deliverLatest(snap *ConfigSnapshot, ch chan proxysnapshot.ProxySnapshot) { func (m *Manager) deliverLatest(snap proxysnapshot.ProxySnapshot, ch chan proxysnapshot.ProxySnapshot) {
// Send if chan is empty m.Logger.Trace("delivering latest proxy snapshot to proxy", "proxyID", snap.(*ConfigSnapshot).ProxyID)
select { err := channels.DeliverLatest(snap, ch)
case ch <- snap: if err != nil {
return m.Logger.Error("failed to deliver proxyState to proxy",
default: "proxy", snap.(*ConfigSnapshot).ProxyID,
}
// Not empty, drain the chan of older snapshots and redeliver. For now we only
// use 1-buffered chans but this will still work if we change that later.
OUTER:
for {
select {
case <-ch:
continue
default:
break OUTER
}
}
// Now send again
select {
case ch <- snap:
return
default:
// This should not be possible since we should be the only sender, enforced
// by m.mu but error and drop the update rather than panic.
m.Logger.Error("failed to deliver ConfigSnapshot to proxy",
"proxy", snap.ProxyID.String(),
) )
} }
} }
// Watch registers a watch on a proxy. It might not exist yet in which case this // Watch registers a watch on a proxy. It might not exist yet in which case this

View File

@ -6,6 +6,7 @@ package proxytracker
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/hashicorp/consul/lib/channels"
"sync" "sync"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
@ -207,32 +208,8 @@ func (pt *ProxyTracker) PushChange(proxyID *pbresource.ID, proxyState proxysnaps
func (pt *ProxyTracker) deliverLatest(proxyID *pbresource.ID, proxyState proxysnapshot.ProxySnapshot, ch chan proxysnapshot.ProxySnapshot) { func (pt *ProxyTracker) deliverLatest(proxyID *pbresource.ID, proxyState proxysnapshot.ProxySnapshot, ch chan proxysnapshot.ProxySnapshot) {
pt.config.Logger.Trace("delivering latest proxy snapshot to proxy", "proxyID", proxyID) pt.config.Logger.Trace("delivering latest proxy snapshot to proxy", "proxyID", proxyID)
// Send if chan is empty err := channels.DeliverLatest(proxyState, ch)
select { if err != nil {
case ch <- proxyState:
return
default:
}
// Not empty, drain the chan of older snapshots and redeliver. For now we only
// use 1-buffered chans but this will still work if we change that later.
OUTER:
for {
select {
case <-ch:
continue
default:
break OUTER
}
}
// Now send again
select {
case ch <- proxyState:
return
default:
// This should not be possible since we should be the only sender, enforced
// by m.mu but error and drop the update rather than panic.
pt.config.Logger.Error("failed to deliver proxyState to proxy", pt.config.Logger.Error("failed to deliver proxyState to proxy",
"proxy", proxyID.String(), "proxy", proxyID.String(),
) )

View File

@ -0,0 +1,35 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package channels
import "fmt"
// DeliverLatest will drain the channel discarding any messages if there are any and sends the current message.
func DeliverLatest[T any](val T, ch chan T) error {
// Send if chan is empty
select {
case ch <- val:
return nil
default:
}
// If it falls through to here, the channel is not empty.
// Drain the channel.
done := false
for !done {
select {
case <-ch:
continue
default:
done = true
}
}
// Attempt to send again. If it is not empty, throw an error
select {
case ch <- val:
return nil
default:
return fmt.Errorf("failed to deliver latest event: chan full again after draining")
}
}