Files
sqlite3/litestream/cache.go

71 lines
1.5 KiB
Go
Raw Permalink Normal View History

2025-11-20 16:54:51 +00:00
package litestream
import (
2025-12-16 15:36:13 +00:00
"context"
"fmt"
2025-11-20 16:54:51 +00:00
"sync"
2025-12-16 15:36:13 +00:00
"github.com/benbjohnson/litestream"
2025-11-20 16:54:51 +00:00
"github.com/superfly/ltx"
)
type pageCache struct {
2025-12-16 15:36:13 +00:00
pages map[uint32]cachedPage // +checklocks:mtx
size int
mtx sync.Mutex
2025-11-20 16:54:51 +00:00
}
type cachedPage struct {
data []byte
txid ltx.TXID
}
2025-12-16 15:36:13 +00:00
func (c *pageCache) getOrFetch(ctx context.Context, client ReplicaClient, pgno uint32, elem ltx.PageIndexElem) ([]byte, error) {
if c.size > 0 {
2025-11-20 16:54:51 +00:00
c.mtx.Lock()
page := c.pages[pgno]
c.mtx.Unlock()
2025-12-16 15:36:13 +00:00
if page.txid == elem.MaxTXID {
2025-11-20 16:54:51 +00:00
return page.data, nil
}
}
2025-12-16 15:36:13 +00:00
h, data, err := litestream.FetchPage(ctx, client, elem.Level, elem.MinTXID, elem.MaxTXID, elem.Offset, elem.Size)
2025-11-20 16:54:51 +00:00
if err != nil {
2025-12-16 15:36:13 +00:00
return nil, fmt.Errorf("fetch page: %w", err)
}
if pgno != h.Pgno {
return nil, fmt.Errorf("fetch page: want %d, got %d", pgno, h.Pgno)
2025-11-20 16:54:51 +00:00
}
2025-12-16 15:36:13 +00:00
if c.size > 0 {
2025-11-20 16:54:51 +00:00
c.mtx.Lock()
2025-12-16 15:36:13 +00:00
if c.pages != nil {
c.evict(len(data))
} else {
c.pages = map[uint32]cachedPage{}
}
c.pages[pgno] = cachedPage{data, elem.MaxTXID}
2025-11-20 16:54:51 +00:00
c.mtx.Unlock()
}
2025-12-16 15:36:13 +00:00
return data, nil
2025-11-20 16:54:51 +00:00
}
// +checklocks:c.mtx
func (c *pageCache) evict(pageSize int) {
// Evict random keys until we're under the maximum size.
// SQLite has its own page cache, which it will use for each connection.
// Since this is a second layer of shared cache,
// random eviction is probably good enough.
if pageSize*len(c.pages) < c.size {
return
}
for key := range c.pages {
delete(c.pages, key)
if pageSize*len(c.pages) < c.size {
return
}
}
}