1
0
mirror of https://github.com/nxshock/zkv.git synced 2024-11-27 11:21:02 +05:00

New version

* Simplify internal structures
* Update minimal Go version
* Add Delete() function
This commit is contained in:
nxshock 2022-12-02 20:32:09 +05:00
parent 5c459416b1
commit 4ec53665af
7 changed files with 100 additions and 31 deletions

5
errors.go Normal file
View File

@ -0,0 +1,5 @@
package zkv
import "errors"
var ErrNotExists = errors.New("not exists")

10
go.mod
View File

@ -1,14 +1,14 @@
module github.com/nxshock/zkv
go 1.17
go 1.19
require (
github.com/klauspost/compress v1.14.2
github.com/stretchr/testify v1.7.0
github.com/klauspost/compress v1.15.12
github.com/stretchr/testify v1.8.1
)
require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

18
go.sum
View File

@ -1,13 +1,19 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/klauspost/compress v1.14.2 h1:S0OHlFk/Gbon/yauFJ4FfJJF5V0fc5HbBTJazi28pRw=
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -15,10 +15,9 @@ const (
)
type Record struct {
Type RecordType `json:"t"`
KeyHash []byte `json:"h"`
KeyBytes []byte `json:"k,omitempty"` // optional
ValueBytes []byte `json:"v"`
Type RecordType
KeyHash []byte
ValueBytes []byte
}
func newRecord(recordType RecordType, key, value interface{}) (*Record, error) {
@ -35,7 +34,6 @@ func newRecord(recordType RecordType, key, value interface{}) (*Record, error) {
record := &Record{
Type: recordType,
KeyHash: hashBytes(keyBytes),
KeyBytes: keyBytes,
ValueBytes: valueBytes}
return record, nil

View File

@ -5,7 +5,6 @@ import (
"crypto/sha256"
"encoding/gob"
"io"
"io/ioutil"
)
func encode(value interface{}) ([]byte, error) {
@ -39,7 +38,7 @@ func skip(r io.Reader, count int64) (err error) {
case io.Seeker:
_, err = r.Seek(count, io.SeekCurrent)
default:
_, err = io.CopyN(ioutil.Discard, r, count)
_, err = io.CopyN(io.Discard, r, count)
}
return err

46
zkv.go
View File

@ -3,7 +3,6 @@ package zkv
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"io"
"os"
@ -12,10 +11,6 @@ import (
"github.com/klauspost/compress/zstd"
)
type Options struct {
SaveKeys bool
}
type Database struct {
dataOffset map[string]int64
file *os.File
@ -23,8 +18,6 @@ type Database struct {
filePath string
offset int64
options Options
mu sync.Mutex
}
@ -49,10 +42,6 @@ func (db *Database) Set(key, value interface{}) error {
return err
}
if !db.options.SaveKeys {
record.KeyBytes = nil
}
b, err := record.Marshal()
if err != nil {
return err
@ -81,7 +70,7 @@ func (db *Database) Get(key, value interface{}) error {
offset, exists := db.dataOffset[string(hashToFind)]
if !exists {
return errors.New("not exists") // TODO: заменить на константную ошибку
return ErrNotExists
}
readF, err := os.Open(db.filePath)
@ -106,7 +95,7 @@ func (db *Database) Get(key, value interface{}) error {
return err
}
if bytes.Compare(record.KeyHash, hashToFind) != 0 {
if !bytes.Equal(record.KeyHash, hashToFind) {
return fmt.Errorf("wrong hash on this offset: expected %s, got %s", base64.StdEncoding.EncodeToString(hashToFind), base64.StdEncoding.EncodeToString(record.KeyHash)) // TODO: заменить на константную ошибку
}
@ -168,3 +157,34 @@ func Open(filePath string) (*Database, error) {
return database, nil
}
func (db *Database) Delete(key interface{}) error {
db.mu.Lock()
defer db.mu.Unlock()
keyHash, err := hashInterface(key)
if err != nil {
return err
}
record := &Record{
Type: RecordTypeDelete,
KeyHash: keyHash,
}
b, err := record.Marshal()
if err != nil {
return err
}
delete(db.dataOffset, string(record.KeyHash))
_, err = db.compressor.Write(b)
if err != nil {
return err
}
db.offset += int64(len(b))
return nil
}

View File

@ -77,3 +77,44 @@ func TestSmallWrites(t *testing.T) {
err = db.Close()
assert.NoError(t, err)
}
func TestDeleteBasic(t *testing.T) {
const filePath = "TestDeleteBasic.zkv"
const recordCount = 100
defer os.Remove(filePath)
db, err := Open(filePath)
assert.NoError(t, err)
for i := 1; i <= recordCount; i++ {
err = db.Set(i, i)
assert.NoError(t, err)
}
assert.Len(t, db.dataOffset, recordCount)
err = db.Delete(50)
assert.NoError(t, err)
assert.Len(t, db.dataOffset, recordCount-1)
var value int
err = db.Get(50, &value)
assert.Equal(t, 0, value)
assert.ErrorIs(t, err, ErrNotExists)
err = db.Close()
assert.NoError(t, err)
// try to read
db, err = Open(filePath)
assert.NoError(t, err)
assert.Len(t, db.dataOffset, recordCount-1)
value = 0
err = db.Get(50, &value)
assert.Equal(t, 0, value)
assert.ErrorIs(t, err, ErrNotExists)
err = db.Close()
assert.NoError(t, err)
}