temporalio / temporalio/ui

UI's ConfigProviderWithRefresh enters tight loop after Close is called

Open
#3,001 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
TypeScript
Stars
431
Forks
179
Avg merge
2d 9h
Merged PRs (30d)
71

Description

Describe the bug

In server/server/config/config_provider_with_refresh.go , refreshConfig enters a tight loop
after the Close() method is called, because break only breaks out of select, rather than the entire for loop. If it used return instead, or a labelled break, it would exit immediately.

That is, in refreshConfig,

	for {
		select {
		case <-s.stop:
			break
		case <-s.ticker.C:
		}

		newConfig, err := s.provider.GetConfig()
                 // ...

should probably instead be

	for {
		select {
		case <-s.stop:
			return
		case <-s.ticker.C:
		}

		newConfig, err := s.provider.GetConfig()
                 // ...

To Reproduce
The bug can be seen in a test similar to

type TestConfigProvider struct {
	callCount int
}

func (tcp *TestConfigProvider) GetConfig() (*Config, error) {
	tcp.callCount++
	return &Config{
		RefreshInterval: time.Second,
	}, nil
}

func TestStopping(t *testing.T) {
	provider := new(TestConfigProvider)
	start := time.Now()
	refresh, err := NewConfigProviderWithRefresh(provider)
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	}
	if provider.callCount != 1 {
		t.Fatalf("Expected refresh to happen once but it occurred %d times", provider.callCount)
	}
	refresh.Close()
	seconds := time.Since(start).Seconds()
	if provider.callCount != 1+int(seconds) {
		t.Fatalf("Expected refresh to happen %d times but it occurred %d times", 1+int(seconds), provider.callCount)
	}
}

(You'll also see a lot of loaded new UI server configuration being output to the log before go test finishes, showing the tight loop it's entered).

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Open server/server/config/config_provider_with_refresh.go and inspect refreshConfig, especially how the stop channel is handled inside the select. Run the reproducer or relevant Go tests after changing the stop path, and confirm that Close prevents further GetConfig calls and the repeated configuration log output.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.