Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSami Hiltunen <shiltunen@gitlab.com>2020-11-10 11:55:09 +0300
committerSami Hiltunen <shiltunen@gitlab.com>2020-11-17 14:12:25 +0300
commit96f3dafa8f00a8c1c7b03753bd3035dbb9673e03 (patch)
treec1f6b3482e2e7144e3c59aedb369367bd01c2945
parent4fa998517623f98a944ccd9e2aa66230519a1acb (diff)
add a concurrency safe wrapper for Random
This commit moves Random to it's own file and introduces a wrapper to make Random safe for concurrent use. The global random in Go is safe for concurrent use but random instances created otherwise are not. Adding a locked wrapper allows us to inject a random in to per repository router and have it provide safe safe access as the global.
-rw-r--r--internal/praefect/random.go26
-rw-r--r--internal/praefect/random_test.go41
-rw-r--r--internal/praefect/router_per_repository.go11
-rw-r--r--internal/praefect/router_per_repository_test.go4
4 files changed, 69 insertions, 13 deletions
diff --git a/internal/praefect/random.go b/internal/praefect/random.go
new file mode 100644
index 000000000..36321c2c3
--- /dev/null
+++ b/internal/praefect/random.go
@@ -0,0 +1,26 @@
+package praefect
+
+import (
+ "sync"
+)
+
+// Random is the interface of the Go random number generator.
+type Random interface {
+ // Intn returns a random integer in the range [0,n).
+ Intn(n int) int
+}
+
+// randomFunc is an adapter to turn conforming functions in to a Random.
+type randomFunc func(n int) int
+
+func (fn randomFunc) Intn(n int) int { return fn(n) }
+
+// NewLockedRandom wraps the passed in Random to make it safe for concurrent use.
+func NewLockedRandom(r Random) Random {
+ var m sync.Mutex
+ return randomFunc(func(n int) int {
+ m.Lock()
+ defer m.Unlock()
+ return r.Intn(n)
+ })
+}
diff --git a/internal/praefect/random_test.go b/internal/praefect/random_test.go
new file mode 100644
index 000000000..915cd977b
--- /dev/null
+++ b/internal/praefect/random_test.go
@@ -0,0 +1,41 @@
+package praefect
+
+import (
+ "sync"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestNewLockedRandom tests that n is correctly passed down to the random
+// implementation and that the return value is correctly passed back. To test
+// that access to the random is actually synchronized, we launch 50 goroutines
+// to call the random func concurrently to increment actual. If the calls to
+// random are not correctly synchronized, actual might be not match expected
+// at the end and the race detector should detect racy accesses even in the
+// cases where the values match.
+func TestNewLockedRandom(t *testing.T) {
+ expected := 50
+ actual := 0
+
+ random := NewLockedRandom(randomFunc(func(n int) int {
+ assert.Equal(t, 1, n)
+ actual++
+ return 2
+ }))
+
+ var wg sync.WaitGroup
+ wg.Add(expected)
+
+ for i := 0; i < expected; i++ {
+ go func() {
+ defer wg.Done()
+ assert.Equal(t, 2, random.Intn(1))
+ }()
+ }
+
+ wg.Wait()
+
+ require.Equal(t, expected, actual)
+}
diff --git a/internal/praefect/router_per_repository.go b/internal/praefect/router_per_repository.go
index fe9008a42..d2174c9bb 100644
--- a/internal/praefect/router_per_repository.go
+++ b/internal/praefect/router_per_repository.go
@@ -51,17 +51,6 @@ type PrimaryGetter interface {
GetPrimary(ctx context.Context, virtualStorage string, relativePath string) (string, error)
}
-// Random is the interface of the Go random number generator.
-type Random interface {
- // Intn returns a random integer in the range [0,n).
- Intn(n int) int
-}
-
-// RandomFunc is an adapter to turn conforming functions in to a Random.
-type RandomFunc func(n int) int
-
-func (fn RandomFunc) Intn(n int) int { return fn(n) }
-
// PerRepositoryRouter implements a router that routes requests respecting per repository primary nodes.
type PerRepositoryRouter struct {
conns Connections
diff --git a/internal/praefect/router_per_repository_test.go b/internal/praefect/router_per_repository_test.go
index abe355e89..02be7775b 100644
--- a/internal/praefect/router_per_repository_test.go
+++ b/internal/praefect/router_per_repository_test.go
@@ -99,7 +99,7 @@ func TestPerRepositoryRouter_RouteStorageAccessor(t *testing.T) {
"valid-choice-2",
},
},
- RandomFunc(func(n int) int {
+ randomFunc(func(n int) int {
require.Equal(t, tc.numCandidates, n)
return tc.pickCandidate
}),
@@ -198,7 +198,7 @@ func TestPerRepositoryRouter_RouteRepositoryAccessor(t *testing.T) {
return "primary", nil
}),
tc.healthyNodes,
- RandomFunc(func(n int) int {
+ randomFunc(func(n int) int {
t.Helper()
require.Equal(t, tc.numCandidates, n)
return tc.pickCandidate