-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathmanager_test.go
116 lines (107 loc) · 3.33 KB
/
manager_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Copyright 2021 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package spanconfigmanager_test
import (
"context"
"sync"
"testing"
"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/jobs"
"github.com/cockroachdb/cockroach/pkg/spanconfig"
"github.com/cockroachdb/cockroach/pkg/spanconfig/spanconfigmanager"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
"github.com/stretchr/testify/require"
)
// TestManagerConcurrentJobCreation ensures that only one of two concurrent
// attempts to create the auto span config reconciliation job are successful. We
// also ensure that the created job is what we expect.
// Sketch:
// - The first goroutine checks and ensures that the auto span config
// reconciliation job does not exists. Blocks after checking.
// - The second goroutine checks and ensures the auto span config reconciliation
// job does not exists and creates it. Unblocks the first goroutine.
// - The first goroutine tries to create the job but gets restarted. It
// subsequently notices that the job does indeed exist so ends up not creating
// one.
func TestManagerConcurrentJobCreation(t *testing.T) {
defer leaktest.AfterTest(t)()
ctx := context.Background()
tc := testcluster.StartTestCluster(t, 1, base.TestClusterArgs{
ServerArgs: base.TestServerArgs{
Knobs: base.TestingKnobs{
SpanConfig: &spanconfig.TestingKnobs{
ManagerDisableJobCreation: true, // disable the automatic job creation
},
},
},
})
defer tc.Stopper().Stop(ctx)
var mu syncutil.Mutex
blocker := make(chan struct{})
var unblocker chan struct{} = nil
ts := tc.Server(0)
manager := spanconfigmanager.New(
ts.DB(),
ts.JobRegistry().(*jobs.Registry),
ts.InternalExecutor().(*sql.InternalExecutor),
tc.Stopper(),
&spanconfig.TestingKnobs{
ManagerCreatedJobInterceptor: func(jobI interface{}) {
job := jobI.(*jobs.Job)
require.True(t, job.Payload().Noncancelable)
require.Equal(t, job.Payload().Description, "reconciling span configurations")
},
ManagerAfterCheckedReconciliationJobExistsInterceptor: func(exists bool) {
// First entrant blocks, second entrant does not.
mu.Lock()
if blocker != nil {
require.False(t, exists)
unblocker = blocker
blocker = nil
mu.Unlock()
<-unblocker
return
}
mu.Unlock()
},
},
)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
started := manager.StartJobIfNoneExists(ctx)
if started {
t.Errorf("expected no job to start, but it did")
}
}()
go func() {
defer wg.Done()
// Only try to start the job once the main testing goroutine has reached the
// blocker and is waiting.
for {
mu.Lock()
if blocker == nil {
mu.Unlock()
break
}
mu.Unlock()
}
started := manager.StartJobIfNoneExists(ctx)
if !started {
t.Errorf("expected job to start, but it did not")
}
close(unblocker)
}()
wg.Wait()
}