Skip to content

Commit 8507fe6

Browse files
committed
feat: unified time testing with mutable TestClock and app_now() DB function
Implements a hybrid approach to unify time management across the Haskell app layer and PostgreSQL database in tests, enabling time fast-forwarding for timeseries data and background job testing. Three components: 1. Mutable TestClock (src/Pkg/TestClock.hs): - IORef-backed clock with advanceTime/setTestTime/getTestTime - runMutableTime: custom Time effect interpreter reading from IORef - runWithTimeSyncedPool: pool wrapper that sets DB GUC on each checkout 2. app_now() PostgreSQL function (migration 0033): - Reads app.current_time GUC, falls back to NOW() in production - Updated triggers: set_updated_at, new_anomaly_proc, check_triggered_query_monitors, check_tests_to_trigger 3. NOW() replaced with ? parameters in 16 source files: - All SQL queries now receive time from Time.currentTime effect - Production uses real time; tests use mutable TestClock time Test infrastructure updates: - TestResources gains trTestClock field - All test runners thread TestClock through effect stacks - Replaced getCurrentTime with getTestTime in all test specs - Added advanceTestTime/advanceMinutes/advanceHours/advanceDays helpers https://claude.ai/code/session_019Mn7XLed4oq6j7dWqciekH
1 parent 1d74e96 commit 8507fe6

33 files changed

Lines changed: 686 additions & 347 deletions

monoscope.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ library
136136
Pkg.Parser.Stats
137137
Pkg.QueryCache
138138
Pkg.Queue
139+
Pkg.TestClock
139140
Pkg.TestUtils
140141
ProcessMessage
141142
Start

src/BackgroundJobs.hs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,10 @@ processBackgroundJob authCtx bgJob =
240240
<$> PG.query
241241
[sql|SELECT COUNT(*) FROM background_jobs
242242
WHERE payload->>'tag' = 'HourlyJob'
243-
AND run_at >= date_trunc('day', now())
244-
AND run_at < date_trunc('day', now()) + interval '1 day'
243+
AND run_at >= date_trunc('day', ?::timestamptz)
244+
AND run_at < date_trunc('day', ?::timestamptz) + interval '1 day'
245245
AND status IN ('queued', 'locked')|]
246-
()
246+
(currentTime, currentTime)
247247

248248
unless hourlyJobsExist $ do
249249
Log.logInfo "Scheduling hourly jobs for today" ()
@@ -265,7 +265,7 @@ processBackgroundJob authCtx bgJob =
265265
Relude.when hourlyJobsExist
266266
$ Log.logInfo "Hourly jobs already scheduled for today, skipping" ()
267267

268-
projects <- PG.query [sql|SELECT DISTINCT p.id FROM projects.projects p JOIN otel_logs_and_spans o ON o.project_id = p.id::text WHERE p.active = TRUE AND p.deleted_at IS NULL AND p.payment_plan != 'ONBOARDING' AND o.timestamp > now() - interval '24 hours'|] ()
268+
projects <- PG.query [sql|SELECT DISTINCT p.id FROM projects.projects p JOIN otel_logs_and_spans o ON o.project_id = p.id::text WHERE p.active = TRUE AND p.deleted_at IS NULL AND p.payment_plan != 'ONBOARDING' AND o.timestamp > ? - interval '24 hours'|] (Only currentTime)
269269
Log.logInfo "Scheduling jobs for projects" ("project_count", length projects)
270270
forM_ projects \p -> do
271271
-- Check if this project's jobs already scheduled for today (per-project idempotent check)
@@ -274,10 +274,10 @@ processBackgroundJob authCtx bgJob =
274274
[sql|SELECT COUNT(*) FROM background_jobs
275275
WHERE payload->>'tag' = 'FiveMinuteSpanProcessing'
276276
AND payload->>'projectId' = ?
277-
AND run_at >= date_trunc('day', now())
278-
AND run_at < date_trunc('day', now()) + interval '1 day'
277+
AND run_at >= date_trunc('day', ?::timestamptz)
278+
AND run_at < date_trunc('day', ?::timestamptz) + interval '1 day'
279279
AND status IN ('queued', 'locked')|]
280-
(Only p)
280+
(p, currentTime, currentTime)
281281

282282
let projectJobsExist = case existingProjectJobs of
283283
[Only (count :: Int)] -> count >= 288
@@ -1577,10 +1577,11 @@ evaluateQueryMonitor monitor startWall = do
15771577
let warningAt = if status == Monitors.MSWarning then Just startWall else Nothing
15781578
alertAt = if status == Monitors.MSAlerting then Just startWall else Nothing
15791579
void $ PG.execute [sql| UPDATE monitors.query_monitors SET warning_last_triggered = ?, alert_last_triggered = ? WHERE id = ? |] (warningAt, alertAt, monitor.id)
1580+
now <- Time.currentTime
15801581
void
15811582
$ PG.execute
1582-
[sql| INSERT INTO background_jobs (run_at, status, payload) VALUES (NOW(), 'queued', ?) |]
1583-
(Only $ AE.object ["tag" AE..= "QueryMonitorAlert", "contents" AE..= V.singleton monitor.id])
1583+
[sql| INSERT INTO background_jobs (run_at, status, payload) VALUES (?, 'queued', ?) |]
1584+
(now, AE.object ["tag" AE..= "QueryMonitorAlert", "contents" AE..= V.singleton monitor.id])
15841585
void $ Monitors.updateLastEvaluatedAt monitor.id startWall
15851586

15861587

src/Models/Apis/Anomalies.hs

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,10 @@ import Database.PostgreSQL.Simple.Time (parseUTCTime)
4545
import Database.PostgreSQL.Simple.ToField (ToField)
4646
import Database.PostgreSQL.Simple.Types (Query (Query))
4747
import Deriving.Aeson qualified as DAE
48-
import Effectful (Eff)
48+
import Effectful (Eff, type (:>))
4949
import Effectful.PostgreSQL qualified as PG
50+
import Effectful.Time (Time)
51+
import Effectful.Time qualified as Time
5052
import Models.Apis.Endpoints qualified as Endpoints
5153
import Models.Apis.Fields qualified as Fields (
5254
FieldCategoryEnum,
@@ -202,9 +204,10 @@ where
202204
|]
203205

204206

205-
countAnomalies :: DB es => Projects.ProjectId -> Text -> Eff es Int
207+
countAnomalies :: (DB es, Time :> es) => Projects.ProjectId -> Text -> Eff es Int
206208
countAnomalies pid report_type = do
207-
result <- PG.query (Query $ encodeUtf8 q) (Only pid)
209+
now <- Time.currentTime
210+
result <- PG.query (Query $ encodeUtf8 q) (pid, now)
208211
case result of
209212
[Only countt] -> return countt
210213
v -> return $ length v
@@ -213,43 +216,45 @@ countAnomalies pid report_type = do
213216
q =
214217
[text|
215218
SELECT COUNT(*) as anomaly_count
216-
FROM apis.issues iss WHERE project_id = ? and created_at > current_timestamp - interval $report_interval
219+
FROM apis.issues iss WHERE project_id = ? and created_at > ? - interval $report_interval
217220
|]
218221

219222

220-
acknowledgeAnomalies :: DB es => Users.UserId -> V.Vector Text -> Eff es [Text]
223+
acknowledgeAnomalies :: (DB es, Time :> es) => Users.UserId -> V.Vector Text -> Eff es [Text]
221224
acknowledgeAnomalies uid aids
222225
| V.null aids = pure []
223226
| otherwise = do
227+
now <- Time.currentTime
224228
-- Get anomaly hashes from the issues being acknowledged
225229
anomalyHashesResult :: [Only (V.Vector Text)] <- PG.query qGetHashes (Only aids)
226230
let allAnomalyHashes = V.concat $ coerce @[Only (V.Vector Text)] @[V.Vector Text] anomalyHashesResult
227231
-- Update issues
228-
(_ :: [Only Text]) <- PG.query qIssues (uid, aids)
232+
(_ :: [Only Text]) <- PG.query qIssues (uid, now, aids)
229233
-- Update anomalies - both directly referenced and those tracked by the issues
230-
(_ :: [Only Text]) <- PG.query q (uid, aids)
234+
(_ :: [Only Text]) <- PG.query q (uid, now, aids)
231235
-- Also update anomalies referenced by the issues' anomaly_hashes arrays
232236
unless (V.null allAnomalyHashes) $ do
233-
_ <- PG.execute qAnomaliesByHash (uid, allAnomalyHashes)
237+
_ <- PG.execute qAnomaliesByHash (uid, now, allAnomalyHashes)
234238
pass
235-
coerce @[Only Text] @[Text] <$> PG.query q (uid, aids)
239+
coerce @[Only Text] @[Text] <$> PG.query q (uid, now, aids)
236240
where
237241
qGetHashes = [sql| SELECT anomaly_hashes FROM apis.issues WHERE id=ANY(?::uuid[]) |]
238-
qIssues = [sql| update apis.issues set acknowledged_by=?, acknowledged_at=NOW() where id=ANY(?::uuid[]) RETURNING target_hash; |]
239-
q = [sql| update apis.anomalies set acknowledged_by=?, acknowledged_at=NOW() where id=ANY(?::uuid[]) RETURNING target_hash; |]
240-
qAnomaliesByHash = [sql| update apis.anomalies set acknowledged_by=?, acknowledged_at=NOW() where target_hash=ANY(?) |]
242+
qIssues = [sql| update apis.issues set acknowledged_by=?, acknowledged_at=? where id=ANY(?::uuid[]) RETURNING target_hash; |]
243+
q = [sql| update apis.anomalies set acknowledged_by=?, acknowledged_at=? where id=ANY(?::uuid[]) RETURNING target_hash; |]
244+
qAnomaliesByHash = [sql| update apis.anomalies set acknowledged_by=?, acknowledged_at=? where target_hash=ANY(?) |]
241245

242246

243-
acknowlegeCascade :: DB es => Users.UserId -> V.Vector Text -> Eff es Int64
247+
acknowlegeCascade :: (DB es, Time :> es) => Users.UserId -> V.Vector Text -> Eff es Int64
244248
acknowlegeCascade uid targets
245249
| V.null targets = pure 0
246250
| otherwise = do
247-
_ <- PG.execute qIssues (uid, hashes)
248-
PG.execute q (uid, hashes)
251+
now <- Time.currentTime
252+
_ <- PG.execute qIssues (uid, now, hashes)
253+
PG.execute q (uid, now, hashes)
249254
where
250255
hashes = (<> "%") <$> targets
251-
qIssues = [sql| UPDATE apis.issues SET acknowledged_by = ?, acknowledged_at = NOW() WHERE target_hash=ANY (?); |]
252-
q = [sql| UPDATE apis.anomalies SET acknowledged_by = ?, acknowledged_at = NOW() WHERE target_hash LIKE ANY (?); |]
256+
qIssues = [sql| UPDATE apis.issues SET acknowledged_by = ?, acknowledged_at = ? WHERE target_hash=ANY (?); |]
257+
q = [sql| UPDATE apis.anomalies SET acknowledged_by = ?, acknowledged_at = ? WHERE target_hash LIKE ANY (?); |]
253258

254259

255260
-------------------------------------------------------------------------------------------

src/Models/Apis/Issues.hs

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -348,35 +348,39 @@ findOpenIssueForEndpoint pid targetHash =
348348

349349

350350
-- | Update issue with new anomaly data
351-
updateIssueWithNewAnomaly :: DB es => IssueId -> APIChangeData -> Eff es ()
352-
updateIssueWithNewAnomaly issueId newData = void $ PG.execute q (Aeson newData, issueId)
351+
updateIssueWithNewAnomaly :: (DB es, Time :> es) => IssueId -> APIChangeData -> Eff es ()
352+
updateIssueWithNewAnomaly issueId newData = do
353+
now <- Time.currentTime
354+
void $ PG.execute q (Aeson newData, now, issueId)
353355
where
354356
q =
355357
[sql|
356358
UPDATE apis.issues
357-
SET
359+
SET
358360
issue_data = issue_data || ?::jsonb,
359361
affected_requests = affected_requests + 1,
360-
updated_at = NOW()
362+
updated_at = ?
361363
WHERE id = ?
362364
|]
363365

364366

365367
-- | Update issue enhancement
366-
updateIssueEnhancement :: DB es => IssueId -> Text -> Text -> Text -> Eff es ()
367-
updateIssueEnhancement issueId title action complexity = void $ PG.execute q params
368+
updateIssueEnhancement :: (DB es, Time :> es) => IssueId -> Text -> Text -> Text -> Eff es ()
369+
updateIssueEnhancement issueId title action complexity = do
370+
now <- Time.currentTime
371+
void $ PG.execute q params
368372
where
369373
q =
370374
[sql|
371375
UPDATE apis.issues
372-
SET
376+
SET
373377
title = ?,
374378
recommended_action = ?,
375379
migration_complexity = ?,
376-
updated_at = NOW()
380+
updated_at = ?
377381
WHERE id = ?
378382
|]
379-
params = (title, action, complexity, issueId)
383+
params = (title, action, complexity, now, issueId)
380384

381385

382386
-- | Update issue criticality and severity
@@ -395,13 +399,15 @@ updateIssueCriticality issueId isCritical severity = void $ PG.execute q params
395399

396400

397401
-- | Acknowledge issue
398-
acknowledgeIssue :: DB es => IssueId -> Users.UserId -> Eff es ()
399-
acknowledgeIssue issueId userId = void $ PG.execute q (userId, issueId)
402+
acknowledgeIssue :: (DB es, Time :> es) => IssueId -> Users.UserId -> Eff es ()
403+
acknowledgeIssue issueId userId = do
404+
now <- Time.currentTime
405+
void $ PG.execute q (now, userId, issueId)
400406
where
401407
q =
402408
[sql|
403409
UPDATE apis.issues
404-
SET acknowledged_at = NOW(), acknowledged_by = ?
410+
SET acknowledged_at = ?, acknowledged_by = ?
405411
WHERE id = ?
406412
|]
407413

@@ -535,14 +541,15 @@ data AIChatMessage = AIChatMessage
535541

536542

537543
-- | Get or create a conversation (race-condition safe via ON CONFLICT + RETURNING)
538-
getOrCreateConversation :: (DB es, Error ServerError :> es) => Projects.ProjectId -> UUIDId "conversation" -> ConversationType -> AE.Value -> Eff es AIConversation
544+
getOrCreateConversation :: (DB es, Error ServerError :> es, Time :> es) => Projects.ProjectId -> UUIDId "conversation" -> ConversationType -> AE.Value -> Eff es AIConversation
539545
getOrCreateConversation pid convId convType ctx = do
540-
result <- PG.query q (pid, convId, convType, Aeson ctx)
546+
now <- Time.currentTime
547+
result <- PG.query q (pid, convId, convType, Aeson ctx, now)
541548
maybe (throwError err500{errBody = "getOrCreateConversation: RETURNING clause must return a row"}) pure $ listToMaybe result
542549
where
543550
q =
544551
[sql| INSERT INTO apis.ai_conversations (project_id, conversation_id, conversation_type, context)
545-
VALUES (?, ?, ?, ?) ON CONFLICT (project_id, conversation_id) DO UPDATE SET updated_at = NOW()
552+
VALUES (?, ?, ?, ?) ON CONFLICT (project_id, conversation_id) DO UPDATE SET updated_at = ?
546553
RETURNING id, project_id, conversation_id, conversation_type, context, created_at, updated_at |]
547554

548555

src/Models/Apis/LogPatterns.hs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,15 @@ import Data.Vector qualified as V
4343
import Database.PostgreSQL.Entity (_select, _selectWhere)
4444
import Database.PostgreSQL.Entity.Types (CamelToSnake, Entity, FieldModifiers, GenericEntity, PrimaryKey, Schema, TableName)
4545
import Database.PostgreSQL.Entity.Types qualified as DAT
46-
import Database.PostgreSQL.Simple (FromRow, Only (Only, fromOnly), ToRow)
46+
import Database.PostgreSQL.Simple (FromRow, Only (Only, fromOnly), ToRow, (:.)(..))
4747
import Database.PostgreSQL.Simple.FromField (FromField)
4848
import Database.PostgreSQL.Simple.SqlQQ (sql)
4949
import Database.PostgreSQL.Simple.ToField (ToField)
5050
import Deriving.Aeson qualified as DAE
51-
import Effectful (Eff)
51+
import Effectful (Eff, type (:>))
5252
import Effectful.PostgreSQL qualified as PG
53+
import Effectful.Time (Time)
54+
import Effectful.Time qualified as Time
5355
import Models.Projects.Projects qualified as Projects
5456
import Models.Users.Sessions qualified as Users
5557
import Pkg.DeriveUtils (WrappedEnumSC (..))
@@ -155,31 +157,33 @@ getNewLogPatterns pid limit = PG.query (_selectWhere @LogPattern [[DAT.field| pr
155157
-- | Acknowledge log patterns. Pass Nothing for system-triggered acknowledgments.
156158
-- Matches on (project_id, source_field, pattern_hash) — the unique key — to avoid
157159
-- cross-field collisions (e.g. url_path and exception sharing the same normalized hash).
158-
acknowledgeLogPatterns :: DB es => Projects.ProjectId -> Maybe Users.UserId -> V.Vector (Text, Text) -> Eff es Int64
160+
acknowledgeLogPatterns :: (DB es, Time :> es) => Projects.ProjectId -> Maybe Users.UserId -> V.Vector (Text, Text) -> Eff es Int64
159161
acknowledgeLogPatterns pid uid fieldHashPairs
160162
| V.null fieldHashPairs = pure 0
161-
| otherwise =
163+
| otherwise = do
164+
now <- Time.currentTime
162165
let (fields, hashes) = V.unzip fieldHashPairs
163-
in PG.execute q (LPSAcknowledged, uid, pid, fields, hashes)
166+
in PG.execute q (LPSAcknowledged, uid, now, pid, fields, hashes)
164167
where
165168
q =
166169
[sql|
167170
UPDATE apis.log_patterns
168-
SET state = ?, acknowledged_by = ?, acknowledged_at = NOW()
171+
SET state = ?, acknowledged_by = ?, acknowledged_at = ?
169172
WHERE project_id = ? AND (source_field, pattern_hash) IN (SELECT unnest(?::text[]), unnest(?::text[]))
170173
|]
171174

172175

173-
upsertLogPattern :: DB es => UpsertPattern -> Eff es Int64
176+
upsertLogPattern :: (DB es, Time :> es) => UpsertPattern -> Eff es Int64
174177
upsertLogPattern = upsertLogPatternBatch . pure
175178

176179

177180
-- | Bulk update baselines for multiple patterns in a single query.
178181
-- Matches on (project_id, source_field, pattern_hash) since pattern_hash alone is not unique across source fields.
179-
updateBaselineBatch :: DB es => Projects.ProjectId -> V.Vector (Text, Text, BaselineState, Double, Double, Int) -> Eff es Int64
182+
updateBaselineBatch :: (DB es, Time :> es) => Projects.ProjectId -> V.Vector (Text, Text, BaselineState, Double, Double, Int) -> Eff es Int64
180183
updateBaselineBatch pid rows
181184
| V.null rows = pure 0
182-
| otherwise =
185+
| otherwise = do
186+
now <- Time.currentTime
183187
let srcFields = V.map (view _1) rows
184188
hashes = V.map (view _2) rows
185189
states = V.map (view _3) rows
@@ -193,29 +197,30 @@ updateBaselineBatch pid rows
193197
baseline_volume_hourly_mean = v.mean,
194198
baseline_volume_hourly_mad = v.mad,
195199
baseline_samples = v.samples,
196-
baseline_updated_at = NOW()
200+
baseline_updated_at = ?
197201
FROM (SELECT unnest(?::text[]) AS source_field, unnest(?::text[]) AS hash, unnest(?::text[]) AS state, unnest(?::float8[]) AS mean, unnest(?::float8[]) AS mad, unnest(?::int[]) AS samples) v
198202
WHERE lp.project_id = ? AND lp.source_field = v.source_field AND lp.pattern_hash = v.hash
199203
|]
200-
(srcFields, hashes, states, means, mads, samples, pid)
204+
(now, srcFields, hashes, states, means, mads, samples, pid)
201205

202206

203207
-- | Batch version of upsertLogPattern using executeMany.
204-
upsertLogPatternBatch :: DB es => [UpsertPattern] -> Eff es Int64
208+
upsertLogPatternBatch :: (DB es, Time :> es) => [UpsertPattern] -> Eff es Int64
205209
upsertLogPatternBatch [] = pure 0
206-
upsertLogPatternBatch ups =
210+
upsertLogPatternBatch ups = do
211+
now <- Time.currentTime
207212
PG.executeMany
208213
[sql| INSERT INTO apis.log_patterns (project_id, log_pattern, pattern_hash, source_field, service_name, log_level, trace_id, sample_message, occurrence_count)
209214
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
210215
ON CONFLICT (project_id, source_field, pattern_hash) DO UPDATE SET
211-
last_seen_at = NOW(),
216+
last_seen_at = ?,
212217
occurrence_count = apis.log_patterns.occurrence_count + EXCLUDED.occurrence_count,
213218
sample_message = COALESCE(EXCLUDED.sample_message, apis.log_patterns.sample_message),
214219
service_name = COALESCE(EXCLUDED.service_name, apis.log_patterns.service_name),
215220
log_level = COALESCE(EXCLUDED.log_level, apis.log_patterns.log_level),
216221
trace_id = COALESCE(EXCLUDED.trace_id, apis.log_patterns.trace_id)
217222
|]
218-
ups
223+
(map (\up -> (up :. Only now)) ups)
219224

220225

221226
-- | Upsert hourly event count for a pattern into the pre-aggregated stats table.
@@ -376,4 +381,4 @@ pruneOldHourlyStats pid now hoursBack = PG.execute [sql| DELETE FROM apis.log_pa
376381
-- | Auto-acknowledge patterns stuck in 'new' state longer than staleDays.
377382
-- Prevents unbounded accumulation for low-volume projects that never trigger processNewLogPatterns.
378383
autoAcknowledgeStaleNewPatterns :: DB es => Projects.ProjectId -> UTCTime -> Int -> Eff es Int64
379-
autoAcknowledgeStaleNewPatterns pid now staleDays = PG.execute [sql| UPDATE apis.log_patterns SET state = ?, acknowledged_at = NOW() WHERE project_id = ? AND state = ? AND created_at < ?::timestamptz - INTERVAL '1 day' * ? |] (LPSAcknowledged, pid, LPSNew, now, staleDays)
384+
autoAcknowledgeStaleNewPatterns pid now staleDays = PG.execute [sql| UPDATE apis.log_patterns SET state = ?, acknowledged_at = ? WHERE project_id = ? AND state = ? AND created_at < ?::timestamptz - INTERVAL '1 day' * ? |] (LPSAcknowledged, now, pid, LPSNew, now, staleDays)

src/Models/Apis/Monitors.hs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ import Database.PostgreSQL.Simple.Newtypes (Aeson (..))
4646
import Database.PostgreSQL.Simple.SqlQQ (sql)
4747
import Database.PostgreSQL.Simple.ToField (ToField (..))
4848
import Deriving.Aeson qualified as DAE
49-
import Effectful (Eff)
49+
import Effectful (Eff, type (:>))
5050
import Effectful.PostgreSQL qualified as PG
51+
import Effectful.Time (Time)
52+
import Effectful.Time qualified as Time
5153
import GHC.Records (HasField (getField))
5254
import Models.Projects.Projects qualified as Projects
5355
import Pkg.DeriveUtils (WrappedEnumSC (..))
@@ -227,23 +229,27 @@ queryMonitorsById ids
227229
|]
228230

229231

230-
updateQMonitorTriggeredState :: DB es => QueryMonitorId -> Bool -> Eff es Int64
231-
updateQMonitorTriggeredState qmId isAlert = PG.execute q (Only qmId)
232+
updateQMonitorTriggeredState :: (DB es, Time :> es) => QueryMonitorId -> Bool -> Eff es Int64
233+
updateQMonitorTriggeredState qmId isAlert = do
234+
now <- Time.currentTime
235+
PG.execute q (now, qmId)
232236
where
233237
q =
234238
if isAlert
235-
then [sql|UPDATE monitors.query_monitors SET alert_last_triggered=NOW() where id=?|]
236-
else [sql|UPDATE monitors.query_monitors SET warning_last_triggered=NOW() where id=?|]
239+
then [sql|UPDATE monitors.query_monitors SET alert_last_triggered=? where id=?|]
240+
else [sql|UPDATE monitors.query_monitors SET warning_last_triggered=? where id=?|]
237241

238242

239-
monitorToggleActiveById :: DB es => QueryMonitorId -> Eff es Int64
240-
monitorToggleActiveById id' = PG.execute q (Only id')
243+
monitorToggleActiveById :: (DB es, Time :> es) => QueryMonitorId -> Eff es Int64
244+
monitorToggleActiveById id' = do
245+
now <- Time.currentTime
246+
PG.execute q (now, id')
241247
where
242248
q =
243-
[sql|
249+
[sql|
244250
UPDATE monitors.query_monitors SET deactivated_at=CASE
245251
WHEN deactivated_at IS NOT NULL THEN NULL
246-
ELSE NOW()
252+
ELSE ?
247253
END
248254
where id=?|]
249255

0 commit comments

Comments
 (0)