Skip to content

flakytest: cache channels closer - #4242

Closed
MustafaSaber wants to merge 1 commit into
masterfrom
showcase-fix
Closed

flakytest: cache channels closer#4242
MustafaSaber wants to merge 1 commit into
masterfrom
showcase-fix

Conversation

@MustafaSaber

@MustafaSaber MustafaSaber commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

In some tests where we use synctest we create the filter outside becuase we don't care about values in channels and the time. However, in go, chan is attributed to a bubble (synctest) or not in creation time and ctx.Done() channel is only created after the first call to ctx.Done() (lazy loading). So we can have a race where revalidationWorker already started and ran s.ctx.Done() or enqueueRevalidation which run inside the bubble. I the first case the chan will be attributed to no bubble so we can close from outside synctest but in the second case chan will be attributed to the bubble so we can't close from outside.

Using _ := ctx.Done() in main code will force the chan to be always attributed to the routing creating the filter (force loading)

This shows the fix but I think we should't merge because it has no effects outside tests and we can just init all tests inside synctest so it doesn't make sense to change main code to fix the test in this case

The error can be reproduced by:

~ GOMAXPROCS=1 go test ./filters/cache/ -run 'SWR_StaleServedAndRevalidated|AgeHeader_STALE' -count=1000 -race
fatal error: close of synctest channel from outside bubble

goroutine 460 [running]:
context.(*cancelCtx).cancel(0xc00032a7d0, 0x1, {0x105337e98, 0x1053c0900}, {0x0, 0x0})
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/context/context.go:568 +0x134
context.WithCancel.func1()
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/context/context.go:243 +0x54
github.com/zalando/skipper/filters/cache.(*cacheSpec).Close.1()
	/Users/msabertalaat/go/src/skipper/filters/cache/filter.go:147 +0x48
sync.(*Once).doSlow(0xc000339e78, 0xc0003cbc08)
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/sync/once.go:78 +0x98
sync.(*Once).Do(0xc000339e78, 0xc0003cbc08)
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/sync/once.go:69 +0x38
github.com/zalando/skipper/filters/cache.(*cacheSpec).Close(...)
	/Users/msabertalaat/go/src/skipper/filters/cache/filter.go:146
github.com/zalando/skipper/filters/cache.newTestFilterWithCleanup.func2()
	/Users/msabertalaat/go/src/skipper/filters/cache/filter_test.go:47 +0x78
testing.(*common).Cleanup.func1()
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/testing/testing.go:1460 +0x128
testing.(*common).runCleanup(0xc0002fa908, 0x0)
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/testing/testing.go:1820 +0x170
testing.tRunner.func2()
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/testing/testing.go:2187 +0x4c
testing.tRunner(0xc0002fa908, 0x105354a18)
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/testing/testing.go:2199 +0x190
created by testing.(*T).Run in goroutine 1
	/opt/homebrew/Cellar/go/1.27.0/libexec/src/testing/testing.go:2258 +0x7c0
FAIL	github.com/zalando/skipper/filters/cache	2.338s
FAIL

see #4241

In some tests where we use synctest we create the filter outside becuase we don't care about values in channels and the time. However, in go chan is attributed to a bubble (synctest) or not in creation time and ctx.Done() channel is only created after the first call to ctx.Done() (lazy loading). So we can have a race where revalidationWorker already started and ran `s.ctx.Done()` or enqueueRevalidation which run inside the bubble. I the first case the chan will be attributed to no bubble so we can close from outside synctest but in the second case chan will be attributed to the bubble so we can't close from outside.

Using `_ := ctx.Done()` in main code will force the chan to be always attributed to the routing creating the filter (force loading)

This shows the fix but I think we should't merge because it has no effects outside tests and we can just init all tests inside synctest so it doesn't make sense to change main code to fix the test in this case

Signed-off-by: Mustafa Abdelrahman <mustafa.abdelrahman@zalando.de>
@MustafaSaber MustafaSaber added bugfix Bug fixes and patches minor no risk changes, for example new filters labels Sep 2, 2026
@zalando-robot

Copy link
Copy Markdown

Docker image "registry-write.opensource.zalan.do/teapot/skipper:83f6a9ec20bf568f4dde8619b781b1a8c0ad76ec" is not based on an approved base image. Any production deployment relying on this image will be blocked.

To create a compliant Docker image of your application, you must reference an allowed Docker image as its base image in your Dockerfile. This base image must come from the Zalando Container Registry namespace library and use a recommended version as listed in the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Bug fixes and patches minor no risk changes, for example new filters

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants