forked from mirror/ledisdb
add base awl package
This commit is contained in:
parent
95cbcc6460
commit
63e2437611
|
@ -0,0 +1,83 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultMaxLogFileSize = 1024 * 1024 * 1024
|
||||
defaultMaxLogFileNum = 10
|
||||
)
|
||||
|
||||
type FileStore struct {
|
||||
Store
|
||||
|
||||
m sync.Mutex
|
||||
|
||||
maxFileSize int
|
||||
maxFileNum int
|
||||
|
||||
first uint64
|
||||
last uint64
|
||||
}
|
||||
|
||||
func NewFileStore(path string) (*FileStore, error) {
|
||||
s := new(FileStore)
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.maxFileSize = defaultMaxLogFileSize
|
||||
s.maxFileNum = defaultMaxLogFileNum
|
||||
|
||||
s.first = 0
|
||||
s.last = 0
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (s *FileStore) SetMaxFileSize(size int) {
|
||||
s.maxFileSize = size
|
||||
}
|
||||
|
||||
func (s *FileStore) SetMaxFileNum(n int) {
|
||||
s.maxFileNum = n
|
||||
}
|
||||
|
||||
func (s *FileStore) GetLog(id uint64, log *Log) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileStore) SeekLog(id uint64, log *Log) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileStore) FirstID() (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *FileStore) LastID() (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *FileStore) StoreLog(log *Log) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileStore) StoreLogs(logs []*Log) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileStore) DeleteRange(start, stop uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileStore) Clear() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FileStore) Close() error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type FileIDGenerator struct {
|
||||
LogIDGenerator
|
||||
|
||||
m sync.Mutex
|
||||
f *os.File
|
||||
|
||||
id uint64
|
||||
}
|
||||
|
||||
func NewFileIDGenerator(base string) (*FileIDGenerator, error) {
|
||||
if err := os.MkdirAll(base, 0755); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g := new(FileIDGenerator)
|
||||
|
||||
name := path.Join(base, "log.id")
|
||||
|
||||
var err error
|
||||
if g.f, err = os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, _ := g.f.Stat()
|
||||
if s.Size() == 0 {
|
||||
g.id = 0
|
||||
} else if s.Size() == 8 {
|
||||
if err = binary.Read(g.f, binary.BigEndian, &g.id); err != nil {
|
||||
g.f.Close()
|
||||
return nil, err
|
||||
} else if g.id == InvalidLogID {
|
||||
g.f.Close()
|
||||
return nil, fmt.Errorf("read invalid log id in %s", name)
|
||||
}
|
||||
} else {
|
||||
g.f.Close()
|
||||
return nil, fmt.Errorf("log id file %s is invalid", name)
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (g *FileIDGenerator) Reset(id uint64) error {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
if g.f == nil {
|
||||
return fmt.Errorf("generator closed")
|
||||
}
|
||||
|
||||
if g.id < id {
|
||||
g.id = id
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *FileIDGenerator) GenerateID() (uint64, error) {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
if g.f == nil {
|
||||
return 0, fmt.Errorf("generator closed")
|
||||
}
|
||||
|
||||
if _, err := g.f.Seek(0, os.SEEK_SET); err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
id := g.id + 1
|
||||
|
||||
if err := binary.Write(g.f, binary.BigEndian, id); err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
g.id = id
|
||||
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (g *FileIDGenerator) Close() error {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
if g.f != nil {
|
||||
err := g.f.Close()
|
||||
g.f = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type MemIDGenerator struct {
|
||||
m sync.Mutex
|
||||
|
||||
LogIDGenerator
|
||||
|
||||
id uint64
|
||||
}
|
||||
|
||||
func NewMemIDGenerator(baseID uint64) *MemIDGenerator {
|
||||
g := &MemIDGenerator{id: baseID}
|
||||
return g
|
||||
}
|
||||
|
||||
func (g *MemIDGenerator) Reset(id uint64) error {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
if g.id < id {
|
||||
g.id = id
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *MemIDGenerator) GenerateID() (uint64, error) {
|
||||
g.m.Lock()
|
||||
defer g.m.Unlock()
|
||||
|
||||
g.id++
|
||||
id := g.id
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (g *MemIDGenerator) Close() error {
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func testGenerator(t *testing.T, g LogIDGenerator, base uint64) {
|
||||
for i := base; i < base+100; i++ {
|
||||
id, err := g.GenerateID()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
} else if id != i {
|
||||
t.Fatal(id, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerator(t *testing.T) {
|
||||
base, err := ioutil.TempDir("", "wal")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(base)
|
||||
|
||||
var g *FileIDGenerator
|
||||
if g, err = NewFileIDGenerator(base); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
testGenerator(t, g, 1)
|
||||
if err = g.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if g, err = NewFileIDGenerator(base); err != nil {
|
||||
t.Fatal(err)
|
||||
} else {
|
||||
testGenerator(t, g, 101)
|
||||
if err = g.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
m := NewMemIDGenerator(100)
|
||||
testGenerator(t, m, 101)
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/siddontang/go/num"
|
||||
"github.com/siddontang/ledisdb/config"
|
||||
"github.com/siddontang/ledisdb/store"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GoLevelDBStore struct {
|
||||
m sync.Mutex
|
||||
db *store.DB
|
||||
|
||||
cfg *config.Config
|
||||
|
||||
first uint64
|
||||
last uint64
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) FirstID() (uint64, error) {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
return s.firstID()
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) LastID() (uint64, error) {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
return s.lastID()
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) firstID() (uint64, error) {
|
||||
if s.first != InvalidLogID {
|
||||
return s.first, nil
|
||||
}
|
||||
|
||||
it := s.db.NewIterator()
|
||||
defer it.Close()
|
||||
|
||||
it.SeekToFirst()
|
||||
|
||||
if it.Valid() {
|
||||
s.first = num.BytesToUint64(it.RawKey())
|
||||
}
|
||||
|
||||
return s.first, nil
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) lastID() (uint64, error) {
|
||||
if s.last != InvalidLogID {
|
||||
return s.last, nil
|
||||
}
|
||||
|
||||
it := s.db.NewIterator()
|
||||
defer it.Close()
|
||||
|
||||
it.SeekToLast()
|
||||
|
||||
if it.Valid() {
|
||||
s.last = num.BytesToUint64(it.RawKey())
|
||||
}
|
||||
|
||||
return s.last, nil
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) GetLog(id uint64, log *Log) error {
|
||||
v, err := s.db.Get(num.Uint64ToBytes(id))
|
||||
if err != nil {
|
||||
return err
|
||||
} else if v == nil {
|
||||
return ErrLogNotFound
|
||||
} else {
|
||||
return log.Decode(bytes.NewBuffer(v))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) SeekLog(id uint64, log *Log) error {
|
||||
it := s.db.NewIterator()
|
||||
defer it.Close()
|
||||
|
||||
it.Seek(num.Uint64ToBytes(id))
|
||||
|
||||
if !it.Valid() {
|
||||
return ErrLogNotFound
|
||||
} else {
|
||||
return log.Decode(bytes.NewBuffer(it.RawValue()))
|
||||
}
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) StoreLog(log *Log) error {
|
||||
return s.StoreLogs([]*Log{log})
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) StoreLogs(logs []*Log) error {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
w := s.db.NewWriteBatch()
|
||||
defer w.Rollback()
|
||||
|
||||
last := s.last
|
||||
|
||||
s.reset()
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, log := range logs {
|
||||
buf.Reset()
|
||||
|
||||
if log.ID <= last {
|
||||
return ErrLessLogID
|
||||
}
|
||||
|
||||
last = log.ID
|
||||
key := num.Uint64ToBytes(log.ID)
|
||||
|
||||
if err := log.Encode(&buf); err != nil {
|
||||
return err
|
||||
}
|
||||
w.Put(key, buf.Bytes())
|
||||
}
|
||||
|
||||
return w.Commit()
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) DeleteRange(min, max uint64) error {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
var first, last uint64
|
||||
var err error
|
||||
|
||||
first, err = s.firstID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
last, err = s.lastID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
min = num.MaxUint64(min, first)
|
||||
max = num.MinUint64(max, last)
|
||||
|
||||
w := s.db.NewWriteBatch()
|
||||
defer w.Rollback()
|
||||
|
||||
n := 0
|
||||
|
||||
s.reset()
|
||||
|
||||
for i := min; i <= max; i++ {
|
||||
w.Delete(num.Uint64ToBytes(i))
|
||||
n++
|
||||
if n > 1024 {
|
||||
if err = w.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
n = 0
|
||||
}
|
||||
}
|
||||
|
||||
if err = w.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) Clear() error {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
if s.db != nil {
|
||||
s.db.Close()
|
||||
}
|
||||
|
||||
os.RemoveAll(s.cfg.DBPath)
|
||||
|
||||
return s.open()
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) reset() {
|
||||
s.first = InvalidLogID
|
||||
s.last = InvalidLogID
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) Close() error {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
if s.db == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := s.db.Close()
|
||||
s.db = nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *GoLevelDBStore) open() error {
|
||||
var err error
|
||||
|
||||
s.first = InvalidLogID
|
||||
s.last = InvalidLogID
|
||||
|
||||
s.db, err = store.Open(s.cfg)
|
||||
return err
|
||||
}
|
||||
|
||||
func NewGoLevelDBStore(base string) (*GoLevelDBStore, error) {
|
||||
cfg := new(config.Config)
|
||||
cfg.DBName = "goleveldb"
|
||||
cfg.DBPath = base
|
||||
cfg.LevelDB.BlockSize = 4 * 1024 * 1024
|
||||
cfg.LevelDB.CacheSize = 16 * 1024 * 1024
|
||||
cfg.LevelDB.WriteBufferSize = 4 * 1024 * 1024
|
||||
cfg.LevelDB.Compression = false
|
||||
|
||||
s := new(GoLevelDBStore)
|
||||
s.cfg = cfg
|
||||
|
||||
if err := s.open(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type Log struct {
|
||||
ID uint64
|
||||
CreateTime uint32
|
||||
// 0 for no compression
|
||||
// 1 for snappy compression
|
||||
Compression uint8
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (l *Log) Encode(w io.Writer) error {
|
||||
length := uint32(17)
|
||||
buf := make([]byte, length)
|
||||
|
||||
pos := 0
|
||||
binary.BigEndian.PutUint64(buf[pos:], l.ID)
|
||||
pos += 8
|
||||
|
||||
binary.BigEndian.PutUint32(buf[pos:], l.CreateTime)
|
||||
pos += 4
|
||||
|
||||
buf[pos] = l.Compression
|
||||
pos++
|
||||
|
||||
binary.BigEndian.PutUint32(buf[pos:], uint32(len(l.Data)))
|
||||
|
||||
if n, err := w.Write(buf); err != nil {
|
||||
return err
|
||||
} else if n != len(buf) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
|
||||
if n, err := w.Write(l.Data); err != nil {
|
||||
return err
|
||||
} else if n != len(l.Data) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *Log) Decode(r io.Reader) error {
|
||||
length := uint32(17)
|
||||
buf := make([]byte, length)
|
||||
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pos := 0
|
||||
l.ID = binary.BigEndian.Uint64(buf[pos:])
|
||||
pos += 8
|
||||
|
||||
l.CreateTime = binary.BigEndian.Uint32(buf[pos:])
|
||||
pos += 4
|
||||
|
||||
l.Compression = buf[pos]
|
||||
pos++
|
||||
|
||||
length = binary.BigEndian.Uint32(buf[pos:])
|
||||
|
||||
l.Data = make([]byte, length)
|
||||
if _, err := io.ReadFull(r, l.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLog(t *testing.T) {
|
||||
l1 := &Log{ID: 1, CreateTime: 100, Compression: 0, Data: []byte("hello world")}
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
if err := l1.Encode(&buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
l2 := &Log{}
|
||||
|
||||
if err := l2.Decode(&buf); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(l1, l2) {
|
||||
t.Fatal("must equal")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGoLevelDBStore(t *testing.T) {
|
||||
// Create a test dir
|
||||
dir, err := ioutil.TempDir("", "wal")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// New level
|
||||
l, err := NewGoLevelDBStore(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
testLogs(t, l)
|
||||
}
|
||||
|
||||
func testLogs(t *testing.T, l Store) {
|
||||
// Should be no first index
|
||||
idx, err := l.FirstID()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
if idx != 0 {
|
||||
t.Fatalf("bad idx: %d", idx)
|
||||
}
|
||||
|
||||
// Should be no last index
|
||||
idx, err = l.LastID()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
if idx != 0 {
|
||||
t.Fatalf("bad idx: %d", idx)
|
||||
}
|
||||
|
||||
// Try a filed fetch
|
||||
var out Log
|
||||
if err := l.GetLog(10, &out); err.Error() != "log not found" {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
|
||||
// Write out a log
|
||||
log := Log{
|
||||
ID: 1,
|
||||
Data: []byte("first"),
|
||||
}
|
||||
for i := 1; i <= 10; i++ {
|
||||
log.ID = uint64(i)
|
||||
if err := l.StoreLog(&log); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to write multiple logs
|
||||
var logs []*Log
|
||||
for i := 11; i <= 20; i++ {
|
||||
nl := &Log{
|
||||
ID: uint64(i),
|
||||
Data: []byte("first"),
|
||||
}
|
||||
logs = append(logs, nl)
|
||||
}
|
||||
if err := l.StoreLogs(logs); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Try to fetch
|
||||
if err := l.GetLog(10, &out); err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
|
||||
// Try to fetch
|
||||
if err := l.GetLog(20, &out); err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
|
||||
// Check the lowest index
|
||||
idx, err = l.FirstID()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
if idx != 1 {
|
||||
t.Fatalf("bad idx: %d", idx)
|
||||
}
|
||||
|
||||
// Check the highest index
|
||||
idx, err = l.LastID()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
if idx != 20 {
|
||||
t.Fatalf("bad idx: %d", idx)
|
||||
}
|
||||
|
||||
// Delete a suffix
|
||||
if err := l.DeleteRange(5, 20); err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
|
||||
// Verify they are all deleted
|
||||
for i := 5; i <= 20; i++ {
|
||||
if err := l.GetLog(uint64(i), &out); err != ErrLogNotFound {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Index should be one
|
||||
idx, err = l.FirstID()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
if idx != 1 {
|
||||
t.Fatalf("bad idx: %d", idx)
|
||||
}
|
||||
idx, err = l.LastID()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
if idx != 4 {
|
||||
t.Fatalf("bad idx: %d", idx)
|
||||
}
|
||||
|
||||
// Should not be able to fetch
|
||||
if err := l.GetLog(5, &out); err != ErrLogNotFound {
|
||||
t.Fatalf("err: %v ", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package wal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
InvalidLogID uint64 = 0
|
||||
)
|
||||
|
||||
var (
|
||||
ErrLogNotFound = errors.New("log not found")
|
||||
ErrLessLogID = errors.New("log id is less")
|
||||
)
|
||||
|
||||
type LogIDGenerator interface {
|
||||
// Force reset to id, if current id is larger than id, nothing reset
|
||||
Reset(id uint64) error
|
||||
|
||||
// ID must be first at 1, and increased monotonously, 0 is invalid
|
||||
GenerateID() (uint64, error)
|
||||
|
||||
Close() error
|
||||
}
|
||||
|
||||
type Store interface {
|
||||
GetLog(id uint64, log *Log) error
|
||||
|
||||
// Get the first log which ID is equal or larger than id
|
||||
SeekLog(id uint64, log *Log) error
|
||||
|
||||
FirstID() (uint64, error)
|
||||
LastID() (uint64, error)
|
||||
|
||||
// if log id is less than current last id, return error
|
||||
StoreLog(log *Log) error
|
||||
StoreLogs(logs []*Log) error
|
||||
|
||||
// Delete logs [start, stop]
|
||||
DeleteRange(start, stop uint64) error
|
||||
|
||||
// Clear all logs
|
||||
Clear() error
|
||||
|
||||
Close() error
|
||||
}
|
Loading…
Reference in New Issue