feat(ray): Add actor support to RayIntegration#1
Open
scorchio wants to merge 6 commits into
Open
Conversation
Ray actors (classes decorated with @ray.remote) were previously skipped by the integration. This adds: - Worker-side tracing: each public actor method is wrapped to receive _sentry_tracing, continue the distributed trace, and create a queue.task.ray span or transaction (both static and stream modes). - Caller-side tracing: ActorHandle._actor_method_call is patched per instance to create a queue.submit.ray span and propagate trace headers via the _sentry_tracing kwarg. - Error capture for actor method exceptions and __init__ failures. - Support for both @ray.remote and @ray.remote(**opts) decorator forms. Key implementation note: Ray calls inspect.unwrap() before reading actor method signatures, which follows __wrapped__ (set by functools.wraps) to the original method. We delete __wrapped__ after wrapping so Ray sees new_method's actual signature, which already includes _sentry_tracing as a real keyword-only parameter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ray Serve actors (and other async Ray actors) use async def __init__ and async def methods. The previous implementation wrapped them with synchronous wrappers, which called the original coroutine function but never awaited the result — causing Ray to see an unawaited coroutine instead of a completed init, manifesting as: TypeError: object of type 'coroutine' has no len() Fix: detect inspect.iscoroutinefunction and produce async wrappers for both _wrap_actor_init and _wrap_actor_method. Sentry's start_span and start_transaction context managers are synchronous and can be used with regular `with` inside async functions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…h mismatch Ray's own actors (ServeController, ProxyActor, etc.) are defined in ray.* modules. Worker processes import these classes by name from the module, so setattr patches applied in the driver never reach the worker. The driver-side handle still injected _sentry_tracing into kwargs, but the worker's unpatched method didn't accept it, causing: TypeError: ServeController.get_proxies() got an unexpected keyword argument '_sentry_tracing' Note: __init__ was unaffected because Ray's _modify_class() explicitly copies __init__ (Class.__init__ = cls.__init__), making it a closure that cloudpickle ships directly rather than importing by name. Fix: introduce _is_ray_internal_class() and skip both method patching and handle patching for any class whose __module__ starts with 'ray.'. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ss guard - test_tracing_in_async_ray_actors: catches sync-wrapper-on-async-method bug (unawaited coroutine crashes actor creation) - test_ray_internal_actors_not_patched: verifies _is_ray_internal_class classification and that ray.* module classes are not patched with _sentry_tracing (driver/worker module mismatch would cause TypeError) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Guard private/dunder method calls in new_actor_method_call: _patch_actor_class only wraps public methods on the worker, so injecting _sentry_tracing into private calls would cause TypeError - Use cls.__dict__ instead of inspect.getmembers in _patch_actor_class to avoid MRO double-wrapping when subclass and superclass are both @ray.remote decorated - Apply functools.update_wrapper on new_class_remote to preserve any __dict__ attributes (e.g. options/bind) from the original .remote callable - Add explanatory comment in _wrap_actor_init clarifying why del __wrapped__ is intentionally omitted Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ray Serve logs "Request failed." with exc_info for every failed request via the "ray.serve" logger, and the same exception is already captured through the ASGI integration. DedupeIntegration cannot deduplicate the two capture paths because Ray Serve runs the ASGI app in a separate asyncio task, so the dedupe ContextVar never spans both, resulting in duplicate error events. Suppressing the framework's error logger is the established pattern used by several other integrations (aiohttp.server, django.request, celery.worker.job, rq.worker, tornado.access). Note that ignore_logger affects error events only -- Sentry Logs have a separate ignore list, so "Request failed." remains visible in the Logs product; this is intentional, same as django.server. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.
Summary
_patch_actor_class,_wrap_actor_method,_wrap_actor_init) to capture exceptions and continue distributed traces inside Ray actor methodsActorHandlepatching (_patch_actor_handle,_wrap_actor_class_remote) to create submit spans and inject_sentry_tracingtrace headers across Ray process boundaries__init__and methods (required for Ray Serve compatibility) by branching oninspect.iscoroutinefunction_is_ray_internal_class) from being patched to avoid driver/worker module identity mismatchescls.__dict__in_patch_actor_classto avoid MRO double-wrapping when a subclass and superclass are both@ray.remote_sentry_tracinginjection since only public methods are wrapped on the worker sideKnown limitations
__init__errors are captured without trace context (Ray calls__init__directly, not via_actor_method_call, so there is no mechanism to propagate headers)_actor_method_callreturns anObjectRef(future); exceptions only surface atray.get()time🤖 Generated with Claude Code