Skip to content

Commit 1de1abb

Browse files
authored
Merge pull request #177 from threatgrid/entity-ids
Files are now cleanly truncated on release, including abandoned connections. Fixes #176 Fixes #137
2 parents a24b912 + 77a0292 commit 1de1abb

13 files changed

+102
-47
lines changed

src/asami/core.cljc

+12-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919

2020
(defonce connections (atom {}))
2121

22+
(defn shutdown
23+
"Releases all connection resources for a clean shutdown"
24+
[]
25+
(doseq [connection (vals @connections)] (storage/release connection)))
26+
27+
#?(:clj
28+
(.addShutdownHook (Runtime/getRuntime) (Thread. shutdown)))
29+
2230
(s/defn ^:private parse-uri :- {:type s/Str
2331
:name s/Str}
2432
"Splits up a database URI string into structured parts"
@@ -81,9 +89,10 @@
8189
(storage/delete-database conn))
8290
;; database not in the connections
8391
;; connect to it to free its resources
84-
(when (db-exists? uri)
85-
(if-let [conn (connection-for uri)]
86-
(storage/delete-database conn)))))
92+
(boolean
93+
(when (db-exists? uri)
94+
(if-let [conn (connection-for uri)]
95+
(storage/delete-database conn))))))
8796

8897
(s/defn get-database-names
8998
"Returns a seq of database names that this instance is aware of."

src/asami/durable/block/block_api.cljc

+3
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,6 @@
3030
(get-block [this id] "Returns the block associated with an ID.")
3131
(get-block-size [this] "Returns the size of blocks allocated by this manager")
3232
(copy-to-tx [this block] "Returns a block that is in the current transaction, possibly returning the current block"))
33+
34+
(defprotocol CountedBlocks
35+
(get-block-count [this] "Returns the number of blocks that this object has allocated, or nil if not managed by this object."))

src/asami/durable/block/file/block_file.clj

+15-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
asami.durable.block.file.block-file
44
(:require [clojure.java.io :as io]
55
[asami.durable.common :refer [Transaction Closeable Forceable rewind! commit! close]]
6-
[asami.durable.block.block-api :refer [BlockManager copy-over! copy-block! allocate-block! get-id]]
6+
[asami.durable.block.block-api :refer [CountedBlocks BlockManager copy-over! copy-block! allocate-block! get-id get-block-count]]
77
[asami.durable.block.bufferblock :refer [create-block]]
88
[asami.durable.block.file.voodoo :as voodoo]
99
[asami.durable.cache :refer [lookup hit miss lru-cache-factory]])
@@ -45,12 +45,14 @@
4545
(defn open-block-file
4646
"Opens a file for storing blocks. Returns a structure with the block file
4747
and the RandomAccessFile that the block file uses. The file will need to be
48-
closed when block files based on this initial block file are no longer needed."
49-
[file block-size]
48+
closed when block files based on this initial block file are no longer needed.
49+
When the init-nr-blocks is not nil, then it holds the recorded number of blocks
50+
in the file."
51+
[file block-size init-nr-blocks]
5052
(let [file (io/file file)
5153
raf (RandomAccessFile. file "rw")
5254
^FileChannel fc (.getChannel raf)
53-
nr-blocks (long (/ (.size fc) block-size))
55+
nr-blocks (or init-nr-blocks (long (/ (.size fc) block-size)))
5456
slack (mod region-size block-size)
5557
stride (if (zero? slack) region-size (+ region-size (- block-size slack)))]
5658
(set-nr-blocks! (->BlockFile 0 block-size 0 [] stride file raf fc) nr-blocks)))
@@ -243,12 +245,16 @@
243245

244246
(get-block-size [this]
245247
(:block-size (:block-file @state)))
246-
248+
247249
(copy-to-tx [this block]
248250
(if (<= (get-id block) (:commit-point @state))
249251
(copy-block! this block)
250252
block))
251253

254+
CountedBlocks
255+
(get-block-count [this]
256+
(get-nr-blocks (:block-file @state)))
257+
252258
Transaction
253259
(rewind! [this]
254260
(vswap! state #(assoc % :next-id (:commit-point %)))
@@ -274,9 +280,11 @@
274280
(.delete ^File file))))
275281

276282
(defn create-managed-block-file
277-
[filename block-size]
278-
(let [block-file (open-block-file filename block-size)
283+
[filename block-size nr-blocks]
284+
(let [block-file (open-block-file filename block-size nr-blocks)
279285
next-id (dec (:nr-blocks block-file))]
286+
(when (and nr-blocks (= next-id nr-blocks))
287+
(throw (ex-info "Inconsistent reopening of block file" {:set-blocks nr-blocks :file-blocks (:nr-blocks block-file)})))
280288
(->ManagedBlockFile (volatile! {:block-file block-file
281289
:next-id next-id
282290
:commit-point next-id

src/asami/durable/common_utils.cljc

+6-5
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,20 @@
77

88
(defn create-block-manager
99
"Creates a block manager"
10-
[name manager-name block-size]
10+
[name manager-name block-size nr-blocks]
1111
#?(:clj
12-
(block-file/create-managed-block-file (.getPath (io/file name manager-name)) block-size)))
12+
(block-file/create-managed-block-file (.getPath (io/file name manager-name)) block-size nr-blocks)))
1313

1414
(defn named-storage
1515
"A common function for opening storage with a given name. Must be provided with a storage constructor and the name.
16-
The root id indicates an index root, and may be nil for an empty index."
17-
[storage-constructor name root-id]
16+
The root id indicates an index root, and may be nil for an empty index.
17+
The block count refers to the count of blocks in the storage."
18+
[storage-constructor name root-id block-count]
1819
#?(:clj
1920
(let [d (io/file name)]
2021
(if (.exists d)
2122
(when-not (.isDirectory d)
2223
(throw (ex-info (str "'" name "' already exists as a file") {:path (.getAbsolutePath d)})))
2324
(when-not (.mkdir d)
2425
(throw (ex-info (str "Unable to create directory '" name "'") {:path (.getAbsolutePath d)}))))
25-
(storage-constructor name root-id))))
26+
(storage-constructor name root-id block-count))))

src/asami/durable/graph.cljc

+10-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
[asami.durable.tuples :as tuples]
1515
[asami.durable.resolver :as resolver :refer [get-from-index get-transitive-from-index]]
1616
#?(:clj [asami.durable.flat-file :as flat-file])
17+
[asami.durable.block.block-api :as block-api]
1718
[zuko.node :as node]
1819
[zuko.logging :as log :include-macros true]))
1920

@@ -194,7 +195,10 @@
194195
{:r-spot (:root-id spot)
195196
:r-post (:root-id post)
196197
:r-ospt (:root-id ospt)
197-
:r-pool (:root-id pool)})
198+
:r-pool (:root-id pool)
199+
:nr-index-node (block-api/get-block-count tree-block-manager)
200+
:nr-index-block (block-api/get-block-count tuple-block-manager)
201+
:nr-pool-node (block-api/get-block-count pool)})
198202

199203
Closeable
200204
(close [this]
@@ -230,23 +234,23 @@
230234
post-index (tuples/create-tuple-index name post-name r-post)
231235
ospt-index (tuples/create-tuple-index name ospt-name r-ospt)
232236
tspo-index #?(:clj (flat-file/record-store name tspo-name tuples/tuple-size-bytes) :cljs nil)
233-
data-pool (pool/create-pool name r-pool)]
237+
data-pool (pool/create-pool name r-pool nil)]
234238
(->BlockGraph spot-index post-index ospt-index tspo-index data-pool node-allocator id-checker nil nil)))
235239

236240
(defn new-merged-block-graph
237241
"Creates a new BlockGraph object, under a given name. If the resources for that name exist, then they are opened.
238242
If the resources do not exist, then they are created.
239243
name: the label of the location for the graph resources.
240244
tx: The transaction record for this graph."
241-
[name {:keys [r-spot r-post r-ospt r-pool]} node-allocator id-checker]
245+
[name {:keys [r-spot r-post r-ospt r-pool nr-index-node nr-index-block nr-pool-node]} node-allocator id-checker]
242246
;; NOTE: Tree nodes blocks must hold the tuples payload and the tree node header
243-
(let [tree-block-manager (common-utils/create-block-manager name index-name tuples/tree-block-size)
244-
tuple-block-manager (common-utils/create-block-manager name block-name tuples/block-bytes)
247+
(let [tree-block-manager (common-utils/create-block-manager name index-name tuples/tree-block-size nr-index-node)
248+
tuple-block-manager (common-utils/create-block-manager name block-name tuples/block-bytes nr-index-block)
245249
spot-index (tuples/create-tuple-index-for-managers "SPO" tree-block-manager tuple-block-manager r-spot)
246250
post-index (tuples/create-tuple-index-for-managers "POS" tree-block-manager tuple-block-manager r-post)
247251
ospt-index (tuples/create-tuple-index-for-managers "OSP" tree-block-manager tuple-block-manager r-ospt)
248252
tspo-index #?(:clj (flat-file/record-store name tspo-name tuples/tuple-size-bytes) :cljs nil)
249-
data-pool (pool/create-pool name r-pool)]
253+
data-pool (pool/create-pool name r-pool nr-pool-node)]
250254
(->BlockGraph spot-index post-index ospt-index tspo-index data-pool node-allocator id-checker
251255
tree-block-manager tuple-block-manager)))
252256

src/asami/durable/pool.cljc

+11-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
[asami.durable.tree :as tree]
99
[asami.durable.encoder :as encoder :refer [to-bytes]]
1010
[asami.durable.decoder :as decoder :refer [type-info long-bytes-compare]]
11-
[asami.durable.block.block-api :refer [get-long get-byte get-bytes put-byte! put-bytes! put-long! get-id]]
11+
[asami.durable.block.block-api :refer [get-long get-byte get-bytes put-byte! put-bytes! put-long! get-id
12+
CountedBlocks get-block-count]]
1213
[asami.durable.cache :refer [lookup hit miss lru-cache-factory]]
1314
#?(:clj [asami.durable.flat-file :as flat-file])))
1415

@@ -124,6 +125,11 @@
124125
(at [this new-root-id]
125126
(->ReadOnlyPool data (tree/at index new-root-id) new-root-id cache))
126127

128+
CountedBlocks
129+
(get-block-count
130+
[this]
131+
(get-block-count index))
132+
127133
Transaction
128134
(commit! [this]
129135
(force! data)
@@ -151,17 +157,17 @@
151157

152158
(defn open-pool
153159
"Opens all the resources required for a pool, and returns the pool structure"
154-
[name root-id]
160+
[name root-id block-count]
155161
(let [data-store (data-constructor name data-name)
156162
data-compare (pool-comparator-fn data-store)
157163
index (tree/new-block-tree (fn
158164
([] true)
159-
([lname size] (common-utils/create-block-manager name lname size)))
165+
([lname size] (common-utils/create-block-manager name lname size block-count)))
160166
index-name tree-node-size data-compare root-id)
161167
encap-cache (atom (lru-cache-factory {} :threshold encap-cache-size))]
162168
(->DataPool data-store index root-id encap-cache)))
163169

164170
(defn create-pool
165171
"Creates a datapool object"
166-
([name] (create-pool name nil))
167-
([name root-id] (common-utils/named-storage open-pool name root-id)))
172+
([name] (create-pool name nil nil))
173+
([name root-id block-count] (common-utils/named-storage open-pool name root-id block-count)))

src/asami/durable/store.cljc

+19-5
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@
2525

2626
;; transactions contain tree roots for the 3 tree indices,
2727
;; the tree root for the data pool,
28+
;; the 3 block counts for the tuples index tree, the tuples blocks, and the data pool index tree
2829
;; the internal node counter
29-
(def tx-record-size (* 5 common/long-size))
30+
(def tx-record-size (* 8 common/long-size))
3031

3132
(def TxRecord {(s/required-key :r-spot) (s/maybe s/Int)
3233
(s/required-key :r-post) (s/maybe s/Int)
3334
(s/required-key :r-ospt) (s/maybe s/Int)
3435
(s/required-key :r-pool) (s/maybe s/Int)
36+
(s/required-key :nr-index-node) (s/maybe s/Int)
37+
(s/required-key :nr-index-block) (s/maybe s/Int)
38+
(s/required-key :nr-pool-node) (s/maybe s/Int)
3539
(s/required-key :nodes) s/Int
3640
(s/required-key :timestamp) s/Int})
3741

@@ -40,27 +44,37 @@
4044
(s/one s/Int "post root id")
4145
(s/one s/Int "ospt root id")
4246
(s/one s/Int "pool root id")
47+
(s/one s/Int "number of index nodes allocated")
48+
(s/one s/Int "number of index blocks allocated")
49+
(s/one s/Int "number of pool index nodes allocated")
4350
(s/one s/Int "node id counter")]})
4451

4552
(s/defn pack-tx :- TxRecordPacked
4653
"Packs a transaction into a vector for serialization"
47-
[{:keys [r-spot r-post r-ospt r-pool nodes timestamp]} :- TxRecord]
48-
{:timestamp timestamp :tx-data [(or r-spot 0) (or r-post 0) (or r-ospt 0) (or r-pool 0) nodes]})
54+
[{:keys [r-spot r-post r-ospt r-pool nr-index-node nr-index-block nr-pool-node nodes timestamp]} :- TxRecord]
55+
{:timestamp timestamp :tx-data [(or r-spot 0) (or r-post 0) (or r-ospt 0) (or r-pool 0)
56+
(or nr-index-node 0) (or nr-index-block 0) (or nr-pool-node 0)
57+
nodes]})
4958

5059
(s/defn unpack-tx :- TxRecord
5160
"Unpacks a transaction vector into a structure when deserializing"
52-
[{[r-spot r-post r-ospt r-pool nodes] :tx-data timestamp :timestamp} :- TxRecordPacked]
61+
[{[r-spot r-post r-ospt r-pool
62+
nr-index-node nr-index-block nr-pool-node nodes] :tx-data
63+
timestamp :timestamp} :- TxRecordPacked]
5364
(letfn [(non-zero [v] (and v (when-not (zero? v) v)))]
5465
{:r-spot (non-zero r-spot)
5566
:r-post (non-zero r-post)
5667
:r-ospt (non-zero r-ospt)
5768
:r-pool (non-zero r-pool)
69+
:nr-index-node (non-zero nr-index-node)
70+
:nr-index-block (non-zero nr-index-block)
71+
:nr-pool-node (non-zero nr-pool-node)
5872
:nodes nodes
5973
:timestamp timestamp}))
6074

6175
(s/defn new-db :- TxRecordPacked
6276
[]
63-
{:timestamp (long-time (now)) :tx-data [0 0 0 0 0]})
77+
{:timestamp (long-time (now)) :tx-data [0 0 0 0 0 0 0 0]})
6478

6579
(declare ->DurableDatabase)
6680

src/asami/durable/tree.cljc

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
(ns ^{:doc "This namespace provides the basic mechanisms for AVL trees"
22
:author "Paula Gearon"}
33
asami.durable.tree
4-
(:require [asami.durable.block.block-api :refer [Block
4+
(:require [asami.durable.block.block-api :refer [Block CountedBlocks
55
get-id get-byte get-int get-long
66
get-bytes get-ints get-longs
77
put-byte! put-int! put-long!
88
put-bytes! put-ints! put-longs!
99
put-block! copy-over!
1010
allocate-block! get-block get-block-size
11-
write-block copy-to-tx]]
11+
write-block copy-to-tx get-block-count]]
1212
[asami.durable.common :refer [Transaction Closeable Forceable close delete! rewind! commit! force! long-size]]
1313
[asami.durable.cache :refer [lookup hit miss lru-cache-factory]]))
1414

@@ -29,6 +29,11 @@
2929

3030
(def ^:const node-cache-size 1024)
3131

32+
(defn to-hex
33+
[n]
34+
#?(:clj (Long/toHexString n)
35+
:cljs (.toString n 16)))
36+
3237
(defprotocol TreeNode
3338
(get-parent [this] "Returns the parent node. Not the Node ID, but the node object.")
3439
(get-child-id [this side] "Gets the Node ID of the child on the given side")
@@ -96,7 +101,7 @@
96101

97102
Object
98103
(toString [this]
99-
(let [payload (mapv #(Long/toHexString (get-long this %)) (range (/ (- (:size block) header-size) 8)))]
104+
(let [payload (mapv #(to-hex (get-long this %)) (range (/ (- (:size block) header-size) 8)))]
100105
(str "Node[" (get-id this) "] L:" (get-child-id this left) " R:" (get-child-id this right) " payload:"
101106
payload)))
102107

@@ -389,6 +394,11 @@
389394
;; if modified-node is not yet set, then new-node was the root
390395
[(assoc this :root nd) (or modified-node new-node)]))))))
391396

397+
CountedBlocks
398+
(get-block-count [this]
399+
(when own-manager
400+
(get-block-count block-manager)))
401+
392402
Transaction
393403
(rewind! [this]
394404
(when own-manager

src/asami/durable/tuples.cljc

+3-3
Original file line numberDiff line numberDiff line change
@@ -591,14 +591,14 @@
591591
(defn open-tuples
592592
[order-name name root-id]
593593
;; create a factory fn that returns true on 0-arity to indicate that the index manager is owned by the calling tree
594-
(let [index (tree/new-block-tree (fn ([] true) ([lname size] (common-utils/create-block-manager name lname size)))
594+
(let [index (tree/new-block-tree (fn ([] true) ([lname size] (common-utils/create-block-manager name lname size nil)))
595595
(str order-name index-name) tree-node-size tuple-node-compare root-id)
596-
blocks (common-utils/create-block-manager name (str order-name block-name) block-bytes)]
596+
blocks (common-utils/create-block-manager name (str order-name block-name) block-bytes nil)]
597597
(->TupleIndex name index blocks root-id true)))
598598

599599
(defn create-tuple-index
600600
"Creates a tuple index for a name"
601601
([name order-name]
602602
(create-tuple-index name order-name nil))
603603
([name order-name root-id]
604-
(common-utils/named-storage (partial open-tuples order-name) name root-id)))
604+
(common-utils/named-storage (partial open-tuples order-name) name root-id nil)))

test/asami/durable/block/blockfile_test.clj

+5-5
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
(defn exec-bf
2020
[filename f]
2121
(let [filename (util/temp-file filename)
22-
{:keys [block-file file]} (open-block-file filename test-block-size)]
22+
{:keys [block-file file]} (open-block-file filename test-block-size nil)]
2323
(try
2424
(f file block-file)
2525
(finally
@@ -34,7 +34,7 @@
3434

3535
(deftest test-allocate
3636
(let [filename (util/temp-file "ualloc")
37-
block-file (open-block-file filename test-block-size)
37+
block-file (open-block-file filename test-block-size nil)
3838
block-file (set-nr-blocks! block-file 1)]
3939
(try
4040
(let [blk (block-for block-file 0)]
@@ -48,7 +48,7 @@
4848
(deftest test-write
4949
(let [file-str "bftest"
5050
filename (util/temp-file file-str)
51-
block-file (open-block-file filename test-block-size)
51+
block-file (open-block-file filename test-block-size nil)
5252
bf (set-nr-blocks! block-file 4)
5353
b (block-for bf 0)
5454
_ (put-string! b str0)
@@ -68,7 +68,7 @@
6868
(unmap bf)
6969
(cleanup)
7070

71-
(let [block-file (open-block-file filename test-block-size)]
71+
(let [block-file (open-block-file filename test-block-size nil)]
7272

7373
;; did it persist
7474

@@ -87,7 +87,7 @@
8787
(deftest test-performance
8888
(let [file-str "perftest"
8989
filename (util/temp-file file-str)
90-
block-file (open-block-file filename test-block-size)
90+
block-file (open-block-file filename test-block-size nil)
9191
block-file (clear! block-file)
9292
nr-blocks 100000
9393
bf (set-nr-blocks! block-file nr-blocks)]

0 commit comments

Comments
 (0)