From 5985fe13f2e4d9b0072fa1bcf0b7bd9f9949f7e5 Mon Sep 17 00:00:00 2001 From: Borislav Borisov Date: Sat, 8 Aug 2026 15:26:30 +0100 Subject: [PATCH 1/2] chore: Update dependencies --- Cargo.toml | 10 +++++----- tests/mysql.rs | 10 ++++++---- tests/postgres.rs | 10 ++++++---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f33f1d8..b0f7da5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,8 +24,8 @@ pedantic = "warn" [dependencies] futures = "0.3" -opentelemetry = "0.31" -opentelemetry-semantic-conventions = { version = "0.31", features = ["semconv_experimental"] } +opentelemetry = "0.32" +opentelemetry-semantic-conventions = { version = "0.32", features = ["semconv_experimental"] } sqlx = { version = "0.8", default-features = false } async-std = { version = "1", optional = true } tokio = { version = "1", features = ["rt", "time"], optional = true } @@ -42,12 +42,12 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dev-dependencies] -ctor = "0.10" -opentelemetry_sdk = { version = "0.31", features = ["testing", "rt-tokio"] } +dtor = "1" +opentelemetry_sdk = { version = "0.32", features = ["testing", "rt-tokio"] } proptest = "1" serial_test = "3" sqlx = { version = "0.8", features = ["macros", "sqlite", "postgres", "mysql", "runtime-tokio"] } -testcontainers = "0.27" +testcontainers = "0.28" tokio = { version = "1", features = ["macros", "rt-multi-thread"] } [[example]] diff --git a/tests/mysql.rs b/tests/mysql.rs index 92269ec..ab8e133 100644 --- a/tests/mysql.rs +++ b/tests/mysql.rs @@ -63,11 +63,13 @@ async fn shared_container() -> &'static SharedContainer { /// Stop and remove the shared container at process exit. Required because the /// `ContainerAsync` value lives in a `'static` (`CONTAINER`), so the language never -/// runs its `Drop`. Using `ctor::dtor` schedules a synchronous shell-out to +/// runs its `Drop`. Using `dtor::dtor` schedules a synchronous shell-out to /// `docker rm -f` that fires after `main` returns – equivalent to the per-test -/// RAII cleanup that existed before the shared-container refactor (commit c29f995). -#[ctor::dtor] -fn drop_container() { +/// RAII cleanup that existed before the shared-container refactor (commit c29f995). The fn is +/// `unsafe` because `dtor` runs it outside the Rust runtime, where most std facilities carry no +/// guarantees; spawning a subprocess and reading a `OnceLock` is safe in practice. +#[dtor::dtor] +unsafe fn drop_container() { if let Some(id) = CONTAINER_ID.get() { let _ = std::process::Command::new("docker") .args(["rm", "-f", id.as_str()]) diff --git a/tests/postgres.rs b/tests/postgres.rs index d3f4e0d..0380ba3 100644 --- a/tests/postgres.rs +++ b/tests/postgres.rs @@ -63,11 +63,13 @@ async fn shared_container() -> &'static SharedContainer { /// Stop and remove the shared container at process exit. Required because the /// `ContainerAsync` value lives in a `'static` (`CONTAINER`), so the language never -/// runs its `Drop`. Using `ctor::dtor` schedules a synchronous shell-out to +/// runs its `Drop`. Using `dtor::dtor` schedules a synchronous shell-out to /// `docker rm -f` that fires after `main` returns – equivalent to the per-test -/// RAII cleanup that existed before the shared-container refactor (commit c29f995). -#[ctor::dtor] -fn drop_container() { +/// RAII cleanup that existed before the shared-container refactor (commit c29f995). The fn is +/// `unsafe` because `dtor` runs it outside the Rust runtime, where most std facilities carry no +/// guarantees; spawning a subprocess and reading a `OnceLock` is safe in practice. +#[dtor::dtor] +unsafe fn drop_container() { if let Some(id) = CONTAINER_ID.get() { let _ = std::process::Command::new("docker") .args(["rm", "-f", id.as_str()]) From 0d717b46256589a6f27106ca2c5a98a096499492 Mon Sep 17 00:00:00 2001 From: Borislav Borisov Date: Sat, 8 Aug 2026 17:59:32 +0100 Subject: [PATCH 2/2] chore: Bump to 0.4.0 Cuts the release covering the opentelemetry 0.32 upgrade and the pool-metrics shutdown fix. Adds a per-release compatibility matrix and writes down the versioning policy the crate has followed since 0.1.0. Both sqlx and opentelemetry have to be upgraded in lockstep with this crate, and they fail differently when they are not: sqlx types are in the public signatures, so a mismatch is a compile error, whereas opentelemetry is coupled only through its `global` provider registry, so a mismatch compiles fine and silently emits nothing. Neither constraint was documented anywhere. Corrects a CHANGELOG slip: v0.3.0 was tagged and published on 2026-05-05 but its notes were still sitting under `## [Unreleased]`. --- CHANGELOG.md | 19 +++++++++++++++++-- Cargo.toml | 2 +- README.md | 27 +++++++++++++++++++++++---- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e4d83..6126779 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.4.0] – 2026-08-08 + +### Changed + +- **Action required:** Upgraded to `opentelemetry` / `opentelemetry-semantic-conventions` `0.32`. Upgrade your own `opentelemetry` and `opentelemetry_sdk` to match, or spans and metrics stop being emitted. + +### Fixed + +- The pool-metrics polling task no longer stops when a single cloned `Pool` is dropped. All clones share one `Arc` shutdown flag, so the first clone to drop was flipping it and silently ending `db.client.connection.*` collection for every surviving clone. The task now stops only once the last clone is dropped, matching what the documentation already implied. + +### Internal + +- Test-only: replaced `ctor` with the `dtor` crate, whose `#[dtor]` macro was split out of `ctor` and removed in `ctor` 1.0, and marked the container-cleanup destructors `unsafe` per `dtor` 1.0's deprecation of the safe form. Bumped `testcontainers` to `0.28`. No effect on the published crate. + +## [0.3.0] – 2026-05-05 ### Added @@ -46,6 +60,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), - SQLite backend ([#3](https://github.com/chmodas/sqlx-otel/pull/3)). - Backend-agnostic instrumentation core: `Pool`, `PoolBuilder`, `Transaction`, `PoolConnection`, and the `Executor` trait wiring that emits OpenTelemetry-native spans and metrics following the [database calls and systems](https://opentelemetry.io/docs/specs/semconv/db/) semantic conventions ([#2](https://github.com/chmodas/sqlx-otel/pull/2)). -[Unreleased]: https://github.com/chmodas/sqlx-otel/compare/v0.1.0...HEAD [0.1.0]: https://github.com/chmodas/sqlx-otel/releases/tag/v0.1.0 [0.2.0]: https://github.com/chmodas/sqlx-otel/releases/tag/v0.2.0 +[0.3.0]: https://github.com/chmodas/sqlx-otel/releases/tag/v0.3.0 +[0.4.0]: https://github.com/chmodas/sqlx-otel/releases/tag/v0.4.0 diff --git a/Cargo.toml b/Cargo.toml index b0f7da5..9f438b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sqlx-otel" -version = "0.3.0" +version = "0.4.0" edition = "2024" authors = ["Borislav Borisov "] license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 19d8642..434155b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The wrapper talks to the [`opentelemetry`](https://docs.rs/opentelemetry) API di ```toml [dependencies] -sqlx-otel = { version = "0.2.0", features = ["postgres", "runtime-tokio"] } +sqlx-otel = { version = "0.4.0", features = ["postgres", "runtime-tokio"] } ``` ```rust @@ -221,9 +221,28 @@ The first four are recorded inline on every `acquire()` / connection drop – no ## Compatibility -- **MSRV:** Rust **1.85.0**. -- **SQLx:** `0.8.x`. -- **OpenTelemetry:** `0.31.x`. +| sqlx-otel | sqlx | opentelemetry | MSRV | +| --------- | ------- | ------------- | -------- | +| `0.4.x` | `0.8.x` | `0.32.x` | `1.85.0` | +| `0.3.x` | `0.8.x` | `0.31.x` | `1.85.0` | +| `0.2.x` | `0.8.x` | `0.31.x` | `1.85.0` | +| `0.1.x` | `0.8.x` | `0.31.x` | `1.85.0` | + +Pick the row matching the `sqlx` and `opentelemetry` versions your application already uses. Older releases stay on crates.io indefinitely and are never yanked, so pinning to an earlier row is a supported way to stay put. + +## Versioning policy + +`sqlx` and `opentelemetry` are both pre-1.0, so Cargo compares their *minor* version for compatibility: `0.31` and `0.32` are separate ranges that never unify, whether or not the release actually changed anything. If your application's versions drift from the ones sqlx-otel was built against, Cargo resolves both copies into the build graph, and the two libraries fail differently. + +**`sqlx` fails at compile time.** Its types are part of this crate's public API, and types from semver-incompatible releases are distinct types. A `Pool` from `sqlx` `0.9` will not go into a wrapper built against `sqlx` `0.8`. + +**`opentelemetry` fails silently at runtime.** It coordinates through process-global state, so two copies means two registries. Your application installs its provider in one; the wrapper emits into the other. Spans and metrics disappear, with no error and no warning. + +Hence: + +- Either library bumping its minor triggers a minor release here. +- Bugs are not backported. Fixes ship in the next release. +- MSRV tracks [sqlx's MSRV policy](https://github.com/transact-rs/sqlx/blob/main/FAQ.md#MSRV). ## License