Compare commits

..
20 Commits
Author SHA1 Message Date
renovate ffaf6a7f27 fix(deps): update opentelemetry (#198)
Unbound Release / Check Preconditions (push) Successful in 20s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 40s
Release / release (push) Successful in 17m53s
Unbound Release / Create Release (push) Successful in 24s
2026-10-05 18:16:37 +00:00
renovate a16f09de4a fix(deps): update module codeberg.org/eventsourced/pg/v2 to v2.3.0 (#200)
Unbound Release / Check Preconditions (push) Successful in 29s
Unbound Release / Create Tag (push) Skipped
Release / release (push) Successful in 4m17s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 44s
Unbound Release / Create Release (push) Successful in 33s
2026-10-05 17:24:43 +00:00
argoyle 2ef8ccb6ee chore(renovate): group OpenTelemetry, otelsetup and logging updates (#196)
Unbound Release / Check Preconditions (push) Successful in 27s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Create Release (push) Successful in 25s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 59s
Release / release (push) Successful in 5m11s
2026-10-05 10:41:20 +00:00
renovate 26d4199330 chore(deps): update pre-commit hook golangci/golangci-lint to v2.14.0 (#192)
Unbound Release / Check Preconditions (push) Successful in 26s
Unbound Release / Create Tag (push) Skipped
Unbound Release / Create Release (push) Successful in 27s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 1m2s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Release / release (push) Successful in 10m12s
2026-09-27 13:23:14 +00:00
renovate b32c8c88d2 chore(deps): update pre-commit hook alessandrojcm/commitlint-pre-commit-hook to v9.27.0 (#190)
Unbound Release / Check Preconditions (push) Successful in 36s
Unbound Release / Create Tag (push) Skipped
Release / release (push) Successful in 8m3s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Create Release (push) Successful in 29s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 42s
2026-09-23 19:28:50 +00:00
renovate 6a3256c9b6 fix(deps): update module codeberg.org/eventsourced/pg/v2 to v2.2.1 (#188)
Unbound Release / Check Preconditions (push) Successful in 22s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 45s
Unbound Release / Create Release (push) Successful in 28s
Release / release (push) Successful in 1m24s
pre-commit / pre-commit (push) Successful in 2m36s
2026-09-21 07:19:07 +00:00
renovate 8a5c8dcdb7 fix(deps): update module codeberg.org/eventsourced/pg/v2 to v2.2.0 (#186)
Unbound Release / Check Preconditions (push) Successful in 27s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Create Release (push) Successful in 28s
Release / release (push) Successful in 1m14s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 40s
pre-commit / pre-commit (push) Successful in 3m21s
2026-09-20 17:19:12 +00:00
releaser 92db06698c chore(release): prepare for v0.6.1 (#185)
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Check Preconditions (push) Successful in 25s
Unbound Release / Create Tag (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 26s
Unbound Release / Create Release (push) Successful in 28s
Release / release (push) Successful in 1m13s
pre-commit / pre-commit (push) Successful in 3m24s
## [0.6.1] - 2026-09-19

### 🐛 Bug Fixes

- Close idle OTLP connections before the collector does (#184)

<!-- generated by git-cliff -->

---

**Note:** Please use **Squash Merge** when merging this PR.

Reviewed-on: https://gitea.unbound.se/shiny/otelsetup/pulls/185
Co-authored-by: Unbound Releaser <releaser@unbound.se>
2026-09-19 13:58:49 +00:00
argoyle 8b3d3110fe fix: close idle OTLP connections before the collector does (#184)
Unbound Release / Check Preconditions (push) Successful in 27s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 39s
Unbound Release / Create Release (push) Successful in 29s
pre-commit / pre-commit (push) Successful in 3m27s
Release / release (push) Successful in 1m14s
2026-09-19 13:51:36 +00:00
releaser 80d749ab92 chore(release): prepare for v0.6.0 (#183)
Unbound Release / Check Preconditions (push) Successful in 22s
Unbound Release / Create Tag (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 24s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Create Release (push) Successful in 24s
pre-commit / pre-commit (push) Successful in 2m53s
Release / release (push) Successful in 1m10s
## [0.6.0] - 2026-09-17

### 🚀 Features

- *(metrics)* Record eventsourced pg outbox publishes and retries (#182)

<!-- generated by git-cliff -->

---

**Note:** Please use **Squash Merge** when merging this PR.

Reviewed-on: https://gitea.unbound.se/shiny/otelsetup/pulls/183
Co-authored-by: Unbound Releaser <releaser@unbound.se>
2026-09-17 12:01:33 +00:00
argoyle 88dde56687 feat(metrics): record eventsourced pg outbox publishes and retries (#182)
Unbound Release / Check Preconditions (push) Successful in 22s
Unbound Release / Create Tag (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 37s
pre-commit / pre-commit (push) Successful in 2m45s
Unbound Release / Create Release (push) Successful in 23s
otelsetup / test (push) Successful in 58s
otelsetup / vulnerabilities (push) Successful in 53s
Release / release (push) Successful in 1m2s
## Why
authz-service now publishes through pg/v2's transactional outbox, and nothing measured it: `NewEventsourcedMetrics` ignored the `pg.Outbox*` metrics. Without them a failing or abandoned publish (a lost privilege revocation for running authz_clients) can't be alerted on.

## What
- `pg.OutboxPublish` → histogram `eventsourced.outbox.publish.duration` (`event.type`, `success`)
- `pg.OutboxRetry` → counter `eventsourced.outbox.retries` (`event.type`, `permanent`)
- `pg.OutboxBatch` / `pg.OutboxCleanup` stay ignored.

otelsetup now imports `codeberg.org/eventsourced/pg/v2` v2.1.1 (`go` directive becomes 1.26.0; grpc patch bump via MVS). All current consumers already depend on pg/v2; a release raises their minimum to v2.1.1.

## Tests
`go test -race ./...` green; the contract test records both new metrics.

## Review
Go Backend expert reviewed: no Critical/High. Noted: the test checks instrument names only, not attributes; the pg/v2 dependency is worth a line in the release notes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_013DJouG8ZZZxKtodF9kDvzj
Reviewed-on: https://gitea.unbound.se/shiny/otelsetup/pulls/182
2026-09-17 11:54:05 +00:00
releaser e9a2dcf720 chore(release): prepare for v0.5.0 (#181)
Unbound Release / Check Preconditions (push) Successful in 26s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 29s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Create Release (push) Successful in 29s
Unbound Release / Create Tag (push) Skipped
Release / release (push) Successful in 1m53s
pre-commit / pre-commit (push) Successful in 3m22s
## [0.5.0] - 2026-09-11

### 🚀 Features

- [**breaking**] Use eventsourced from Codeberg (#178)

### 🐛 Bug Fixes

- *(deps)* Update module github.com/99designs/gqlgen to v0.17.95 (#176)

### ⚙️ Miscellaneous Tasks

- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.2 (#174)
- Bump the minor version for breaking changes before 1.0.0 (#180)

<!-- generated by git-cliff -->

---

**Note:** Please use **Squash Merge** when merging this PR.

Reviewed-on: https://gitea.unbound.se/shiny/otelsetup/pulls/181
Co-authored-by: Unbound Releaser <releaser@unbound.se>
2026-09-14 06:49:56 +00:00
argoyle e824f8b5b9 chore: bump the minor version for breaking changes before 1.0.0 (#180)
Unbound Release / Check Preconditions (push) Successful in 24s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 45s
Unbound Release / Create Release (push) Successful in 27s
Release / release (push) Successful in 1m13s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Successful in 1m16s
otelsetup / vulnerabilities (push) Successful in 1m2s
pre-commit / pre-commit (push) Successful in 2m43s
2026-09-11 21:04:54 +00:00
argoyle d8ed1a9bf8 feat!: use eventsourced from Codeberg (#178)
Unbound Release / Generate Changelog and Handle PR (push) Successful in 45s
Unbound Release / Check Preconditions (push) Successful in 21s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Create Release (push) Successful in 24s
Release / release (push) Successful in 1m9s
pre-commit / pre-commit (push) Successful in 2m50s
2026-09-11 20:52:42 +00:00
renovate d1e99a1f1f fix(deps): update module github.com/99designs/gqlgen to v0.17.95 (#176)
Unbound Release / Create Tag (push) Skipped
Unbound Release / Check Preconditions (push) Successful in 31s
Unbound Release / Create Release (push) Successful in 23s
Release / release (push) Successful in 1m59s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 35s
2026-09-04 21:13:00 +00:00
renovate 52ba172550 chore(deps): update pre-commit hook golangci/golangci-lint to v2.13.2 (#174)
Unbound Release / Create Tag (push) Skipped
Unbound Release / Check Preconditions (push) Successful in 21s
Unbound Release / Create Release (push) Successful in 25s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 38s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Release / release (push) Successful in 7m51s
2026-08-31 00:25:33 +00:00
releaser 5b128fb3e2 chore(release): prepare for v0.4.2 (#173)
Unbound Release / Check Preconditions (push) Successful in 27s
Unbound Release / Create Tag (push) Skipped
Release / release (push) Successful in 3m24s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 31s
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
pre-commit / pre-commit (push) Skipped
Unbound Release / Create Release (push) Successful in 30s
## [0.4.2] - 2026-08-28

### 🐛 Bug Fixes

- *(deps)* Update opentelemetry-go monorepo (#172)

### ⚙️ Miscellaneous Tasks

- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.0 (#168)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.1 (#170)

<!-- generated by git-cliff -->

---

**Note:** Please use **Squash Merge** when merging this PR.

Reviewed-on: https://gitea.unbound.se/shiny/otelsetup/pulls/173
Co-authored-by: Unbound Releaser <releaser@unbound.se>
2026-08-29 11:25:15 +00:00
renovate 9b04b1b897 fix(deps): update opentelemetry-go monorepo (#172)
otelsetup / vulnerabilities (push) Successful in 59s
pre-commit / pre-commit (push) Successful in 3m16s
Unbound Release / Check Preconditions (push) Successful in 33s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Successful in 1m9s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 43s
Unbound Release / Create Release (push) Successful in 42s
Release / release (push) Successful in 1m32s
2026-08-28 17:14:50 +00:00
renovate d0b6302893 chore(deps): update pre-commit hook golangci/golangci-lint to v2.13.1 (#170)
Unbound Release / Check Preconditions (push) Successful in 26s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Create Release (push) Successful in 28s
Unbound Release / Generate Changelog and Handle PR (push) Successful in 54s
Release / release (push) Successful in 1m25s
pre-commit / pre-commit (push) Successful in 3m12s
2026-08-23 15:15:48 +00:00
renovate fdb17a58c8 chore(deps): update pre-commit hook golangci/golangci-lint to v2.13.0 (#168)
Unbound Release / Check Preconditions (push) Successful in 21s
Unbound Release / Create Tag (push) Skipped
otelsetup / test (push) Skipped
otelsetup / vulnerabilities (push) Skipped
Unbound Release / Generate Changelog and Handle PR (push) Successful in 46s
Release / release (push) Successful in 1m12s
Unbound Release / Create Release (push) Successful in 24s
pre-commit / pre-commit (push) Successful in 2m31s
2026-08-22 23:13:59 +00:00
12 changed files with 264 additions and 92 deletions

No files matched your search

+2 -2
View File
@@ -11,7 +11,7 @@ repos:
- --allow-multiple-documents
- id: check-added-large-files
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.26.0
rev: v9.27.0
hooks:
- id: commitlint
stages: [ commit-msg ]
@@ -30,7 +30,7 @@ repos:
- id: go-test
- id: gofumpt
- repo: https://github.com/golangci/golangci-lint
rev: v2.12.2
rev: v2.14.0
hooks:
- id: golangci-lint-full
- repo: https://github.com/gitleaks/gitleaks
+1 -1
View File
@@ -1,3 +1,3 @@
{
"version": "v0.4.1"
"version": "v0.6.1"
}
+39 -1
View File
@@ -2,7 +2,45 @@
All notable changes to this project will be documented in this file.
## [0.4.1] - 2026-08-06
## [0.6.1] - 2026-09-19
### 🐛 Bug Fixes
- Close idle OTLP connections before the collector does (#184)
## [0.6.0] - 2026-09-17
### 🚀 Features
- *(metrics)* Record eventsourced pg outbox publishes and retries (#182)
## [0.5.0] - 2026-09-14
### 🚀 Features
- [**breaking**] Use eventsourced from Codeberg (#178)
### 🐛 Bug Fixes
- *(deps)* Update module github.com/99designs/gqlgen to v0.17.95 (#176)
### ⚙️ Miscellaneous Tasks
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.2 (#174)
- Bump the minor version for breaking changes before 1.0.0 (#180)
## [0.4.2] - 2026-08-29
### 🐛 Bug Fixes
- *(deps)* Update opentelemetry-go monorepo (#172)
### ⚙️ Miscellaneous Tasks
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.0 (#168)
- *(deps)* Update pre-commit hook golangci/golangci-lint to v2.13.1 (#170)
## [0.4.1] - 2026-08-07
### 🐛 Bug Fixes
+1
View File
@@ -42,3 +42,4 @@ http.Handle("/", otelsetup.Handler(myHandler))
- `OTEL_RESOURCE_ATTRIBUTES` - Auto-set if not provided (service.name, service.version, service.environment)
- Standard OTLP environment variables for endpoint configuration
- The trace and metric exporters use our own HTTP client (idle connections closed after 30s, before the collector's 1m idle timeout), so `OTEL_EXPORTER_OTLP_*TIMEOUT` and the `OTEL_EXPORTER_OTLP_*CERTIFICATE` / `*CLIENT_*` TLS variables are ignored
+4
View File
@@ -78,3 +78,7 @@ filter_commits = false
topo_order = false
# sort the commits inside sections by oldest/newest order
sort_commits = "oldest"
[bump]
# Before 1.0.0, a breaking change bumps the minor version instead of the major.
breaking_always_bump_major = false
+31 -24
View File
@@ -1,20 +1,21 @@
module gitea.unbound.se/shiny/otelsetup
go 1.25.0
go 1.26.0
require (
github.com/99designs/gqlgen v0.17.94
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.23.0
go.opentelemetry.io/otel v1.45.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0
go.opentelemetry.io/otel/log v0.21.0
go.opentelemetry.io/otel/metric v1.45.0
go.opentelemetry.io/otel/sdk v1.45.0
go.opentelemetry.io/otel/sdk/log v0.21.0
go.opentelemetry.io/otel/sdk/metric v1.45.0
go.opentelemetry.io/otel/trace v1.45.0
codeberg.org/eventsourced/eventsourced v1.24.0
codeberg.org/eventsourced/pg/v2 v2.3.0
github.com/99designs/gqlgen v0.17.95
go.opentelemetry.io/otel v1.47.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0
go.opentelemetry.io/otel/log v1.47.0
go.opentelemetry.io/otel/metric v1.47.0
go.opentelemetry.io/otel/sdk v1.47.0
go.opentelemetry.io/otel/sdk/log v1.47.0
go.opentelemetry.io/otel/sdk/metric v1.47.0
go.opentelemetry.io/otel/trace v1.47.0
)
require (
@@ -23,18 +24,24 @@ require (
github.com/go-logr/logr v1.4.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect
github.com/jmoiron/sqlx v1.4.0 // indirect
github.com/lib/pq v1.12.3 // indirect
github.com/mfridman/interpolate v0.0.2 // indirect
github.com/pressly/goose/v3 v3.28.0 // indirect
github.com/sethvargo/go-retry v0.4.0 // indirect
github.com/sosodev/duration v1.4.0 // indirect
github.com/vektah/gqlparser/v2 v2.5.36 // indirect
github.com/vektah/gqlparser/v2 v2.5.37 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect
go.opentelemetry.io/proto/otlp v1.11.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/text v0.40.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d // indirect
google.golang.org/grpc v1.83.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.59.0 // indirect
golang.org/x/sync v0.23.0 // indirect
golang.org/x/sys v0.48.0 // indirect
golang.org/x/text v0.42.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc // indirect
google.golang.org/grpc v1.83.2 // indirect
google.golang.org/protobuf v1.36.12 // indirect
)
+97 -58
View File
@@ -1,85 +1,124 @@
github.com/99designs/gqlgen v0.17.94 h1:+3EUDVgX/8gDyDL+7NUqCo4cy2ylylwW0GvR1dGiEsA=
github.com/99designs/gqlgen v0.17.94/go.mod h1:o+XaAMpPA/AX4rqeiK03tZUb/5T+WCgpRDD4aujgdas=
codeberg.org/eventsourced/eventsourced v1.24.0 h1:yNB+74JNB/NHo7D8ZR/yQcTVDYrWnjFf0xUKLgViqRc=
codeberg.org/eventsourced/eventsourced v1.24.0/go.mod h1:uy0HxejDdNFpI+Yp7VOEvjfEUFJQX1VkCZ4e4JyDp7Q=
codeberg.org/eventsourced/pg/v2 v2.3.0 h1:g6kZ9DASeFrC8C+bDcXzbYD8409KHzWmyLOI5sCdNWI=
codeberg.org/eventsourced/pg/v2 v2.3.0/go.mod h1:qPxl3382m7lbhbrEaxwSZ9PrAPoODj2+TqnV3tIKWzU=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo=
filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc=
github.com/99designs/gqlgen v0.17.95 h1:882h7F5iJImgtyUVttc4MOK2NbzbMYc2oyNeHqkjpP4=
github.com/99designs/gqlgen v0.17.95/go.mod h1:kHYPrpwOXDU1OQyxIg3Z7nVXSnlUoHVWBY7CMJCAM4M=
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=
github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-sql-driver/mysql v1.10.0 h1:Q+1LV8DkHJvSYAdR83XzuhDaTykuDx0l6fkXxoWCWfw=
github.com/go-sql-driver/mysql v1.10.0/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/pressly/goose/v3 v3.28.0 h1:D2M+iL31GmpZxSHOhX8mqyqAT3CXnokUmm0eKoSP+Vc=
github.com/pressly/goose/v3 v3.28.0/go.mod h1:v26MOuB8bL3kzzrt3Vqhb3R0PRVsl8hFQKdrht/L6Rk=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sethvargo/go-retry v0.4.0 h1:9qy1OoIAxBL+gBYnkTnTnWle5wlfsXQlwRzIbbpdqPw=
github.com/sethvargo/go-retry v0.4.0/go.mod h1:tvsjdKG6xfiCx4LSiUZ06kcv38xvdVQwv8R6/VnnVWg=
github.com/sosodev/duration v1.4.0 h1:35ed0KiVFriGHHzZZJaZLgmTEEICIyt8Sx0RQfj9IjE=
github.com/sosodev/duration v1.4.0/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/vektah/gqlparser/v2 v2.5.36 h1:CN9mKVHgMkc+XftdOWIhb4HEL8wKSYkFAqhf8booa7s=
github.com/vektah/gqlparser/v2 v2.5.36/go.mod h1:cAJ9qwVgPaUkWv6Gn8vn0mqOE0Ui5Pn56wNy5396XWo=
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.23.0 h1:qcteJH9D7kHaOgLQ0fzlW9dv42hSa0Vluqt7p4kooWA=
gitlab.com/unboundsoftware/eventsourced/eventsourced v1.23.0/go.mod h1:LrA7I7etRmhIC1PjO8c26BHm+gWsy2rC3eSMe5+XUWE=
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
github.com/vektah/gqlparser/v2 v2.5.37 h1:jbb1Ilv+xBklV6653tKb4oVUupPNTLb5LmrnBKVI12Y=
github.com/vektah/gqlparser/v2 v2.5.37/go.mod h1:9O4Ox6Ngd3Y12bMD3w6i3CRQXh8W1oC1q0m6olCymDM=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0 h1:pnxy6c/kvNBWdNNFzqpjuJLm9Hjhgk/Q0nY221rwuk0=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0/go.mod h1:qw6YsFapotRwoDhXRZvljzaOvCQB7UfnafEJagpN2TA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 h1:QRefszxJmfPdjXUUm3j6iDzY03mTPXMjqErFqQ67vUg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0/go.mod h1:Tiz03lTBVBrm7eWZBOidzEaYaJa8tjwGUGv6d8mlTyk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 h1:QBajQ2SrwQijzHyZbQlPsuIzpl/ll8DY6wPWsajeGcI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0/go.mod h1:08ZQLjrPLQ6R4kAXvuOvODEer5Yh4CoFvll5qB2BCI8=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0 h1:2lpf4hnrasYIsUyEXwnTZq5lsxrMm4T2Bwb06IctAZQ=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0/go.mod h1:YWOW6h7jwApz9Pl76ie/izUsSPj0s2MdIlpqbPqaf3U=
go.opentelemetry.io/otel/log v0.21.0 h1:SLsVDGmtyBrdw8/a2Z0bOIxou/+bN4z56GebH7T0LvA=
go.opentelemetry.io/otel/log v0.21.0/go.mod h1:iReetQrZL9Wyg84cCkOoCmqDHS5RCFfyxC7J+r8fn8g=
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns=
go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI=
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
go.opentelemetry.io/otel/sdk/log v0.21.0 h1:QsE7XSR0ktQdKmRKGnR+f1ObGF32WG+7MER/P9KgmYc=
go.opentelemetry.io/otel/sdk/log v0.21.0/go.mod h1:m9mApjCoD2/1QuKCAptjv+BrG9WKOvQLVdNx+iBldTo=
go.opentelemetry.io/otel/sdk/log/logtest v0.21.0 h1:X+JBBgKlswCGYsmgL0CnoUUtlE//VB345c84jYAYkdQ=
go.opentelemetry.io/otel/sdk/log/logtest v0.21.0/go.mod h1:HD1575K8e6sIFBBDd5tZB3t9DlMytWXq9FuR+Y4rfjE=
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM=
go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0 h1:7kszRN4Ze5GeAkUnBjBSUIunVBMA+mZIG04auueqguM=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0/go.mod h1:CLzakYy8jOdkBfcveCWYkeKJxujcF4xPhh2Z7IfE98U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 h1:julhjPeUH/q/7hinbSdDdqt5h7Zw9YWmRlWRhI0jd54=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0/go.mod h1:Ao2mz688LH/tFf0yMAenidq6k2YNSx6SIY2q6jDACck=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 h1:aXZXsZ012wOgVkqaiQv+Z/d8V0xKjmg70F6m4CE94lc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0/go.mod h1:ziu1gUIJhAaxM3EV1e3Ralk5AAyL1UOYz3lJJ4VSI38=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0 h1:PW/E9Tw6ZykC88pey0MZY8hKOUeEw+hX6PU+oXA4A9o=
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0/go.mod h1:hUw4VNfiOnQ/yzGgy6ZdWVnudXw7iRfImhd6FjXOnvI=
go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI=
go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw=
go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY=
go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU=
go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM=
go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k=
go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I=
go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0=
go.opentelemetry.io/otel/sdk/log v1.47.0 h1:W4N/ZfgpkRRnmARnqYoQ6BrWA0mrj8IzVDNLlpmUJZY=
go.opentelemetry.io/otel/sdk/log v1.47.0/go.mod h1:Wu74Z2EmOKXi2FleSNAb0Hyau2G+r5kubSEMJw5aAMs=
go.opentelemetry.io/otel/sdk/log/logtest v0.23.0 h1:j3pm42QCrKYYLLkI6OrV1QHFkUfDA6/4EP+Y1PzggLg=
go.opentelemetry.io/otel/sdk/log/logtest v0.23.0/go.mod h1:zt0gEnuTZgDvqvczHwYswz93eqMaIr6upTTsdV/HUlU=
go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw=
go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw=
go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM=
go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0=
go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk=
go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc=
google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d h1:IL4hdHzcUv2l/gcg98/Rj3FbtE6axwqslOW8SW0C+S0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260803160001-6ac0973c030d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc h1:rZHRz0ogq4Pid1IKtA/ivHi8XONVF5tqUQXA2TtgpkE=
google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:K/n0XLazCJHjD73YuMyNJsN38BPPy1kfFnKxUXaThFU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc h1:4bNTbnb44EqGVy9HaQxSY2jnafSUdgV3qHtedvNpDKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc=
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
modernc.org/libc v1.75.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
modernc.org/memory v1.12.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/sqlite v1.57.0 h1:qNQP6xnx5M0ISNtlnxoOX0+cD5bJ0/gr9aMmndFczzg=
modernc.org/sqlite v1.57.0/go.mod h1:yCJ2cmAaIkHQ25oXWrF8H4O1lIfPYPR26yCEDj2P3pQ=
+18 -3
View File
@@ -4,7 +4,8 @@ import (
"context"
"errors"
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
"codeberg.org/eventsourced/eventsourced"
"codeberg.org/eventsourced/pg/v2"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
@@ -40,6 +41,8 @@ type eventsourcedMetrics struct {
snapshotStoreDur metric.Float64Histogram
snapshotLoadDur metric.Float64Histogram
idempotencyCheck metric.Int64Counter
outboxPublishDur metric.Float64Histogram
outboxRetries metric.Int64Counter
}
// NewEventsourcedMetrics builds an eventsourced.MetricsRecorder that records to
@@ -78,6 +81,8 @@ func NewEventsourcedMetrics() (eventsourced.MetricsRecorder, error) {
snapshotStoreDur: hist("eventsourced.snapshot.store.duration", "Time taken to persist a snapshot."),
snapshotLoadDur: hist("eventsourced.snapshot.load.duration", "Time taken to load a snapshot."),
idempotencyCheck: counter("eventsourced.idempotency.checks", "Number of command idempotency lookups."),
outboxPublishDur: hist("eventsourced.outbox.publish.duration", "Time taken to publish an event from the outbox."),
outboxRetries: counter("eventsourced.outbox.retries", "Failed outbox publishes; permanent ones are never retried."),
}
if err := errors.Join(errs...); err != nil {
return nil, err
@@ -86,8 +91,8 @@ func NewEventsourcedMetrics() (eventsourced.MetricsRecorder, error) {
}
// Record implements eventsourced.MetricsRecorder. Metric types the adapter does
// not recognise (for example pg outbox metrics when the outbox is not enabled)
// are ignored.
// not recognise are ignored, and so are pg.OutboxBatch and pg.OutboxCleanup: a failed
// publish already shows in the publish and retry instruments.
func (e *eventsourcedMetrics) Record(ctx context.Context, raw eventsourced.Metric) {
switch m := raw.(type) {
case eventsourced.CommandDuration:
@@ -119,5 +124,15 @@ func (e *eventsourcedMetrics) Record(ctx context.Context, raw eventsourced.Metri
attribute.String("aggregate.type", m.AggregateType),
attribute.Bool("hit", m.Hit),
))
case pg.OutboxPublish:
e.outboxPublishDur.Record(ctx, m.Duration.Seconds(), metric.WithAttributes(
attribute.String("event.type", m.EventType),
attribute.Bool("success", m.Success),
))
case pg.OutboxRetry:
e.outboxRetries.Add(ctx, 1, metric.WithAttributes(
attribute.String("event.type", m.EventType),
attribute.Bool("permanent", m.Permanent),
))
}
}
+6 -1
View File
@@ -6,7 +6,8 @@ import (
"testing"
"time"
"gitlab.com/unboundsoftware/eventsourced/eventsourced"
"codeberg.org/eventsourced/eventsourced"
"codeberg.org/eventsourced/pg/v2"
"go.opentelemetry.io/otel"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
@@ -33,6 +34,8 @@ func TestNewEventsourcedMetrics_RecordsContract(t *testing.T) {
eventsourced.SnapshotStored{AggregateType: "Entry", Duration: time.Millisecond, Success: true},
eventsourced.SnapshotLoaded{AggregateType: "Entry", Found: false, Duration: time.Millisecond},
eventsourced.IdempotencyCheck{AggregateType: "Entry", Hit: true},
pg.OutboxPublish{EventType: "EntryAdded", Success: false, Duration: time.Millisecond},
pg.OutboxRetry{EventType: "EntryAdded", RetryCount: 1},
unknownMetric{},
} {
r.Record(context.Background(), m)
@@ -57,6 +60,8 @@ func TestNewEventsourcedMetrics_RecordsContract(t *testing.T) {
"eventsourced.snapshot.store.duration",
"eventsourced.snapshot.load.duration",
"eventsourced.idempotency.checks",
"eventsourced.outbox.publish.duration",
"eventsourced.outbox.retries",
}
var missing []string
for _, w := range want {
+33 -2
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"time"
@@ -65,7 +66,7 @@ func SetupOTelSDK(ctx context.Context, enabled bool, serviceName, buildVersion,
)
otel.SetTextMapPropagator(prop)
traceExporter, err := otlptracehttp.New(ctx)
traceExporter, err := otlptracehttp.New(ctx, otlptracehttp.WithHTTPClient(otlpHTTPClient()))
if err != nil {
return handleErr(err)
}
@@ -88,7 +89,7 @@ func SetupOTelSDK(ctx context.Context, enabled bool, serviceName, buildVersion,
global.SetLoggerProvider(logProvider)
shutdownFuncs = append(shutdownFuncs, logProvider.Shutdown)
exp, err := otlpmetrichttp.New(ctx)
exp, err := otlpmetrichttp.New(ctx, otlpmetrichttp.WithHTTPClient(otlpHTTPClient()))
if err != nil {
return handleErr(err)
}
@@ -99,6 +100,36 @@ func SetupOTelSDK(ctx context.Context, enabled bool, serviceName, buildVersion,
return shutdown, err
}
// otlpHTTPClient is the OTLP exporters' default client, except that it drops idle
// connections after 30s. The collector (Alloy's OTLP receiver, idle_timeout 1m) closes
// connections idle for a minute, and metrics are pushed every 60s. With the exporters'
// default of 90s, a push could reuse a connection the collector was closing. The push
// then failed (EOF, connection reset) and was dropped: net/http doesn't retry a POST and
// the exporters don't retry transport errors. Traces hit the same race after a quiet
// spell. Closing first means such a push dials fresh.
//
// The transport mirrors the exporters' own (otlpmetrichttp/otlptracehttp v1.46.0
// ourTransport). A custom client makes the exporters ignore OTEL_EXPORTER_OTLP_*TIMEOUT
// and the OTEL_EXPORTER_OTLP_*CERTIFICATE/CLIENT_* TLS variables; nothing sets them.
func otlpHTTPClient() *http.Client {
return &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
// The exporters' default.
Timeout: 10 * time.Second,
}
}
func Handler(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
+22
View File
@@ -0,0 +1,22 @@
package otelsetup
import (
"net/http"
"testing"
"time"
)
func TestOTLPHTTPClient_ClosesIdleConnectionsBeforeTheCollector(t *testing.T) {
c := otlpHTTPClient()
tr, ok := c.Transport.(*http.Transport)
if !ok {
t.Fatalf("transport is %T, want *http.Transport", c.Transport)
}
// Alloy's OTLP receiver closes connections idle for 1m; the client must close first.
if tr.IdleConnTimeout <= 0 || tr.IdleConnTimeout >= time.Minute {
t.Errorf("IdleConnTimeout = %v, want in (0, 1m)", tr.IdleConnTimeout)
}
if c.Timeout != 10*time.Second {
t.Errorf("Timeout = %v, want 10s (the exporters' default)", c.Timeout)
}
}
+10
View File
@@ -2,5 +2,15 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"packageRules": [
{
"groupName": "OpenTelemetry",
"matchPackageNames": [
"go.opentelemetry.io/**",
"gitea.unbound.se/shiny/otelsetup",
"gitea.unbound.se/shiny/logging"
]
}
]
}