kunt

golang IRC bot
git clone git://git.2f30.org/kunt
Log | Files | Refs | LICENSE

commit f2b445f2b8681df1d52fc347702e7725a3491dcc
parent 152c9521a6ce27dd0603e74df4591c83f03074f0
Author: sin <sin@2f30.org>
Date:   Fri, 26 Apr 2013 15:33:19 +0100

fsdb: Cleanup the code

Diffstat:
Msrc/fsdb/fsdb.go | 161++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/kunt/kunt.go | 12++++++------
2 files changed, 109 insertions(+), 64 deletions(-)

diff --git a/src/fsdb/fsdb.go b/src/fsdb/fsdb.go @@ -1,127 +1,172 @@ +// Simple filesystem DB package fsdb import ( "errors" "fmt" "io/ioutil" - "log" "math/rand" "os" "path" "path/filepath" "strconv" - "strings" "sync" ) type Fsdb struct { - name string - path string - prefix string - dbMap map[int][]byte - lastKey int - lock sync.Mutex + name string + path string + prefix string + dbMap map[int]fsdbVal + lock sync.Mutex +} + +type fsdbVal struct { + dirty bool // Do we need to sync this entry to the disk? + data []byte // Actual raw data } func NewFsdb(name string, path string, prefix string) *Fsdb { return &Fsdb{ - name: name, - path: path, - prefix: prefix, - dbMap: make(map[int][]byte), - lastKey: -1, + name: name, + path: path, + prefix: prefix, + dbMap: make(map[int]fsdbVal), } } -func (d *Fsdb) FsRead(quote int) ([]byte, error) { - path := fmt.Sprintf("%s/%s%d.txt", d.path, d.prefix, quote) +func (d *Fsdb) fsRead(key int) ([]byte, error) { + path := fmt.Sprintf("%s/%s%d.txt", d.path, d.prefix, key) b, err := ioutil.ReadFile(path) if err != nil { - return b, err + return nil, err } return b, err } -func (d *Fsdb) FsWrite(quote int, buf []byte) error { - path := fmt.Sprintf("%s/%s%d.txt", d.path, d.prefix, quote) +func (d *Fsdb) fsWrite(key int, buf []byte) error { + path := fmt.Sprintf("%s/%s%d.txt", d.path, d.prefix, key) _, err := os.Stat(path) if err == nil { - return fmt.Errorf("Entry %s already exists in fsdb\n", - path) + return fmt.Errorf("Entry %s already exists in %s db", + path, d.name) } - s := string(buf) - last := strings.Index(s, "\n") - err = ioutil.WriteFile(path, buf[0:last+1], 0644) + err = ioutil.WriteFile(path, buf, 0644) if err != nil { return err } return nil } -func (d *Fsdb) Load() { - err := filepath.Walk(d.path, func(p string, f os.FileInfo, err error) error { - fi, err := os.Stat(p) - if err != nil { - log.Fatal(err) +// Sync dirty entries to disk +func (d *Fsdb) Sync() error { + d.lock.Lock() + defer d.lock.Unlock() + for k, v := range d.dbMap { + if v.dirty { + err := d.fsWrite(k, v.data) + if err != nil { + return err + } + fmt.Printf("Synced entry (%d, %v)\n", + k, v.data) + v.dirty = false } + } + return nil +} + +// Print DB +func (d *Fsdb) String() string { + d.lock.Lock() + defer d.lock.Unlock() + s := fmt.Sprintf("*** %s DB DUMP ***\n", d.name) + for k, v := range d.dbMap { + s += fmt.Sprintf("k: %d -> v: %v\n", + k, v) + } + return s +} + +// Load the DB from disk +func (d *Fsdb) Load() error { + _, err := os.Stat(d.path) + if err != nil { + // Just create the db if it does not exist + return os.MkdirAll(d.path, 0755) + } + walkFunc := func(p string, fi os.FileInfo, err error) error { if fi.IsDir() { return nil } + if err != nil { + return err + } base, ext := path.Base(p), path.Ext(p) entry := base[len(d.prefix) : len(base)-len(ext)] i, err := strconv.Atoi(entry) if err != nil { - log.Fatal(err) + return err } - b, err := d.FsRead(i) + b, err := d.fsRead(i) if err != nil { - log.Fatal(err) + return err } - _, err = d.Put(i, b) + err = d.putIdx(i, b, false) if err != nil { - log.Fatal(err) + return err } return nil - }) - if err != nil { - log.Fatal(err) } + return filepath.Walk(d.path, walkFunc) } -func (d *Fsdb) Put(key int, buf []byte) (int, error) { - d.lock.Lock() - defer d.lock.Unlock() +// Check if `buf' already exists in the DB +func (d *Fsdb) dup(buf []byte) bool { for _, v := range d.dbMap { - if string(v) == string(buf) { - return -1, errors.New("Duplicate entry: " + string(buf)) + if string(v.data) == string(buf) { + return true } } - if key == -1 { - d.lastKey++ - key = d.lastKey - } else { - if key > d.lastKey { - d.lastKey = key - } + return false +} + +func (d *Fsdb) putIdx(key int, buf []byte, dirty bool) error { + d.lock.Lock() + defer d.lock.Unlock() + if key < 0 { + return fmt.Errorf("Invalid key: %d", key) } - if d.dbMap[key] != nil { - log.Fatal("Duplicate entry in fsdb") + if d.dup(buf) { + return fmt.Errorf("Duplicate entry: %s", buf) + } + d.dbMap[key] = fsdbVal{dirty, buf} + return nil +} + +// Append buf into the DB +func (d *Fsdb) Append(buf []byte) (int, error) { + d.lock.Lock() + defer d.lock.Unlock() + if d.dup(buf) { + return -1, fmt.Errorf("Duplicate entry: %s", buf) } - d.dbMap[key] = buf - return key, nil + d.dbMap[d.Len()] = fsdbVal{true, buf} + return -1, nil } +// Return the raw data based on the key func (d *Fsdb) Get(key int) ([]byte, error) { d.lock.Lock() defer d.lock.Unlock() if len(d.dbMap) == 0 { return nil, errors.New("Empty DB, can't fetch entry") } - buf := d.dbMap[key] - if buf == nil { - log.Fatal("Fetching nil entry from fsdb") + val, ok := d.dbMap[key] + if !ok { + return nil, fmt.Errorf("No entry with key: %d", key) } - return buf, nil + return val.data, nil } func (d *Fsdb) Rand() ([]byte, int) { @@ -132,8 +177,8 @@ func (d *Fsdb) Rand() ([]byte, int) { i := 0 for k, _ := range d.dbMap { if i == idx { - buf := d.dbMap[k] - return buf, k + val := d.dbMap[k] + return val.data, k } i++ } diff --git a/src/kunt/kunt.go b/src/kunt/kunt.go @@ -120,12 +120,12 @@ func cmdAddQuote(msg irc.IrcMessage) { s = strings.TrimSpace(s) s += "\n" buf := []byte(s) - quote, err := quoteDb.Put(-1, buf) + _, err := quoteDb.Append(buf) if err != nil { kunt.ircCtx.SendPrivmsg(msg.Params[0], err.Error()) return } - err = quoteDb.FsWrite(quote, buf) + err = quoteDb.Sync() if err != nil { log.Fatal(err) } @@ -165,12 +165,12 @@ func cmdAddUrl(msg irc.IrcMessage) { s = strings.TrimSpace(s) s += "\n" buf := []byte(s) - url, err := urlDb.Put(-1, buf) + _, err := urlDb.Append(buf) if err != nil { kunt.ircCtx.SendPrivmsg(msg.Params[0], err.Error()) return } - err = urlDb.FsWrite(url, buf) + err = urlDb.Sync() if err != nil { log.Fatal(err) } @@ -271,8 +271,8 @@ func main() { hostport := strings.Split(flag.Arg(0), ":") - quoteDb = fsdb.NewFsdb("Quote DB", "db/quotes", "quote") - urlDb = fsdb.NewFsdb("Url DB", "db/urls", "url") + quoteDb = fsdb.NewFsdb("Quotes", "db/quotes", "quote") + urlDb = fsdb.NewFsdb("Urls", "db/urls", "url") quoteDb.Load() urlDb.Load()