Concurrency

Concurrent Go programs use goroutines, a form of lightweight thread managed by the Go runtime. Because goroutines run concurrently, every goroutine has to keep track of its own Sentry-related data locally. Otherwise, data races can introduce subtle bugs to your programs, and the consequences vary from nothing apparent to unexpected crashes or, worse, accidentally mixing up data stored in the Scope. More on this in the Scopes and Hubs section.

The easiest way to handle this, is to create a new Hub for every goroutine you start, however this would require you to rebind the current Client and handle Scope yourself. That is why we provide a helper method called Clone. It takes care of creating a Hub, cloning existing Scope and reassigning it alongside Client to newly create instance.

Once cloned, Hub is completely isolated and can be used safely inside concurrent call. However, instead of using globally exposed methods, they should be called directly on the Hub.

Here are two examples:

  • a recommended deterministic call on Hub that is safe
Copied
// Example of __CORRECT__ use of scopes inside a Goroutine

go func(localHub *sentry.Hub) {
	// as goroutine argument
	localHub.ConfigureScope(func(scope *sentry.Scope) {
		scope.SetTag("secretTag", "go#1")
	})
	localHub.CaptureMessage("Hello from Goroutine! #1")
}(sentry.CurrentHub().Clone())

go func() {
	// or created locally
	localHub := sentry.CurrentHub().Clone()
	localHub.ConfigureScope(func(scope *sentry.Scope) {
		scope.SetTag("secretTag", "go#2")
	})
	localHub.CaptureMessage("Hello from Goroutine! #2")
}()
  • a discouraged non-deterministic call on Hub that would leak information between threads
Copied
// Example of __INCORRECT__ use of scopes inside a Goroutine - DON'T USE IT!

go func() {
	sentry.ConfigureScope(func(scope *sentry.Scope) {
		scope.SetTag("secretTag", "go#1")
	})
	sentry.CaptureMessage("Hello from Goroutine! #1")
}()

go func() {
	sentry.ConfigureScope(func(scope *sentry.Scope) {
		scope.SetTag("secretTag", "go#2")
	})
	sentry.CaptureMessage("Hello from Goroutine! #2")
}()

// at this point both events can have either `go#1` tag or `go#2` tag. We'll never know.