A thin wrapper for the Java API for FoundationDB.
To get started, you need to read/use the functions defined in src/clj_fdb/core.clj. The impatient reader can jump to the Examples section to see the functions in action.
At the moment, this ns provides the following functions:
- set
- get
- clear
- get-range
- clear-range
- set-subspaced-key
- get-subspaced-key
- clear-subspaced-key
- get-subspaced-range
- clear-subspaced-range
Since FDB only stores data as bytes, these functions will use the byte-streams library to try and convert the input to byte-arrays. You can also pass your own custom functions to convert data to/from byte-arrays.
The idea is to write a really thin "clojure-y" wrapper on top of the
Java API. The core.clj
file provides wrapped functions that make
using the API simpler, but you should be able to drop down when you
need to. I've chosen to mimic the directory structure of the
underlying Java driver. So the style is as follows:
- `src/clj_fdb/` mimics `com.apple.foundationdb` (with
`transaction.clj` and `FDB.clj`)
- `src/clj_fdb/tuple/` mimics `com.apple.foundationdb.tuple` (with
`tuple.clj`)
... and so on. I haven't gotten around to actually writing the other
parts of the Java API at the moment. Going through transaction.clj
or tuple.clj
or FDB.clj
will give you a clear idea of what I have
in mind, please help me by contributing PRs!
The complete documentation is available at: https://vedang.github.io/clj_fdb/
Currently, installing this library from Clojars is broken. This is because the FoundationDB Java API Jar is not available for download from a central repository like Maven. To use this library, you need to download and install it locally. You can do this as follows:
- Download the FoundationDB Java API Jar from here: https://www.foundationdb.org/downloads/5.1.7/bindings/java/fdb-java-5.1.7.jar
- Install it to your Maven repository as follows:
$ mvn install:install-file -Dfile=fdb-java-5.1.7.jar -DgroupId=com.apple.foundationdb -DartifactId=fdb-java -Dversion=5.1.7 -Dpackaging=jar
- Download this library from Github by cloning this project.
- Run the following command in the top level of the library
$ lein do clean, compile, install
- Use the library in your Clojure projects by adding the dep in
project.clj
[clj-fdb "0.1.0"]
Here is some test code to demonstrate how to use the functions defined in the core ns:
(comment
(require '[byte-streams :as bs]
'[clj-fdb.FDB :as cfdb]
'[clj-fdb.core :as fc]
'[clj-fdb.transaction :as ftr]
'[clj-fdb.tuple.tuple :as ftup]
'[clj-fdb.subspace.subspace :as fsubspace])
;; Set a value in the DB.
(let [fdb (cfdb/select-api-version 510)]
(with-open [db (cfdb/open fdb)]
(fc/set db "a:test:key" "some value")))
;; => nil
;; Read this value back in the DB.
(let [fdb (cfdb/select-api-version 510)]
(with-open [db (cfdb/open fdb)]
(fc/get db "a:test:key" :valfn bs/to-string)))
;; => "some value"
;; FDB's Tuple Layer is super handy for efficient range reads. Each
;; element of the tuple can act as a prefix (from left to right).
(let [fdb (cfdb/select-api-version 510)]
(with-open [db (cfdb/open fdb)]
(fc/set db (ftup/from "test" "keys" "A") "value A")
(fc/set db (ftup/from "test" "keys" "B") "value B")
(fc/set db (ftup/from "test" "keys" "C") "value C")
(fc/get-range db
(ftup/range (ftup/from "test" "keys"))
:keyfn (comp ftup/get-items ftup/from-bytes)
:valfn bs/to-string)))
;; => {["test" "keys" "A"] "value A",
;; ["test" "keys" "B"] "value B",
;; ["test" "keys" "C"] "value C"}
;; FDB's Subspace Layer provides a neat way to logically namespace keys.
(let [fdb (cfdb/select-api-version 510)
subspace (fsubspace/create-subspace (ftup/from "test" "keys"))]
(with-open [db (cfdb/open fdb)]
(fc/set-subspaced-key db subspace (ftup/from "A") "Value A")
(fc/set-subspaced-key db subspace (ftup/from "B") "Value B")
(fc/get-subspaced-key db subspace (ftup/from "A")
:valfn #(bs/convert % String))
(fc/get-subspaced-range db subspace (ftup/from)
:keyfn (comp ftup/get-items ftup/from-bytes)
:valfn #(bs/convert % String))))
;; => {["test" "keys" "A"] "Value A", ["test" "keys" "B"] "Value B"}
;; FDB's functions are beautifully composable. So you needn't
;; execute each step of the above function in independent
;; transactions. You can perform them all inside a single
;; transaction. (with the full power of ACID behind you)
(let [fdb (cfdb/select-api-version 510)]
(with-open [db (cfdb/open fdb)]
(ftr/run db
(fn [tr]
(fc/set tr (ftup/from "test" "keys" "A") "value inside transaction A")
(fc/set tr (ftup/from "test" "keys" "B") "value inside transaction B")
(fc/set tr (ftup/from "test" "keys" "C") "value inside transaction C")
(fc/get-range tr
(ftup/range (ftup/from "test" "keys"))
:keyfn (comp ftup/get-items ftup/from-bytes)
:valfn bs/to-string)))))
;; => {["test" "keys" "A"] "value inside transaction A",
;; ["test" "keys" "B"] "value inside transaction B",
;; ["test" "keys" "C"] "value inside transaction C"}
;; The beauty and power of this is here:
(let [fdb (cfdb/select-api-version 510)]
(with-open [db (cfdb/open fdb)]
(try (ftr/run db
(fn [tr]
(fc/set tr (ftup/from "test" "keys" "A") "NEW value A")
(fc/set tr (ftup/from "test" "keys" "B") "NEW value B")
(fc/set tr (ftup/from "test" "keys" "C") "NEW value C")
(throw (ex-info "I don't like completing transactions"
{:boo :hoo}))))
(catch Exception _
(fc/get-range db
(ftup/range (ftup/from "test" "keys"))
:keyfn (comp ftup/get-items ftup/from-bytes)
:valfn bs/to-string)))))
;; => {["test" "keys" "A"] "value inside transaction A",
;; ["test" "keys" "B"] "value inside transaction B",
;; ["test" "keys" "C"] "value inside transaction C"}
;; No change to the values because the transaction did not succeed!
;; I hope this helps you get started with using this library!
)
I started writing this code in order to write the example that FoundationDB has documented here: https://apple.github.io/foundationdb/class-scheduling-java.html
This library has taken shape as a side-effect of trying to write that example in Clojure.
You can find the Class Scheduler example in the top-level examples/
folder
(here).
This gives the reader a good idea of how to use clj-fdb
. Refer to
the comment block at the end of the example for how to run the
example.
There are no tests at the moment. I do plan on writing them at some point in time. Please contribute tests if you can!