Skip to content

Commit 6c6fd75

Browse files
committed
cache: don't mark blocks as accessed for background work
We don't want block cache hits for compactions to cause the respective block to stay in the cache longer (in fact, it is about to become obsolete). This change adds a `Peek()` method to the block cache which is like `Get()` but does not mark the block as recently accessed.
1 parent 6e9ddf2 commit 6c6fd75

File tree

6 files changed

+91
-28
lines changed

6 files changed

+91
-28
lines changed

db_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,7 +858,7 @@ func TestMemTableReservation(t *testing.T) {
858858
t.Fatalf("expected 2 refs, but found %d", refs)
859859
}
860860
// Verify the memtable reservation has caused our test block to be evicted.
861-
if cv := tmpHandle.Get(base.DiskFileNum(0), 0); cv != nil {
861+
if cv := tmpHandle.Peek(base.DiskFileNum(0), 0); cv != nil {
862862
t.Fatalf("expected failure, but found success: %#v", cv)
863863
}
864864

internal/cache/cache.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -259,15 +259,19 @@ func (c *Handle) Cache() *Cache {
259259
return c.cache
260260
}
261261

262+
// Peek retrieves the cache value for the specified file and offset, returning
263+
// nil if no value is present. Peek does not affect the state of the cache (it
264+
// does not "count" as an access as far as the cache replacement is concerned).
265+
func (c *Handle) Peek(fileNum base.DiskFileNum, offset uint64) *Value {
266+
k := makeKey(c.id, fileNum, offset)
267+
return c.cache.getShard(k).get(k, true /* peekOnly */)
268+
}
269+
262270
// Get retrieves the cache value for the specified file and offset, returning
263271
// nil if no value is present.
264272
func (c *Handle) Get(fileNum base.DiskFileNum, offset uint64) *Value {
265273
k := makeKey(c.id, fileNum, offset)
266-
cv, re := c.cache.getShard(k).getWithMaybeReadEntry(k, false /* desireReadEntry */)
267-
if invariants.Enabled && re != nil {
268-
panic("readEntry should be nil")
269-
}
270-
return cv
274+
return c.cache.getShard(k).get(k, false /* peekOnly */)
271275
}
272276

273277
// GetWithReadHandle retrieves the cache value for the specified handleID, fileNum
@@ -304,7 +308,7 @@ func (c *Handle) GetWithReadHandle(
304308
err error,
305309
) {
306310
k := makeKey(c.id, fileNum, offset)
307-
cv, re := c.cache.getShard(k).getWithMaybeReadEntry(k, true /* desireReadEntry */)
311+
cv, re := c.cache.getShard(k).getWithReadEntry(k)
308312
if cv != nil {
309313
return cv, ReadHandle{}, 0, 0, true, nil
310314
}

internal/cache/cache_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,46 @@ func setTestValue(c *Handle, fileNum base.DiskFileNum, offset uint64, s string,
6969
v.Release()
7070
}
7171

72+
// TestCachePeek verifies that Peek does not update the recently accessed status
73+
// of an entry.
74+
func TestCachePeek(t *testing.T) {
75+
const size = 10
76+
cache := NewWithShards(size, 1)
77+
defer cache.Unref()
78+
h := cache.NewHandle()
79+
defer h.Close()
80+
81+
for i := range size {
82+
setTestValue(h, 0, uint64(i), "a", 1)
83+
}
84+
for i := range size / 2 {
85+
v := h.Get(base.DiskFileNum(0), uint64(i))
86+
if v == nil {
87+
t.Fatalf("expected to find block %d", i)
88+
}
89+
v.Release()
90+
}
91+
for i := size / 2; i < size; i++ {
92+
v := h.Peek(base.DiskFileNum(0), uint64(i))
93+
if v == nil {
94+
t.Fatalf("expected to find block %d", i)
95+
}
96+
v.Release()
97+
}
98+
// Now add more entries to cause eviction.
99+
for i := range size / 4 {
100+
setTestValue(h, 0, uint64(size+i), "a", 1)
101+
}
102+
// Verify that the Gets still find their values, despite the Peeks.
103+
for i := range size / 2 {
104+
v := h.Get(base.DiskFileNum(0), uint64(i))
105+
if v == nil {
106+
t.Fatalf("expected to find block %d", i)
107+
}
108+
v.Release()
109+
}
110+
}
111+
72112
func TestCacheDelete(t *testing.T) {
73113
cache := NewWithShards(100, 1)
74114
defer cache.Unref()

internal/cache/clockpro.go

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -130,32 +130,50 @@ func (c *shard) init(maxSize int64) {
130130
c.readShard.Init(c)
131131
}
132132

133-
// getWithMaybeReadEntry is the internal helper for implementing
133+
// get returns the cache value if it exists.
134+
//
135+
// If peekOnly is true, the state of the cache is not modified to reflect the
136+
// access.
137+
func (c *shard) get(k key, peekOnly bool) *Value {
138+
c.mu.RLock()
139+
if e, _ := c.blocks.Get(k); e != nil {
140+
if value := e.acquireValue(); value != nil {
141+
// Note: we Load first to avoid an atomic XCHG when not necessary.
142+
if !peekOnly && !e.referenced.Load() {
143+
e.referenced.Store(true)
144+
}
145+
c.mu.RUnlock()
146+
c.hits.Add(1)
147+
return value
148+
}
149+
}
150+
c.mu.RUnlock()
151+
c.misses.Add(1)
152+
return nil
153+
}
154+
155+
// getWithReadEntry is the internal helper for implementing
134156
// Cache.{Get,GetWithReadHandle}. When desireReadEntry is true, and the block
135157
// is not in the cache (nil Value), a non-nil readEntry is returned (in which
136158
// case the caller is responsible to dereference the entry, via one of
137159
// unrefAndTryRemoveFromMap(), setReadValue(), setReadError()).
138-
func (c *shard) getWithMaybeReadEntry(k key, desireReadEntry bool) (*Value, *readEntry) {
160+
func (c *shard) getWithReadEntry(k key) (*Value, *readEntry) {
139161
c.mu.RLock()
140-
var value *Value
141162
if e, _ := c.blocks.Get(k); e != nil {
142-
value = e.acquireValue()
143-
// Note: we Load first to avoid an atomic XCHG when not necessary.
144-
if value != nil && !e.referenced.Load() {
145-
e.referenced.Store(true)
163+
if value := e.acquireValue(); value != nil {
164+
// Note: we Load first to avoid an atomic XCHG when not necessary.
165+
if !e.referenced.Load() {
166+
e.referenced.Store(true)
167+
}
168+
c.mu.RUnlock()
169+
c.hits.Add(1)
170+
return value, nil
146171
}
147172
}
148-
var re *readEntry
149-
if value == nil && desireReadEntry {
150-
re = c.readShard.acquireReadEntry(k)
151-
}
173+
re := c.readShard.acquireReadEntry(k)
152174
c.mu.RUnlock()
153-
if value == nil {
154-
c.misses.Add(1)
155-
} else {
156-
c.hits.Add(1)
157-
}
158-
return value, re
175+
c.misses.Add(1)
176+
return nil, re
159177
}
160178

161179
func (c *shard) set(k key, value *Value, markAccessed bool) {

internal/cache/read_shard_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func newTestReader(
5050
}
5151

5252
func (r *testReader) getAsync(shard *shard) *string {
53-
v, re := shard.getWithMaybeReadEntry(r.key, true /* desireReadEntry */)
53+
v, re := shard.getWithReadEntry(r.key)
5454
if v != nil {
5555
str := string(v.RawBuffer())
5656
v.Release()

sstable/block/block.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ func (r *Reader) Read(
366366
// reading a block.
367367
if r.opts.CacheOpts.CacheHandle == nil || env.BufferPool != nil {
368368
if r.opts.CacheOpts.CacheHandle != nil {
369-
if cv := r.opts.CacheOpts.CacheHandle.Get(r.opts.CacheOpts.FileNum, bh.Offset); cv != nil {
369+
if cv := r.opts.CacheOpts.CacheHandle.Peek(r.opts.CacheOpts.FileNum, bh.Offset); cv != nil {
370370
recordCacheHit(ctx, env, readHandle, bh, kind)
371371
return CacheBufferHandle(cv), nil
372372
}
@@ -528,12 +528,13 @@ func (r *Reader) Readable() objstorage.Readable {
528528
return r.readable
529529
}
530530

531-
// GetFromCache retrieves the block from the cache, if it is present.
531+
// GetFromCache retrieves the block from the cache, if it is present. It does
532+
// not mark the block as recently used.
532533
//
533534
// Users should prefer using Read, which handles reading from object storage on
534535
// a cache miss.
535536
func (r *Reader) GetFromCache(bh Handle) *cache.Value {
536-
return r.opts.CacheOpts.CacheHandle.Get(r.opts.CacheOpts.FileNum, bh.Offset)
537+
return r.opts.CacheOpts.CacheHandle.Peek(r.opts.CacheOpts.FileNum, bh.Offset)
537538
}
538539

539540
// UsePreallocatedReadHandle returns a ReadHandle that reads from the reader and

0 commit comments

Comments
 (0)