Skip to content

feat(gax): implement active scope management and attribute validation for T3 attempt spans - #13977

Draft
jinseopkim0 wants to merge 3 commits into
mainfrom
tracing-t3-spans-gax
Draft

feat(gax): implement active scope management and attribute validation for T3 attempt spans#13977
jinseopkim0 wants to merge 3 commits into
mainfrom
tracing-t3-spans-gax

Conversation

@jinseopkim0

@jinseopkim0 jinseopkim0 commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Description

Implements active scope context propagation and attribute validation for individual RPC attempt spans (T3 spans) in OpenTelemetryTracingTracer.

Changes

  • Activated OpenTelemetry context Scope during attemptStarted(...) via otelContext.makeCurrent().
  • Added cleanup for attemptScope in endAttempt().
  • Set StatusCode.ERROR on attemptSpan when an attempt fails in recordErrorAndEndAttempt.
  • Added unit tests in OpenTelemetryTracingTracerTest verifying scope management and error status propagation.

b/541322523

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces OpenTelemetry context scope management to OpenTelemetryTracingTracer by storing the active scope during an attempt and closing it upon completion, as well as setting the span status to error when an attempt fails. However, the review highlights a critical thread-safety issue: storing the thread-local Scope in an instance variable and closing it asynchronously across different threads (e.g., from the application thread to the executor thread) can lead to context leaks and corruption. Additionally, the new unit tests execute synchronously on a single thread, which masks this multi-threading issue.

@jinseopkim0

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces error status tracking on OpenTelemetry spans during failed attempts and adds a corresponding unit test. However, the newly declared attemptScope field is never initialized or used. Feedback recommends implementing the scope activation, ensuring that attemptScope is closed in an exception-safe manner to prevent context leaks, and adding unit tests to verify proper scope management.

private final String attemptSpanName;
private final ApiTracerContext apiTracerContext;
private @Nullable Span attemptSpan;
private io.opentelemetry.context.@Nullable Scope attemptScope;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The field attemptScope is declared here but is never initialized, used, or closed anywhere in the class. The PR description states that the OpenTelemetry context Scope is activated during attemptStarted(...), but that implementation is missing. Please implement the scope activation in attemptStarted(...) using otelContext.makeCurrent() as described.

Comment on lines 241 to 246
private void endAttempt() {
if (attemptSpan == null) {
return;
if (attemptSpan != null) {
attemptSpan.end();
attemptSpan = null;
}

attemptSpan.end();
attemptSpan = null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

To prevent context leaks, the attemptScope must be closed when ending the attempt. Ensure that closeable resources are closed in the reverse order of their creation (LIFO) and that the implementation is exception-safe so that all resources are closed even if an exception occurs during closing.

  private void endAttempt() {
    try {
      if (attemptScope != null) {
        attemptScope.close();
      }
    } finally {
      attemptScope = null;
      try {
        if (attemptSpan != null) {
          attemptSpan.end();
        }
      } finally {
        attemptSpan = null;
      }
    }
  }
References
  1. When managing a collection of closeable resources (e.g., scopes), ensure they are closed in the reverse order of their creation (LIFO). The implementation must be exception-safe to prevent resource leaks, meaning all opened resources should be closed even if exceptions occur during their creation or closing.

Comment on lines +91 to +98
void testAttemptFailed_setsErrorStatus() {
openTelemetryTracingTracer.attemptStarted(new Object(), 1);
openTelemetryTracingTracer.attemptFailedDuration(
new RuntimeException("Test error"), java.time.Duration.ofSeconds(1));

verify(span).setStatus(io.opentelemetry.api.trace.StatusCode.ERROR);
verify(span).end();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

This test only verifies that the span status is set to ERROR and that the span is ended. It does not verify that the active scope is managed (i.e., activated and closed/restored). Once the scope management is implemented, please add assertions/verifications to ensure the scope is correctly activated and closed.

@jinseopkim0

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates OpenTelemetryTracingTracer to set the span status to StatusCode.ERROR when an attempt fails, and adds a corresponding unit test. It also introduces an unused attemptScope field. The review feedback highlights that attemptScope is declared but never initialized or closed, which could lead to context leaks. Additionally, a null check is recommended for attemptSpan before calling methods on it in recordErrorAndEndAttempt to prevent potential NullPointerExceptions.

private final String attemptSpanName;
private final ApiTracerContext apiTracerContext;
private @Nullable Span attemptSpan;
private io.opentelemetry.context.@Nullable Scope attemptScope;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The field attemptScope is declared here but is never initialized, used, or closed. The PR description states that active scope context propagation is implemented via otelContext.makeCurrent() and cleaned up in endAttempt(), but these changes are missing from the implementation. Please ensure that the scope is properly activated when the attempt starts and closed when the attempt ends to prevent context leaks.

Comment on lines 241 to 246
private void endAttempt() {
if (attemptSpan == null) {
return;
if (attemptSpan != null) {
attemptSpan.end();
attemptSpan = null;
}

attemptSpan.end();
attemptSpan = null;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

The attemptScope is not closed in endAttempt(). To prevent context leaks, ensure that the scope is closed in endAttempt(). According to general rules, closeable resources should be closed in the reverse order of their creation (LIFO) and in an exception-safe manner.

  private void endAttempt() {
    try {
      if (attemptScope != null) {
        attemptScope.close();
      }
    } finally {
      attemptScope = null;
      try {
        if (attemptSpan != null) {
          attemptSpan.end();
        }
      } finally {
        attemptSpan = null;
      }
    }
  }
References
  1. When managing a collection of closeable resources (e.g., scopes), ensure they are closed in the reverse order of their creation (LIFO). The implementation must be exception-safe to prevent resource leaks, meaning all opened resources should be closed even if exceptions occur during their creation or closing.

Comment on lines +230 to 236
if (error != null) {
attemptSpan.setStatus(StatusCode.ERROR);
if (!Strings.isNullOrEmpty(error.getMessage())) {
attemptSpan.setAttribute(
ObservabilityAttributes.STATUS_MESSAGE_ATTRIBUTE, error.getMessage());
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Since attemptSpan is annotated with @Nullable, we should perform a null check before calling methods on it to prevent potential NullPointerExceptions.

Suggested change
if (error != null) {
attemptSpan.setStatus(StatusCode.ERROR);
if (!Strings.isNullOrEmpty(error.getMessage())) {
attemptSpan.setAttribute(
ObservabilityAttributes.STATUS_MESSAGE_ATTRIBUTE, error.getMessage());
}
}
if (attemptSpan != null && error != null) {
attemptSpan.setStatus(StatusCode.ERROR);
if (!Strings.isNullOrEmpty(error.getMessage())) {
attemptSpan.setAttribute(
ObservabilityAttributes.STATUS_MESSAGE_ATTRIBUTE, error.getMessage());
}
}

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

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.

1 participant