Skip to content
This repository was archived by the owner on Jan 30, 2025. It is now read-only.

Commit c5c2cea

Browse files
Ivan Mirićimiric
authored andcommitted
Fix context canceled error when running more than 1 iteration
This was a regression introduced by #515, caused by the fact the VU context changes after each iteration[1], so our context initialization needs to happen on each iteration (launch() call) as well. [1]: https://github.com/grafana/k6/blob/e09bb87277865d668586429eee97158fbbfc58e5/js/runner.go#L710
1 parent b157f21 commit c5c2cea

File tree

3 files changed

+51
-48
lines changed

3 files changed

+51
-48
lines changed

chromium/browser_type.go

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,41 +33,34 @@ var _ api.BrowserType = &BrowserType{}
3333
// BrowserType provides methods to launch a Chrome browser instance or connect to an existing one.
3434
// It's the entry point for interacting with the browser.
3535
type BrowserType struct {
36-
Ctx context.Context
37-
CancelFn context.CancelFunc
38-
hooks *common.Hooks
39-
fieldNameMapper *common.FieldNameMapper
40-
vu k6modules.VU
41-
42-
execPath string // path to the Chromium executable
43-
storage *storage.Dir // stores temporary data for the extension and user
36+
// FIXME: This is only exported because testBrowser needs it. Contexts
37+
// shouldn't be stored on structs if we can avoid it.
38+
Ctx context.Context
39+
vu k6modules.VU
40+
hooks *common.Hooks
41+
k6Metrics *k6ext.CustomMetrics
42+
execPath string // path to the Chromium executable
43+
storage *storage.Dir // stores temporary data for the extension and user
4444
}
4545

46-
// NewBrowserType returns a new Chrome browser type.
47-
// Before returning a new browser type:
48-
// - Initializes the extension-wide context
49-
// - Initializes the goja runtime.
50-
func NewBrowserType(ctx context.Context) api.BrowserType {
46+
// NewBrowserType registers our custom k6 metrics, creates method mappings on
47+
// the goja runtime, and returns a new Chrome browser type.
48+
func NewBrowserType(vu k6modules.VU) api.BrowserType {
5149
var (
52-
vu = k6ext.GetVU(ctx)
5350
rt = vu.Runtime()
5451
hooks = common.NewHooks()
5552
)
5653

57-
// Create the extension master context.
58-
// If this context is cancelled we'll initiate an extension wide cancellation and shutdown.
59-
extensionCtx, extensionCancelFn := context.WithCancel(ctx)
60-
extensionCtx = common.WithHooks(extensionCtx, hooks)
61-
54+
// NOTE: vu.InitEnv() *must* be called from the script init scope,
55+
// otherwise it will return nil.
56+
k6m := k6ext.RegisterCustomMetrics(vu.InitEnv().Registry)
6257
b := BrowserType{
63-
Ctx: extensionCtx,
64-
CancelFn: extensionCancelFn,
65-
hooks: hooks,
66-
fieldNameMapper: common.NewFieldNameMapper(),
67-
vu: vu,
68-
storage: &storage.Dir{},
58+
vu: vu,
59+
hooks: hooks,
60+
k6Metrics: k6m,
61+
storage: &storage.Dir{},
6962
}
70-
rt.SetFieldNameMapper(b.fieldNameMapper)
63+
rt.SetFieldNameMapper(common.NewFieldNameMapper())
7164

7265
return &b
7366
}
@@ -118,34 +111,42 @@ func (b *BrowserType) ExecutablePath() (execPath string) {
118111
return ""
119112
}
120113

114+
func (b *BrowserType) initContext() context.Context {
115+
ctx := k6ext.WithVU(b.vu.Context(), b.vu)
116+
ctx = k6ext.WithCustomMetrics(ctx, b.k6Metrics)
117+
ctx = common.WithHooks(ctx, b.hooks)
118+
return ctx
119+
}
120+
121121
// Launch allocates a new Chrome browser process and returns a new api.Browser value,
122122
// which can be used for controlling the Chrome browser.
123123
func (b *BrowserType) Launch(opts goja.Value) api.Browser {
124+
ctx := b.initContext()
124125
launchOpts := common.NewLaunchOptions()
125-
if err := launchOpts.Parse(b.Ctx, opts); err != nil {
126-
k6ext.Panic(b.Ctx, "parsing launch options: %w", err)
126+
if err := launchOpts.Parse(ctx, opts); err != nil {
127+
k6ext.Panic(ctx, "parsing launch options: %w", err)
127128
}
128-
b.Ctx = common.WithLaunchOptions(b.Ctx, launchOpts)
129+
ctx = common.WithLaunchOptions(ctx, launchOpts)
129130

130-
bp, err := b.launch(launchOpts)
131+
bp, err := b.launch(ctx, launchOpts)
131132
if err != nil {
132133
err = &k6ext.UserFriendlyError{
133134
Err: err,
134135
Timeout: launchOpts.Timeout,
135136
}
136-
k6ext.Panic(b.Ctx, "%w", err)
137+
k6ext.Panic(ctx, "%w", err)
137138
}
138139

139140
return bp
140141
}
141142

142-
func (b *BrowserType) launch(opts *common.LaunchOptions) (*common.Browser, error) {
143+
func (b *BrowserType) launch(ctx context.Context, opts *common.LaunchOptions) (*common.Browser, error) {
143144
envs := make([]string, 0, len(opts.Env))
144145
for k, v := range opts.Env {
145146
envs = append(envs, fmt.Sprintf("%s=%s", k, v))
146147
}
147148

148-
logger, err := makeLogger(b.Ctx, opts)
149+
logger, err := makeLogger(ctx, opts)
149150
if err != nil {
150151
return nil, fmt.Errorf("setting up logger: %w", err)
151152
}
@@ -159,7 +160,7 @@ func (b *BrowserType) launch(opts *common.LaunchOptions) (*common.Browser, error
159160
}
160161
flags["user-data-dir"] = dataDir.Dir
161162

162-
go func(ctx context.Context) {
163+
go func(c context.Context) {
163164
defer func() {
164165
if err := dataDir.Cleanup(); err != nil {
165166
logger.Errorf("BrowserType:Launch", "cleaning up the user data directory: %v", err)
@@ -170,21 +171,26 @@ func (b *BrowserType) launch(opts *common.LaunchOptions) (*common.Browser, error
170171
// guarantee the cleanup we would need to orchestrate
171172
// it correctly which https://github.com/grafana/k6/issues/2432
172173
// will enable once it's complete.
173-
<-ctx.Done()
174-
}(b.Ctx)
174+
<-c.Done()
175+
}(ctx)
175176

176-
browserProc, err := b.allocate(opts, flags, envs, dataDir, logger)
177+
browserProc, err := b.allocate(ctx, opts, flags, envs, dataDir, logger)
177178
if browserProc == nil {
178179
return nil, fmt.Errorf("launching browser: %w", err)
179180
}
180181

181182
browserProc.AttachLogger(logger)
182183

184+
// If this context is cancelled we'll initiate an extension wide
185+
// cancellation and shutdown.
186+
browserCtx, browserCtxCancel := context.WithCancel(ctx)
183187
// attach the browser process ID to the context
184188
// so that we can kill it afterward if it lingers
185189
// see: k6ext.Panic function.
186-
b.Ctx = k6ext.WithProcessID(b.Ctx, browserProc.Pid())
187-
browser, err := common.NewBrowser(b.Ctx, b.CancelFn, browserProc, opts, logger)
190+
browserCtx = k6ext.WithProcessID(browserCtx, browserProc.Pid())
191+
b.Ctx = browserCtx
192+
browser, err := common.NewBrowser(browserCtx, browserCtxCancel,
193+
browserProc, opts, logger)
188194
if err != nil {
189195
return nil, fmt.Errorf("launching browser: %w", err)
190196
}
@@ -206,9 +212,11 @@ func (b *BrowserType) Name() string {
206212

207213
// allocate starts a new Chromium browser process and returns it.
208214
func (b *BrowserType) allocate(
209-
opts *common.LaunchOptions, flags map[string]interface{}, env []string, dataDir *storage.Dir, logger *log.Logger,
215+
ctx context.Context, opts *common.LaunchOptions,
216+
flags map[string]interface{}, env []string, dataDir *storage.Dir,
217+
logger *log.Logger,
210218
) (_ *common.BrowserProcess, rerr error) {
211-
ctx, cancel := context.WithTimeout(b.Ctx, opts.Timeout)
219+
ctx, cancel := context.WithTimeout(ctx, opts.Timeout)
212220
defer func() {
213221
if rerr != nil {
214222
cancel()

main.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"github.com/grafana/xk6-browser/api"
1010
"github.com/grafana/xk6-browser/chromium"
1111
"github.com/grafana/xk6-browser/common"
12-
"github.com/grafana/xk6-browser/k6ext"
1312

1413
k6modules "go.k6.io/k6/js/modules"
1514
)
@@ -57,13 +56,9 @@ func New() *RootModule {
5756
// NewModuleInstance implements the k6modules.Module interface to return
5857
// a new instance for each VU.
5958
func (*RootModule) NewModuleInstance(vu k6modules.VU) k6modules.Instance {
60-
k6m := k6ext.RegisterCustomMetrics(vu.InitEnv().Registry)
61-
ctx := k6ext.WithVU(vu.Context(), vu)
62-
ctx = k6ext.WithCustomMetrics(ctx, k6m)
63-
6459
return &ModuleInstance{
6560
mod: &JSModule{
66-
Chromium: chromium.NewBrowserType(ctx),
61+
Chromium: chromium.NewBrowserType(vu),
6762
Devices: common.GetDevices(),
6863
Version: version,
6964
},

tests/test_browser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func newTestBrowser(tb testing.TB, opts ...interface{}) *testBrowser {
123123
}
124124

125125
// launch the browser
126-
v := chromium.NewBrowserType(vu.Context())
126+
v := chromium.NewBrowserType(vu)
127127
bt, ok := v.(*chromium.BrowserType)
128128
if !ok {
129129
panic(fmt.Errorf("testBrowser: unexpected browser type %T", v))

0 commit comments

Comments
 (0)