Yearling - laforge49/aatree GitHub Wiki

API

Yearling is a database written using virtual aatree structures, Closer, Db File, Null Db Cache or LRU Db Cache and Db Chan or Db Agent. It is very similar to the Calf database, only it provides a facility for managing disk space--which those virtual data structures require. Think of Yearling as a framework ready to support things something like B-trees.

Yearling uses the following key vectors to organize its contents:

  • [:release-pending] A vector of vectors, where each sub-vector contains:
    1. The time (in milliseconds) when a disk block was released.
    2. The sequence number of the database update which released the disk block. (the transaction count) And
    3. The number of the disk block. (block-position / db-block-size)
  • [:app-map] Location of application data.

The API is a bit long, but many of the functions are the same which work with the Calf database:

  • (aatree.yearling.yearling-open file) Opens the database. The top-level data structure held by the database is a lazy sorted map. The updated opts map is returned.
  • (aatree.yearling.yearling-open this file) Opens the database. The updated opts map (this) is returned.
  • (aatree.core.db-block-size this) Returns the block size.
  • (aatree.core.check-buffer-size this byte-buffer) Throws an exception if (.limit byte-buffer) exceeds (db-block-size this)
  • (aatree.core.get-transaction-count this) Returns the transaction count.
  • (aatree.core.db-allocated this) Returns the number of disk blocks which are either in-use or pending release.
  • (aatree.core.db-allocate this) Allocates a disk block and returns the block number. Callable only while updating the database.
  • (aatree.core.db-release this block-nbr) Adds a disk block which is no longer in use to the vector of disk blocks that are pending release. Callable only while updating the database.
  • (aatree.core.db-process-pending this age updates) Releases all the pending disk blocks whose release is at least as old as the age given (in milliseconds) and were the release was several updates back, as specified by the updates parameter. This function is called at the beginning of each update operation. Callable only while updating the database.
  • (aatree.core.db-new-node-id this) Returns a unique id. Used to identify virtual node values. Undefined outside the scope of a transaction.
  • (aatree.core.get-time-millis this) Returns the time when the current transaction started. Undefined outside the scope of a transaction.
  • (aatree.core.get-allocated-bit-set this) Returns a mutable BitSet for tracking allocated blocks.

See also Yearling Database Options.

Yearling Test

(ns aatree.yearling-test
  (:require [clojure.test :refer :all]
            [aatree.core :refer :all]
            [aatree.yearling :refer :all]
            [aatree.closer-trait :refer :all])
  (:import (java.io File)))

(set! *warn-on-reflection* true)

(deftest yearling
  (.delete (File. "yearling-test.yearling"))

  (let [yearling {:max-db-size   100000
                  :db-block-size 10000}
        yearling (yearling-open yearling (File. "yearling-test.yearling"))
        app-map (db-get-in yearling [:app-map])
        _ (is (= (get-transaction-count yearling) 2))
        _ (is (= app-map nil))
        _ (is (= (db-allocated yearling) 2))
        _ (db-update
            yearling
            (fn [db]
              (update-assoc-in! db [:app-map :block] (db-allocate db))))
        block (db-get-in yearling [:app-map :block])
        _ (is (= (get-transaction-count yearling) 3))
        _ (is (= block 2))
        _ (is (= (db-allocated yearling) 3))
        _ (is (= (count (db-get-in yearling [:release-pending])) 0))
        _ (db-update
            yearling
            (fn [db]
              (println "new node id" (db-new-node-id db))
              (db-release db block)
              (update-dissoc-in! db [:app-map :block])))
        app-map (db-get-in yearling [:app-map])
        _ (is (= (get-transaction-count yearling) 4))
        _ (is (= app-map nil))
        _ (is (= (db-allocated yearling) 3))
        _ (is (= (count (db-get-in yearling [:release-pending])) 1))
        _ (close-components yearling)])

  (let [yearling {:db-pending-count 99
                  :max-db-size      100000
                  :db-block-size    10000}
        yearling (yearling-open yearling (File. "yearling-test.yearling"))
        app-map (db-get-in yearling [:app-map])
        _ (is (= (get-transaction-count yearling) 4))
        _ (is (= app-map nil))
        _ (is (= (db-allocated yearling) 3))
        _ (is (= (count (db-get-in yearling [:release-pending])) 1))
        _ (db-update
            yearling
            (fn [db]
              (println "new node id" (db-new-node-id db))
              (db-process-pending db 0 1)))
        app-map (db-get-in yearling [:app-map])
        _ (is (= (get-transaction-count yearling) 5))
        _ (is (= app-map nil))
        _ (is (= (db-allocated yearling) 2))
        _ (is (= (count (db-get-in yearling [:release-pending])) 0))
        _ (close-components yearling)])

  (Thread/sleep 200))