Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'develop'

  • Loading branch information...
commit 8b439a2199543bb9ff99d7e783bd34725db33792 2 parents 524df6b + d8e0b62
@ninjudd ninjudd authored
View
8 .gitignore
@@ -1,11 +1,5 @@
-.cake
pom.xml
*.jar
*.war
-lib
-classes
-build
-/ruminate
-docs
+target/
.lein*
-native
View
7 README.md
@@ -8,11 +8,12 @@ Currently supported by Masai are Tokyo Cabinent and Redis. Memcached support is
# Getting It
-You can get Masai from [clojars](http://clojars.org) with [cake](https://github.com/ninjudd/cake). Just add it as a dependency to your project like so:
+You can get Masai from [clojars](http://clojars.org) with [leiningen](https://github.com/technomancy/leiningen).
+Just add it as a dependency to your project like so:
- [masai "0.6.0"]
+ [masai "0.7.0"]
-Masai doesn't download the necessary dependencies for all of its backends. Our reasoning is that it would be wasteful to require users to download dependencies for all of the backends when they only need one of them. Therefore, you'll need to add dependencies for whatever backend you plan to use. You'll want to look in Masai's project.clj for `:dev-dependencies`. Those are the dependencies that we test Masai with for each backend. Use whatever dependencies you need from there.
+Masai doesn't download the necessary dependencies for all of its backends. Our reasoning is that it would be wasteful to require users to download dependencies for all of the backends when they only need one of them. Therefore, you'll need to add dependencies for whatever backend you plan to use. You'll want to look in Masai's project.clj for the `:dev` profile. Those are the dependencies that we test Masai with for each backend. Use whatever dependencies you need from there.
# Usage
View
8 project.clj
@@ -1,14 +1,14 @@
-(defproject masai "0.7.0"
+(defproject org.flatland/masai "0.8.0"
:url "https://github.com/flatland/masai"
:license {:name "Eclipse Public License - v 1.0"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:description "Key-value database for Clojure with pluggable backends."
:dependencies [[org.clojure/clojure "1.4.0"]
- [useful "0.8.4"]
- [retro "0.7.0"]]
+ [org.flatland/useful "0.9.0"]
+ [org.flatland/retro "0.8.0"]]
:profiles {:1.5 {:dependencies [[org.clojure/clojure "1.5.0-master-SNAPSHOT"]]}
:1.3 {:dependencies [[org.clojure/clojure "1.3.0"]]}
- :dev {:dependencies [[tokyocabinet "1.24.4"]
+ :dev {:dependencies [[org.flatland/tokyocabinet "1.24.6"]
[spy/memcached "2.4rc1"]
[org.clojars.raynes/jedis "2.0.0-SNAPSHOT"]]}}
:aliases {"testall" ["with-profile" "dev,default:dev,1.3,default:dev,1.5,default" "test"]}
View
2  src/masai/cursor.clj → src/flatland/masai/cursor.clj
@@ -1,4 +1,4 @@
-(ns masai.cursor
+(ns flatland.masai.cursor
(:refer-clojure :exclude [next key val]))
(defprotocol Cursor
View
79 src/masai/db.clj → src/flatland/masai/db.clj
@@ -1,6 +1,7 @@
-(ns masai.db
- (:require [masai.cursor :as c])
- (:use [useful.macro :only [macro-do]]))
+(ns flatland.masai.db
+ (:require [flatland.masai.cursor :as c]
+ [flatland.useful.macro :refer [macro-do]]
+ [flatland.useful.io :refer [compare-bytes]]))
;; Instead of having separate, incompatible libraries to interface with
;; different key-value stores, Masai opts to define a common and simple
@@ -28,6 +29,8 @@
"Get the length of a record from the database. Returns -1 if record is non-existent.")
(exists? [db key]
"Check to see if a record exists in the database.")
+
+ ;; TODO replace with fetch-[key-][r]seq
(key-seq [db]
"Get a sequence of all of the keys in the database.")
(add! [db key val]
@@ -58,38 +61,40 @@
"Return a Cursor on this db, starting at key. The key may be an actual string key, or one of
the special keywords :first or :last"))
-;; have to do this as a macro instead of a function-generator, because protocol functions
-;; (like c/next) don't behave well when closed over. instead, this macro includes them as
-;; literals so that the compiler knows how to handle them.
-(macro-do [name next-fn default-key]
- `(let [default# ~default-key]
- (defn ~name [db# key#]
- (seq (->> (cursor db# (or key# default#))
- (iterate ~next-fn)
- (take-while (complement nil?))
- (map (juxt #(String. (c/key %) "UTF-8") c/val))))))
- fetch-seq c/next :first,
- fetch-rseq c/prev :last)
-
(letfn [(include [test key]
- (fn [[k]] (test (compare k key) 0)))
- (subseq* ;; A helper for creating a subseq or rsubseq using fetch-seq and fetch-rseq.
- ([fetch-seq bounded? db test key]
- (seq (let [include? (include test key)]
- (if (bounded? test)
- (drop-while (complement include?) (fetch-seq db key))
- (take-while include? (fetch-seq db nil))))))
- ([fetch-seq db start-test start-key end-test end-key]
- (seq (->> (fetch-seq db start-key)
- (drop-while (complement (include start-test start-key)))
- (take-while (include end-test end-key))))))]
- (defn fetch-subseq
- ([db test key]
- (subseq* fetch-seq #{>= >} db test key))
- ([db start-test start end-test end]
- (subseq* fetch-seq db start-test start end-test end)))
- (defn fetch-rsubseq
- ([db test key]
- (subseq* fetch-rseq #{<= <} db test key))
- ([db start-test start end-test end]
- (subseq* fetch-rseq db end-test end start-test start))))
+ (if key
+ (fn [[k]] (test (compare-bytes k key) 0))
+ (constantly true)))]
+ (defn- subseq-fn
+ [& {:keys [reverse? keys-only?]}]
+ (let [val-fn (apply juxt #'c/key (when-not keys-only? [#'c/val]))
+ [bounded? default next-fn] (if reverse?
+ [#{<= <} :last #'c/prev]
+ [#{>= >} :first #'c/next])
+ fetch-seq (fn [db key]
+ (->> (cursor db (or key default))
+ (iterate next-fn)
+ (take-while (complement nil?))
+ (map val-fn)))
+ val-seq (if keys-only?
+ (comp seq (partial map first))
+ seq)]
+ (fn
+ ([db start-test start]
+ (val-seq (let [include? (include start-test start)]
+ (if (bounded? start-test)
+ (drop-while (complement include?) (fetch-seq db start))
+ (take-while include? (fetch-seq db nil))))))
+ ([db start-test start end-test end]
+ (let [[start-test start end-test end]
+ (if reverse?
+ [end-test end start-test start]
+ [start-test start end-test end])]
+ (val-seq (->> (fetch-seq db start)
+ (drop-while (complement (include start-test start)))
+ (take-while (include end-test end))))))))))
+
+(def fetch-subseq (subseq-fn))
+(def fetch-key-subseq (subseq-fn :keys-only? true))
+(def fetch-rsubseq (subseq-fn :reverse? true))
+(def fetch-key-rsubseq (subseq-fn :reverse? true :keys-only? true))
View
24 src/masai/memcached.clj → src/flatland/masai/memcached.clj
@@ -1,18 +1,22 @@
-(ns masai.memcached
- (:use [useful.map :only [into-map]])
- (:require masai.db)
+(ns flatland.masai.memcached
+ (:use [flatland.useful.map :only [into-map]])
+ (:require flatland.masai.db)
(:import net.spy.memcached.MemcachedClient
[java.net InetSocketAddress InetAddress]))
+;; TODO fix this to reify a transcoder that throws an exception if the val is
+;; not a byte array and to base64 encode the keys. Then reenable and fix the
+;; tests.
+
(defn key-format [^String s] (identity s))
(deftype DB [^MemcachedClient mdb endpoints]
- masai.db/EphemeralDB
+ flatland.masai.db/EphemeralDB
(add-expiry! [db key val exp] (.get (.add mdb (key-format key) exp val)))
(put-expiry! [db key val exp] (.get (.set mdb (key-format key) exp val)))
- masai.db/DB
+ flatland.masai.db/DB
(close [db]
(.shutdown mdb))
@@ -23,23 +27,23 @@
(fetch [db key]
(.get mdb (key-format key)))
- (add! [db key val] (masai.db/add-expiry! db key val 0))
- (put! [db key val] (masai.db/put-expiry! db key val 0))
+ (add! [db key val] (flatland.masai.db/add-expiry! db key val 0))
+ (put! [db key val] (flatland.masai.db/put-expiry! db key val 0))
(append! [db key val]
(let [fkey (key-format key)]
(if-let [appended (when-let [cas (.gets mdb key)]
(.get (.append mdb (.getCas cas) fkey val)))]
appended
- (masai.db/add! db key val))))
+ (flatland.masai.db/add! db key val))))
(len [db key]
- (if-let [record (masai.db/fetch db key)]
+ (if-let [record (flatland.masai.db/fetch db key)]
(count (str record))
-1))
(exists? [db key]
- (not= nil (masai.db/fetch db key)))
+ (not= nil (flatland.masai.db/fetch db key)))
(inc! [db key i]
(if (> 0 i)
View
44 src/masai/redis.clj → src/flatland/masai/redis.clj
@@ -1,7 +1,7 @@
-(ns masai.redis
- (:use [useful.map :only [into-map]]
+(ns flatland.masai.redis
+ (:use [flatland.useful.map :only [into-map]]
[clojure.stacktrace :only [root-cause]])
- (:require masai.db)
+ (:require flatland.masai.db)
(:import redis.clients.jedis.BinaryJedis))
(defn i-to-b
@@ -17,17 +17,17 @@
[db & body]
`(if (.isConnected ~db) ~@body))
-(deftype DB [^BinaryJedis rdb opts key-format]
- masai.db/EphemeralDB
+(deftype DB [^BinaryJedis rdb opts]
+ flatland.masai.db/EphemeralDB
(add-expiry! [db key val exp]
- (masai.db/add! db key val)
+ (flatland.masai.db/add! db key val)
(.expire rdb key exp))
(put-expiry! [db key val exp]
(.setex rdb key exp (bytes val)))
- masai.db/DB
+ flatland.masai.db/DB
(open [db]
(when-let [pass (:password opts)]
@@ -51,34 +51,34 @@
(fetch [db key]
(if-connected rdb
- (.get rdb (key-format key))))
+ (.get rdb key)))
(len [db key]
(if-connected rdb
- (if-let [record (masai.db/fetch db key)]
+ (if-let [record (flatland.masai.db/fetch db key)]
(count record)
-1)
-1))
(exists? [db key]
(if-connected rdb
- (.exists rdb (key-format key))
+ (.exists rdb key)
false))
(key-seq [db]
- (set (.keys rdb "*")))
+ (set (.keys rdb (.getBytes "*"))))
- (add! [db key val] (i-to-b (.setnx rdb (key-format key) (bytes val))))
- (put! [db key val] (i-to-b (.set rdb (key-format key) (bytes val))))
- (append! [db key val] (i-to-b (.append rdb (key-format key) (bytes val))))
+ (add! [db key val] (i-to-b (.setnx rdb key (bytes val))))
+ (put! [db key val] (i-to-b (.set rdb key (bytes val))))
+ (append! [db key val] (i-to-b (.append rdb key (bytes val))))
(inc! [db key i]
(if (> 0 i)
- (.decrBy rdb (key-format key) (long (Math/abs ^Integer i)))
- (.incrBy rdb (key-format key) (long i))))
+ (.decrBy rdb key (long (Math/abs ^Integer i)))
+ (.incrBy rdb key (long i))))
(delete! [db key]
- (i-to-b (.del rdb (into-array [(key-format key)]))))
+ (i-to-b (.del rdb (into-array [key]))))
(truncate! [db]
(= "OK" (.flushDB rdb))))
@@ -86,13 +86,11 @@
(defn make
"Create an instance of DB with redis as the backend."
[& opts]
- (let [{:keys [host port timeout key-format]
- :or {host "localhost" port 6379
- key-format (fn [^String s] (bytes (.getBytes s)))}
- :as opts}
- (into-map opts)]
+ (let [{:keys [host port timeout]
+ :or {host "localhost" port 6379}
+ :as opts} (into-map opts)]
(DB.
(if timeout
(BinaryJedis. host port timeout)
(BinaryJedis. host port))
- opts key-format)))
+ opts)))
View
75 src/masai/tokyo.clj → src/flatland/masai/tokyo.clj
@@ -1,8 +1,8 @@
-(ns masai.tokyo
- (:use [useful.map :only [into-map]]
- [useful.utils :only [thread-local]])
- (:require masai.db retro.core
- [masai.tokyo-common :as tokyo])
+(ns flatland.masai.tokyo
+ (:use [flatland.useful.map :only [into-map]]
+ [flatland.useful.utils :only [thread-local]])
+ (:require flatland.masai.db flatland.retro.core
+ [flatland.masai.tokyo-common :as tokyo])
(:import [tokyocabinet HDB]))
(def compress
@@ -45,70 +45,75 @@
(defn- key-seq*
"Get a truly lazy sequence of the keys in the database." [^HDB hdb]
(lazy-seq
- (if-let [key (.iternext2 hdb)]
+ (if-let [key (.iternext hdb)]
(cons key (key-seq* hdb))
nil)))
-(defrecord DB [^HDB hdb opts key-format]
+;; tokyocabinet makes it an error to open an open db, or close a closed one.
+;; we'd prefer that it be a no-op, so we just ignore the request.
+(def ^:private open-paths (atom #{}))
+
+(defrecord DB [^HDB hdb opts]
Object
(toString [this]
(pr-str this))
- masai.db/DB
+ flatland.masai.db/DB
(open [this]
- (let [path (:path opts)
- bnum (or (:bnum opts) 0)
- apow (or (:apow opts) -1)
- fpow (or (:fpow opts) -1)]
- (.mkdirs (.getParentFile (java.io.File. ^String path)))
- (check (.tune hdb bnum apow fpow (tflags opts)))
- (when-let [rcnum (:cache opts)]
- (check (.setcache hdb rcnum)))
- (when-let [xmsiz (:xmsiz opts)]
- (check (.setxmsiz hdb xmsiz)))
- (check (.open hdb path (oflags opts)))))
+ (let [path (:path opts)]
+ (when-not (@open-paths path)
+ (let [bnum (or (:bnum opts) 0)
+ apow (or (:apow opts) -1)
+ fpow (or (:fpow opts) -1)]
+ (.mkdirs (.getParentFile (java.io.File. ^String path)))
+ (check (.tune hdb bnum apow fpow (tflags opts)))
+ (when-let [rcnum (:cache opts)]
+ (check (.setcache hdb rcnum)))
+ (when-let [xmsiz (:xmsiz opts)]
+ (check (.setxmsiz hdb xmsiz)))
+ (check (.open hdb path (oflags opts)))
+ (swap! open-paths conj path)))))
(close [this]
- (.close hdb))
+ (let [path (:path opts)]
+ (when (@open-paths path)
+ (.close hdb)
+ (swap! open-paths disj path))))
(sync! [this]
(.sync hdb))
(optimize! [this]
(.optimize hdb))
(unique-id [this]
- (.path hdb))
+ (:path opts))
(fetch [this key]
- (.get hdb ^bytes (key-format key)))
+ (.get hdb ^bytes key))
(len [this key]
- (.vsiz hdb ^bytes (key-format key)))
+ (.vsiz hdb ^bytes key))
(exists? [this key]
- (not (= -1 (masai.db/len this key))))
+ (not (= -1 (flatland.masai.db/len this key))))
(key-seq [this]
(.iterinit hdb)
(key-seq* hdb))
(add! [this key val]
- (check (.putkeep hdb ^bytes (key-format key) (bytes val))))
+ (check (.putkeep hdb ^bytes key (bytes val))))
(put! [this key val]
- (check (.put hdb ^bytes (key-format key) (bytes val))))
+ (check (.put hdb ^bytes key (bytes val))))
(append! [this key val]
- (check (.putcat hdb ^bytes (key-format key) (bytes val))))
+ (check (.putcat hdb ^bytes key (bytes val))))
(inc! [this key i]
- (.addint hdb ^bytes (key-format key) ^Integer i))
+ (.addint hdb ^bytes key ^Integer i))
(delete! [db key]
- (check (.out hdb ^bytes (key-format key))))
+ (check (.out hdb ^bytes key)))
(truncate! [db]
(check (.vanish hdb))))
(extend DB
- retro.core/Transactional
+ flatland.retro.core/Transactional
(tokyo/transaction-impl DB hdb HDB))
(defn make
"Create an instance of DB with Tokyo Cabinet Hash as the backend."
[& opts]
- (let [{:keys [key-format]
- :or {key-format (fn [^String s] (.getBytes s))}
- :as opts}
- (into-map opts)]
- (DB. (HDB.) opts key-format)))
+ (DB. (HDB.) (into-map opts)))
View
8 src/masai/tokyo_common.clj → src/flatland/masai/tokyo_common.clj
@@ -1,7 +1,7 @@
-(ns masai.tokyo-common
- (:use [useful.utils :only [thread-local]])
- (:require [masai.db :as db]
- [retro.core :as retro]))
+(ns flatland.masai.tokyo-common
+ (:use [flatland.useful.utils :only [thread-local]])
+ (:require [flatland.masai.db :as db]
+ [flatland.retro.core :as retro]))
;; I hate this so much. But we need per-thread mutable state, without the safety of binding
(def transaction-depths (thread-local (atom {})))
View
108 src/masai/tokyo_sorted.clj → src/flatland/masai/tokyo_sorted.clj
@@ -1,9 +1,9 @@
-(ns masai.tokyo-sorted
- (:use [useful.map :only [into-map]]
- [useful.seq :only [lazy-loop]]
- [useful.experimental :only [order-let-if]])
- (:require masai.db retro.core masai.cursor
- [masai.tokyo-common :as tokyo])
+(ns flatland.masai.tokyo-sorted
+ (:use [flatland.useful.map :only [into-map]]
+ [flatland.useful.seq :only [lazy-loop]]
+ [flatland.useful.experimental :only [order-let-if]])
+ (:require flatland.masai.db flatland.retro.core flatland.masai.cursor
+ [flatland.masai.tokyo-common :as tokyo])
(:import [tokyocabinet BDB BDBCUR]))
(def compress
@@ -39,84 +39,77 @@
~BDB/ENOREC false
(throw (java.io.IOException. (.errmsg ~'bdb) )))))
-(defn- cursor-seq
- "Return a lazy cursor sequence by initializing the cursor by calling first and advances the cursor
- with next."
- [bdb first next]
- (let [cursor (BDBCUR. bdb)]
- (lazy-loop [more? (first cursor)]
- (when more?
- (cons [(.key2 cursor) (.val cursor)]
- (lazy-recur (next cursor)))))))
-
-(defmacro curfn
- "Like memfn, except type hinted for BDBCUR."
- [method & args]
- `(fn [^BDBCUR cur#]
- (. cur# ~method ~@args)))
-
-(deftype DB [^BDB bdb opts key-format]
- masai.db/DB
+;; tokyocabinet makes it an error to open an open db, or close a closed one.
+;; we'd prefer that it be a no-op, so we just ignore the request.
+(def ^:private open-paths (atom #{}))
+
+(defrecord DB [^BDB bdb opts]
+ flatland.masai.db/DB
(open [db]
- (let [path (:path opts)
- bnum (or (:bnum opts) 0)
- apow (or (:apow opts) -1)
- fpow (or (:fpow opts) -1)
- lmemb (or (:lmemb opts) 0)
- nmemb (or (:nmemb opts) 0)]
- (.mkdirs (.getParentFile (java.io.File. ^String path)))
- (check (.tune bdb lmemb nmemb bnum apow fpow (tflags opts)))
- (when-let [[lcnum rcnum] (:cache opts)]
- (check (.setcache bdb lcnum rcnum)))
- (when-let [xmsiz (:xmsiz opts)]
- (check (.setxmsiz bdb xmsiz)))
- (check (.open bdb path (oflags opts)))))
+ (let [path (:path opts)]
+ (when-not (@open-paths path)
+ (let [bnum (or (:bnum opts) 0)
+ apow (or (:apow opts) -1)
+ fpow (or (:fpow opts) -1)
+ lmemb (or (:lmemb opts) 0)
+ nmemb (or (:nmemb opts) 0)]
+ (.mkdirs (.getParentFile (java.io.File. ^String path)))
+ (check (.tune bdb lmemb nmemb bnum apow fpow (tflags opts)))
+ (when-let [[lcnum rcnum] (:cache opts)]
+ (check (.setcache bdb lcnum rcnum)))
+ (when-let [xmsiz (:xmsiz opts)]
+ (check (.setxmsiz bdb xmsiz)))
+ (check (.open bdb path (oflags opts)))
+ (swap! open-paths conj path)))))
(close [db]
- (.close bdb))
+ (let [path (:path opts)]
+ (when (@open-paths path)
+ (.close bdb)
+ (swap! open-paths disj path))))
(sync! [db]
(.sync bdb))
(optimize! [db]
(.optimize bdb))
(unique-id [db]
- (.path bdb))
+ (:path opts))
(fetch [db key]
- (.get bdb ^bytes (key-format key)))
+ (.get bdb ^bytes key))
(len [db key]
- (.vsiz bdb ^bytes (key-format key)))
+ (.vsiz bdb ^bytes key))
(exists? [db key]
- (not (= -1 (masai.db/len db key))))
+ (not (= -1 (flatland.masai.db/len db key))))
(key-seq [db]
(.iterinit bdb)
(lazy-loop []
- (when-let [key (.iternext2 bdb)]
+ (when-let [key (.iternext bdb)]
(cons key (lazy-recur)))))
(add! [db key val]
- (check (.putkeep bdb ^bytes (key-format key) (bytes val))))
+ (check (.putkeep bdb ^bytes key (bytes val))))
(put! [db key val]
- (check (.put bdb ^bytes (key-format key) (bytes val))))
+ (check (.put bdb ^bytes key (bytes val))))
(append! [db key val]
- (check (.putcat bdb ^bytes (key-format key) (bytes val))))
+ (check (.putcat bdb ^bytes key (bytes val))))
(inc! [db key i]
- (.addint bdb ^bytes (key-format key) ^Integer i))
+ (.addint bdb ^bytes key ^Integer i))
(delete! [db key]
- (check (.out bdb ^bytes (key-format key))))
+ (check (.out bdb ^bytes key)))
(truncate! [db]
(check (.vanish bdb)))
- masai.db/SequentialDB
+ flatland.masai.db/SequentialDB
(cursor [db key]
(-> (BDBCUR. bdb)
- (masai.cursor/jump (key-format key)))))
+ (flatland.masai.cursor/jump key))))
(extend DB
- retro.core/Transactional
+ flatland.retro.core/Transactional
(tokyo/transaction-impl DB bdb BDB))
(extend-type BDBCUR
- masai.cursor/Cursor
+ flatland.masai.cursor/Cursor
(next [this]
(when (.next this)
this))
@@ -139,7 +132,7 @@
(.jump this ^bytes k))
this))
- masai.cursor/MutableCursor
+ flatland.masai.cursor/MutableCursor
(put [this value]
(if (.put this ^bytes value BDBCUR/CPBEFORE)
this
@@ -156,11 +149,4 @@
(defn make
"Create an instance of DB with Tokyo Cabinet B-Tree as the backend."
[& opts]
- (let [{:keys [key-format]
- :or {key-format (fn [^String s] (bytes (.getBytes (str s))))}
- :as opts}
- (into-map opts)]
- (DB. (BDB.) opts (fn [k]
- (if (or (nil? k) (keyword? k))
- k
- (key-format k))))))
+ (DB. (BDB.) (into-map opts)))
View
158 test/flatland/masai/db_test.clj
@@ -0,0 +1,158 @@
+(ns flatland.masai.db-test
+ (:refer-clojure :exclude [get count sync])
+ (:use clojure.test flatland.useful.debug)
+ (:require [flatland.masai.tokyo :as tokyo]
+ [flatland.masai.tokyo-sorted :as tokyo-btree]
+ [flatland.masai.memcached :as memcached]
+ [flatland.masai.redis :as redis]
+ [flatland.masai.db :as db]
+ [flatland.masai.cursor :as cursor]
+ [flatland.retro.core :as retro]))
+
+(def b (memfn getBytes))
+
+(deftest tests
+ (doseq [db [;(redis/make) TODO uncomment this test once it starts redis automatically
+ (tokyo/make {:path "/tmp/masai-test-tokyo-db" :create true :prepop true})
+ (tokyo-btree/make {:path "/tmp/masai-test-sorted-tokyo-db" :create true :prepop true})]]
+ (db/open db)
+ (db/truncate! db)
+
+ (testing "add! doesn't overwrite existing record"
+ (is (= nil (db/fetch db (.getBytes "foo"))))
+ (is (= true (db/add! db (.getBytes "foo") (.getBytes "bar"))))
+ (is (= "bar" (String. (db/fetch db (.getBytes "foo")))))
+ (is (= false (db/add! db (.getBytes "foo") (.getBytes "baz"))))
+ (is (= "bar" (String. (db/fetch db (.getBytes "foo"))))))
+
+ (testing "put! overwrites existing record"
+ (is (= true (db/put! db (.getBytes "foo") (.getBytes "baz"))))
+ (is (= "baz" (String. (db/fetch db (.getBytes "foo"))))))
+
+ (testing "append! to existing record"
+ (is (= true (db/append! db (.getBytes "foo") (.getBytes "bar"))))
+ (is (= "bazbar" (String. (db/fetch db (.getBytes "foo")))))
+ (is (= true (db/append! db (.getBytes "foo") (.getBytes "!"))))
+ (is (= "bazbar!" (String. (db/fetch db (.getBytes "foo"))))))
+
+ (testing "append! to nonexistent record"
+ (is (= true (db/append! db (.getBytes "baz") (.getBytes "1234"))))
+ (is (= "1234" (String. (db/fetch db (.getBytes "baz"))))))
+
+ (testing "delete! record returns true on success"
+ (is (= true (db/delete! db (.getBytes "foo"))))
+ (is (= nil (db/fetch db (.getBytes "foo"))))
+ (is (= true (db/delete! db (.getBytes "baz"))))
+ (is (= nil (db/fetch db (.getBytes "baz")))))
+
+ (testing "delete! nonexistent records returns false"
+ (is (= false (db/delete! db (.getBytes "foo"))))
+ (is (= false (db/delete! db (.getBytes "bar")))))
+
+ (testing "len returns -1 for nonexistent records"
+ (is (= nil (db/fetch db (.getBytes "foo"))))
+ (is (= -1 (db/len db (.getBytes "foo")))))
+
+ (testing "len returns the length for existing records"
+ (is (= true (db/put! db (.getBytes "foo") (.getBytes ""))))
+ (is (= "" (String. (db/fetch db (.getBytes "foo")))))
+ (is (= 0 (db/len db (.getBytes "foo"))))
+ (is (= true (db/put! db (.getBytes "bar") (.getBytes "12345"))))
+ (is (= 5 (db/len db (.getBytes "bar"))))
+ (is (= true (db/append! db (.getBytes "bar") (.getBytes "6789"))))
+ (is (= 9 (db/len db (.getBytes "bar"))))
+ (is (= true (db/add! db (.getBytes "baz") (.getBytes ".........."))))
+ (is (= 10 (db/len db (.getBytes "baz")))))
+
+ (testing "exists? returns true if record exists"
+ (is (= true (db/add! db (.getBytes "bazr") (.getBytes ""))))
+ (is (= true (db/exists? db (.getBytes "bazr")))))
+
+ (testing "exists? returns false if record is non-existent"
+ (is (= nil (db/fetch db (.getBytes "baze"))))
+ (is (= false (db/exists? db (.getBytes "baze")))))
+
+ (testing "a closed db appears empty"
+ (db/close db)
+ (is (= nil (db/fetch db (.getBytes "bar"))))
+ (is (= nil (db/fetch db (.getBytes "baz"))))
+ (is (= -1 (db/len db (.getBytes "baz"))))
+ (is (= false (db/exists? db (.getBytes "baz")))))
+
+ (testing "can reopen a closed db"
+ (db/open db)
+ (is (not= nil (db/fetch db (.getBytes "bar"))))
+ (is (not= nil (db/fetch db (.getBytes "baz"))))
+ (is (= "123456789" (String. (db/fetch db (.getBytes "bar"))))))
+
+ (testing "truncate deletes all records"
+ (is (= true (db/truncate! db)))
+ (is (= nil (db/fetch db (.getBytes "foo"))))
+ (is (= nil (db/fetch db (.getBytes "bar"))))
+ (is (= nil (db/fetch db (.getBytes "baz")))))
+
+ (db/close db)))
+
+(deftest transactions
+ (doseq [db [(tokyo/make {:path "/tmp/masai-test-tokyo-db" :create true :prepop true})
+ (tokyo-btree/make {:path "/tmp/masai-test-sorted-tokyo-db" :create true :prepop true})]]
+ (retro/txn-begin! db)
+ (retro/txn-begin! db) ; this will block forever if we actually open two transactions
+ (retro/txn-commit! db)
+ (retro/txn-commit! db)
+ (is (= true true)))) ; if we got here, things are A-OK
+
+(deftest sorted-db
+ (let [db (tokyo-btree/make {:path "/tmp/masai-test-sorted-tokyo-db2" :create true :prepop true})]
+ (db/open db)
+ (db/truncate! db)
+ (let [xs ["bar" "baz" "bam" "cat" "foo"]
+ sorted-xs (sort xs)]
+ (doseq [x xs]
+ (db/add! db (.getBytes x) (.getBytes "")))
+
+ (testing "fetch-subseq works as in core"
+ (are [ks kvs] (= ks (map #(String. (first %)) kvs))
+ ["baz" "cat" "foo"] (db/fetch-subseq db > (.getBytes "bar"))
+ ["baz" "cat"] (db/fetch-subseq db > (.getBytes "bar") < (.getBytes "foo"))
+ ["bam" "bar" "baz" "cat"] (db/fetch-subseq db < (.getBytes "foo"))))
+
+ (testing "fetch-rsubseq works as in core"
+ (are [ks kvs] (= ks (map #(String. (first %)) kvs))
+ ["foo" "cat" "baz"] (db/fetch-rsubseq db > (.getBytes "bar"))
+ ["cat" "baz"] (db/fetch-rsubseq db > (.getBytes "bar") < (.getBytes "foo"))
+ ["cat" "baz" "bar" "bam"] (db/fetch-rsubseq db < (.getBytes "foo"))))
+
+ (testing "fetch-key-subseq works as in core"
+ (are [ks keys] (= ks (map #(String. %) keys))
+ ["baz" "cat" "foo"] (db/fetch-key-subseq db > (.getBytes "bar"))
+ ["baz" "cat"] (db/fetch-key-subseq db > (.getBytes "bar") < (.getBytes "foo"))
+ ["bam" "bar" "baz" "cat"] (db/fetch-key-subseq db < (.getBytes "foo"))))
+
+ (testing "fetch-key-rsubseq works as in core"
+ (are [ks kvs] (= ks (map #(String. %) kvs))
+ ["foo" "cat" "baz"] (db/fetch-key-rsubseq db > (.getBytes "bar"))
+ ["cat" "baz"] (db/fetch-key-rsubseq db > (.getBytes "bar") < (.getBytes "foo"))
+ ["cat" "baz" "bar" "bam"] (db/fetch-key-rsubseq db < (.getBytes "foo"))))
+
+ (testing "cursors"
+ (is (nil?
+ (reduce (fn [cursor x]
+ (is (= x (String. (cursor/key cursor))))
+ (is (= "" (String. (cursor/val cursor))))
+ (cursor/next cursor))
+ (db/cursor db :first)
+ sorted-xs)))
+ (let [test (.getBytes "test")
+ append (.getBytes "append")
+ c (db/cursor db :first)
+ c (cursor/next c)
+ _ (is (not (nil? c)))
+ c (cursor/put c test)
+ _ (is (= (seq test) (seq (cursor/val c))))
+ c (cursor/prev c)
+ _ (is (= (first sorted-xs) (String. (cursor/key c))))
+ c (cursor/delete c)
+ _ (is (= (seq test) (seq (cursor/val c))))
+ c (cursor/append c append)
+ _ (is (= (concat test append) (seq (cursor/val c))))])))))
View
145 test/masai/db_test.clj
@@ -1,145 +0,0 @@
-(ns masai.db-test
- (:refer-clojure :exclude [get count sync])
- (:use clojure.test useful.debug)
- (:require [masai.tokyo :as tokyo]
- [masai.tokyo-sorted :as tokyo-btree]
- [masai.memcached :as memcached]
- [masai.redis :as redis]
- [masai.db :as db]
- [masai.cursor :as cursor]
- [retro.core :as retro]))
-
-(deftest tests
- (doseq [db [(redis/make)
- (tokyo/make {:path "/tmp/masai-test-tokyo-db" :create true :prepop true})
- (tokyo-btree/make {:path "/tmp/masai-test-sorted-tokyo-db" :create true :prepop true})]]
- (db/open db)
- (db/truncate! db)
-
- (testing "add! doesn't overwrite existing record"
- (is (= nil (db/fetch db "foo")))
- (is (= true (db/add! db "foo" (.getBytes "bar"))))
- (is (= "bar" (String. (db/fetch db "foo"))))
- (is (= false (db/add! db "foo" (.getBytes "baz"))))
- (is (= "bar" (String. (db/fetch db "foo")))))
-
- (testing "put! overwrites existing record"
- (is (= true (db/put! db "foo" (.getBytes "baz"))))
- (is (= "baz" (String. (db/fetch db "foo")))))
-
- (testing "append! to existing record"
- (is (= true (db/append! db "foo" (.getBytes "bar"))))
- (is (= "bazbar" (String. (db/fetch db "foo"))))
- (is (= true (db/append! db "foo" (.getBytes "!"))))
- (is (= "bazbar!" (String. (db/fetch db "foo")))))
-
- (testing "append! to nonexistent record"
- (is (= true (db/append! db "baz" (.getBytes "1234"))))
- (is (= "1234" (String. (db/fetch db "baz")))))
-
- (testing "delete! record returns true on success"
- (is (= true (db/delete! db "foo")))
- (is (= nil (db/fetch db "foo")))
- (is (= true (db/delete! db "baz")))
- (is (= nil (db/fetch db "baz"))))
-
- (testing "delete! nonexistent records returns false"
- (is (= false (db/delete! db "foo")))
- (is (= false (db/delete! db "bar"))))
-
- (testing "len returns -1 for nonexistent records"
- (is (= nil (db/fetch db "foo")))
- (is (= -1 (db/len db "foo"))))
-
- (testing "len returns the length for existing records"
- (is (= true (db/put! db "foo" (.getBytes ""))))
- (is (= "" (String. (db/fetch db "foo"))))
- (is (= 0 (db/len db "foo")))
- (is (= true (db/put! db "bar" (.getBytes "12345"))))
- (is (= 5 (db/len db "bar")))
- (is (= true (db/append! db "bar" (.getBytes "6789"))))
- (is (= 9 (db/len db "bar")))
- (is (= true (db/add! db "baz" (.getBytes ".........."))))
- (is (= 10 (db/len db "baz"))))
-
- (testing "exists? returns true if record exists"
- (is (= true (db/add! db "bazr" (.getBytes ""))))
- (is (= true (db/exists? db "bazr"))))
-
- (testing "exists? returns false if record is non-existent"
- (is (= nil (db/fetch db "baze")))
- (is (= false (db/exists? db "baze"))))
-
- (testing "a closed db appears empty"
- (db/close db)
- (is (= nil (db/fetch db "bar")))
- (is (= nil (db/fetch db "baz")))
- (is (= -1 (db/len db "baz")))
- (is (= false (db/exists? db "baz"))))
-
- (testing "can reopen a closed db"
- (db/open db)
- (is (not= nil (db/fetch db "bar")))
- (is (not= nil (db/fetch db "baz")))
- (is (= "123456789" (String. (db/fetch db "bar")))))
-
- (testing "truncate deletes all records"
- (is (= true (db/truncate! db)))
- (is (= nil (db/fetch db "foo")))
- (is (= nil (db/fetch db "bar")))
- (is (= nil (db/fetch db "baz"))))
-
- (db/close db)))
-
-(deftest transactions
- (doseq [db [(tokyo/make {:path "/tmp/masai-test-tokyo-db" :create true :prepop true})
- (tokyo-btree/make {:path "/tmp/masai-test-sorted-tokyo-db" :create true :prepop true})]]
- (retro/txn-begin! db)
- (retro/txn-begin! db) ; this will block forever if we actually open two transactions
- (retro/txn-commit! db)
- (retro/txn-commit! db)
- (is (= true true)))) ; if we got here, things are A-OK
-
-(deftest sorted-db
- (let [db (tokyo-btree/make {:path "/tmp/masai-test-sorted-tokyo-db2" :create true :prepop true})]
- (db/open db)
- (db/truncate! db)
- (let [xs ["bar" "baz" "bam" "cat" "foo"]
- sorted-xs (sort xs)
- bytes (.getBytes "")]
- (doseq [x xs]
- (db/add! db x bytes))
-
- (testing "fetch-subseq works as in core"
- (are [ks kvs] (= ks (map first kvs))
- '("baz" "cat" "foo") (db/fetch-subseq db > "bar")
- '("baz" "cat") (db/fetch-subseq db > "bar" < "foo")
- '("bam" "bar" "baz" "cat") (db/fetch-subseq db < "foo")))
-
- (testing "fetch-rsubseq works as in core"
- (are [ks kvs] (= ks (map first kvs))
- '("foo" "cat" "baz") (db/fetch-rsubseq db > "bar")
- '("cat" "baz") (db/fetch-rsubseq db > "bar" < "foo")
- '("cat" "baz" "bar" "bam") (db/fetch-rsubseq db < "foo")))
-
- (testing "cursors"
- (is (nil?
- (reduce (fn [cursor x]
- (is (= x (String. (cursor/key cursor) "UTF-8")))
- (is (= (seq bytes) (seq (cursor/val cursor))))
- (cursor/next cursor))
- (db/cursor db :first)
- sorted-xs)))
- (let [test (.getBytes "test")
- append (.getBytes "append")
- c (db/cursor db :first)
- c (cursor/next c)
- _ (is (not (nil? c)))
- c (cursor/put c test)
- _ (is (= (seq test) (seq (cursor/val c))))
- c (cursor/prev c)
- _ (is (= (first sorted-xs) (String. (cursor/key c) "UTF-8")))
- c (cursor/delete c)
- _ (is (= (seq test) (seq (cursor/val c))))
- c (cursor/append c append)
- _ (is (= (concat test append) (seq (cursor/val c))))])))))
Please sign in to comment.
Something went wrong with that request. Please try again.