konserve.core
api feels just like a clojure map except it has mutable persistence behind it(require '[konserve.filestore :refer [connect-fs-store]])
(def store (<!! (connect-fs-store "/tmp/store")))
(require '[konserve.node-filestore :refer [connect-fs-store]])
;; node-js supports sync but no <!!
(def store (connect-fs-store "/tmp/store" :opts {:sync? true}))
;; in the browser
(require '[konserve.indexeddb :refer [connect-idb-store]])
(go
;; indexeddb is async only!
(let [store (<! (connect-idb-store "idb-store"))]
...))
Stores also accept var-args. Support for each entry varies per implementation
:opts
=> {:sync? <boolean>}
:sync?
which is used to control whether functions return channels or valueskonserve.core
functions, but for creating stores, it must be identified by the keyword :opts
:config
=> map
:buffer-size
=> number
:default-serializer
=> keyword
:FressianSerializer
, but you can override with a keyword identifying a different serializer implementation(connect-store store-name :default-serializer :StringSerializer)
=> writes string edn:CBORSerializer
{:MySerializer PStoreSerializerImpl}
to :serializers
(..see next bullet) and then referencing it via :default-serializer :MySerializer
:serializers
=> Map<keyword, PStoreSerializerImpl>
:default-serializer
konserve.serializers/fressian-serializer
is a convenience function that accepts 2 maps: a map of read-handlers and a map of write-handlers and returns a fressian serializer supporting your custom types
org.fressian.ReadHandlers
& org.fressian.WriteHandlers
:encryptor
=> {:type :aes :key "s3cr3t"}
:aes
in default stores:compressor
=> {:type :lz4}
Konserve intercepts records and writes them as incognito tagged literals such that the details of serialization formats are abstracted away and allowing easier interop between different formats. The :read-handlers
and :write-handlers
varg args are explicitly meant for working with incognito's tagged literals.
:read-handlers
expects an atom wrapping {'symbol.for.MyRecord map->MyRecord}
for recovering records from incognito tagged literals:write-handlers
expects an atom wrapping {'symbol.for.MyRecord (fn [record] ..)}
for writing records as incognito tagged literalsOnce you have a store you can access it using konserve.core
functions. By default functions are asynchronous and return channels yielding values or errors. You can override this by passing an opts map with :sync? true
(require '[konserve.core :as k])
(k/exists? store :false) ;=> channel<false>
(<!! (k/assoc store :fruits {:parcha nil :mango nil :banana nil}))
(k/exists? store :fruits {:sync? true}) ;=> true
You can get
get-in
update
update-in
and dissoc
just like a clojure map.
(k/assoc-in store [:fruits :parcha] {:color "yellow" :taste "sour" :quantity 0})
(k/update-in store [:fruits :parcha :quantity] inc)
(k/get-in store [:fruits :parcha :quantity]) ;=> channel<1>
(k/dissoc store :fruits)
In the fruits example a simple keyword is the store key, but keys themselves can be arbitrary edn:
(defn memoize-to-store-sync [f]
(fn [& args]
(if-let [result (<!! (k/get store args))]
result
(let [result (apply f args)]
(<!! (k/assoc store args result))
result))))
(def memoized-fn (memoize-to-store-sync expensive-fn))
(memoized-fn {:any/such #{"set"}}, [0x6F \f], 'haschable.argu/ments) ;=> channel<result>
(k/bassoc store :blob blob)
(k/bget store :blob
(fn locked-cb [{is :input-stream}]
(go (input-stream->byte-buffer is)))) ;=> ch<bytebuffer>
With bassoc
binary data is written as-is without passing through serialization/encryption/compression.
bget
is probably the trickiest function in konserve. It accepts a callback function that is passed a map of {:input-stream <host-input-stream>}
. While locked-cb
is running, konserve locks & holds onto underyling resources (ie file descriptors) until the function exits. You can choose to read from the input stream however you like, but rather than running side-effects within the callback, you should instead return your desired value else the lock will remain held.
locked-cb
should return a channel yielding the desired value that will be read from and yielded by bget
's channelbget
synchronously but the locked-cb will be called with {:blob js/Buffer}
bget
calls its locked-cb with {:input-stream <readable-webstream> :offset <number>}
where offset indicates the amount of bytes to drop before reaching the desired blob start.
konserve.indexeddb/read-web-stream
can serve as a locked-cb that will yield a Uint8Array
.Konserve does some bookkeeping for values by storing them with metadata
(k/get-meta store :key {:sync? true})
;=>
; {:key :key
; :type <binary|edn>
; :last-write <inst>}
Konserve provides an append log for writing values quickly. These entries are a special case managed by konserve, where the sequence is stored as a linked list of blobs where each blob is a cons cell of the list. You can name the log with any key, but that key should only be written to or read from using the append
, log
, and reduce-log
functions.
(dotimes [n 6]
(<!! (k/append store :log n)))
(k/get store :log) ;=> channel<(0 1 2 3 4 5)>
(k/reduce-log store :log
(fn [acc n]
(if (even? n)
(conj acc n)
acc))
[]) ;=> channel<[0 2 4]>
konserve.gc/sweep!
lets you prune the store based on a whitelist set of keys to keep and a timestamp cutoff before which un whitelisted entries should be deleted
konserve.cache/ensure-cache
wraps a store with a lru-cache to avoid hitting external memory for frequently accessed keys
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close