mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
73 lines
1.4 KiB
Go
73 lines
1.4 KiB
Go
|
|
package litestream
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/binary"
|
||
|
|
"sync"
|
||
|
|
|
||
|
|
"golang.org/x/sync/singleflight"
|
||
|
|
|
||
|
|
"github.com/superfly/ltx"
|
||
|
|
)
|
||
|
|
|
||
|
|
type pageCache struct {
|
||
|
|
single singleflight.Group
|
||
|
|
pages map[uint32]cachedPage // +checklocks:mtx
|
||
|
|
size int
|
||
|
|
mtx sync.Mutex
|
||
|
|
}
|
||
|
|
|
||
|
|
type cachedPage struct {
|
||
|
|
data []byte
|
||
|
|
txid ltx.TXID
|
||
|
|
}
|
||
|
|
|
||
|
|
func (c *pageCache) getOrFetch(pgno uint32, maxTXID ltx.TXID, fetch func() (any, error)) ([]byte, error) {
|
||
|
|
if c.size >= 0 {
|
||
|
|
c.mtx.Lock()
|
||
|
|
if c.pages == nil {
|
||
|
|
c.pages = map[uint32]cachedPage{}
|
||
|
|
}
|
||
|
|
page := c.pages[pgno]
|
||
|
|
c.mtx.Unlock()
|
||
|
|
|
||
|
|
if page.txid == maxTXID {
|
||
|
|
return page.data, nil
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var key [12]byte
|
||
|
|
binary.LittleEndian.PutUint32(key[0:], pgno)
|
||
|
|
binary.LittleEndian.PutUint64(key[4:], uint64(maxTXID))
|
||
|
|
v, err, _ := c.single.Do(string(key[:]), fetch)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
page := cachedPage{v.([]byte), maxTXID}
|
||
|
|
if c.size >= 0 {
|
||
|
|
c.mtx.Lock()
|
||
|
|
c.evict(len(page.data))
|
||
|
|
c.pages[pgno] = page
|
||
|
|
c.mtx.Unlock()
|
||
|
|
}
|
||
|
|
return page.data, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// +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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|