Skip to content

Conversation

cbosss
Copy link
Contributor

@cbosss cbosss commented Apr 27, 2022

Use an rwmutex atomic pointer when accessing the global featureflag client.

@cbosss cbosss requested a review from a team as a code owner April 27, 2022 03:49
@cbosss cbosss self-assigned this Apr 27, 2022
@cbosss cbosss added the type: feature code contributing to the implementation of a feature and/or user facing functionality label Apr 27, 2022
paulo
paulo previously approved these changes Apr 27, 2022
Copy link
Contributor

@paulo paulo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

mraerino
mraerino previously approved these changes Apr 27, 2022
Copy link
Contributor

@mraerino mraerino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is good, but we should replace it with an atomic ptr in the future.
we also need to improve testability of featureflags which is kinda messy right now.

@mrdg
Copy link
Contributor

mrdg commented Apr 27, 2022

I think the only reason there's a lock here is because there's a SetGlobalClient function, which is mostly used in tests it seems. Probably makes sense to solve that some other way and get rid of the lock altogether.

@cbosss
Copy link
Contributor Author

cbosss commented Apr 27, 2022

Did a quick benchmark:

var globalLock sync.RWMutex
var globalClient Client = MockClient{}

// See https://blog.dubbelboer.com/2015/08/23/rwmutex-vs-atomicvalue-vs-unsafepointer.html
var globalLockAtomic = atomic.NewUnsafePointer(unsafe.Pointer(&globalClient))

func SetGlobalClient(client Client) {
	if client == nil {
		return
	}
	globalLock.Lock()
	globalClient = client
	globalLock.Unlock()

	globalLockAtomic.Store(unsafe.Pointer(&client))
}

func GetGlobalClient() Client {
	globalLock.RLock()
	defer globalLock.RUnlock()
	return globalClient
}

func GetGlobalClientAtomic() Client {
	c := (*Client)(globalLockAtomic.Load())
	return *c
}

func BenchmarkGlobalAccess(b *testing.B) {
	SetGlobalClient(&ldClient{})

	var c1, c2 Client

	b.Run("rwmutex", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			c1 = GetGlobalClient()
		}
	})

	b.Run("atomic", func(b *testing.B) {
		b.ReportAllocs()
		for i := 0; i < b.N; i++ {
			c2 = GetGlobalClientAtomic()
		}
	})
	require.Equal(b, c1, c2)
}
goos: darwin
goarch: amd64
pkg: github.com/netlify/netlify-commons/featureflag
cpu: Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
BenchmarkGlobalAccess
BenchmarkGlobalAccess/rwmutex
BenchmarkGlobalAccess/rwmutex-8         	76735899	        15.40 ns/op	       0 B/op	       0 allocs/op
BenchmarkGlobalAccess/atomic
BenchmarkGlobalAccess/atomic-8          	1000000000	         0.8881 ns/op	       0 B/op	       0 allocs/op
PASS

Take this with a grain of salt, because there's no contention.

@cbosss cbosss dismissed stale reviews from mraerino and paulo via 4482ef1 April 27, 2022 18:40
@cbosss cbosss requested review from mraerino and paulo April 27, 2022 18:46
@cbosss cbosss changed the title feat: use rwmutex when accessing global feature flag client feat: use atomic pointers for global feature flag client Apr 29, 2022
@cbosss cbosss changed the title feat: use atomic pointers for global feature flag client feat: use atomic pointer for global feature flag client Apr 29, 2022
Copy link
Contributor

@mraerino mraerino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think the dependency on the atomic lib from uber is needed but i don't care a lot

@mraerino
Copy link
Contributor

@cbosss wanna ship this?

@cbosss cbosss merged commit 2b6a48f into main May 30, 2022
@cbosss cbosss deleted the cole/feature-flag-rwmutex branch May 30, 2022 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature code contributing to the implementation of a feature and/or user facing functionality
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants