Compare commits

..

No commits in common. "7847236dbc9e9584ea35f62cade8b77b29f208d6" and "59e315cf95088ea86927e684a09071d81dfd06fa" have entirely different histories.

3 changed files with 25 additions and 24 deletions

View File

@ -1,7 +1,8 @@
Copyright (c) 2017, Přemysl Janouch <p@janouch.name> Copyright (c) 2017, Přemysl Janouch <p.janouch@gmail.com>
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. purpose with or without fee is hereby granted, provided that the above
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
View File

@ -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
} }
// OpenDB opens an existing database file, loading the contents to memory. // Open 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
} }
// CreateDB creates a new database, overwriting any previous file contents. // Create a new database, overwriting any previous contents of the file
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
} }
// Get retrieves the value corresponding to the given key. // Retrieve 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
} }
// Put saves a key-value pair in the database storage. // Save 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 deletes a key from the database storage. // Delete 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
} }
// Checkpoint gets rid of historical data in the database file. // Get 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 closes the database file, rendering the object unusable. // Close 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
View File

@ -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.Fatalf("Usage: %s LISTEN-ADDRESS DATABASE-FILE\n", os.Args[0]) log.Fatalln("usage: %s LISTEN-ADDRESS DATABASE-FILE", 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}
errs := make(chan error, 1) go func() {
go func() { errs <- server.ListenAndServe() }() if err := http.ListenAndServe(listenAddr, nil); err != nil &&
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)
select { <-sig
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)
} }