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

OpenAPI 3.1.0 support: Performance drop when changing versions from latest stable to 5.0-alpha #8606

Closed
dchicchon opened this issue Apr 28, 2023 · 28 comments

Comments

@dchicchon
Copy link

dchicchon commented Apr 28, 2023

Q&A (please complete the following information)

  • OS: macOS
  • Browser: chrome
  • Version: 3.19.6
  • Method of installation: npm
  • Swagger-UI version: 5.0.0.alpha-7
  • Swagger/OpenAPI version: OpenAPI 3.1.0

Content & configuration

I'm using the react flavor of swagger ui

Swagger/OpenAPI definition:
N/A => large definition (around 10MB)

Swagger-UI configuration options:

    <SwaggerUI
            spec={spec}
            tryItOutEnabled
            filter
            queryConfigEnabled
            displayRequestDuration
            docExpansion="none"
     />
?yourQueryStringConfig

Screenshots

N/A

How can we help?

I've noticed that going from the latest version of Swagger-UI to the latest alpha version that there is a noticeable performance drop when clicking on each endpoint and viewing it's contents (the loading icon hangs for a long time, especially when clicking on multiple definitions). Additionally, when I move to another chrome tab and come back, all of the endpoints I inspected are folded again.

My question is, is there limit for using a spec size in Swagger UI?

@dchicchon dchicchon changed the title Performance drop when changing versions from official to current 5.0-alpha.7 Performance drop when changing versions from latest to latest 5.0-alpha.7 Apr 28, 2023
@char0n char0n changed the title Performance drop when changing versions from latest to latest 5.0-alpha.7 OpenAPI 3.1.0 support: Performance drop when changing versions from latest to latest 5.0-alpha.7 May 2, 2023
@char0n char0n self-assigned this May 2, 2023
@char0n
Copy link
Member

char0n commented May 2, 2023

Hi @dchicchon,

Thanks for the feedback.

My question is, is there limit for using a spec size in Swagger UI?

No known limit. As SwaggerUI is browser renderer, the performance depends on the configuration of your hardware (CPU cores, RAM) and choosing of the browser (Firefox, Chrome, ...). So it may vary significantly.

I've noticed that going from the latest version of Swagger-UI to the latest alpha version that there is a noticeable performance drop when clicking on each endpoint and viewing it's contents

This might be directly connected to new JSON Schema 2020 12 renderer. For 5.0.0.alpha-7 release I've turned off optimization in rendering - this cases JSON Schema 2020 12 to always render entire trees and hide the collapsed section with CSS.

I will turn the optimization on again which will cause progressive rendering of JSON Schemas as you expand them. This has certain UX drawbacks, but it's great for performance. In future we can expose this option as configuration.

char0n added a commit that referenced this issue May 2, 2023
Schema Object sub-trees are not renderer until
expanded.

Before the entire Scheam Object tree was rendered
and collapsed sub-trees here hidden by css.

Refs #8606
@char0n
Copy link
Member

char0n commented May 2, 2023

Addressed in #8609

char0n added a commit that referenced this issue May 2, 2023
Schema Object sub-trees are not renderer until
expanded.

Before the entire Scheam Object tree was rendered
and collapsed sub-trees here hidden by CSS.

Refs #8606
@char0n
Copy link
Member

char0n commented May 2, 2023

@char0n
Copy link
Member

char0n commented May 2, 2023

@dchicchon please net me know ASAP if alpha.8 solved your performance issue.

@dchicchon
Copy link
Author

dchicchon commented May 2, 2023

@char0n after testing this on my model, it looks like it did help performance but not by a noticeable amount in my case.

For reference, I did a quick (albeit not very empirical) test on my swagger page to show the difference in performance for the same OpenAPI spec in 4.18.3, alpha-7, and alpha-8. The test is clicking on the api tag to expand all the available endpoints, then clicking on an endpoint to expand. The part that takes the longest is when the loading icon is shown.

For additional reference, with 4.18.3 I changed my OpenAPI spec to a 3.0, and for the latest alpha versions I have it set to OpenAPI 3.1 since our target is to support the latest OpenAPI specs

Latest 4.18.3
latest-stable
Alpha 7
alpha7
Alpha 8
alpha8

As far as I can tell, it appears that the total blocking time is reduced by 100ms to a total of 4728ms in alpha 8 vs alpha 7. But as you can see, to accomplish the same task in latest stable 4.18.3 its only 405ms of blocking time.

@char0n
Copy link
Member

char0n commented May 2, 2023

@dchicchon so just for clarification we're talking about comparing v4 and v5 and not v5-alpha.6 vs v5-alpha.7, right?

@dchicchon
Copy link
Author

dchicchon commented May 2, 2023

@char0n Yes that is correct, apologies if that was not clarified. I just wanted to compare v5-alpha.7 and v5-alpha.8 as well since it was recently updated.

Also as a final test, I made my OpenAPI spec 3.0 and tested v5-alpha.8 and found that the performance was equivalent to v4. This being the case, I believe the issue only occurs when using an OpenAPI 3.1 spec.

@dchicchon dchicchon changed the title OpenAPI 3.1.0 support: Performance drop when changing versions from latest to latest 5.0-alpha.7 OpenAPI 3.1.0 support: Performance drop when changing versions from latest to latest 5.0-alpha May 2, 2023
@dchicchon dchicchon changed the title OpenAPI 3.1.0 support: Performance drop when changing versions from latest to latest 5.0-alpha OpenAPI 3.1.0 support: Performance drop when changing versions from latest to 5.0-alpha May 2, 2023
@dchicchon dchicchon changed the title OpenAPI 3.1.0 support: Performance drop when changing versions from latest to 5.0-alpha OpenAPI 3.1.0 support: Performance drop when changing versions from latest stable to 5.0-alpha May 2, 2023
@char0n
Copy link
Member

char0n commented May 2, 2023

@dchicchon would you be able to record a short video to demonstrate what's happening with OpenAPI 3.1.0 and your definition?

@char0n
Copy link
Member

char0n commented May 2, 2023

Observation1

The different between v4 and v5 is that components.schemas are resolved for OpenAPI 3.1.0 on SwaggerUI load now. That might have some performance implications when firstly displaying the SwaggerUI, but how this relates to degraded performance in opening Operations is still a mystery.

@char0n
Copy link
Member

char0n commented May 2, 2023

@dchicchon swagger-ui-react has support for defaultModelExpandDepth prop. This prop controls the display of Schemas section. Could you set it to -1 to disable displaying the Schemas section? Has the performance stabilized after utilizing this prop?

Last question: can you share your OpenAPI 3.1.0 definition?

@dchicchon
Copy link
Author

dchicchon commented May 2, 2023

@char0n after adding the defaultModelExpandDepth prop, I did not notice any improvement when doing a quick test with that. Below I will share a clip of the definition, for privacy purposes I have hashed the document. Unsure if I can share the definition, but I will try to see if I could hash it further.

But as you can see, as I click on each operation to unfold it takes a couple seconds to open. This issue is exacerbated when trying to open multiple operations as well.

Screen.Recording.2023-05-02.at.11.09.48.AM.mov

@char0n
Copy link
Member

char0n commented May 2, 2023

Correction: defaultModelExpandDepth -> defaultModelsExpandDepth

This should disable the Schemas bottom section.

@char0n
Copy link
Member

char0n commented May 2, 2023

I think I can reproduce what you're seeing on Twitter API v2: https://gist.github.com/char0n/0623cf602c23de8628b7e06012cfbf00

It takes more time to open the Operation in OpenAPI 3.1.0 than in OpenAPI 3.0.x. The reason is that for OpenAPI 3.1.0, ApiDOM is used to facilitate the resolution capability. Resolving/dereferencing the OpenAPI 3.1.0 definition is exponentially more complex then resolving OpenAPI 3.0.x. I'll make a research if I can speed up the lazy resolution in some way - e.g. utilizing caching of refracted ApiDOM

@dchicchon
Copy link
Author

dchicchon commented May 2, 2023

@char0n sounds good, thank you for taking the time to look into this. I'll check back if there are any updates

@char0n
Copy link
Member

char0n commented May 2, 2023

Another thing we need to handle is not to resolve the entire components.schemas on SwaggerUI load, but resolve every schema individually and lazily as we progressively expand each schema.

@char0n
Copy link
Member

char0n commented May 4, 2023

Here are some additional notes about the performance degradation. I've done some profiling on Twitter v2 API and here are some results:


Refracting phase takes around ~450ms. This is the phase when spec object is transformed to ApiDOM. This can be optimized by utilizing caching. Only first lazy resolution will do the actual refracting, subsequent refractings will be skipped. If swagger-client is provided with the same spec object, cached ApiDOM will be reused. There is a complication that need to be tackled first - spec is stored in SwaggerUI as immutable object and before it's provided to swagger-client, it's converted to JavaScript object, which means that swagger-client always gets the new object reference even though the spec is stored in SwaggerUI as immutable object.


Transclusion takes ~40ms. Tranclusion is a process of replacing a lazily resolved sub-tree to the original tree. This is not worth optimizing now.


Serialization of ApiDOM to JavaScript takes ~90ms. This is not worth optimizing now.


Dereferencing individual sub-trees takes from ~300ms to ~8000s (depending on the complexity of the definition). This is caused by the fact that in OpenAPI 3.1.0 dereferencing if we have Schema Object with $ref=http://example.com/schema.json, we first need to check if we have Schema Object with $id=http://example.com/schema.json, before we try to actually resolve the http://example.com/schema.json URL and fetch data from it. It's event more complicated than that, the Schema Object $id field can be relative URI Referece and needs to be resolved against all it's parent Schema Objects and retrievalURI before we can compare it. Here I'll have to come up with caching mechanism that doesn't repeatably search the original definition for Schema Object with $id field. I've tried the POC of caching mechanism here and managed to get the execution time to tens of milliseconds.


Displaying Schemas. This is actually first optimization to utilize. PR is already almost finished. Schemas section will be displayed as it is, and only when individual Schema Objects are expanded for the first time, the Schema Objects are individually lazily resolved and re-rendered.


@char0n
Copy link
Member

char0n commented May 4, 2023

#8616 addresses:

image

@dchicchon
Copy link
Author

dchicchon commented May 8, 2023

@char0n just wanted to check in again, I have noticed in my OpenAPI 3.1 schema that I showed previously that we are not utilizing the components.schemas field and we are manually placing each schema at a path location whenever it is used. Curious to know, could this cause performance issues as well?

@char0n
Copy link
Member

char0n commented May 10, 2023

@char0n just wanted to check in again, I have noticed in my OpenAPI 3.1 schema that I showed previously that we are not utilizing the components.schemas field and we are manually placing each schema at a path location whenever it is used. Curious to know, could this cause performance issues as well?

Hi @dchicchon, not sure I fully understand your message. The new renderer of JSON Shcema 2020 is currently only rendering only components.schemas. Schemas within Path Items and other locations are still rendered with legacy JSON Schema Draft 4 renderer.

We've currently pushed MVP1 of JSON Schema 2020-12 renderer and are in the process of finishing MVP2 phase. More info in #8513.

image

The main performance issue has been identified already in #8606 (comment) as:

image

When processed, the performance is going to be similar to the SwaggerUI@4.

@PatchRanger
Copy link

OS: Ubuntu 22.04.1
Browser: Chromium-based
Version 110.0.0.0
Method of installation: via docker image swaggerapi/swagger-ui
Swagger-UI version: v5.0.0-alpha.14
Swagger/OpenAPI version: 3.1.0

Hello. I inform you that we have a similar performance problem: after switching to v5.0.0-alpha (for the sake of OpenAPI 3.1.0 support), any opening of the SwaggerUI page leads to a long (several minutes) loading of all schemes.
The situation is further aggravated by the fact that the browser cache does not help speed it up at all: in the browser console I see that the schemes are taken from the browser cache, but it still takes a couple of minutes.
As far as I understand from the discussion above, this is due to the duration of the "Dereferencing" stage.
Thank you for your attention to this issue and efforts to fix it.

@char0n
Copy link
Member

char0n commented Jun 5, 2023

@PatchRanger yes you're hitting the performance degradation issue related to OpenAPI 3.1.0 and described in #8606 (comment).

We know about this, and will work on this issue in foreseeable future.

char0n added a commit that referenced this issue Jun 16, 2023
This selector always returns the same JavaScript
object unless it really changes.

Before this change it customary to call
specJson().toJS() which always resulted
in transformation operation and new JavaScript
object.

Refs #8606
char0n added a commit that referenced this issue Jun 16, 2023
This selector always returns the same JavaScript
object unless it really changes.

Before this change it customary to call
specJson().toJS() which always resulted
in transformation operation and new JavaScript
object.

Refs #8606
@char0n
Copy link
Member

char0n commented Jun 16, 2023

List of PRs that addresses the following:

There is a complication that need to be tackled first - spec is stored in SwaggerUI as immutable object and before it's provided to swagger-client, it's converted to JavaScript object, which means that swagger-client always gets the new object reference even though the spec is stored in SwaggerUI as immutable object.

@char0n
Copy link
Member

char0n commented Jun 16, 2023

List of PRs that addresses the following:

Refracting phase takes around ~450ms. This is the phase when spec object is transformed to ApiDOM. This can be optimized by utilizing caching. Only first lazy resolution will do the actual refracting, subsequent refractings will be skipped. If swagger-client is provided with the same spec object, cached ApiDOM will be reused.

char0n added a commit to swagger-api/swagger-js that referenced this issue Jun 16, 2023
@char0n
Copy link
Member

char0n commented Jun 16, 2023

Following has been addressed by [email protected] already:

Displaying Schemas. This is actually first optimization to utilize. PR is already almost finished. Schemas section will be displayed as it is, and only when individual Schema Objects are expanded for the first time, the Schema Objects are individually lazily resolved and re-rendered.

@char0n
Copy link
Member

char0n commented Jun 16, 2023

List of PRs that addresses the following:

Dereferencing individual sub-trees takes from ~300ms to ~8000s (depending on the complexity of the definition). This is caused by the fact that in OpenAPI 3.1.0 dereferencing if we have Schema Object with $ref=http://example.com/schema.json, we first need to check if we have Schema Object with $id=http://example.com/schema.json, before we try to actually resolve the http://example.com/schema.json URL and fetch data from it. It's event more complicated than that, the Schema Object $id field can be relative URI Referece and needs to be resolved against all it's parent Schema Objects and retrievalURI before we can compare it. Here I'll have to come up with caching mechanism that doesn't repeatably search the original definition for Schema Object with $id field. I've tried the POC of caching mechanism here and managed to get the execution time to tens of milliseconds.

@char0n
Copy link
Member

char0n commented Jun 16, 2023

For swagger-client OpenAPI 3.1.0 resolve strategy it is possible to warm up the cache using following:

    if (!uriEvaluate.cache.has(openApiElement) && isPathsElement(openApiElement.paths)) {
      const isSchemaElementWith$id = e => isSchemaElement(e) && typeof e.$id !== 'undefined';
      const schemaObjectElements = filter(isSchemaElementWith$id, openApiElement);

      uriEvaluate.cache.set(openApiElement, schemaObjectElements);
      openApiElement.paths.filter(isPathItemElement).forEach(pathItem => {
        pathItem.filter(isOperationElement).forEach(operationElement => {
          uriEvaluate.cache.set(operationElement, schemaObjectElements);
        });
      });
    }

Need to test if performance implication are measurable, and if they are, it's worth implementing as well.

Update: will not be implementing this warming up as the performance difference is negligible

@char0n
Copy link
Member

char0n commented Jun 16, 2023

The performance is almost exactly the same now for OpenAPI 3.1.0 as it is for OpenAPI 3.0.x. Will release new version of SwaggerUI ASAP.

@char0n
Copy link
Member

char0n commented Jun 19, 2023

Resolved by https://github.com/swagger-api/swagger-ui/releases/tag/v5.1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants