github.com/coocood/badger@v1.5.1-0.20200528065104-c02ac3616d04/README.md (about)

     1  # BadgerDB [![GoDoc](https://godoc.org/github.com/coocood/badger?status.svg)](https://godoc.org/github.com/coocood/badger) [![Go Report Card](https://goreportcard.com/badge/github.com/coocood/badger)](https://goreportcard.com/report/github.com/coocood/badger) [![Build Status](https://teamcity.dgraph.io/guestAuth/app/rest/builds/buildType:(id:Badger_UnitTests)/statusIcon.svg)](https://teamcity.dgraph.io/viewLog.html?buildTypeId=Badger_UnitTests&buildId=lastFinished&guest=1) ![Appveyor](https://ci.appveyor.com/api/projects/status/github/dgraph-io/badger?branch=master&svg=true) [![Coverage Status](https://coveralls.io/repos/github/dgraph-io/badger/badge.svg?branch=master)](https://coveralls.io/github/dgraph-io/badger?branch=master)
     2  
     3  ![Badger mascot](images/diggy-shadow.png)
     4  
     5  BadgerDB is an embeddable, persistent, simple and fast key-value (KV) database
     6  written in pure Go. It's meant to be a performant alternative to non-Go-based
     7  key-value stores like [RocksDB](https://github.com/facebook/rocksdb).
     8  
     9  ## Project Status
    10  Badger v1.0 was released in Nov 2017. Check the [Changelog] for the full details.
    11  
    12  [Changelog]:https://github.com/coocood/badger/blob/master/CHANGELOG.md
    13  
    14  We introduced transactions in [v0.9.0] which involved a major API change. If you have a Badger
    15  datastore prior to that, please use [v0.8.1], but we strongly urge you to upgrade. Upgrading from
    16  both v0.8 and v0.9 will require you to [take backups](#database-backup) and restore using the new
    17  version.
    18  
    19  [v1.0.1]: //github.com/coocood/badger/tree/v1.0.1
    20  [v0.8.1]: //github.com/coocood/badger/tree/v0.8.1
    21  [v0.9.0]: //github.com/coocood/badger/tree/v0.9.0
    22  
    23  ## Table of Contents
    24   * [Getting Started](#getting-started)
    25      + [Installing](#installing)
    26      + [Opening a database](#opening-a-database)
    27      + [Transactions](#transactions)
    28        - [Read-only transactions](#read-only-transactions)
    29        - [Read-write transactions](#read-write-transactions)
    30        - [Managing transactions manually](#managing-transactions-manually)
    31      + [Using key/value pairs](#using-keyvalue-pairs)
    32      + [Iterating over keys](#iterating-over-keys)
    33        - [Prefix scans](#prefix-scans)
    34        - [Key-only iteration](#key-only-iteration)
    35      + [Garbage Collection](#garbage-collection)
    36      + [Database backup](#database-backup)
    37      + [Memory usage](#memory-usage)
    38      + [Statistics](#statistics)
    39    * [Resources](#resources)
    40      + [Blog Posts](#blog-posts)
    41    * [Contact](#contact)
    42    * [Design](#design)
    43      + [Comparisons](#comparisons)
    44      + [Benchmarks](#benchmarks)
    45    * [Other Projects Using Badger](#other-projects-using-badger)
    46    * [Frequently Asked Questions](#frequently-asked-questions)
    47  
    48  ## Getting Started
    49  
    50  ### Installing
    51  To start using Badger, install Go 1.8 or above and run `go get`:
    52  
    53  ```sh
    54  $ go get github.com/coocood/badger/...
    55  ```
    56  
    57  This will retrieve the library and install the `badger_info` command line
    58  utility into your `$GOBIN` path.
    59  
    60  
    61  ### Opening a database
    62  The top-level object in Badger is a `DB`. It represents multiple files on disk
    63  in specific directories, which contain the data for a single database.
    64  
    65  To open your database, use the `badger.Open()` function, with the appropriate
    66  options. The `Dir` and `ValueDir` options are mandatory and must be
    67  specified by the client. They can be set to the same value to simplify things.
    68  
    69  ```go
    70  package main
    71  
    72  import (
    73  	"log"
    74  
    75  	"github.com/coocood/badger"
    76  )
    77  
    78  func main() {
    79    // Open the Badger database located in the /tmp/badger directory.
    80    // It will be created if it doesn't exist.
    81    opts := badger.DefaultOptions
    82    opts.Dir = "/tmp/badger"
    83    opts.ValueDir = "/tmp/badger"
    84    db, err := badger.Open(opts)
    85    if err != nil {
    86  	  log.Fatal(err)
    87    }
    88    defer db.Close()
    89    // Your code here…
    90  }
    91  ```
    92  
    93  Please note that Badger obtains a lock on the directories so multiple processes
    94  cannot open the same database at the same time.
    95  
    96  ### Transactions
    97  
    98  #### Read-only transactions
    99  To start a read-only transaction, you can use the `DB.View()` method:
   100  
   101  ```go
   102  err := db.View(func(txn *badger.Txn) error {
   103    // Your code here…
   104    return nil
   105  })
   106  ```
   107  
   108  You cannot perform any writes or deletes within this transaction. Badger
   109  ensures that you get a consistent view of the database within this closure. Any
   110  writes that happen elsewhere after the transaction has started, will not be
   111  seen by calls made within the closure.
   112  
   113  #### Read-write transactions
   114  To start a read-write transaction, you can use the `DB.Update()` method:
   115  
   116  ```go
   117  err := db.Update(func(txn *badger.Txn) error {
   118    // Your code here…
   119    return nil
   120  })
   121  ```
   122  
   123  All database operations are allowed inside a read-write transaction.
   124  
   125  Always check the returned error value. If you return an error
   126  within your closure it will be passed through.
   127  
   128  An `ErrConflict` error will be reported in case of a conflict. Depending on the state
   129  of your application, you have the option to retry the operation if you receive
   130  this error.
   131  
   132  An `ErrTxnTooBig` will be reported in case the number of pending writes/deletes in
   133  the transaction exceed a certain limit. In that case, it is best to commit the
   134  transaction and start a new transaction immediately. Here is an example (we are
   135  not checking for errors in some places for simplicity):
   136  
   137  ```go
   138  updates := make(map[string]string)
   139  txn := db.NewTransaction(true)
   140  for k,v := range updates {
   141    if err := txn.Set([]byte(k),[]byte(v)); err == ErrTxnTooBig {
   142      _ = txn.Commit()
   143      txn = db.NewTransaction(..)
   144      _ = txn.Set([]byte(k),[]byte(v))
   145    }
   146  }
   147  _ = txn.Commit()
   148  ```
   149  
   150  #### Managing transactions manually
   151  The `DB.View()` and `DB.Update()` methods are wrappers around the
   152  `DB.NewTransaction()` and `Txn.Commit()` methods (or `Txn.Discard()` in case of
   153  read-only transactions). These helper methods will start the transaction,
   154  execute a function, and then safely discard your transaction if an error is
   155  returned. This is the recommended way to use Badger transactions.
   156  
   157  However, sometimes you may want to manually create and commit your
   158  transactions. You can use the `DB.NewTransaction()` function directly, which
   159  takes in a boolean argument to specify whether a read-write transaction is
   160  required. For read-write transactions, it is necessary to call `Txn.Commit()`
   161  to ensure the transaction is committed. For read-only transactions, calling
   162  `Txn.Discard()` is sufficient. `Txn.Commit()` also calls `Txn.Discard()`
   163  internally to cleanup the transaction, so just calling `Txn.Commit()` is
   164  sufficient for read-write transaction. However, if your code doesn’t call
   165  `Txn.Commit()` for some reason (for e.g it returns prematurely with an error),
   166  then please make sure you call `Txn.Discard()` in a `defer` block. Refer to the
   167  code below.
   168  
   169  ```go
   170  // Start a writable transaction.
   171  txn, err := db.NewTransaction(true)
   172  if err != nil {
   173      return err
   174  }
   175  defer txn.Discard()
   176  
   177  // Use the transaction...
   178  err := txn.Set([]byte("answer"), []byte("42"))
   179  if err != nil {
   180      return err
   181  }
   182  
   183  // Commit the transaction and check for error.
   184  if err := txn.Commit(); err != nil {
   185      return err
   186  }
   187  ```
   188  
   189  The first argument to `DB.NewTransaction()` is a boolean stating if the transaction
   190  should be writable.
   191  
   192  Badger allows an optional callback to the `Txn.Commit()` method. Normally, the
   193  callback can be set to `nil`, and the method will return after all the writes
   194  have succeeded. However, if this callback is provided, the `Txn.Commit()`
   195  method returns as soon as it has checked for any conflicts. The actual writing
   196  to the disk happens asynchronously, and the callback is invoked once the
   197  writing has finished, or an error has occurred. This can improve the throughput
   198  of the application in some cases. But it also means that a transaction is not
   199  durable until the callback has been invoked with a `nil` error value.
   200  
   201  ### Using key/value pairs
   202  To save a key/value pair, use the `Txn.Set()` method:
   203  
   204  ```go
   205  err := db.Update(func(txn *badger.Txn) error {
   206    err := txn.Set([]byte("answer"), []byte("42"))
   207    return err
   208  })
   209  ```
   210  
   211  This will set the value of the `"answer"` key to `"42"`. To retrieve this
   212  value, we can use the `Txn.Get()` method:
   213  
   214  ```go
   215  err := db.View(func(txn *badger.Txn) error {
   216    item, err := txn.Get([]byte("answer"))
   217    if err != nil {
   218      return err
   219    }
   220    val, err := item.Value()
   221    if err != nil {
   222      return err
   223    }
   224    fmt.Printf("The answer is: %s\n", val)
   225    return nil
   226  })
   227  ```
   228  
   229  `Txn.Get()` returns `ErrKeyNotFound` if the value is not found.
   230  
   231  Please note that values returned from `Get()` are only valid while the
   232  transaction is open. If you need to use a value outside of the transaction
   233  then you must use `copy()` to copy it to another byte slice.
   234  
   235  Use the `Txn.Delete()` method to delete a key.
   236  
   237  
   238  
   239  ### Iterating over keys
   240  To iterate over keys, we can use an `Iterator`, which can be obtained using the
   241  `Txn.NewIterator()` method. Iteration happens in byte-wise lexicographical sorting
   242  order.
   243  
   244  
   245  ```go
   246  err := db.View(func(txn *badger.Txn) error {
   247    opts := badger.DefaultIteratorOptions
   248    opts.PrefetchSize = 10
   249    it := txn.NewIterator(opts)
   250    defer it.Close()
   251    for it.Rewind(); it.Valid(); it.Next() {
   252      item := it.Item()
   253      k := item.Key()
   254      v, err := item.Value()
   255      if err != nil {
   256        return err
   257      }
   258      fmt.Printf("key=%s, value=%s\n", k, v)
   259    }
   260    return nil
   261  })
   262  ```
   263  
   264  The iterator allows you to move to a specific point in the list of keys and move
   265  forward or backward through the keys one at a time.
   266  
   267  By default, Badger prefetches the values of the next 100 items. You can adjust
   268  that with the `IteratorOptions.PrefetchSize` field. However, setting it to
   269  a value higher than GOMAXPROCS (which we recommend to be 128 or higher)
   270  shouldn’t give any additional benefits. You can also turn off the fetching of
   271  values altogether. See section below on key-only iteration.
   272  
   273  #### Prefix scans
   274  To iterate over a key prefix, you can combine `Seek()` and `ValidForPrefix()`:
   275  
   276  ```go
   277  db.View(func(txn *badger.Txn) error {
   278    it := txn.NewIterator(badger.DefaultIteratorOptions)
   279    defer it.Close()
   280    prefix := []byte("1234")
   281    for it.Seek(prefix); it.ValidForPrefix(prefix); it.Next() {
   282      item := it.Item()
   283      k := item.Key()
   284      v, err := item.Value()
   285      if err != nil {
   286        return err
   287      }
   288      fmt.Printf("key=%s, value=%s\n", k, v)
   289    }
   290    return nil
   291  })
   292  ```
   293  
   294  #### Key-only iteration
   295  Badger supports a unique mode of iteration called _key-only_ iteration. It is
   296  several order of magnitudes faster than regular iteration, because it involves
   297  access to the LSM-tree only, which is usually resident entirely in RAM. To
   298  enable key-only iteration, you need to set the `IteratorOptions.PrefetchValues`
   299  field to `false`. This can also be used to do sparse reads for selected keys
   300  during an iteration, by calling `item.Value()` only when required.
   301  
   302  ```go
   303  err := db.View(func(txn *badger.Txn) error {
   304    opts := badger.DefaultIteratorOptions
   305    opts.PrefetchValues = false
   306    it := txn.NewIterator(opts)
   307    defer it.Close()
   308    for it.Rewind(); it.Valid(); it.Next() {
   309      item := it.Item()
   310      k := item.Key()
   311      fmt.Printf("key=%s\n", k)
   312    }
   313    return nil
   314  })
   315  ```
   316  
   317  ### Garbage Collection
   318  Badger values need to be garbage collected, because of two reasons:
   319  
   320  * Badger keeps values separately from the LSM tree. This means that the compaction operations
   321  that clean up the LSM tree do not touch the values at all. Values need to be cleaned up
   322  separately.
   323  
   324  * Concurrent read/write transactions could leave behind multiple values for a single key, because they
   325  are stored with different versions. These could accumulate, and take up unneeded space beyond the
   326  time these older versions are needed.
   327  
   328  Badger relies on the client to perform garbage collection at a time of their choosing. It provides
   329  the following methods, which can be invoked at an appropriate time:
   330  
   331  * `DB.PurgeOlderVersions()`: This method iterates over the database, and cleans up all but the latest
   332  versions of the key-value pairs. It marks the older versions as deleted, which makes them eligible for
   333  garbage collection.
   334  * `DB.PurgeVersionsBelow(key, ts)`: This method is useful to do a more targeted clean up of older versions
   335  of key-value pairs. You can specify a key, and a timestamp. All versions of the key older than the timestamp
   336  are marked as deleted, making them eligible for garbage collection.
   337  * `DB.RunValueLogGC()`: This method is designed to do garbage collection while
   338    Badger is online. Please ensure that you call the `DB.Purge…()` methods first
   339    before invoking this method. It uses any statistics generated by the
   340    `DB.Purge(…)` methods to pick files that are likely to lead to maximum space
   341    reclamation. It loops until it encounters a file which does not lead to any
   342    garbage collection.
   343  
   344    It could lead to increased I/O if `DB.RunValueLogGC()` hasn’t been called for
   345    a long time, and many deletes have happened in the meanwhile. So it is recommended
   346    that this method be called regularly.
   347  
   348  ### Database backup
   349  There are two public API methods `DB.Backup()` and `DB.Load()` which can be
   350  used to do online backups and restores. Badger v0.9 provides a CLI tool
   351  `badger`, which can do offline backup/restore. Make sure you have `$GOPATH/bin`
   352  in your PATH to use this tool.
   353  
   354  The command below will create a version-agnostic backup of the database, to a
   355  file `badger.bak` in the current working directory
   356  
   357  ```
   358  badger backup --dir <path/to/badgerdb>
   359  ```
   360  
   361  To restore `badger.bak` in the current working directory to a new database:
   362  
   363  ```
   364  badger restore --dir <path/to/badgerdb>
   365  ```
   366  
   367  See `badger --help` for more details.
   368  
   369  If you have a Badger database that was created using v0.8 (or below), you can
   370  use the `badger_backup` tool provided in v0.8.1, and then restore it using the
   371  command above to upgrade your database to work with the latest version.
   372  
   373  ```
   374  badger_backup --dir <path/to/badgerdb> --backup-file badger.bak
   375  ```
   376  
   377  ### Memory usage
   378  Badger's memory usage can be managed by tweaking several options available in
   379  the `Options` struct that is passed in when opening the database using
   380  `DB.Open`.
   381  
   382  - `Options.ValueLogLoadingMode` can be set to `options.FileIO` (instead of the
   383    default `options.MemoryMap`) to avoid memory-mapping log files. This can be
   384    useful in environments with low RAM.
   385  - Number of memtables (`Options.NumMemtables`)
   386    - If you modify `Options.NumMemtables`, also adjust `Options.NumLevelZeroTables` and
   387     `Options.NumLevelZeroTablesStall` accordingly.
   388  - Number of concurrent compactions (`Options.NumCompactors`)
   389  - Mode in which LSM tree is loaded (`Options.TableLoadingMode`)
   390  - Size of table (`Options.MaxTableSize`)
   391  - Size of value log file (`Options.ValueLogFileSize`)
   392  
   393  If you want to decrease the memory usage of Badger instance, tweak these
   394  options (ideally one at a time) until you achieve the desired
   395  memory usage.
   396  
   397  ### Statistics
   398  Badger records metrics using the [expvar] package, which is included in the Go
   399  standard library. All the metrics are documented in [y/metrics.go][metrics]
   400  file.
   401  
   402  `expvar` package adds a handler in to the default HTTP server (which has to be
   403  started explicitly), and serves up the metrics at the `/debug/vars` endpoint.
   404  These metrics can then be collected by a system like [Prometheus], to get
   405  better visibility into what Badger is doing.
   406  
   407  [expvar]: https://golang.org/pkg/expvar/
   408  [metrics]: https://github.com/coocood/badger/blob/master/y/metrics.go
   409  [Prometheus]: https://prometheus.io/
   410  
   411  ## Resources
   412  
   413  ### Blog Posts
   414  1. [Introducing Badger: A fast key-value store written natively in
   415  Go](https://open.dgraph.io/post/badger/)
   416  2. [Make Badger crash resilient with ALICE](https://blog.dgraph.io/post/alice/)
   417  3. [Badger vs LMDB vs BoltDB: Benchmarking key-value databases in Go](https://blog.dgraph.io/post/badger-lmdb-boltdb/)
   418  4. [Concurrent ACID Transactions in Badger](https://blog.dgraph.io/post/badger-txn/)
   419  
   420  ## Design
   421  Badger was written with these design goals in mind:
   422  
   423  - Write a key-value database in pure Go.
   424  - Use latest research to build the fastest KV database for data sets spanning terabytes.
   425  - Optimize for SSDs.
   426  
   427  Badger’s design is based on a paper titled _[WiscKey: Separating Keys from
   428  Values in SSD-conscious Storage][wisckey]_.
   429  
   430  [wisckey]: https://www.usenix.org/system/files/conference/fast16/fast16-papers-lu.pdf
   431  
   432  ### Comparisons
   433  | Feature             | Badger                                       | RocksDB                       | BoltDB    |
   434  | -------             | ------                                       | -------                       | ------    |
   435  | Design              | LSM tree with value log                      | LSM tree only                 | B+ tree   |
   436  | High Read throughput | Yes                                          | No                           | Yes        |
   437  | High Write throughput | Yes                                          | Yes                           | No        |
   438  | Designed for SSDs   | Yes (with latest research <sup>1</sup>)      | Not specifically <sup>2</sup> | No        |
   439  | Embeddable          | Yes                                          | Yes                           | Yes       |
   440  | Sorted KV access    | Yes                                          | Yes                           | Yes       |
   441  | Pure Go (no Cgo)    | Yes                                          | No                            | Yes       |
   442  | Transactions        | Yes, ACID, concurrent with SSI<sup>3</sup> | Yes (but non-ACID)            | Yes, ACID |
   443  | Snapshots           | Yes                                           | Yes                           | Yes       |
   444  | TTL support         | No                                           | Yes                           | No       |
   445  
   446  <sup>1</sup> The [WISCKEY paper][wisckey] (on which Badger is based) saw big
   447  wins with separating values from keys, significantly reducing the write
   448  amplification compared to a typical LSM tree.
   449  
   450  <sup>2</sup> RocksDB is an SSD optimized version of LevelDB, which was designed specifically for rotating disks.
   451  As such RocksDB's design isn't aimed at SSDs.
   452  
   453  <sup>3</sup> SSI: Serializable Snapshot Isolation. For more details, see the blog post [Concurrent ACID Transactions in Badger](https://blog.dgraph.io/post/badger-txn/)
   454  
   455  ### Benchmarks
   456  We have run comprehensive benchmarks against RocksDB, Bolt and LMDB. The
   457  benchmarking code, and the detailed logs for the benchmarks can be found in the
   458  [badger-bench] repo. More explanation, including graphs can be found the blog posts (linked
   459  above).
   460  
   461  [badger-bench]: https://github.com/coocood/badger-bench
   462  
   463  ## Other Projects Using Badger
   464  Below is a list of known projects that use Badger:
   465  
   466  * [0-stor](https://github.com/zero-os/0-stor) - Single device object store.
   467  * [Dgraph](https://github.com/coocood/dgraph) - Distributed graph database.
   468  * [Sandglass](https://github.com/celrenheit/sandglass) - distributed, horizontally scalable, persistent, time sorted message queue.
   469  * [Usenet Express](https://usenetexpress.com/) - Serving over 300TB of data with Badger.
   470  * [go-ipfs](https://github.com/ipfs/go-ipfs) - Go client for the InterPlanetary File System (IPFS), a new hypermedia distribution protocol.
   471  * [gorush](https://github.com/appleboy/gorush) - A push notification server written in Go.
   472  
   473  If you are using Badger in a project please send a pull request to add it to the list.
   474  
   475  ## Frequently Asked Questions
   476  - **My writes are getting stuck. Why?**
   477  
   478  This can happen if a long running iteration with `Prefetch` is set to false, but
   479  a `Item::Value` call is made internally in the loop. That causes Badger to
   480  acquire read locks over the value log files to avoid value log GC removing the
   481  file from underneath. As a side effect, this also blocks a new value log GC
   482  file from being created, when the value log file boundary is hit.
   483  
   484  Please see Github issues [#293](https://github.com/coocood/badger/issues/293)
   485  and [#315](https://github.com/coocood/badger/issues/315).
   486  
   487  There are multiple workarounds during iteration:
   488  
   489  1. Use `Item::ValueCopy` instead of `Item::Value` when retrieving value.
   490  1. Set `Prefetch` to true. Badger would then copy over the value and release the
   491     file lock immediately.
   492  1. When `Prefetch` is false, don't call `Item::Value` and do a pure key-only
   493     iteration. This might be useful if you just want to delete a lot of keys.
   494  1. Do the writes in a separate transaction after the reads.
   495  
   496  - **My writes are really slow. Why?**
   497  
   498  Are you creating a new transaction for every single key update? This will lead
   499  to very low throughput. To get best write performance, batch up multiple writes
   500  inside a transaction using single `DB.Update()` call. You could also have
   501  multiple such `DB.Update()` calls being made concurrently from multiple
   502  goroutines.
   503  
   504  - **I don't see any disk write. Why?**
   505  
   506  If you're using Badger with `SyncWrites=false`, then your writes might not be written to value log
   507  and won't get synced to disk immediately. Writes to LSM tree are done inmemory first, before they
   508  get compacted to disk. The compaction would only happen once `MaxTableSize` has been reached. So, if
   509  you're doing a few writes and then checking, you might not see anything on disk. Once you `Close`
   510  the database, you'll see these writes on disk.
   511  
   512  - **Reverse iteration doesn't give me the right results.**
   513  
   514  Just like forward iteration goes to the first key which is equal or greater than the SEEK key, reverse iteration goes to the first key which is equal or lesser than the SEEK key. Therefore, SEEK key would not be part of the results. You can typically add a tilde (~) as a suffix to the SEEK key to include it in the results. See the following issues: [#436](https://github.com/coocood/badger/issues/436) and [#347](https://github.com/coocood/badger/issues/347).
   515  
   516  - **Which instances should I use for Badger?**
   517  
   518  We recommend using instances which provide local SSD storage, without any limit
   519  on the maximum IOPS. In AWS, these are storage optimized instances like i3. They
   520  provide local SSDs which clock 100K IOPS over 4KB blocks easily.
   521  
   522  - **I'm getting a closed channel error. Why?**
   523  
   524  ```
   525  panic: close of closed channel
   526  panic: send on closed channel
   527  ```
   528  
   529  If you're seeing panics like above, this would be because you're operating on a closed DB. This can happen, if you call `Close()` before sending a write, or multiple times. You should ensure that you only call `Close()` once, and all your read/write operations finish before closing.
   530  
   531  - **Are there any Go specific settings that I should use?**
   532  
   533  We *highly* recommend setting a high number for GOMAXPROCS, which allows Go to
   534  observe the full IOPS throughput provided by modern SSDs. In Dgraph, we have set
   535  it to 128. For more details, [see this
   536  thread](https://groups.google.com/d/topic/golang-nuts/jPb_h3TvlKE/discussion).
   537  
   538  - Are there any linux specific settings that I should use?
   539  
   540  We recommend setting max file descriptors to a high number depending upon the expected size of you data.
   541  
   542  ## Contact
   543  - Please use [discuss.dgraph.io](https://discuss.dgraph.io) for questions, feature requests and discussions.
   544  - Please use [Github issue tracker](https://github.com/coocood/badger/issues) for filing bugs or feature requests.
   545  - Join [![Slack Status](http://slack.dgraph.io/badge.svg)](http://slack.dgraph.io).
   546  - Follow us on Twitter [@dgraphlabs](https://twitter.com/dgraphlabs).
   547