🎉 Add virtual clock implementation

This commit is contained in:
Andrey Antukh 2025-10-03 11:44:51 +02:00 committed by Alejandro Alonso
parent fc8029abf7
commit bd63598185
4 changed files with 77 additions and 15 deletions

View File

@ -332,16 +332,16 @@
(def ^:private (def ^:private
sql:delete-expired sql:delete-expired
"delete from http_session "DELETE FROM http_session
where updated_at < now() - ?::interval WHERE updated_at < ?::timestamptz
or (updated_at is null and or (updated_at is null and
created_at < now() - ?::interval)") created_at < ?::timestamptz)")
(defn- collect-expired-tasks (defn- collect-expired-tasks
[{:keys [::db/conn ::tasks/max-age]}] [{:keys [::db/conn ::tasks/max-age]}]
(let [interval (db/interval max-age) (let [threshold (ct/minus (ct/now) max-age)
result (db/exec-one! conn [sql:delete-expired interval interval]) result (-> (db/exec-one! conn [sql:delete-expired threshold threshold])
result (:next.jdbc/update-count result)] (db/get-update-count))]
(l/debug :task "gc" (l/debug :task "gc"
:hint "clean http sessions" :hint "clean http sessions"
:deleted result) :deleted result)
@ -350,4 +350,5 @@
(defmethod ig/init-key ::tasks/gc (defmethod ig/init-key ::tasks/gc
[_ {:keys [::tasks/max-age] :as cfg}] [_ {:keys [::tasks/max-age] :as cfg}]
(l/debug :hint "initializing session gc task" :max-age max-age) (l/debug :hint "initializing session gc task" :max-age max-age)
(fn [_] (db/tx-run! cfg collect-expired-tasks))) (fn [_]
(db/tx-run! cfg collect-expired-tasks)))

View File

@ -334,7 +334,7 @@
:app.rpc.doc/routes :app.rpc.doc/routes
{:app.rpc/methods (ig/ref :app.rpc/methods)} {:app.rpc/methods (ig/ref :app.rpc/methods)}
:app.rpc/routes ::rpc/routes
{::rpc/methods (ig/ref :app.rpc/methods) {::rpc/methods (ig/ref :app.rpc/methods)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)
::session/manager (ig/ref ::session/manager) ::session/manager (ig/ref ::session/manager)
@ -426,6 +426,9 @@
;; module requires the migrations to run before initialize. ;; module requires the migrations to run before initialize.
::migrations (ig/ref :app.migrations/migrations)} ::migrations (ig/ref :app.migrations/migrations)}
::setup/clock
{}
:app.loggers.audit.archive-task/handler :app.loggers.audit.archive-task/handler
{::setup/props (ig/ref ::setup/props) {::setup/props (ig/ref ::setup/props)
::db/pool (ig/ref ::db/pool) ::db/pool (ig/ref ::db/pool)

View File

@ -0,0 +1,48 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC
(ns app.setup.clock
"A service/module that manages the system clock and allows runtime
modification of time offset (useful for testing and time adjustments)."
(:require
[app.common.logging :as l]
[app.common.time :as ct]
[app.setup :as-alias setup]
[integrant.core :as ig])
(:import
java.time.Clock
java.time.Duration))
(defonce current
(atom {:clock (Clock/systemDefaultZone)
:offset nil}))
(defmethod ig/init-key ::setup/clock
[_ _]
(add-watch current ::common
(fn [_ _ _ {:keys [clock offset]}]
(let [clock (if (ct/duration? offset)
(Clock/offset ^Clock clock
^Duration offset)
clock)]
(l/wrn :hint "altering clock" :clock (str clock))
(alter-var-root #'ct/*clock* (constantly clock))))))
(defmethod ig/halt-key! ::setup/clock
[_ _]
(remove-watch current ::common))
(defn set-offset!
[duration]
(swap! current assoc :offset (some-> duration ct/duration)))
(defn set-clock!
([]
(swap! current assoc :clock (Clock/systemDefaultZone)))
([clock]
(when (instance? Clock clock)
(swap! current assoc :clock clock))))

View File

@ -52,6 +52,7 @@
[cuerdas.core :as str]) [cuerdas.core :as str])
#?(:clj #?(:clj
(:import (:import
java.time.Clock
java.time.Duration java.time.Duration
java.time.Instant java.time.Instant
java.time.OffsetDateTime java.time.OffsetDateTime
@ -63,10 +64,15 @@
java.time.temporal.TemporalAmount java.time.temporal.TemporalAmount
java.time.temporal.TemporalUnit))) java.time.temporal.TemporalUnit)))
#?(:clj (def ^:dynamic *clock* (Clock/systemDefaultZone)))
(defn now (defn now
[] ([]
#?(:clj (Instant/now) #?(:clj (Instant/now *clock*)
:cljs (new js/Date))) :cljs (new js/Date)))
([clock]
#?(:cljs (throw (js/Error. "not implemented" {:clock clock}))
:clj (Instant/now ^Clock clock))))
;; --- DURATION ;; --- DURATION
@ -319,12 +325,16 @@
:cljs (js/Error. "unsupported type")))))) :cljs (js/Error. "unsupported type"))))))
(defn in-future (defn in-future
[v] ([v]
(plus (now) v)) (plus (now) v))
([clock v]
(plus (now clock) v)))
(defn in-past (defn in-past
[v] ([v]
(minus (now) v)) (minus (now) v))
([clock v]
(minus (now clock) v)))
#?(:clj #?(:clj
(defn diff (defn diff