Skip to content
Open
Changes from 1 commit
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
d0d211b
Initial commit
SiarheiKresik Dec 3, 2018
ea4772a
Implement client
SiarheiKresik Dec 15, 2018
69a5d82
Add dockerfile
SiarheiKresik Dec 17, 2018
80a3267
Add gitignore file
SiarheiKresik Dec 17, 2018
cc457c7
Add makefile
SiarheiKresik Dec 17, 2018
5af629d
Update dockerfile
SiarheiKresik Dec 17, 2018
1f02fee
Update makefile
SiarheiKresik Dec 17, 2018
0921290
Update dockerfile
SiarheiKresik Dec 17, 2018
e7d2129
Update makefile
SiarheiKresik Dec 17, 2018
4ba8093
Update makefile
SiarheiKresik Dec 17, 2018
f4d8890
Add code checks into makefile
SiarheiKresik Dec 17, 2018
e3989f8
Update makefile
SiarheiKresik Dec 17, 2018
b748ad8
Refactor code checks in the makefile
SiarheiKresik Dec 18, 2018
1b60b6a
Set a image in the run-dev target to the dev-image
SiarheiKresik Dec 18, 2018
b9d512d
Add test targets into the makefile
SiarheiKresik Dec 18, 2018
efc01a1
Reorder check order in the makefile
SiarheiKresik Dec 18, 2018
ed3372d
Add coverage.out into gitignore
SiarheiKresik Dec 18, 2018
6f17329
Add test coverage target into the makefile
SiarheiKresik Dec 18, 2018
5454fd0
Add clean target into the makefile
SiarheiKresik Dec 18, 2018
b8703d6
Refactor the makefile
SiarheiKresik Dec 18, 2018
b8500aa
Implement client
SiarheiKresik Dec 18, 2018
3c88d85
Implement database
SiarheiKresik Dec 18, 2018
71ac094
Improve client log messages
SiarheiKresik Dec 19, 2018
73498de
Update gitignore
SiarheiKresik Dec 19, 2018
7c7f346
Implement database
SiarheiKresik Dec 19, 2018
2bb639d
Implement server
SiarheiKresik Dec 19, 2018
3c4868c
Refactor client
SiarheiKresik Dec 19, 2018
d253adc
Fix coverage target of the makefile
SiarheiKresik Dec 19, 2018
f6e3543
Add .editorconfig
SiarheiKresik Dec 19, 2018
da3d544
Refactor KEY command to filter keys by glob pattern
SiarheiKresik Dec 20, 2018
7269f88
Update code comments
SiarheiKresik Dec 20, 2018
06b2faf
Fix typo
SiarheiKresik Dec 20, 2018
0cc3bea
Add comments
SiarheiKresik Dec 20, 2018
e8dcb3a
Remove unnecessary log print
SiarheiKresik Dec 20, 2018
582fa27
Implement verbose server mode and client ids
SiarheiKresik Dec 20, 2018
636527c
Make logs formatting more consistent
SiarheiKresik Dec 20, 2018
7a41945
Now server keep on running after accept error
SiarheiKresik Dec 20, 2018
b381169
Remove unneccesery code
SiarheiKresik Dec 20, 2018
7fda69c
Make string formatting more consistent
SiarheiKresik Dec 20, 2018
24d9aac
Update readme file
SiarheiKresik Dec 20, 2018
1fe248e
Fix type in the readme file
SiarheiKresik Dec 20, 2018
95182c0
Add comments
SiarheiKresik Dec 20, 2018
f489399
Fix typo
SiarheiKresik Dec 20, 2018
771af49
Correct log message
SiarheiKresik Dec 20, 2018
c82f95c
Refactor database tests
SiarheiKresik Dec 20, 2018
203cf21
Remove client flags for non implemented features
SiarheiKresik Dec 20, 2018
a32d74b
Update readme file
SiarheiKresik Dec 20, 2018
6eb8112
Update readme file
SiarheiKresik Dec 20, 2018
d577ce3
Remove wrong setting from the editorconfig
SiarheiKresik Dec 20, 2018
6b39598
Add comments
SiarheiKresik Dec 20, 2018
e186d85
Remove trailing whitespaces to pass goimports check
SiarheiKresik Dec 20, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implement database
  • Loading branch information
SiarheiKresik committed Dec 19, 2018
commit 7c7f346ed6e77267db7483a3551cd406b36bdf97
188 changes: 120 additions & 68 deletions db/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,88 +3,156 @@ package db
import (
"encoding/gob"
"encoding/json"
"fmt"
"errors"
"io"
"log"
"os"
"sync"
)

const defaultPath = "./go-kvdb.db"
const defaultPath = "go-kvdb.db"

type mode int

const (
disk mode = iota + 1
memory
)

// DataBase ...
// DataBase struct.
type DataBase struct {
mode string
sync.RWMutex
m map[string]string
mod mode
m map[string]string
}

// NewDB ...
func NewDB(mod mode) *DataBase {
return &DataBase{m: map[string]string{"a": "1", "b": "2"}}
// newDB creates a new database object.
func newDB(mode string) *DataBase {
return &DataBase{
mode: mode,
m: map[string]string{},
}
}

// Open ...
func (db *DataBase) Open(path string) error {
db.Lock()
defer db.Unlock()
// InitDB init a database according to given mode
func InitDB(mode string) (*DataBase, error) {
db := newDB(mode)
log.Println("Database mode:", mode)

switch mode {
case "memory":
return db, nil
case "disk":
// check if the database file exists
if _, err := os.Stat(defaultPath); os.IsNotExist(err) {
log.Print("The database file not found. Creating a new one")
// create a database file if not exist
err := db.Save()
if err != nil {
return nil, err
}
}
// load a database from a file
err := db.Load()
if err != nil {
return nil, err
}
return db, nil
default:
return nil, errors.New("Unknown database mode")
}
}

// Load database from a file on disk.
func (db *DataBase) Load() error {
f, errFile := os.Open(defaultPath)
if errFile != nil {
log.Println("Error loading database from", defaultPath)
return errFile
}

err := readGob(path, &db.m)
fmt.Print(err)
return err
// decode to a database struct map
errDec := decodeGob(f, &db.m)
if errDec != nil {
log.Println("Error decoding the database file")
return errDec
}

log.Println("Database loaded from", defaultPath)
return nil
}

// Save ...
// Save database on disk into a file.
func (db *DataBase) Save() error {
db.Lock()
defer db.Unlock()

// f, _ := db.Dump()
// err := ioutil.WriteFile("./go-kvdb.db", f, 0644)
// if err != nil {
// return err
// }
// return nil
f, errFile := os.Create(defaultPath)
if errFile != nil {
return errFile
}
defer f.Close()

// path := "./go-kvdb.db"
err := writeGob(path, db.m)
return err
errEnc := encodeGob(f, db.m)
if errEnc != nil {
log.Println("Error encoding the database for saving")
return errEnc
}
return nil
}

// Get ...
// Get returns the value of a key and the key state.
func (db *DataBase) Get(key string) (string, bool) {
db.RLock()
defer db.RUnlock()

value, ok := db.m[key]
fmt.Println("getting", key, ": ", value, ok)
log.Println("getting", key, ": ", value, ok)
return value, ok
}

// Set ...
func (db *DataBase) Set(key, value string) {
// Set a key with the given value.
func (db *DataBase) Set(key, value string) error {
db.Lock()
defer db.Unlock()

oldValue := db.m[key]
db.m[key] = value

// TODO refactor code repetition in Set and Delete functions,
// maybe we need to implement 'transactions':
// func (db *DataBase) (operation, payload) error {
// db.Lock()
// run operation with payload
// db.Save() if mode=disk
// revert operation if saving failed
// db.Unlock()
// }
if db.mode == "disk" {
err := db.Save()
if err != nil {
// revert old value and return error
db.m[key] = oldValue
return err
}
}

return nil
}

// Delete ...
func (db *DataBase) Delete(key string) {
// Delete deletes a key from database.
func (db *DataBase) Delete(key string) error {
db.Lock()
defer db.Unlock()

oldValue := db.m[key]
delete(db.m, key)

if db.mode == "disk" {
err := db.Save()
if err != nil {
// revert old value and return error
db.m[key] = oldValue
return err
}
}

return nil
}

// TODO add wildcard support

// Keys ...
// Keys returns all keys.
func (db *DataBase) Keys() []string {
db.RLock()
defer db.RUnlock()
Expand All @@ -97,35 +165,19 @@ func (db *DataBase) Keys() []string {
return result
}

// Dump ...
// Dump serializes database data into a json format.
func (db *DataBase) Dump() ([]byte, error) {
result, err := json.Marshal(db.m)
return result, err
return json.Marshal(db.m)
}

// func (db *DataBase) Restore(path string) error {
// result, err := json.Marshal(db.m)
// return result, err
// }

// Serialize object using gob and save result to disk.
func writeGob(path string, object interface{}) error {
file, err := os.Create(path)
if err == nil {
encoder := gob.NewEncoder(file)
encoder.Encode(object)
}
file.Close()
return err
// encodeGob encodes using gob.
func encodeGob(r io.Writer, object interface{}) error {
encoder := gob.NewEncoder(r)
return encoder.Encode(object)
}

// Read object from disk and deserialize it using gob.
func readGob(path string, object interface{}) error {
file, err := os.Open(path)
if err == nil {
decoder := gob.NewDecoder(file)
err = decoder.Decode(object)
}
file.Close()
return err
// decodeGob decodes using gob.
func decodeGob(r io.Reader, object interface{}) error {
decoder := gob.NewDecoder(r)
return decoder.Decode(object)
}