1
votes

I am trying to use file database bbolt as a key/value storage. Below is my code


package handler

import (
    "encoding/json"
    "log"
    "net/http"
    "os"

    "go.etcd.io/bbolt"
    bolt "go.etcd.io/bbolt"
    yml "gopkg.in/yaml.v3"
)

type urlDB struct {
    db *bbolt.DB
}

func (u urlDB) ensureDB() {
    u.db, _ = bolt.Open("url.db", 0600, nil)
}


func MapHandler(pathsToURLs map[string]string, fallback http.Handler) http.HandlerFunc {

    return func(w http.ResponseWriter, r *http.Request) {
        path := r.URL.Path
        if _, ok := pathsToURLs[path]; ok {
            http.Redirect(w, r, pathsToURLs[path], http.StatusFound)

        } else {
            fallback.ServeHTTP(w, r)
        }
    }

}


func DefaultMap(fallback http.Handler) http.HandlerFunc {
    db := urlDB{}
    db.ensureDB()
    //bkt := db.db.createBucket()
    //createSampleData(bkt)
    defer db.db.Close()

    bktName := "URLBucket"

    createBucket(db.db, bktName)
    addSampleData(db.db, bktName, "/gm", "https://mail.google.com")
    addSampleData(db.db, bktName, "/ym", "https://mail.yahoo.com")
    pathToURLs := make(map[string]string)
    getData(db.db, bktName, pathToURLs)
    return MapHandler(pathToURLs, fallback)
}

func createBucket(db *bbolt.DB, bktName string) {
    db.Update(func(tx *bbolt.Tx) error {
        _, _ = tx.CreateBucketIfNotExists([]byte(bktName))
        return nil
    })
}

func addSampleData(db *bbolt.DB, bktName, key, value string) {
    db.Update(func(tx *bbolt.Tx) error {
        b := tx.Bucket([]byte(bktName))
        _ = b.Put([]byte(key), []byte(value))
        return nil
    })
}

func getData(db *bbolt.DB, bktName string, pathToURLs map[string]string) {
    db.View(func(tx *bbolt.Tx) error {
        b := tx.Bucket([]byte(bktName))
        c := b.Cursor()

        for k, v := c.First(); k != nil; k, v = c.Next() {
            pathToURLs[string(k)] = string(v)
        }
        return nil
    })
}

So while calling this handler:DefaultMap from main.go with below code

mux := http.NewServeMux()
var urlHandler http.HandlerFunc 
urlHandler = handler.DefaultMap(mux)
http.ListenAndServe(":8080", urlHandler)

But I am getting error as below

panic: runtime error: invalid memory address or nil pointer dereference
    panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x1b8 pc=0x6126cf]

goroutine 1 [running]:
go.etcd.io/bbolt.(*DB).Close(0x0)
panic({0x686180, 0x8dcb50})
    /usr/local/go/src/runtime/panic.go:1038 +0x215
go.etcd.io/bbolt.(*DB).beginRWTx(0x0)
    /home/xx/coding/go/packages/pkg/mod/go.etcd.io/[email protected]/db.go:634 +0x38
go.etcd.io/bbolt.(*DB).Begin(0x0, 0x0)
    /home/xx/coding/go/packages/pkg/mod/go.etcd.io/[email protected]/db.go:589 +0x1d
go.etcd.io/bbolt.(*DB).Update(0x0, 0xc000187e38)
    /home/xx/coding/go/packages/pkg/mod/go.etcd.io/[email protected]/db.go:725 +0x3e
github.com/xx/urlshortner/handler.createBucket(0x6c588d, {0x6c6358, 0xc000187ec8})
    /home/xx/coding/go/src/urlshortner/handler/handler.go:97 +0x48
github.com/xx/urlshortner/handler.DefaultMap({0x7243e0, 0xc00014a300})
    /home/xx/coding/go/src/urlshortner/handler/handler.go:88 +0x95
main.main()
    /home/xx/coding/go/src/urlshortner/main.go:24 +0xcc
exit status 2

Any help much appreciated.

Thank you.

1
Check errors in Go. Always! And all of them!icza
unrelated to the error, you are referring to the package as both bolt and bbolt in the code, so you may want to unify those imports.JimB
Issue solved, I am referring by value, so even though url.db created, my struct instance dont know it, instead passed reference ` func (u *urlDB) ensureDB() {`, may be I need more understanding on GoLang pointers. Any recommended reads, much appreciated. Thank you.rɑːdʒɑ

1 Answers

2
votes

This line:

github.com/xx/urlshortner/handler.createBucket(0x6c588d, {0x6c6358, 0xc000187ec8})

says error is in createBucket. Looking at createBucket:

    db.Update(func(tx *bbolt.Tx) error {

Based on the stack trace, it calls Update, and db is the only thing that can be nil here.

Tracing the call back, you can see:

func (u urlDB) ensureDB() {
    u.db, _ = bolt.Open("url.db", 0600, nil)
}

Thus, db open failed, and u.db is nil.