Compare commits
4 Commits
59e315cf95
...
7847236dbc
Author | SHA1 | Date | |
---|---|---|---|
7847236dbc | |||
0fcc67c121 | |||
c7597b73c2 | |||
47de2cebaa |
5
LICENSE
5
LICENSE
@ -1,8 +1,7 @@
|
|||||||
Copyright (c) 2017, Přemysl Janouch <p.janouch@gmail.com>
|
Copyright (c) 2017, Přemysl Janouch <p@janouch.name>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
purpose with or without fee is hereby granted.
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
24
db.go
24
db.go
@ -12,20 +12,20 @@ import (
|
|||||||
var ErrClosed = errors.New("database has been closed")
|
var ErrClosed = errors.New("database has been closed")
|
||||||
|
|
||||||
type DB struct {
|
type DB struct {
|
||||||
sync.RWMutex // Locking
|
sync.RWMutex // locking
|
||||||
data map[string]string // Current state of the database
|
data map[string]string // current state of the database
|
||||||
file *os.File // Data storage
|
file *os.File // data storage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open an existing database file, loading the contents to memory
|
// OpenDB opens an existing database file, loading the contents to memory.
|
||||||
func OpenDB(path string) (*DB, error) {
|
func OpenDB(path string) (*DB, error) {
|
||||||
file, err := os.OpenFile(path, os.O_RDWR, 0 /* not used */)
|
file, err := os.OpenFile(path, os.O_RDWR, 0 /* not used */)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO we might want a recover flag that just reads as much as it can
|
// TODO: We might want a recover flag that just reads as much as it can
|
||||||
// instead of returning io.ErrUnexpectedEOF
|
// instead of returning io.ErrUnexpectedEOF.
|
||||||
db := &DB{data: make(map[string]string), file: file}
|
db := &DB{data: make(map[string]string), file: file}
|
||||||
for {
|
for {
|
||||||
var header struct{ KeyLen, ValueLen int32 }
|
var header struct{ KeyLen, ValueLen int32 }
|
||||||
@ -64,7 +64,7 @@ func OpenDB(path string) (*DB, error) {
|
|||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new database, overwriting any previous contents of the file
|
// CreateDB creates a new database, overwriting any previous file contents.
|
||||||
func CreateDB(path string) (*DB, error) {
|
func CreateDB(path string) (*DB, error) {
|
||||||
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -73,7 +73,7 @@ func CreateDB(path string) (*DB, error) {
|
|||||||
return &DB{data: make(map[string]string), file: file}, nil
|
return &DB{data: make(map[string]string), file: file}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the value corresponding to the given key
|
// Get retrieves the value corresponding to the given key.
|
||||||
func (db *DB) Get(key string) (string, bool, error) {
|
func (db *DB) Get(key string) (string, bool, error) {
|
||||||
db.RLock()
|
db.RLock()
|
||||||
defer db.RUnlock()
|
defer db.RUnlock()
|
||||||
@ -106,7 +106,7 @@ func put(file *os.File, key, value string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save a key-value pair in the database storage
|
// Put saves a key-value pair in the database storage.
|
||||||
func (db *DB) Put(key, value string) error {
|
func (db *DB) Put(key, value string) error {
|
||||||
db.Lock()
|
db.Lock()
|
||||||
defer db.Unlock()
|
defer db.Unlock()
|
||||||
@ -127,7 +127,7 @@ func (db *DB) Put(key, value string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a key from the database storage
|
// Delete deletes a key from the database storage.
|
||||||
func (db *DB) Delete(key string) error {
|
func (db *DB) Delete(key string) error {
|
||||||
db.Lock()
|
db.Lock()
|
||||||
defer db.Unlock()
|
defer db.Unlock()
|
||||||
@ -157,7 +157,7 @@ func (db *DB) Delete(key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get rid of historical data in the database file
|
// Checkpoint gets rid of historical data in the database file.
|
||||||
func (db *DB) Checkpoint() error {
|
func (db *DB) Checkpoint() error {
|
||||||
db.Lock()
|
db.Lock()
|
||||||
defer db.Unlock()
|
defer db.Unlock()
|
||||||
@ -194,7 +194,7 @@ func (db *DB) Checkpoint() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close the database file, rendering the object unusable
|
// Close closes the database file, rendering the object unusable.
|
||||||
func (db *DB) Close() error {
|
func (db *DB) Close() error {
|
||||||
db.Lock()
|
db.Lock()
|
||||||
defer db.Unlock()
|
defer db.Unlock()
|
||||||
|
20
main.go
20
main.go
@ -1,4 +1,4 @@
|
|||||||
// Demos a trivial key-value database backed by a file
|
// Demos a trivial key-value database backed by a file.
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) != 3 {
|
if len(os.Args) != 3 {
|
||||||
log.Fatalln("usage: %s LISTEN-ADDRESS DATABASE-FILE", os.Args[0])
|
log.Fatalf("Usage: %s LISTEN-ADDRESS DATABASE-FILE\n", os.Args[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
listenAddr, dbFilename := os.Args[1], os.Args[2]
|
listenAddr, dbFilename := os.Args[1], os.Args[2]
|
||||||
@ -66,18 +66,18 @@ func main() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
server := &http.Server{Addr: listenAddr}
|
server := &http.Server{Addr: listenAddr}
|
||||||
go func() {
|
errs := make(chan error, 1)
|
||||||
if err := http.ListenAndServe(listenAddr, nil); err != nil &&
|
go func() { errs <- server.ListenAndServe() }()
|
||||||
err != http.ErrServerClosed {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
sig := make(chan os.Signal)
|
sig := make(chan os.Signal)
|
||||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-sig
|
select {
|
||||||
|
case <-sig:
|
||||||
|
case err := <-errs:
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
// For simplicity, we'll wait for everything to finish, including snapshots
|
// For simplicity, we'll wait for everything to finish, including snapshots.
|
||||||
if err := server.Shutdown(context.Background()); err != nil {
|
if err := server.Shutdown(context.Background()); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user