An embedded key/value database for Go.
 
 
Go to file
Ben Johnson e6a6383ed5 Add pre-alpha badge. 2014-02-04 12:27:30 -07:00
LICENSE Initial commit 2013-12-20 10:26:14 -08:00
Makefile Initial db.open. 2014-01-11 22:51:01 -07:00
NOTES Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
README.md Add pre-alpha badge. 2014-02-04 12:27:30 -07:00
TODO Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
bucket.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
const.go Clean up API. 2014-01-31 12:22:58 -05:00
cursor.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
db.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
db_test.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
doc.go Clean up API. 2014-01-31 12:22:58 -05:00
error.go Clean up API. 2014-01-31 12:22:58 -05:00
meta.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
node.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
node_test.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
os.go TODO 2014-01-24 16:32:18 -07:00
os_test.go Intermediate. 2014-01-27 10:11:54 -05:00
page.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
quick_test.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
rwtransaction.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
rwtransaction_test.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
stat.go TODO 2014-01-24 16:32:18 -07:00
sys.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
sys_test.go Add RWTransaction.write(). 2014-01-30 22:20:50 -05:00
syscall_darwin.go Mock syscall. 2014-01-12 15:50:35 -07:00
syscall_darwin_test.go Mock syscall. 2014-01-12 15:50:35 -07:00
syscall_linux.go Fix Linux build. 2014-01-30 16:56:34 -05:00
syscall_linux_test.go Fix Linux build. 2014-01-30 16:56:34 -05:00
transaction.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00
warn.go Add RWTransaction.Put(). 2014-02-01 12:30:37 -05:00

README.md

Bolt Build Status Coverage Status Project status

Overview

Bolt is a pure Go key/value store inspired by Howard Chu and the LMDB project. The goal of the project is to provide a simple, fast, and reliable database for projects that don't require a full database server such as Postgres or MySQL. It is also meant to be educational. Most of us use tools without understanding how the underlying data really works.

Since Bolt is meant to be used as such a low-level piece of functionality, simplicity is key. The API will be small and only center around getting values and setting values. That's it. If you want to see additional functionality added then we encourage you submit a Github issue and we can discuss developing it as a separate fork.

Simple is the new beautiful.

Tobias Lütke

Project Status

Bolt is currently in development and is currently not functional.

Comparing Bolt vs LMDB

Bolt is inspired by LMDB so there are many similarities between the two:

  1. Both use a B+Tree data structure.

  2. ACID semantics with fully serializable transactions.

  3. Lock-free MVCC support using a single writer and multiple readers.

There are also several differences between Bolt and LMDB:

  1. LMDB supports more additional features such as multi-value keys, fixed length keys, multi-key insert, direct writes, and bi-directional cursors. Bolt only supports basic Get(), Put(), and Delete() operations and unidirectional cursors.

  2. LMDB databases can be shared between processes. Bolt only allows a single process to use a database at a time.

  3. LMDB is written in C and extremely fast. Bolt is written in pure Go and, while it's fast, it is not as fast as LMDB.

  4. LMDB is a more mature library and is used heavily in projects such as OpenLDAP.

So why use Bolt? The goal of Bolt is provide a simple, fast data store that is easily integrated into Go projects. The library does not require CGO so it is compatible with go get and you can easily build static binaries with it. We are not accepting additional functionality into the library so the API and file format are stable. Bolt also has near 100% unit test coverage and also includes heavy black box testing using the testing/quick package.

API

Database

The database is the object that represents your data as a whole It is split up into buckets which is analogous to tables in a relational database.

Opening and closing a database

db := DB()
err := db.Open("/path/to/db", 0666)
...
err := db.Close()

Transactions

Versioning of data in the bucket data happens through a Transaction. These transactions can be either be read-only or read/write transactions. Transactions are what keeps Bolt consistent and allows data to be rolled back if needed.

It may seem strange that read-only access needs to be wrapped in a transaction but this is so Bolt can keep track of what version of the data is currently in use. The space used to hold data is kept until the transaction closes.

One important note is that long running transactions can cause the database to grow in size quickly. Please double check that you are appropriately closing all transactions after you're done with them.

Creating and closing a read-only transaction

t, err := db.Transaction()
t.Close()

Creating and committing a read/write transaction

t, err := db.RWTransaction()
err := t.Commit()

Creating and aborting a read/write transaction

t, err := db.RWTransaction()
err := t.Abort()

Buckets

Buckets are where your actual key/value data gets stored. You can create new buckets from the database and look them up by name.

Creating a bucket

t, err := db.RWTransaction()
err := t.CreateBucket("widgets")

Renaming a bucket

t, err := db.RWTransaction()
err := t.RenameBucket("widgets", "woojits")

Deleting a bucket

t, err := db.RWTransaction()
err := t.DeleteBucket("widgets")

Retrieve an existing bucket

t, err := db.Transaction()
b, err := t.Bucket("widgets")

Retrieve a list of all buckets

t, err := db.Transaction()
buckets, err := db.Buckets()

Key/Value Access

Retrieve a value for a specific key

t, err := db.Transaction()
value, err := t.Get("widgets", []byte("foo"))
value, err := t.GetString("widgets", "foo")

Set the value for a key

t, err := db.RWTransaction()
err := t.Put("widgets", []byte("foo"), []byte("bar"))
err := t.PutString("widgets", "foo", "bar")

Delete a given key

t, err := db.RWTransaction()
err := t.Delete("widgets", []byte("foo"))
err := t.DeleteString("widgets", "foo")

Cursors

Cursors provide fast read-only access to a specific bucket within a transaction.

Creating a read-only cursor

t, err := db.Transaction()
c, err := b.Cursor("widgets")

Iterating over a cursor

for k, v, err := c.First(); k != nil; k, v, err = c.Next() {
	if err != nil {
		return err
	}
	... DO SOMETHING ...
}

Internals

The Bolt database is meant to be a clean, readable implementation of a fast single-level key/value data store. This section gives an overview of the basic concepts and structure of the file format.

B+ Tree

Bolt uses a data structure called an append-only B+ tree to store its data. This structure allows for efficient traversal of data.

TODO: Explain better. :)

Pages

Bolt stores its data in discrete units called pages. The page size can be configured but is typically between 4KB and 32KB.

There are several different types of pages:

  • Meta pages - The first two pages in a database are meta pages. These are used to store references to root pages for system buckets as well as keep track of the last transaction identifier.

  • Branch pages - These pages store references to the location of deeper branch pages or leaf pages.

  • Leaf pages - These pages store the actual key/value data.

  • Overflow pages - These are special pages used when a key's data is too large for a leaf page and needs to spill onto additional pages.

Nodes

Within each page there are one or more elements called nodes. In branch pages, these nodes store references to other child pages in the tree. In leaf pages, these nodes store the actual key/value data.

TODO

The following is a list of items to do on the Bolt project:

  1. Calculate freelist on db.Open(). (Traverse branches, set bitmap index, load free pages into a list -- lazy load in the future).
  2. Resize map. (Make sure there are no reader txns before resizing)
  3. DB.Copy()
  4. Merge pages.
  5. Rebalance (after deletion).