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.
CodePudding user response:
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.