mirror of
https://github.com/luxfi/zapdb.git
synced 2026-10-30 15:12:20 +00:00
## Problem Resolving more lint errors. ## Solution We fix some basic gosimple and gofmt lint errors. We raise the lll line length to 120 (as in Dgraph repo).
289 lines
8.0 KiB
Go
289 lines
8.0 KiB
Go
/*
|
|
* Copyright 2017 Dgraph Labs, Inc. and Contributors
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package badger
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"io"
|
|
|
|
"github.com/dgraph-io/badger/v3/pb"
|
|
"github.com/dgraph-io/badger/v3/y"
|
|
"github.com/dgraph-io/ristretto/z"
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// flushThreshold determines when a buffer will be flushed. When performing a
|
|
// backup/restore, the entries will be batched up until the total size of batch
|
|
// is more than flushThreshold or entry size (without the value size) is more
|
|
// than the maxBatchSize.
|
|
const flushThreshold = 100 << 20
|
|
|
|
// Backup dumps a protobuf-encoded list of all entries in the database into the
|
|
// given writer, that are newer than or equal to the specified version. It
|
|
// returns a timestamp (version) indicating the version of last entry that is
|
|
// dumped, which after incrementing by 1 can be passed into later invocation to
|
|
// generate incremental backup of entries that have been added/modified since
|
|
// the last invocation of DB.Backup().
|
|
// DB.Backup is a wrapper function over Stream.Backup to generate full and
|
|
// incremental backups of the DB. For more control over how many goroutines are
|
|
// used to generate the backup, or if you wish to backup only a certain range
|
|
// of keys, use Stream.Backup directly.
|
|
func (db *DB) Backup(w io.Writer, since uint64) (uint64, error) {
|
|
stream := db.NewStream()
|
|
stream.LogPrefix = "DB.Backup"
|
|
stream.SinceTs = since
|
|
return stream.Backup(w, since)
|
|
}
|
|
|
|
// Backup dumps a protobuf-encoded list of all entries in the database into the
|
|
// given writer, that are newer than or equal to the specified version. It returns a
|
|
// timestamp(version) indicating the version of last entry that was dumped, which
|
|
// after incrementing by 1 can be passed into a later invocation to generate an
|
|
// incremental dump of entries that have been added/modified since the last
|
|
// invocation of Stream.Backup().
|
|
//
|
|
// This can be used to backup the data in a database at a given point in time.
|
|
func (stream *Stream) Backup(w io.Writer, since uint64) (uint64, error) {
|
|
stream.KeyToList = func(key []byte, itr *Iterator) (*pb.KVList, error) {
|
|
list := &pb.KVList{}
|
|
a := itr.Alloc
|
|
for ; itr.Valid(); itr.Next() {
|
|
item := itr.Item()
|
|
if !bytes.Equal(item.Key(), key) {
|
|
return list, nil
|
|
}
|
|
if item.Version() < since {
|
|
return nil, errors.Errorf("Backup: Item Version: %d less than sinceTs: %d",
|
|
item.Version(), since)
|
|
}
|
|
|
|
var valCopy []byte
|
|
if !item.IsDeletedOrExpired() {
|
|
// No need to copy value, if item is deleted or expired.
|
|
err := item.Value(func(val []byte) error {
|
|
valCopy = a.Copy(val)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
stream.db.opt.Errorf("Key [%x, %d]. Error while fetching value [%v]\n",
|
|
item.Key(), item.Version(), err)
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// clear txn bits
|
|
meta := item.meta &^ (bitTxn | bitFinTxn)
|
|
kv := y.NewKV(a)
|
|
*kv = pb.KV{
|
|
Key: a.Copy(item.Key()),
|
|
Value: valCopy,
|
|
UserMeta: a.Copy([]byte{item.UserMeta()}),
|
|
Version: item.Version(),
|
|
ExpiresAt: item.ExpiresAt(),
|
|
Meta: a.Copy([]byte{meta}),
|
|
}
|
|
list.Kv = append(list.Kv, kv)
|
|
|
|
switch {
|
|
case item.DiscardEarlierVersions():
|
|
// If we need to discard earlier versions of this item, add a delete
|
|
// marker just below the current version.
|
|
list.Kv = append(list.Kv, &pb.KV{
|
|
Key: item.KeyCopy(nil),
|
|
Version: item.Version() - 1,
|
|
Meta: []byte{bitDelete},
|
|
})
|
|
return list, nil
|
|
|
|
case item.IsDeletedOrExpired():
|
|
return list, nil
|
|
}
|
|
}
|
|
return list, nil
|
|
}
|
|
|
|
var maxVersion uint64
|
|
stream.Send = func(buf *z.Buffer) error {
|
|
list, err := BufferToKVList(buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
out := list.Kv[:0]
|
|
for _, kv := range list.Kv {
|
|
if maxVersion < kv.Version {
|
|
maxVersion = kv.Version
|
|
}
|
|
if !kv.StreamDone {
|
|
// Don't pick stream done changes.
|
|
out = append(out, kv)
|
|
}
|
|
}
|
|
list.Kv = out
|
|
return writeTo(list, w)
|
|
}
|
|
|
|
if err := stream.Orchestrate(context.Background()); err != nil {
|
|
return 0, err
|
|
}
|
|
return maxVersion, nil
|
|
}
|
|
|
|
func writeTo(list *pb.KVList, w io.Writer) error {
|
|
if err := binary.Write(w, binary.LittleEndian, uint64(proto.Size(list))); err != nil {
|
|
return err
|
|
}
|
|
buf, err := proto.Marshal(list)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write(buf)
|
|
return err
|
|
}
|
|
|
|
// KVLoader is used to write KVList objects in to badger. It can be used to restore a backup.
|
|
type KVLoader struct {
|
|
db *DB
|
|
throttle *y.Throttle
|
|
entries []*Entry
|
|
entriesSize int64
|
|
totalSize int64
|
|
}
|
|
|
|
// NewKVLoader returns a new instance of KVLoader.
|
|
func (db *DB) NewKVLoader(maxPendingWrites int) *KVLoader {
|
|
return &KVLoader{
|
|
db: db,
|
|
throttle: y.NewThrottle(maxPendingWrites),
|
|
entries: make([]*Entry, 0, db.opt.maxBatchCount),
|
|
}
|
|
}
|
|
|
|
// Set writes the key-value pair to the database.
|
|
func (l *KVLoader) Set(kv *pb.KV) error {
|
|
var userMeta, meta byte
|
|
if len(kv.UserMeta) > 0 {
|
|
userMeta = kv.UserMeta[0]
|
|
}
|
|
if len(kv.Meta) > 0 {
|
|
meta = kv.Meta[0]
|
|
}
|
|
e := &Entry{
|
|
Key: y.KeyWithTs(kv.Key, kv.Version),
|
|
Value: kv.Value,
|
|
UserMeta: userMeta,
|
|
ExpiresAt: kv.ExpiresAt,
|
|
meta: meta,
|
|
}
|
|
estimatedSize := e.estimateSizeAndSetThreshold(l.db.valueThreshold())
|
|
// Flush entries if inserting the next entry would overflow the transactional limits.
|
|
if int64(len(l.entries))+1 >= l.db.opt.maxBatchCount ||
|
|
l.entriesSize+estimatedSize >= l.db.opt.maxBatchSize ||
|
|
l.totalSize >= flushThreshold {
|
|
if err := l.send(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
l.entries = append(l.entries, e)
|
|
l.entriesSize += estimatedSize
|
|
l.totalSize += estimatedSize + int64(len(e.Value))
|
|
return nil
|
|
}
|
|
|
|
func (l *KVLoader) send() error {
|
|
if err := l.throttle.Do(); err != nil {
|
|
return err
|
|
}
|
|
if err := l.db.batchSetAsync(l.entries, func(err error) {
|
|
l.throttle.Done(err)
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
l.entries = make([]*Entry, 0, l.db.opt.maxBatchCount)
|
|
l.entriesSize = 0
|
|
l.totalSize = 0
|
|
return nil
|
|
}
|
|
|
|
// Finish is meant to be called after all the key-value pairs have been loaded.
|
|
func (l *KVLoader) Finish() error {
|
|
if len(l.entries) > 0 {
|
|
if err := l.send(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return l.throttle.Finish()
|
|
}
|
|
|
|
// Load reads a protobuf-encoded list of all entries from a reader and writes
|
|
// them to the database. This can be used to restore the database from a backup
|
|
// made by calling DB.Backup(). If more complex logic is needed to restore a badger
|
|
// backup, the KVLoader interface should be used instead.
|
|
//
|
|
// DB.Load() should be called on a database that is not running any other
|
|
// concurrent transactions while it is running.
|
|
func (db *DB) Load(r io.Reader, maxPendingWrites int) error {
|
|
br := bufio.NewReaderSize(r, 16<<10)
|
|
unmarshalBuf := make([]byte, 1<<10)
|
|
|
|
ldr := db.NewKVLoader(maxPendingWrites)
|
|
for {
|
|
var sz uint64
|
|
err := binary.Read(br, binary.LittleEndian, &sz)
|
|
if err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
if cap(unmarshalBuf) < int(sz) {
|
|
unmarshalBuf = make([]byte, sz)
|
|
}
|
|
|
|
if _, err = io.ReadFull(br, unmarshalBuf[:sz]); err != nil {
|
|
return err
|
|
}
|
|
|
|
list := &pb.KVList{}
|
|
if err := proto.Unmarshal(unmarshalBuf[:sz], list); err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, kv := range list.Kv {
|
|
if err := ldr.Set(kv); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Update nextTxnTs, memtable stores this
|
|
// timestamp in badger head when flushed.
|
|
if kv.Version >= db.orc.nextTxnTs {
|
|
db.orc.nextTxnTs = kv.Version + 1
|
|
}
|
|
}
|
|
}
|
|
|
|
if err := ldr.Finish(); err != nil {
|
|
return err
|
|
}
|
|
db.orc.txnMark.Done(db.orc.nextTxnTs - 1)
|
|
return nil
|
|
}
|