fastapi icon indicating copy to clipboard operation
fastapi copied to clipboard

Update `create_cloned_field` to use a global cache

Open zanieb opened this issue 3 years ago • 16 comments

Closes https://github.com/tiangolo/fastapi/issues/4644 by instantiating a global dictionary to store cloned fields. This prevents repeated copying of models which can have significant performance affects. Please see the issue for more details.

This uses a WeakKeyDictionary so we avoid holding a reference to the model. I doubt this will have much practical affect since response model types are often globally defined, but it seems like good practice. This should not increase memory overhead since all the models generated by create_cloned_field will be in scope for the scope of the FastAPI application anyway.

It assigns the dictionary as a default value instead of moving the parameter to an explicitly global value. This allows a new cache to be passed if desired. However, mutable default values can be confusing and it would be fair to move it out of the function signature.

#4644 includes profiling indicating the performance increases here.

As mentioned in the issue, https://github.com/tiangolo/fastapi/issues/894#issuecomment-576484427 refers to an alternative approach that would require work in Pydantic that would make this function irrelevant. That seems better in the long, but this is an easier patch for now.

zanieb avatar Mar 04 '22 01:03 zanieb

Does not close #4644 - see comment in the linked issue.

ddanier avatar Apr 07 '22 10:04 ddanier

Isn't this a similar solution as what has been proposed here? #4105

orfisko avatar Nov 08 '22 08:11 orfisko

@orfisko Yeah it is! I missed that implementation since it wasn't linked to an issue. The choices here are a little different:

  • The cloned_types parameter is retained. This prevents a breaking change to the function signature and allows a caller to opt-out of the cache or provide a different one.
  • A WeakKeyDictionary is used. This is mostly a matter of best-practice preventing a cache from creating strong references to types. This could prevent increased memory usage if types are defined dynamically. I doubt it'll have much practical effect though.

zanieb avatar Nov 08 '22 18:11 zanieb

Does not close #4644 - see comment in the linked issue.

For others who read this, this comment refers specifically to an issue with using dynamically generated models with ForwardRef fields with this patch. Even then, you can work around the problem by updating forward refs. If someone provides an MRE, I can look into resolving the issue. This does fix the memory consumption and initialization times, see the update from the same user in https://github.com/tiangolo/fastapi/issues/4644#issuecomment-1091825267.

zanieb avatar Nov 08 '22 18:11 zanieb

📝 Docs preview for commit 4d7d18e90d4d79913b79c35053eb0479e4a7b42d at: https://636aa2502be5da007c6dc54e--fastapi.netlify.app

github-actions[bot] avatar Nov 08 '22 18:11 github-actions[bot]

📝 Docs preview for commit d7414905dd42f80c3d676deafbc04522537015cd at: https://636aa752e169f6006f8afab1--fastapi.netlify.app

github-actions[bot] avatar Nov 08 '22 19:11 github-actions[bot]

This is a great addition and sorely needed for the application I'm working on. Please let me know if there's anything I can do to help push this over the finish line!

michaelgmiller1 avatar Nov 21 '22 10:11 michaelgmiller1

when is this going to merge and release?

exherb avatar Nov 21 '22 10:11 exherb

Note that coverage is failing because this utility is not explicitly tested and consequently there's not coverage for actually using a custom cloned_types value.

@tiangolo The CI failures at https://github.com/tiangolo/fastapi/actions/runs/3535147825/jobs/5932836062 appear to be related to an change in GitHub actions that breaks Python when using actions/cache@v3. We encountered this at Prefect as well, resetting the cache resolved the issue. See https://github.com/tiangolo/fastapi/pull/5680.

zanieb avatar Nov 23 '22 19:11 zanieb

📝 Docs preview for commit 6ab34f200e915d73dbfea3f428c347e79b268332 at: https://637e77005289633eb24dbfd5--fastapi.netlify.app

github-actions[bot] avatar Nov 23 '22 19:11 github-actions[bot]

📝 Docs preview for commit b64052ffb20b0b1c1eeac704517b5c92faa76627 at: https://638366a8319a3972152f2ec9--fastapi.netlify.app

github-actions[bot] avatar Nov 27 '22 13:11 github-actions[bot]

📝 Docs preview for commit f378dfadfef09114bc11c3a56651c8f440927fa2 at: https://63837048cb2392708ef8aca8--fastapi.netlify.app

github-actions[bot] avatar Nov 27 '22 14:11 github-actions[bot]

📝 Docs preview for commit 076f15a0a4e49edbb86d614206cc2e26e4e63a58 at: https://639ce7f5ecf1fd09839b403e--fastapi.netlify.app

github-actions[bot] avatar Dec 16 '22 21:12 github-actions[bot]

Is this ready to ship? Anything I can do to help push this across the finish line?

michaelgmiller1 avatar Jan 04 '23 13:01 michaelgmiller1

This is ready to ship ~although it does not currently meet coverage requirements for the project~. The maintainer has marked this as an issue to investigate when they have time.

zanieb avatar Jan 04 '23 16:01 zanieb

@tiangolo any chance we could get this merged, if it's ready to go? this is a huge win, making both app startup and test startup significantly faster.

michaelgmiller1 avatar Jan 05 '23 13:01 michaelgmiller1

📝 Docs preview for commit bf5c3385f236b346c1347bfd675f00f86c199697 at: https://63fcf8ba6775e90057a15a43--fastapi.netlify.app

github-actions[bot] avatar Feb 27 '23 18:02 github-actions[bot]

Indeed @tiangolo. This would be a nice win all around :)

Lawouach avatar Apr 14 '23 20:04 Lawouach

📝 Docs preview for commit d8bea1e45546aae6ddd8ca7e7514a87e6e75433a at: https://643b6e9ed45d4c5bfe2bf01b--fastapi.netlify.app

github-actions[bot] avatar Apr 16 '23 03:04 github-actions[bot]

📝 Docs preview for commit ac827259919b06c6239c72b939a8c3a89c8c1f3d at: https://645507701034401069366570--fastapi.netlify.app

github-actions[bot] avatar May 05 '23 13:05 github-actions[bot]

@tiangolo What's required to get this merged?

acookin avatar May 10 '23 14:05 acookin

📝 Docs preview for commit 718425927facec27993d8b3800ef527ec80baa1e at: https://645ea712446d774207e234db--fastapi.netlify.app

github-actions[bot] avatar May 12 '23 20:05 github-actions[bot]

Thanks for keeping this up to date @madkinsz. Personally I think this is an improvement over the proposal in #4105 and would be my preference. @tiangolo, sorry to bug but can you tell shed light on what's holding this up?

followben avatar May 16 '23 00:05 followben

📝 Docs preview for commit 730717c915089adad667814d66b4a37c621801e5 at: https://646f7e9ccc99ff0466e10339--fastapi.netlify.app

github-actions[bot] avatar May 25 '23 15:05 github-actions[bot]

Hey team, looking forward for this merge :)

ulan-yisaev avatar May 30 '23 19:05 ulan-yisaev

Awesome, thanks a lot @madkinsz! 🙇

Thanks everyone for the comments, sorry for the long delay. 😅 🙈

I've been working on the migration to Pydantic v2, which will make this clone_field logic unnecessary altogether, as everything will be done on the Pydantic-core (Rust) side. Nevertheless, I'll first make a release with this improvement.

This will be available in FastAPI 0.96.0, released in the next hours. 🚀


@huonw I'm making you an honorary co-author of this PR for your work on https://github.com/tiangolo/fastapi/pull/4105 that was before this. I'm taking this one just for the simplification in the changes/diff, the weakref, and keeping the signature. 🤓

tiangolo avatar Jun 03 '23 13:06 tiangolo