🎉 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
sql:delete-expired
"delete from http_session
where updated_at < now() - ?::interval
"DELETE FROM http_session
WHERE updated_at < ?::timestamptz
or (updated_at is null and
created_at < now() - ?::interval)")
created_at < ?::timestamptz)")
(defn- collect-expired-tasks
[{:keys [::db/conn ::tasks/max-age]}]
(let [interval (db/interval max-age)
result (db/exec-one! conn [sql:delete-expired interval interval])
result (:next.jdbc/update-count result)]
(let [threshold (ct/minus (ct/now) max-age)
result (-> (db/exec-one! conn [sql:delete-expired threshold threshold])
(db/get-update-count))]
(l/debug :task "gc"
:hint "clean http sessions"
:deleted result)
@ -350,4 +350,5 @@
(defmethod ig/init-key ::tasks/gc
[_ {:keys [::tasks/max-age] :as cfg}]
(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/methods (ig/ref :app.rpc/methods)}
:app.rpc/routes
::rpc/routes
{::rpc/methods (ig/ref :app.rpc/methods)
::db/pool (ig/ref ::db/pool)
::session/manager (ig/ref ::session/manager)
@ -426,6 +426,9 @@
;; module requires the migrations to run before initialize.
::migrations (ig/ref :app.migrations/migrations)}
::setup/clock
{}
:app.loggers.audit.archive-task/handler
{::setup/props (ig/ref ::setup/props)
::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])
#?(:clj
(:import
java.time.Clock
java.time.Duration
java.time.Instant
java.time.OffsetDateTime
@ -63,10 +64,15 @@
java.time.temporal.TemporalAmount
java.time.temporal.TemporalUnit)))
#?(:clj (def ^:dynamic *clock* (Clock/systemDefaultZone)))
(defn now
[]
#?(:clj (Instant/now)
:cljs (new js/Date)))
([]
#?(:clj (Instant/now *clock*)
:cljs (new js/Date)))
([clock]
#?(:cljs (throw (js/Error. "not implemented" {:clock clock}))
:clj (Instant/now ^Clock clock))))
;; --- DURATION
@ -319,12 +325,16 @@
:cljs (js/Error. "unsupported type"))))))
(defn in-future
[v]
(plus (now) v))
([v]
(plus (now) v))
([clock v]
(plus (now clock) v)))
(defn in-past
[v]
(minus (now) v))
([v]
(minus (now) v))
([clock v]
(minus (now clock) v)))
#?(:clj
(defn diff