Skip to content

Fix requestEndTime is set after send response when empty content request#6587

Draft
gibeom-gwon wants to merge 1 commit intoline:mainfrom
gibeom-gwon:fix-end-request-for-empty-content
Draft

Fix requestEndTime is set after send response when empty content request#6587
gibeom-gwon wants to merge 1 commit intoline:mainfrom
gibeom-gwon:fix-end-request-for-empty-content

Conversation

@gibeom-gwon
Copy link
Contributor

@gibeom-gwon gibeom-gwon commented Jan 12, 2026

Motivation:

requestEndTimeNanos is set after send response when processing the empty body request (ex. GET).
This led to the inclusion of business logic processing time and response transmission time in the requestDurationNanos.

Modifications:

Result:

  • requestDurationNanos returns expected durations as documented.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Empty-content requests are marked ended during initialization: EmptyContentDecodedHttpRequest.init now calls ctx.logBuilder().requestEnd(). RequestLogBuilder and DefaultRequestLog gain requestEnd() / requestEnd(long), with guarded assignment so an existing end time is preserved. Tests added to assert log ordering.

Changes

Cohort / File(s) Summary
Implementation: empty-content request
core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
During init, call ctx.logBuilder().requestEnd() after assigning the context to mark empty-body requests ended immediately; no public signature changes.
Logging core
core/src/main/java/com/linecorp/armeria/common/logging/RequestLogBuilder.java, core/src/main/java/com/linecorp/armeria/common/logging/DefaultRequestLog.java
Add requestEnd() and requestEnd(long requestEndTimeNanos) to RequestLogBuilder; add public requestEnd APIs in DefaultRequestLog that delegate to a guarded helper which sets request end time only if not already set, preserving prior end-time assignments.
Testing
core/src/test/java/.../EmptyContentDecodedHttpRequestTest.java
Add a ServerExtension-backed integration test with an AnnotatedService at /get; issue GET /get, aggregate response, and assert captured RequestLog has requestEndTimeNanos < responseStartTimeNanos.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble timestamps at the bend, 🐇
A gentle call: "requestEnd".
Empty hops, no crumbs remain,
Logs align — the order plain.
I bound away, pleased with the trend.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: addressing the timing issue where requestEndTime is set after response sending for empty content requests.
Description check ✅ Passed The description is directly related to the changeset, explaining the motivation, modifications, and expected result of the fix for empty content request handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (1)

44-54: Consider removing unused verboseResponses parameter.

The configureServer method has an unused verboseResponses parameter. If it's not needed for future expansion, consider removing it to keep the code clean.

♻️ Suggested simplification
     @RegisterExtension
     static ServerExtension server = new ServerExtension() {
         @Override
         protected void configure(ServerBuilder sb) {
-            configureServer(sb, true);
+            sb.annotatedService(new AnnotatedService());
         }
     };

-    private static void configureServer(ServerBuilder sb, boolean verboseResponses) {
-        sb.annotatedService(new AnnotatedService());
-    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d897515 and e404d82.

📒 Files selected for processing (2)
  • core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
  • core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit configuration file

**/*.java: - The primary coding conventions and style guide for this project are defined in site/src/pages/community/developer-guide.mdx. Please strictly adhere to this file as the ultimate source of truth for all style and convention-related feedback.

2. Specific check for @UnstableApi

  • Review all newly added public classes and methods to ensure they have the @UnstableApi annotation.
  • However, this annotation is NOT required under the following conditions:
    • If the class or method is located in a package containing .internal or .testing.
    • If the class or method is located in a test source set.
    • If a public method is part of a class that is already annotated with @UnstableApi.

Files:

  • core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
  • core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java
🧬 Code graph analysis (1)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (2)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/common/EventLoopExtension.java (1)
  • EventLoopExtension (45-97)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/server/ServerExtension.java (1)
  • ServerExtension (49-405)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build-windows-latest-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-11
  • GitHub Check: build-ubicloud-standard-16-jdk-8
  • GitHub Check: build-ubicloud-standard-16-jdk-21-snapshot-blockhound
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-11
  • GitHub Check: build-ubicloud-standard-16-jdk-17-leak
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-17-coverage
  • GitHub Check: build-macos-latest-jdk-25
  • GitHub Check: Kubernetes Chaos test
  • GitHub Check: flaky-tests
  • GitHub Check: site
  • GitHub Check: lint
  • GitHub Check: Summary
🔇 Additional comments (3)
core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java (1)

84-89: LGTM! The fix correctly addresses the timing issue.

Calling endRequest() during init() ensures requestEndTimeNanos is recorded immediately when the request context is initialized, since empty content requests have no additional data to read. This aligns with the documented behavior where requestDurationNanos should not include business logic processing and response transmission time.

core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (2)

56-61: LGTM!

The annotated service is minimal and appropriate for testing the empty content request scenario.


77-90: Good integration test for verifying the timing fix.

The test correctly validates that requestEndTimeNanos is recorded before responseStartTimeNanos for empty content requests, which is the core fix of this PR. The use of requestContextCaptor() to capture the server-side context is appropriate.

@gibeom-gwon gibeom-gwon force-pushed the fix-end-request-for-empty-content branch 2 times, most recently from 51d667a to 9b1cfc4 Compare January 12, 2026 14:55
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (1)

44-54: Remove unused verboseResponses parameter.

The verboseResponses parameter in configureServer is never used. Consider simplifying the configuration.

Suggested simplification
     @RegisterExtension
     static ServerExtension server = new ServerExtension() {
         @Override
         protected void configure(ServerBuilder sb) {
-            configureServer(sb, true);
+            sb.annotatedService(new AnnotatedService());
         }
     };

-    private static void configureServer(ServerBuilder sb, boolean verboseResponses) {
-        sb.annotatedService(new AnnotatedService());
-    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e404d82 and 51d667a.

📒 Files selected for processing (2)
  • core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
  • core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit configuration file

**/*.java: - The primary coding conventions and style guide for this project are defined in site/src/pages/community/developer-guide.mdx. Please strictly adhere to this file as the ultimate source of truth for all style and convention-related feedback.

2. Specific check for @UnstableApi

  • Review all newly added public classes and methods to ensure they have the @UnstableApi annotation.
  • However, this annotation is NOT required under the following conditions:
    • If the class or method is located in a package containing .internal or .testing.
    • If the class or method is located in a test source set.
    • If a public method is part of a class that is already annotated with @UnstableApi.

Files:

  • core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
  • core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java
🧬 Code graph analysis (1)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (2)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/common/EventLoopExtension.java (1)
  • EventLoopExtension (45-97)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/server/ServerExtension.java (1)
  • ServerExtension (49-405)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build-windows-latest-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-25
  • GitHub Check: flaky-tests
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-17-coverage
  • GitHub Check: build-ubicloud-standard-16-jdk-21-snapshot-blockhound
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-11
  • GitHub Check: build-ubicloud-standard-16-jdk-8
  • GitHub Check: build-ubicloud-standard-16-jdk-11
  • GitHub Check: build-ubicloud-standard-16-jdk-17-leak
  • GitHub Check: build-macos-latest-jdk-25
  • GitHub Check: site
  • GitHub Check: lint
  • GitHub Check: Kubernetes Chaos test
  • GitHub Check: Summary
🔇 Additional comments (3)
core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java (1)

83-90: LGTM! Clean fix for the request timing issue.

The change correctly ends the request immediately during initialization since EmptyContentDecodedHttpRequest has no body data to read. This ensures requestEndTimeNanos is recorded before business logic and response transmission, as documented.

core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (2)

56-61: LGTM!

Simple and appropriate test endpoint for validating empty-content request timing.


77-90: LGTM! Good integration test validating the timing fix.

The test correctly verifies that requestEndTimeNanos is recorded before responseStartTimeNanos for empty-content GET requests, which is the core behavior this PR fixes. Uses JUnit 5 and AssertJ as per coding guidelines.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (1)

73-86: Consider a more semantically accurate test name.

The test logic correctly validates the fix. However, the method name uses "IsFasterThan" when comparing timestamps—times are "earlier" or "later," not "faster" or "slower." Consider renaming for clarity:

  • requestEndTimeNanosPrecedesResponseStartTimeNanos
  • requestEndTimeNanosIsLessThanResponseStartTimeNanos
  • requestEndsBeforeResponseStarts
♻️ Optional: rename for semantic clarity
     @Test
-    void requestEndTimeNanosIsFasterThanResponseStartTimeNanos()
+    void requestEndTimeNanosPrecedesResponseStartTimeNanos()
             throws InterruptedException {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51d667a and 9b1cfc4.

📒 Files selected for processing (2)
  • core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
  • core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/src/main/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequest.java
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit configuration file

**/*.java: - The primary coding conventions and style guide for this project are defined in site/src/pages/community/developer-guide.mdx. Please strictly adhere to this file as the ultimate source of truth for all style and convention-related feedback.

2. Specific check for @UnstableApi

  • Review all newly added public classes and methods to ensure they have the @UnstableApi annotation.
  • However, this annotation is NOT required under the following conditions:
    • If the class or method is located in a package containing .internal or .testing.
    • If the class or method is located in a test source set.
    • If a public method is part of a class that is already annotated with @UnstableApi.

Files:

  • core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java
🧬 Code graph analysis (1)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (2)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/common/EventLoopExtension.java (1)
  • EventLoopExtension (45-97)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/server/ServerExtension.java (1)
  • ServerExtension (49-405)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: build-ubicloud-standard-16-jdk-25
  • GitHub Check: build-macos-latest-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-11
  • GitHub Check: build-ubicloud-standard-16-jdk-17-leak
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-17-coverage
  • GitHub Check: build-ubicloud-standard-16-jdk-11
  • GitHub Check: build-ubicloud-standard-16-jdk-21-snapshot-blockhound
  • GitHub Check: build-ubicloud-standard-16-jdk-8
  • GitHub Check: build-windows-latest-jdk-25
  • GitHub Check: flaky-tests
  • GitHub Check: lint
  • GitHub Check: site
  • GitHub Check: Kubernetes Chaos test
  • GitHub Check: Summary
🔇 Additional comments (3)
core/src/test/java/com/linecorp/armeria/server/EmptyContentDecodedHttpRequestTest.java (3)

25-35: LGTM!

All new imports are necessary for the added test infrastructure and test method.


44-50: LGTM!

The ServerExtension setup follows the established pattern, correctly wiring the annotated service for integration testing.


52-57: LGTM!

The AnnotatedService is appropriately minimal for testing empty-content GET requests. Per coding guidelines, @UnstableApi is not required since this is in a test source set.

@gibeom-gwon gibeom-gwon marked this pull request as draft January 12, 2026 15:16

// EmptyContentDecodedHttpRequest does not have any additional data to read.
// End the request immediately.
ctx.logBuilder().endRequest();
Copy link
Contributor

Choose a reason for hiding this comment

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

There are some regressions due to this changes.

AnnotatedServiceContentTest > fileTypeReturn() FAILED
    java.lang.AssertionError: 
    Expecting actual not to be null
        at com.linecorp.armeria.internal.server.annotation.AnnotatedServiceContentTest.fileTypeReturn(AnnotatedServiceContentTest.java:219)

AnnotatedServiceRequestLogNameTest > customServiceNameWithDecorator() FAILED
    org.opentest4j.AssertionFailedError: 
    expected: "DecoratedService"
     but was: "MyBarService"
        at app//com.linecorp.armeria.internal.server.annotation.AnnotatedServiceRequestLogNameTest.customServiceNameWithDecorator(AnnotatedServiceRequestLogNameTest.java:135)

What do you think of introducing RequestLogBuilder.requestEndTimeNanos(requestEndTimeNanos) to manually recode the end time?

Suggested change
ctx.logBuilder().endRequest();
ctx.logBuilder().requestEndTimeNanos(System.nanoTime());

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think that's a good way.
If REQUEST_END_TIME is already set, I think it will be resolved by not overwriting it in endRequest().
The one minor thing I'm concern is, if TimeNanos keyword is included in the method name, I think it will be inconsistent with existing similar implementation (requestFirstBytesTransferred()). However, if we remove it, I think this can be confused with endRequest(). How do you think about the method naming?

Copy link
Contributor

Choose a reason for hiding this comment

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

Although requestEndTimeNanos() looks verbose, I prefer a clearer name. I'm open to better suggestions.

@gibeom-gwon gibeom-gwon force-pushed the fix-end-request-for-empty-content branch from 9b1cfc4 to 0f03dfe Compare January 17, 2026 15:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@core/src/main/java/com/linecorp/armeria/common/logging/RequestLogBuilder.java`:
- Around line 192-200: The two newly added public methods
RequestLogBuilder.requestEnd() and RequestLogBuilder.requestEnd(long) must be
annotated with `@UnstableApi` per project guidelines; update the method
declarations to add the `@UnstableApi` annotation and ensure the corresponding
import for the annotation is added (import
com.linecorp.armeria.common.annotation.UnstableApi or the correct package used
in the project) so the public API is properly marked unstable.

@gibeom-gwon gibeom-gwon force-pushed the fix-end-request-for-empty-content branch from 0f03dfe to e14a5d3 Compare January 17, 2026 15:39
@codecov
Copy link

codecov bot commented Jan 17, 2026

Codecov Report

❌ Patch coverage is 66.66667% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.40%. Comparing base (8150425) to head (e14a5d3).
⚠️ Report is 347 commits behind head on main.

Files with missing lines Patch % Lines
...corp/armeria/common/logging/DefaultRequestLog.java 63.63% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6587      +/-   ##
============================================
- Coverage     74.46%   74.40%   -0.07%     
- Complexity    22234    23715    +1481     
============================================
  Files          1963     2128     +165     
  Lines         82437    88605    +6168     
  Branches      10764    11586     +822     
============================================
+ Hits          61385    65923    +4538     
- Misses        15918    17147    +1229     
- Partials       5134     5535     +401     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot added Stale and removed Stale labels Feb 20, 2026
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