goboltdb

golang http handler context


I'm trying to understand variable scopes in golang with the following code. In this example, calling in http a page will echo the uri query combined with a stored value in Boltdb.

The problem is that the database driver doesn't seem to run correctly in the http handler context: it doesn't print anything to stdout nor to the http request.

I was expecting it to print :

He's loving <'uri query content'> but prefers pizza (data from bolt.db driver)

How to fix this code? package main

import (
    "fmt"
    "net/http"
    "log"
    "github.com/boltdb/bolt"
)

var db bolt.DB

func handler(w http.ResponseWriter, r *http.Request) {
    dberr := db.Update(func(tx *bolt.Tx) error {
    log.Println("here")
        b := tx.Bucket([]byte("MyBucket"))
            loving := b.Get([]byte("loving"))
        log.Printf("He's loving %s but prefers %s",r.URL.Path[1:], string(loving))
            fmt.Fprintf(w,"He's loving %s but prefers %s",r.URL.Path[1:], string(loving) )
        return nil
    })
    if dberr != nil {
        fmt.Errorf("db update: %s", dberr)
    }
    log.Printf("Finished handling")
}

func main() {

    db, err := bolt.Open("my.db", 0600, nil)
    if err != nil {
        log.Fatal(err)
    }else{
        log.Println("database opened")
    }
    dberr := db.Update(func(tx *bolt.Tx) error {
        b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
        if err != nil {
        return fmt.Errorf("create bucket: %s", err)
        }
        err2 := b.Put([]byte("loving"), []byte("pizza"))
        if err2 != nil {
        return fmt.Errorf("put loving: %s", err2)
        }
        loving := b.Get([]byte("loving"))
        log.Printf("He's loving %s", string(loving))
        return nil
    })

        if dberr != nil {
        fmt.Errorf("db update: %s", err)
        }
    defer db.Close()

    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)

   }

Solution

  • I think I see your bug. This one is usually a little difficult to track because its just the : in front of the equals. It was basically a scoping issue because you declared db as a global while at the same time creating a db variable that was scoped to your main function.

    You used db, err := ... to assign the values instead of just =. := will both declare and infer the type. Since its also doing declaration, the db you're using in the main function is not the db you have declared in the global scope. Meanwhile the handler is still attempting to use the db that was declared in the global scope. The below code is the same code as you initially had with a few comments in the code to outline what the working changes are. Hope this helps!

    package main
    
    import (
        "fmt"
        "log"
        "net/http"
    
        "github.com/boltdb/bolt"
    )
    
    var db *bolt.DB // this is going to be a pointer and is going to be nil until its set by the main function
    
    func handler(w http.ResponseWriter, r *http.Request) {
        dberr := db.Update(func(tx *bolt.Tx) error {
            log.Println("here")
            b := tx.Bucket([]byte("MyBucket"))
            loving := b.Get([]byte("loving"))
            log.Printf("He's loving %s but prefers %s", r.URL.Path[1:], string(loving))
            fmt.Fprintf(w, "He's loving %s but prefers %s", r.URL.Path[1:], string(loving))
            return nil
        })
        if dberr != nil {
            fmt.Errorf("db update: %s", dberr)
        }
        log.Printf("Finished handling")
    }
    
    func main() {
        var err error                           // this will have to be declared because of the next line to assign db the first value returned from `bolt.Open`
        db, err = bolt.Open("my.db", 0600, nil) // notice that this has changed and is no longer `db, err := ...` rather its `db, err = ...`
        if err != nil {
            log.Fatal(err)
        } else {
            log.Println("database opened")
        }
        dberr := db.Update(func(tx *bolt.Tx) error {
            b, err := tx.CreateBucketIfNotExists([]byte("MyBucket"))
            if err != nil {
                return fmt.Errorf("create bucket: %s", err)
            }
            err2 := b.Put([]byte("loving"), []byte("pizza"))
            if err2 != nil {
                return fmt.Errorf("put loving: %s", err2)
            }
            loving := b.Get([]byte("loving"))
            log.Printf("He's loving %s", string(loving))
            return nil
        })
    
        if dberr != nil {
            fmt.Errorf("db update: %s", err)
        }
        defer db.Close()
    
        http.HandleFunc("/", handler)
        http.ListenAndServe(":3000", nil)
    }