Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Load specifications for DocService lazily #4491

Merged
merged 10 commits into from
Nov 16, 2022

Conversation

jrhee17
Copy link
Contributor

@jrhee17 jrhee17 commented Oct 19, 2022

Motivation:

Following auto generation of request/response objects for DocService, some users have reported that long loading times block server startup. We may load DocService specifications lazily on startup with additional generalizations so that we can later also handle updates resulting from DocService#reconfigure.

Modifications:

  • Introduce a SpecificationLoader which loads resources lazily if needed.
  • Trigger lazy specification loading on server startup.
  • Added hints on specification load for docs-client.

Result:

Screen Shot 2022-10-20 at 3 41 05 PM

Screen Shot 2022-10-20 at 3 55 12 PM

@jrhee17 jrhee17 marked this pull request as ready for review October 20, 2022 07:09
@jrhee17 jrhee17 added the defect label Oct 20, 2022
@jrhee17 jrhee17 added this to the 1.21.0 milestone Oct 20, 2022
@codecov
Copy link

codecov bot commented Oct 20, 2022

Codecov Report

Base: 74.05% // Head: 74.11% // Increases project coverage by +0.06% 🎉

Coverage data is based on head (02defaf) compared to base (8bc6f19).
Patch coverage: 88.88% of modified lines in pull request are covered.

❗ Current head 02defaf differs from pull request most recent head 722cbb7. Consider uploading reports for the commit 722cbb7 to get more accurate results

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #4491      +/-   ##
============================================
+ Coverage     74.05%   74.11%   +0.06%     
- Complexity    18167    18183      +16     
============================================
  Files          1536     1537       +1     
  Lines         67378    67457      +79     
  Branches       8520     8530      +10     
============================================
+ Hits          49894    49999     +105     
+ Misses        13412    13387      -25     
+ Partials       4072     4071       -1     
Impacted Files Coverage Δ
...a/com/linecorp/armeria/server/docs/DocService.java 90.54% <88.88%> (-3.27%) ⬇️
...nternal/common/stream/EmptyFixedStreamMessage.java 76.00% <0.00%> (-7.34%) ⬇️
...rnal/common/GracefulConnectionShutdownHandler.java 68.08% <0.00%> (-6.39%) ⬇️
...rp/armeria/common/HeaderOverridingHttpRequest.java 70.58% <0.00%> (-5.89%) ⬇️
...a/common/logging/DefaultContentPreviewFactory.java 85.71% <0.00%> (-5.72%) ⬇️
...armeria/server/EmptyContentDecodedHttpRequest.java 80.00% <0.00%> (-3.02%) ⬇️
...al/common/stream/TwoElementFixedStreamMessage.java 86.76% <0.00%> (-2.95%) ⬇️
.../armeria/client/endpoint/dns/DnsEndpointGroup.java 75.32% <0.00%> (-2.60%) ⬇️
...linecorp/armeria/common/RequestContextWrapper.java 9.75% <0.00%> (-2.44%) ⬇️
...ia/common/stream/ConcatPublisherStreamMessage.java 80.62% <0.00%> (-1.56%) ⬇️
... and 36 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Thanks! Left minor comments. 😉

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

Looks good! Left only minor comments. 🙇‍♂️

Comment on lines 264 to 266
void triggerPreload(Executor executor) {
targetPaths.forEach(path -> load(path, executor));
}
Copy link
Contributor

Choose a reason for hiding this comment

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

How about consolidating triggerPreload and updateServices? and it seems AtomicReference could be removed.

void triggerPreload(List<ServiceConfig> services, Executor executor) {
    this.services = services;
    targetPaths.forEach(path -> load(path, executor));
}

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Left two more nits. 😄

@@ -103,18 +110,17 @@ public final class DocService extends SimpleDecoratingHttpService {
spiNamedTypeInfoProviders);
}

private static final ExecutorService DEFAULT_EXECUTOR = Executors.newSingleThreadScheduledExecutor(
Copy link
Member

Choose a reason for hiding this comment

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

newSingleThreadExecutor because we don't do scheduling?
Also how about just creating this executor where it's used because it's mostly used only once when the server is started?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also how about just creating this executor where it's used because it's mostly used only once when the server is started?

Not sure if this is what you meant, but scheduled the executor to be shut down after file loading has completed 🙏

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Great work, @jrhee17! 😄

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

Thanks, @jrhee17! 🙇‍♂️💯

@minwoox minwoox merged commit 5f8d5e5 into line:master Nov 16, 2022
@minwoox
Copy link
Member

minwoox commented Nov 16, 2022

Thanks a lot, @jrhee17! 🙇

@chris-ryan-square
Copy link

We're looking forward to this - excellent!

minwoox pushed a commit that referenced this pull request Dec 13, 2022
Motivation:

Previously, `DocService` wasn't able to handle the changed services as a result of `reconfigure`.

```
sb = Server.builder()
           .annotatedService("/hello", new HelloService())
           .serviceUnder("/docs", DocService.builder().build())
...
sb.reconfigure(sb2 -> sb2.annotatedService("/hello2", new HelloService())
                                             .serviceUnder("/docs", DocService.builder().build()))
```

- If a shared `DocService` is used, services wouldn't be updated and the old specification would be used.
- If a new `DocService` is added, appropriate callbacks wouldn't be called and `/specifications.json` would result in a 404.

https://github.com/line/armeria/blob/cae3b6da373101c0976e2d00f1f3cbb706d07e00/core/src/main/java/com/linecorp/armeria/server/docs/DocService.java#L217-L220

We may note that `Server#configs` is immutable, and that `Server#configs` is fixed at the timing when the `serviceAdded` callback is called.

https://github.com/line/armeria/blob/cae3b6da373101c0976e2d00f1f3cbb706d07e00/core/src/main/java/com/linecorp/armeria/server/DefaultServerConfig.java#L246

Modifications:

- Change the timing when `DocService#specifications` is loaded to `DecoratingService#serviceAdded`.

Result:

- Users can use `DocService` even when `Server#reconfigure` is called.

Note: This PR contains changes from #4491 .  Review this PR first 🙏
trustin pushed a commit that referenced this pull request Mar 27, 2023
… service (#4688)

Motivation:

We use thrift/grpc internal APIs when constructing `DocService`.
Following #4491, these internal APIs are called concurrently with service logic.
Due to a thrift bug, a deadlock can occur when `FieldMetaData.getStructMetaDataMap` is called for a thrift class while the class is initialized.
https://issues.apache.org/jira/browse/THRIFT-5430
This PR attempts to circumvent this issue by pre-loading the class for affected versions (<= armeria-thrift 0.14)

I've also double checked that other `*MetaData` implementations don't have a similar implementation.

Modifications:

- Add a `ThriftMetadataAccess` utility class
- When `ThriftMetadataAccess` is instantiated, check if (<= armeria-thrift 0.14) is in the classpath
- If the vulnerable artifact is loaded, then pre-instantiate thrift classes before `FieldMetaData.getStructMetaDataMap` is called from `DocService`

Result:

- A deadlock is no longer observed when loading `DocService` for thrift services where `<= armeria-thrift 0.14`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Consider adding an option to lazily generate DocService specifications
4 participants