Compare commits

...

4 Commits

Author SHA1 Message Date
Přemysl Eric Janouch 7847236dbc
Improve server shutdown
Less likely to corrupt data now.
2019-04-19 12:30:48 +02:00
Přemysl Eric Janouch 0fcc67c121
Fix the usage message 2019-04-19 12:30:13 +02:00
Přemysl Eric Janouch c7597b73c2
Style fixes 2019-04-19 12:30:01 +02:00
Přemysl Eric Janouch 47de2cebaa
Relicense to 0BSD, update mail address
I've come to the conclusion that copyright mostly just stands in the way
of software development.  In my jurisdiction I cannot give up my own
copyright and 0BSD seems to be the closest thing to public domain.

The updated mail address, also used in my author/committer lines,
is shorter and looks nicer.  People rarely interact anyway.
2018-06-24 05:30:32 +02:00
3 changed files with 24 additions and 25 deletions

View File

@ -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
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
} }
// 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
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.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)
} }