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

OpenApiClientProvider with Stripe's open API hangs (3MB json) #150

Open
vrescobar opened this issue May 8, 2020 · 8 comments
Open

OpenApiClientProvider with Stripe's open API hangs (3MB json) #150

vrescobar opened this issue May 8, 2020 · 8 comments

Comments

@vrescobar
Copy link

Description

The autocompletion hangs and stops working when using OpenApiClientProvider with Stripe's open API. No visible error happens anywhere.

Repro steps

  1. Try to use OpenApiClientProvider with the stripe API
open SwaggerProvider

let [<Literal>] Schema = "https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.sdk.json"
// __SOURCE_DIRECTORY__ + "/openapi/stripe.spec3.json"
type Stripe = OpenApiClientProvider<Schema>

let main argv =
    let stripe = Stripe.Client()
    // the autocompletion dies or stops working at this point

That JSON file is about 3MB

Expected behaviour

I would expect to work as with any other open API or to get a visible error somewhere

Actual behavior

The autocompletion dies or stops working and doesn't seem to recover. To wait a large amount of time does not help.

Known workarounds

None:

  • All F# IDEs behave in the same way.
  • Serving the schema from the localhost doesn't make any difference.

Related information

@Thorium Thorium mentioned this issue May 29, 2020
@Thorium
Copy link
Member

Thorium commented Jun 2, 2020

A workaround I use is using the Stripe .NET library.
But it's not painless. It's quite low-level library, I have over 500 lines of F# code around it. They change and rename and release new versions all the time, and don't mind the backward compatibility:
Not keeping up

A payment system should be a generic component. That would be so nice to have an out-of-box working payment solution for new F# projects. There is nothing project specific, "I just want to make an online-payment". I would be willing to help creating this kind of library...

But the workflows are more complex than you initially think: A customer may want to store a card (Stripe PaymentApiCardId), but the card will expire. A customer can call to you and ask for a (partial) refund. A customer can call to the card company and dispute the transaction. Stripe can respond to a webhook, but you have to have the transaction state-fully stored. And another topic is the EU PSD2 SCA regulations... and local country specific payment regulations.

A generic library is hard to make if you have to include a data storage (some kind of database) and a web-server endpoint for webhooks and also a way of redirecting users to 3D Secure. Suddenly you have to make decisions about technology and that is not generic anymore.

@Thorium
Copy link
Member

Thorium commented Jun 2, 2020

Digging a bit this, it seems to me that the reading of the network schema, parsing it, etc, takes only 2.5 seconds total. But after that, there is some background process, involving ProvidedTypes.fs, that generates a temp-dll (like \AppData\Local\Temp\tmp434F.dll) of having the types. It will finish in around 50 seconds in my computer, and then it's actually done.

This is my little FSI script I used to test what happens:

#I @"C:\git\SwaggerProvider\src\SwaggerProvider.DesignTime\obj\Debug\netstandard2.0\"
#r @"C:\git\SwaggerProvider\src\SwaggerProvider.Runtime\bin\Debug\netstandard2.0\SwaggerProvider.Runtime.dll"
let [<Literal>] Schema = "https://raw.githubusercontent.com/stripe/openapi/master/openapi/spec3.sdk.json";;
let t = System.DateTime.Now;; //#time didn't give correct results so let's user the old way...
type Stripe = SwaggerProvider.OpenApiClientProvider<Schema, PreferAsync = true>;;
printfn "%f" (System.DateTime.Now-t).TotalSeconds;;

What could be done to fix this, is lazy schema exploration:

  • Also cache the schema type (OpenApiDocument) and use .AddMembersDelayed (.AddXmlDocDelayed, ...) instead of .AddMembers
  • ...to compile the types from the parsed tree only when the user explores the type, not all at the beginning.
  • And the recursive type generations should yield lazily recursion, not all at once. And they should be cached (e.g. with lazy-keyword) to avoid resolving them many times.
  • That way the slowness would at least divide evenly over the whole user experience, but with e.g. Stripe API, most of the users use actually only tiny bit of the whole API.
  • I guess FSharp.Data (its also generative type-provider like this) does it with World Bank provider, I'm expecting they don't download the whole bank at once...
  • That would be quite a big change for non-major use-case.

@toburger
Copy link
Contributor

toburger commented Dec 16, 2020

I'm also experiencing a substantial slow down when consuming a bigger OpenApi file like https://api.tourism.testingmachine.eu/swagger/v1/swagger.json.

@Thorium
Copy link
Member

Thorium commented Feb 6, 2021

We have a solution for Stripe now:
https://github.com/simontreanor/FunStripe

@toburger
Copy link
Contributor

toburger commented Feb 6, 2021

Nice! But I've experienced those issues also with other OpenAPI services (not 3MB though) and it would be nice to have a solution for those performance problems.

@jkone27
Copy link
Contributor

jkone27 commented Jun 12, 2021

I also had issues of slowdown with bigger openapi files, would be great if there could be some perf optimization, or improve caching (or generate some offline schema that could be re-used from file system, once the generation has happened already?) This type provider is awesome 💌

@Thorium
Copy link
Member

Thorium commented May 20, 2024

There are a lot of nested types. The time is spent on ProvidedTypes.fs on method .Compile(isHostedExecution)
This takes 7 seconds wait:
// phase 2 - emit member definitions
This takes 45 seconds wait:
// phase 3 - emit member code
Most of the time in these 2:
// Emit the constructor (if any)
// Emit the methods

If iterateTypes function could just run parallel on nestedTypes, that could help a lot. However the parameter f is doing operations on non-theadsafe manner. So using Array.Parallel.iter would need a major refactor to avoid causing erros.

@Thorium
Copy link
Member

Thorium commented May 20, 2024

As this is TP issue in general, I did open issue for the SDK: fsprojects/FSharp.TypeProviders.SDK#341

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

No branches or pull requests

4 participants