mirror of
https://github.com/luxfi/zapdb.git
synced 2026-10-30 15:12:20 +00:00
The module has been github.com/luxfi/zapdb for a while, but the package inside it still declared itself badger, so every consumer had to write zapdb "github.com/luxfi/zapdb" // package badger and the alias existed only to paper over the mismatch. 56 root files and the 134 qualified references inside the repo now say zapdb. Backward compatible in both directions: an existing consumer with the alias keeps compiling (the alias is now merely redundant), and one without it starts compiling instead of failing on an undefined zapdb. Directory badger/ and its cmd package are untouched — that is the CLI, and its binary is named badger. Build and vet clean; suite green except TestZRestore, which wants a /tmp/zb/src benchmark fixture and fails identically without this change. Co-authored-by: Hanzo Dev <[email protected]>
158 lines
3.5 KiB
Go
158 lines
3.5 KiB
Go
/*
|
|
* SPDX-FileCopyrightText: © 2017-2025 Istari Digital, Inc.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package zapdb
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"sync"
|
|
|
|
"github.com/dgraph-io/ristretto/v2/z"
|
|
"github.com/luxfi/zapdb/y"
|
|
)
|
|
|
|
// discardStats keeps track of the amount of data that could be discarded for
|
|
// a given logfile.
|
|
type discardStats struct {
|
|
sync.Mutex
|
|
|
|
*z.MmapFile
|
|
opt Options
|
|
nextEmptySlot int
|
|
}
|
|
|
|
const discardFname string = "DISCARD"
|
|
|
|
func InitDiscardStats(opt Options) (*discardStats, error) {
|
|
fname := filepath.Join(opt.ValueDir, discardFname)
|
|
|
|
// 1MB file can store 65.536 discard entries. Each entry is 16 bytes.
|
|
mf, err := z.OpenMmapFile(fname, os.O_CREATE|os.O_RDWR, 1<<20)
|
|
lf := &discardStats{
|
|
MmapFile: mf,
|
|
opt: opt,
|
|
}
|
|
if err == z.NewFile {
|
|
// We don't need to zero out the entire 1MB.
|
|
lf.zeroOut()
|
|
|
|
} else if err != nil {
|
|
return nil, y.Wrapf(err, "while opening file: %s\n", discardFname)
|
|
}
|
|
|
|
for slot := 0; slot < lf.maxSlot(); slot++ {
|
|
if lf.get(16*slot) == 0 {
|
|
lf.nextEmptySlot = slot
|
|
break
|
|
}
|
|
}
|
|
sort.Sort(lf)
|
|
opt.Infof("Discard stats nextEmptySlot: %d\n", lf.nextEmptySlot)
|
|
return lf, nil
|
|
}
|
|
|
|
func (lf *discardStats) Len() int {
|
|
return lf.nextEmptySlot
|
|
}
|
|
func (lf *discardStats) Less(i, j int) bool {
|
|
return lf.get(16*i) < lf.get(16*j)
|
|
}
|
|
func (lf *discardStats) Swap(i, j int) {
|
|
left := lf.Data[16*i : 16*i+16]
|
|
right := lf.Data[16*j : 16*j+16]
|
|
var tmp [16]byte
|
|
copy(tmp[:], left)
|
|
copy(left, right)
|
|
copy(right, tmp[:])
|
|
}
|
|
|
|
// offset is not slot.
|
|
func (lf *discardStats) get(offset int) uint64 {
|
|
return binary.BigEndian.Uint64(lf.Data[offset : offset+8])
|
|
}
|
|
func (lf *discardStats) set(offset int, val uint64) {
|
|
binary.BigEndian.PutUint64(lf.Data[offset:offset+8], val)
|
|
}
|
|
|
|
// zeroOut would zero out the next slot.
|
|
func (lf *discardStats) zeroOut() {
|
|
lf.set(lf.nextEmptySlot*16, 0)
|
|
lf.set(lf.nextEmptySlot*16+8, 0)
|
|
}
|
|
|
|
func (lf *discardStats) maxSlot() int {
|
|
return len(lf.Data) / 16
|
|
}
|
|
|
|
// Update would update the discard stats for the given file id. If discard is
|
|
// 0, it would return the current value of discard for the file. If discard is
|
|
// < 0, it would set the current value of discard to zero for the file.
|
|
func (lf *discardStats) Update(fidu uint32, discard int64) int64 {
|
|
fid := uint64(fidu)
|
|
lf.Lock()
|
|
defer lf.Unlock()
|
|
|
|
idx := sort.Search(lf.nextEmptySlot, func(slot int) bool {
|
|
return lf.get(slot*16) >= fid
|
|
})
|
|
if idx < lf.nextEmptySlot && lf.get(idx*16) == fid {
|
|
off := idx*16 + 8
|
|
curDisc := lf.get(off)
|
|
if discard == 0 {
|
|
return int64(curDisc)
|
|
}
|
|
if discard < 0 {
|
|
lf.set(off, 0)
|
|
return 0
|
|
}
|
|
lf.set(off, curDisc+uint64(discard))
|
|
return int64(curDisc + uint64(discard))
|
|
}
|
|
if discard <= 0 {
|
|
// No need to add a new entry.
|
|
return 0
|
|
}
|
|
|
|
// Could not find the fid. Add the entry.
|
|
idx = lf.nextEmptySlot
|
|
lf.set(idx*16, fid)
|
|
lf.set(idx*16+8, uint64(discard))
|
|
|
|
// Move to next slot.
|
|
lf.nextEmptySlot++
|
|
for lf.nextEmptySlot >= lf.maxSlot() {
|
|
y.Check(lf.Truncate(2 * int64(len(lf.Data))))
|
|
}
|
|
lf.zeroOut()
|
|
|
|
sort.Sort(lf)
|
|
return discard
|
|
}
|
|
|
|
func (lf *discardStats) Iterate(f func(fid, stats uint64)) {
|
|
for slot := 0; slot < lf.nextEmptySlot; slot++ {
|
|
idx := 16 * slot
|
|
f(lf.get(idx), lf.get(idx+8))
|
|
}
|
|
}
|
|
|
|
// MaxDiscard returns the file id with maximum discard bytes.
|
|
func (lf *discardStats) MaxDiscard() (uint32, int64) {
|
|
lf.Lock()
|
|
defer lf.Unlock()
|
|
|
|
var maxFid, maxVal uint64
|
|
lf.Iterate(func(fid, val uint64) {
|
|
if maxVal < val {
|
|
maxVal = val
|
|
maxFid = fid
|
|
}
|
|
})
|
|
return uint32(maxFid), int64(maxVal)
|
|
}
|