Util to put data into a temporary file and schedule it for deletion after a specified time period. Currently used to store card's rows data when sending notification since it can be large and we don't want to keep it in memory. | (ns metabase.notification.payload.temp-storage (:require [clojure.java.io :as io] [metabase.util.random :as random] [taoensso.nippy :as nippy]) (:import (java.io File) (java.util.concurrent Executors ScheduledThreadPoolExecutor))) |
(set! *warn-on-reflection* true) | |
(def ^:private temp-dir
(delay
(let [dir (io/file (System/getProperty "java.io.tmpdir")
(str "metabase-notification-" (random/random-name)))]
(.mkdirs dir)
(.deleteOnExit dir)
dir))) | |
(def ^:private deletion-scheduler
(delay
(Executors/newScheduledThreadPool 1))) | |
(defn- temp-file
[]
(doto (File/createTempFile "notification-" ".npy" @temp-dir)
(.deleteOnExit))) | |
(defn- write-to-file [^File file data] (nippy/freeze-to-file file data)) | |
(defn- read-from-file
[^File file]
(when (.exists file)
(nippy/thaw-from-file file))) | |
(.addShutdownHook
(Runtime/getRuntime)
(Thread. ^Runnable (fn []
(when @deletion-scheduler
(.shutdown ^ScheduledThreadPoolExecutor @deletion-scheduler))))) | |
(defprotocol Cleanable (cleanup! [this] "Cleanup any resources associated with this object")) | |
(deftype TempFileStorage [^File file]
Cleanable
(cleanup! [_]
(when (.exists file)
(io/delete-file file true)))
clojure.lang.IDeref
(deref [_]
(if (.exists file)
(read-from-file file)
(throw (ex-info "File no longer exists" {:file file}))))
Object
(toString [_]
(str "#TempFileStorage{:file " file "}"))
;; Add equality behavior
(equals [_this other]
(and (instance? TempFileStorage other)
(= file (.file ^TempFileStorage other))))
(hashCode [_]
(.hashCode file))) | |
------------------------------------------------------------------------------------------------;; Public APIs ;; ------------------------------------------------------------------------------------------------;; | |
Write data to a temporary file. Returns a TempFileStorage type that: - Implements IDeref - use @ to read the data from the file - Implements Cleanable - call cleanup! when the file is no longer needed | (defn to-temp-file!
[data]
(let [f (temp-file)]
(write-to-file f data)
(TempFileStorage. f))) |