feat(tests): active job tracing specs#2947
Draft
solnic wants to merge 25 commits into
Draft
Conversation
6309f45 to
03ad132
Compare
Sets messaging.message.id, messaging.destination.name, messaging.message.retry.count, and messaging.message.receive.latency on the consumer transaction, mirroring sentry-sidekiq's middleware. Adds an opt-in shared example that adapters can include to verify the data fields are populated correctly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wraps ActiveJob enqueue with a `queue.publish` child span when an active parent transaction exists, mirroring sentry-sidekiq's client middleware. Uses the public `around_enqueue` callback so no new ActiveJob monkey-patching is introduced. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the OpenTelemetry pattern (the only documented way to add metadata to an ActiveJob payload — Rails has no public extension hook for serialize/deserialize): prepends the existing ActiveJobExtensions module with serialize/deserialize overrides that inject and recover sentry-trace and baggage headers under a namespaced "_sentry" key, wrapped in rescue blocks so a Sentry bug never breaks job execution. Threads the deserialized headers into SentryReporter.record, which now uses Sentry.continue_trace when present so the consumer transaction shares the producer's trace_id and chains under the producer queue.publish span. Guards the around_enqueue producer-span registration against duplicate registration (each Test::Application.define re-runs the railtie and without idempotency this stacks dozens of nested queue.publish spans). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…er spec The producer-span change makes ActiveStorage's internally-enqueued AnalyzeJob emit an extra queue.publish span on the request transaction, which the previous index-based span lookups did not anticipate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…load When config.send_default_pii is true, the producer-side serialize override now copies a whitelisted set of user fields (id, email, username) into the _sentry payload block. The consumer-side deserialize stashes them and SentryReporter.record applies them to the new scope so that the consumer transaction (and any error event captured during perform) carries the originating user without leaking ip_address, segment, or other fields back into the queue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Calls Sentry.clone_hub_to_current_thread before opening the consumer scope when perform_now runs on a non-main thread (mirrors the sentry-sidekiq server middleware). This ensures that worker-side state captured during job execution lives on a thread-local hub clone and cannot leak back into the main process hub. Adds a behaviour-driven shared example: two concurrent jobs in separate worker threads do not cross-pollute each other's tags, and the calling thread's scope is unchanged after both complete. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_tracing meta Replaces the five individual it_behaves_like opt-ins in test_adapter_spec.rb with one composite shared example so future AJ adapter specs can opt in with a single line. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…data spec Rails 6.x's ActiveSupport::Testing::TimeHelpers#travel does not accept the with_usec: option, so it truncates Time.now to whole seconds and the measured latency can land up to ~999ms below the travel delta. Use a 1100ms tolerance on Rails < 7.0 and the original 50ms tolerance on 7.0+ where with_usec: true is available. Verified against Rails 6.1, 7.1, and 8.1 via ./bin/test --version. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lation example The worker_hub_isolation shared example previously hard-coded Thread.new for the two concurrent jobs it spawns. That works fine on adapters that keep their queue state in-process (:test, :inline) but some real adapters need per-thread setup (e.g. :solid_queue on SQLite needs an isolated database per worker thread to avoid SQLite3::BusyException). Adds a `worker_thread(&block)` hook on the harness, defaulting to `Thread.new(&block)`, and switches the shared example to call it. Adapters that need extra worker setup override the hook. Behaviour on :test (the only adapter on this branch) is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Use rescue_handlers.any? to determine if a job is configured as retryable. When it is, always set messaging.message.retry.count (starting at 0 on the first execution) to match Sidekiq behavior. Non-retryable jobs never emit this key. Co-Authored-By: github-copilot <noreply@example.com>
Mirrors Sidekiq's propagate_traces flag. When set to false, trace propagation headers are not injected into the serialized job payload and the consumer starts a new unconnected transaction. Defaults to true (existing behaviour is preserved). Co-Authored-By: github-copilot <noreply@example.com>
Mirror Sidekiq's scope enrichment: set a 'queue' scope tag and an 'active_job' context block (job_class, job_id, queue, provider_job_id) on every event captured within the consumer scope, including the transaction and any captured errors. Co-Authored-By: github-copilot <noreply@example.com>
Co-Authored-By: github-copilot <noreply@example.com>
c92d39e to
766834e
Compare
This correctly handles all execution modes: - Dedicated async workers (new thread, nil hub): clone -> restore nil - Inline inside a Rack request (rack hub on thread): clone -> restore rack hub so the HTTP response completes normally - Thread-pool workers (recycled thread, stale hub): clone -> restore stale hub (irrelevant; next job will clone again) Co-Authored-By: github-copilot <noreply@example.com>
Co-Authored-By: github-copilot <noreply@example.com>
Replace the flaky timing window with the span-containment invariant: a child span cannot outlast its enclosing transaction. Catches a seconds vs milliseconds scale regression without depending on CI query timing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8cf9486 to
c09ad03
Compare
The harness embedded :test-adapter specifics — the Rails 5.2 payload- preservation shim, the drain loop, and the enqueued-payload accessor. It also reached past ActiveJob::TestHelper to set ActiveJob::Base.queue_adapter directly, which conflicts with TestHelper's own _test_adapter slot (TestHelper's before_setup runs outside our around hook, so any direct assignment is silently shadowed). Switch the harness to ActiveJob's official queue_adapter_for_test hook and a small set of abstract methods (queue_adapter_for_test, with_adapter_active, drain, last_enqueued_payload, boot_adapter, reset_adapter) that adapter contexts implement. The :test-adapter shared context now owns everything specific to TestAdapter — including the Rails52FullPayloadTestAdapter shim and the drain loop. Subsequent adapter backends (e.g. Sidekiq) can compose with the harness without fighting it. Generalises the one shared-example line that reached into the TestAdapter shape (trace_propagation) via last_enqueued_payload. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
acf0fc3 to
c80bcba
Compare
…adapter Runs the common ActiveJob spec suite end-to-end against ActiveJob::QueueAdapters::SidekiqAdapter, driven by Sidekiq::Testing.fake! (block form, public API) and Sidekiq::Job.drain_all. Validates that the AJ tracing extension works as a generic, adapter- agnostic instrumentation — independent of sentry-sidekiq's native middleware. The :sidekiq context plugs into the harness via queue_adapter_for_test (installing a SidekiqAdapter instance through ActiveJob::TestHelper) and with_adapter_active (wrapping example.run in Sidekiq::Testing.fake! so fake mode is scoped per-example without touching global state). The context deliberately does not load sentry-sidekiq: loading it would install Sidekiq's client/server middleware globally and register SidekiqAdapter in skippable_job_adapters, both of which would short-circuit the AJ extension we're exercising. Sidekiq becomes a sentry-rails dev dependency, gated on Rails version (Sidekiq 7+ doesn't support Rails 5.2). The spec file and support file no-op cleanly on older matrices where the gem isn't bundled. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drives the svelte-mini app to click a new "Trigger Job" button, which fetches POST /jobs/sample on the rails-mini app. The browser SDK propagates sentry-trace + baggage to the Rails request; the AJ extension this branch adds emits a queue.publish span on the http.server transaction at enqueue, and a queue.active_job consumer transaction when the :async pool runs the job. The spec asserts all three rails-side artifacts share one trace and are correctly linked (sentry-trace header on the controller request, parent_span_id on the consumer transaction, and matching messaging.* data on the producer and consumer ends). Polls the shared envelope log because :async runs the job on a separate thread, so the HTTP response returns before the consumer transaction is recorded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
c80bcba to
71980b0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TBD