forked from leadtune/clj-cache
/
ehcache.clj
194 lines (162 loc) · 6.87 KB
/
ehcache.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
(ns clj-cache.test.ehcache
"Ehcache tests"
(:use clojure.test)
(:use clj-cache.cache)
(:use [clojure.set :only [union]])
(:require [clj-cache.ehcache :as ehcache]
[clojure.java.io :as io]
;;[clojure.contrib.jmx :as jmx]
)
(:import [java.io File]))
;;--- Copy and paste of clj-cache.test.cache (different src tree)
(defn mkdir-p
"Create a directory and all parent directories if they do not exist."
[dir]
(.mkdirs (io/file dir)))
(defn delete-file-recursively
"Delete file f. If it's a directory, recursively delete all its contents.
Raise an exception if any deletion fails unless silently is true."
[f & [silently]]
(let [f (io/file f)]
(if (.isDirectory f)
(doseq [child (.listFiles f)]
(delete-file-recursively child silently)))
(io/delete-file f silently)))
(defn rm-rf
"Remove a directory, ignoring any errors."
[path]
(delete-file-recursively path true))
(defmacro defilefun [name docstring args & body]
`(do
(defmulti ~name ~docstring class)
(defmethod ~name File ~args ~@body)
(defmethod ~name String ~args (~name (io/file ~@args)))
(defmethod ~name :default ~args false)))
(defilefun exists?
"Returns true if path exists; false otherwise."
[path]
(.exists ^File path))
(defn slow [a] (Thread/sleep a) a)
;; Default ehcache setup has a ttl of 120 secs but unittests should
;; not be affected by this
(def fast-default (cached slow (ehcache/strategy)))
(defmacro how-long [expr]
`(let [start# (. System (nanoTime))
ret# ~expr
msecs# (/ (double (- (. System (nanoTime)) start#))
1000000.0)]
{:msecs msecs# :ret ret#}))
(defn expect [situation f check t should]
(let [{:keys [msecs ret]} (how-long (f t))]
(is (check msecs t) (str situation " (expected time:" t ", actual time: " msecs ") " should))
(is (= ret t) (str situation " returns correct value"))))
(defn is-caching [f t]
(invalidate-cache f t)
(expect "First call" f > t "hits function" )
(expect "Second call" f < t "is cached")
(expect "Third call" f < t "is cached"))
(deftest is-caching-ehcache (is-caching fast-default 100))
(defn invalidating [f t1 t2 t3]
(invalidate-cache f t1)
(invalidate-cache f t2)
(invalidate-cache f t3)
(expect "First call" f > t1 "hits function")
(expect "First call" f > t2 "hits function")
(expect "First call" f > t3 "hits function")
(invalidate-cache f t1)
(expect "Invalidated entry" f > t1 "hits function")
(expect "Second call" f < t2 "is cached")
(expect "Second call" f < t3 "is cached")
(expect "Third call" f < t1 "is cached"))
(deftest invalidating-ehcache (invalidating fast-default 50 51 52))
(defn-cached cached-fn
(ehcache/strategy)
"A cached function definition"
[t]
(Thread/sleep t)
t)
(deftest is-caching-def (is-caching cached-fn 100))
;; ------------- Persistence tests ----------------------------------------------
;; Directory to store peristent cache files
(def cache-directory* (str (io/file (System/getProperty "java.io.tmpdir")
"ehcache")))
;; A CacheManager config to use for persistence tests
(def persistent-manager-config*
[:ehcache
[:disk-store
{:path cache-directory*}]
[:default-cache
{:max-elements-in-memory 100
:eternal false
:overflow-to-disk false
:disk-persistent false
:memory-store-eviction-policy "LRU"}]])
;; A cache config to use for persistence tests
(def persistent-cache-config* {:max-elements-in-memory 100
:eternal true
:overflow-to-disk true
:disk-persistent true
:clear-on-flush true
:statistics true})
(deftest is-persistent
;; Start with a clean cache directory
(if (exists? cache-directory*)
(rm-rf cache-directory*))
(mkdir-p cache-directory*)
;; Create the persistent cache
(let [first-manager (ehcache/new-manager persistent-manager-config*)
_ (ehcache/register-with-jmx first-manager)
f (cached slow (ehcache/strategy first-manager
persistent-cache-config*))]
;;(is (not (empty? (jmx/mbean-names "net.sf.ehcache:*"))))
(expect "First call" f > 100 "hits function")
(expect "Second call" f > 101 "hits function")
(expect "Third call" f > 102 "hits function")
(expect "Second call" f < 100 "is cached")
(expect "Second call" f < 101 "is cached")
(expect "Second call" f < 102 "is cached")
;; Simulate end of VM
(ehcache/shutdown first-manager))
;; Simulate start of new VM
(let [second-manager (ehcache/new-manager persistent-manager-config*)
f (cached slow (ehcache/strategy second-manager
persistent-cache-config*))]
(expect "First call" f < 100 "is cached")
(expect "First call" f < 101 "is cached")
(expect "First call" f < 102 "is cached")
(ehcache/shutdown second-manager)))
(defrecord RecordThatHasNonUniqueToString [x]
Object
(toString [x] "Look at me, I'm always the same so I would be a horrible cache key."))
;; ------------- Misc tests ----------------------------------------------
;; This test ensures that we are internally using serialization in the key, as the docs advertise
(deftest serialization-is-used
(let [cached-identity (cached identity (ehcache/strategy))]
(is (not= (cached-identity (RecordThatHasNonUniqueToString. 55))
(cached-identity (RecordThatHasNonUniqueToString. 42))))))
(deftest cache-names
(let [cache-config {:max-elements-in-memory 100
:eternal false
:overflow-to-disk false
:disk-persistent false}
manager (ehcache/new-manager [:ehcache
[:default-cache
cache-config]])
_ (cached slow (ehcache/strategy manager cache-config))
_ (cached identity (ehcache/strategy manager cache-config))
expected #{"user.slow" ;; *ns* is user here due to an artifact of how deftest runs I believe...
"user.identity"}]
(is (= (set (ehcache/cache-seq manager))
expected))))
;; ------------- Blocking tests ----------------------------------------------
(deftest blocking-cache-via-runtime-config
(let [total (atom 0)
side-effect (fn [x] (Thread/sleep x) (swap! total + x))
cached-side-effect (cached side-effect
(ehcache/strategy
{:max-elements-in-memory 10
:block true ;; <-- where the magic happens
}))]
(future (cached-side-effect 100)) ;; Fire functions a the same time to ensure the second one blocks on first computation
(cached-side-effect 100)
(is (= 100 @total))))