Skip to content

Commit

Permalink
Added tests for backup engine
Browse files Browse the repository at this point in the history
  • Loading branch information
linxGnu committed Oct 15, 2020
1 parent 73fd48e commit 1dcbf1c
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 93 deletions.
114 changes: 34 additions & 80 deletions backup.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,78 +7,19 @@ import (
"unsafe"
)

// BackupEngineInfo represents the information about the backups
// in a backup engine instance. Use this to get the state of the
// backup like number of backups and their ids and timestamps etc.
type BackupEngineInfo struct {
c *C.rocksdb_backup_engine_info_t
}

// GetCount gets the number backsup available.
func (b *BackupEngineInfo) GetCount() int {
return int(C.rocksdb_backup_engine_info_count(b.c))
}

// GetTimestamp gets the timestamp at which the backup index was taken.
func (b *BackupEngineInfo) GetTimestamp(index int) int64 {
return int64(C.rocksdb_backup_engine_info_timestamp(b.c, C.int(index)))
}

// GetBackupID gets an id that uniquely identifies a backup
// regardless of its position.
func (b *BackupEngineInfo) GetBackupID(index int) int64 {
return int64(C.rocksdb_backup_engine_info_backup_id(b.c, C.int(index)))
}

// GetSize get the size of the backup in bytes.
func (b *BackupEngineInfo) GetSize(index int) int64 {
return int64(C.rocksdb_backup_engine_info_size(b.c, C.int(index)))
}

// GetNumFiles gets the number of files in the backup index.
func (b *BackupEngineInfo) GetNumFiles(index int) int32 {
return int32(C.rocksdb_backup_engine_info_number_files(b.c, C.int(index)))
}

// Destroy destroys the backup engine info instance.
func (b *BackupEngineInfo) Destroy() {
C.rocksdb_backup_engine_info_destroy(b.c)
b.c = nil
}

// RestoreOptions captures the options to be used during
// restoration of a backup.
type RestoreOptions struct {
c *C.rocksdb_restore_options_t
}

// NewRestoreOptions creates a RestoreOptions instance.
func NewRestoreOptions() *RestoreOptions {
return &RestoreOptions{
c: C.rocksdb_restore_options_create(),
}
}

// SetKeepLogFiles is used to set or unset the keep_log_files option
// If true, restore won't overwrite the existing log files in wal_dir. It will
// also move all log files from archive directory to wal_dir.
// By default, this is false.
func (ro *RestoreOptions) SetKeepLogFiles(v int) {
C.rocksdb_restore_options_set_keep_log_files(ro.c, C.int(v))
}

// Destroy destroys this RestoreOptions instance.
func (ro *RestoreOptions) Destroy() {
C.rocksdb_restore_options_destroy(ro.c)
ro.c = nil
// BackupInfo represents the information about a backup.
type BackupInfo struct {
ID uint32
Timestamp int64
Size uint64
NumFiles uint32
}

// BackupEngine is a reusable handle to a RocksDB Backup, created by
// OpenBackupEngine.
type BackupEngine struct {
c *C.rocksdb_backup_engine_t
path string
opts *Options
c *C.rocksdb_backup_engine_t
db *DB
}

// OpenBackupEngine opens a backup engine with specified options.
Expand All @@ -89,34 +30,35 @@ func OpenBackupEngine(opts *Options, path string) (be *BackupEngine, err error)
bEngine := C.rocksdb_backup_engine_open(opts.c, cpath, &cErr)
if err = fromCError(cErr); err == nil {
be = &BackupEngine{
c: bEngine,
path: path,
opts: opts,
c: bEngine,
}
}

C.free(unsafe.Pointer(cpath))
return
}

// UnsafeGetBackupEngine returns the underlying c backup engine.
func (b *BackupEngine) UnsafeGetBackupEngine() unsafe.Pointer {
return unsafe.Pointer(b.c)
// CreateBackupEngine opens a backup engine from DB.
func CreateBackupEngine(db *DB) (be *BackupEngine, err error) {
if be, err = OpenBackupEngine(db.opts, db.Name()); err == nil {
be.db = db
}
return
}

// CreateNewBackup takes a new backup from db.
func (b *BackupEngine) CreateNewBackup(db *DB) (err error) {
func (b *BackupEngine) CreateNewBackup() (err error) {
var cErr *C.char
C.rocksdb_backup_engine_create_new_backup(b.c, db.c, &cErr)
C.rocksdb_backup_engine_create_new_backup(b.c, b.db.c, &cErr)
err = fromCError(cErr)
return
}

// CreateNewBackupFlush takes a new backup from db.
// Backup would be created after flushing.
func (b *BackupEngine) CreateNewBackupFlush(db *DB, flushBeforeBackup bool) (err error) {
func (b *BackupEngine) CreateNewBackupFlush(flushBeforeBackup bool) (err error) {
var cErr *C.char
C.rocksdb_backup_engine_create_new_backup_flush(b.c, db.c, boolToChar(flushBeforeBackup), &cErr)
C.rocksdb_backup_engine_create_new_backup_flush(b.c, b.db.c, boolToChar(flushBeforeBackup), &cErr)
err = fromCError(cErr)
return
}
Expand All @@ -139,10 +81,21 @@ func (b *BackupEngine) VerifyBackup(backupID uint32) (err error) {

// GetInfo gets an object that gives information about
// the backups that have already been taken
func (b *BackupEngine) GetInfo() *BackupEngineInfo {
return &BackupEngineInfo{
c: C.rocksdb_backup_engine_get_backup_info(b.c),
func (b *BackupEngine) GetInfo() (infos []BackupInfo) {
info := C.rocksdb_backup_engine_get_backup_info(b.c)

n := int(C.rocksdb_backup_engine_info_count(info))
infos = make([]BackupInfo, n)
for i := range infos {
index := C.int(i)
infos[i].ID = uint32(C.rocksdb_backup_engine_info_backup_id(info, index))
infos[i].Timestamp = int64(C.rocksdb_backup_engine_info_timestamp(info, index))
infos[i].Size = uint64(C.rocksdb_backup_engine_info_size(info, index))
infos[i].NumFiles = uint32(C.rocksdb_backup_engine_info_number_files(info, index))
}

C.rocksdb_backup_engine_info_destroy(info)
return
}

// RestoreDBFromLatestBackup restores the latest backup to dbDir. walDir
Expand Down Expand Up @@ -180,4 +133,5 @@ func (b *BackupEngine) RestoreDBFromBackup(dbDir, walDir string, ro *RestoreOpti
func (b *BackupEngine) Close() {
C.rocksdb_backup_engine_close(b.c)
b.c = nil
b.db = nil
}
115 changes: 115 additions & 0 deletions backup_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package grocksdb

import (
"io/ioutil"
"testing"

"github.com/facebookgo/ensure"
"github.com/stretchr/testify/require"
)

func TestBackupEngine(t *testing.T) {
db := newTestDB(t, "TestDBBackup", nil)
defer db.Close()

var (
givenKey = []byte("hello")
givenVal1 = []byte("")
givenVal2 = []byte("world1")
wo = NewDefaultWriteOptions()
ro = NewDefaultReadOptions()
)
defer wo.Destroy()
defer ro.Destroy()

// create
ensure.Nil(t, db.Put(wo, givenKey, givenVal1))

// retrieve
v1, err := db.Get(ro, givenKey)
defer v1.Free()
ensure.Nil(t, err)
ensure.DeepEqual(t, v1.Data(), givenVal1)

// retrieve bytes
_v1, err := db.GetBytes(ro, givenKey)
ensure.Nil(t, err)
ensure.DeepEqual(t, _v1, givenVal1)

// update
ensure.Nil(t, db.Put(wo, givenKey, givenVal2))
v2, err := db.Get(ro, givenKey)
defer v2.Free()
ensure.Nil(t, err)
ensure.DeepEqual(t, v2.Data(), givenVal2)

// retrieve pinned
v3, err := db.GetPinned(ro, givenKey)
defer v3.Destroy()
ensure.Nil(t, err)
ensure.DeepEqual(t, v3.Data(), givenVal2)

engine, err := CreateBackupEngine(db)
require.Nil(t, err)
defer engine.Close()

t.Run("createBackupAndVerify", func(t *testing.T) {
infos := engine.GetInfo()
require.Empty(t, infos)

// create first backup
require.Nil(t, engine.CreateNewBackup())

// create second backup
require.Nil(t, engine.CreateNewBackupFlush(true))

infos = engine.GetInfo()
require.Equal(t, 2, len(infos))
for i := range infos {
require.Nil(t, engine.VerifyBackup(infos[i].ID))
require.True(t, infos[i].Size > 0)
require.True(t, infos[i].NumFiles > 0)
}
})

t.Run("purge", func(t *testing.T) {
require.Nil(t, engine.PurgeOldBackups(1))

infos := engine.GetInfo()
require.Equal(t, 1, len(infos))
})

t.Run("restoreFromLatest", func(t *testing.T) {
dir, err := ioutil.TempDir("", "gorocksdb-restoreFromLatest")
require.Nil(t, err)

ro := NewRestoreOptions()
defer ro.Destroy()
require.Nil(t, engine.RestoreDBFromLatestBackup(dir, dir, ro))
require.Nil(t, engine.RestoreDBFromLatestBackup(dir, dir, ro))
})

t.Run("restoreFromBackup", func(t *testing.T) {
infos := engine.GetInfo()
require.Equal(t, 1, len(infos))

dir, err := ioutil.TempDir("", "gorocksdb-restoreFromBackup")
require.Nil(t, err)

ro := NewRestoreOptions()
defer ro.Destroy()
require.Nil(t, engine.RestoreDBFromBackup(dir, dir, ro, infos[0].ID))

// try to reopen restored db
backupDB, err := OpenDb(db.opts, dir)
require.Nil(t, err)

r := NewDefaultReadOptions()
defer r.Destroy()

v3, err := backupDB.GetPinned(r, givenKey)
defer v3.Destroy()
ensure.Nil(t, err)
ensure.DeepEqual(t, v3.Data(), givenVal2)
})
}
8 changes: 4 additions & 4 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -870,7 +870,7 @@ func (opts *Options) SetMaxBytesForLevelMultiplierAdditional(value []int) {
// filesystem like ext3 that can lose files after a reboot.
// Default: false
func (opts *Options) SetUseFsync(value bool) {
C.rocksdb_options_set_use_fsync(opts.c, C.int(btoi(value)))
C.rocksdb_options_set_use_fsync(opts.c, C.int(boolToChar(value)))
}

// UseFsync returns fsync setting.
Expand Down Expand Up @@ -1112,7 +1112,7 @@ func (opts *Options) GetArenaBlockSize() uint64 {
//
// Default: false
func (opts *Options) SetDisableAutoCompactions(value bool) {
C.rocksdb_options_set_disable_auto_compactions(opts.c, C.int(btoi(value)))
C.rocksdb_options_set_disable_auto_compactions(opts.c, C.int(boolToChar(value)))
}

// DisabledAutoCompactions returns if automatic compactions is disabled.
Expand Down Expand Up @@ -1750,7 +1750,7 @@ func (opts *Options) GetMemTablePrefixBloomSizeRatio() float64 {
//
// Default: false
func (opts *Options) SetOptimizeFiltersForHits(value bool) {
C.rocksdb_options_set_optimize_filters_for_hits(opts.c, C.int(btoi(value)))
C.rocksdb_options_set_optimize_filters_for_hits(opts.c, C.int(boolToChar(value)))
}

// OptimizeFiltersForHits gets setting for optimize_filters_for_hits.
Expand Down Expand Up @@ -2012,7 +2012,7 @@ func (opts *Options) EnabledWriteThreadAdaptiveYield() bool {
//
// Dynamically changeable through SetOptions() API
func (opts *Options) SetReportBackgroundIOStats(value bool) {
C.rocksdb_options_set_report_bg_io_stats(opts.c, C.int(btoi(value)))
C.rocksdb_options_set_report_bg_io_stats(opts.c, C.int(boolToChar(value)))
}

// ReportBackgroundIOStats returns if measureing IO stats in compactions and
Expand Down
32 changes: 32 additions & 0 deletions options_restore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package grocksdb

// #include <stdlib.h>
// #include "rocksdb/c.h"
import "C"

// RestoreOptions captures the options to be used during
// restoration of a backup.
type RestoreOptions struct {
c *C.rocksdb_restore_options_t
}

// NewRestoreOptions creates a RestoreOptions instance.
func NewRestoreOptions() *RestoreOptions {
return &RestoreOptions{
c: C.rocksdb_restore_options_create(),
}
}

// SetKeepLogFiles is used to set or unset the keep_log_files option
// If true, restore won't overwrite the existing log files in wal_dir. It will
// also move all log files from archive directory to wal_dir.
// By default, this is false.
func (ro *RestoreOptions) SetKeepLogFiles(v int) {
C.rocksdb_restore_options_set_keep_log_files(ro.c, C.int(v))
}

// Destroy destroys this RestoreOptions instance.
func (ro *RestoreOptions) Destroy() {
C.rocksdb_restore_options_destroy(ro.c)
ro.c = nil
}
10 changes: 10 additions & 0 deletions options_restore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package grocksdb

import "testing"

func TestRestoreOption(t *testing.T) {
ro := NewRestoreOptions()
defer ro.Destroy()

ro.SetKeepLogFiles(123)
}
2 changes: 1 addition & 1 deletion options_write.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (opts *WriteOptions) IsSync() bool {
//
// Default: false
func (opts *WriteOptions) DisableWAL(value bool) {
C.rocksdb_writeoptions_disable_WAL(opts.c, C.int(btoi(value)))
C.rocksdb_writeoptions_disable_WAL(opts.c, C.int(boolToChar(value)))
}

// IsDisableWAL returns if we turned on DisableWAL flag for writing.
Expand Down
8 changes: 0 additions & 8 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ import (
"unsafe"
)

// btoi converts a bool value to int.
func btoi(b bool) int {
if b {
return 1
}
return 0
}

// boolToChar converts a bool value to C.uchar.
func boolToChar(b bool) C.uchar {
if b {
Expand Down

0 comments on commit 1dcbf1c

Please sign in to comment.