6

Bolt obtains a file lock on the data file so multiple processes cannot open the same database at the same time. Opening an already open Bolt database will cause it to hang until the other process closes it.

As this is the case,is there any connection pooling concept like various clients connecting and accessing the database at the same time.? Is this possible in boltdb?Like there are various connections reading and writing in the database at the same time.How it can be implemented?

Didier Spezia
  • 70,911
  • 12
  • 189
  • 154
Raunak Dugar
  • 247
  • 3
  • 14
  • 1
    Being a file-based DB, it's unlikely to change due to the way file locks work. This is also probably a better question on the Bolt repository rather than StackOverflow: https://github.com/boltdb/bolt – elithrar Jun 08 '15 at 10:53

2 Answers2

10

A Bolt database is usually embedded into a larger program and is not used over the network like you would with shared databases (think SQLite vs MySQL). Using Bolt is a bit like having a persistent map[[]byte][]byte if that were possible. Depending on what you are doing, you might want to just use something like Redis.

That said, if you need to use Bolt this way, it is not very difficult to wrap with a simple server. Here is an example that writes/reads keys from a Bolt DB over HTTP. You can use Keep-Alive for connection pooling.

Code at: https://github.com/skyec/boltdb-server

package main

import (
    "flag"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "time"

    "github.com/boltdb/bolt"
    "github.com/gorilla/mux"
)

type server struct {
    db *bolt.DB
}

func newServer(filename string) (s *server, err error) {
    s = &server{}
    s.db, err = bolt.Open(filename, 0600, &bolt.Options{Timeout: 1 * time.Second})
    return
}

func (s *server) Put(bucket, key, contentType string, val []byte) error {
    return s.db.Update(func(tx *bolt.Tx) error {
        b, err := tx.CreateBucketIfNotExists([]byte(bucket))
        if err != nil {
            return err
        }
        if err = b.Put([]byte(key), val); err != nil {
            return err
        }
        return b.Put([]byte(fmt.Sprintf("%s-ContentType", key)), []byte(contentType))
    })
}

func (s *server) Get(bucket, key string) (ct string, data []byte, err error) {
    s.db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(bucket))
        r := b.Get([]byte(key))
        if r != nil {
            data = make([]byte, len(r))
            copy(data, r)
        }

        r = b.Get([]byte(fmt.Sprintf("%s-ContentType", key)))
        ct = string(r)
        return nil
    })
    return
}

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)

    if vars["bucket"] == "" || vars["key"] == "" {
        http.Error(w, "Missing bucket or key", http.StatusBadRequest)
        return
    }

    switch r.Method {
    case "POST", "PUT":
        data, err := ioutil.ReadAll(r.Body)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        err = s.Put(vars["bucket"], vars["key"], r.Header.Get("Content-Type"), data)
        w.WriteHeader(http.StatusOK)
    case "GET":
        ct, data, err := s.Get(vars["bucket"], vars["key"])
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Add("Content-Type", ct)
        w.Write(data)
    }
}

func main() {
    var (
        addr   string
        dbfile string
    )

    flag.StringVar(&addr, "l", ":9988", "Address to listen on")
    flag.StringVar(&dbfile, "db", "/var/data/bolt.db", "Bolt DB file")
    flag.Parse()

    log.Println("Using Bolt DB file:", dbfile)
    log.Println("Listening on:", addr)

    server, err := newServer(dbfile)
    if err != nil {
        log.Fatalf("Error: %s", err)
    }

    router := mux.NewRouter()
    router.Handle("/v1/buckets/{bucket}/keys/{key}", server)
    http.Handle("/", router)

    log.Fatal(http.ListenAndServe(addr, nil))
}
SkyeC
  • 536
  • 2
  • 5
  • Thanks a lot! I understood how its different from other databases which are shared on the network.will have it owned by one process which exposes an API over the network. – Raunak Dugar Jun 09 '15 at 12:10
  • Sounds good. One benefit of wrapping a storage engine like this is you can build the interface to meet your specific needs. Are you only using small keys and values where throughput matters? Make it a UDP interface. Or maybe a protobuf interface is more suitable for you. I'm going to keep tinkering with this code as a side project. So will probably try each of these. Best of luck. – SkyeC Jun 09 '15 at 12:52
  • There are various unique id and all are bidding a amount at a rate of milliseconds and I have to store it and update their current spend(the total sum of the bids till that time) asap. The schema i am using is like --a bucket for every unique id and storing the time as the key and value as the bid. --a common bucket for all unique and updating their current spend in that where key=unique id and value = latest current spend . As in this case which interface should I prefer. And how can I increase the rate of updating the value i.e.should i use db.Update() or db.Batch() and how ? – Raunak Dugar Jun 10 '15 at 04:25
  • This is kind of going off topic for this question. To continue this discussion, feel free to open an issue on the github project I included. – SkyeC Jun 11 '15 at 01:14
  • In boltdb how can efficient data sharding, data re-balancing and data distribution can be done ? – Raunak Dugar Jun 11 '15 at 10:00
  • This is usable code except for the fact that the Get method doesn't check that a valid bucket is returned. Something like ... if b == nil { fmt.Printf("Yikes! Bucket %s not found!", b) return fmt.Errorf("Bucket %q not found!", b) } – auro Nov 26 '15 at 02:29
4

There is no connection pooling concept in boltdb, because there is no connection. It is not a client/server database, it is an embedded database (like sqlite or Berkeley-DB).

Boltdb is designed so that multiple goroutines of the same process can access the database at the same time (using different transactions). The model is single writer, multiple readers. Boltdb is not designed to support accesses from multiple processes.

If you need a Go program to use an embedded database supporting access from multiple processes at the same time, you may want to have a look at the wrappers over LMDB, such as:

Didier Spezia
  • 70,911
  • 12
  • 189
  • 154