Skip to content

Commit dd515ac

Browse files
committed
sql/hints: refactor tests to use the hints cache of the test server
Previously, all tests created a separate hints cache that was running side-by-side with the hints cache of the test server. I'm not sure whether there is a reason for it (other than possibly trying to prevent flakes when we start actually using the cache on the main query path), but I don't think it's necessary (e.g. we shouldn't be touching the hints cache for internal queries, and the tests themselves are the only workload that should be affected by the hints cache, and we have full control of that). This commit hooks up all tests to just use the test server's hints cache. One test for verifying that the initial scan populates the existing hints into the cache needed a minor refactor - we now restart the single-node test cluster to simulate the initial scan. Additionally, the LRU test needed hardening if the initial scan is somewhat delayed. What prompted me to look into this is that I was confused why all rangefeed logs (which I enabled temporarily) were being duplicated in tests. It also expands a comment where the behavior wasn't obvious to me. Release note: None
1 parent b3e4300 commit dd515ac

File tree

3 files changed

+57
-49
lines changed

3 files changed

+57
-49
lines changed

pkg/sql/hints/BUILD.bazel

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,18 @@ go_test(
5858
deps = [
5959
":hints",
6060
"//pkg/base",
61-
"//pkg/kv/kvclient/rangefeed",
6261
"//pkg/security/securityassets",
6362
"//pkg/security/securitytest",
6463
"//pkg/server",
65-
"//pkg/sql/catalog",
64+
"//pkg/sql",
6665
"//pkg/sql/catalog/descs",
6766
"//pkg/sql/hintpb",
6867
"//pkg/sql/isql",
6968
"//pkg/sql/randgen",
7069
"//pkg/sql/stats",
70+
"//pkg/storage/fs",
7171
"//pkg/testutils",
72+
"//pkg/testutils/listenerutil",
7273
"//pkg/testutils/serverutils",
7374
"//pkg/testutils/sqlutils",
7475
"//pkg/testutils/testcluster",

pkg/sql/hints/hint_cache.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,9 @@ func (c *StatementHintsCache) checkHashHasHintsAsync(
322322
c.mu.Lock()
323323
defer c.mu.Unlock()
324324
if refreshTS.Forward(c.mu.hintedHashes[hash]) {
325-
// The refresh timestamp was bumped by a rangefeed event. Retry at the
326-
// new timestamp.
325+
// The refresh timestamp was bumped by a rangefeed event.
326+
// Retry at the new timestamp (refreshTS has been updated in
327+
// place).
327328
return false
328329
}
329330
if hasHints {

pkg/sql/hints/hint_cache_test.go

Lines changed: 51 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import (
1212
"time"
1313

1414
"github.com/cockroachdb/cockroach/pkg/base"
15-
"github.com/cockroachdb/cockroach/pkg/kv/kvclient/rangefeed"
16-
"github.com/cockroachdb/cockroach/pkg/sql/catalog"
17-
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descs"
15+
"github.com/cockroachdb/cockroach/pkg/server"
16+
"github.com/cockroachdb/cockroach/pkg/sql"
1817
"github.com/cockroachdb/cockroach/pkg/sql/hintpb"
1918
"github.com/cockroachdb/cockroach/pkg/sql/hints"
19+
"github.com/cockroachdb/cockroach/pkg/storage/fs"
2020
"github.com/cockroachdb/cockroach/pkg/testutils"
21+
"github.com/cockroachdb/cockroach/pkg/testutils/listenerutil"
2122
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
2223
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
2324
"github.com/cockroachdb/cockroach/pkg/testutils/testcluster"
@@ -41,8 +42,7 @@ func TestHintCacheBasic(t *testing.T) {
4142
r := sqlutils.MakeSQLRunner(db)
4243
setTestDefaults(t, srv)
4344

44-
// Create a hints cache.
45-
hc := createHintsCache(t, ctx, ts)
45+
hc := ts.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
4646
require.Equal(t, 0, hc.TestingHashCount())
4747

4848
// Insert a hint for a statement. The cache should soon contain the hash.
@@ -96,7 +96,7 @@ func TestHintCacheLRU(t *testing.T) {
9696
// Set cache size to 2 for testing eviction.
9797
r.Exec(t, "SET CLUSTER SETTING sql.hints.statement_hints_cache_size = 2")
9898

99-
hc := createHintsCache(t, ctx, ts)
99+
hc := ts.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
100100
require.Equal(t, 0, hc.TestingHashCount())
101101

102102
// Create test data: 3 different fingerprints.
@@ -121,46 +121,51 @@ func TestHintCacheLRU(t *testing.T) {
121121
return nil
122122
})
123123

124+
// Before the initial scan is complete, all queries unconditionally check
125+
// the hint cache and might perform DB reads, so we need to ignore all reads
126+
// that happened already.
127+
ignoredReads := hc.TestingNumTableReads()
128+
124129
// Access the first two fingerprints to populate the cache.
125130
// This should result in 2 table reads.
126131
requireHintsCount(t, hc, ctx, fingerprints[0], 1)
127-
require.Equal(t, 1, hc.TestingNumTableReads())
132+
require.Equal(t, ignoredReads+1, hc.TestingNumTableReads())
128133

129134
requireHintsCount(t, hc, ctx, fingerprints[1], 1)
130-
require.Equal(t, 2, hc.TestingNumTableReads())
135+
require.Equal(t, ignoredReads+2, hc.TestingNumTableReads())
131136

132137
// Access the same fingerprints again - should be served from cache with no
133138
// additional reads.
134139
requireHintsCount(t, hc, ctx, fingerprints[0], 1)
135140
requireHintsCount(t, hc, ctx, fingerprints[1], 1)
136-
require.Equal(t, 2, hc.TestingNumTableReads())
141+
require.Equal(t, ignoredReads+2, hc.TestingNumTableReads())
137142

138143
// Access the third fingerprint. This should evict the first (LRU) due to
139144
// cache size limit of 2, resulting in one more table read.
140145
requireHintsCount(t, hc, ctx, fingerprints[2], 1)
141-
require.Equal(t, 3, hc.TestingNumTableReads())
146+
require.Equal(t, ignoredReads+3, hc.TestingNumTableReads())
142147

143148
// Access the first fingerprint again. Since it was evicted, this should
144149
// result in another table read on the first access.
145150
requireHintsCount(t, hc, ctx, fingerprints[0], 1)
146-
require.Equal(t, 4, hc.TestingNumTableReads())
151+
require.Equal(t, ignoredReads+4, hc.TestingNumTableReads())
147152

148153
// Access the second fingerprint. It should have been evicted by now, so
149154
// another table read on the first access.
150155
requireHintsCount(t, hc, ctx, fingerprints[1], 1)
151-
require.Equal(t, 5, hc.TestingNumTableReads())
156+
require.Equal(t, ignoredReads+5, hc.TestingNumTableReads())
152157

153158
// The first and second fingerprint should now be cached, so accessing them
154159
// again should not increase table reads.
155160
requireHintsCount(t, hc, ctx, fingerprints[0], 1)
156161
requireHintsCount(t, hc, ctx, fingerprints[1], 1)
157-
require.Equal(t, 5, hc.TestingNumTableReads())
162+
require.Equal(t, ignoredReads+5, hc.TestingNumTableReads())
158163

159164
// Access the third fingerprint again - should have been evicted, so the first
160165
// access should cause a table read.
161166
requireHintsCount(t, hc, ctx, fingerprints[2], 1)
162167
requireHintsCount(t, hc, ctx, fingerprints[2], 1)
163-
require.Equal(t, 6, hc.TestingNumTableReads())
168+
require.Equal(t, ignoredReads+6, hc.TestingNumTableReads())
164169
}
165170

166171
// TestHintCacheInitialScan tests that a new cache correctly populates from
@@ -169,14 +174,29 @@ func TestHintCacheInitialScan(t *testing.T) {
169174
defer leaktest.AfterTest(t)()
170175
defer log.Scope(t).Close(t)
171176

177+
listenerReg := listenerutil.NewListenerRegistry()
178+
defer listenerReg.Close()
179+
stickyVFSRegistry := fs.NewStickyRegistry()
180+
172181
ctx := context.Background()
173-
srv, db, _ := serverutils.StartServer(t, base.TestServerArgs{})
174-
defer srv.Stopper().Stop(ctx)
175-
ts := srv.ApplicationLayer()
176-
r := sqlutils.MakeSQLRunner(db)
177-
setTestDefaults(t, srv)
182+
tc := serverutils.StartCluster(t, 1, base.TestClusterArgs{
183+
ServerArgs: base.TestServerArgs{
184+
Knobs: base.TestingKnobs{
185+
Server: &server.TestingKnobs{
186+
// Sticky vfs is needed for cluster restart.
187+
StickyVFSRegistry: stickyVFSRegistry,
188+
},
189+
},
190+
},
191+
// A listener is required for cluster restart.
192+
ReusableListenerReg: listenerReg,
193+
})
194+
defer tc.Stopper().Stop(ctx)
195+
ts := tc.ApplicationLayer(0)
196+
r := sqlutils.MakeSQLRunner(ts.SQLConn(t))
197+
setTestDefaults(t, tc.Server(0))
178198

179-
// Insert multiple hints into the system table BEFORE creating the cache.
199+
// Insert multiple hints into the system table.
180200
fingerprints := []string{
181201
"SELECT a FROM t WHERE b = $1",
182202
"SELECT c FROM t WHERE d = $1",
@@ -191,9 +211,12 @@ func TestHintCacheInitialScan(t *testing.T) {
191211
// Insert multiple hints for the first fingerprint.
192212
insertStatementHint(t, r, fingerprints[0])
193213

194-
// Now create a hints cache - it should populate from existing hints during
195-
// initial scan.
196-
hc := createHintsCache(t, ctx, ts)
214+
// Restart the cluster to trigger the initial scan for the hints cache.
215+
require.NoError(t, tc.Restart())
216+
ts = tc.ApplicationLayer(0)
217+
r = sqlutils.MakeSQLRunner(ts.SQLConn(t))
218+
219+
hc := ts.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
197220

198221
// The cache should have all the hashes once the initial scan completes.
199222
testutils.SucceedsSoon(t, func() error {
@@ -246,8 +269,8 @@ func TestHintCacheMultiNode(t *testing.T) {
246269
r2 := sqlutils.MakeSQLRunner(tc.ServerConn(2))
247270
setTestDefaults(t, tc.Server(0))
248271

249-
// Create a hints cache on node 0.
250-
hc := createHintsCache(t, ctx, ts)
272+
// Use the hints cache from node 0.
273+
hc := ts.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
251274
require.Equal(t, 0, hc.TestingHashCount())
252275

253276
// Insert hints from node 1.
@@ -307,9 +330,8 @@ func TestHintCacheMultiTenant(t *testing.T) {
307330
r1 := sqlutils.MakeSQLRunner(tenantConn1)
308331
r2 := sqlutils.MakeSQLRunner(tenantConn2)
309332

310-
// Create hints caches for both tenants.
311-
hc1 := createHintsCache(t, ctx, tenant1)
312-
hc2 := createHintsCache(t, ctx, tenant2)
333+
hc1 := tenant1.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
334+
hc2 := tenant2.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
313335
require.Equal(t, 0, hc1.TestingHashCount())
314336
require.Equal(t, 0, hc2.TestingHashCount())
315337

@@ -390,8 +412,7 @@ func TestHintCacheGeneration(t *testing.T) {
390412
r := sqlutils.MakeSQLRunner(db)
391413
setTestDefaults(t, srv)
392414

393-
// Create a hints cache.
394-
hc := createHintsCache(t, ctx, ts)
415+
hc := ts.ExecutorConfig().(sql.ExecutorConfig).StatementHintsCache
395416

396417
// Helper that retrieves the generation and verifies that it doesn't change
397418
// over a short period.
@@ -475,21 +496,6 @@ func setTestDefaults(t *testing.T, srv serverutils.TestServerInterface) {
475496
r.Exec(t, "SET CLUSTER SETTING kv.rangefeed.closed_timestamp_refresh_interval = '10ms'")
476497
}
477498

478-
func createHintsCache(
479-
t *testing.T, ctx context.Context, ts serverutils.ApplicationLayerInterface,
480-
) *hints.StatementHintsCache {
481-
hc := hints.NewStatementHintsCache(
482-
ts.Clock(),
483-
ts.RangeFeedFactory().(*rangefeed.Factory),
484-
ts.AppStopper(),
485-
ts.Codec(),
486-
ts.InternalDB().(descs.DB),
487-
ts.ClusterSettings(),
488-
)
489-
require.NoError(t, hc.Start(ctx, ts.SystemTableIDResolver().(catalog.SystemTableIDResolver)))
490-
return hc
491-
}
492-
493499
// waitForUpdateOnFingerprintHash waits for the cache to automatically refresh
494500
// to reflect a hint insertion or deletion.
495501
func waitForUpdateOnFingerprintHash(

0 commit comments

Comments
 (0)