Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion pkg/vmcp/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,11 @@ type Server struct {
// Nil if status reporting is disabled.
statusReporter vmcpstatus.Reporter

// versionPollInterval overrides how often the registry version is polled.
// Zero means the package default. Set per-Server so a test can shorten it
// without mutating shared state a parallel test also reads.
versionPollInterval time.Duration

// shutdownFuncs contains cleanup functions to run during Stop().
// Populated during Start() initialization before blocking; no mutex needed
// since Stop() is only called after Start()'s select returns.
Expand Down Expand Up @@ -880,7 +885,7 @@ func (s *Server) Start(ctx context.Context) error {
// (#6546). Runs independently of status reporting; a no-op for static registries.
if _, isDynamic := s.backendRegistry.(vmcp.DynamicRegistry); isDynamic && s.vmcpSessionMgr != nil {
reconcileCtx, reconcileCancel := context.WithCancel(ctx)
go s.reconcileSessionsOnRegistryChange(reconcileCtx, versionPollInterval)
go s.reconcileSessionsOnRegistryChange(reconcileCtx, s.pollInterval())
s.shutdownFuncs = append(s.shutdownFuncs, func(context.Context) error {
reconcileCancel()
return nil
Expand Down
4 changes: 2 additions & 2 deletions pkg/vmcp/server/session_reconcile.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import (
// membership never changes, so this returns immediately.
//
// The loop runs until ctx is cancelled (on server Stop). pollInterval is passed
// in (rather than read from the package-level versionPollInterval) so tests can
// drive it without mutating shared state that a parallel test also touches.
// in (rather than read from a package-level default) so tests can drive it
// without mutating shared state that a parallel test also touches.
func (s *Server) reconcileSessionsOnRegistryChange(ctx context.Context, pollInterval time.Duration) {
dynamicReg, isDynamic := s.backendRegistry.(vmcp.DynamicRegistry)
if !isDynamic || s.vmcpSessionMgr == nil {
Expand Down
18 changes: 14 additions & 4 deletions pkg/vmcp/server/status_reporting.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@ import (
vmcpstatus "github.com/stacklok/toolhive/pkg/vmcp/status"
)

// versionPollInterval is how often to check the registry version for changes.
// Exposed as a package-level var so tests can set a shorter interval.
var versionPollInterval = 2 * time.Second
// defaultVersionPollInterval is how often to check the registry version for
// changes when a Server does not override it.
const defaultVersionPollInterval = 2 * time.Second

// pollInterval returns this Server's version-poll interval, falling back to the
// package default. Reading it per-Server rather than from a mutable package var
// is what lets a test shorten it while staying parallel-safe.
func (s *Server) pollInterval() time.Duration {
if s.versionPollInterval > 0 {
return s.versionPollInterval
}
return defaultVersionPollInterval
}

// StatusReportingConfig configures periodic status reporting.
type StatusReportingConfig struct {
Expand Down Expand Up @@ -75,7 +85,7 @@ func (s *Server) periodicStatusReporting(ctx context.Context, config StatusRepor
var versionTickerC <-chan time.Time
var lastRegistryVersion uint64
if isDynamic {
versionTicker := time.NewTicker(versionPollInterval)
versionTicker := time.NewTicker(s.pollInterval())
defer versionTicker.Stop()
versionTickerC = versionTicker.C
}
Expand Down
8 changes: 3 additions & 5 deletions pkg/vmcp/server/status_reporting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,13 @@ func (r *testDynamicRegistry) Remove(_ string) error {
func TestPeriodicStatusReporting_ReactsToVersionChange(t *testing.T) {
t.Parallel()

// Speed up the version-polling ticker so the test completes in milliseconds.
orig := versionPollInterval
versionPollInterval = 10 * time.Millisecond
t.Cleanup(func() { versionPollInterval = orig })

reporter := &mockReporter{}
reg := &testDynamicRegistry{}
server := &Server{
backendRegistry: reg,
// Speed up the version-polling ticker so the test completes in
// milliseconds. Per-Server, so this stays safe under t.Parallel.
versionPollInterval: 10 * time.Millisecond,
}

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
Expand Down
Loading