Skip to content

Thrift instrumentation simplifications#410

Open
trask wants to merge 9 commits into
laurit:thriftfrom
trask:thrift-simplifications
Open

Thrift instrumentation simplifications#410
trask wants to merge 9 commits into
laurit:thriftfrom
trask:thrift-simplifications

Conversation

@trask
Copy link
Copy Markdown

@trask trask commented May 4, 2026

Simplifications for review on top of open-telemetry#18405. Each commit is a separate refactor:

  • Drop ThriftRequestAccess / ThriftResponseAccess indirection — expose ThriftRequest constructors and ThriftResponse.FAILED directly.
  • Remove ThreadLocal-based endSpan flag and circular construction-vs-registration order in ClientProtocolDecorator / ClientCallContext.
  • Allocate a fresh State per ClientProtocolDecorator.Factory.getProtocol(...) call (was shared, concurrency bug for async).
  • Drop duplicate protocol field in ClientProtocolDecorator and ServerInProtocolDecorator.
  • ThriftRpcAttributesGetter.getErrorType: switch on the cached errorCode instead of calling getType() twice.
  • AsyncMethodCallbackUtil.wrap: drop dead @Nullable + null-check (callers null-check first).
  • ThriftTelemetry.wrapAsyncClient: keep separate InvocationTargetException and Throwable catches but call getCause() once.
  • Extract ThriftSingletons.thriftServiceName(Class<?>) to dedupe the getDeclaringClass() unwrap in both javaagent ConstructorAdvices.

private final Instrumenter<ThriftRequest, ThriftResponse> instrumenter;
private final ContextPropagators propagators;
@Nullable private final TTransport configuredTransport;
private final State state = new State();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I remember this was deliberate. The factory is used to create separate protocol instances for input and output, we wish to share state for these. This isn't a concurrency issue because the clients (including the async client) aren't thread safe and can only execute a single request at a time.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking more about this I'm not sure whether the shared state would always be safe. I reworked the client instrumentation so it isn't needed any more.

// propagation field, span will be started in readMessageEnd()
if (field.id == ContextPropagationUtil.TRACE_CONTEXT_FIELD_ID && field.type == TType.MAP) {
Map<String, String> headers = ContextPropagationUtil.readHeaders(protocol);
Map<String, String> headers = ContextPropagationUtil.readHeaders(this);
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I find it easier to reason when the underlying protocol is passed. When this is passed then overriding a protocol methods used in readHeaders could have unexpected results.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

trask added 2 commits May 5, 2026 11:20
This reverts commit cc80e17.
This reverts commit 9312257.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants