Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ io.github.latacora/sqlite-cache {:git/url "https://github.com/latacora/sqlite-ca
:read {"some-tag" read-handler}}})
```

Filesystem `:dbname` paths may include missing parent directories; `cache`
creates them before opening SQLite. New filesystem databases on POSIX systems
are created with mode `0600`; existing files keep their permissions. SQLite
URI paths (`file:...`) and in-memory databases are passed through unchanged.

### Supported types

The cache uses [transit-canon](https://github.com/latacora/transit-canon) for serialization, so out of the box it supports:
Expand Down
1 change: 1 addition & 0 deletions deps.edn
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{:paths ["src" "resources"]
:deps
{org.clojure/clojure {:mvn/version "1.12.4"}
babashka/fs {:mvn/version "0.5.22"}

;; Database
com.github.seancorfield/next.jdbc {:mvn/version "1.3.1070"}
Expand Down
20 changes: 19 additions & 1 deletion src/com/latacora/sqlite_cache/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@
All cache analysis and cleanup (actually deleting expired records and
VACUUMing afterwards) is done manually (see `maintain!`)."
(:require
[babashka.fs :as fs]
[clojure.string :as str]
[honey.sql.helpers :as h]
[next.jdbc :as jdbc]
[com.latacora.sqlite-cache.serialization :as ser]
[com.latacora.sqlite-cache.db :as db]
[com.latacora.sqlite-cache.ddl :as ddl]
[com.latacora.sqlite-cache.maintenance :as maint])
(:import
(java.nio.file FileAlreadyExistsException)
(java.util.concurrent LinkedBlockingQueue TimeUnit)))

;; This currently does not use clojure.core.cache, but it probably could stand
Expand Down Expand Up @@ -214,11 +217,25 @@
:ttl default-ttl
:max-age default-max-age})

(defn- prepare-db-file!
[{:keys [dbname]}]
(when (and dbname (not= dbname ":memory:")
(not (str/starts-with? dbname "file:")))
(let [target (fs/absolutize dbname)]
(fs/create-dirs (fs/parent target))
(try
(fs/create-file target {:posix-file-permissions "rw-------"})
(catch FileAlreadyExistsException _ nil)
;; Let SQLite use platform defaults when POSIX permissions are unavailable.
(catch UnsupportedOperationException _ nil)))))

(defn cache
"Builds a cache with given opts.

This will ensure the cache is ready to use (has the appropriate schema,
indexes, et cetera), and returns a function with the same signature as the
indexes, et cetera), creates missing parent directories for filesystem
`:dbname` paths, creates new POSIX database files with owner-only access,
and returns a function with the same signature as the
function being cached.

The returned function has metadata containing the full cache configuration,
Expand All @@ -245,6 +262,7 @@
are required for cache-key correctness."
[opts]
(let [{:keys [db] :as opts} (merge default-opts opts)
_ (prepare-db-file! db)
read-conn (jdbc/get-connection db)
write-conn (jdbc/get-connection db)
write-queue (make-write-queue! write-conn)
Expand Down
18 changes: 18 additions & 0 deletions test/com/latacora/sqlite_cache/core_test.clj
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns com.latacora.sqlite-cache.core-test
(:require
[babashka.fs :as fs]
[cognitect.transit :as transit]
[com.latacora.sqlite-cache.core :as c]
[com.latacora.sqlite-cache.maintenance :as maint]
Expand Down Expand Up @@ -690,3 +691,20 @@

;; Second call should succeed with the same arguments
(t/is (= 10 (cached-fn 5)))))))

(t/deftest creates-missing-cache-parent-directories
(let [root (fs/create-temp-dir {:prefix "sqlite-cache-parents-"})
path (fs/path root "nested" "cache.db")]
(try
(t/is (not (fs/exists? (fs/parent path))))
(let [cached (c/cache {:db {:dbtype "sqlite" :dbname (str path)}
:func inc :func-name "test/parents"})]
(t/is (fs/regular-file? path))
(when-let [permissions (try
(fs/posix-file-permissions path)
(catch UnsupportedOperationException _ nil))]
(t/is (= "rw-------" (fs/posix->str permissions))))
(t/is (= 2 (cached 1)))
(tu/assert-n-entries! cached 1))
(finally
(fs/delete-tree root)))))
Loading