Skip to content

Commit 030b103

Browse files
hasql-interpolate + record-dot migration: derived ENUM/CITEXT decoders (#409)
* wip: hasql-interpolate + record-dot migration in progress * telemetry: derive FromRow/DecodeRow for OtelLogsAndSpans Reorder OtelLogsAndSpans fields to match otelSpanColsSql so both row instances drop to one-liner anyclass derives. Add the previously-absent errors and message_size_bytes columns to the SELECT path (and errors to the INSERT path) so they round-trip instead of being silently defaulted to Nothing / 0 on read. errors switches to Maybe (AesonText Value) for consistency with body/events. Consolidates spanRecordByName onto the shared otelSpanColsSql snippet so it can't drift again. Net: ~110 lines of hand-written instance plumbing gone. * hasql: cast int4 columns to bigint in two SELECTs QueryCache.hs (hit_count) and Issues.hs (affected_requests, affected_clients) decode into Haskell Int (= int8) but the underlying columns are INTEGER (int4), so every dashboard fetch and every cache lookup was throwing UnexpectedColumnTypeStatementError under hasql. Cast on the SELECT rather than narrowing the record field — safer because the same field types are used as Int across the codebase. * fix decode-type mismatches surfaced by hasql-interpolate strictness apis.issues.seq_num is INT; cast to bigint in the issue-list SELECT so the generic DecodeRow doesn't blow up on int4-vs-int8. projects.query_library.query_type is an enum; cast to text in the three UNION branches of queryLibHistoryForUser so the generic decoder reads it as Text instead of a custom OID. Add migration 0096 to convert the four remaining VARCHAR(n) columns (users.first_name/last_name, projects.teams.name/handle) to TEXT. VARCHAR's only practical difference is the catalog OID, and that OID mismatch was crashing every team-list and member-list lookup under the new hasql decoders. * drop vendored stackage-pins.config; pin via cabal.project.freeze cabal.project imported a 3300-line in-repo stackage snapshot just to strip hs-opentelemetry-* pins so the source-repository-package override at 1.0.0.0 could win. cabal.project.freeze already covers all pinned versions including source-repo packages, so the vendored snapshot is redundant. Also fixes CI: the Dockerfile COPY glob did not include stackage-pins.config, so docker builds were failing with 'stackage-pins.config: does not exist'. * nav: keep API Catalog highlighted on /endpoints pages The sidenav active-class hyperscript matched only the link href prefix, so navigating to /p/<pid>/endpoints (rendered by the API Catalog handler) lost the highlight after htmx pushState. Add a data-match attribute carrying extra path prefixes; the API Catalog item now declares /endpoints as a secondary match. * Auto-format code with fourmolu * untrack WIP incident report and migration plan drafts These were accidentally committed in 3e6f8a2; they are local work-in-progress notes, not source. Keep working copies on disk. * endpoints: rewrite SQL builders with hasql-interpolate ^{} splices Replace string-concat + rawSql glue with single multiline [HI.sql|...|] quasiquotes using ^{sql} splices for fragments and #{val} for values. Drop NeatInterpolation usage. Collapse countEndpointInbox default to correct enp alias. * DeriveUtils: use maybeToRight in refineText (hlint warning) * hasql: derive ENUM/CITEXT decoders, widen INT/NUMERIC columns WrappedEnumSC now takes a leading `Maybe Symbol` pinning the backing PG type. `'Nothing` keeps the old text-backed behaviour; `('Just "schema.name")` swaps in `D.enum`/`E.enum` so hasql-interpolate's strict OID lookup resolves real `CREATE TYPE … AS ENUM` columns through generic DecodeRow without per-query `::text` casts. `HI.DecodeValue (CI Text)` switches to `D.citext` to cover the citext extension type. Updated qualified sites: Permissions, IssueType, AnomalyTypes, AnomalyActions, QueryLibType, KeyKind. Everything else gets `'Nothing`. Migrations: - 0097 drops the `email` domain (now plain citext + format CHECK) and widens `apis.log_patterns.baseline_samples` / `projects.replay_sessions.event_file_count` to BIGINT. - 0098 batches the rest of the int4 → int8 widening (issues affected_*, llm_enhancement_version, seq_num; error_patterns occurrences/etc.; query_monitors check_interval_mins/threshold/notification_count/…; notification_rate_limit.count) plus `apis.issues.error_rate` → float8. Code fixes surfaced by the strict decoders: - `Issue` gains `cooldownUntil` / `lastNotifiedAt` (rows existed since 0075/0085) and the IssueL list SQL selects them. - BackgroundJobs SafetyNet uses `Telemetry.otelSpanColsSql` (was missing `errors` + `message_size_bytes`). - consumeNotificationToken returns `count` directly (no int4 cast). - make_interval call casts hours to `int` (only signature PG provides). - Anomalies VM format_examples now `TEXT[]` (matches Haskell Vector Text). - LogQueries bis/cnts and totalSessions widened to Int64. - Parser alert select emits `count(*)::float8` for Double consumers. - Projects.downgradeToFree / upgradeToPaid cast bigint params to text for the TEXT `order_id` comparison. - isInCooldown SELECTs `1::bigint` so the Int64 decoder matches. * Auto-format code with fourmolu * 0098: combine per-table ALTERs to one rewrite each PG rewrites the heap+TOAST+indexes once per ALTER TABLE statement regardless of how many ALTER COLUMN clauses it contains, so collapsing 23 statements into 4 (one per table) cuts the migration's I/O by ~6x. Notable on apis.error_patterns (8.9 GB TOAST) which goes from 9 rewrites to 1; reduces lock-hold window during prod deploy. Also drop the Int64->Int round-trip in fetchLogPatterns: hasql's DecodeValue Int instance already reads int8, so decoding directly into Int (and dropping the fromIntegral) matches buildHourlyBuckets's signature without the cast. * tests: drop unused imports across integration specs * fix(llm): parse criticality response directly instead of via getNormalTupleReponse getNormalTupleReponse returns only the first line as text and the second line as Maybe — so 'lines response' always yielded one element and every classifyIssueCriticality call short-circuited to 'Invalid response format from LLM'. Parse the raw LLM response with a prefix pattern and strip each line. * refactor: inline directionClauseSql and uncurry severityBadge - archiveHosts/unarchiveHosts: drop let-bound dir, inline as ^{} splice - normalLogElements: replace lambda-on-tuple with uncurry * issues: drop unused HI.EncodeRow/ToRow from Issue and Report Both types are written via named-column INSERTs (insertIssue, the reports insert); the positional encoders were never exercised. Dropping them removes a latent foot-gun now that Issue carries cooldown_until and last_notified_at columns whose positions don't match the named insertIssue column list. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 7a4b94c commit 030b103

68 files changed

Lines changed: 918 additions & 4652 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cabal.project

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@ semaphore: True
77
-- import: https://www.stackage.org/nightly-2025-01-20/cabal.config
88
-- documentation: True
99

10-
-- with-compiler: ghc-9.8.2
11-
-- Local copy of https://www.stackage.org/nightly-2025-11-17/cabal.config with
12-
-- hs-opentelemetry-* pins stripped so we can pull 1.0.0.0 from the monorepo.
13-
import: stackage-pins.config
14-
--import: https://www.stackage.org/lts-24.19/cabal.config
10+
-- Dependency versions are pinned via cabal.project.freeze. Refresh with `cabal freeze`.
1511

1612

1713
-- Override dependencies from stack.yaml
@@ -120,9 +116,16 @@ source-repository-package
120116
source-repository-package
121117
type: git
122118
location: https://github.com/tonyalaribe/hs-opentelemetry-instrumentation-hasql
123-
tag: 037ef4331f8861059c234e6057cafbf8ef8ced83
119+
tag: bb90850a0eeed02a0b03244283a1236b5ce03471
124120
subdir: .
125121

122+
-- Upstream merge of haskell-src-meta -> ghc-hs-meta (PR #32) not yet on
123+
-- Hackage; pin to the merge commit until next release.
124+
source-repository-package
125+
type: git
126+
location: https://github.com/awkward-squad/hasql-interpolate
127+
tag: 8a13a8b06babc569ffd216fd9561aae58818b66f
128+
126129
constraints:
127130
streamly ^>=0.10.0,
128131
effectful-postgresql +enable-otel,

cabal.project.freeze

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,6 @@ constraints: any.Cabal ==3.14.1.0,
211211
any.haskell-lexer ==1.2.1,
212212
any.haskell-src-exts ==1.23.1,
213213
any.haskell-src-meta ==0.8.15,
214-
any.hasql ==1.9.3.1,
215-
any.hasql-dynamic-statements ==0.3.1.8,
216-
any.hasql-implicits ==0.2.0.1,
217-
any.hasql-interpolate ==1.0.1.0,
218-
any.hasql-pool ==1.3.0.4,
219-
any.hasql-th ==0.4.0.23,
220-
any.hasql-transaction ==1.2.1,
221214
any.hdaemonize ==0.5.7,
222215
any.headed-megaparsec ==0.2.1.3,
223216
any.hostname ==1.0,
@@ -353,7 +346,6 @@ constraints: any.Cabal ==3.14.1.0,
353346
any.pg-transact ==0.3.2.0,
354347
any.port-utils ==0.2.1.0,
355348
any.postgres-options ==0.2.2.0,
356-
any.postgresql-binary ==0.14.2,
357349
any.postgresql-libpq ==0.11.0.0,
358350
postgresql-libpq -use-pkg-config,
359351
any.postgresql-libpq-configure ==0.11,

monoscope.cabal

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ extra-source-files:
121121
static/migrations/0093_log_patterns_is_error.sql
122122
static/migrations/0094_app_now_function.sql
123123
static/migrations/0095_app_now_in_log_auto_resolve.sql
124+
static/migrations/0096_varchar_to_text.sql
125+
static/migrations/0097_hasql_decoder_alignment.sql
126+
static/migrations/0098_widen_int_to_bigint.sql
124127

125128
source-repository head
126129
type: git
@@ -577,7 +580,7 @@ test-suite doctests
577580
TypeFamilies
578581
UndecidableInstances
579582
ViewPatterns
580-
ghc-options: -fwrite-ide-info -threaded -Weverything -Werror -fno-defer-typed-holes -Wno-error=deprecations -Wno-error=unused-packages -Wno-error=implicit-lift -Wno-error=missing-poly-kind-signatures -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-safe-haskell-mode -Wno-partial-fields -Wno-prepositive-qualified-module -Wno-missing-export-lists -Wno-orphans -Wno-missing-deriving-strategies -Wno-missing-role-annotations -Wno-unused-matches -Wno-missing-kind-signatures -Wno-type-defaults -Wno-unused-top-binds -Wno-unused-imports -Wno-error=incomplete-record-selectors -Wno-ambiguous-fields -threaded -rtsopts -with-rtsopts=-N -O0
583+
ghc-options: -fwrite-ide-info -threaded -Weverything -Werror -fno-defer-typed-holes -Wno-error=deprecations -Wno-error=unused-packages -Wno-error=implicit-lift -Wno-error=missing-poly-kind-signatures -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-safe-haskell-mode -Wno-partial-fields -Wno-prepositive-qualified-module -Wno-missing-export-lists -Wno-orphans -Wno-missing-deriving-strategies -Wno-missing-role-annotations -Wno-unused-matches -Wno-missing-kind-signatures -Wno-type-defaults -Wno-unused-top-binds -Wno-unused-imports -Wno-error=incomplete-record-selectors -Wno-ambiguous-fields -fno-write-ide-info -threaded -rtsopts "-with-rtsopts=-N -A64m" -O0
581584
build-tool-depends:
582585
proto-lens-protoc:proto-lens-protoc
583586
build-depends:
@@ -663,7 +666,7 @@ test-suite integration-tests
663666
TypeFamilies
664667
UndecidableInstances
665668
ViewPatterns
666-
ghc-options: -fwrite-ide-info -threaded -Weverything -Werror -fno-defer-typed-holes -Wno-error=deprecations -Wno-error=unused-packages -Wno-error=implicit-lift -Wno-error=missing-poly-kind-signatures -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-safe-haskell-mode -Wno-partial-fields -Wno-prepositive-qualified-module -Wno-missing-export-lists -Wno-orphans -Wno-missing-deriving-strategies -Wno-missing-role-annotations -Wno-unused-matches -Wno-missing-kind-signatures -Wno-type-defaults -Wno-unused-top-binds -Wno-unused-imports -Wno-error=incomplete-record-selectors -Wno-ambiguous-fields -threaded -rtsopts -with-rtsopts=-N -O0
669+
ghc-options: -fwrite-ide-info -threaded -Weverything -Werror -fno-defer-typed-holes -Wno-error=deprecations -Wno-error=unused-packages -Wno-error=implicit-lift -Wno-error=missing-poly-kind-signatures -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-safe-haskell-mode -Wno-partial-fields -Wno-prepositive-qualified-module -Wno-missing-export-lists -Wno-orphans -Wno-missing-deriving-strategies -Wno-missing-role-annotations -Wno-unused-matches -Wno-missing-kind-signatures -Wno-type-defaults -Wno-unused-top-binds -Wno-unused-imports -Wno-error=incomplete-record-selectors -Wno-ambiguous-fields -fno-write-ide-info -threaded -rtsopts "-with-rtsopts=-N -A64m" -O0
667670
build-tool-depends:
668671
hspec-discover:hspec-discover
669672
, proto-lens-protoc:proto-lens-protoc
@@ -761,7 +764,7 @@ test-suite unit-tests
761764
TypeFamilies
762765
UndecidableInstances
763766
ViewPatterns
764-
ghc-options: -fwrite-ide-info -threaded -Weverything -Werror -fno-defer-typed-holes -Wno-error=deprecations -Wno-error=unused-packages -Wno-error=implicit-lift -Wno-error=missing-poly-kind-signatures -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-safe-haskell-mode -Wno-partial-fields -Wno-prepositive-qualified-module -Wno-missing-export-lists -Wno-orphans -Wno-missing-deriving-strategies -Wno-missing-role-annotations -Wno-unused-matches -Wno-missing-kind-signatures -Wno-type-defaults -Wno-unused-top-binds -Wno-unused-imports -Wno-error=incomplete-record-selectors -Wno-ambiguous-fields -threaded -rtsopts -with-rtsopts=-N -O0
767+
ghc-options: -fwrite-ide-info -threaded -Weverything -Werror -fno-defer-typed-holes -Wno-error=deprecations -Wno-error=unused-packages -Wno-error=implicit-lift -Wno-error=missing-poly-kind-signatures -Wno-missing-exported-signatures -Wno-missing-import-lists -Wno-missed-specialisations -Wno-all-missed-specialisations -Wno-unsafe -Wno-safe -Wno-missing-local-signatures -Wno-monomorphism-restriction -Wno-missing-safe-haskell-mode -Wno-partial-fields -Wno-prepositive-qualified-module -Wno-missing-export-lists -Wno-orphans -Wno-missing-deriving-strategies -Wno-missing-role-annotations -Wno-unused-matches -Wno-missing-kind-signatures -Wno-type-defaults -Wno-unused-top-binds -Wno-unused-imports -Wno-error=incomplete-record-selectors -Wno-ambiguous-fields -fno-write-ide-info -threaded -rtsopts "-with-rtsopts=-N -A64m" -O0
765768
build-tool-depends:
766769
proto-lens-protoc:proto-lens-protoc
767770
build-depends:

package.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,10 @@ tests:
362362
main: Main.hs
363363
source-dirs: test/doctests
364364
ghc-options:
365+
- -fno-write-ide-info
365366
- -threaded
366367
- -rtsopts
367-
- -with-rtsopts=-N
368+
- '"-with-rtsopts=-N -A64m"'
368369
- -O0
369370
dependencies:
370371
- base
@@ -376,9 +377,10 @@ tests:
376377
main: Main.hs
377378
source-dirs: test/integration
378379
ghc-options:
380+
- -fno-write-ide-info
379381
- -threaded
380382
- -rtsopts
381-
- -with-rtsopts=-N
383+
- '"-with-rtsopts=-N -A64m"'
382384
- -O0
383385
build-tools:
384386
- hspec-discover:hspec-discover
@@ -441,9 +443,10 @@ tests:
441443
main: Main.hs
442444
source-dirs: test/unit
443445
ghc-options:
446+
- -fno-write-ide-info
444447
- -threaded
445448
- -rtsopts
446-
- -with-rtsopts=-N
449+
- '"-with-rtsopts=-N -A64m"'
447450
- -O0
448451
dependencies:
449452
- monoscope

0 commit comments

Comments
 (0)