Compare commits

..
58 Commits
Author SHA1 Message Date
NotAlexNoyle c645ed1adf Exclude CODEOWNERS from language statistics 2026-11-01 09:18:32 -05:00
NotAlexNoyle 87234d9f76 Add CODEOWNERS (one staff approval) 2026-11-01 09:18:32 -05:00
Alexey Palazhchenko 0d5eb3343a Prepare v2.7.0 release (#5538) 2025-11-10 21:18:24 +04:00
Alexey Palazhchenko 52ad11f0af Bump deps (#5537) 2025-11-10 20:45:21 +04:00
Alexey Palazhchenko 71b6eb3a08 Bump DocumentDB (#5536) 2025-11-09 22:46:54 +04:00
5cb62a1155 Add MCP tools (#5452)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-11-09 20:36:10 +04:00
Alexey Palazhchenko 867537dd01 Use Go 1.25.4 (#5534) 2025-11-08 09:35:59 +04:00
487880fd19 Add basic MCP server (#5279)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-11-06 09:08:44 +04:00
Chi FujiiandAlexey Palazhchenko 3b29cece0c Refactor DataAPI with graceful shutdown and add OpenAPI specification endpoint (#5357)
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-10-27 18:55:12 +04:00
Alexey Palazhchenko c1bb0b6db6 Update DocumentDB (#5528) 2025-10-22 18:46:58 +04:00
Alexey Palazhchenko a04b20a58f Use forked lazyerrors (#5527) 2025-10-22 16:13:02 +04:00
Josh Soref 2b2ce550d4 Fix CODEOWNERS grammar (#5495)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-10-21 12:51:38 +04:00
Alexey Palazhchenko 87293d3434 Skip flaky test for now (#5526) 2025-10-21 12:50:19 +04:00
Alexey Palazhchenko 7b0d200262 Use Go 1.25.3 (#5525) 2025-10-21 11:43:35 +04:00
Patryk KwiatekandAlexey Palazhchenko d1d037a030 Add a test for *documentdb.Conn finalizing (#5484)
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-10-21 11:28:03 +04:00
Alexey Palazhchenko c37a62b0a2 Bump dependencies (#5519) 2025-10-09 17:57:31 +04:00
Alexey Palazhchenko cff1577d19 Improve proxy mode observability (#5440) 2025-10-03 19:53:25 +04:00
Alexey Palazhchenko efa50d4877 Add tracing of SQL queries (#5514) 2025-10-02 21:02:46 +04:00
Alexey Palazhchenko 067eb2f143 Rename OpenTelemetry spans (#5512) 2025-09-29 14:58:13 +04:00
Alexey Palazhchenko 4e8e51751d Minor observability cleanup (#5511) 2025-09-28 22:32:36 +03:00
Alexey Palazhchenko ae0b94de12 Bump deps (#5509) 2025-09-28 20:56:00 +03:00
Alexey Palazhchenko 2c24483c06 Use Go 1.25.1 (#5508) 2025-09-28 15:48:00 +03:00
Josh Soref 221d5cbd08 Fix spelling in .go files (#5492)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-09-08 10:33:12 +09:00
Alexey Palazhchenko 62ce1019b1 Simplify licensing 2025-09-07 12:08:29 +04:00
Josh Soref ef9934e3ed Replace malicious container registry references with ghcr (#5486) 2025-09-03 20:05:21 +04:00
Alexey Palazhchenko e78eaffb58 Finalize PostgreSQL metrics (#5485) 2025-09-03 15:25:44 +04:00
Chi FujiiandAlexey Palazhchenko 370b1586c5 Add PostgreSQL metrics (#5427)
Closes #5402.

Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-09-02 14:50:16 +04:00
Alexander Tobi FashakinandCopilot 1f8d160296 Update cloud installation docs (#5466)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-01 19:52:16 +04:00
3656ebd1c4 Use generated structs in Data API (#5382)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-09-01 13:24:21 +04:00
Patryk KwiatekandAlexey Palazhchenko e3a7406b9e Update wire dependency (#5465)
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-09-01 12:48:10 +04:00
Alexey Palazhchenko 4847e59e25 Update dependencies (#5479) 2025-09-01 11:55:26 +04:00
Alexey Palazhchenko b6fa806add Move to documentdb/documentdb (#5468) 2025-08-27 12:35:57 +04:00
Chi FujiiandAlexey Palazhchenko 5394f00550 Avoid hijacking connections from the pool and immediately closing them (#5443)
Closes #5402.
Closes FerretDB/FerretDB-DocumentDB#270.

Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-08-27 11:37:26 +04:00
Alexey Palazhchenko 9a48b7132c Add helpers for lazy logging (#5467) 2025-08-27 10:26:35 +04:00
Alexey Palazhchenko 4cea93524d Fix FerretDB Cloud link 🤦 2025-08-26 19:38:39 +04:00
Alexey Palazhchenko 3cd4554d87 Add Cease and Desist Notice (#5462) 2025-08-25 18:50:08 +04:00
Alexey Palazhchenko 2e6ca25078 Add FerretDB Cloud blog post (#5461) 2025-08-25 17:04:31 +04:00
Alexey Palazhchenko 901a8f6030 Add MongoDB's complaint (#5460) 2025-08-25 12:45:03 +04:00
Alexey Palazhchenko b1feb6377d Add documentation page stub for MCP server (#5451) 2025-08-22 07:49:36 +00:00
Alexey Palazhchenko 3fd8c982a5 Bump deps (#5455) 2025-08-22 11:32:52 +04:00
Alexander Tobi FashakinandCopilot c05de8bdea Document cloud instance deployment and setup (#5424)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-21 20:15:06 +04:00
Alexey Palazhchenko d97f2612b8 Add TODO comment about Debian 13 Trixie (#5450)
Refs FerretDB/FerretDB#5449.
2025-08-21 13:42:59 +04:00
Alexey Palazhchenko 6d8e86b774 Remove flaky test (#5444) 2025-08-20 12:17:35 +04:00
Alexander Tobi Fashakin 7a117e768d Update compatible apps (#5442) 2025-08-20 05:51:21 +00:00
Alexander Tobi FashakinandCopilot bfd1650ee2 Update content guide (#5441)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-19 19:09:52 +04:00
Alexey Palazhchenko d9a14c883e Add comments to supervisord config (#5430) 2025-08-19 14:07:17 +04:00
Alexey Palazhchenko 2b652dae20 Use middleware (#5377) 2025-08-19 13:57:06 +04:00
Alexey Palazhchenko 6631594402 Add middleware (#5437) 2025-08-19 07:38:31 +04:00
c5f69fe962 Add blog post on Formio (#5395)
Co-authored-by: Chi Fujii <chi.fujii@ferretdb.io>
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-08-18 22:39:46 +04:00
Alexey Palazhchenko a65042d381 Update expected PostgreSQL version (#5438) 2025-08-18 20:12:33 +04:00
Alexey Palazhchenko 24981ad991 Tweak conninfo (#5435) 2025-08-18 12:23:23 +04:00
Alexey Palazhchenko 2078f859f3 Make Run wait for all Handle calls to finish (#5434) 2025-08-18 11:52:42 +04:00
Chi FujiiandAlexey Palazhchenko 16670ff34f Upload YugabyteDB log files (#5401)
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-08-18 09:27:56 +04:00
Alexey Palazhchenko b994d18792 Update DocumentDB (#5431) 2025-08-14 19:21:04 +04:00
Alexey Palazhchenko 4ea90be4aa Use QEMU on arm64 for Yugabyte for now (#5429) 2025-08-14 12:32:49 +04:00
3770c1e4eb Add release blog post for FerretDB v2.5.0 (#5412)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Palazhchenko <alexey.palazhchenko@ferretdb.io>
2025-08-14 12:06:53 +04:00
Chi Fujii 5fd75c160a Bump YugabyteDB to stable version (#5428) 2025-08-14 10:55:16 +04:00
Alexey Palazhchenko c60131a69c Update documentation to point to the next release (#5423) 2025-08-14 10:54:45 +04:00
357 changed files with 9039 additions and 3201 deletions

No files matched your search

+3
View File
@@ -4,4 +4,7 @@
*.webp filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.go diff=golang
.gitea/CODEOWNERS linguist-detectable=false
+3
View File
@@ -0,0 +1,3 @@
# Pull requests request review from the eligible staff team(s).
# One approval from either an Admin or Developer satisfies branch protection.
* @trueog/Admins @trueog/Developers
+1 -1
View File
@@ -1,5 +1,5 @@
# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners
# Order is important; the last matching pattern takes the most precedence.
# Order is important; the last matching pattern has the highest precedence.
* @AlekSi @chilagrow
+3 -3
View File
@@ -9,7 +9,7 @@
- [local image](../build/deps/postgres-documentdb.Dockerfile);
- [bw image](../build/ferretdb-bw/Dockerfile).
3. Update constants in [`documentdb.go`](../build/version/documentdb.go).
4. Check DocumentDB images tags in documentation (where we specify full tags).
4. Check DocumentDB images/tags in documentation (where we specify full tags).
5. Re-run tests, linters.
6. Check issues and pull requests, update milestones and labels.
7. Check if there are breaking changes or anything that should be reflected in the changelog and/or user documentation.
@@ -64,8 +64,8 @@
1. Publish and announce blog post.
2. Add link to blog post to `CHANGELOG.md` and release page.
3. Bump the latest version on https://beacon.ferretdb.com and https://beacon.ferretdb.io.
4. Update DocumentDB image tags to point back to development version.
5. Update DocumentDB images tags in the current documentation (where we specify full tags)
4. Update DocumentDB images/tags in Dockerfiles to point back to development version.
5. Update DocumentDB images/tags in the current documentation (where we specify full tags)
to point to the next (non-existing) version.
6. Update URLs with `/refs/tags/` in them in the current documentation
to point to the next (non-existing) version.
+10 -10
View File
@@ -43,7 +43,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -79,11 +79,11 @@ jobs:
- name: Setup Ollama and MCPHost
run: |
mkdir -p /tmp/logs
mkdir -p tmp/logs
curl -fsSL https://ollama.com/install.sh | sh
sudo systemctl stop ollama
bin/task ollama > /tmp/logs/ollama.log 2>&1 &
bin/task mcphost > /tmp/logs/mcphost.log 2>&1 &
bin/task ollama > tmp/logs/ollama.log 2>&1 &
bin/task mcphost > tmp/logs/mcphost.log 2>&1 &
- name: Wait for and setup environment
run: bin/task env-setup
@@ -103,16 +103,16 @@ jobs:
- name: Collect logs
if: failure()
run: |
bin/task env-logs-collect > /tmp/logs/compose-logs.txt
cp -v integration/*.json.tmp integration/*.txt.tmp /tmp/logs/
bin/task env-logs-collect
cp -v integration/*.json.tmp integration/*.txt.tmp tmp/logs/
- name: Compress logs before upload
if: failure()
run: zip -r -q -9 logs.zip /tmp/logs
run: zip -r -q -9 logs.zip tmp/logs
- name: Upload logs
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: logs-${{ inputs.task }}-${{ inputs.shard_index }}-${{ inputs.shard_total }}
path: logs.zip
@@ -123,7 +123,7 @@ jobs:
uses: codecov/codecov-action@v5
with:
files: ./integration/integration-${{ inputs.task }}.txt
flags: integration,${{ inputs.task }}-${{ inputs.shard_index }}
flags: integration,${{ inputs.task }},${{ inputs.task }}-${{ inputs.shard_index }}
disable_file_fixes: true
fail_ci_if_error: true
verbose: true
@@ -147,7 +147,7 @@ jobs:
if: always()
uses: codecov/test-results-action@v1
with:
flags: integration,${{ inputs.task }}-${{ inputs.shard_index }}
flags: integration,${{ inputs.task }},${{ inputs.task }}-${{ inputs.shard_index }}
file: ./integration/${{ inputs.task }}.xml
# we don't want them on CI
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
+2 -2
View File
@@ -43,7 +43,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: true
@@ -90,7 +90,7 @@ jobs:
# it has to be exactly "docs.tar" for actions/deploy-pages below
- name: Upload archive artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: docs
path: tmp/docs.tar
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
+18 -4
View File
@@ -42,7 +42,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -102,7 +102,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -123,6 +123,16 @@ jobs:
run: go generate -x
working-directory: tools
# install.sh starts ollama service, stop it since we want to run with `bin/task ollama` with environment variables
- name: Setup Ollama
run: |
curl -fsSL https://ollama.com/install.sh | sudo -E sh
sudo systemctl stop ollama
- name: Start Ollama in the background
run: |
bin/task ollama &
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
@@ -133,6 +143,10 @@ jobs:
- name: Start environment
run: bin/task env-up-detach
- name: Pull and load Ollama model
run: |
bin/task ollama-model
- name: Run init
run: bin/task init
@@ -281,7 +295,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -341,7 +355,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `golangci-lint run --new` to work
lfs: false # LFS is used only by website
+6 -6
View File
@@ -64,7 +64,7 @@ jobs:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
if: github.event_name != 'pull_request_target'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -72,7 +72,7 @@ jobs:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout pull request code
if: github.event_name == 'pull_request_target'
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -156,7 +156,7 @@ jobs:
run: bin/task build-development
- name: Upload development binaries
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: bin-dev
path: tmp/bin-dev/
@@ -167,7 +167,7 @@ jobs:
run: bin/task build-production
- name: Upload production binaries
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: bin
path: tmp/bin/
@@ -178,7 +178,7 @@ jobs:
run: bin/task packages
- name: Upload .deb packages
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: debs
path: tmp/debs/
@@ -186,7 +186,7 @@ jobs:
if-no-files-found: error
- name: Upload .rpm packages
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: rpms
path: tmp/rpms/
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
+2 -2
View File
@@ -45,7 +45,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for `git describe` to work
lfs: false # LFS is used only by website
@@ -106,7 +106,7 @@ jobs:
- name: Upload FerretDB log
if: failure()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v5
with:
name: ferretdb-log
path: ferretdb-log.zip
+2 -2
View File
@@ -45,7 +45,7 @@ jobs:
steps:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for wrangler to work
lfs: true
@@ -116,7 +116,7 @@ jobs:
# TODO https://github.com/FerretDB/github-actions/issues/211
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v5
with:
fetch-depth: 0 # for wrangler to work
lfs: true
+2 -1
View File
@@ -53,6 +53,7 @@ linters-settings:
- "!**/internal/documentdb/**.go"
- "!**/internal/driver/*.go"
- "!**/internal/handler/**.go"
- "!**/internal/mcp/*.go"
- "!**/internal/mongoerrors/*.go"
- "!**/internal/util/logging/*.go"
- "!**/internal/util/scram/*.go"
@@ -179,7 +180,7 @@ linters-settings:
alias: otelsdkresource
- pkg: go.opentelemetry.io/otel/sdk/trace
alias: otelsdktrace
- pkg: go.opentelemetry.io/otel/semconv/v1.21.0
- pkg: go.opentelemetry.io/otel/semconv/v1.34.0
alias: otelsemconv
# ineffassign
lll:
+92
View File
@@ -2,6 +2,98 @@
<!-- markdownlint-disable MD024 MD034 -->
## [v2.7.0](https://github.com/FerretDB/FerretDB/releases/tag/v2.7.0) (2025-11-10)
(We skipped v2.6.0 to align DocumentDB and FerretDB version numbers.)
This version works best with
[DocumentDB v0.107.0-ferretdb-2.7.0](https://github.com/FerretDB/documentdb/releases/tag/v0.107.0-ferretdb-2.7.0).
### Breaking changes
There are some changes in our Prometheus metrics and OpenTelemetry traces.
Please note that they are
[not stable yet](https://docs.ferretdb.io/v2.7/configuration/observability/).
### What's Changed
#### Basic MCP server
This release adds basic MCP server support.
Future release will bring additional functionality.
#### Connectivity improvements
PostgreSQL connections are now reused, often yielding modest performance gains.
### New Features 🎉
- Add basic MCP server by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5279
- Implement DataAPI graceful shutdown and add OpenAPI spec endpoint by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5357
- Improve proxy mode observability by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5440
- Add tracing of SQL queries by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5514
### Enhancements 🛠
- Add `PostgreSQL` metrics by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5427
- Rename OpenTelemetry spans by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5512
### Documentation 📄
- Add blog post on Formio by @Fashander in https://github.com/FerretDB/FerretDB/pull/5395
- Document cloud instance deployment and setup by @Fashander in https://github.com/FerretDB/FerretDB/pull/5424
- Update content guide by @Fashander in https://github.com/FerretDB/FerretDB/pull/5441
- Update compatible apps by @Fashander in https://github.com/FerretDB/FerretDB/pull/5442
- Add documentation page stub for MCP server by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5451
- Add FerretDB Cloud blog post by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5461
- Update cloud installation docs by @Fashander in https://github.com/FerretDB/FerretDB/pull/5466
### Other Changes 🤖
- Use middleware by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5377
- Use generated structs in Data API by @noisersup in https://github.com/FerretDB/FerretDB/pull/5382
- Upload YugabyteDB log files by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5401
- Update documentation to point to the next release by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5423
- Bump YugabyteDB to stable version by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5428
- Use QEMU on arm64 for Yugabyte for now by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5429
- Add comments to supervisord config by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5430
- Update DocumentDB by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5431
- Make `Run` wait for all `Handle` calls to finish by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5434
- Tweak `conninfo` by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5435
- Add middleware by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5437
- Update expected PostgreSQL version by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5438
- Avoid hijacking connections from the pool and immediately closing them by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5443
- Remove flaky test by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5444
- Add TODO comment about Debian 13 Trixie by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5450
- Add MCP tools by @chilagrow in https://github.com/FerretDB/FerretDB/pull/5452
- Bump deps by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5455
- Update `wire` dependency by @noisersup in https://github.com/FerretDB/FerretDB/pull/5465
- Add helpers for lazy logging by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5467
- Move to `documentdb/documentdb` by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5468
- Update dependencies by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5479
- Add a test for `*documentdb.Conn` finalizing by @noisersup in https://github.com/FerretDB/FerretDB/pull/5484
- Finalize PostgreSQL metrics by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5485
- Fix spelling in `.go` files by @jsoref in https://github.com/FerretDB/FerretDB/pull/5492
- Fix `CODEOWNERS` grammar by @jsoref in https://github.com/FerretDB/FerretDB/pull/5495
- Use Go 1.25.1 by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5508
- Bump deps by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5509
- Minor observability cleanup by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5511
- Bump dependencies by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5519
- Use Go 1.25.3 by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5525
- Skip flaky test for now by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5526
- Use forked lazyerrors by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5527
- Update DocumentDB by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5528
- Use Go 1.25.4 by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5534
- Bump DocumentDB by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5536
- Bump deps by @AlekSi in https://github.com/FerretDB/FerretDB/pull/5537
### New Contributors
- @jsoref made their first contribution in https://github.com/FerretDB/FerretDB/pull/5486
[All closed issues and pull requests](https://github.com/FerretDB/FerretDB/milestone/80?closed=1).
[All commits](https://github.com/FerretDB/FerretDB/compare/v2.5.0...v2.7.0).
## [v2.5.0](https://github.com/FerretDB/FerretDB/releases/tag/v2.5.0) (2025-08-12)
This version works best with
+1 -1
View File
@@ -30,7 +30,7 @@ On macOS and Windows, [Docker Desktop](https://www.docker.com/products/docker-de
On Windows, it should be [configured to use WSL 2](https://docs.docker.com/desktop/windows/wsl/) without any distro;
all commands should be run on the host.
You will need Go 1.24 or later on the host.
You will need Go 1.25 or later on the host.
If your package manager doesn't provide it yet,
please install it from [go.dev](https://go.dev/dl/).
-2
View File
@@ -1,2 +0,0 @@
FerretDB database system (https://www.ferretdb.com/).
Copyright 2021 FerretDB Inc.
+1 -3
View File
@@ -19,7 +19,7 @@
FerretDB is an open-source alternative to MongoDB.
It is a proxy that converts MongoDB 5.0+ wire protocol queries to SQL
and uses PostgreSQL with [DocumentDB extension](https://github.com/microsoft/documentdb) as a database engine.
and uses PostgreSQL with [DocumentDB extension](https://github.com/documentdb/documentdb) as a database engine.
```mermaid
flowchart LR
@@ -116,7 +116,5 @@ See [there](https://pkg.go.dev/github.com/FerretDB/FerretDB/v2/build/version) fo
- [Slack chat](https://slack.ferretdb.io/) for quick questions.
- [GitHub Discussions](https://github.com/FerretDB/FerretDB/discussions) for longer topics.
- [GitHub Issues](https://github.com/FerretDB/FerretDB/issues) for bugs and missing features.
- [Open Office Hours meeting](https://calendar.google.com/calendar/event?action=TEMPLATE&tmeid=NGhrZTA5dXZ0MzQzN2gyaGVtZmx2aWxmN2pfMjAyNDA0MDhUMTcwMDAwWiBjX24zN3RxdW9yZWlsOWIwMm0wNzQwMDA3MjQ0QGc&tmsrc=c_n37tquoreil9b02m0740007244%40group.calendar.google.com&scp=ALL)
every Monday at 17:00 UTC at [Google Meet](https://meet.google.com/mcb-arhw-qbq).
If you want to contact FerretDB Inc., please use [this form](https://www.ferretdb.com/contact/).
+19 -7
View File
@@ -17,7 +17,7 @@ vars:
BUILD_TAGS: ferretdb_dev
MODE: diff-normal
SERVICES: jaeger mongodb mongodb-secure postgres yugabytedb
LLM: qwen3:0.6b
LLM: qwen3:0.6b # sync with code
LLM_CONTEXT: 40960 # https://ollama.com/library/qwen3:0.6b
tasks:
@@ -115,10 +115,13 @@ tasks:
- docker compose logs --follow
env-logs-collect:
desc: "Collect logs from Docker and save them to tmp/logs"
cmds:
- docker compose ps --all
- docker compose logs
- docker compose exec yugabytedb sh -c "bin/yugabyted collect_logs --stdout | gunzip | tar -xOf -"
- bin/envtool{{exeExt}} shell mkdir tmp/logs
- docker compose ps --all > tmp/logs/compose-ps.txt
- docker compose logs > tmp/logs/compose-logs.txt
- docker compose exec yugabytedb sh -c "bin/yugabyted collect_logs --stdout | gunzip | tar -xf - --one-top-level=/tmp/logs"
- docker compose cp yugabytedb:/tmp/logs tmp/logs/yugabytedb
env-up:
desc: "Start development environment"
@@ -319,6 +322,7 @@ tasks:
--no-auth
--listen-addr=:27017
--listen-data-api-addr=127.0.0.1:8899
--listen-mcp-addr=127.0.0.1:8081
--proxy-addr=127.0.0.1:37017
--postgresql-url='postgres://username:password@127.0.0.1:5432/postgres'
--otel-traces-url=http://127.0.0.1:4318/v1/traces
@@ -333,6 +337,7 @@ tasks:
--mode={{.MODE}}
--listen-addr=:27017
--listen-data-api-addr=127.0.0.1:8899
--listen-mcp-addr=127.0.0.1:8081
--proxy-addr=127.0.0.1:47017
--postgresql-url='postgres://username:password@127.0.0.1:5432/postgres'
--otel-traces-url=http://127.0.0.1:4318/v1/traces
@@ -349,6 +354,7 @@ tasks:
--no-auth
--listen-addr=:27017
--listen-data-api-addr=127.0.0.1:8899
--listen-mcp-addr=127.0.0.1:8081
--proxy-addr=127.0.0.1:37017
--postgresql-url='postgres://username:password@127.0.0.1:5433/yugabyte'
--otel-traces-url=http://127.0.0.1:4318/v1/traces
@@ -363,6 +369,7 @@ tasks:
--mode={{.MODE}}
--listen-addr=:27017
--listen-data-api-addr=127.0.0.1:8899
--listen-mcp-addr=127.0.0.1:8081
--proxy-addr=127.0.0.1:47017
--postgresql-url='postgres://username:password@127.0.0.1:5433/yugabyte'
--otel-traces-url=http://127.0.0.1:4318/v1/traces
@@ -406,18 +413,23 @@ tasks:
- bin/pkgsite{{exeExt}} -http=127.0.0.1:6060 -open
ollama:
desc: "Run Ollama server"
desc: "Run local Ollama server"
cmds:
- ollama serve
env:
OLLAMA_CONTEXT_LENGTH: "{{.LLM_CONTEXT}}"
OLLAMA_DEBUG: 2
ollama-model:
desc: "Pull and load Ollama model"
cmds:
- ollama pull {{.LLM}}
- ollama run --think=false {{.LLM}} 'Hi!'
mcphost:
desc: "Run MCPHost for local Ollama server"
cmds:
# TODO https://github.com/FerretDB/FerretDB/issues/5277
# - bin/mcphost --config build/mcphost.json --model ollama:{{.LLM}}
- bin/mcphost --config=build/mcphost.json --model=ollama:{{.LLM}}
psql:
desc: "Run psql"
+1 -1
View File
@@ -1 +1 @@
FROM jaegertracing/all-in-one:1.71.0
FROM jaegertracing/all-in-one:1.74.0
+1 -1
View File
@@ -1,5 +1,5 @@
# Use development image and full tag close to the release.
FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-0.106.0-ferretdb-2.5.0
FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-0.107.0-ferretdb-2.7.0
# Use moving development image during development.
# FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-ferretdb
+1 -1
View File
@@ -1,2 +1,2 @@
FROM aquasec/trivy:0.64.1
FROM aquasec/trivy:0.67.2
WORKDIR /workdir
+1 -1
View File
@@ -1 +1 @@
FROM yugabytedb/yugabyte:2.25.2.0-b359
FROM yugabytedb/yugabyte:2025.1.1.0-b197
+4 -4
View File
@@ -1,5 +1,5 @@
# Use production image and full tag close to the release.
FROM --platform=linux/amd64 ghcr.io/ferretdb/postgres-documentdb:17-0.106.0-ferretdb-2.5.0
FROM --platform=linux/amd64 ghcr.io/ferretdb/postgres-documentdb:17-0.107.0-ferretdb-2.7.0
# Use moving development image during development.
# FROM --platform=linux/amd64 ghcr.io/ferretdb/postgres-documentdb-dev:17-ferretdb
@@ -15,9 +15,9 @@ apt update
apt install -y git-lfs mongodb-mongosh tmux nano
cd /tmp
curl -OJL https://go.dev/dl/go1.24.6.linux-amd64.tar.gz
tar -xf go1.24.6.linux-amd64.tar.gz
rm go1.24.6.linux-amd64.tar.gz
curl -OJL https://go.dev/dl/go1.25.4.linux-amd64.tar.gz
tar -xf go1.25.4.linux-amd64.tar.gz
rm go1.25.4.linux-amd64.tar.gz
mv go /usr/local/
ln -s /usr/local/go/bin/* /usr/local/bin/
go version
+6 -3
View File
@@ -12,7 +12,8 @@ ARG LABEL_COMMIT
# prepare stage
FROM --platform=$BUILDPLATFORM golang:1.24.5 AS development-prepare
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM --platform=$BUILDPLATFORM golang:1.25.4-bookworm AS development-prepare
# use a single directory for all Go caches to simplify RUN --mount commands below
ENV GOPATH=/cache/gopath
@@ -36,7 +37,8 @@ EOF
# build stage
FROM golang:1.24.5 AS development-build
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM golang:1.25.4-bookworm AS development-build
ARG TARGETARCH
@@ -103,7 +105,8 @@ COPY --from=development-build /src/bin/ferretdb /ferretdb
# final stage
FROM golang:1.24.5 AS development
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM golang:1.25.4-bookworm AS development
ENV GOCOVERDIR=/tmp/cover
ENV GORACE=halt_on_error=1,history_size=2
+5 -3
View File
@@ -12,7 +12,8 @@ ARG LABEL_COMMIT
# prepare stage
FROM --platform=$BUILDPLATFORM golang:1.24.5 AS eval-dev-prepare
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM --platform=$BUILDPLATFORM golang:1.25.4-bookworm AS eval-dev-prepare
# use a single directory for all Go caches to simplify RUN --mount commands below
ENV GOPATH=/cache/gopath
@@ -36,7 +37,8 @@ EOF
# build stage
FROM golang:1.24.5 AS eval-dev-build
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM golang:1.25.4-bookworm AS eval-dev-build
ARG TARGETARCH
@@ -97,7 +99,7 @@ EOF
# final stage
# Use development image and full tag close to the release.
FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-0.106.0-ferretdb-2.5.0 AS eval-dev
FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-0.107.0-ferretdb-2.7.0 AS eval-dev
# Use moving development image during development.
# FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-ferretdb AS eval-dev
+5 -3
View File
@@ -12,7 +12,8 @@ ARG LABEL_COMMIT
# prepare stage
FROM --platform=$BUILDPLATFORM golang:1.24.5 AS eval-prepare
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM --platform=$BUILDPLATFORM golang:1.25.4-bookworm AS eval-prepare
# use a single directory for all Go caches to simplify RUN --mount commands below
ENV GOPATH=/cache/gopath
@@ -36,7 +37,8 @@ EOF
# build stage
FROM golang:1.24.5 AS eval-build
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM golang:1.25.4-bookworm AS eval-build
ARG TARGETARCH
@@ -89,7 +91,7 @@ EOF
# final stage
# Use production image and full tag close to the release.
FROM ghcr.io/ferretdb/postgres-documentdb:17-0.106.0-ferretdb-2.5.0 AS eval
FROM ghcr.io/ferretdb/postgres-documentdb:17-0.107.0-ferretdb-2.7.0 AS eval
# Use moving development image during development.
# FROM ghcr.io/ferretdb/postgres-documentdb-dev:17-ferretdb AS eval
+4 -3
View File
@@ -1,11 +1,11 @@
; https://supervisord.org/configuration.html
; https://supervisord.org/configuration.html#supervisord-section-values
[supervisord]
nodaemon=true
user=root
loglevel=info
logfile=/dev/stderr
logfile_maxbytes=0
; https://supervisord.org/configuration.html#program-x-section-values
[program:postgres]
command=/usr/local/bin/docker-entrypoint.sh postgres
; lower priority postgres starts before ferretdb (default priority 999)
@@ -16,6 +16,7 @@ stdout_logfile=/dev/stderr
stdout_logfile_maxbytes=0
redirect_stderr=true
; https://supervisord.org/configuration.html#program-x-section-values
[program:ferretdb]
command=/usr/local/bin/ferretdb
stdout_logfile=/dev/stderr
+4 -2
View File
@@ -12,7 +12,8 @@ ARG LABEL_COMMIT
# prepare stage
FROM --platform=$BUILDPLATFORM golang:1.24.5 AS production-prepare
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM --platform=$BUILDPLATFORM golang:1.25.4-bookworm AS production-prepare
# use a single directory for all Go caches to simplify RUN --mount commands below
ENV GOPATH=/cache/gopath
@@ -36,7 +37,8 @@ EOF
# build stage
FROM golang:1.24.5 AS production-build
# TODO https://github.com/FerretDB/FerretDB/issues/5449
FROM golang:1.25.4-bookworm AS production-build
ARG TARGETARCH
+10 -9
View File
@@ -18,19 +18,20 @@ import "runtime"
const (
// DocumentDB is a version of DocumentDB this version of FerretDB is compatible with.
DocumentDB = "0.106.0 gitref: HEAD sha:beb9d25d98 buildId:0"
DocumentDB = "0.107.0 gitref: HEAD sha:e63835403d buildId:0"
// DocumentDBURL points to the release page of the DocumentDB version above.
DocumentDBURL = "https://github.com/FerretDB/documentdb/releases/tag/v0.106.0-ferretdb-2.5.0"
DocumentDBURL = "https://github.com/FerretDB/documentdb/releases/tag/v0.107.0-ferretdb-2.7.0"
)
// DocumentDBSafeToUpdate represents versions of DocumentDB that FerretDB can update.
var DocumentDBSafeToUpdate = []string{
"0.102.0 gitref: HEAD sha:80462f5 buildId:0", // v2.1.0
"0.103.0 gitref: HEAD sha:7514232 buildId:0", // v2.2.0
"0.104.0 gitref: HEAD sha:2045d0e buildId:0", // v2.3.0, v2.3.1
"0.105.0 gitref: HEAD sha:8453d93b buildId:0", // v2.4.0
// "0.106.0 gitref: HEAD sha:beb9d25d98 buildId:0", // v2.5.0
"0.102.0 gitref: HEAD sha:80462f5 buildId:0", // v2.1.0
"0.103.0 gitref: HEAD sha:7514232 buildId:0", // v2.2.0
"0.104.0 gitref: HEAD sha:2045d0e buildId:0", // v2.3.0, v2.3.1
"0.105.0 gitref: HEAD sha:8453d93b buildId:0", // v2.4.0
"0.106.0 gitref: HEAD sha:beb9d25d98 buildId:0", // v2.5.0
// FerretDB v2.6.0 wasn't released
}
// PostgreSQLTest is a version of PostgreSQL used by tests.
@@ -42,6 +43,6 @@ func init() {
arch = "aarch64-unknown-linux-gnu"
}
PostgreSQLTest = "PostgreSQL 17.5 (Debian 17.5-1.pgdg120+1) on " + arch + ", " +
"compiled by gcc (Debian 12.2.0-14) 12.2.0, 64-bit"
PostgreSQLTest = "PostgreSQL 17.6 (Debian 17.6-2.pgdg12+1) on " + arch + ", " +
"compiled by gcc (Debian 12.2.0-14+deb12u1) 12.2.0, 64-bit"
}
+1 -1
View File
@@ -93,7 +93,7 @@ const ferretdbModule = "github.com/FerretDB/FerretDB/v2"
// semVerTag is a https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string,
// but with a leading `v`.
//
//nolint:lll // for readibility
//nolint:lll // for readability
var semVerTag = regexp.MustCompile(`^v(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`)
// Get returns current build's info.
+1 -1
View File
@@ -261,7 +261,7 @@ func main() {
switch cmd {
case "setup":
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
err = setup(ctx, logger)
+1 -1
View File
@@ -28,7 +28,7 @@ import (
"slices"
"strings"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/AlekSi/lazyerrors"
)
// copyFile copies a file from src to dst, overwriting dst if it exists.
+5 -9
View File
@@ -21,9 +21,9 @@ import (
"log/slog"
"net"
"net/url"
"runtime"
"time"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/jackc/pgx/v5"
"github.com/prometheus/client_golang/prometheus"
@@ -31,7 +31,6 @@ import (
"github.com/FerretDB/FerretDB/v2/internal/documentdb"
"github.com/FerretDB/FerretDB/v2/internal/util/ctxutil"
"github.com/FerretDB/FerretDB/v2/internal/util/debug"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/state"
)
@@ -162,7 +161,7 @@ func setup(ctx context.Context, logger *slog.Logger) error {
return lazyerrors.Error(err)
}
go h.Serve(ctx)
go h.Run(ctx)
if err = setupMongoDB(ctx, logger, "mongodb://username:password@127.0.0.1:47017/", "mongodb-secure"); err != nil {
return lazyerrors.Error(err)
@@ -173,12 +172,9 @@ func setup(ctx context.Context, logger *slog.Logger) error {
return lazyerrors.Error(err)
}
// TODO https://github.com/FerretDB/FerretDB/issues/5369
if runtime.GOARCH != "arm64" {
uri = "postgres://pg-user:pg-pass@127.0.0.1:5433/yugabyte"
if err = setupUser(ctx, uri, logging.WithName(logger, "yugabytedb")); err != nil {
return lazyerrors.Error(err)
}
uri = "postgres://pg-user:pg-pass@127.0.0.1:5433/yugabyte"
if err = setupUser(ctx, uri, logging.WithName(logger, "yugabytedb")); err != nil {
return lazyerrors.Error(err)
}
logger.InfoContext(ctx, "Done")
+6 -7
View File
@@ -31,12 +31,12 @@ import (
"strings"
"time"
"github.com/AlekSi/lazyerrors"
"go.opentelemetry.io/otel"
otelattribute "go.opentelemetry.io/otel/attribute"
otelcodes "go.opentelemetry.io/otel/codes"
oteltrace "go.opentelemetry.io/otel/trace"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
"github.com/FerretDB/FerretDB/v2/internal/util/observability"
)
@@ -72,7 +72,7 @@ type testResult struct {
// listTestFuncs returns a sorted slice of all top-level test functions (tests, benchmarks, examples, fuzz functions)
// matching given regular expression in the specified directory and subdirectories.
func listTestFuncs(ctx context.Context, dir, re string, logger *slog.Logger) ([]string, error) {
ctx, span := otel.Tracer("").Start(ctx, "listTestFuncs")
ctx, span := otel.Tracer("").Start(ctx, "envtool.listTestFuncs")
defer span.End()
var buf bytes.Buffer
@@ -158,7 +158,7 @@ func shardTestFuncs(index, total uint, testFuncs []string) ([]string, error) {
// testArgs handles `envtool tests run` arguments and returns a slice of `go test` arguments.
func testArgs(ctx context.Context, dir string, index, total uint, run, skip string, logger *slog.Logger) ([]string, uint, error) {
ctx, span := otel.Tracer("").Start(ctx, "testArgs")
ctx, span := otel.Tracer("").Start(ctx, "envtool.testArgs")
defer span.End()
listRE := "."
@@ -309,7 +309,7 @@ func runGoTest(runCtx context.Context, opts *runGoTestOpts) (resErr error) {
totalTests = strconv.Itoa(int(opts.total))
}
runCtx, runSpan := otel.Tracer("").Start(runCtx, "run")
runCtx, runSpan := otel.Tracer("").Start(runCtx, "envtool.runGoTest")
runSpan.SetAttributes(otelattribute.String("db.ferretdb.envtool.total_tests", totalTests))
defer runSpan.End()
@@ -377,7 +377,6 @@ func runGoTest(runCtx context.Context, opts *runGoTestOpts) (resErr error) {
key = resultKey(event.Package, parentTest(event.Test))
parent := results[key]
// TODO https://github.com/FerretDB/FerretDB/issues/4465
if parent == nil {
panic(fmt.Sprintf(
"no parent test found: package=%q, test=%q, key=%q", event.Package, event.Test, key,
@@ -585,14 +584,14 @@ func testsRun(ctx context.Context, params *TestsRunParams, logger *slog.Logger)
ot, err := observability.NewOTelTraceExporter(&observability.OTelTraceExporterOpts{
Logger: logger,
Service: "envtool-tests",
URL: "http://127.0.0.1:4318/v1/traces",
Service: "envtool-tests",
})
if err != nil {
return lazyerrors.Error(err)
}
ctx, span := otel.Tracer("").Start(ctx, "testsRun")
ctx, span := otel.Tracer("").Start(ctx, "envtool.testsRun")
ctx, cancel := context.WithCancel(ctx)
done := make(chan struct{})
+31 -24
View File
@@ -37,7 +37,6 @@ import (
"github.com/FerretDB/FerretDB/v2/build/version"
"github.com/FerretDB/FerretDB/v2/internal/clientconn"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/connmetrics"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/util/ctxutil"
"github.com/FerretDB/FerretDB/v2/internal/util/debug"
@@ -75,6 +74,7 @@ var cli struct {
TLSKeyFile string `default:"" help:"TLS key file path."`
TLSCaFile string `default:"" help:"TLS CA file path."`
DataAPIAddr string `default:"" help:"Listen TCP address for HTTP Data API."`
MCPAddr string `default:"" help:"Listen TCP address for HTTP MCP server."`
} `embed:"" prefix:"listen-" group:"Interfaces"`
Proxy struct {
@@ -99,7 +99,8 @@ var cli struct {
MetricsUUID bool `default:"false" help:"Add instance UUID to all metrics." group:"Miscellaneous" negatable:""`
OTel struct {
Traces struct {
ServiceName string `default:"ferretdb" help:"OpenTelemetry service name."`
Traces struct {
URL string `default:"" help:"OpenTelemetry OTLP/HTTP traces endpoint URL (e.g. 'http://host:4318/v1/traces')."`
} `embed:"" prefix:"traces-"`
} `embed:"" prefix:"otel-" group:"Miscellaneous"`
@@ -253,6 +254,7 @@ func checkFlags(logger *slog.Logger) {
&cli.Listen.Unix,
&cli.Listen.TLS,
&cli.Listen.DataAPIAddr,
&cli.Listen.MCPAddr,
&cli.DebugAddr,
&cli.OTel.Traces.URL,
} {
@@ -274,6 +276,13 @@ func checkFlags(logger *slog.Logger) {
if !cli.Auth {
logger.WarnContext(ctx, "Authentication is disabled; the server will accept any connection")
}
if cli.Auth && cli.Listen.MCPAddr != "" {
logger.Log(
ctx, logging.LevelFatal,
"MCP server does not support authentication; please disable authentication or MCP server",
)
}
}
// dumpMetrics dumps all Prometheus metrics to stderr.
@@ -364,8 +373,6 @@ func run() {
checkFlags(logger)
setGOMAXPROCS(logger)
ctx, stop := ctxutil.SigTerm(context.Background())
go func() {
@@ -410,7 +417,7 @@ func run() {
l.LogAttrs(ctx, logging.LevelFatal, "Failed to create debug handler", logging.Error(e))
}
h.Serve(ctx)
h.Run(ctx)
}()
}
@@ -424,9 +431,8 @@ func run() {
ot, e := observability.NewOTelTraceExporter(&observability.OTelTraceExporterOpts{
Logger: l,
Service: "ferretdb",
Version: version.Get().Version,
URL: cli.OTel.Traces.URL,
Service: cli.OTel.ServiceName,
})
if e != nil {
l.LogAttrs(ctx, logging.LevelFatal, "Failed to create Otel tracer", logging.Error(e))
@@ -436,7 +442,7 @@ func run() {
}()
}
lm := connmetrics.NewListenerMetrics()
mm := middleware.NewMetrics()
{
wg.Add(1)
@@ -453,7 +459,7 @@ func run() {
DNT: os.Getenv("DO_NOT_TRACK"),
ExecName: os.Args[0],
P: stateProvider,
ConnMetrics: lm.ConnMetrics,
Metrics: mm,
L: l,
UndecidedDelay: cli.Dev.Telemetry.UndecidedDelay,
ReportInterval: cli.Dev.Telemetry.ReportInterval,
@@ -468,31 +474,32 @@ func run() {
//exhaustruct:enforce
res := setup.Setup(ctx, &setup.SetupOpts{
Logger: logger,
StateProvider: stateProvider,
ListenerMetrics: lm,
PostgreSQLURL: cli.PostgreSQLURL,
Logger: logger,
StateProvider: stateProvider,
Metrics: mm,
PostgreSQLURL: cli.PostgreSQLURL,
Auth: cli.Auth,
ReplSetName: cli.Dev.ReplSetName,
SessionCleanupInterval: 0,
TCPAddr: cli.Listen.Addr,
UnixAddr: cli.Listen.Unix,
TLSAddr: cli.Listen.TLS,
TLSCertFile: cli.Listen.TLSCertFile,
TLSKeyFile: cli.Listen.TLSKeyFile,
TLSCAFile: cli.Listen.TLSCaFile,
Mode: middleware.Mode(cli.Mode),
ProxyAddr: cli.Proxy.Addr,
ProxyTLSCertFile: cli.Proxy.TLSCertFile,
ProxyTLSKeyFile: cli.Proxy.TLSKeyFile,
ProxyTLSCAFile: cli.Proxy.TLSCaFile,
RecordsDir: cli.Dev.RecordsDir,
TCPAddr: cli.Listen.Addr,
UnixAddr: cli.Listen.Unix,
TLSAddr: cli.Listen.TLS,
TLSCertFile: cli.Listen.TLSCertFile,
TLSKeyFile: cli.Listen.TLSKeyFile,
TLSCAFile: cli.Listen.TLSCaFile,
Mode: middleware.Mode(cli.Mode),
TestRecordsDir: cli.Dev.RecordsDir,
DataAPIAddr: cli.Listen.DataAPIAddr,
MCPAddr: cli.Listen.MCPAddr,
})
if res == nil {
os.Exit(1)
@@ -500,7 +507,7 @@ func run() {
listener.Store(res.WireListener)
metricsRegisterer.MustRegister(res.WireListener)
metricsRegisterer.MustRegister(res)
res.Run(ctx)
+7 -1
View File
@@ -75,7 +75,13 @@ func (r *ReadyZ) Probe(ctx context.Context) bool {
for _, u := range urls {
r.l.DebugContext(ctx, fmt.Sprintf("Pinging %s", u))
conn, err := wireclient.Connect(ctx, u, r.l)
cleanUri, _, _, _, err := wireclient.Credentials(u)
if err != nil {
r.l.ErrorContext(ctx, "Getting credentials failed", logging.Error(err))
return false
}
conn, err := wireclient.Connect(ctx, cleanUri, r.l)
if err != nil {
r.l.ErrorContext(ctx, "Connection failed", logging.Error(err))
return false
+1
View File
@@ -31,6 +31,7 @@ services:
context: ./build/deps
dockerfile: yugabytedb.Dockerfile
container_name: ferretdb_yugabytedb
platform: linux/amd64 # TODO https://github.com/FerretDB/FerretDB/issues/5369
# postgresql.conf is populated by "ysql_pg_conf_csv" flag
# https://docs.yugabyte.com/preview/reference/configuration/yugabyted/#pass-additional-flags-to-yb-master-and-yb-tserver
command: >
+20 -20
View File
@@ -35,7 +35,6 @@ import (
"time"
"github.com/FerretDB/FerretDB/v2/build/version"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/connmetrics"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/setup"
@@ -72,7 +71,7 @@ type Config struct {
// FerretDB represents an instance of embedded FerretDB implementation.
type FerretDB struct {
tl *telemetry.Reporter
tr *telemetry.Reporter
res *setup.SetupResult
}
@@ -105,7 +104,7 @@ func New(config *Config) (*FerretDB, error) {
}
logger := logging.WithName(logging.Logger(logOutput, lOpts, ""), "ferretdb")
lm := connmetrics.NewListenerMetrics()
mm := middleware.NewMetrics()
tr, err := telemetry.NewReporter(&telemetry.NewReporterOpts{
URL: "https://beacon.ferretdb.com/",
@@ -114,7 +113,7 @@ func New(config *Config) (*FerretDB, error) {
DNT: os.Getenv("DO_NOT_TRACK"),
ExecName: os.Args[0],
P: stateProvider,
ConnMetrics: lm.ConnMetrics,
Metrics: mm,
L: logging.WithName(logger, "telemetry"),
UndecidedDelay: time.Hour,
ReportInterval: 24 * time.Hour,
@@ -125,38 +124,39 @@ func New(config *Config) (*FerretDB, error) {
//exhaustruct:enforce
res := setup.Setup(context.TODO(), &setup.SetupOpts{
Logger: logger,
StateProvider: stateProvider,
ListenerMetrics: lm,
PostgreSQLURL: config.PostgreSQLURL,
Logger: logger,
StateProvider: stateProvider,
Metrics: mm,
PostgreSQLURL: config.PostgreSQLURL,
Auth: false,
ReplSetName: "",
SessionCleanupInterval: 0,
TCPAddr: config.ListenAddr,
UnixAddr: "",
TLSAddr: "",
TLSCertFile: "",
TLSKeyFile: "",
TLSCAFile: "",
Mode: middleware.NormalMode,
ProxyAddr: "",
ProxyTLSCertFile: "",
ProxyTLSKeyFile: "",
ProxyTLSCAFile: "",
RecordsDir: "",
TCPAddr: config.ListenAddr,
UnixAddr: "",
TLSAddr: "",
TLSCertFile: "",
TLSKeyFile: "",
TLSCAFile: "",
Mode: middleware.NormalMode,
TestRecordsDir: "",
DataAPIAddr: "",
MCPAddr: "",
})
if res == nil {
return nil, fmt.Errorf("failed to create FerretDB")
}
return &FerretDB{
tl: tr,
tr: tr,
res: res,
}, nil
}
@@ -174,7 +174,7 @@ func (f *FerretDB) Run(ctx context.Context) {
go func() {
defer wg.Done()
f.tl.Run(ctx)
f.tr.Run(ctx)
}()
wg.Add(1)
+1 -1
View File
@@ -89,7 +89,7 @@ func TestFerretDB(t *testing.T) {
ListenAddr: "127.0.0.1:0",
StateDir: t.TempDir(),
LogLevel: slog.LevelDebug,
LogOutput: testutil.NewLogWriter(t),
LogOutput: t.Output(),
})
require.NoError(t, err)
+36 -33
View File
@@ -1,70 +1,73 @@
module github.com/FerretDB/FerretDB/v2
go 1.24
go 1.25
toolchain go1.24.6
toolchain go1.25.4
// replace github.com/FerretDB/wire => ../wire
require (
github.com/AlekSi/lazyerrors v0.0.0-20251102120325-67e66cc79778
github.com/AlekSi/pointer v1.2.0
github.com/FerretDB/wire v0.1.7-0.20250708055527-67b1f7c98628
github.com/FerretDB/wire v0.1.7
github.com/alecthomas/kong v1.12.1
github.com/arl/statsviz v0.6.0
github.com/arl/statsviz v0.7.2
github.com/google/uuid v1.6.0
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438
github.com/jackc/pgx/v5 v5.7.5
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6
github.com/jackc/pgx/v5 v5.7.6
github.com/modelcontextprotocol/go-sdk v0.2.0
github.com/oapi-codegen/runtime v1.1.2
github.com/pmezard/go-difflib v1.0.0
github.com/prometheus/client_golang v1.22.0
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_model v0.6.2
github.com/prometheus/common v0.65.0
github.com/stretchr/testify v1.10.0
github.com/prometheus/common v0.67.2
github.com/stretchr/testify v1.11.1
github.com/xdg-go/scram v1.1.2
go.mongodb.org/mongo-driver/v2 v2.2.2
go.opentelemetry.io/otel v1.37.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0
go.opentelemetry.io/otel/sdk v1.37.0
go.opentelemetry.io/otel/trace v1.37.0
go.uber.org/automaxprocs v1.6.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20250711192710-b903b535d3ef
golang.org/x/net v0.42.0
golang.org/x/sys v0.34.0
golang.org/x/term v0.33.0
go.mongodb.org/mongo-driver/v2 v2.4.0
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/crypto/x509roots/fallback v0.0.0-20251028130051-c0531f9c3451
golang.org/x/net v0.46.0
golang.org/x/sys v0.38.0
golang.org/x/term v0.36.0
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver v1.17.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/text v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+76 -70
View File
@@ -1,7 +1,9 @@
github.com/AlekSi/lazyerrors v0.0.0-20251102120325-67e66cc79778 h1:Hv1Z0ZnUWI1NYHXKUE16SWUcv+VyFARJHQ6dGkd2TIM=
github.com/AlekSi/lazyerrors v0.0.0-20251102120325-67e66cc79778/go.mod h1:S8uLHjY/2pJvG9qkWwDX1AVYYknPI92r2qh/f0uysr0=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/FerretDB/wire v0.1.7-0.20250708055527-67b1f7c98628 h1:QqqnK0XPruJ4Z2XyjaYZn+Up396LFCBFb/mE4u+B7dk=
github.com/FerretDB/wire v0.1.7-0.20250708055527-67b1f7c98628/go.mod h1:bXVDsxwA8ugBIfpD/eLYjHjdC7U95ygaWVX7RW2UysY=
github.com/FerretDB/wire v0.1.7 h1:yh6tL0rU8ub+KuKqiqTlFJGpdfL4V7kg/8PBPDX0sBE=
github.com/FerretDB/wire v0.1.7/go.mod h1:KjV8URsECCZWwJBBzlTvyl9LEXvnE362tL4tg52VHXI=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
@@ -11,13 +13,13 @@ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE=
github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s=
github.com/arl/statsviz v0.7.2 h1:xnuIfRiXE4kvxEcfGL+IE3mKH1BXNHuE+eJELIh7oOA=
github.com/arl/statsviz v0.7.2/go.mod h1:XlrbiT7xYT03xaW9JMMfD8KFUhBOESJwfyNJu83PbB0=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -36,20 +38,20 @@ 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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 h1:D/V0gu4zQ3cL2WKeVNVM4r2gLxGGf6McLwgXzRTo2RQ=
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
@@ -61,112 +63,116 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/modelcontextprotocol/go-sdk v0.2.0 h1:PESNYOmyM1c369tRkzXLY5hHrazj8x9CY1Xu0fLCryM=
github.com/modelcontextprotocol/go-sdk v0.2.0/go.mod h1:0sL9zUKKs2FTTkeCCVnKqbLJTw5TScefPAzojjU459E=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQgXIyI=
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
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/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver/v2 v2.2.2 h1:9cYuS3fl1Xhqwpfazso10V7BHQD58kCgtzhfAmJYz9c=
go.mongodb.org/mongo-driver/v2 v2.2.2/go.mod h1:qQkDMhCGWl3FN509DfdPd4GRBLU/41zqF/k8eTRceps=
go.mongodb.org/mongo-driver/v2 v2.4.0 h1:Oq6BmUAAFTzMeh6AonuDlgZMuAuEiUxoAD1koK5MuFo=
go.mongodb.org/mongo-driver/v2 v2.4.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250711192710-b903b535d3ef h1:EJekzaXZlPQg739ghq7w/XWZVcuAOY6mh35JX2D+7Gc=
golang.org/x/crypto/x509roots/fallback v0.0.0-20250711192710-b903b535d3ef/go.mod h1:lxN5T34bK4Z/i6cMaU7frUU57VkDXFD4Kamfl/cp9oU=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto/x509roots/fallback v0.0.0-20251028130051-c0531f9c3451 h1:ntT/VrHMoSyusO9qDIYKxtkMfluoOkLrr6AJfVRROsE=
golang.org/x/crypto/x509roots/fallback v0.0.0-20251028130051-c0531f9c3451/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+1 -1
View File
@@ -107,7 +107,7 @@ linters-settings:
alias: otelsdkresource
- pkg: go.opentelemetry.io/otel/sdk/trace
alias: otelsdktrace
- pkg: go.opentelemetry.io/otel/semconv/v1.21.0
- pkg: go.opentelemetry.io/otel/semconv/v1.34.0
alias: otelsemconv
# ineffassign
lll:
+2 -2
View File
@@ -148,10 +148,10 @@ func TestCreateUserCommand(t *testing.T) {
},
"MissingPwdOrExternal": {
payload: bson.D{
{"createUser", "mising_pwd_or_external"},
{"createUser", "missing_pwd_or_external"},
{"roles", bson.A{}},
},
user: "mising_pwd_or_external",
user: "missing_pwd_or_external",
err: &mongo.CommandError{
Code: 2,
Name: "BadValue",
+4 -1
View File
@@ -1266,7 +1266,10 @@ func TestSASLContinueOpQueryErrors(t *testing.T) {
q.FullCollectionName = "admin.$cmd"
q.NumberToReturn = -1
conn, err := wireclient.Connect(ctx, s.MongoDBURI, testutil.Logger(t))
cleanUri, _, _, _, err := wireclient.Credentials(s.MongoDBURI)
require.NoError(t, err)
conn, err := wireclient.Connect(ctx, cleanUri, testutil.Logger(t))
require.NoError(t, err)
_, resBody, err := conn.Request(ctx, q)
+2 -2
View File
@@ -108,7 +108,7 @@ func TestInsertFind(t *testing.T) {
func TestFindOtelComment(t *testing.T) {
ctx, collection := setup.Setup(t, shareddata.Scalars)
ctx, span := otel.Tracer("").Start(ctx, "TestOtelComment")
ctx, span := otel.Tracer("").Start(ctx, "TestFindOtelComment")
defer span.End()
comment, err := observability.CommentFromSpanContext(span.SpanContext())
@@ -580,7 +580,7 @@ func TestDebugCommandErrors(t *testing.T) {
}
AssertMatchesCommandError(t, expected, res.Err())
msg := `(InternalError) [msg_ferretdebugerror.go:59 handler.(*Handler).msgFerretDebugError] lazy error`
msg := `(InternalError) msg_ferretdebugerror.go:59 (handler.(*Handler).msgFerretDebugError): lazy error`
assert.EqualError(t, res.Err(), msg)
var doc bson.D
+14 -14
View File
@@ -269,48 +269,48 @@ func TestHostInfoCommand(t *testing.T) {
for _, elem := range a {
switch elem.Key {
case "system":
var elemForComparsion bson.D
var elemForComparison bson.D
for _, subElem := range elem.Value.(bson.D) {
switch subElem.Key {
case "currentTime":
assert.IsType(t, primitive.DateTime(0), subElem.Value)
elemForComparsion = append(elemForComparsion, bson.E{"currentTime", primitive.DateTime(0)})
elemForComparison = append(elemForComparison, bson.E{"currentTime", primitive.DateTime(0)})
case "hostname", "cpuArch":
assert.IsType(t, "", subElem.Value)
elemForComparsion = append(elemForComparsion, bson.E{subElem.Key, ""})
elemForComparison = append(elemForComparison, bson.E{subElem.Key, ""})
case "cpuAddrSize", "numCores":
assert.IsType(t, int32(0), subElem.Value)
elemForComparsion = append(elemForComparsion, bson.E{subElem.Key, int32(0)})
elemForComparison = append(elemForComparison, bson.E{subElem.Key, int32(0)})
case "numPhysicalCores", "numCpuSockets", "numNumaNodes", "numaEnabled", "memSizeMB", "memLimitMB":
// not implemented in FerretDB, do nothing
// TODO https://github.com/FerretDB/FerretDB-DocumentDB/issues/587
default:
elemForComparsion = append(elemForComparsion, subElem)
elemForComparison = append(elemForComparison, subElem)
}
}
actualComparable = append(actualComparable, bson.E{"system", elemForComparsion})
actualComparable = append(actualComparable, bson.E{"system", elemForComparison})
case "os":
var elemForComparsion bson.D
var elemForComparison bson.D
for _, subElem := range elem.Value.(bson.D) {
switch subElem.Key {
case "type", "name", "version":
assert.IsType(t, "", subElem.Value)
elemForComparsion = append(elemForComparsion, bson.E{subElem.Key, ""})
elemForComparison = append(elemForComparison, bson.E{subElem.Key, ""})
default:
elemForComparsion = append(elemForComparsion, subElem)
elemForComparison = append(elemForComparison, subElem)
}
}
actualComparable = append(actualComparable, bson.E{"os", elemForComparsion})
actualComparable = append(actualComparable, bson.E{"os", elemForComparison})
case "extra":
assert.IsType(t, bson.D{}, elem.Value)
@@ -349,7 +349,7 @@ func TestListCommandsCommand(t *testing.T) {
err := collection.Database().RunCommand(ctx, bson.D{{"listCommands", 42}}).Decode(&res)
require.NoError(t, err)
var actualForComparsion bson.D
var actualForComparison bson.D
for _, v := range res {
switch v.Key {
@@ -383,10 +383,10 @@ func TestListCommandsCommand(t *testing.T) {
}
}
actualForComparsion = append(actualForComparsion, bson.E{"commands", commandsComparable})
actualForComparison = append(actualForComparison, bson.E{"commands", commandsComparable})
default:
actualForComparsion = append(actualForComparsion, v)
actualForComparison = append(actualForComparison, v)
}
}
@@ -395,7 +395,7 @@ func TestListCommandsCommand(t *testing.T) {
{"ok", float64(1)},
}
AssertEqualDocuments(t, expected, actualForComparsion)
AssertEqualDocuments(t, expected, actualForComparison)
}
func TestValidateCommand(t *testing.T) {
@@ -17,8 +17,9 @@ const _CompatTestCaseResultType_name = "NonEmptyResultEmptyResult"
var _CompatTestCaseResultType_index = [...]uint8{0, 14, 25}
func (i CompatTestCaseResultType) String() string {
if i < 0 || i >= CompatTestCaseResultType(len(_CompatTestCaseResultType_index)-1) {
idx := int(i) - 0
if i < 0 || idx >= len(_CompatTestCaseResultType_index)-1 {
return "CompatTestCaseResultType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _CompatTestCaseResultType_name[_CompatTestCaseResultType_index[i]:_CompatTestCaseResultType_index[i+1]]
return _CompatTestCaseResultType_name[_CompatTestCaseResultType_index[idx]:_CompatTestCaseResultType_index[idx+1]]
}
+3
View File
@@ -170,6 +170,9 @@ func TestCurrentOpGetMore(tt *testing.T) {
}
func TestCurrentOpExplain(tt *testing.T) {
// TODO https://github.com/FerretDB/FerretDB/issues/4871
tt.Skip("https://github.com/FerretDB/FerretDB/issues/4871")
tt.Parallel()
s := setup.SetupWithOpts(tt, new(setup.SetupOpts))
+3 -4
View File
@@ -249,15 +249,14 @@ func TestFindCommandSingleBatch(t *testing.T) {
func TestFindCommandExhausted(tt *testing.T) {
tt.Parallel()
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB-DocumentDB/issues/270")
s := setup.SetupWithOpts(tt, &setup.SetupOpts{PoolSize: 1})
collection, ctx := s.Collection, s.Ctx
arr := integration.GenerateDocuments(1, 3)
_, err := collection.InsertMany(ctx, arr)
require.NoError(t, err)
require.NoError(tt, err)
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/5445")
var res bson.D
err = collection.Database().RunCommand(ctx, bson.D{
+13 -10
View File
@@ -28,6 +28,8 @@ import (
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/FerretDB/FerretDB/v2/internal/util/ctxutil"
"github.com/FerretDB/FerretDB/v2/integration"
"github.com/FerretDB/FerretDB/v2/integration/setup"
"github.com/FerretDB/FerretDB/v2/integration/shareddata"
@@ -831,16 +833,15 @@ func TestGetMoreCommandMaxTimeMSErrors(t *testing.T) {
func TestGetMoreCommandExhausted(tt *testing.T) {
s := setup.SetupWithOpts(tt, nil)
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB-DocumentDB/issues/270")
collection := s.Collection
db, ctx := collection.Database(), s.Ctx
arr := integration.GenerateDocuments(0, 10)
_, err := collection.InsertMany(ctx, arr)
require.NoError(t, err)
require.NoError(tt, err)
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/5445")
var res bson.D
err = db.RunCommand(ctx, bson.D{
@@ -1135,10 +1136,11 @@ func TestFindCommandFirstBatchMaxTimeMS(t *testing.T) {
}
t.Run("GetMore", func(tt *testing.T) {
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB-DocumentDB/issues/270")
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/5445")
for i := 0; i < 2; i++ {
time.Sleep(100 * time.Millisecond)
ctxutil.Sleep(ctx, 100*time.Millisecond)
var res bson.D
err = collection.Database().RunCommand(ctx, getMoreCmd).Decode(&res)
require.NoError(t, err)
@@ -1158,9 +1160,10 @@ func TestFindCommandFirstBatchMaxTimeMS(t *testing.T) {
})
t.Run("GetMoreEmpty", func(tt *testing.T) {
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB-DocumentDB/issues/270")
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/5445")
ctxutil.Sleep(ctx, 100*time.Millisecond)
time.Sleep(100 * time.Millisecond)
var res bson.D
err = collection.Database().RunCommand(ctx, getMoreCmd).Decode(&res)
require.NoError(t, err)
@@ -1230,7 +1233,7 @@ func TestGetMoreCommandAfterInsertion(t *testing.T) {
}
t.Run("GetMore", func(tt *testing.T) {
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB-DocumentDB/issues/270")
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/5445")
for i := 0; i < 2; i++ {
var res bson.D
@@ -1252,7 +1255,7 @@ func TestGetMoreCommandAfterInsertion(t *testing.T) {
})
t.Run("GetMoreEmpty", func(tt *testing.T) {
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB-DocumentDB/issues/270")
t := setup.FailsForFerretDB(tt, "https://github.com/FerretDB/FerretDB/issues/5445")
var res bson.D
err = collection.Database().RunCommand(ctx, getMoreCmd).Decode(&res)
+37 -33
View File
@@ -1,72 +1,76 @@
module github.com/FerretDB/FerretDB/v2/integration
go 1.24
go 1.25
toolchain go1.24.6
toolchain go1.25.4
replace github.com/FerretDB/FerretDB/v2 => ../
// replace github.com/FerretDB/wire => ../../wire
require (
github.com/AlekSi/lazyerrors v0.0.0-20251102120325-67e66cc79778
github.com/AlekSi/pointer v1.2.0
github.com/FerretDB/FerretDB/v2 v2.0.0-00010101000000-000000000000
github.com/FerretDB/wire v0.1.7-0.20250708055527-67b1f7c98628
github.com/FerretDB/wire v0.1.7
github.com/FerretDB/xfail v0.1.0
github.com/google/uuid v1.6.0
github.com/prometheus/client_golang v1.22.0
github.com/stretchr/testify v1.10.0
github.com/prometheus/client_golang v1.23.2
github.com/stretchr/testify v1.11.1
github.com/xdg-go/scram v1.1.2
go.mongodb.org/mongo-driver v1.17.4
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.62.0
go.opentelemetry.io/otel v1.37.0
go.mongodb.org/mongo-driver v1.17.6
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.63.0
go.opentelemetry.io/otel v1.38.0
)
require (
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/arl/statsviz v0.6.0 // indirect
github.com/arl/statsviz v0.7.2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.7.5 // indirect
github.com/jackc/pgx/v5 v5.7.6 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/modelcontextprotocol/go-sdk v0.2.0 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oapi-codegen/runtime v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/common v0.67.2 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
go.mongodb.org/mongo-driver/v2 v2.2.2 // indirect
go.mongodb.org/mongo-driver/v2 v2.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
go.opentelemetry.io/otel/trace v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
golang.org/x/crypto v0.40.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/text v0.27.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/grpc v1.73.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/net v0.46.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.30.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+78 -68
View File
@@ -1,19 +1,21 @@
github.com/AlekSi/lazyerrors v0.0.0-20251102120325-67e66cc79778 h1:Hv1Z0ZnUWI1NYHXKUE16SWUcv+VyFARJHQ6dGkd2TIM=
github.com/AlekSi/lazyerrors v0.0.0-20251102120325-67e66cc79778/go.mod h1:S8uLHjY/2pJvG9qkWwDX1AVYYknPI92r2qh/f0uysr0=
github.com/AlekSi/pointer v1.2.0 h1:glcy/gc4h8HnG2Z3ZECSzZ1IX1x2JxRVuDzaJwQE0+w=
github.com/AlekSi/pointer v1.2.0/go.mod h1:gZGfd3dpW4vEc/UlyfKKi1roIqcCgwOIvb0tSNSBle0=
github.com/FerretDB/wire v0.1.7-0.20250708055527-67b1f7c98628 h1:QqqnK0XPruJ4Z2XyjaYZn+Up396LFCBFb/mE4u+B7dk=
github.com/FerretDB/wire v0.1.7-0.20250708055527-67b1f7c98628/go.mod h1:bXVDsxwA8ugBIfpD/eLYjHjdC7U95ygaWVX7RW2UysY=
github.com/FerretDB/wire v0.1.7 h1:yh6tL0rU8ub+KuKqiqTlFJGpdfL4V7kg/8PBPDX0sBE=
github.com/FerretDB/wire v0.1.7/go.mod h1:KjV8URsECCZWwJBBzlTvyl9LEXvnE362tL4tg52VHXI=
github.com/FerretDB/xfail v0.1.0 h1:NmwdtwCmQ12X4VutLL+XilG/vbLRbG6RUVQabnI1JiQ=
github.com/FerretDB/xfail v0.1.0/go.mod h1:u34yzRVZdvuc/fjegLYi+FztscWts9pP9ZUJ7wY9OyE=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE=
github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s=
github.com/arl/statsviz v0.7.2 h1:xnuIfRiXE4kvxEcfGL+IE3mKH1BXNHuE+eJELIh7oOA=
github.com/arl/statsviz v0.7.2/go.mod h1:XlrbiT7xYT03xaW9JMMfD8KFUhBOESJwfyNJu83PbB0=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -32,18 +34,18 @@ 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/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438 h1:Dj0L5fhJ9F82ZJyVOmBx6msDp/kfd1t9GRfny/mfJA0=
github.com/jackc/pgerrcode v0.0.0-20240316143900-6e2875d9b438/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 h1:D/V0gu4zQ3cL2WKeVNVM4r2gLxGGf6McLwgXzRTo2RQ=
github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.7.5 h1:JHGfMnQY+IEtGM63d+NGMjoRpysB2JBwDr5fsngwmJs=
github.com/jackc/pgx/v5 v5.7.5/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
@@ -55,6 +57,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/modelcontextprotocol/go-sdk v0.2.0 h1:PESNYOmyM1c369tRkzXLY5hHrazj8x9CY1Xu0fLCryM=
github.com/modelcontextprotocol/go-sdk v0.2.0/go.mod h1:0sL9zUKKs2FTTkeCCVnKqbLJTw5TScefPAzojjU459E=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -63,102 +67,108 @@ github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQ
github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg=
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/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8=
github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
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/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver/v2 v2.2.2 h1:9cYuS3fl1Xhqwpfazso10V7BHQD58kCgtzhfAmJYz9c=
go.mongodb.org/mongo-driver/v2 v2.2.2/go.mod h1:qQkDMhCGWl3FN509DfdPd4GRBLU/41zqF/k8eTRceps=
go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss=
go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver/v2 v2.4.0 h1:Oq6BmUAAFTzMeh6AonuDlgZMuAuEiUxoAD1koK5MuFo=
go.mongodb.org/mongo-driver/v2 v2.4.0/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.62.0 h1:IDI0wUpSFq/RUr1rRTHT7nF/Mr3V4kENTn05P39fH7k=
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.62.0/go.mod h1:PxUlDgXfAHM+OrUrqs3pbc2OR59ZLDSe9r5NiS0B/4E=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.63.0 h1:6IOE2J+3fFJKJ/8riwf6XrazdEr261L8TEY6T0uSjEM=
go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo v0.63.0/go.mod h1:kbPDiVJGSE06bBx6sJlDMXFQ15/gnY4MA1ppkso9LYE=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+2 -2
View File
@@ -475,7 +475,7 @@ func TestCreateIndexesCompatUnique(t *testing.T) {
insertDoc: bson.D{{"v", "value"}},
new: true,
// This test case passes only because of our hack for
// TODO https://github.com/microsoft/documentdb/issues/25
// TODO https://github.com/documentdb/documentdb/issues/25
// failsForFerretDB: "https://github.com/FerretDB/FerretDB-DocumentDB/issues/296",
},
"NotExistingFieldIndex": {
@@ -515,7 +515,7 @@ func TestCreateIndexesCompatUnique(t *testing.T) {
},
insertDoc: bson.D{{"v", int32(42)}},
// This test case passes only because of our hack for
// TODO https://github.com/microsoft/documentdb/issues/25
// TODO https://github.com/documentdb/documentdb/issues/25
// failsForFerretDB: "https://github.com/FerretDB/FerretDB-DocumentDB/issues/296",
},
} {
+2 -2
View File
@@ -116,10 +116,10 @@ func TestQueryArrayCompatDotNotation(t *testing.T) {
"FieldArrayIndex": {
filter: bson.D{{"v.foo[0]", int32(42)}},
},
"FieldArrayAsterix": {
"FieldArrayAsterisk": {
filter: bson.D{{"v.foo[*]", int32(42)}},
},
"FieldAsterix": {
"FieldAsterisk": {
filter: bson.D{{"v.*", int32(42)}},
},
"FieldAt": {
+2 -2
View File
@@ -125,14 +125,14 @@ func testQueryCompatWithProviders(t *testing.T, providers shareddata.Providers,
t = setup.FailsForFerretDB(tt, tc.failsForFerretDB)
}
targetIdx, tagetErr := targetCollection.Indexes().CreateOne(ctx, mongo.IndexModel{
targetIdx, targetErr := targetCollection.Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: bson.D{{"v", 1}},
})
compatIdx, compatErr := compatCollection.Indexes().CreateOne(ctx, mongo.IndexModel{
Keys: bson.D{{"v", 1}},
})
require.NoError(t, tagetErr)
require.NoError(t, targetErr)
require.NoError(t, compatErr)
require.Equal(t, compatIdx, targetIdx)
@@ -17,6 +17,7 @@ package session
import (
"context"
"fmt"
"net/url"
"testing"
"github.com/FerretDB/wire"
@@ -890,14 +891,17 @@ func createKillSessionUser(t *testing.T, ctx context.Context, db *mongo.Database
}).Err()
require.NoError(t, err)
conn, err := wireclient.Connect(ctx, mongoDBURI, testutil.Logger(t))
clearUri, _, _, authMechanism, err := wireclient.Credentials(mongoDBURI)
require.NoError(t, err)
conn, err := wireclient.Connect(ctx, clearUri, testutil.Logger(t))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, conn.Close())
})
err = conn.Login(ctx, username, password, db.Name())
err = conn.Login(ctx, url.UserPassword(username, password), db.Name(), authMechanism)
require.NoError(t, err)
return conn
+18 -6
View File
@@ -19,6 +19,7 @@ import (
"crypto/sha256"
"encoding/base64"
"fmt"
"net/url"
"testing"
"github.com/FerretDB/wire"
@@ -43,14 +44,17 @@ func TestSessionConnection(t *testing.T) {
ctx, collection, db, conn1 := s.Ctx, s.Collection, s.Collection.Database(), s.WireConn
cName, dbName := collection.Name(), db.Name()
conn2, err := wireclient.Connect(ctx, s.MongoDBURI, testutil.Logger(t))
clearUri, _, _, authMechanism, err := wireclient.Credentials(s.MongoDBURI)
require.NoError(t, err)
conn2, err := wireclient.Connect(ctx, clearUri, testutil.Logger(t))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, conn2.Close())
})
err = conn2.Login(ctx, "username", "password", "admin")
err = conn2.Login(ctx, url.UserPassword("username", "password"), "admin", authMechanism)
require.NoError(t, err)
_, err = collection.InsertMany(ctx, bson.A{
@@ -122,10 +126,15 @@ func TestSessionConnection(t *testing.T) {
t.Run("CloseConnection", func(t *testing.T) {
var anotherConn *wireclient.Conn
anotherConn, err = wireclient.Connect(ctx, s.MongoDBURI, testutil.Logger(t))
var creds *url.Userinfo
clearUri, creds, _, authMechanism, err = wireclient.Credentials(s.MongoDBURI)
require.NoError(t, err)
err = anotherConn.Login(ctx, "username", "password", "admin")
anotherConn, err = wireclient.Connect(ctx, clearUri, testutil.Logger(t))
require.NoError(t, err)
err = anotherConn.Login(ctx, creds, "admin", authMechanism)
require.NoError(t, err)
sessionID := startSession(t, ctx, anotherConn)
@@ -380,14 +389,17 @@ func TestSessionConnectionDifferentUser(t *testing.T) {
}).Err()
require.NoError(t, err)
userConn, err := wireclient.Connect(ctx, s.MongoDBURI, testutil.Logger(t))
clearUri, _, _, authMechanism, err := wireclient.Credentials(s.MongoDBURI)
require.NoError(t, err)
userConn, err := wireclient.Connect(ctx, clearUri, testutil.Logger(t))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, userConn.Close())
})
err = userConn.Login(ctx, user, pass, dbName)
err = userConn.Login(ctx, url.UserPassword(user, pass), "admin", authMechanism)
require.NoError(t, err)
t.Cleanup(func() {
+3 -3
View File
@@ -22,6 +22,7 @@ import (
"strings"
"testing"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wireclient"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/mongo"
@@ -29,7 +30,6 @@ import (
"go.opentelemetry.io/contrib/instrumentation/go.mongodb.org/mongo-driver/mongo/otelmongo"
"go.opentelemetry.io/otel"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/testutil"
)
@@ -95,7 +95,7 @@ func makeClient(ctx context.Context, uri string, disableOtel bool) (*mongo.Clien
func setupClient(tb testing.TB, ctx context.Context, uri string, disableOtel bool) *mongo.Client {
tb.Helper()
ctx, span := otel.Tracer("").Start(ctx, "setupClient")
ctx, span := otel.Tracer("").Start(ctx, "setup.setupClient")
defer span.End()
u, err := url.Parse(uri)
@@ -137,7 +137,7 @@ func setupClient(tb testing.TB, ctx context.Context, uri string, disableOtel boo
func setupWireConn(tb testing.TB, ctx context.Context, uri string, l *slog.Logger) *wireclient.Conn {
tb.Helper()
ctx, span := otel.Tracer("").Start(ctx, "setupWireConn")
ctx, span := otel.Tracer("").Start(ctx, "setup.setupWireConn")
defer span.End()
conn, err := wireclient.Connect(ctx, uri, l)
+15 -16
View File
@@ -83,7 +83,7 @@ func listenerMongoDBURI(tb testing.TB, hostPort, unixSocketPath string) string {
func setupListener(tb testing.TB, ctx context.Context, opts *ListenerOpts, logger *slog.Logger) string {
tb.Helper()
ctx, span := otel.Tracer("").Start(ctx, "setupListener")
ctx, span := otel.Tracer("").Start(ctx, "setup.setupListener")
defer span.End()
require.Empty(tb, *targetURLF, "-target-url must be empty for in-process FerretDB")
@@ -109,29 +109,28 @@ func setupListener(tb testing.TB, ctx context.Context, opts *ListenerOpts, logge
//exhaustruct:enforce
wireOpts := &setup.SetupOpts{
Logger: logger,
StateProvider: sp,
ListenerMetrics: listenerMetrics,
PostgreSQLURL: *postgreSQLURLF,
Logger: logger,
StateProvider: sp,
Metrics: middleware.NewMetrics(),
PostgreSQLURL: *postgreSQLURLF,
Auth: true,
ReplSetName: "", // TODO https://github.com/FerretDB/FerretDB-DocumentDB/issues/566
SessionCleanupInterval: opts.SessionCleanupInterval,
TCPAddr: "",
UnixAddr: "",
TLSAddr: "",
TLSCertFile: "",
TLSKeyFile: "",
TLSCAFile: "",
Mode: middleware.NormalMode,
ProxyAddr: "",
ProxyTLSCertFile: "",
ProxyTLSKeyFile: "",
ProxyTLSCAFile: "",
RecordsDir: testutil.TmpRecordsDir,
TCPAddr: "",
UnixAddr: "",
TLSAddr: "",
TLSCertFile: "",
TLSKeyFile: "",
TLSCAFile: "",
Mode: middleware.NormalMode,
TestRecordsDir: testutil.TmpRecordsDir,
DataAPIAddr: "",
}
@@ -156,7 +155,7 @@ func setupListener(tb testing.TB, ctx context.Context, opts *ListenerOpts, logge
go func() {
defer close(runDone)
runCtx, runSpan := otel.Tracer("").Start(ctx, "setupListener.Run")
runCtx, runSpan := otel.Tracer("").Start(ctx, "setup.setupListener.Run")
defer runSpan.End()
res.Run(runCtx)
+8 -5
View File
@@ -147,7 +147,7 @@ func SetupWithOpts(tb testing.TB, opts *SetupOpts) *SetupResult {
ctx, cancel := context.WithCancel(testutil.Ctx(tb))
setupCtx, span := otel.Tracer("").Start(ctx, "SetupWithOpts")
setupCtx, span := otel.Tracer("").Start(ctx, "setup.SetupWithOpts")
defer span.End()
if opts == nil {
@@ -204,7 +204,10 @@ func SetupWithOpts(tb testing.TB, opts *SetupOpts) *SetupResult {
var conn *wireclient.Conn
if opts.WireConn != WireConnNoConn {
conn = setupWireConn(tb, setupCtx, uri, testutil.Logger(tb))
clearUri, creds, _, authMechanism, err := wireclient.Credentials(uri)
require.NoError(tb, err)
conn = setupWireConn(tb, setupCtx, clearUri, testutil.Logger(tb))
if opts.WireConn == WireConnAuth {
u, err := url.Parse(uri)
@@ -216,7 +219,7 @@ func SetupWithOpts(tb testing.TB, opts *SetupOpts) *SetupResult {
pass, _ := u.User.Password()
require.NotEmpty(tb, pass)
require.NoError(tb, conn.Login(ctx, user, pass, "admin"))
require.NoError(tb, conn.Login(ctx, creds, "admin", authMechanism))
}
}
@@ -245,7 +248,7 @@ func Setup(tb testing.TB, providers ...shareddata.Provider) (context.Context, *m
func setupCollection(tb testing.TB, ctx context.Context, client *mongo.Client, opts *SetupOpts) *mongo.Collection {
tb.Helper()
ctx, span := otel.Tracer("").Start(ctx, "setupCollection")
ctx, span := otel.Tracer("").Start(ctx, "setup.setupCollection")
defer span.End()
var ownDatabase bool
@@ -306,7 +309,7 @@ func setupCollection(tb testing.TB, ctx context.Context, client *mongo.Client, o
func InsertProviders(tb testing.TB, ctx context.Context, collection *mongo.Collection, providers ...shareddata.Provider) (inserted bool) {
tb.Helper()
ctx, span := otel.Tracer("").Start(ctx, "InsertProviders")
ctx, span := otel.Tracer("").Start(ctx, "setup.InsertProviders")
defer span.End()
for _, provider := range providers {
+2 -2
View File
@@ -65,7 +65,7 @@ func SetupCompatWithOpts(tb testing.TB, opts *SetupCompatOpts) *SetupCompatResul
ctx, cancel := context.WithCancel(testutil.Ctx(tb))
setupCtx, span := otel.Tracer("").Start(ctx, "SetupCompatWithOpts")
setupCtx, span := otel.Tracer("").Start(ctx, "setup.SetupCompatWithOpts")
defer span.End()
if opts == nil {
@@ -129,7 +129,7 @@ func SetupCompat(tb testing.TB) (context.Context, []*mongo.Collection, []*mongo.
func setupCompatCollections(tb testing.TB, ctx context.Context, client *mongo.Client, opts *SetupCompatOpts, backend string) []*mongo.Collection {
tb.Helper()
ctx, span := otel.Tracer("").Start(ctx, "setupCompatCollections")
ctx, span := otel.Tracer("").Start(ctx, "setup.setupCompatCollections")
defer span.End()
database := client.Database(opts.databaseName)
+2 -8
View File
@@ -27,7 +27,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"go.mongodb.org/mongo-driver/mongo"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/connmetrics"
"github.com/FerretDB/FerretDB/v2/internal/util/debug"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/observability"
@@ -35,9 +34,6 @@ import (
"github.com/FerretDB/FerretDB/v2/integration/shareddata"
)
// listenerMetrics are shared between tests.
var listenerMetrics = connmetrics.NewListenerMetrics()
// shutdown cancels context passed to startup components.
var shutdown context.CancelFunc
@@ -63,8 +59,6 @@ func Startup() {
*debugSetupF = true
}
prometheus.DefaultRegisterer.MustRegister(listenerMetrics)
ctx, shutdown = context.WithCancel(ctx)
startupWG.Add(1)
@@ -82,7 +76,7 @@ func Startup() {
l.LogAttrs(ctx, logging.LevelFatal, "Failed to create debug handler", logging.Error(err))
}
h.Serve(ctx)
h.Run(ctx)
}()
if *otelTracesURLF != "" {
@@ -93,8 +87,8 @@ func Startup() {
ot, err := observability.NewOTelTraceExporter(&observability.OTelTraceExporterOpts{
Logger: logging.WithName(l, "otel"),
Service: "integration-tests",
URL: *otelTracesURLF,
Service: "integration-tests",
})
if err != nil {
l.LogAttrs(ctx, logging.LevelFatal, "Failed to create Otel tracer", logging.Error(err))
+1 -1
View File
@@ -1446,7 +1446,7 @@ func TestUpdateFieldCompatSet(t *testing.T) {
doublesProvider := []shareddata.Provider{shareddata.Doubles}
testCases := map[string]updateCompatTestCase{
"SetNullInExisingField": {
"SetNullInExistingField": {
update: bson.D{{"$set", bson.D{{"v", nil}}}},
},
"DuplicateKeys": {
+35 -499
View File
@@ -29,95 +29,24 @@ import (
"net/netip"
"os"
"path/filepath"
"strings"
"sync/atomic"
"time"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire"
"github.com/FerretDB/wire/wirebson"
"github.com/pmezard/go-difflib/difflib"
"go.opentelemetry.io/otel"
otelattribute "go.opentelemetry.io/otel/attribute"
otelcodes "go.opentelemetry.io/otel/codes"
oteltrace "go.opentelemetry.io/otel/trace"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/conninfo"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/connmetrics"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/handler/proxy"
"github.com/FerretDB/FerretDB/v2/internal/mongoerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
"github.com/FerretDB/FerretDB/v2/internal/util/observability"
)
// conn represents client connection.
type conn struct {
netConn net.Conn
mode middleware.Mode
l *slog.Logger
h middleware.Handler
m *connmetrics.ConnMetrics
proxy *proxy.Handler
lastRequestID atomic.Int32
m *middleware.Middleware
testRecordsDir string // if empty, no records are created
}
// newConnOpts represents newConn options.
type newConnOpts struct {
netConn net.Conn
mode middleware.Mode
l *slog.Logger
handler middleware.Handler
connMetrics *connmetrics.ConnMetrics
proxyAddr string
proxyTLSCertFile string
proxyTLSKeyFile string
proxyTLSCAFile string
testRecordsDir string // if empty, no records are created
}
// newConn creates a new client connection for given net.Conn.
func newConn(opts *newConnOpts) (*conn, error) {
if opts.mode == "" {
panic("mode required")
}
if opts.handler == nil {
panic("handler required")
}
var p *proxy.Handler
if opts.mode != middleware.NormalMode {
var err error
proxyOpts := &proxy.NewOpts{
Addr: opts.proxyAddr,
CertFile: opts.proxyTLSCertFile,
KeyFile: opts.proxyTLSKeyFile,
CAFile: opts.proxyTLSCAFile,
L: logging.WithName(opts.l, "proxy"),
}
p, err = proxy.New(proxyOpts)
if err != nil {
return nil, lazyerrors.Error(err)
}
}
return &conn{
netConn: opts.netConn,
mode: opts.mode,
l: opts.l,
h: opts.handler,
m: opts.connMetrics,
proxy: p,
testRecordsDir: opts.testRecordsDir,
}, nil
}
// run runs the client connection until ctx is canceled, client disconnects,
// or fatal error or panic is encountered.
//
@@ -125,54 +54,32 @@ func newConn(opts *newConnOpts) (*conn, error) {
//
// The caller is responsible for closing the underlying net.Conn.
func (c *conn) run(ctx context.Context) (err error) {
ctx, cancel := context.WithCancelCause(ctx)
defer func() {
cancel(lazyerrors.Errorf("run exits: %w", err))
}()
connInfo := conninfo.New()
defer connInfo.Close()
if c.netConn.RemoteAddr().Network() != "unix" {
connInfo.Peer, err = netip.ParseAddrPort(c.netConn.RemoteAddr().String())
if err != nil {
return
}
}
ctx = conninfo.Ctx(ctx, connInfo)
// That's not the best – it makes proxy handler very different from the main handler.
// Instead, proxy handler should map connections based on connInfo.
// TODO https://github.com/FerretDB/FerretDB/issues/4965
if c.proxy != nil {
go c.proxy.Run(ctx)
}
done := make(chan struct{})
// handle ctx cancellation
go func() {
select {
case <-done:
// nothing, let goroutine exit
case <-ctx.Done():
// unblocks ReadMessage below; any non-zero past value will do
if e := c.netConn.SetDeadline(time.Unix(0, 0)); e != nil {
c.l.WarnContext(ctx, fmt.Sprintf("Failed to set deadline: %s", e))
}
}
}()
ci := conninfo.New()
ctx, cancel := context.WithCancelCause(conninfo.Ctx(ctx, ci))
defer func() {
if p := recover(); p != nil {
c.l.LogAttrs(ctx, logging.LevelDPanic, fmt.Sprint(p), logging.Error(err))
err = errors.New("panic")
err = lazyerrors.New("panic")
}
// let goroutine above exit
close(done)
cancel(lazyerrors.Errorf("run exits: %w", err))
ci.Close()
}()
if c.netConn.RemoteAddr().Network() != "unix" {
ci.Peer, err = netip.ParseAddrPort(c.netConn.RemoteAddr().String())
if err != nil {
err = lazyerrors.Error(err)
return
}
}
go func() {
<-ctx.Done()
// unblocks ReadMessage in the processRequest below; any non-zero past value will do
_ = c.netConn.SetDeadline(time.Unix(0, 0))
}()
bufr := bufio.NewReader(c.netConn)
@@ -203,327 +110,43 @@ func (c *conn) run(ctx context.Context) (err error) {
bufw := bufio.NewWriter(c.netConn)
defer func() {
if e := bufw.Flush(); err == nil {
err = e
}
// c.netConn is closed by the caller
}()
for {
if err = c.processMessage(ctx, bufr, bufw); err != nil {
if err = c.processRequest(ctx, bufr, bufw); err != nil {
return
}
}
}
// processMessage reads the request, routes the request based on the operation mode
// and writes the response.
// processRequest reads the request, passes it to the middleware, and writes the response.
//
// Any error returned indicates the connection should be closed.
func (c *conn) processMessage(ctx context.Context, bufr *bufio.Reader, bufw *bufio.Writer) error {
func (c *conn) processRequest(ctx context.Context, bufr *bufio.Reader, bufw *bufio.Writer) error {
reqHeader, reqBody, err := wire.ReadMessage(bufr)
if err != nil {
return err
return lazyerrors.Error(err)
}
if c.l.Enabled(ctx, slog.LevelDebug) {
c.l.DebugContext(ctx, "Request header: "+reqHeader.String())
c.l.DebugContext(ctx, "Request message:\n"+reqBody.StringIndent())
req, err := middleware.RequestWire(reqHeader, reqBody)
if err != nil {
return lazyerrors.Error(err)
}
// diffLogLevel provides the level of logging for the diff between the "normal" and "proxy" responses.
// It is set to the highest level of logging used to log response.
diffLogLevel := slog.LevelDebug
// send request to proxy first (unless we are in normal mode)
// because FerretDB's handling could modify reqBody's documents,
// creating a data race
var proxyHeader *wire.MsgHeader
var proxyBody wire.MsgBody
if c.mode != middleware.NormalMode {
if c.proxy == nil {
panic("proxy addr was nil")
}
// TODO https://github.com/FerretDB/FerretDB/issues/1997
var req *middleware.Request
if req, err = middleware.RequestWire(reqHeader, reqBody); err != nil {
return lazyerrors.Error(err)
}
var resp *middleware.Response
if resp, err = c.proxy.Handle(ctx, req); err != nil {
return lazyerrors.Error(err)
}
proxyHeader = resp.WireHeader()
proxyBody = resp.WireBody()
resp := c.m.Handle(ctx, req)
if resp == nil {
return lazyerrors.New("middleware returned nil response")
}
// handle request unless we are in proxy mode
var resCloseConn bool
var resHeader *wire.MsgHeader
var resBody wire.MsgBody
if c.mode != middleware.ProxyMode {
resHeader, resBody, resCloseConn = c.route(ctx, reqHeader, reqBody)
if level := c.logResponse(ctx, "Response", resHeader, resBody, resCloseConn); level > diffLogLevel {
diffLogLevel = level
}
}
// log proxy response after the normal response to make it less confusing
if c.mode != middleware.NormalMode {
if level := c.logResponse(ctx, "Proxy response", proxyHeader, proxyBody, false); level > diffLogLevel {
diffLogLevel = level
}
}
// diff in diff mode
if c.l.Enabled(ctx, diffLogLevel) && (c.mode == middleware.DiffNormalMode || c.mode == middleware.DiffProxyMode) {
if err = c.logDiff(ctx, resHeader, proxyHeader, resBody, proxyBody, diffLogLevel); err != nil {
return err
}
}
// replace response with one from proxy in proxy and diff-proxy modes
if c.mode == middleware.ProxyMode || c.mode == middleware.DiffProxyMode {
resHeader = proxyHeader
resBody = proxyBody
}
if resHeader == nil || resBody == nil {
panic("no response to send to client")
}
if err = wire.WriteMessage(bufw, resHeader, resBody); err != nil {
c.l.DebugContext(ctx, "Failed to write message", logging.Error(err))
return err
if err = wire.WriteMessage(bufw, resp.WireHeader(), resp.WireBody()); err != nil {
return lazyerrors.Error(err)
}
if err = bufw.Flush(); err != nil {
c.l.DebugContext(ctx, "Failed to flush buffer", logging.Error(err))
return err
}
if resCloseConn {
err = errors.New("fatal error")
c.l.DebugContext(ctx, "Connection closed unexpectedly", logging.Error(err))
return err
return lazyerrors.Error(err)
}
return nil
}
// route sends request to a handler's command based on the op code provided in the request header.
//
// The passed context is canceled when the client disconnects.
//
// Handlers to which it routes, should not panic on bad input, but may do so in "impossible" cases.
// They also should not use recover(). That allows us to use fuzzing.
//
// Returned resBody can be nil.
func (c *conn) route(connCtx context.Context, reqHeader *wire.MsgHeader, reqBody wire.MsgBody) (resHeader *wire.MsgHeader, resBody wire.MsgBody, closeConn bool) { //nolint:lll // argument list is too long
var span oteltrace.Span
var command, result, argument string
defer func() {
if result == "" {
result = "panic"
}
if argument == "" {
argument = "unknown"
}
c.m.Responses.WithLabelValues(resHeader.OpCode.String(), command, argument, result).Inc()
must.NotBeZero(span)
if result != "ok" {
span.SetStatus(otelcodes.Error, result)
}
span.SetName(command)
span.SetAttributes(
otelattribute.String("db.ferretdb.opcode", resHeader.OpCode.String()),
otelattribute.Int("db.ferretdb.request_id", int(resHeader.ResponseTo)),
otelattribute.String("db.ferretdb.argument", argument),
)
span.End()
}()
resHeader = new(wire.MsgHeader)
var err error
switch reqHeader.OpCode {
case wire.OpCodeMsg:
msg := reqBody.(*wire.OpMsg)
resHeader.OpCode = wire.OpCodeMsg
var doc *wirebson.Document
if doc, err = msg.Section0(); err == nil {
command = doc.Command()
}
if err == nil {
comment, _ := doc.Get("comment").(string)
spanCtx, e := observability.SpanContextFromComment(comment)
if e == nil {
connCtx = oteltrace.ContextWithRemoteSpanContext(connCtx, spanCtx)
} else {
c.l.DebugContext(connCtx, "Failed to extract span context from comment", logging.Error(e))
}
}
connCtx, span = otel.Tracer("").Start(connCtx, "")
if err == nil {
req, _ := middleware.RequestWire(reqHeader, msg)
var resp *middleware.Response
if resp, err = c.h.Handle(connCtx, req); err == nil {
resBody = resp.WireBody()
}
}
case wire.OpCodeQuery:
query := reqBody.(*wire.OpQuery)
resHeader.OpCode = wire.OpCodeReply
var q *wirebson.Document
if q, err = query.Query(); err == nil {
command = q.Command()
}
connCtx, span = otel.Tracer("").Start(connCtx, "")
if err == nil {
req, _ := middleware.RequestWire(reqHeader, query)
var resp *middleware.Response
if resp, err = c.h.Handle(connCtx, req); err == nil {
resBody = resp.WireBody()
}
}
case wire.OpCodeReply:
fallthrough
case wire.OpCodeUpdate:
fallthrough
case wire.OpCodeInsert:
fallthrough
case wire.OpCodeGetByOID:
fallthrough
case wire.OpCodeGetMore:
fallthrough
case wire.OpCodeDelete:
fallthrough
case wire.OpCodeKillCursors:
fallthrough
case wire.OpCodeCompressed:
connCtx, span = otel.Tracer("").Start(connCtx, "")
err = lazyerrors.Errorf("unhandled OpCode %s", reqHeader.OpCode)
default:
connCtx, span = otel.Tracer("").Start(connCtx, "")
err = lazyerrors.Errorf("unexpected OpCode %s", reqHeader.OpCode)
}
if command == "" {
command = "unknown"
}
c.m.Requests.WithLabelValues(reqHeader.OpCode.String(), command).Inc()
// set body for error
if err != nil {
switch resHeader.OpCode {
case wire.OpCodeMsg:
protoErr := mongoerrors.Make(connCtx, err, "", c.l)
resBody = protoErr.Msg()
result = protoErr.Name
argument = protoErr.Argument
case wire.OpCodeReply:
protoErr := mongoerrors.Make(connCtx, err, "", c.l)
resBody = protoErr.Reply()
result = protoErr.Name
argument = protoErr.Argument
case wire.OpCodeQuery:
fallthrough
case wire.OpCodeUpdate:
fallthrough
case wire.OpCodeInsert:
fallthrough
case wire.OpCodeGetByOID:
fallthrough
case wire.OpCodeGetMore:
fallthrough
case wire.OpCodeDelete:
fallthrough
case wire.OpCodeKillCursors:
fallthrough
case wire.OpCodeCompressed:
// do not panic to make fuzzing easier
closeConn = true
result = "unhandled"
c.l.ErrorContext(
connCtx,
"Handler error for unhandled response opcode",
logging.Error(err),
slog.Any("opcode", resHeader.OpCode),
)
return
default:
// do not panic to make fuzzing easier
closeConn = true
result = "unexpected"
c.l.ErrorContext(
connCtx,
"Handler error for unexpected response opcode",
logging.Error(err),
slog.Any("opcode", resHeader.OpCode),
)
return
}
}
// Don't call MarshalBinary there. Fix header in the caller?
// TODO https://github.com/FerretDB/FerretDB/issues/273
b, err := resBody.MarshalBinary()
if err != nil {
result = ""
panic(err)
}
resHeader.MessageLength = int32(wire.MsgHeaderLen + len(b))
resHeader.RequestID = c.lastRequestID.Add(1)
resHeader.ResponseTo = reqHeader.RequestID
if result == "" {
result = "ok"
}
return
}
// renamePartialFile takes over an open file `f` and closes it.
// It uses the given error to check if the connection was closed by the client,
// if so the given file is renamed to a name generated by hash,
@@ -558,90 +181,3 @@ func (c *conn) renamePartialFile(ctx context.Context, f *os.File, h hash.Hash, e
c.l.WarnContext(ctx, "Failed to rename file", logging.Error(e))
}
}
// logResponse logs response's header and body and returns the log level that was used.
//
// The param `who` will be used in logs and should represent the type of the response,
// for example "Response" or "Proxy Response".
func (c *conn) logResponse(ctx context.Context, who string, resHeader *wire.MsgHeader, resBody wire.MsgBody, closeConn bool) slog.Level { //nolint:lll // for readability
level := slog.LevelDebug
if resHeader.OpCode == wire.OpCodeMsg {
msg := resBody.(*wire.OpMsg)
doc, _ := msg.Section0()
var ok bool
if doc != nil {
switch v := doc.Get("ok").(type) {
case float64:
ok = v == 1
case int32:
ok = v == 1
case int64:
ok = v == 1
}
}
if !ok {
level = slog.LevelWarn
}
}
if closeConn {
level = slog.LevelError
}
if c.l.Enabled(ctx, level) {
c.l.Log(ctx, level, who+" header: "+resHeader.String())
c.l.Log(ctx, level, who+" message:\n"+resBody.StringIndent())
}
return level
}
// logDiff logs the diff between the response and the proxy response.
func (c *conn) logDiff(ctx context.Context, resHeader, proxyHeader *wire.MsgHeader, resBody, proxyBody wire.MsgBody, logLevel slog.Level) error { //nolint:lll // for readability
diffHeader, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(resHeader.String()),
FromFile: "res header",
B: difflib.SplitLines(proxyHeader.String()),
ToFile: "proxy header",
Context: 1,
})
if err != nil {
return err
}
// resBody can be nil if we got a message we could not handle at all, like unsupported OpQuery.
var resBodyString, proxyBodyString string
if resBody != nil {
resBodyString = resBody.StringIndent()
}
if proxyBody != nil {
proxyBodyString = proxyBody.StringIndent()
}
diffBody, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(resBodyString),
FromFile: "res body",
B: difflib.SplitLines(proxyBodyString),
ToFile: "proxy body",
Context: 1,
})
if err != nil {
return err
}
if len(diffBody) > 0 {
// the diff control lines (those with ---, +++, or @@) are created with a trailing newline
diffBody = "\n" + strings.TrimSpace(diffBody)
}
c.l.Log(ctx, logLevel, "Header diff:\n"+diffHeader+"\nBody diff:"+diffBody)
return nil
}
+12 -1
View File
@@ -24,6 +24,7 @@ import (
)
// ConnInfo represents client connection information.
// It must be one-to-one mapped to a client connection.
type ConnInfo struct {
// the order of fields is weird to make the struct smaller due to alignment
@@ -33,7 +34,8 @@ type ConnInfo struct {
metadataRecv bool // protected by rw
steps int // protected by rw
token *resource.Token
token *resource.Token
onClose func(*ConnInfo)
}
// New creates a new ConnInfo.
@@ -49,9 +51,18 @@ func New() *ConnInfo {
// Close untracks ConnInfo.
func (ci *ConnInfo) Close() {
if ci.onClose != nil {
ci.onClose(ci)
}
resource.Untrack(ci, ci.token)
}
// OnClose sets a callback to be called when ConnInfo is [Close]'d.
func (ci *ConnInfo) OnClose(f func(*ConnInfo)) {
ci.onClose = f
}
// Conv returns SCRAM conversation.
func (ci *ConnInfo) Conv() *scram.Conv {
ci.rw.RLock()
+15 -46
View File
@@ -27,14 +27,12 @@ import (
"sync"
"time"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire"
"github.com/prometheus/client_golang/prometheus"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/connmetrics"
"github.com/FerretDB/FerretDB/v2/internal/handler"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/util/ctxutil"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
)
@@ -44,6 +42,7 @@ type Listener struct {
*ListenerOpts
ll *slog.Logger
lm *listenerMetrics
tcpListener net.Listener
unixListener net.Listener
@@ -54,9 +53,8 @@ type Listener struct {
// ListenerOpts represents listener configuration.
type ListenerOpts struct {
Handler *handler.Handler
Metrics *connmetrics.ListenerMetrics
Logger *slog.Logger
M *middleware.Middleware
Logger *slog.Logger
TCP string // empty value disables TCP listener
Unix string // empty value disables Unix listener
@@ -119,13 +117,13 @@ func tlsConfig(certFile, keyFile, caFile string) (*tls.Config, error) {
}
// Listen creates a new listener and starts listening on configured interfaces.
// It takes over the passed handler.
// [Listener.Run] must be called on the returned value.
func Listen(opts *ListenerOpts) (l *Listener, err error) {
ll := logging.WithName(opts.Logger, "listener")
l = &Listener{
ListenerOpts: opts,
ll: ll,
lm: NewListenerMetrics(),
listenersClosed: make(chan struct{}),
}
@@ -209,15 +207,6 @@ func (l *Listener) Listening() bool {
//
// When this method returns, listener and all connections are closed, and handler is stopped.
func (l *Listener) Run(ctx context.Context) {
// inherit ctx's values
handlerCtx, handlerCancel := context.WithCancel(context.WithoutCancel(ctx))
handlerDone := make(chan struct{})
go func() {
defer close(handlerDone)
l.Handler.Run(handlerCtx)
}()
var wg sync.WaitGroup
if l.tcpListener != nil {
@@ -263,11 +252,6 @@ func (l *Listener) Run(ctx context.Context) {
l.close()
l.ll.InfoContext(ctx, "Waiting for all connections to close")
wg.Wait()
// to properly handle last client commands like endSession,
// stop handler only after the last client disconnects
handlerCancel()
<-handlerDone
}
// acceptLoop runs listener's connection accepting loop until context is canceled.
@@ -281,7 +265,7 @@ func acceptLoop(ctx context.Context, listener net.Listener, wg *sync.WaitGroup,
return
}
l.Metrics.Accepts.WithLabelValues("1").Inc()
l.lm.accepts.WithLabelValues("1").Inc()
l.ll.WarnContext(ctx, "Failed to accept connection", logging.Error(err))
if !errors.Is(err, net.ErrClosed) {
@@ -292,7 +276,7 @@ func acceptLoop(ctx context.Context, listener net.Listener, wg *sync.WaitGroup,
}
wg.Add(1)
l.Metrics.Accepts.WithLabelValues("0").Inc()
l.lm.accepts.WithLabelValues("0").Inc()
go func() {
var connErr error
@@ -304,7 +288,7 @@ func acceptLoop(ctx context.Context, listener net.Listener, wg *sync.WaitGroup,
lv = "1"
}
l.Metrics.Durations.WithLabelValues(lv).Observe(time.Since(start).Seconds())
l.lm.durations.WithLabelValues(lv).Observe(time.Since(start).Seconds())
netConn.Close()
wg.Done()
}()
@@ -321,27 +305,14 @@ func acceptLoop(ctx context.Context, listener net.Listener, wg *sync.WaitGroup,
connID := fmt.Sprintf("%s -> %s", remoteAddr, netConn.LocalAddr())
opts := &newConnOpts{
netConn: netConn,
mode: l.Mode,
l: logging.WithName(l.ll, "// "+connID+" "), // derive from the original unnamed logger
handler: l.Handler,
connMetrics: l.Metrics.ConnMetrics, // share between all conns
proxyAddr: l.ProxyAddr,
proxyTLSCertFile: l.ProxyTLSCertFile,
proxyTLSKeyFile: l.ProxyTLSKeyFile,
proxyTLSCAFile: l.ProxyTLSCAFile,
//exhaustruct:enforce
conn := &conn{
netConn: netConn,
l: logging.WithName(l.ll, "// "+connID+" "),
m: l.M,
testRecordsDir: l.TestRecordsDir,
}
conn, connErr := newConn(opts)
if connErr != nil {
l.ll.WarnContext(connCtx, "Failed to create connection", slog.String("conn", connID), logging.Error(connErr))
return
}
l.ll.InfoContext(ctx, "Connection started", slog.String("conn", connID))
connErr = conn.run(connCtx)
@@ -387,14 +358,12 @@ func (l *Listener) TLSAddr() net.Addr {
// Describe implements [prometheus.Collector].
func (l *Listener) Describe(ch chan<- *prometheus.Desc) {
l.Metrics.Describe(ch)
l.Handler.Describe(ch)
l.lm.Describe(ch)
}
// Collect implements [prometheus.Collector].
func (l *Listener) Collect(ch chan<- prometheus.Metric) {
l.Metrics.Collect(ch)
l.Handler.Collect(ch)
l.lm.Collect(ch)
}
// check interfaces
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package connmetrics
package clientconn
import (
"time"
@@ -23,35 +23,34 @@ import (
// Parts of Prometheus metric names.
const (
// TODO https://github.com/FerretDB/FerretDB/issues/3420
namespace = "ferretdb_unstable"
namespace = "ferretdb"
subsystem = "client"
)
// ListenerMetrics represents listener metrics.
type ListenerMetrics struct {
Accepts *prometheus.CounterVec
Durations *prometheus.HistogramVec
ConnMetrics *ConnMetrics
// listenerMetrics represents listener metrics.
type listenerMetrics struct {
accepts *prometheus.CounterVec
durations *prometheus.HistogramVec
}
// NewListenerMetrics creates new listener metrics.
func NewListenerMetrics() *ListenerMetrics {
lm := &ListenerMetrics{
Accepts: prometheus.NewCounterVec(
func NewListenerMetrics() *listenerMetrics {
lm := &listenerMetrics{
accepts: prometheus.NewCounterVec(
prometheus.CounterOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "accepts_total",
Help: "Unstable: Total number of accepted client connections.",
Help: "Total number of accepted client connections.",
},
[]string{"error"},
),
Durations: prometheus.NewHistogramVec(
durations: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "duration_seconds",
Help: "Unstable: Client connection lifetime in seconds.",
Help: "Client connection lifetime in seconds.",
Buckets: []float64{
1,
5,
@@ -65,31 +64,27 @@ func NewListenerMetrics() *ListenerMetrics {
},
[]string{"error"},
),
ConnMetrics: newConnMetrics(),
}
lm.Accepts.WithLabelValues("0")
lm.Durations.WithLabelValues("0")
lm.accepts.WithLabelValues("0")
lm.durations.WithLabelValues("0")
return lm
}
// Describe implements [prometheus.Collector].
func (lm *ListenerMetrics) Describe(ch chan<- *prometheus.Desc) {
lm.Accepts.Describe(ch)
lm.Durations.Describe(ch)
lm.ConnMetrics.Describe(ch)
func (lm *listenerMetrics) Describe(ch chan<- *prometheus.Desc) {
lm.accepts.Describe(ch)
lm.durations.Describe(ch)
}
// Collect implements [prometheus.Collector].
func (lm *ListenerMetrics) Collect(ch chan<- prometheus.Metric) {
lm.Accepts.Collect(ch)
lm.Durations.Collect(ch)
lm.ConnMetrics.Collect(ch)
func (lm *listenerMetrics) Collect(ch chan<- prometheus.Metric) {
lm.accepts.Collect(ch)
lm.durations.Collect(ch)
}
// check interfaces
var (
_ prometheus.Collector = (*ListenerMetrics)(nil)
_ prometheus.Collector = (*listenerMetrics)(nil)
)
+34
View File
@@ -14,4 +14,38 @@
package api
import _ "embed"
// Spec is the embedded OpenAPI specification.
//
//go:embed openapi.json
var Spec []byte
//go:generate ../../../bin/oapi-codegen --config=./oapi-config.yml ./openapi.json
// Response types are used to represent the responses from the API.
//
//sumtype:decl
type Response interface {
sealed()
}
func (r *AggregateResponseBody) sealed() {}
func (r *DeleteResponseBody) sealed() {}
func (r *Error) sealed() {}
func (r *FindOneResponseBody) sealed() {}
func (r *FindManyResponseBody) sealed() {}
func (r *InsertOneResponseBody) sealed() {}
func (r *InsertManyResponseBody) sealed() {}
func (r *UpdateResponseBody) sealed() {}
var (
_ Response = (*AggregateResponseBody)(nil)
_ Response = (*DeleteResponseBody)(nil)
_ Response = (*Error)(nil)
_ Response = (*FindOneResponseBody)(nil)
_ Response = (*FindManyResponseBody)(nil)
_ Response = (*InsertManyResponseBody)(nil)
_ Response = (*InsertOneResponseBody)(nil)
_ Response = (*UpdateResponseBody)(nil)
)
+12 -12
View File
@@ -2,7 +2,7 @@
// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT.
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.1 DO NOT EDIT.
package api
import (
@@ -39,7 +39,7 @@ type AggregateRequestBody struct {
// AggregateResponseBody defines model for AggregateResponseBody.
type AggregateResponseBody struct {
// Documents An array that contains the result set of the aggregation.
Documents []map[string]interface{} `json:"documents"`
Documents json.RawMessage `json:"documents"`
}
// DeleteRequestBody defines model for DeleteRequestBody.
@@ -80,8 +80,8 @@ type Error struct {
// ErrorMissingAuthenticationParameter defines model for ErrorMissingAuthenticationParameter.
type ErrorMissingAuthenticationParameter struct {
Error *interface{} `json:"error,omitempty"`
ErrorCode *interface{} `json:"error_code,omitempty"`
Error interface{} `json:"error,omitempty"`
ErrorCode interface{} `json:"error_code,omitempty"`
// Link A link to a [log entry](https://www.mongodb.com/docs/atlas/app-services/logs/endpoint/) for the failed operation.
Link string `json:"link,omitempty"`
@@ -89,8 +89,8 @@ type ErrorMissingAuthenticationParameter struct {
// ErrorNoAuthenticationSpecified defines model for ErrorNoAuthenticationSpecified.
type ErrorNoAuthenticationSpecified struct {
Error *interface{} `json:"error,omitempty"`
ErrorCode *interface{} `json:"error_code,omitempty"`
Error interface{} `json:"error,omitempty"`
ErrorCode interface{} `json:"error_code,omitempty"`
// Link A link to a [log entry](https://www.mongodb.com/docs/atlas/app-services/logs/endpoint/) for the failed operation.
Link string `json:"link,omitempty"`
@@ -98,8 +98,8 @@ type ErrorNoAuthenticationSpecified struct {
// ErrorUserNotFound defines model for ErrorUserNotFound.
type ErrorUserNotFound struct {
Error *interface{} `json:"error,omitempty"`
ErrorCode *interface{} `json:"error_code,omitempty"`
Error interface{} `json:"error,omitempty"`
ErrorCode interface{} `json:"error_code,omitempty"`
// Link A link to a [log entry](https://www.mongodb.com/docs/atlas/app-services/logs/endpoint/) for the failed operation.
Link string `json:"link,omitempty"`
@@ -144,7 +144,7 @@ type FindManyRequestBody struct {
// FindManyResponseBody The result of a find operation.
type FindManyResponseBody struct {
// Documents A list of documents that match the specified filter.
Documents *[]map[string]interface{} `json:"documents,omitempty"`
Documents *json.RawMessage `json:"documents,omitempty"`
}
// FindOneRequestBody defines model for FindOneRequestBody.
@@ -171,7 +171,7 @@ type FindOneRequestBody struct {
// FindOneResponseBody The result of a findOne operation.
type FindOneResponseBody struct {
// Document A document that matches the specified filter. If no documents match, this is `null`.
Document *map[string]interface{} `json:"document"`
Document *json.RawMessage `json:"document"`
}
// InsertManyRequestBody defines model for InsertManyRequestBody.
@@ -195,7 +195,7 @@ type InsertManyRequestBody struct {
// InsertManyResponseBody The result of an insertMany operation.
type InsertManyResponseBody struct {
// InsertedIds A list of the `_id` values of the inserted documents.
InsertedIds *[]string `json:"insertedIds,omitempty"`
InsertedIds *[]any `json:"insertedIds,omitempty"`
}
// InsertOneRequestBody defines model for InsertOneRequestBody.
@@ -219,7 +219,7 @@ type InsertOneRequestBody struct {
// InsertOneResponseBody The result of an insertOne operation.
type InsertOneResponseBody struct {
// InsertedId The `_id` value of the inserted document.
InsertedId *interface{} `json:"insertedId,omitempty"`
InsertedId *any `json:"insertedId,omitempty"`
}
// Limit defines model for Limit.
+5 -1
View File
@@ -1263,6 +1263,7 @@
"description": "The result of a findOne operation.",
"properties": {
"document": {
"x-go-type": "json.RawMessage",
"type": "object",
"nullable": true,
"description": "A document that matches the specified filter. If no documents match, this is `null`."
@@ -1404,6 +1405,7 @@
"properties": {
"documents": {
"type": "array",
"x-go-type": "json.RawMessage",
"items": {
"type": "object"
},
@@ -1438,6 +1440,7 @@
"type": "object",
"properties": {
"insertedId": {
"x-go-type": "any",
"description": "The `_id` value of the inserted document."
}
}
@@ -1475,7 +1478,7 @@
"type": "array",
"description": "A list of the `_id` values of the inserted documents.",
"items": {
"type": "string",
"x-go-type": "any",
"description": "The `_id` value of an inserted document."
}
}
@@ -1587,6 +1590,7 @@
"documents": {
"description": "An array that contains the result set of the aggregation.",
"type": "array",
"x-go-type": "json.RawMessage",
"items": {
"type": "object",
"description": "A document included in the result set of the aggregation."
@@ -24,70 +24,96 @@ import (
"net"
"net/http"
"github.com/AlekSi/lazyerrors"
"github.com/prometheus/client_golang/prometheus"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/server"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/ctxutil"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
)
// Listener represents dataapi listener.
// Listener represents Data API TCP listener and HTTP handler.
type Listener struct {
opts *ListenOpts
lis net.Listener
srv *server.Server
h http.Handler
}
// ListenOpts represents [Listen] options.
type ListenOpts struct {
L *slog.Logger
Handler middleware.Handler
M *middleware.Middleware
TCPAddr string
Auth bool
}
// Listen creates a new dataapi handler and starts listener on the given TCP address.
// Listen creates a new Data API handler and starts listener on the given TCP address.
// [Listener.Run] must be called on the returned value.
func Listen(opts *ListenOpts) (*Listener, error) {
lis, err := net.Listen("tcp", opts.TCPAddr)
if err != nil {
return nil, lazyerrors.Error(err)
}
s := server.New(opts.L, opts.M)
mux := http.NewServeMux()
mux.HandleFunc("GET /openapi.json", s.OpenAPISpec)
h := api.HandlerFromMux(s, mux)
if opts.Auth {
h = s.AuthMiddleware(h)
}
h = s.ConnInfoMiddleware(h)
return &Listener{
opts: opts,
lis: lis,
srv: server.New(opts.L, opts.Handler),
h: h,
}, nil
}
// Run runs dataapi handler until ctx is canceled.
// Run runs Data API handler until ctx is canceled.
//
// It exits when handler is stopped and listener closed.
func (lis *Listener) Run(ctx context.Context) {
srvHandler := api.HandlerFromMux(lis.srv, http.NewServeMux())
if lis.opts.Auth {
srvHandler = lis.srv.AuthMiddleware(srvHandler)
}
srv := &http.Server{
Handler: lis.srv.ConnInfoMiddleware(srvHandler),
s := &http.Server{
Handler: lis.h,
ErrorLog: slog.NewLogLogger(lis.opts.L.Handler(), slog.LevelError),
BaseContext: func(net.Listener) context.Context {
return ctx
},
}
lis.opts.L.InfoContext(ctx, fmt.Sprintf("Starting DataAPI server on http://%s/", lis.lis.Addr()))
l := lis.opts.L
l.InfoContext(ctx, fmt.Sprintf("Starting Data API server on http://%s/", lis.Addr()))
l.InfoContext(ctx, fmt.Sprintf("http://%s/openapi.json - OpenAPI spec", lis.Addr()))
go func() {
if err := srv.Serve(lis.lis); !errors.Is(err, http.ErrServerClosed) {
lis.opts.L.LogAttrs(ctx, logging.LevelDPanic, "Serve exited with unexpected error", logging.Error(err))
if err := s.Serve(lis.lis); !errors.Is(err, http.ErrServerClosed) {
l.LogAttrs(ctx, logging.LevelDPanic, "Serve exited with unexpected error", logging.Error(err))
}
}()
// TODO https://github.com/FerretDB/FerretDB/issues/4848
<-ctx.Done()
// ctx is already canceled, but we want to inherit its values
shutdownCtx, shutdownCancel := ctxutil.WithDelay(ctx)
defer shutdownCancel(nil)
if err := s.Shutdown(shutdownCtx); err != nil {
l.LogAttrs(ctx, logging.LevelDPanic, "Shutdown exited with unexpected error", logging.Error(err))
}
if err := s.Close(); err != nil {
l.LogAttrs(ctx, logging.LevelDPanic, "Close exited with unexpected error", logging.Error(err))
}
l.InfoContext(ctx, "Data API server stopped")
}
// Addr returns TCP listener's address.
@@ -95,3 +121,11 @@ func (lis *Listener) Run(ctx context.Context) {
func (lis *Listener) Addr() net.Addr {
return lis.lis.Addr()
}
// Describe implements [prometheus.Collector].
func (lis *Listener) Describe(ch chan<- *prometheus.Desc) {
}
// Collect implements [prometheus.Collector].
func (lis *Listener) Collect(ch chan<- prometheus.Metric) {
}
@@ -17,6 +17,7 @@ package dataapi_test
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
@@ -28,7 +29,6 @@ import (
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/connmetrics"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/util/setup"
"github.com/FerretDB/FerretDB/v2/internal/util/state"
@@ -57,6 +57,22 @@ func TestSmokeDataAPI(t *testing.T) {
assert.JSONEq(t, `{"documents":[]}`, string(body))
})
t.Run("FindOneEmpty", func(t *testing.T) {
jsonBody := `{
"database": "` + db + `",
"collection": "` + coll + `",
"filter": {}
}`
res, err := postJSON(t, "http://"+addr+"/action/findOne", jsonBody)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.JSONEq(t, `{"document":null}`, string(body))
})
docs := []string{
`{"_id":1,"v":"foo"}`,
`{"_id":2,"v":42}`,
@@ -76,7 +92,7 @@ func TestSmokeDataAPI(t *testing.T) {
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.JSONEq(t, `{"n":1}`, string(body))
assert.JSONEq(t, `{"insertedId":1}`, string(body))
})
t.Run("InsertMany", func(t *testing.T) {
@@ -92,7 +108,7 @@ func TestSmokeDataAPI(t *testing.T) {
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
assert.JSONEq(t, `{"n":1}`, string(body))
assert.JSONEq(t, `{"insertedIds": [2, 3]}`, string(body))
})
t.Run("UpdateOne", func(t *testing.T) {
@@ -208,6 +224,144 @@ func TestSmokeDataAPI(t *testing.T) {
require.NoError(t, err)
assert.JSONEq(t, `{"documents":[`+docs[2]+`]}`, string(body))
})
t.Run("InsertOneEmptyId", func(t *testing.T) {
jsonBody := `{
"database": "` + db + `",
"collection": "` + coll + `",
"document": ` + `{"v":"foo"}` + `
}`
res, err := postJSON(t, "http://"+addr+"/action/insertOne", jsonBody)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
bodyStr := strings.TrimSpace(string(body))
assert.Regexp(t,
`^{"insertedId":"[0-9a-f]{24}"}$`,
bodyStr,
"expected %q, got %q",
`{"insertedId":"<ObjectID>"}`,
string(body),
)
})
t.Run("UpsertOne", func(t *testing.T) {
jsonBody := `{
"database": "` + db + `",
"collection": "` + coll + `",
"filter": {"v":"non-existent"},
"update": {"$set":{"v":"foo"}},
"upsert": true
}`
res, err := postJSON(t, "http://"+addr+"/action/updateOne", jsonBody)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
bodyStr := strings.TrimSpace(string(body))
assert.Regexp(t,
`^{"matchedCount":1,"modifiedCount":0,"upsertedId":".*[0-9a-f]{24}.*"}$`,
bodyStr,
"expected %q, got %q",
`{"upsertedId":"<ObjectID>"}`,
string(body),
)
})
t.Run("UpsertOneStringId", func(t *testing.T) {
jsonBody := `{
"database": "` + db + `",
"collection": "` + coll + `",
"filter": {"v":"non-existent"},
"update": {"$set":{"_id":"string_id","v":"foo"}},
"upsert": true
}`
res, err := postJSON(t, "http://"+addr+"/action/updateOne", jsonBody)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
require.NoError(t, err)
assert.JSONEq(t, `{"matchedCount":1,"modifiedCount":0,"upsertedId":"string_id"}`, string(body))
})
t.Run("UpsertMany", func(t *testing.T) {
jsonBody := `{
"database": "` + db + `",
"collection": "` + coll + `",
"filter": {"v":"non-existent"},
"update": {"$set":{"v":"foo"}},
"upsert": true
}`
res, err := postJSON(t, "http://"+addr+"/action/updateMany", jsonBody)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)
body, err := io.ReadAll(res.Body)
require.NoError(t, err)
bodyStr := strings.TrimSpace(string(body))
assert.Regexp(t,
`^{"matchedCount":1,"modifiedCount":0,"upsertedId":".*[0-9a-f]{24}.*"}$`,
bodyStr,
"expected %q, got %q",
`{"upsertedId":"<ObjectID>"}`,
string(body),
)
})
}
func TestOpenAPI(t *testing.T) {
t.Parallel()
addr, _ := setupDataAPI(t, false)
t.Run("Spec", func(t *testing.T) {
resp, err := http.Get("http://" + addr + "/openapi.json")
require.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, resp.Body.Close())
})
assert.Equal(t, http.StatusOK, resp.StatusCode)
assert.Equal(t, "application/json", resp.Header.Get("Content-Type"))
assert.Equal(t, "53813", resp.Header.Get("Content-Length"))
body, err := io.ReadAll(resp.Body)
require.NoError(t, err)
var spec map[string]any
err = json.Unmarshal(body, &spec)
require.NoError(t, err)
assert.Equal(t, "3.0.0", spec["openapi"])
assert.Contains(t, spec, "info")
assert.Contains(t, spec, "paths")
info := spec["info"].(map[string]any)
assert.Equal(t, "FerretDB Data API", info["title"])
})
t.Run("MethodNotAllowed", func(t *testing.T) {
resp, err := http.Post("http://"+addr+"/openapi.json", "", nil)
require.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, resp.Body.Close())
})
assert.Equal(t, http.StatusMethodNotAllowed, resp.StatusCode)
})
}
// postJSON sends POST request with provided JSON to data API under provided uri.
@@ -238,31 +392,32 @@ func setupDataAPI(tb testing.TB, auth bool) (addr string, dbName string) {
//exhaustruct:enforce
res := setup.Setup(tb.Context(), &setup.SetupOpts{
Logger: l,
StateProvider: sp,
ListenerMetrics: connmetrics.NewListenerMetrics(),
PostgreSQLURL: uri,
Logger: l,
StateProvider: sp,
Metrics: middleware.NewMetrics(),
PostgreSQLURL: uri,
Auth: auth,
ReplSetName: "",
SessionCleanupInterval: 0,
TCPAddr: "127.0.0.1:0",
UnixAddr: "",
TLSAddr: "",
TLSCertFile: "",
TLSKeyFile: "",
TLSCAFile: "",
Mode: middleware.NormalMode,
ProxyAddr: "",
ProxyTLSCertFile: "",
ProxyTLSKeyFile: "",
ProxyTLSCAFile: "",
RecordsDir: "",
TCPAddr: "127.0.0.1:0",
UnixAddr: "",
TLSAddr: "",
TLSCertFile: "",
TLSKeyFile: "",
TLSCAFile: "",
Mode: middleware.NormalMode,
TestRecordsDir: "",
DataAPIAddr: "127.0.0.1:0",
MCPAddr: "",
})
require.NotNil(tb, res)
+20 -11
View File
@@ -20,15 +20,15 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// Aggregate implements [ServerInterface].
func (s *Server) Aggregate(w http.ResponseWriter, r *http.Request) {
func (s *Server) Aggregate(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +37,7 @@ func (s *Server) Aggregate(w http.ResponseWriter, r *http.Request) {
var req api.AggregateRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -48,24 +48,33 @@ func (s *Server) Aggregate(w http.ResponseWriter, r *http.Request) {
"cursor", wirebson.MustDocument(),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
cursor := resp.Document().Get("cursor").(wirebson.AnyDocument)
firstBatch := must.NotFail(cursor.Decode()).Get("firstBatch").(wirebson.AnyArray)
s.writeJSONResponse(ctx, w, wirebson.MustDocument(
"documents", firstBatch,
))
b, err := marshalSingleJSON(firstBatch)
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
res := api.AggregateResponseBody{
Documents: b,
}
s.writeJSONResponse(ctx, rw, &res)
}
+14 -12
View File
@@ -20,15 +20,15 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// DeleteMany implements [ServerInterface].
func (s *Server) DeleteMany(w http.ResponseWriter, r *http.Request) {
func (s *Server) DeleteMany(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +37,7 @@ func (s *Server) DeleteMany(w http.ResponseWriter, r *http.Request) {
var req api.DeleteRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -46,7 +46,7 @@ func (s *Server) DeleteMany(w http.ResponseWriter, r *http.Request) {
"limit", float64(0),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -56,22 +56,24 @@ func (s *Server) DeleteMany(w http.ResponseWriter, r *http.Request) {
"deletes", wirebson.MustArray(deleteDoc),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
s.writeJSONResponse(ctx, w, wirebson.MustDocument(
"deletedCount", resp.Document().Get("n"),
))
res := api.DeleteResponseBody{
DeletedCount: resp.Document().Get("n"),
}
s.writeJSONResponse(ctx, rw, &res)
}
+14 -12
View File
@@ -20,15 +20,15 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// DeleteOne implements [ServerInterface].
func (s *Server) DeleteOne(w http.ResponseWriter, r *http.Request) {
func (s *Server) DeleteOne(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +37,7 @@ func (s *Server) DeleteOne(w http.ResponseWriter, r *http.Request) {
var req api.DeleteRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -46,7 +46,7 @@ func (s *Server) DeleteOne(w http.ResponseWriter, r *http.Request) {
"limit", float64(1),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -56,22 +56,24 @@ func (s *Server) DeleteOne(w http.ResponseWriter, r *http.Request) {
"deletes", wirebson.MustArray(deleteDoc),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
s.writeJSONResponse(ctx, w, wirebson.MustDocument(
"deletedCount", resp.Document().Get("n"),
))
res := api.DeleteResponseBody{
DeletedCount: resp.Document().Get("n"),
}
s.writeJSONResponse(ctx, rw, &res)
}
+5 -5
View File
@@ -39,10 +39,10 @@ var (
// writeError encodes [api.Error] into JSON and writes it to w
// with provided HTTP status code.
// TODO https://github.com/FerretDB/FerretDB/issues/4965
func writeError(w http.ResponseWriter, err api.Error, code int) {
w.WriteHeader(code)
w.Header().Set("Content-Type", "application/json")
func writeError(rw http.ResponseWriter, err api.Error, code int) {
rw.Header().Set("Content-Type", "application/json")
// ignore error, as writing to connection may fail if the client disconnects
_ = json.NewEncoder(w).Encode(err)
rw.WriteHeader(code)
_ = json.NewEncoder(rw).Encode(err)
}
+20 -11
View File
@@ -20,15 +20,15 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// Find implements [ServerInterface].
func (s *Server) Find(w http.ResponseWriter, r *http.Request) {
func (s *Server) Find(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +37,7 @@ func (s *Server) Find(w http.ResponseWriter, r *http.Request) {
var req api.FindManyRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -51,24 +51,33 @@ func (s *Server) Find(w http.ResponseWriter, r *http.Request) {
"sort", req.Sort,
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
cursor := resp.Document().Get("cursor").(wirebson.AnyDocument)
firstBatch := must.NotFail(cursor.Decode()).Get("firstBatch").(wirebson.AnyArray)
s.writeJSONResponse(ctx, w, wirebson.MustDocument(
"documents", firstBatch,
))
b, err := marshalSingleJSON(firstBatch)
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
res := api.FindManyResponseBody{
Documents: &b,
}
s.writeJSONResponse(ctx, rw, &res)
}
+22 -15
View File
@@ -20,15 +20,15 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// FindOne implements [ServerInterface].
func (s *Server) FindOne(w http.ResponseWriter, r *http.Request) {
func (s *Server) FindOne(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +37,7 @@ func (s *Server) FindOne(w http.ResponseWriter, r *http.Request) {
var req api.FindOneRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -49,34 +49,41 @@ func (s *Server) FindOne(w http.ResponseWriter, r *http.Request) {
"limit", float64(1),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
cursor := resp.Document().Get("cursor").(wirebson.AnyDocument)
firstBatch := must.NotFail(cursor.Decode()).Get("firstBatch").(wirebson.AnyArray)
var doc wirebson.RawDocument
var res api.FindOneResponseBody
docs := must.NotFail(firstBatch.Decode())
if docs.Len() > 0 {
doc = must.NotFail(docs.Get(0).(wirebson.AnyDocument).Encode())
if docs.Len() == 0 {
s.writeJSONResponse(ctx, rw, &res)
return
}
res := wirebson.MustDocument(
"document", doc,
)
doc := docs.Get(0).(wirebson.AnyDocument)
s.writeJSONResponse(ctx, w, res)
b, err := marshalSingleJSON(doc)
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
res.Document = &b
s.writeJSONResponse(ctx, rw, &res)
}
+58 -13
View File
@@ -20,15 +20,15 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// InsertMany implements [ServerInterface].
func (s *Server) InsertMany(w http.ResponseWriter, r *http.Request) {
func (s *Server) InsertMany(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,34 +37,79 @@ func (s *Server) InsertMany(w http.ResponseWriter, r *http.Request) {
var req api.InsertManyRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
docsArr, err := unmarshalSingleJSON(&req.Documents)
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
documents, err := docsArr.(wirebson.RawArray).Decode()
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
var insertedIds []any
for i, v := range documents.All() {
v, ok := v.(wirebson.AnyDocument)
if !ok {
http.Error(rw, fmt.Sprintf("document %d is not a valid BSON document", i), http.StatusBadRequest)
return
}
var doc *wirebson.Document
doc, err = ensureID(v)
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
var insertedId any
insertedId, err = wirebson.ToDriver(doc.Get("_id"))
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
if err = documents.Replace(i, doc); err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
insertedIds = append(insertedIds, insertedId)
}
msg, err := prepareRequest(
"insert", req.Collection,
"$db", req.Database,
"documents", req.Documents,
"documents", documents,
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
res := wirebson.MustDocument(
"n", float64(1),
)
res := api.InsertManyResponseBody{
InsertedIds: &insertedIds,
}
s.writeJSONResponse(ctx, w, res)
s.writeJSONResponse(ctx, rw, &res)
}
+61 -17
View File
@@ -20,15 +20,16 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"go.mongodb.org/mongo-driver/v2/bson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// InsertOne implements [ServerInterface].
func (s *Server) InsertOne(w http.ResponseWriter, r *http.Request) {
func (s *Server) InsertOne(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,45 +38,88 @@ func (s *Server) InsertOne(w http.ResponseWriter, r *http.Request) {
var req api.InsertOneRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
insertDoc, err := unmarshalSingleJSON(&req.Document)
insert, err := unmarshalSingleJSON(&req.Document)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
var documents any
if insertDoc != nil {
documents = wirebson.MustArray(insertDoc)
insertDoc, ok := insert.(wirebson.AnyDocument)
if !ok {
http.Error(rw, lazyerrors.New("document must be a BSON document").Error(), http.StatusInternalServerError)
return
}
var doc *wirebson.Document
doc, err = ensureID(insertDoc)
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
documents := wirebson.MustArray(doc)
msg, err := prepareRequest(
"insert", req.Collection,
"$db", req.Database,
"documents", documents,
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
res := wirebson.MustDocument(
"n", float64(1),
)
insertedId, err := wirebson.ToDriver(doc.Get("_id"))
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
s.writeJSONResponse(ctx, w, res)
res := api.InsertOneResponseBody{
InsertedId: &insertedId,
}
s.writeJSONResponse(ctx, rw, &res)
}
// ensureID ensures that inserted document has an "_id" field.
func ensureID(doc wirebson.AnyDocument) (*wirebson.Document, error) {
decoded, err := doc.Decode()
if err != nil {
return nil, lazyerrors.Error(err)
}
for field := range decoded.Fields() {
if field == "_id" {
return decoded, nil
}
}
id, err := wirebson.FromDriver(bson.NewObjectID())
if err != nil {
return nil, lazyerrors.Error(err)
}
err = decoded.Add("_id", id)
if err != nil {
return nil, lazyerrors.Error(err)
}
return decoded, err
}
+31 -9
View File
@@ -15,25 +15,47 @@
package server
import (
"bytes"
"encoding/json"
"fmt"
"io"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"go.mongodb.org/mongo-driver/v2/bson"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
)
// marshalJSON encodes wirebson.RawDocument into extended JSON.
func marshalJSON(raw wirebson.RawDocument, jsonDst io.Writer) error {
encoder := bson.NewEncoder(bson.NewExtJSONValueWriter(jsonDst, false, false))
// marshalSingleJSON converts wirebson value to the standard JSON representation.
func marshalSingleJSON(v any) (json.RawMessage, error) {
var err error
if err := encoder.Encode(bson.Raw(raw)); err != nil {
return lazyerrors.Error(err)
switch vt := v.(type) {
case wirebson.RawDocument:
v, err = vt.DecodeDeep()
if err != nil {
return nil, lazyerrors.Error(err)
}
case wirebson.RawArray:
v, err = vt.DecodeDeep()
if err != nil {
return nil, lazyerrors.Error(err)
}
default:
// no need to decode other types
}
return nil
bv, err := wirebson.ToDriver(v)
if err != nil {
return nil, lazyerrors.Error(err)
}
var buf bytes.Buffer
err = json.NewEncoder(&buf).Encode(bv)
if err != nil {
return nil, lazyerrors.Error(err)
}
return json.RawMessage(buf.Bytes()), nil
}
// unmarshalSingleJSON takes extended JSON object and unmarshals it into the wirebson composite.
@@ -12,34 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// We should remove that code (and this dependency) with 1.25:
// https://tip.golang.org/doc/go1.25#container-aware-gomaxprocs
//
//go:build !go1.25
package main
package server
import (
"fmt"
"log/slog"
"math"
"go.uber.org/automaxprocs/maxprocs"
"net/http"
"strconv"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
)
func setGOMAXPROCS(logger *slog.Logger) {
maxprocsOpts := []maxprocs.Option{
maxprocs.Min(2),
maxprocs.RoundQuotaFunc(func(v float64) int {
return int(math.Ceil(v))
}),
maxprocs.Logger(func(format string, a ...any) {
logger.Info(fmt.Sprintf(format, a...))
}),
}
if _, err := maxprocs.Set(maxprocsOpts...); err != nil {
logger.Warn("Failed to set GOMAXPROCS", logging.Error(err))
// OpenAPISpec serves the OpenAPI specification.
func (s *Server) OpenAPISpec(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "application/json")
rw.Header().Set("Content-Length", strconv.Itoa(len(api.Spec)))
if _, err := rw.Write(api.Spec); err != nil {
s.l.WarnContext(r.Context(), "Failed to write OpenAPI spec", logging.Error(err))
}
}
+43 -45
View File
@@ -25,51 +25,51 @@ import (
"net/http"
"strings"
"github.com/AlekSi/lazyerrors"
"github.com/FerretDB/wire/wirebson"
"github.com/xdg-go/scram"
"github.com/FerretDB/FerretDB/v2/internal/clientconn/conninfo"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/handler/middleware"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// New creates a new Server.
func New(l *slog.Logger, handler middleware.Handler) *Server {
func New(l *slog.Logger, handler *middleware.Middleware) *Server {
return &Server{
l: l,
handler: handler,
l: l,
m: handler,
}
}
// Server implements services described by OpenAPI description file.
type Server struct {
l *slog.Logger
handler middleware.Handler
l *slog.Logger
m *middleware.Middleware
}
// AuthMiddleware handles SCRAM authentication based on the username and password specified in request.
// After a successful handshake it calls the next handler.
func (s *Server) AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
username, password, ok := r.BasicAuth()
if !ok {
writeError(w, errorNoAuthenticationSpecified, http.StatusBadRequest)
writeError(rw, errorNoAuthenticationSpecified, http.StatusBadRequest)
return
}
if password == "" || username == "" {
writeError(w, errorMissingAuthenticationParameter, http.StatusBadRequest)
writeError(rw, errorMissingAuthenticationParameter, http.StatusBadRequest)
return
}
client, err := scram.SHA256.NewClient(username, password, "")
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusBadRequest)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusBadRequest)
return
}
@@ -77,7 +77,7 @@ func (s *Server) AuthMiddleware(next http.Handler) http.Handler {
payload, err := conv.Step("")
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusBadRequest)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusBadRequest)
return
}
@@ -90,14 +90,14 @@ func (s *Server) AuthMiddleware(next http.Handler) http.Handler {
"$db", "admin",
))
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
@@ -106,7 +106,7 @@ func (s *Server) AuthMiddleware(next http.Handler) http.Handler {
payload, err = conv.Step(string(payloadBytes))
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusUnauthorized)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusUnauthorized)
return
}
@@ -117,67 +117,61 @@ func (s *Server) AuthMiddleware(next http.Handler) http.Handler {
"$db", "admin",
))
resp, err = s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp = s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
if !resp.Document().Get("done").(bool) {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
payloadBytes = resp.Document().Get("payload").(wirebson.Binary).B
if _, err = conv.Step(string(payloadBytes)); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusUnauthorized)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusUnauthorized)
return
}
if !conv.Valid() {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r.WithContext(ctx))
next.ServeHTTP(rw, r.WithContext(ctx))
})
}
// ConnInfoMiddleware returns a handler function that creates a new [*conninfo.ConnInfo],
// calls the next handler, and closes the connection info after the request is done.
func (s *Server) ConnInfoMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
connInfo := conninfo.New()
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
ci := conninfo.New()
defer connInfo.Close()
defer ci.Close()
next.ServeHTTP(w, r.WithContext(conninfo.Ctx(r.Context(), connInfo)))
next.ServeHTTP(rw, r.WithContext(conninfo.Ctx(r.Context(), ci)))
})
}
// writeJSONResponse marshals provided res document into extended JSON and
// writes it to provided [http.ResponseWriter].
func (s *Server) writeJSONResponse(ctx context.Context, w http.ResponseWriter, res wirebson.AnyDocument) {
resRaw, err := res.Encode()
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
func (s *Server) writeJSONResponse(ctx context.Context, rw http.ResponseWriter, res api.Response) {
rw.Header().Set("Content-Type", "application/json")
w.Header().Set("Content-Type", "application/json")
var resWriter io.Writer = w
var resWriter io.Writer = rw
if s.l.Enabled(ctx, slog.LevelDebug) {
buf := new(bytes.Buffer)
resWriter = io.MultiWriter(w, buf)
resWriter = io.MultiWriter(rw, buf)
defer func() {
// extended JSON value writer always finish with '\n' character
@@ -185,22 +179,26 @@ func (s *Server) writeJSONResponse(ctx context.Context, w http.ResponseWriter, r
}()
}
if err = marshalJSON(resRaw, resWriter); err != nil {
err := json.NewEncoder(resWriter).Encode(res)
if err != nil {
s.l.ErrorContext(ctx, "marshalJSON failed", logging.Error(err))
}
}
// TODO https://github.com/FerretDB/FerretDB/issues/4965
func (s *Server) writeJSONError(ctx context.Context, w http.ResponseWriter, resp *middleware.Response) {
func (s *Server) writeJSONError(ctx context.Context, rw http.ResponseWriter, resp *middleware.Response) {
doc := resp.Document()
errmsg := doc.Get("errmsg").(string)
codeName := doc.Get("codeName").(string)
w.WriteHeader(http.StatusInternalServerError)
s.writeJSONResponse(ctx, w, wirebson.MustDocument(
"error", errmsg,
"error_code", codeName,
))
rw.Header().Set("Content-Type", "application/json")
rw.WriteHeader(http.StatusInternalServerError)
s.writeJSONResponse(ctx, rw, &api.Error{
Error: errmsg,
ErrorCode: codeName,
})
}
// prepareDocument creates a new bson document from the given pairs of
+1 -1
View File
@@ -50,7 +50,7 @@ func TestPrepareRequest(t *testing.T) {
},
"EmptyRawMessage": {
pairs: []any{"foo", pointer.To(json.RawMessage{})},
err: fmt.Errorf("[server.go:274 server.prepareRequest] Invalid object: []"),
err: fmt.Errorf("server.go:272 (server.prepareRequest): Invalid object: []"),
},
} {
t.Run(name, func(t *testing.T) {
+24 -15
View File
@@ -20,15 +20,16 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/AlekSi/pointer"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// UpdateMany implements [ServerInterface].
func (s *Server) UpdateMany(w http.ResponseWriter, r *http.Request) {
func (s *Server) UpdateMany(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +38,7 @@ func (s *Server) UpdateMany(w http.ResponseWriter, r *http.Request) {
var req api.UpdateRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -48,7 +49,7 @@ func (s *Server) UpdateMany(w http.ResponseWriter, r *http.Request) {
"multi", true,
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -58,25 +59,25 @@ func (s *Server) UpdateMany(w http.ResponseWriter, r *http.Request) {
"updates", wirebson.MustArray(updateDoc),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
res := wirebson.MustDocument(
"matchedCount", resp.Document().Get("n"),
"modifiedCount", resp.Document().Get("nModified"),
)
res := api.UpdateResponseBody{
MatchedCount: resp.Document().Get("n").(int32),
ModifiedCount: resp.Document().Get("nModified").(int32),
}
if upsertedRaw := resp.Document().Get("upserted"); upsertedRaw != nil {
upserted := must.NotFail(upsertedRaw.(wirebson.AnyArray).Decode())
@@ -84,9 +85,17 @@ func (s *Server) UpdateMany(w http.ResponseWriter, r *http.Request) {
if upserted.Len() > 0 {
item := must.NotFail(upserted.Get(0).(wirebson.AnyDocument).Decode())
must.NoError(res.Add("upsertedId", item.Get("_id")))
var upsertedId any
upsertedId, err = wirebson.ToDriver(item.Get("_id"))
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
res.UpsertedId = pointer.To(fmt.Sprint(upsertedId))
}
}
s.writeJSONResponse(ctx, w, res)
s.writeJSONResponse(ctx, rw, &res)
}
+24 -15
View File
@@ -20,15 +20,16 @@ import (
"net/http"
"net/http/httputil"
"github.com/AlekSi/lazyerrors"
"github.com/AlekSi/pointer"
"github.com/FerretDB/wire/wirebson"
"github.com/FerretDB/FerretDB/v2/internal/dataapi/api"
"github.com/FerretDB/FerretDB/v2/internal/util/lazyerrors"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
)
// UpdateOne implements [ServerInterface].
func (s *Server) UpdateOne(w http.ResponseWriter, r *http.Request) {
func (s *Server) UpdateOne(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
if s.l.Enabled(ctx, slog.LevelDebug) {
@@ -37,7 +38,7 @@ func (s *Server) UpdateOne(w http.ResponseWriter, r *http.Request) {
var req api.UpdateRequestBody
if err := decodeJSONRequest(r, &req); err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -48,7 +49,7 @@ func (s *Server) UpdateOne(w http.ResponseWriter, r *http.Request) {
"multi", false,
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
@@ -58,25 +59,25 @@ func (s *Server) UpdateOne(w http.ResponseWriter, r *http.Request) {
"updates", wirebson.MustArray(updateDoc),
)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
resp, err := s.handler.Handle(ctx, msg)
if err != nil {
http.Error(w, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
resp := s.m.Handle(ctx, msg)
if resp == nil {
http.Error(rw, "internal error", http.StatusInternalServerError)
return
}
if !resp.OK() {
s.writeJSONError(ctx, w, resp)
s.writeJSONError(ctx, rw, resp)
return
}
res := wirebson.MustDocument(
"matchedCount", resp.Document().Get("n"),
"modifiedCount", resp.Document().Get("nModified"),
)
res := api.UpdateResponseBody{
MatchedCount: resp.Document().Get("n").(int32),
ModifiedCount: resp.Document().Get("nModified").(int32),
}
if upsertedRaw := resp.Document().Get("upserted"); upsertedRaw != nil {
upserted := must.NotFail(upsertedRaw.(wirebson.AnyArray).Decode())
@@ -84,9 +85,17 @@ func (s *Server) UpdateOne(w http.ResponseWriter, r *http.Request) {
if upserted.Len() > 0 {
item := must.NotFail(upserted.Get(0).(wirebson.AnyDocument).Decode())
must.NoError(res.Add("upsertedId", item.Get("_id")))
var upsertedId any
upsertedId, err = wirebson.ToDriver(item.Get("_id"))
if err != nil {
http.Error(rw, lazyerrors.Error(err).Error(), http.StatusInternalServerError)
return
}
res.UpsertedId = pointer.To(fmt.Sprint(upsertedId))
}
}
s.writeJSONResponse(ctx, w, res)
s.writeJSONResponse(ctx, rw, &res)
}
+24
View File
@@ -17,11 +17,13 @@ package cursor
import (
"context"
"log/slog"
"time"
"github.com/FerretDB/wire/wirebson"
"github.com/jackc/pgx/v5"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
"github.com/FerretDB/FerretDB/v2/internal/util/resource"
)
@@ -52,6 +54,23 @@ func newCursor(continuation wirebson.RawDocument, conn *pgx.Conn) *cursor {
return res
}
// Type returns cursor type for logging and Prometheus label value.
func (c *cursor) Type() string {
if c.conn != nil {
return "persistent"
}
return "normal"
}
// LogValue implements [slog.LogValuer] interface.
func (c *cursor) LogValue() slog.Value {
return slog.GroupValue(
slog.String("type", c.Type()),
slog.Any("continuation", logging.LazyDeepDecoder(c.continuation)),
)
}
// close closes the underlying connection, if any.
//
// It attempts a clean close by sending the exit message to PostgreSQL.
@@ -71,3 +90,8 @@ func (c *cursor) close(ctx context.Context) {
resource.Untrack(c, c.token)
}
// check interfaces
var (
_ slog.LogValuer = (*cursor)(nil)
)
+82 -111
View File
@@ -24,14 +24,14 @@ import (
"github.com/jackc/pgx/v5"
"github.com/prometheus/client_golang/prometheus"
"github.com/FerretDB/FerretDB/v2/internal/util/logging"
"github.com/FerretDB/FerretDB/v2/internal/util/must"
"github.com/FerretDB/FerretDB/v2/internal/util/resource"
)
// Parts of Prometheus metric names.
const (
// TODO https://github.com/FerretDB/FerretDB/issues/5402
namespace = "ferretdb_unstable"
namespace = "ferretdb"
subsystem = "cursors"
)
@@ -61,16 +61,16 @@ func NewRegistry(l *slog.Logger) *Registry {
Namespace: namespace,
Subsystem: subsystem,
Name: "created_total",
Help: "Unstable: Total number of cursors created.",
Help: "Total number of cursors created.",
},
[]string{"user", "type"},
[]string{"type"},
),
duration: prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: namespace,
Subsystem: subsystem,
Name: "duration_seconds",
Help: "Unstable: Cursors lifetime in seconds.",
Help: "Cursors lifetime in seconds.",
Buckets: []float64{
(1 * time.Millisecond).Seconds(),
(5 * time.Millisecond).Seconds(),
@@ -86,22 +86,12 @@ func NewRegistry(l *slog.Logger) *Registry {
(10000 * time.Millisecond).Seconds(),
},
},
[]string{"user", "type"},
[]string{"type"},
),
}
// That probably should be the user from the PostgreSQL URI?
// TODO https://github.com/FerretDB/FerretDB/issues/3974
user := "unknown"
res.created.With(prometheus.Labels{
"user": user,
"type": "normal",
})
res.duration.With(prometheus.Labels{
"user": user,
"type": "normal",
})
res.created.With(prometheus.Labels{"type": "normal"})
res.duration.With(prometheus.Labels{"type": "normal"})
resource.Track(res, res.token)
@@ -114,71 +104,47 @@ func (r *Registry) Close(ctx context.Context) {
defer r.rw.Unlock()
for id := range r.cursors {
r.closeCursor(ctx, id)
c := r.removeCursor(ctx, id)
must.NotBeZero(c)
c.close(ctx)
}
r.cursors = nil
resource.Untrack(r, r.token)
}
// NewCursor stores a cursor with given continuation and connection (if any).
//
// As a special case, if continuation is empty, this method does nothing.
// That simplifies the typical usage.
func (r *Registry) NewCursor(id int64, continuation wirebson.RawDocument, conn *pgx.Conn) {
// to have better logging for now
var cont *wirebson.Document
if len(continuation) > 0 {
cont = must.NotFail(continuation.Decode())
}
persist := conn != nil
// TODO https://github.com/FerretDB/FerretDB-DocumentDB/issues/270
if len(continuation) == 0 {
if persist {
r.l.Debug(
"Not persisting connection with empty continuation",
slog.Int64("id", id), slog.Any("continuation", cont), slog.Bool("persist", persist),
)
_ = conn.Close(context.TODO())
}
if id != 0 {
r.l.Debug(
"Not storing cursor with empty continuation",
slog.Int64("id", id), slog.Any("continuation", cont), slog.Bool("persist", persist),
)
}
return
}
// Passed context is used for logging/tracing, and for closing existing cursor, if any.
// See [Registry.CloseCursor].
func (r *Registry) NewCursor(ctx context.Context, id int64, continuation wirebson.RawDocument, conn *pgx.Conn) {
must.NotBeZero(id)
must.BeTrue(len(continuation) > 0)
r.rw.Lock()
defer r.rw.Unlock()
if _, ok := r.cursors[id]; ok {
r.l.Error("Replacing existing cursor", slog.Int64("id", id))
r.closeCursor(context.TODO(), id)
c := newCursor(continuation, conn)
existing := r.cursors[id]
if existing != nil {
r.l.WarnContext(
ctx, "Replacing existing cursor",
slog.Int64("id", id), slog.Any("existing", existing), slog.Any("cursor", c),
)
r.removeCursor(ctx, id)
}
r.l.Debug("Creating new cursor",
slog.Int64("id", id), slog.Any("continuation", cont), slog.Bool("persist", persist),
)
r.l.DebugContext(ctx, "Storing new cursor", slog.Int64("id", id), slog.Any("cursor", c))
r.cursors[id] = newCursor(continuation, conn)
r.created.With(prometheus.Labels{"type": c.Type()}).Inc()
t := "normal"
if persist {
t = "persist"
r.cursors[id] = c
r.rw.Unlock()
if existing != nil {
existing.close(ctx)
}
user := "unknown" // TODO https://github.com/FerretDB/FerretDB/issues/3974
r.created.With(prometheus.Labels{"user": user, "type": t}).Inc()
}
// GetCursor returns the continuation and the connection for the given cursor id.
@@ -193,44 +159,46 @@ func (r *Registry) GetCursor(id int64) (wirebson.RawDocument, *pgx.Conn) {
return nil, nil
}
// UpdateCursor updates existing cursor with given continuation.
func (r *Registry) UpdateCursor(id int64, continuation wirebson.RawDocument) {
// to have better logging for now
var cont *wirebson.Document
if len(continuation) > 0 {
cont = must.NotFail(continuation.Decode())
}
// UpdateCursor updates existing cursor with given continuation,
// or closes it if continuation is empty.
//
// Passed context is used for logging/tracing,
// and for closing the cursor (see [Registry.CloseCursor]).
func (r *Registry) UpdateCursor(ctx context.Context, id int64, continuation wirebson.RawDocument) {
r.rw.Lock()
defer r.rw.Unlock()
c := r.cursors[id]
if c == nil {
r.l.Warn("Cursor not found", slog.Int64("id", id))
r.rw.Unlock()
r.l.WarnContext(ctx, "Cursor not found", slog.Int64("id", id))
return
}
persist := c.conn != nil
// TODO https://github.com/FerretDB/FerretDB-DocumentDB/issues/270
// TODO https://github.com/FerretDB/FerretDB/issues/5445
if len(continuation) == 0 {
r.l.Debug(
"Closing instead of updating cursor with empty continuation",
slog.Int64("id", id), slog.Any("continuation", cont), slog.Bool("persist", persist),
r.l.DebugContext(
ctx, "Closing instead of updating cursor with empty continuation",
slog.Int64("id", id), slog.Any("cursor", c),
)
r.removeCursor(ctx, id)
r.closeCursor(context.TODO(), id)
r.rw.Unlock()
c.close(ctx)
return
}
r.l.Debug(
"Updating cursor",
slog.Int64("id", id), slog.Any("continuation", cont), slog.Bool("persist", persist),
r.l.DebugContext(
ctx, "Updating cursor",
slog.Int64("id", id), slog.Any("cursor", c), slog.Any("continuation", logging.LazyDeepDecoder(continuation)),
)
c.continuation = continuation
r.rw.Unlock()
}
// CloseCursor closes the cursor with the given id and removes it from the registry.
// CloseCursor removes the cursor with the given id from the registry and closes it, if any.
// It returns true if the cursor was found and removed.
//
// It attempts a clean close by sending the exit message to PostgreSQL.
@@ -238,41 +206,44 @@ func (r *Registry) UpdateCursor(id int64, continuation wirebson.RawDocument) {
// The underlying connection will always be called regardless of any other errors.
func (r *Registry) CloseCursor(ctx context.Context, id int64) bool {
r.rw.Lock()
defer r.rw.Unlock()
return r.closeCursor(ctx, id)
}
c := r.removeCursor(ctx, id)
r.rw.Unlock()
// closeCursor is a private function that is wrapped by CloseCursor.
// It doesn't block RWMutex, hence it should be used only if necessary.
func (r *Registry) closeCursor(ctx context.Context, id int64) bool {
c := r.cursors[id]
if c == nil {
r.l.WarnContext(ctx, "Cursor not found", slog.Int64("id", id))
return false
}
dur := time.Since(c.created)
persist := c.conn != nil
r.l.DebugContext(
ctx, "Closing and removing cursor",
slog.Int64("id", id), slog.Bool("persist", persist), slog.Duration("duration", dur),
)
c.close(ctx)
delete(r.cursors, id)
t := "normal"
if persist {
t = "persist"
}
user := "unknown" // TODO https://github.com/FerretDB/FerretDB/issues/3974
r.duration.With(prometheus.Labels{"user": user, "type": t}).Observe(dur.Seconds())
return true
}
// removeCursor removes the cursor with the given id from the registry and returns it, if any.
// The caller is responsible for closing it.
// Registry's rw also should be held by the caller.
func (r *Registry) removeCursor(ctx context.Context, id int64) *cursor {
c := r.cursors[id]
if c == nil {
r.l.WarnContext(ctx, "Cursor not found", slog.Int64("id", id))
return nil
}
dur := time.Since(c.created)
r.l.DebugContext(
ctx, "Removing cursor",
slog.Int64("id", id), slog.Any("cursor", c), slog.Duration("duration", dur),
)
r.duration.With(prometheus.Labels{"type": c.Type()}).Observe(dur.Seconds())
delete(r.cursors, id)
return c
}
// Describe implements [prometheus.Collector].
func (r *Registry) Describe(ch chan<- *prometheus.Desc) {
r.created.Describe(ch)
+2 -2
View File
@@ -24,8 +24,8 @@ import "context"
// We use documentdb_api_catalog schema for `explain` command.
//
// We use documentdb_api_internal schema for indexes and authentication:
// TODO https://github.com/microsoft/documentdb/issues/25
// TODO https://github.com/microsoft/documentdb/issues/46
// TODO https://github.com/documentdb/documentdb/issues/25
// TODO https://github.com/documentdb/documentdb/issues/333
//
// And we generate code for documentdb_core just to track changes.
//
@@ -9,6 +9,7 @@ import (
"github.com/FerretDB/wire/wirebson"
"github.com/jackc/pgx/v5"
"go.opentelemetry.io/otel"
otelsemconv "go.opentelemetry.io/otel/semconv/v1.34.0"
oteltrace "go.opentelemetry.io/otel/trace"
"github.com/FerretDB/FerretDB/v2/internal/mongoerrors"
@@ -18,7 +19,14 @@ import (
//
// documentdb_api.aggregate_cursor_first_page(database text, commandspec documentdb_core.bson, cursorid bigint DEFAULT 0, OUT cursorpage documentdb_core.bson, OUT continuation documentdb_core.bson, OUT persistconnection boolean, OUT cursorid bigint).
func AggregateCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, commandSpec wirebson.RawDocument, cursorID int64) (outCursorPage wirebson.RawDocument, outContinuation wirebson.RawDocument, outPersistConnection bool, outCursorID int64, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.aggregate_cursor_first_page", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.AggregateCursorFirstPage",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.aggregate_cursor_first_page"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT cursorpage::bytea, continuation::bytea, persistconnection, cursorid FROM documentdb_api.aggregate_cursor_first_page($1, $2::bytea, $3)", database, commandSpec, cursorID)
@@ -32,7 +40,14 @@ func AggregateCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Logge
//
// documentdb_api.binary_extended_version(OUT binary_extended_version text).
func BinaryExtendedVersion(ctx context.Context, conn *pgx.Conn, l *slog.Logger) (outBinaryExtendedVersion string, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.binary_extended_version", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.BinaryExtendedVersion",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.binary_extended_version"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT binary_extended_version FROM documentdb_api.binary_extended_version()")
@@ -46,7 +61,14 @@ func BinaryExtendedVersion(ctx context.Context, conn *pgx.Conn, l *slog.Logger)
//
// documentdb_api.binary_version(OUT binary_version text).
func BinaryVersion(ctx context.Context, conn *pgx.Conn, l *slog.Logger) (outBinaryVersion string, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.binary_version", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.BinaryVersion",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.binary_version"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT binary_version FROM documentdb_api.binary_version()")
@@ -60,7 +82,14 @@ func BinaryVersion(ctx context.Context, conn *pgx.Conn, l *slog.Logger) (outBina
//
// documentdb_api.coll_mod(p_database_name text, p_collection_name text, p_spec documentdb_core.bson, OUT coll_mod documentdb_core.bson).
func CollMod(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string, spec wirebson.RawDocument) (outCollMod wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.coll_mod", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CollMod",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.coll_mod"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT coll_mod::bytea FROM documentdb_api.coll_mod($1, $2, $3::bytea)", databaseName, collectionName, spec)
@@ -74,7 +103,14 @@ func CollMod(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName s
//
// documentdb_api.coll_stats(p_database_name text, p_collection_name text, p_scale double precision DEFAULT 1, OUT coll_stats documentdb_core.bson).
func CollStats(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string, scale float64) (outCollStats wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.coll_stats", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CollStats",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.coll_stats"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT coll_stats::bytea FROM documentdb_api.coll_stats($1, $2, $3)", databaseName, collectionName, scale)
@@ -88,7 +124,14 @@ func CollStats(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName
//
// documentdb_api.collection(p_database_name text, p_collection_name text, OUT shard_key_value bigint, OUT object_id documentdb_core.bson, OUT document documentdb_core.bson).
func Collection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string) (outShardKeyValue int64, outObjectID wirebson.RawDocument, outDocument wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.Collection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT shard_key_value, object_id::bytea, document::bytea FROM documentdb_api.collection($1, $2)", databaseName, collectionName)
@@ -102,7 +145,14 @@ func Collection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseNam
//
// documentdb_api.compact(p_spec documentdb_core.bson, OUT compact documentdb_core.bson).
func Compact(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outCompact wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.compact", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.Compact",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.compact"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT compact::bytea FROM documentdb_api.compact($1::bytea)", spec)
@@ -116,7 +166,14 @@ func Compact(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.
//
// documentdb_api.connection_status(p_spec documentdb_core.bson, OUT connection_status documentdb_core.bson).
func ConnectionStatus(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outConnectionStatus wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.connection_status", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ConnectionStatus",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.connection_status"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT connection_status::bytea FROM documentdb_api.connection_status($1::bytea)", spec)
@@ -130,7 +187,14 @@ func ConnectionStatus(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec
//
// documentdb_api.count_query(database text, countspec documentdb_core.bson, OUT document documentdb_core.bson).
func CountQuery(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, countSpec wirebson.RawDocument) (outDocument wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.count_query", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CountQuery",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.count_query"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT document::bytea FROM documentdb_api.count_query($1, $2::bytea)", database, countSpec)
@@ -144,7 +208,14 @@ func CountQuery(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database st
//
// documentdb_api.create_collection(p_database_name text, p_collection_name text, OUT create_collection boolean).
func CreateCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string) (outCreateCollection bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.create_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CreateCollection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.create_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT create_collection FROM documentdb_api.create_collection($1, $2)", databaseName, collectionName)
@@ -158,7 +229,14 @@ func CreateCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, datab
//
// documentdb_api.create_collection_view(dbname text, createspec documentdb_core.bson, OUT create_collection_view documentdb_core.bson).
func CreateCollectionView(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, createSpec wirebson.RawDocument) (outCreateCollectionView wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.create_collection_view", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CreateCollectionView",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.create_collection_view"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT create_collection_view::bytea FROM documentdb_api.create_collection_view($1, $2::bytea)", database, createSpec)
@@ -172,7 +250,14 @@ func CreateCollectionView(ctx context.Context, conn *pgx.Conn, l *slog.Logger, d
//
// documentdb_api.create_indexes_background(p_database_name text, p_index_spec documentdb_core.bson, OUT retval documentdb_core.bson, OUT ok boolean, OUT requests documentdb_core.bson).
func CreateIndexesBackground(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, indexSpec wirebson.RawDocument) (outRetVal wirebson.RawDocument, outOk bool, outRequests wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.create_indexes_background", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CreateIndexesBackground",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.create_indexes_background"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT retval::bytea, ok, requests::bytea FROM documentdb_api.create_indexes_background($1, $2::bytea)", databaseName, indexSpec)
@@ -186,7 +271,14 @@ func CreateIndexesBackground(ctx context.Context, conn *pgx.Conn, l *slog.Logger
//
// documentdb_api.create_role(p_spec documentdb_core.bson, OUT create_role documentdb_core.bson).
func CreateRole(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outCreateRole wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.create_role", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CreateRole",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.create_role"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT create_role::bytea FROM documentdb_api.create_role($1::bytea)", spec)
@@ -200,7 +292,14 @@ func CreateRole(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebs
//
// documentdb_api.create_user(p_spec documentdb_core.bson, OUT create_user documentdb_core.bson).
func CreateUser(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outCreateUser wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.create_user", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CreateUser",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.create_user"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT create_user::bytea FROM documentdb_api.create_user($1::bytea)", spec)
@@ -214,7 +313,14 @@ func CreateUser(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebs
//
// documentdb_api.current_op_command(p_spec documentdb_core.bson, OUT document documentdb_core.bson).
func CurrentOpCommand(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outDocument wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.current_op_command", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CurrentOpCommand",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.current_op_command"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT document::bytea FROM documentdb_api.current_op_command($1::bytea)", spec)
@@ -228,7 +334,14 @@ func CurrentOpCommand(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec
//
// documentdb_api.cursor_get_more(database text, getmorespec documentdb_core.bson, continuationspec documentdb_core.bson, OUT cursorpage documentdb_core.bson, OUT continuation documentdb_core.bson).
func CursorGetMore(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, getMoreSpec wirebson.RawDocument, continuationSpec wirebson.RawDocument) (outCursorPage wirebson.RawDocument, outContinuation wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.cursor_get_more", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.CursorGetMore",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.cursor_get_more"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT cursorpage::bytea, continuation::bytea FROM documentdb_api.cursor_get_more($1, $2::bytea, $3::bytea)", database, getMoreSpec, continuationSpec)
@@ -242,7 +355,14 @@ func CursorGetMore(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database
//
// documentdb_api.db_stats(p_database_name text, p_scale double precision DEFAULT 1, p_freestorage boolean DEFAULT false, OUT db_stats documentdb_core.bson).
func DbStats(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, scale float64, freestorage bool) (outDbStats wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.db_stats", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DbStats",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.db_stats"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT db_stats::bytea FROM documentdb_api.db_stats($1, $2, $3)", databaseName, scale, freestorage)
@@ -256,7 +376,14 @@ func DbStats(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName s
//
// documentdb_api.delete(p_database_name text, p_delete documentdb_core.bson, p_insert_documents documentdb_core.bsonsequence DEFAULT NULL, OUT p_result documentdb_core.bson, OUT p_success boolean).
func Delete(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, delete wirebson.RawDocument, insertDocuments []byte) (outResult wirebson.RawDocument, outSuccess bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.delete", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.Delete",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.delete"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT p_result::bytea, p_success FROM documentdb_api.delete($1, $2::bytea, $3::bytea)", databaseName, delete, insertDocuments)
@@ -270,7 +397,14 @@ func Delete(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName st
//
// documentdb_api.distinct_query(database text, distinctspec documentdb_core.bson, OUT document documentdb_core.bson).
func DistinctQuery(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, distinctSpec wirebson.RawDocument) (outDocument wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.distinct_query", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DistinctQuery",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.distinct_query"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT document::bytea FROM documentdb_api.distinct_query($1, $2::bytea)", database, distinctSpec)
@@ -284,7 +418,14 @@ func DistinctQuery(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database
//
// documentdb_api.drop_collection(p_database_name text, p_collection_name text, p_write_concern documentdb_core.bson DEFAULT NULL, p_collection_uuid uuid DEFAULT NULL, p_track_changes boolean DEFAULT true, OUT drop_collection boolean).
func DropCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string, writeConcern wirebson.RawDocument, collectionUuid []byte, trackChanges bool) (outDropCollection bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.drop_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DropCollection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.drop_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT drop_collection FROM documentdb_api.drop_collection($1, $2, $3::bytea, $4, $5)", databaseName, collectionName, writeConcern, collectionUuid, trackChanges)
@@ -298,7 +439,14 @@ func DropCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databas
//
// documentdb_api.drop_database(p_database_name text, p_write_concern documentdb_core.bson DEFAULT NULL).
func DropDatabase(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, writeConcern wirebson.RawDocument) (err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.drop_database", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DropDatabase",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.drop_database"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT FROM documentdb_api.drop_database($1, $2::bytea)", databaseName, writeConcern)
@@ -312,7 +460,14 @@ func DropDatabase(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseN
//
// documentdb_api.drop_role(p_spec documentdb_core.bson, OUT drop_role documentdb_core.bson).
func DropRole(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outDropRole wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.drop_role", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DropRole",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.drop_role"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT drop_role::bytea FROM documentdb_api.drop_role($1::bytea)", spec)
@@ -326,7 +481,14 @@ func DropRole(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson
//
// documentdb_api.drop_user(p_spec documentdb_core.bson, OUT drop_user documentdb_core.bson).
func DropUser(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outDropUser wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.drop_user", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DropUser",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.drop_user"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT drop_user::bytea FROM documentdb_api.drop_user($1::bytea)", spec)
@@ -340,7 +502,14 @@ func DropUser(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson
//
// documentdb_api.find_and_modify(p_database_name text, p_message documentdb_core.bson, OUT p_result documentdb_core.bson, OUT p_success boolean).
func FindAndModify(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, message wirebson.RawDocument) (outResult wirebson.RawDocument, outSuccess bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.find_and_modify", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.FindAndModify",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.find_and_modify"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT p_result::bytea, p_success FROM documentdb_api.find_and_modify($1, $2::bytea)", databaseName, message)
@@ -354,7 +523,14 @@ func FindAndModify(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database
//
// documentdb_api.find_cursor_first_page(database text, commandspec documentdb_core.bson, cursorid bigint DEFAULT 0, OUT cursorpage documentdb_core.bson, OUT continuation documentdb_core.bson, OUT persistconnection boolean, OUT cursorid bigint).
func FindCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, commandSpec wirebson.RawDocument, cursorID int64) (outCursorPage wirebson.RawDocument, outContinuation wirebson.RawDocument, outPersistConnection bool, outCursorID int64, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.find_cursor_first_page", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.FindCursorFirstPage",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.find_cursor_first_page"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT cursorpage::bytea, continuation::bytea, persistconnection, cursorid FROM documentdb_api.find_cursor_first_page($1, $2::bytea, $3)", database, commandSpec, cursorID)
@@ -368,7 +544,14 @@ func FindCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Logger, da
//
// documentdb_api.insert(p_database_name text, p_insert documentdb_core.bson, p_insert_documents documentdb_core.bsonsequence DEFAULT NULL, OUT p_result documentdb_core.bson, OUT p_success boolean).
func Insert(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, insert wirebson.RawDocument, insertDocuments []byte) (outResult wirebson.RawDocument, outSuccess bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.insert", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.Insert",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.insert"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT p_result::bytea, p_success FROM documentdb_api.insert($1, $2::bytea, $3::bytea)", databaseName, insert, insertDocuments)
@@ -382,7 +565,14 @@ func Insert(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName st
//
// documentdb_api.insert_one(p_database_name text, p_collection_name text, p_document documentdb_core.bson, OUT insert_one documentdb_core.bson).
func InsertOne(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string, document wirebson.RawDocument) (outInsertOne wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.insert_one", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.InsertOne",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.insert_one"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT insert_one::bytea FROM documentdb_api.insert_one($1, $2, $3::bytea)", databaseName, collectionName, document)
@@ -396,7 +586,14 @@ func InsertOne(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName
//
// documentdb_api.list_collections_cursor_first_page(database text, commandspec documentdb_core.bson, cursorid bigint DEFAULT 0, OUT cursorpage documentdb_core.bson, OUT continuation documentdb_core.bson, OUT persistconnection boolean, OUT cursorid bigint).
func ListCollectionsCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, commandSpec wirebson.RawDocument, cursorID int64) (outCursorPage wirebson.RawDocument, outContinuation wirebson.RawDocument, outPersistConnection bool, outCursorID int64, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.list_collections_cursor_first_page", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ListCollectionsCursorFirstPage",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.list_collections_cursor_first_page"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT cursorpage::bytea, continuation::bytea, persistconnection, cursorid FROM documentdb_api.list_collections_cursor_first_page($1, $2::bytea, $3)", database, commandSpec, cursorID)
@@ -410,7 +607,14 @@ func ListCollectionsCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog
//
// documentdb_api.list_databases(p_list_databases_spec documentdb_core.bson, OUT list_databases documentdb_core.bson).
func ListDatabases(ctx context.Context, conn *pgx.Conn, l *slog.Logger, listDatabasesSpec wirebson.RawDocument) (outListDatabases wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.list_databases", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ListDatabases",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.list_databases"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT list_databases::bytea FROM documentdb_api.list_databases($1::bytea)", listDatabasesSpec)
@@ -424,7 +628,14 @@ func ListDatabases(ctx context.Context, conn *pgx.Conn, l *slog.Logger, listData
//
// documentdb_api.list_indexes_cursor_first_page(database text, commandspec documentdb_core.bson, cursorid bigint DEFAULT 0, OUT cursorpage documentdb_core.bson, OUT continuation documentdb_core.bson, OUT persistconnection boolean, OUT cursorid bigint).
func ListIndexesCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, commandSpec wirebson.RawDocument, cursorID int64) (outCursorPage wirebson.RawDocument, outContinuation wirebson.RawDocument, outPersistConnection bool, outCursorID int64, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.list_indexes_cursor_first_page", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ListIndexesCursorFirstPage",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.list_indexes_cursor_first_page"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT cursorpage::bytea, continuation::bytea, persistconnection, cursorid FROM documentdb_api.list_indexes_cursor_first_page($1, $2::bytea, $3)", database, commandSpec, cursorID)
@@ -438,7 +649,14 @@ func ListIndexesCursorFirstPage(ctx context.Context, conn *pgx.Conn, l *slog.Log
//
// documentdb_api.rename_collection(p_database_name text, p_collection_name text, p_target_name text, p_drop_target boolean DEFAULT false).
func RenameCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string, targetName string, dropTarget bool) (err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.rename_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.RenameCollection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.rename_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT FROM documentdb_api.rename_collection($1, $2, $3, $4)", databaseName, collectionName, targetName, dropTarget)
@@ -452,7 +670,14 @@ func RenameCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, datab
//
// documentdb_api.reshard_collection(p_shard_key_spec documentdb_core.bson).
func ReshardCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, shardKeySpec wirebson.RawDocument) (err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.reshard_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ReshardCollection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.reshard_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT FROM documentdb_api.reshard_collection($1::bytea)", shardKeySpec)
@@ -466,7 +691,14 @@ func ReshardCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, shar
//
// documentdb_api.roles_info(p_spec documentdb_core.bson, OUT roles_info documentdb_core.bson).
func RolesInfo(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outRolesInfo wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.roles_info", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.RolesInfo",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.roles_info"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT roles_info::bytea FROM documentdb_api.roles_info($1::bytea)", spec)
@@ -480,7 +712,14 @@ func RolesInfo(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebso
//
// documentdb_api.shard_collection(p_database_name text, p_collection_name text, p_shard_key documentdb_core.bson, p_is_reshard boolean DEFAULT true).
func ShardCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, collectionName string, shardKey wirebson.RawDocument, isReshard bool) (err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.shard_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ShardCollection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.shard_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT FROM documentdb_api.shard_collection($1, $2, $3::bytea, $4)", databaseName, collectionName, shardKey, isReshard)
@@ -494,7 +733,14 @@ func ShardCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databa
//
// documentdb_api.shard_collection(p_shard_key_spec documentdb_core.bson).
func ShardCollection1(ctx context.Context, conn *pgx.Conn, l *slog.Logger, shardKeySpec wirebson.RawDocument) (err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.shard_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.ShardCollection1",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.shard_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT FROM documentdb_api.shard_collection($1::bytea)", shardKeySpec)
@@ -508,7 +754,14 @@ func ShardCollection1(ctx context.Context, conn *pgx.Conn, l *slog.Logger, shard
//
// documentdb_api.unshard_collection(p_shard_key_spec documentdb_core.bson).
func UnshardCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, shardKeySpec wirebson.RawDocument) (err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.unshard_collection", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.UnshardCollection",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.unshard_collection"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT FROM documentdb_api.unshard_collection($1::bytea)", shardKeySpec)
@@ -522,7 +775,14 @@ func UnshardCollection(ctx context.Context, conn *pgx.Conn, l *slog.Logger, shar
//
// documentdb_api.update(p_database_name text, p_update documentdb_core.bson, p_insert_documents documentdb_core.bsonsequence DEFAULT NULL, OUT p_result documentdb_core.bson, OUT p_success boolean).
func Update(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, update wirebson.RawDocument, insertDocuments []byte) (outResult wirebson.RawDocument, outSuccess bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.update", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.Update",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.update"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT p_result::bytea, p_success FROM documentdb_api.update($1, $2::bytea, $3::bytea)", databaseName, update, insertDocuments)
@@ -536,7 +796,14 @@ func Update(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName st
//
// documentdb_api.update_role(p_spec documentdb_core.bson, OUT update_role documentdb_core.bson).
func UpdateRole(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outUpdateRole wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.update_role", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.UpdateRole",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.update_role"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT update_role::bytea FROM documentdb_api.update_role($1::bytea)", spec)
@@ -550,7 +817,14 @@ func UpdateRole(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebs
//
// documentdb_api.update_user(p_spec documentdb_core.bson, OUT update_user documentdb_core.bson).
func UpdateUser(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outUpdateUser wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.update_user", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.UpdateUser",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.update_user"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT update_user::bytea FROM documentdb_api.update_user($1::bytea)", spec)
@@ -564,7 +838,14 @@ func UpdateUser(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebs
//
// documentdb_api.users_info(p_spec documentdb_core.bson, OUT users_info documentdb_core.bson).
func UsersInfo(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebson.RawDocument) (outUsersInfo wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.users_info", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.UsersInfo",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.users_info"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT users_info::bytea FROM documentdb_api.users_info($1::bytea)", spec)
@@ -578,7 +859,14 @@ func UsersInfo(ctx context.Context, conn *pgx.Conn, l *slog.Logger, spec wirebso
//
// documentdb_api.validate(database text, validatespec documentdb_core.bson, OUT document documentdb_core.bson).
func Validate(ctx context.Context, conn *pgx.Conn, l *slog.Logger, database string, validateSpec wirebson.RawDocument) (outDocument wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.validate", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.Validate",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.validate"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT document::bytea FROM documentdb_api.validate($1, $2::bytea)", database, validateSpec)
@@ -21,6 +21,7 @@ import (
"github.com/FerretDB/wire/wirebson"
"github.com/jackc/pgx/v5"
"go.opentelemetry.io/otel"
otelsemconv "go.opentelemetry.io/otel/semconv/v1.34.0"
oteltrace "go.opentelemetry.io/otel/trace"
"github.com/FerretDB/FerretDB/v2/internal/documentdb/bsonhex"
@@ -31,11 +32,18 @@ import (
//
// documentdb_api.drop_indexes(p_database_name text, p_arg documentdb_core.bson, INOUT retval documentdb_core.bson DEFAULT NULL).
//
// TODO https://github.com/microsoft/documentdb/issues/49
// TODO https://github.com/documentdb/documentdb/issues/49
//
//nolint:lll // copied from generated code
func DropIndexes(ctx context.Context, conn *pgx.Conn, l *slog.Logger, databaseName string, arg wirebson.RawDocument, retVal wirebson.RawDocument) (outRetVal wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_api.drop_indexes", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_api.DropIndexes",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_api.drop_indexes"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "CALL documentdb_api.drop_indexes($1, $2::bytea, $3::bytea)", databaseName, arg, retVal)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -9,6 +9,7 @@ import (
"github.com/FerretDB/wire/wirebson"
"github.com/jackc/pgx/v5"
"go.opentelemetry.io/otel"
otelsemconv "go.opentelemetry.io/otel/semconv/v1.34.0"
oteltrace "go.opentelemetry.io/otel/trace"
"github.com/FerretDB/FerretDB/v2/internal/mongoerrors"
@@ -18,7 +19,14 @@ import (
//
// documentdb_core.bson_compare(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_compare integer).
func BsonCompare(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonCompare int32, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_compare", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonCompare",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_compare"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_compare FROM documentdb_core.bson_compare($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -32,7 +40,14 @@ func BsonCompare(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bson_equal(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_equal boolean).
func BsonEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonEqual bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_equal", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonEqual",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_equal"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_equal FROM documentdb_core.bson_equal($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -46,7 +61,14 @@ func BsonEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wi
//
// documentdb_core.bson_from_bytea(anonymous bytea, OUT bson_from_bytea documentdb_core.bson).
func BsonFromBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonFromBytea wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_from_bytea", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonFromBytea",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_from_bytea"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_from_bytea::bytea FROM documentdb_core.bson_from_bytea($1)", anonymous)
@@ -60,7 +82,14 @@ func BsonFromBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymou
//
// documentdb_core.bson_get_value(anonymous documentdb_core.bson, anonymous1 text, OUT bson_get_value documentdb_core.bson).
func BsonGetValue(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 string) (outBsonGetValue wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_get_value", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonGetValue",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_get_value"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_get_value::bytea FROM documentdb_core.bson_get_value($1::bytea, $2)", anonymous, anonymous1)
@@ -74,7 +103,14 @@ func BsonGetValue(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bson_get_value_text(anonymous documentdb_core.bson, anonymous1 text, OUT bson_get_value_text text).
func BsonGetValueText(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 string) (outBsonGetValueText string, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_get_value_text", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonGetValueText",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_get_value_text"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_get_value_text FROM documentdb_core.bson_get_value_text($1::bytea, $2)", anonymous, anonymous1)
@@ -88,7 +124,14 @@ func BsonGetValueText(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anony
//
// documentdb_core.bson_gt(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_gt boolean).
func BsonGt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonGt bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_gt", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonGt",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_gt"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_gt FROM documentdb_core.bson_gt($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -102,7 +145,14 @@ func BsonGt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wireb
//
// documentdb_core.bson_gte(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_gte boolean).
func BsonGte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonGte bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_gte", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonGte",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_gte"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_gte FROM documentdb_core.bson_gte($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -116,7 +166,14 @@ func BsonGte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wire
//
// documentdb_core.bson_hash_int4(anonymous documentdb_core.bson, OUT bson_hash_int4 integer).
func BsonHashInt4(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonHashInt4 int32, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_hash_int4", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonHashInt4",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_hash_int4"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_hash_int4 FROM documentdb_core.bson_hash_int4($1::bytea)", anonymous)
@@ -130,7 +187,14 @@ func BsonHashInt4(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bson_hash_int8(anonymous documentdb_core.bson, anonymous1 bigint, OUT bson_hash_int8 bigint).
func BsonHashInt8(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 int64) (outBsonHashInt8 int64, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_hash_int8", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonHashInt8",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_hash_int8"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_hash_int8 FROM documentdb_core.bson_hash_int8($1::bytea, $2)", anonymous, anonymous1)
@@ -144,7 +208,14 @@ func BsonHashInt8(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bson_hex_to_bson(anonymous cstring, OUT bson_hex_to_bson documentdb_core.bson).
func BsonHexToBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonHexToBson wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_hex_to_bson", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonHexToBson",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_hex_to_bson"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_hex_to_bson::bytea FROM documentdb_core.bson_hex_to_bson($1)", anonymous)
@@ -158,7 +229,14 @@ func BsonHexToBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymou
//
// documentdb_core.bson_in(anonymous cstring, OUT bson_in documentdb_core.bson).
func BsonIn(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonIn wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_in", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonIn",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_in"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_in::bytea FROM documentdb_core.bson_in($1)", anonymous)
@@ -172,7 +250,14 @@ func BsonIn(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struc
//
// documentdb_core.bson_in_range_interval(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, anonymous12 interval, anonymous123 boolean, anonymous1234 boolean, OUT bson_in_range_interval boolean).
func BsonInRangeInterval(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument, anonymous12 struct{}, anonymous123 bool, anonymous1234 bool) (outBsonInRangeInterval bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_in_range_interval", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonInRangeInterval",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_in_range_interval"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_in_range_interval FROM documentdb_core.bson_in_range_interval($1::bytea, $2::bytea, $3, $4, $5)", anonymous, anonymous1, anonymous12, anonymous123, anonymous1234)
@@ -186,7 +271,14 @@ func BsonInRangeInterval(ctx context.Context, conn *pgx.Conn, l *slog.Logger, an
//
// documentdb_core.bson_in_range_numeric(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, anonymous12 documentdb_core.bson, anonymous123 boolean, anonymous1234 boolean, OUT bson_in_range_numeric boolean).
func BsonInRangeNumeric(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument, anonymous12 wirebson.RawDocument, anonymous123 bool, anonymous1234 bool) (outBsonInRangeNumeric bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_in_range_numeric", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonInRangeNumeric",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_in_range_numeric"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_in_range_numeric FROM documentdb_core.bson_in_range_numeric($1::bytea, $2::bytea, $3::bytea, $4, $5)", anonymous, anonymous1, anonymous12, anonymous123, anonymous1234)
@@ -200,7 +292,14 @@ func BsonInRangeNumeric(ctx context.Context, conn *pgx.Conn, l *slog.Logger, ano
//
// documentdb_core.bson_json_to_bson(anonymous text, OUT bson_json_to_bson documentdb_core.bson).
func BsonJsonToBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous string) (outBsonJsonToBson wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_json_to_bson", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonJsonToBson",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_json_to_bson"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_json_to_bson::bytea FROM documentdb_core.bson_json_to_bson($1)", anonymous)
@@ -214,7 +313,14 @@ func BsonJsonToBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymo
//
// documentdb_core.bson_lt(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_lt boolean).
func BsonLt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonLt bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_lt", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonLt",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_lt"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_lt FROM documentdb_core.bson_lt($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -228,7 +334,14 @@ func BsonLt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wireb
//
// documentdb_core.bson_lte(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_lte boolean).
func BsonLte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonLte bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_lte", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonLte",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_lte"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_lte FROM documentdb_core.bson_lte($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -242,7 +355,14 @@ func BsonLte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wire
//
// documentdb_core.bson_not_equal(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_not_equal boolean).
func BsonNotEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonNotEqual bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_not_equal", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonNotEqual",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_not_equal"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_not_equal FROM documentdb_core.bson_not_equal($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -256,7 +376,14 @@ func BsonNotEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bson_object_keys(anonymous documentdb_core.bson, OUT bson_object_keys text).
func BsonObjectKeys(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonObjectKeys string, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_object_keys", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonObjectKeys",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_object_keys"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_object_keys FROM documentdb_core.bson_object_keys($1::bytea)", anonymous)
@@ -270,7 +397,14 @@ func BsonObjectKeys(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymo
//
// documentdb_core.bson_operator_selectivity(anonymous internal, anonymous1 oid, anonymous12 internal, anonymous123 integer, OUT bson_operator_selectivity double precision).
func BsonOperatorSelectivity(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}, anonymous12 struct{}, anonymous123 int32) (outBsonOperatorSelectivity float64, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_operator_selectivity", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonOperatorSelectivity",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_operator_selectivity"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_operator_selectivity FROM documentdb_core.bson_operator_selectivity($1, $2, $3, $4)", anonymous, anonymous1, anonymous12, anonymous123)
@@ -284,7 +418,14 @@ func BsonOperatorSelectivity(ctx context.Context, conn *pgx.Conn, l *slog.Logger
//
// documentdb_core.bson_out(anonymous documentdb_core.bson, OUT bson_out cstring).
func BsonOut(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonOut struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_out", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonOut",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_out"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_out FROM documentdb_core.bson_out($1::bytea)", anonymous)
@@ -298,7 +439,14 @@ func BsonOut(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wire
//
// documentdb_core.bson_recv(anonymous internal, OUT bson_recv documentdb_core.bson).
func BsonRecv(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonRecv wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_recv", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonRecv",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_recv"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_recv::bytea FROM documentdb_core.bson_recv($1)", anonymous)
@@ -312,7 +460,14 @@ func BsonRecv(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous str
//
// documentdb_core.bson_repath_and_build(anonymous "any", OUT bson_repath_and_build documentdb_core.bson).
func BsonRepathAndBuild(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonRepathAndBuild wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_repath_and_build", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonRepathAndBuild",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_repath_and_build"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_repath_and_build::bytea FROM documentdb_core.bson_repath_and_build($1)", anonymous)
@@ -326,7 +481,14 @@ func BsonRepathAndBuild(ctx context.Context, conn *pgx.Conn, l *slog.Logger, ano
//
// documentdb_core.bson_send(anonymous documentdb_core.bson, OUT bson_send bytea).
func BsonSend(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonSend struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_send", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonSend",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_send"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_send FROM documentdb_core.bson_send($1::bytea)", anonymous)
@@ -340,7 +502,14 @@ func BsonSend(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wir
//
// documentdb_core.bson_to_bson_hex(anonymous documentdb_core.bson, OUT bson_to_bson_hex cstring).
func BsonToBsonHex(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonToBsonHex struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_to_bson_hex", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonToBsonHex",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_to_bson_hex"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_to_bson_hex FROM documentdb_core.bson_to_bson_hex($1::bytea)", anonymous)
@@ -354,7 +523,14 @@ func BsonToBsonHex(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymou
//
// documentdb_core.bson_to_bsonsequence(anonymous documentdb_core.bson, OUT bson_to_bsonsequence documentdb_core.bsonsequence).
func BsonToBsonsequence(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonToBsonsequence []byte, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_to_bsonsequence", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonToBsonsequence",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_to_bsonsequence"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_to_bsonsequence::bytea FROM documentdb_core.bson_to_bsonsequence($1::bytea)", anonymous)
@@ -368,7 +544,14 @@ func BsonToBsonsequence(ctx context.Context, conn *pgx.Conn, l *slog.Logger, ano
//
// documentdb_core.bson_to_bytea(anonymous documentdb_core.bson, OUT bson_to_bytea bytea).
func BsonToBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonToBytea struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_to_bytea", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonToBytea",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_to_bytea"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_to_bytea FROM documentdb_core.bson_to_bytea($1::bytea)", anonymous)
@@ -382,7 +565,14 @@ func BsonToBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bson_to_json_string(anonymous documentdb_core.bson, OUT bson_to_json_string cstring).
func BsonToJsonString(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument) (outBsonToJsonString struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_to_json_string", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonToJsonString",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_to_json_string"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_to_json_string FROM documentdb_core.bson_to_json_string($1::bytea)", anonymous)
@@ -396,7 +586,14 @@ func BsonToJsonString(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anony
//
// documentdb_core.bson_typanalyze(anonymous internal, OUT bson_typanalyze boolean).
func BsonTypanalyze(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonTypanalyze bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_typanalyze", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonTypanalyze",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_typanalyze"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_typanalyze FROM documentdb_core.bson_typanalyze($1)", anonymous)
@@ -410,7 +607,14 @@ func BsonTypanalyze(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymo
//
// documentdb_core.bson_unique_index_equal(anonymous documentdb_core.bson, anonymous1 documentdb_core.bson, OUT bson_unique_index_equal boolean).
func BsonUniqueIndexEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 wirebson.RawDocument) (outBsonUniqueIndexEqual bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bson_unique_index_equal", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonUniqueIndexEqual",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bson_unique_index_equal"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bson_unique_index_equal FROM documentdb_core.bson_unique_index_equal($1::bytea, $2::bytea)", anonymous, anonymous1)
@@ -424,7 +628,14 @@ func BsonUniqueIndexEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, a
//
// documentdb_core.bsonquery_compare(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_compare integer).
func BsonqueryCompare(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryCompare int32, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_compare", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryCompare",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_compare"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_compare FROM documentdb_core.bsonquery_compare($1, $2)", anonymous, anonymous1)
@@ -438,7 +649,14 @@ func BsonqueryCompare(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anony
//
// documentdb_core.bsonquery_compare(anonymous documentdb_core.bson, anonymous1 documentdb_core.bsonquery, OUT bsonquery_compare integer).
func BsonqueryCompare1(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous wirebson.RawDocument, anonymous1 struct{}) (outBsonqueryCompare int32, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_compare", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryCompare1",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_compare"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_compare FROM documentdb_core.bsonquery_compare($1::bytea, $2)", anonymous, anonymous1)
@@ -452,7 +670,14 @@ func BsonqueryCompare1(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anon
//
// documentdb_core.bsonquery_equal(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_equal boolean).
func BsonqueryEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryEqual bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_equal", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryEqual",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_equal"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_equal FROM documentdb_core.bsonquery_equal($1, $2)", anonymous, anonymous1)
@@ -466,7 +691,14 @@ func BsonqueryEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymo
//
// documentdb_core.bsonquery_gt(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_gt boolean).
func BsonqueryGt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryGt bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_gt", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryGt",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_gt"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_gt FROM documentdb_core.bsonquery_gt($1, $2)", anonymous, anonymous1)
@@ -480,7 +712,14 @@ func BsonqueryGt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bsonquery_gte(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_gte boolean).
func BsonqueryGte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryGte bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_gte", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryGte",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_gte"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_gte FROM documentdb_core.bsonquery_gte($1, $2)", anonymous, anonymous1)
@@ -494,7 +733,14 @@ func BsonqueryGte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bsonquery_in(anonymous cstring, OUT bsonquery_in documentdb_core.bsonquery).
func BsonqueryIn(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonqueryIn struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_in", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryIn",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_in"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_in FROM documentdb_core.bsonquery_in($1)", anonymous)
@@ -508,7 +754,14 @@ func BsonqueryIn(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bsonquery_lt(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_lt boolean).
func BsonqueryLt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryLt bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_lt", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryLt",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_lt"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_lt FROM documentdb_core.bsonquery_lt($1, $2)", anonymous, anonymous1)
@@ -522,7 +775,14 @@ func BsonqueryLt(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bsonquery_lte(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_lte boolean).
func BsonqueryLte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryLte bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_lte", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryLte",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_lte"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_lte FROM documentdb_core.bsonquery_lte($1, $2)", anonymous, anonymous1)
@@ -536,7 +796,14 @@ func BsonqueryLte(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bsonquery_not_equal(anonymous documentdb_core.bsonquery, anonymous1 documentdb_core.bsonquery, OUT bsonquery_not_equal boolean).
func BsonqueryNotEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}, anonymous1 struct{}) (outBsonqueryNotEqual bool, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_not_equal", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryNotEqual",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_not_equal"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_not_equal FROM documentdb_core.bsonquery_not_equal($1, $2)", anonymous, anonymous1)
@@ -550,7 +817,14 @@ func BsonqueryNotEqual(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anon
//
// documentdb_core.bsonquery_out(anonymous documentdb_core.bsonquery, OUT bsonquery_out cstring).
func BsonqueryOut(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonqueryOut struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_out", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryOut",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_out"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_out FROM documentdb_core.bsonquery_out($1)", anonymous)
@@ -564,7 +838,14 @@ func BsonqueryOut(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous
//
// documentdb_core.bsonquery_recv(anonymous internal, OUT bsonquery_recv documentdb_core.bsonquery).
func BsonqueryRecv(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonqueryRecv struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_recv", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonqueryRecv",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_recv"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_recv FROM documentdb_core.bsonquery_recv($1)", anonymous)
@@ -578,7 +859,14 @@ func BsonqueryRecv(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymou
//
// documentdb_core.bsonquery_send(anonymous documentdb_core.bsonquery, OUT bsonquery_send bytea).
func BsonquerySend(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonquerySend struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonquery_send", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonquerySend",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonquery_send"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonquery_send FROM documentdb_core.bsonquery_send($1)", anonymous)
@@ -592,7 +880,14 @@ func BsonquerySend(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymou
//
// documentdb_core.bsonsequence_from_bytea(anonymous bytea, OUT bsonsequence_from_bytea documentdb_core.bsonsequence).
func BsonsequenceFromBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonsequenceFromBytea []byte, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_from_bytea", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceFromBytea",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_from_bytea"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_from_bytea::bytea FROM documentdb_core.bsonsequence_from_bytea($1)", anonymous)
@@ -606,7 +901,14 @@ func BsonsequenceFromBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger,
//
// documentdb_core.bsonsequence_get_bson(anonymous documentdb_core.bsonsequence, OUT bsonsequence_get_bson documentdb_core.bson).
func BsonsequenceGetBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous []byte) (outBsonsequenceGetBson wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_get_bson", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceGetBson",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_get_bson"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_get_bson::bytea FROM documentdb_core.bsonsequence_get_bson($1::bytea)", anonymous)
@@ -620,7 +922,14 @@ func BsonsequenceGetBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, an
//
// documentdb_core.bsonsequence_in(anonymous cstring, OUT bsonsequence_in documentdb_core.bsonsequence).
func BsonsequenceIn(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonsequenceIn []byte, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_in", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceIn",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_in"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_in::bytea FROM documentdb_core.bsonsequence_in($1)", anonymous)
@@ -634,7 +943,14 @@ func BsonsequenceIn(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymo
//
// documentdb_core.bsonsequence_out(anonymous documentdb_core.bsonsequence, OUT bsonsequence_out cstring).
func BsonsequenceOut(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous []byte) (outBsonsequenceOut struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_out", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceOut",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_out"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_out FROM documentdb_core.bsonsequence_out($1::bytea)", anonymous)
@@ -648,7 +964,14 @@ func BsonsequenceOut(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonym
//
// documentdb_core.bsonsequence_recv(anonymous internal, OUT bsonsequence_recv documentdb_core.bsonsequence).
func BsonsequenceRecv(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outBsonsequenceRecv []byte, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_recv", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceRecv",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_recv"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_recv::bytea FROM documentdb_core.bsonsequence_recv($1)", anonymous)
@@ -662,7 +985,14 @@ func BsonsequenceRecv(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anony
//
// documentdb_core.bsonsequence_send(anonymous documentdb_core.bsonsequence, OUT bsonsequence_send bytea).
func BsonsequenceSend(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous []byte) (outBsonsequenceSend struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_send", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceSend",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_send"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_send FROM documentdb_core.bsonsequence_send($1::bytea)", anonymous)
@@ -676,7 +1006,14 @@ func BsonsequenceSend(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anony
//
// documentdb_core.bsonsequence_to_bytea(anonymous documentdb_core.bsonsequence, OUT bsonsequence_to_bytea bytea).
func BsonsequenceToBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous []byte) (outBsonsequenceToBytea struct{}, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.bsonsequence_to_bytea", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.BsonsequenceToBytea",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.bsonsequence_to_bytea"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT bsonsequence_to_bytea FROM documentdb_core.bsonsequence_to_bytea($1::bytea)", anonymous)
@@ -690,7 +1027,14 @@ func BsonsequenceToBytea(ctx context.Context, conn *pgx.Conn, l *slog.Logger, an
//
// documentdb_core.row_get_bson(anonymous record, OUT row_get_bson documentdb_core.bson).
func RowGetBson(ctx context.Context, conn *pgx.Conn, l *slog.Logger, anonymous struct{}) (outRowGetBson wirebson.RawDocument, err error) {
ctx, span := otel.Tracer("").Start(ctx, "documentdb_core.row_get_bson", oteltrace.WithSpanKind(oteltrace.SpanKindClient))
ctx, span := otel.Tracer("").Start(
ctx,
"documentdb_core.RowGetBson",
oteltrace.WithSpanKind(oteltrace.SpanKindClient),
oteltrace.WithAttributes(
otelsemconv.DBStoredProcedureName("documentdb_core.row_get_bson"),
),
)
defer span.End()
row := conn.QueryRow(ctx, "SELECT row_get_bson::bytea FROM documentdb_core.row_get_bson($1)", anonymous)
+42 -1
View File
@@ -17,6 +17,7 @@ package documentdb
import (
"context"
"fmt"
"runtime"
"testing"
"github.com/FerretDB/wire/wirebson"
@@ -31,6 +32,7 @@ import (
"github.com/FerretDB/FerretDB/v2/internal/util/must"
"github.com/FerretDB/FerretDB/v2/internal/util/state"
"github.com/FerretDB/FerretDB/v2/internal/util/testutil"
"github.com/FerretDB/FerretDB/v2/internal/util/testutil/teststress"
)
// testPool tries to create a new pool of PostgreSQL connections and use it.
@@ -38,7 +40,8 @@ import (
func testPool(t testing.TB, ctx context.Context, uri string, sp *state.Provider) (error, error) {
t.Helper()
pool, err := newPgxPool(uri, testutil.Logger(t), sp)
l := testutil.Logger(t)
pool, err := newPgxPool(uri, l, newTracer(l), sp)
if err != nil {
return err, nil
}
@@ -234,3 +237,41 @@ func TestSeqNoSeq(t *testing.T) {
)
wiretest.AssertEqual(t, expected, res.Get("cursor").(*wirebson.Document).Get("firstBatch"))
}
func TestWithConn(t *testing.T) {
sp, err := state.NewProvider("")
require.NoError(t, err)
pool, err := NewPool(testutil.PostgreSQLURL(t), testutil.Logger(t), sp)
require.NoError(t, err)
defer pool.Close()
// TODO https://github.com/FerretDB/FerretDB/issues/5446
teststress.Stress(t, func(ready chan<- struct{}, start <-chan struct{}) {
ready <- struct{}{}
<-start
_ = pool.WithConn(func(conn *pgx.Conn) error {
must.NotBeZero(conn)
for range 10 {
runtime.GC()
runtime.Gosched()
}
return nil
})
for range 10 {
runtime.GC()
runtime.Gosched()
}
})
for range 10 {
runtime.GC()
runtime.Gosched()
}
}
+2 -1
View File
@@ -112,6 +112,7 @@ func Convert(rows []map[string]any, l *slog.Logger) map[string]map[string]templa
uniqueFunctionName := c.uniqueName(slices.Collect(maps.Keys(schemas[schema])), routineName)
r := templateData{
Package: schema,
FuncName: c.pascalCase(uniqueFunctionName),
SQLFuncName: fmt.Sprintf("%s.%s", schema, routineName),
Comment: fmt.Sprintf("%s.%s(%s)", schema, routineName, strings.Join(comment, ", ")),
@@ -124,7 +125,7 @@ func Convert(rows []map[string]any, l *slog.Logger) map[string]map[string]templa
ScanArgs: strings.Join(scanArgs, ", "),
}
// TODO https://github.com/microsoft/documentdb/issues/49
// TODO https://github.com/documentdb/documentdb/issues/49
if r.IsProcedure {
l.Warn("Procedure skipped due to unable to decode output", slog.String("function", r.SQLFuncName))
Loaded 100 of 357 files, more files were not shown because too many files have changed in this diff. Show more