Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5e15229
feat(sentry-types): Add `Envelope::into_items` method
szokeasaurusrex Feb 5, 2026
b04b1d2
fix(core): Make HubSwitchGuard !Send to prevent thread corruption
szokeasaurusrex Jan 14, 2026
c77642e
style(tracing): import HubSwitchGuard
szokeasaurusrex Jan 28, 2026
ab3f7b9
chore: remove pr-findings doc
szokeasaurusrex Jan 28, 2026
ddf0e1f
fix(tracing): drop hub guard before span lookup
szokeasaurusrex Jan 30, 2026
6169b08
fix issue #946
szokeasaurusrex Feb 5, 2026
4e1c529
avoid unneeded clones
szokeasaurusrex Feb 5, 2026
96bcd82
reorganize, add some comments
szokeasaurusrex Feb 5, 2026
1f99fe5
ref(cargo): Extract common items to workspace
szokeasaurusrex Feb 3, 2026
3dccbf3
chore(msrv): Increase MSRV to 1.88 (#970)
szokeasaurusrex Feb 9, 2026
972a0ae
build(deps): bump time from 0.3.41 to 0.3.47 (#984)
dependabot[bot] Feb 9, 2026
0b1ab41
ci: Use `cargo test` instead of `nextest` (#986)
szokeasaurusrex Feb 11, 2026
86c2d44
ci: Replace make targets and remove Makefile (#987)
szokeasaurusrex Feb 12, 2026
1fdbf9e
ci: enforce workflow-level RUSTFLAGS in CI (#988)
szokeasaurusrex Feb 12, 2026
6f94b69
ci: Add required jobs aggregator check (#990)
szokeasaurusrex Feb 13, 2026
e080911
ci: align stable and MSRV check/test coverage
szokeasaurusrex Feb 12, 2026
3defd7a
ci: Run cargo CI jobs with --locked
szokeasaurusrex Feb 12, 2026
c0c5ac6
ci: Add CI concurrency cancel-in-progress
szokeasaurusrex Feb 13, 2026
97175ff
ci(workflow): Split CI lanes into reusable workflows
szokeasaurusrex Feb 13, 2026
173b883
build: `cargo update` (#968)
szokeasaurusrex Feb 16, 2026
35c8ba1
ci(release): Switch from action-prepare-release to Craft (#955)
BYK Feb 16, 2026
a8cbd6c
feat: expose transport utilities (#949)
thomaseizinger Feb 16, 2026
fe28971
chore(repo): Add Claude Code settings with basic permissions (#959)
philipphofmann Feb 16, 2026
c73d834
Merge branch 'master' into szokeasaurusrex/hubswitchguard
szokeasaurusrex Feb 23, 2026
c4f2f72
add benchmark for #957
szokeasaurusrex Feb 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
- Added a `Envelope::into_items` method, which returns an iterator over owned [`EnvelopeItem`s](https://docs.rs/sentry/0.46.2/sentry/protocol/enum.EnvelopeItem.html) in the [`Envelope`](https://docs.rs/sentry/0.46.2/sentry/struct.Envelope.html) ([#983](https://github.com/getsentry/sentry-rust/pull/983)).
- Expose transport utilities ([#949](https://github.com/getsentry/sentry-rust/pull/949))

### Fixes

- Fixed thread corruption bug where `HubSwitchGuard` could be dropped on wrong thread ([#957](https://github.com/getsentry/sentry-rust/pull/957))
- **Breaking change**: `sentry_core::HubSwitchGuard` is now `!Send`, preventing it from being moved across threads. Code that previously sent the guard to another thread will no longer compile.

## 0.46.2

### New Features
Expand Down
59 changes: 57 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 53 additions & 0 deletions docs/2026-02-24-tracing-hub-forking-benchmark-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Tracing hub-forking benchmark setup

## What was added

- `sentry-tracing/Cargo.toml`
- Added dev dependency: `criterion = "0.8.2"`
- Added bench target:
- `[[bench]]`
- `name = "tracing_layer_perf"`
- `harness = false`

- `sentry-tracing/benches/tracing_layer_perf.rs`
- Criterion benchmarks for:
- `enter_exit_existing_span`
- `create_enter_exit_close_span`
- `reenter_same_span_depth2`
- `cross_thread_shared_span`
- Each scenario runs in:
- `sentry_active`
- `tracing_only_control`

- `scripts/bench/compare-tracing-perf-master-vs-head.sh`
- Automates `origin/master` vs current branch comparison
- Creates/reuses a master worktree at `target/bench-worktrees/tracing-perf-master`
- Reuses current checkout as head candidate
- Runs identical `cargo bench` commands on both
- Saves artifacts to `target/bench-compare/<timestamp>/`
- Produces:
- raw logs
- commands used
- summary markdown table with deltas

## How to run

Full profile (default):

```bash
scripts/bench/compare-tracing-perf-master-vs-head.sh
```

Reduced profile:

```bash
scripts/bench/compare-tracing-perf-master-vs-head.sh --reduced
```

## Output

Artifacts are written to:

- `target/bench-compare/<timestamp>/summary.md`
- `target/bench-compare/<timestamp>/commands.txt`
- `target/bench-compare/<timestamp>/raw/`
160 changes: 160 additions & 0 deletions scripts/bench/compare-tracing-perf-master-vs-head.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
#!/usr/bin/env bash
set -euo pipefail

profile="full"
if [[ "${1:-}" == "--reduced" ]]; then
profile="reduced"
fi

if [[ "$profile" == "full" ]]; then
sample_size=100
measurement_time=10
warm_up_time=3
else
sample_size=20
measurement_time=2
warm_up_time=1
fi

repo_root="$(git rev-parse --show-toplevel)"
current_branch="$(git rev-parse --abbrev-ref HEAD)"
head_sha="$(git rev-parse HEAD)"
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"

worktree_root="$repo_root/target/bench-worktrees"
master_worktree="$worktree_root/tracing-perf-master"
head_worktree="$repo_root"

artifact_dir="$repo_root/target/bench-compare/$timestamp"
raw_dir="$artifact_dir/raw"
mkdir -p "$raw_dir/master" "$raw_dir/head"

mkdir -p "$worktree_root"

git config --global --add safe.directory "$repo_root" >/dev/null 2>&1 || true
git config --global --add safe.directory "$master_worktree" >/dev/null 2>&1 || true

git fetch origin master >/dev/null

if [[ ! -e "$master_worktree/.git" ]]; then
git worktree add --detach "$master_worktree" origin/master >/dev/null
else
git -C "$master_worktree" fetch origin master >/dev/null
git -C "$master_worktree" checkout --detach -f origin/master >/dev/null
fi

# Keep benchmark harness identical between baseline and candidate.
mkdir -p "$master_worktree/sentry-tracing/benches"
cp "$repo_root/sentry-tracing/Cargo.toml" "$master_worktree/sentry-tracing/Cargo.toml"
cp "$repo_root/sentry-tracing/benches/tracing_layer_perf.rs" "$master_worktree/sentry-tracing/benches/tracing_layer_perf.rs"
cp "$repo_root/Cargo.lock" "$master_worktree/Cargo.lock"

scenarios=(
enter_exit_existing_span
create_enter_exit_close_span
reenter_same_span_depth2
cross_thread_shared_span
)

modes=(
sentry_active
tracing_only_control
)

commands_file="$artifact_dir/commands.txt"
{
echo "# profile=$profile"
echo "# branch=$current_branch"
echo "# head_sha=$head_sha"
echo "cargo bench -p sentry-tracing --bench tracing_layer_perf --no-run"
for scenario in "${scenarios[@]}"; do
echo "cargo bench -p sentry-tracing --bench tracing_layer_perf $scenario -- --sample-size $sample_size --measurement-time $measurement_time --warm-up-time $warm_up_time --noplot"
done
} >"$commands_file"

run_suite() {
local label="$1"
local worktree="$2"

(
cd "$worktree"
cargo bench -p sentry-tracing --bench tracing_layer_perf --no-run
) >"$raw_dir/$label/build.log" 2>&1

for scenario in "${scenarios[@]}"; do
local log_file="$raw_dir/$label/$scenario.log"
(
cd "$worktree"
cargo bench -p sentry-tracing --bench tracing_layer_perf "$scenario" -- \
--sample-size "$sample_size" \
--measurement-time "$measurement_time" \
--warm-up-time "$warm_up_time" \
--noplot
) 2>&1 | tee "$log_file"
done
}

parse_mean_ns() {
local log_file="$1"
local bench_name="$2"

awk -v bench="$bench_name" '
function to_ns(v, u) {
if (u == "ns") return v
if (u == "us" || u == "µs" || u == "μs") return v * 1000
if (u == "ms") return v * 1000000
if (u == "s") return v * 1000000000
return -1
}
/^Benchmarking / {
current = $0
sub(/^Benchmarking /, "", current)
sub(/:.*/, "", current)
}
$1 == "time:" && current == bench {
gsub("\\[", "", $2)
middle_value = $4
middle_unit = $5
print to_ns(middle_value + 0, middle_unit)
exit
}
' "$log_file"
}

run_suite master "$master_worktree"
run_suite head "$head_worktree"

summary_file="$artifact_dir/summary.md"
{
echo "# tracing_layer_perf: origin/master vs $current_branch"
echo
printf -- "- profile: \`%s\`\n" "$profile"
printf -- "- baseline: \`%s\`\n" "origin/master"
printf -- "- candidate: \`%s (%s)\`\n" "$current_branch" "$head_sha"
echo
echo "| scenario | mode | master mean (ns) | head mean (ns) | delta |"
echo "|---|---|---:|---:|---:|"

for scenario in "${scenarios[@]}"; do
for mode in "${modes[@]}"; do
bench_name="$scenario/$mode"
master_ns="$(parse_mean_ns "$raw_dir/master/$scenario.log" "$bench_name")"
head_ns="$(parse_mean_ns "$raw_dir/head/$scenario.log" "$bench_name")"

if [[ -z "$master_ns" || -z "$head_ns" ]]; then
delta="n/a"
else
delta="$(awk -v a="$master_ns" -v b="$head_ns" 'BEGIN { printf "%.2f%%", ((b - a) / a) * 100 }')"
fi

echo "| $scenario | $mode | $master_ns | $head_ns | $delta |"
done
done
echo
printf -- "Commands: \`%s\`\n" "$commands_file"
printf -- "Raw logs: \`%s\`\n" "$raw_dir"
} >"$summary_file"

echo "Benchmark comparison complete."
echo "Artifacts: $artifact_dir"
echo "Summary: $summary_file"
16 changes: 12 additions & 4 deletions sentry-core/src/hub_impl.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::cell::{Cell, UnsafeCell};
use std::sync::{Arc, LazyLock, PoisonError, RwLock};
use std::marker::PhantomData;
use std::sync::{Arc, LazyLock, MutexGuard, PoisonError, RwLock};
use std::thread;

use crate::Scope;
Expand All @@ -19,10 +20,14 @@ thread_local! {
);
}

/// A Hub switch guard used to temporarily swap
/// active hub in thread local storage.
/// A guard that temporarily swaps the active hub in thread-local storage.
///
/// This type is `!Send` because it manages thread-local state and must be
/// dropped on the same thread where it was created.
pub struct SwitchGuard {
inner: Option<(Arc<Hub>, bool)>,
/// Makes this type `!Send` while keeping it `Sync`.
_not_send: PhantomData<MutexGuard<'static, ()>>,
}

impl SwitchGuard {
Expand All @@ -41,7 +46,10 @@ impl SwitchGuard {
let was_process_hub = is_process_hub.replace(false);
Some((hub, was_process_hub))
});
SwitchGuard { inner }
SwitchGuard {
inner,
_not_send: PhantomData,
}
}

fn swap(&mut self) -> Option<Arc<Hub>> {
Expand Down
5 changes: 5 additions & 0 deletions sentry-tracing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,8 @@ serde_json = "1"
tracing = "0.1"
tracing-subscriber = { version = "0.3.20", features = ["fmt", "registry"] }
tokio = { version = "1.44", features = ["rt-multi-thread", "macros", "time"] }
criterion = "0.8.2"

[[bench]]
name = "tracing_layer_perf"
harness = false
Loading
Loading