WEBVTT
00:00:00.020 --> 00:00:04.940
Today on Talk Python, the creators behind FastAPI, Flask, Django, Quart, and Litestar
00:00:05.500 --> 00:00:09.740
get practical about running apps based on their frameworks in production.
00:00:10.580 --> 00:00:15.060
Deployment patterns, async gotchas, servers, scalings, and the stuff that you only learn
00:00:15.060 --> 00:00:16.900
at 2 a.m. when the pager starts going off.
00:00:17.460 --> 00:00:20.700
For Django, we have Carlton Gibson and Jeff Triplich.
00:00:21.140 --> 00:00:23.220
For Flask, we have David Lord and Phil Jones.
00:00:23.860 --> 00:00:27.660
And on Team Litestar, we have Janek Nouvertné and Cody Fincher.
00:00:28.140 --> 00:00:31.700
And finally, Sebastian Ramirez from FastAPI is here as well.
00:00:32.279 --> 00:00:32.860
Let's jump in.
00:00:33.240 --> 00:00:38.400
This is Talk Python To Me, episode 533, recorded December 17th, 2025.
00:00:55.900 --> 00:00:57.260
Welcome to Talk Python To Me.
00:00:57.380 --> 00:01:00.620
the number one Python podcast for developers and data scientists.
00:01:01.300 --> 00:01:02.520
This is your host, Michael Kennedy.
00:01:02.900 --> 00:01:06.440
I'm a PSF fellow who's been coding for over 25 years.
00:01:07.100 --> 00:01:08.200
Let's connect on social media.
00:01:08.540 --> 00:01:11.680
You'll find me and Talk Python on Mastodon, Bluesky, and X.
00:01:11.870 --> 00:01:13.800
The social links are all in your show notes.
00:01:14.560 --> 00:01:18.080
You can find over 10 years of past episodes at talkpython.fm.
00:01:18.260 --> 00:01:21.560
And if you want to be part of the show, you can join our recording live streams.
00:01:21.760 --> 00:01:22.280
That's right.
00:01:22.470 --> 00:01:25.740
We live stream the raw uncut version of each episode on YouTube.
00:01:26.140 --> 00:01:30.740
Just visit talkpython.fm/youtube to see the schedule of upcoming events.
00:01:30.950 --> 00:01:34.620
Be sure to subscribe there and press the bell so you'll get notified anytime we're recording.
00:01:35.400 --> 00:01:39.500
Hey, before we jump into the interview, I just want to send a little message to all the companies
00:01:39.800 --> 00:01:43.420
out there with products and services trying to reach developers.
00:01:44.260 --> 00:01:45.680
That is the listeners of this show.
00:01:46.160 --> 00:01:50.000
As we're rolling into 2026, I have a bunch of spots open.
00:01:50.130 --> 00:01:55.980
So please reach out to me if you're looking to sponsor a podcast or just generally sponsor
00:01:56.040 --> 00:01:59.820
things in the community and you haven't necessarily considered podcasts, you really should.
00:02:00.720 --> 00:02:04.120
Reach out to me and I'll help you connect with the Talk Python audience.
00:02:05.080 --> 00:02:10.220
Thanks everyone for listening all of 2025. And here we go into 2026. Cheers.
00:02:11.640 --> 00:02:19.140
Hello, hello, Carlton, Sebastian, David, Cody, Yannick, Phil, Jeff, welcome back to Talk Python,
00:02:19.300 --> 00:02:22.920
all of you. Thanks for having us. Thank you for having us. Happy to be here again. We're here for
00:02:22.940 --> 00:02:30.260
what may be my favorite topic for sure. Something I spend most of my time on is web API stuff,
00:02:30.600 --> 00:02:36.320
which is awesome. So excited to have you here to give your inside look at how people should
00:02:36.320 --> 00:02:41.400
be running your framework, at least the one that you significantly contribute to, depending on
00:02:42.040 --> 00:02:47.500
which framework we're talking about, right? It's going to be a lot of fun, and I'm really excited
00:02:47.500 --> 00:02:51.739
to talk about it. However, there's an interesting fact that I've been throwing out a lot lately is
00:02:51.760 --> 00:02:56.680
that fully half of the people doing professional Python development have only been doing it for two
00:02:56.820 --> 00:03:01.760
years or less. And some of you been on the show, it was maybe two years longer than that. Let's just
00:03:01.760 --> 00:03:06.740
do a quick round of introductions for people who don't necessarily know you. We'll go around the
00:03:06.880 --> 00:03:11.280
squares here in the screen sharing. So Carlton, you're up first. Oh, I get to go first. Brilliant.
00:03:11.520 --> 00:03:15.980
Well, I'm Carlton. I work on the Django Red framework mostly. I'm a former Django fellow.
00:03:16.080 --> 00:03:20.220
I maintain a number of packages in the ecosystem. And the last few years I've been back to building
00:03:20.240 --> 00:03:25.420
stuff with Django rather than working on it. So I run a build startup that's, well, we're still
00:03:25.600 --> 00:03:30.680
going. So I'm quite excited about that. Awesome. How is it to be building with Django than building
00:03:30.900 --> 00:03:36.060
Django? Oh, I'm literally having the time of my life. Like I spent five years as a Django fellow
00:03:36.200 --> 00:03:42.000
working on Django and I just built up this backlog of things I wanted to do and I had no time and no
00:03:42.200 --> 00:03:46.740
capacity and no, no sort of nothing to work on with them. And it's just, it's just a delight.
00:03:46.980 --> 00:03:50.460
And every day I sit down on my computer thinking, oh, what's today?
00:03:50.610 --> 00:03:51.420
I look at the background.
00:03:51.780 --> 00:03:52.100
Oh, yes.
00:03:52.680 --> 00:03:54.260
And every day, a delight.
00:03:54.400 --> 00:03:56.640
So I'm still just loving it.
00:03:56.700 --> 00:03:57.100
That's awesome.
00:03:57.250 --> 00:04:01.500
So more often you're appreciating your former self than cursing your former self
00:04:01.680 --> 00:04:02.300
for the way you built.
00:04:04.040 --> 00:04:05.840
Yeah, that's an interesting one.
00:04:05.840 --> 00:04:07.100
I think we should move on before.
00:04:07.960 --> 00:04:08.300
All right.
00:04:08.420 --> 00:04:08.620
All right.
00:04:09.040 --> 00:04:14.100
Speaking of building with and for Sebastian, FastAPI.
00:04:14.230 --> 00:04:14.340
Hello.
00:04:14.700 --> 00:04:14.900
Hello.
00:04:15.980 --> 00:04:18.100
So, okay, intro for the ones that don't know me.
00:04:18.100 --> 00:04:19.060
I'm Sebastian Ramirez.
00:04:19.579 --> 00:04:21.100
I created FastAPI.
00:04:21.620 --> 00:04:23.000
Yeah, that's pretty much it.
00:04:23.340 --> 00:04:27.780
And now I have been building a company since the last two years, FastAPI Cloud, to deploy
00:04:27.980 --> 00:04:28.360
FastAPI.
00:04:28.400 --> 00:04:33.060
So, I get to drink from funny cups, as you can see.
00:04:33.520 --> 00:04:34.720
The world's best boss.
00:04:35.220 --> 00:04:35.400
Amazing.
00:04:36.180 --> 00:04:39.520
So, I think you deserve to give a bit of a shout out to FastAPI Cloud.
00:04:39.680 --> 00:04:40.200
That's a big deal.
00:04:40.360 --> 00:04:40.660
Thank you.
00:04:40.860 --> 00:04:41.480
Thank you very much.
00:04:41.620 --> 00:04:42.880
Yeah, it's super fun.
00:04:42.900 --> 00:04:47.080
And the idea is to make it super simple to deploy FastAPI applications.
00:04:47.540 --> 00:04:52.120
The idea with FastAPI was to make it very simple to build applications, build APIs,
00:04:52.940 --> 00:04:57.700
and get the idea from idea to product in record time.
00:04:57.800 --> 00:04:59.060
That was the idea with FastAPI.
00:04:59.140 --> 00:05:02.820
But then deploying that, in many cases, is just too cumbersome.
00:05:02.880 --> 00:05:03.500
It's too complicated.
00:05:03.820 --> 00:05:05.260
There are just so many things to that.
00:05:05.700 --> 00:05:09.200
So I wanted to bring something for people to be able to say, like,
00:05:09.360 --> 00:05:12.360
hey, just one command FastAPI deploy, and we take care of the rest.
00:05:12.700 --> 00:05:15.660
And then we and the team, I have an amazing thing
00:05:15.660 --> 00:05:16.980
that I've been able to work with.
00:05:17.320 --> 00:05:20.200
We suffer all the cloud pains so that people
00:05:20.380 --> 00:05:21.400
don't have to deal with that.
00:05:21.520 --> 00:05:25.840
And yeah, it's painful to build, but it's so cool to use it.
00:05:25.860 --> 00:05:27.780
You know, like that's the part when I say like, yes,
00:05:28.220 --> 00:05:29.400
this was worth it.
00:05:29.480 --> 00:05:32.240
When I get to use the thing myself, that is super cool.
00:05:32.240 --> 00:05:34.240
MARK MANDEL: Yeah, I'm assuming you build FastAPI
00:05:34.400 --> 00:05:35.560
Cloud with FastAPI somewhat.
00:05:35.720 --> 00:05:37.440
FRANCISCO MOLIN: Yes, yes, yes, exactly.
00:05:37.820 --> 00:05:39.720
FastAPI Cloud runs on FastAPI Cloud.
00:05:40.420 --> 00:05:44.200
And I get just like now random things in there and like, yes.
00:05:44.480 --> 00:05:45.260
Congrats to that again.
00:05:45.440 --> 00:05:45.980
That's super cool.
00:05:46.520 --> 00:05:47.220
David Lord, welcome.
00:05:47.600 --> 00:05:47.880
Welcome back.
00:05:48.040 --> 00:05:48.260
Yeah.
00:05:48.460 --> 00:05:48.580
Hello.
00:05:49.080 --> 00:05:49.640
I'm David Lord.
00:05:49.960 --> 00:05:54.820
I'm the lead maintainer of Pallets, which is Flask, Jinja, Click, Werkzeug.
00:05:55.020 --> 00:05:56.020
ItsDangerous, MarkupSafe.
00:05:56.560 --> 00:06:02.540
And now Pallets Eco, which is a bunch of the famous extensions for Flask that are getting
00:06:02.800 --> 00:06:03.720
community maintenance now.
00:06:04.100 --> 00:06:08.200
I've been doing that since, I think I've been the lead maintainer since like 2019, but a
00:06:08.400 --> 00:06:09.460
maintainer since like 2017.
00:06:09.920 --> 00:06:10.560
So it's been a while.
00:06:10.660 --> 00:06:11.380
That's been a while.
00:06:11.560 --> 00:06:13.860
We're coming up on seven, eight years.
00:06:14.160 --> 00:06:14.520
That's crazy.
00:06:15.200 --> 00:06:15.540
Time flies.
00:06:15.580 --> 00:06:18.280
It's always funny because I always feel like I've been doing it for way, way longer.
00:06:18.600 --> 00:06:21.340
And then I look at the actual date that I got added as a maintainer.
00:06:21.340 --> 00:06:22.660
I'm like, well, it couldn't have been that late.
00:06:22.800 --> 00:06:23.860
I was doing stuff before that, right?
00:06:24.360 --> 00:06:27.720
Well, I'm sure you were deep in flask before you got added as a maintainer of it, right?
00:06:28.140 --> 00:06:28.200
Yeah.
00:06:28.320 --> 00:06:31.780
Phil Jones, since you are also on the same org, next.
00:06:32.240 --> 00:06:32.900
Hey, welcome back.
00:06:32.980 --> 00:06:33.080
Hello.
00:06:33.440 --> 00:06:34.500
Yeah, I'm Phil Jones.
00:06:34.720 --> 00:06:37.760
I am the author of Quart, which is also part of Pallets.
00:06:37.960 --> 00:06:41.800
I also work on Werkzeug and Flask and help out there.
00:06:42.220 --> 00:06:44.660
And I've done a server called Hypercorn as well.
00:06:44.820 --> 00:06:47.680
So a bit of interest in that part of the ecosystem.
00:06:47.860 --> 00:06:49.300
What is Quart for people who don't know?
00:06:50.000 --> 00:06:52.840
Quart is basically Flask with async await.
00:06:53.340 --> 00:06:56.880
And that was the idea behind it really to make it possible to do async await.
00:06:57.240 --> 00:06:58.800
So yeah, that's pretty much it.
00:06:58.800 --> 00:07:00.820
If we, when we manage to merge them, we will.
00:07:00.920 --> 00:07:07.240
And the goal now with Quart as part of palettes is to eventually have it be one code base with Flask.
00:07:07.600 --> 00:07:11.760
But given that we both have small children now, we're moving a lot slower.
00:07:13.060 --> 00:07:13.900
Having kids is great.
00:07:14.040 --> 00:07:14.820
I have three kids.
00:07:15.480 --> 00:07:19.920
Productivity is not a thing that they are known to imbue on the parents, right?
00:07:20.060 --> 00:07:21.320
Especially in the early days.
00:07:21.780 --> 00:07:23.080
I want to say, Phil, thank you.
00:07:23.200 --> 00:07:26.820
I've been running Quart for a couple of my websites lately, and it's been amazing.
00:07:26.980 --> 00:07:27.260
Nice.
00:07:27.580 --> 00:07:29.080
Yeah, I also use it at work.
00:07:29.200 --> 00:07:31.840
We've got all our stuff in Quart, which is, yeah, it's really good fun.
00:07:31.960 --> 00:07:32.660
A bit like Carlton.
00:07:32.800 --> 00:07:39.240
So when people, if they get, if they listen to the show or they go to the website of the show and they're not on YouTube, then that somehow involves Quart.
00:07:39.640 --> 00:07:40.160
Janek, welcome.
00:07:40.560 --> 00:07:40.700
Hey.
00:07:41.160 --> 00:07:42.300
Yeah, I'm Janek Nouvertné.
00:07:42.400 --> 00:07:44.260
I work on Litestar.
00:07:44.360 --> 00:07:47.300
I just looked up how long it's been because I was curious myself.
00:07:48.020 --> 00:07:52.780
I also had the same feeling that it's been a lot longer than, it's actually only been three years.
00:07:53.380 --> 00:07:53.460
Yeah.
00:07:53.820 --> 00:07:57.060
And I also, I noticed something with all you guys here in the room.
00:07:57.200 --> 00:08:00.580
I use almost all of the projects you maintain at work,
00:08:01.960 --> 00:08:04.360
which is quite nice.
00:08:04.420 --> 00:08:06.660
We have a very big Django deployment.
00:08:06.840 --> 00:08:08.180
We have some Flask deployments.
00:08:08.180 --> 00:08:09.760
We have a few FastAPI deployments.
00:08:10.100 --> 00:08:12.360
I think we have one Quart deployment
00:08:12.700 --> 00:08:14.560
and we also have two Litestar deployments,
00:08:15.280 --> 00:08:17.620
which obviously is a lot of fun to work with.
00:08:17.800 --> 00:08:20.420
And I find it really, really nice actually
00:08:20.560 --> 00:08:23.240
to work with all these different things.
00:08:23.400 --> 00:08:26.039
It's super interesting also because like everything
00:08:26.060 --> 00:08:29.540
has its own niche that it's really good at.
00:08:29.760 --> 00:08:31.600
And even, you know, you think
00:08:31.910 --> 00:08:33.260
if you maintain a framework yourself,
00:08:33.539 --> 00:08:36.099
you tend to always recommend it for everything.
00:08:36.440 --> 00:08:38.440
But I noticed it's not actually true.
00:08:38.640 --> 00:08:40.360
There's actually quite a few cases
00:08:40.640 --> 00:08:42.479
where I don't recommend Litestar.
00:08:42.580 --> 00:08:44.480
I recommend, you know, just, you know,
00:08:44.680 --> 00:08:46.740
use Django for this or, you know,
00:08:47.120 --> 00:08:49.340
use Flask for that or use FastAPI for this
00:08:49.430 --> 00:08:52.200
because, well, they are quite different after all.
00:08:52.340 --> 00:08:54.999
And I find that really, really interesting
00:08:55.020 --> 00:09:00.480
and nice. And I think it's a good sign of a healthy ecosystem if it's not just, you know,
00:09:00.740 --> 00:09:04.560
the same thing, but different, but it actually brings something very unique and different to
00:09:04.560 --> 00:09:08.760
the table. I think that's a great attitude. And it's very interesting. You know, I feel like
00:09:08.880 --> 00:09:13.280
there's a lot of people who feel like they've kind of got to pick their tech team for everything.
00:09:13.820 --> 00:09:17.260
I'm going to build a static site. Like, well, I've got to have a Python-based static site builder.
00:09:17.300 --> 00:09:21.220
Like, well, it's a static site. Who cares what technology makes it turn? You're writing Markdown,
00:09:21.260 --> 00:09:22.080
and out comes HTML.
00:09:22.350 --> 00:09:24.000
Who cares what's in the middle, for example, right?
00:09:24.540 --> 00:09:27.600
And, you know, I feel like that's kind of a life lessons learned.
00:09:28.060 --> 00:09:28.980
Absolutely, yeah.
00:09:29.140 --> 00:09:29.760
Yeah, that's awesome.
00:09:30.290 --> 00:09:31.140
Cody, hello, hello.
00:09:31.340 --> 00:09:32.780
Yeah, hey guys, I'm Cody Fincher.
00:09:32.900 --> 00:09:34.560
I'm also one of the maintainers of Litestar.
00:09:34.840 --> 00:09:37.400
I've been there just a little bit longer than Yannick.
00:09:37.940 --> 00:09:39.700
And so it's been about four years now.
00:09:40.080 --> 00:09:41.880
And Yannick actually teed this up perfectly
00:09:42.000 --> 00:09:43.380
because I was going to say something very similar.
00:09:43.500 --> 00:09:44.820
I currently work for Google.
00:09:44.960 --> 00:09:46.460
I've been there for about three and a half years now.
00:09:46.760 --> 00:09:49.240
And we literally have every one of the frameworks
00:09:49.370 --> 00:09:50.220
you guys just mentioned,
00:09:50.240 --> 00:09:51.240
and they're all in production.
00:09:51.480 --> 00:09:54.080
And so one of the things that you'll see
00:09:54.380 --> 00:09:56.420
on the Litestar org and part of the projects
00:09:56.440 --> 00:09:59.040
that we maintain are that we have these optional batteries
00:09:59.320 --> 00:10:01.060
and most of the batteries that we have
00:10:01.260 --> 00:10:03.120
all work with the frameworks for you guys.
00:10:03.220 --> 00:10:05.800
And so it's nice to be able to use that stuff,
00:10:06.080 --> 00:10:08.580
you know, regardless of what tooling you've got
00:10:08.720 --> 00:10:10.120
or what project it is.
00:10:10.180 --> 00:10:12.140
And so, yeah, having that interoperability
00:10:12.400 --> 00:10:14.120
and the ability to kind of go between the frameworks
00:10:14.380 --> 00:10:16.800
that work the best for the right situation is crucial.
00:10:16.920 --> 00:10:18.500
And so I'm glad you mentioned that, Yannick.
00:10:18.520 --> 00:10:20.200
But yeah, nice to see you guys
00:10:20.220 --> 00:10:25.140
on the show. Cody, tell people what Litestar is. I know I had both you guys and Jacob on a while
00:10:25.260 --> 00:10:29.900
ago, but it's been a couple of years, I think. Litestar at its core is really a web framework
00:10:30.010 --> 00:10:36.260
that kind of sits somewhere in between, I'd say, Flask and FastAPI and Django. So whereas, you know,
00:10:36.300 --> 00:10:41.040
Flask doesn't really, you know, bundle a lot of batteries. There's a huge amount of, you know,
00:10:41.180 --> 00:10:44.540
third-party libraries and ecosystem that's built around it that people can add into it, but there's
00:10:44.560 --> 00:10:50.000
not really like, for instance, a database adapter or a database plugin or plugins for
00:10:50.180 --> 00:10:54.100
Vite or something like that, right, for front end development. And so what we have been doing
00:10:54.130 --> 00:10:59.820
is building a API framework that is very similar in concept to FastAPI that is also extensible.
00:10:59.930 --> 00:11:03.280
So if you want to use the batteries, they're there for you. But if you don't want to use
00:11:03.380 --> 00:11:07.560
them, you don't have to, right? And so a lot of the tooling that we built for Litestar
00:11:07.600 --> 00:11:12.920
was birthed out of a startup that I was in prior to joining Google. And so having all
00:11:12.940 --> 00:11:15.160
this boilerplate, really, it needed somewhere to go.
00:11:15.320 --> 00:11:17.620
And so a lot of this stuff ended up being plugins,
00:11:17.960 --> 00:11:19.800
which is what we bundled into Litestar
00:11:19.940 --> 00:11:22.460
so that you can kind of add in this extra functionality.
00:11:22.960 --> 00:11:24.360
And so I know I'm getting long-winded.
00:11:24.480 --> 00:11:27.340
It's somewhere between Django and Flask,
00:11:27.400 --> 00:11:29.280
if you were to think about it in terms of a spectrum,
00:11:29.340 --> 00:11:32.520
in terms of what it gives you in terms of a web framework.
00:11:32.820 --> 00:11:34.960
But in short, it does everything that all the other guys do.
00:11:35.120 --> 00:11:37.520
Very neat. It's definitely a framework I admire.
00:11:37.820 --> 00:11:39.760
Jeff Triplett, so glad you could make it.
00:11:39.860 --> 00:11:40.720
Yeah, thanks for having me.
00:11:40.860 --> 00:11:42.560
Yeah, I'm Jeff Triplett. I'm out of Lawrence, Kansas.
00:11:42.840 --> 00:11:45.100
I'm a consultant at a company called Revolution Systems.
00:11:45.550 --> 00:11:48.320
I was on, some people know me from being on the Python Software Foundation board.
00:11:48.680 --> 00:11:49.920
I've been off that for a few years.
00:11:50.240 --> 00:11:52.880
As of last week, I'm the president of the Django Software Foundation.
00:11:53.290 --> 00:11:54.620
So I've been on that board for a year.
00:11:54.860 --> 00:11:56.620
I'm kind of a Django power user, I guess.
00:11:56.720 --> 00:11:57.900
I've used it for about 20 years.
00:11:58.420 --> 00:12:02.040
And I've kind of not really worked on, I don't even think I have a patch anymore in Django.
00:12:02.460 --> 00:12:04.280
But I've done a lot with the community.
00:12:04.720 --> 00:12:08.660
I've done a lot with contributing through conferences and using utilities.
00:12:09.260 --> 00:12:12.180
I try to promote Carleton's applications like Neapolitan.
00:12:12.260 --> 00:12:15.780
And if I like tools, Python tools in general, I try to advocate for it.
00:12:16.040 --> 00:12:18.620
I've also used all of these applications.
00:12:18.900 --> 00:12:21.020
Litestar, I haven't, but I have a friend who talks about it a lot.
00:12:21.320 --> 00:12:22.860
And so I feel like I know a lot from it.
00:12:23.100 --> 00:12:25.440
As a consultant, we tend to go with the best tool for the job.
00:12:25.600 --> 00:12:26.840
So I've done a little bit of FastAPI.
00:12:27.340 --> 00:12:31.020
I worked with Flask a lot over the years, even though we're primarily a Django shop.
00:12:31.360 --> 00:12:32.500
It just depends on what the client needs.
00:12:32.580 --> 00:12:35.940
And you see a lot of different sizes of web app deployments.
00:12:36.100 --> 00:12:38.140
So I think that's going to be an interesting angle for sure.
00:12:38.320 --> 00:12:38.860
Yeah, absolutely.
00:12:39.220 --> 00:12:41.560
Small ones to hundreds of servers.
00:12:42.100 --> 00:12:46.400
We don't see it as much anymore the last four or five years, especially with like CDNs and caching.
00:12:46.800 --> 00:12:49.720
We just don't see load like we did, you know, 10 years ago or so.
00:12:50.060 --> 00:12:55.160
And then I also do a lot of like small, I kind of call them some of them little dumb projects, but some are just fun.
00:12:55.480 --> 00:12:59.800
Like I've got a FastAPI web ring that I wrote a year ago for April Fool's Day.
00:13:00.100 --> 00:13:03.440
And for some reason that kind of took off and people liked it, even though it was kind of a joke.
00:13:03.780 --> 00:13:07.620
So I started like peppering it on a bunch of sites and I maintain like Django packages.
00:13:08.000 --> 00:13:11.020
I do a newsletter, Django News newsletter, just kind of lots of fun stuff.
00:13:11.340 --> 00:13:13.360
Definitely looking forward to hearing all of your opinions.
00:13:14.000 --> 00:13:17.460
So I've got a bunch of different your app in production topics
00:13:17.580 --> 00:13:20.560
I thought we could just work around or talk over.
00:13:20.900 --> 00:13:24.180
So I thought maybe the first one is what would you recommend,
00:13:24.540 --> 00:13:26.340
or if you don't really have a strong recommendation,
00:13:26.620 --> 00:13:31.900
what would you choose for yourself to put your app in your framework in production?
00:13:32.080 --> 00:13:35.820
I'm thinking app servers, reverse proxies like Nginx or Caddy.
00:13:36.360 --> 00:13:37.320
Do you go for threaded?
00:13:37.860 --> 00:13:39.180
Try to scale out with threads.
00:13:39.340 --> 00:13:44.820
you try to scale out with processes, Docker, no Docker, Kubernetes. What are we doing here,
00:13:44.940 --> 00:13:49.560
folks? Carlton. I think we'll just keep going around the circle here. So you may get the first
00:13:49.880 --> 00:13:52.280
round of everyone. No, I'll try to mix it up, but let's do it this time.
00:13:52.360 --> 00:14:00.020
I do the oldest school thing in the book. I run Nginx as my front end. I'll stick a
00:14:00.100 --> 00:14:05.839
WSGI server behind it with a pre-fork, a few workers, depending on CPU size, depending on
00:14:05.860 --> 00:14:10.900
the kind of requests I'm handling. These days, in order to handle long-lived requests,
00:14:10.960 --> 00:14:14.860
like server-sent events, that kind of, or WebSocket type things, I'll run an ASGI server as a kind
00:14:14.860 --> 00:14:18.040
of sidecar. I've been thinking about this a lot, actually. But yeah, this is interesting.
00:14:18.280 --> 00:14:22.120
If you're running a small site and you want long-lived requests, just run ASGI. Just use
00:14:22.240 --> 00:14:29.360
ASGI. Because any of the servers, Hypercorn, Uvicorn, Daphne, Granian is the new hot kid on
00:14:29.360 --> 00:14:34.279
the bot, right? All of those will handle your traffic, no problem. But for me, the scaling
00:14:34.300 --> 00:14:39.720
Pallets and WSGI are so well known and just like i can do the maths on the back of the pencil i know
00:14:39.900 --> 00:14:45.740
exactly how to scale it up having done it for so long for me for my core application i would still
00:14:45.880 --> 00:14:51.080
rather use the wsgi server and then limit the async stuff to just to the use cases where it's
00:14:51.180 --> 00:14:58.040
particularly suited so i'll do that um process manager i deploy using systemd if i want to if i
00:14:58.120 --> 00:15:03.719
want a container i'll use podman by systemd it's as old school as it gets i'll very often run a
00:15:03.740 --> 00:15:05.740
a Redis instance on localhost for caching,
00:15:05.960 --> 00:15:07.780
and that will be it.
00:15:08.100 --> 00:15:09.300
And that will get me an awful long way.
00:15:09.340 --> 00:15:10.020
If I have to schedule,
00:15:10.880 --> 00:15:11.940
I just get a bigger box.
00:15:12.260 --> 00:15:12.960
And a bigger box.
00:15:13.020 --> 00:15:13.460
Yeah, yeah, yeah.
00:15:13.560 --> 00:15:16.040
I really, really, really need multiple boxes.
00:15:16.260 --> 00:15:16.720
Well, then we'll talk.
00:15:16.820 --> 00:15:18.400
I feel like you and I are in a similar vibe.
00:15:18.840 --> 00:15:20.680
But one thing I want to sort of throw out there to you,
00:15:20.760 --> 00:15:22.360
but also sort of the others is,
00:15:22.720 --> 00:15:23.780
what are we talking with databases?
00:15:24.300 --> 00:15:26.460
Like, who is bold enough to go SQLite?
00:15:26.740 --> 00:15:28.100
Anyone's going SQLite out there?
00:15:28.900 --> 00:15:30.020
Yeah, it depends, right?
00:15:30.100 --> 00:15:31.580
It just depends on what you're doing, right?
00:15:31.580 --> 00:15:33.460
And how many concurrent users you're going to have.
00:15:33.600 --> 00:15:34.640
It really is amazing there.
00:15:34.800 --> 00:15:36.860
The Palette's website is running on Flask,
00:15:37.160 --> 00:15:38.080
which I wasn't doing for a while.
00:15:38.080 --> 00:15:39.400
I was doing a static site generator.
00:15:39.770 --> 00:15:43.340
Then I got inspired by Andrew Godwin's static dynamic sites.
00:15:43.860 --> 00:15:46.520
And so it loads up all these markdown files,
00:15:46.730 --> 00:15:49.900
static markdown files into a SQLite database at runtime
00:15:50.480 --> 00:15:51.720
and then serves off of that
00:15:51.750 --> 00:15:52.920
because you can query really fast.
00:15:53.100 --> 00:15:54.160
Oh, that's awesome. I love it.
00:15:54.260 --> 00:15:56.140
So I am using SQLite for the Palette's website.
00:15:56.480 --> 00:16:00.220
Yeah, I also do have a few small apps that use SQLite.
00:16:00.340 --> 00:16:02.980
And one recently that's Cody's fault
00:16:03.000 --> 00:16:04.580
because he put me on that track
00:16:05.240 --> 00:16:08.740
where it's running a SQLite database in the browser
00:16:08.960 --> 00:16:12.300
because nowadays it's quite easy to do that.
00:16:12.860 --> 00:16:15.200
And then you can do all sorts of stuff with it,
00:16:15.300 --> 00:16:19.080
like hook into it with DuckDB and perform some analysis.
00:16:19.320 --> 00:16:23.020
So you don't actually need to run any sort of server at all.
00:16:23.060 --> 00:16:26.780
You can just throw some files into Nginx and serve your data.
00:16:26.980 --> 00:16:28.240
And as long as that's static,
00:16:28.300 --> 00:16:30.160
you have a super, super simple deployment.
00:16:30.680 --> 00:16:32.200
So yeah, definitely SQLite.
00:16:32.580 --> 00:16:34.280
If you can, I like it.
00:16:34.360 --> 00:16:35.000
I agree.
00:16:35.100 --> 00:16:35.460
It's interesting.
00:16:36.080 --> 00:16:38.100
The database probably won't go down with that, probably.
00:16:38.500 --> 00:16:40.020
Let's do this by framework.
00:16:40.180 --> 00:16:42.340
So we'll do vertical slices in the visual here.
00:16:42.600 --> 00:16:43.140
So Jeff.
00:16:43.560 --> 00:16:46.000
Yeah, Django, Postgres, pretty old school stack.
00:16:46.320 --> 00:16:49.120
I think putting a CD in front of anything is just a win.
00:16:49.210 --> 00:16:51.000
So whether you like Fastly or Cloudflare,
00:16:51.150 --> 00:16:52.380
you get a lot of mileage out of it.
00:16:52.660 --> 00:16:53.760
You learn a lot about caching
00:16:53.920 --> 00:16:55.880
because it's kind of hard to cache Django by default.
00:16:56.200 --> 00:16:57.200
So you get to play with curl
00:16:57.430 --> 00:16:59.260
and kind of figure out why very headers are there.
00:16:59.670 --> 00:17:02.180
And it's a good learning experience to get through that.
00:17:02.240 --> 00:17:06.600
I also like Coolify, which is kind of new, at least new to me and new to Michael.
00:17:06.689 --> 00:17:08.240
We talk about this in our spare time a lot.
00:17:08.520 --> 00:17:11.800
It's just kind of a boring service that'll launch a bunch of containers for you.
00:17:12.280 --> 00:17:15.040
There's a bunch of one-click installs, so Postgres is a one-click.
00:17:15.270 --> 00:17:18.240
It also does backups for you, which is really nice to have for free.
00:17:18.520 --> 00:17:21.640
I run a couple dozen sites through it and really like it.
00:17:21.900 --> 00:17:26.140
You can either do a hosted forum, I don't get any money from it, or you can run the open-source version.
00:17:26.560 --> 00:17:27.140
I do both.
00:17:27.310 --> 00:17:30.500
I've got like a home lab that I just run stuff using the open-source version.
00:17:30.520 --> 00:17:32.860
And for five bucks a month, it's worth it to run a couple servers.
00:17:33.300 --> 00:17:35.400
And like Carlton said, you can just scale up.
00:17:35.880 --> 00:17:40.080
Yeah, it's got a bunch of one-click deploy for self-hosted SaaS things as well.
00:17:40.260 --> 00:17:44.860
Like I want an analytics stack of containers that run in its own isolated bit.
00:17:44.880 --> 00:17:45.960
Just click here and go.
00:17:46.280 --> 00:17:48.520
Yeah, one-click, it's installed and you're up.
00:17:48.740 --> 00:17:52.760
And then once you get one Django, Flask, FastAPI site working with it,
00:17:53.020 --> 00:17:54.440
and it uses like a Docker container.
00:17:54.780 --> 00:17:58.500
Once you get that set up, it's really easy to just kind of duplicate that site,
00:17:58.960 --> 00:18:01.160
plug it in to GitHub or whatever your Git provider is.
00:18:01.260 --> 00:18:05.460
And it's a nice experience for what normally is just our syncing files
00:18:05.820 --> 00:18:06.860
and life's too short for that.
00:18:06.960 --> 00:18:09.500
Sebastian, I want to have you go last on this one
00:18:09.500 --> 00:18:12.160
because I think you've got something pretty interesting
00:18:12.280 --> 00:18:14.260
with FastAPI Cloud to dive into.
00:18:14.480 --> 00:18:16.520
But let's do Litestar next. Cody.
00:18:16.840 --> 00:18:19.580
I have actually bought all the way in on Granian.
00:18:19.840 --> 00:18:22.420
So for the ASGI server, I've actually been running Granian now
00:18:22.560 --> 00:18:24.060
for I'd say a year in production.
00:18:24.420 --> 00:18:25.660
It's worked pretty well.
00:18:26.180 --> 00:18:28.920
There's a couple of new things that I'm actually kind of
00:18:28.940 --> 00:18:31.460
with. I don't know how well they're going to work out. So I'm going to go ahead and throw this out
00:18:31.540 --> 00:18:37.900
there. But Granian is one of the few ASGI servers that supports HTTP2. And it actually can do HTTP2
00:18:38.040 --> 00:18:42.100
clear text. And so this is part of the next thing I'm going to say. Because I work for Google, I'm
00:18:42.480 --> 00:18:47.440
actively using lots of Kubernetes and Cloud Run mainly. And so most of the things that I deploy
00:18:47.520 --> 00:18:52.860
are containerized on Cloud Run. And I typically would suggest if you're not using something like
00:18:53.100 --> 00:18:57.140
SystemD and deploying it directly on bare metal, then you are going to want to let the container
00:18:57.160 --> 00:18:59.740
or whatever you're using to manage your processes,
00:19:00.340 --> 00:19:01.640
manage that and spin that up.
00:19:01.670 --> 00:19:03.900
And so I typically try to allocate,
00:19:04.070 --> 00:19:05.780
you know, like one CPU for the container
00:19:05.920 --> 00:19:08.660
and let the actual framework scale it up and down as needed.
00:19:09.080 --> 00:19:11.600
Cloud Run itself has a, like an ingress,
00:19:11.830 --> 00:19:13.320
like a load balancer that sits in front
00:19:13.480 --> 00:19:14.480
that it automatically configures.
00:19:14.510 --> 00:19:16.980
And you're required to basically serve up
00:19:17.160 --> 00:19:19.300
clear text traffic in when you run Cloud Run.
00:19:19.740 --> 00:19:22.580
And because now Granian supports HTTP2
00:19:22.820 --> 00:19:25.340
and Cloud Run supports HTTP2 clear text,
00:19:25.500 --> 00:19:28.140
you can now serve Granian as HTTP2 traffic.
00:19:28.520 --> 00:19:30.960
The good thing about that is that you get an unlimited upload size.
00:19:31.060 --> 00:19:33.380
And so there are max thresholds to what you can upload
00:19:33.660 --> 00:19:34.720
into the various cloud environments.
00:19:35.500 --> 00:19:37.820
HTTP2 usually circumvents that or gets around it
00:19:37.900 --> 00:19:39.120
because of the way the protocol works.
00:19:39.200 --> 00:19:42.020
And so you git additional features and functionality because of that.
00:19:42.200 --> 00:19:43.780
So anyway, that's what I typically do.
00:19:44.000 --> 00:19:46.280
And most of my databases are usually Postgres,
00:19:47.020 --> 00:19:50.180
AlloyDB if it needs to be something that's on the analytical side.
00:19:50.600 --> 00:19:52.160
Yeah, I'm on Team Granian as well.
00:19:52.160 --> 00:19:53.600
I think that's a super neat framework.
00:19:53.840 --> 00:19:56.660
I had Giovanni on who's behind it a while ago.
00:19:57.050 --> 00:19:59.820
It seems like it's not as popular,
00:20:00.220 --> 00:20:02.000
but it's based on Hyper from the Rust world,
00:20:02.220 --> 00:20:05.500
which has like 130,000 projects based on it or something.
00:20:05.530 --> 00:20:09.420
So, you know, at its core, it's still pretty battle-tested.
00:20:11.420 --> 00:20:16.140
this portion of talk python to me is brought to you by our course just enough python for data
00:20:16.380 --> 00:20:21.160
scientists if you live in notebooks but need your work to hold up in the real world check out just
00:20:21.220 --> 00:20:26.580
enough python for data scientists it's a focused code first course that tightens the python you
00:20:26.840 --> 00:20:33.080
actually use and adds the habits that make results repeatable we refactor messy cells into functions
00:20:33.100 --> 00:20:38.700
and packages, use Git on easy mode, lock environments with uv, and even ship with Docker.
00:20:39.380 --> 00:20:42.020
Keep your notebook speed, add engineering reliability.
00:20:42.720 --> 00:20:43.900
Find it at Talk Python Training.
00:20:44.140 --> 00:20:46.900
Just click courses in the navbar at talkpython.fm.
00:20:47.959 --> 00:20:49.260
Yannick, how about you?
00:20:49.560 --> 00:20:50.800
You've got a variety, it sounds like.
00:20:50.860 --> 00:20:51.680
Yeah, definitely.
00:20:53.000 --> 00:20:59.340
There's a pretty clear split between what I do at work and what I do outside of that.
00:20:59.440 --> 00:21:01.720
So at work, it's Kubernetes deployments.
00:21:01.900 --> 00:21:05.360
And we managed that pretty much the same way that Cody described.
00:21:05.540 --> 00:21:09.700
So it's one or two processes per pod max.
00:21:10.060 --> 00:21:14.660
So you can have Kubernetes scaled or even manually easily scale that up.
00:21:14.760 --> 00:21:19.540
You can just go into Kubernetes and say, OK, do me one to five more pods or whatever.
00:21:20.060 --> 00:21:20.680
And don't have to worry.
00:21:21.040 --> 00:21:22.620
Don't have to start calculating whatever.
00:21:23.280 --> 00:21:28.580
Most of the stuff we run nowadays with uvicorn or Django deployment
00:21:28.900 --> 00:21:34.080
up until I think three months ago or so was running under Gunicorn,
00:21:34.160 --> 00:21:35.240
but we switched that actually.
00:21:35.760 --> 00:21:37.800
And it's been a really great experience.
00:21:38.380 --> 00:21:42.880
I think we tried that a year ago and it didn't work out quite so well.
00:21:42.900 --> 00:21:47.440
There was some things that didn't work as expected or didn't perform great
00:21:47.700 --> 00:21:52.220
or Django was throwing some errors or Uvicorn was throwing some errors.
00:21:52.480 --> 00:21:55.040
And then apparently all of that got fixed
00:21:55.360 --> 00:21:58.380
because now it runs without any issue for the production.
00:21:58.560 --> 00:22:06.400
Yeah, for people who don't know, the vibe used to be run Gunicorn, but then with uvicorn workers, if you're doing async stuff.
00:22:06.510 --> 00:22:12.620
And then uvicorn kind of stepped up its game and said, you can actually treat us as our own app server.
00:22:12.940 --> 00:22:14.580
We'll manage lifecycle and stuff.
00:22:14.590 --> 00:22:16.120
And so that's the path you took, right?
00:22:16.240 --> 00:22:16.740
Yeah, exactly.
00:22:16.970 --> 00:22:17.340
Before that.
00:22:17.630 --> 00:22:21.980
Well, no, actually, before that, we didn't because our Django is fully synchronous.
00:22:22.170 --> 00:22:24.020
It doesn't do any async.
00:22:24.220 --> 00:22:26.820
So it was just bare metal Gunicorn.
00:22:26.960 --> 00:22:30.100
And it's still synchronous with just running it under Uvicorn.
00:22:30.540 --> 00:22:34.200
But interestingly, still quite a bit faster in a few cases.
00:22:34.800 --> 00:22:38.520
We tried that out and we low tested it in a couple of scenarios
00:22:38.800 --> 00:22:41.100
and we found that it makes a lot of sense.
00:22:41.560 --> 00:22:44.200
But outside of that, I do have a lot of, well,
00:22:44.780 --> 00:22:48.100
very simplistic deployments that are also just systemd
00:22:48.380 --> 00:22:53.339
and a couple of Docker compose files and containers
00:22:53.360 --> 00:22:58.620
that are managed through some old coupled together Ansible things.
00:22:59.160 --> 00:23:02.620
But I think the oldest one that I have still running is from 2017.
00:23:03.500 --> 00:23:07.040
And it's been running without a change for like four or five years.
00:23:07.220 --> 00:23:07.920
That is awesome.
00:23:08.180 --> 00:23:11.680
I don't see a reason to do anything about it because the app works.
00:23:11.920 --> 00:23:13.500
It's being used productively.
00:23:14.100 --> 00:23:16.060
So why change anything about that?
00:23:16.060 --> 00:23:17.000
No need to introduce.
00:23:17.380 --> 00:23:18.180
Just don't touch it.
00:23:18.560 --> 00:23:22.340
Yeah, I was actually looking into Coolify that you two guys mentioned.
00:23:22.760 --> 00:23:28.040
I was thinking about, you know, maybe upgrading it to that, but I played around with it and I thought, well, why?
00:23:28.220 --> 00:23:31.520
You know, if I have to look into that deployment maybe once a year.
00:23:31.960 --> 00:23:36.080
So that's really nothing to gain for me to make it more complicated.
00:23:36.560 --> 00:23:37.960
David, Team Flask.
00:23:38.320 --> 00:23:45.000
I mentioned this before the show started, but I think I'm pretty sure I've said this the last time I was on Talk Python,
00:23:45.400 --> 00:23:50.760
but the projects I do for work typically have less than 100 users.
00:23:51.700 --> 00:23:54.500
And so my deployment is usually really simple.
00:23:54.680 --> 00:23:57.780
And usually they've chosen like Azure or AWS already.
00:23:58.180 --> 00:24:00.020
So we just have a Docker container
00:24:00.360 --> 00:24:03.020
and we put it on the relevant Docker container host
00:24:03.080 --> 00:24:05.480
in that service and it just works for them.
00:24:05.740 --> 00:24:08.400
We have a Postgres database and we have like Redis.
00:24:08.880 --> 00:24:11.280
But I never really had to deal with like scaling
00:24:11.820 --> 00:24:13.380
or that sort of stuff.
00:24:13.740 --> 00:24:16.780
But the funny thing is like, at least for my work,
00:24:16.940 --> 00:24:19.360
I'm always, we're often replacing older systems.
00:24:19.840 --> 00:24:32.620
And so even a single Docker container running a Flask application is way more performant and responsive than anything they're used to from like some 20 year old or 30 year old Java system.
00:24:32.880 --> 00:24:38.420
Right. And it can just respond on a small container with like a little bit of CPU and a little bit of memory.
00:24:38.740 --> 00:24:41.380
They're always shocked at like, how much do we need to pay for?
00:24:41.520 --> 00:24:43.280
Oh, just like it'll run a potato.
00:24:44.160 --> 00:24:46.680
You know, there's only 100 users and they're like, that's a lot of users.
00:24:48.400 --> 00:24:52.740
So my recommendation is always start small and then scale up from there.
00:24:52.900 --> 00:24:54.980
Don't try to overthink it ahead of time.
00:24:55.440 --> 00:24:58.860
Yeah, for my personal stuff, I'm using like Docker containers now and fly.io.
00:24:59.070 --> 00:25:00.160
I haven't gotten in.
00:25:00.550 --> 00:25:04.420
So I do want to look into Granian and Coolify, but I haven't gotten there yet.
00:25:04.920 --> 00:25:09.160
And for the Docker container, I can definitely recommend pythonspeed.org.
00:25:09.480 --> 00:25:13.260
I don't remember off the top of my head who writes that, but it's somebody in the Python
00:25:13.860 --> 00:25:14.240
ecosystem.
00:25:14.720 --> 00:25:18.480
And they have a whole series of articles on how to optimize your Docker container.
00:25:18.960 --> 00:25:22.440
And that sounds really complicated, but you end up with a Docker file that's like 20 lines
00:25:22.530 --> 00:25:23.260
long or something.
00:25:23.450 --> 00:25:26.280
So it's not like there's crazy things.
00:25:26.290 --> 00:25:28.140
It's just you have to know how to structure it.
00:25:28.330 --> 00:25:30.100
And then I just copy and paste that to the next project.
00:25:30.380 --> 00:25:30.560
Nice.
00:25:30.820 --> 00:25:30.900
Yeah.
00:25:31.300 --> 00:25:34.360
I resisted doing Docker for a long time because I'm like, I don't want that extra complexity.
00:25:34.550 --> 00:25:38.420
But then I realized the stuff you put in the Docker file is really what you just type in
00:25:38.420 --> 00:25:40.260
the terminal once and then you forget.
00:25:41.140 --> 00:25:44.580
I mean, always using Postgres, Redis, probably if I need some background.
00:25:44.680 --> 00:25:51.120
tasks, just plain SMTP server for email. I wrote for all three of those things. I wrote new
00:25:51.440 --> 00:25:55.920
extensions in the Flask ecosystem that I'm trying to get more people to know about now. So Flask
00:25:56.060 --> 00:26:01.920
SQLAlchemy Lite, L-I-T-E, instead of Flask SQLAlchemy, takes a much more lightweight approach to
00:26:02.360 --> 00:26:08.100
integrating SQLAlchemy with Flask. And then Flask Redis, I revived from like 10 years of
00:26:08.420 --> 00:26:12.699
non-maintenance. And then I wrote this whole system, this whole pluggable email system called
00:26:12.720 --> 00:26:18.560
email simplified, kind of inspired by Django, Django's pluggable system, except, and so there's
00:26:18.560 --> 00:26:23.560
like Flask email simplified to integrate that with Flask. But unlike Django, you can use email
00:26:23.780 --> 00:26:27.580
simplified in any library you're writing, in any Python application you're writing. It doesn't have
00:26:27.580 --> 00:26:32.780
to be a Flask web framework. It's pluggable as the library itself. And then you can also integrate
00:26:32.830 --> 00:26:38.640
it with Flask or something else. So Flask email simplified. I get like three downloads a month
00:26:38.640 --> 00:26:43.240
right now. So it needs some popularity. Awesome. I've been doing the non-simplified email lately.
00:26:43.440 --> 00:26:48.620
So I'm happy to hear that there might be a better way. Yeah. I think people do underappreciate just
00:26:48.720 --> 00:26:53.780
how much performance you got out of Python web apps. You know, they're like, oh, we're going to
00:26:53.800 --> 00:26:59.340
need to rewrite this and something else because the GIL or whatever. Like I decided just to make
00:26:59.340 --> 00:27:04.499
a point to pull up the tail till my log running court, by the way. And each one of these requests
00:27:04.520 --> 00:27:10.120
doing like multiple db calls and it's like 23 milliseconds six milliseconds three milliseconds
00:27:10.600 --> 00:27:15.840
you know nine milliseconds it's like that's good enough for that's a lot of requests per second
00:27:16.320 --> 00:27:21.800
per worker until you gotta you gotta have a lot of traffic speaking of court phil what's your take
00:27:21.940 --> 00:27:27.860
on this one i think it's very similar i also build docker containers and uh with a postgres database
00:27:27.980 --> 00:27:34.480
on the back end and i run hypercorn as the ascii server and put them behind a aws load balancer
00:27:34.560 --> 00:27:35.840
and just run them in ECS.
00:27:36.080 --> 00:27:37.840
And I think it's pretty simple,
00:27:38.120 --> 00:27:39.680
but I guess it depends on your biases.
00:27:39.960 --> 00:27:41.840
But yeah, that's all we do really.
00:27:41.880 --> 00:27:42.760
And it goes a long way.
00:27:42.940 --> 00:27:44.800
There are multiple ECS tasks,
00:27:45.240 --> 00:27:47.480
mostly because if one falls over rather than scaling,
00:27:47.760 --> 00:27:50.240
it's usually the database that you need to scale, I find.
00:27:50.520 --> 00:27:51.940
But yeah, that's how we run it.
00:27:52.180 --> 00:27:53.940
The nice thing for me about Hypercorn
00:27:54.280 --> 00:27:55.700
is that I can play with HTTP 3.
00:27:56.040 --> 00:27:57.320
So that's what we're doing at times.
00:27:57.640 --> 00:27:58.780
Oh, HTTP 3, okay.
00:27:59.380 --> 00:28:01.240
I've just been getting my HTTP 2 game down,
00:28:01.780 --> 00:28:02.980
so I'm already behind the game.
00:28:03.860 --> 00:28:04.820
What's the deal with HTTP/3?
00:28:05.080 --> 00:28:08.400
It's obviously a totally new way of doing it over UDP now
00:28:08.720 --> 00:28:09.520
rather than TCP.
00:28:10.340 --> 00:28:11.720
Although at the application level,
00:28:11.960 --> 00:28:13.180
you can't tell any difference really.
00:28:13.460 --> 00:28:15.480
But I mean, I just find it interesting.
00:28:15.660 --> 00:28:17.580
I'm not really sure it will help too much.
00:28:17.920 --> 00:28:21.140
And it's probably best if you've got users who have not that
00:28:21.340 --> 00:28:22.100
great a network connection.
00:28:22.600 --> 00:28:25.840
But for most other cases, I don't think it matters too much.
00:28:25.880 --> 00:28:29.140
Just keep blasting packets until some of them get through.
00:28:29.940 --> 00:28:30.320
OK, fine.
00:28:30.320 --> 00:28:31.200
We'll give you a page eventually.
00:28:31.380 --> 00:28:32.260
There's three pages, actually.
00:28:32.620 --> 00:28:43.300
All right, Sebastian, you are running not just FastAPI from your experience, but you're running FastAPI for a ton of people through FastAPI Cloud at, I'm sure, many different levels.
00:28:43.580 --> 00:28:47.660
This probably sounds like a shameless plug, and it kind of is, but it's sort of expected.
00:28:48.120 --> 00:28:50.980
I will deploy FastAPI or FastAPI Cloud.
00:28:51.520 --> 00:28:54.760
Just because, well, the idea is just to make it super simple to do that.
00:28:54.960 --> 00:28:58.440
You know, like if you are being able to run the command FastAPI run.
00:28:58.820 --> 00:29:01.540
So FastAPI run has like the production server
00:29:01.600 --> 00:29:03.020
that is using Uvicorn underneath.
00:29:03.440 --> 00:29:04.680
And if you can run that,
00:29:04.800 --> 00:29:06.420
then you can run also FastAPI deploy.
00:29:06.740 --> 00:29:10.160
And then like, you know, like it will most probably just work.
00:29:10.600 --> 00:29:13.260
And, you know, we just wrap everything and like deploy,
00:29:13.740 --> 00:29:16.580
build, install, deploy, handle HTTPS, all the stuff
00:29:17.020 --> 00:29:19.100
without needing any Docker file or anything like that.
00:29:19.320 --> 00:29:20.720
And I think for many use cases,
00:29:20.900 --> 00:29:22.880
it's just like simpler being able just to do that.
00:29:23.100 --> 00:29:25.280
There are so many projects that I have been now building,
00:29:25.520 --> 00:29:29.660
like random stuff that is not really important, but now I can.
00:29:30.220 --> 00:29:34.580
And before it was like, yeah, well, I know how to deploy this thing like fully with like
00:29:34.700 --> 00:29:37.380
all the bells and whistles, but it's just so much work that yeah, I know later.
00:29:38.000 --> 00:29:40.700
So for that, I would end up just like going with that.
00:29:41.160 --> 00:29:42.140
Now if I didn't...
00:29:42.140 --> 00:29:46.580
Well, what I was going to ask is how much are you willing to tell us how things run inside
00:29:47.140 --> 00:29:47.940
FastAPI Cloud?
00:29:48.280 --> 00:29:52.180
Oh, I can't, it's just so much stuff that is going on.
00:29:52.200 --> 00:29:56.780
And it's also, it's fun that nowadays that they're like,
00:29:57.340 --> 00:30:00.380
we have Docker and we have Docker Swarm and there was Nomad
00:30:00.540 --> 00:30:02.720
and Kubernetes and oh, Kubernetes won.
00:30:02.810 --> 00:30:05.940
And then we have the cloud providers and there's AWS
00:30:06.460 --> 00:30:07.840
and Google and Azure.
00:30:08.290 --> 00:30:10.800
And you will expect that all these things
00:30:11.120 --> 00:30:14.360
and all this complexity is like, now that it's like, okay,
00:30:14.540 --> 00:30:15.420
these are the clear winners.
00:30:15.640 --> 00:30:17.400
So it's like a lot of complexity to take on,
00:30:17.470 --> 00:30:20.980
but once you do it all works, but it doesn't.
00:30:21.200 --> 00:30:27.360
And it's just like so much work to get things to work together, to work correctly.
00:30:27.920 --> 00:30:32.380
And the official resources from the different providers and things,
00:30:32.750 --> 00:30:36.540
in many cases, it's like, oh, the solution is hidden in this issue somewhere in GitHub
00:30:36.700 --> 00:30:38.240
because the previous version was obsolete,
00:30:38.460 --> 00:30:43.180
but now the new version of this package or whatever is like, it's just, it's crazy.
00:30:43.620 --> 00:30:50.400
But like, yeah, so if I didn't have FastAPI Cloud, I will probably use containers.
00:30:50.660 --> 00:30:51.460
I will probably use Docker.
00:30:51.940 --> 00:30:52.980
If it's like something simple,
00:30:53.030 --> 00:30:54.860
I will deploy with Docker Compose,
00:30:55.000 --> 00:30:57.400
probably try to scale minimum replicas.
00:30:57.920 --> 00:30:59.840
I don't remember Docker Compose has that.
00:30:59.940 --> 00:31:01.840
I remember that Docker Swarm had that,
00:31:01.940 --> 00:31:04.900
but then Docker Swarm sort of lost against Kubernetes.
00:31:05.420 --> 00:31:08.320
I will put a traffic load balancer in front
00:31:08.390 --> 00:31:11.840
to handle HTTPS and, yeah, well, like regular load balancing.
00:31:12.970 --> 00:31:14.700
And, yeah, just regular Uvicorn.
00:31:14.860 --> 00:31:17.800
What some of the folks we were talking about before,
00:31:18.440 --> 00:31:22.200
At some point, we needed to have Gunicorn on top of Uvicorn
00:31:22.300 --> 00:31:24.580
because Uvicorn wouldn't be able to handle workers.
00:31:25.040 --> 00:31:27.240
But now Uvicorn can handle its workers like everything
00:31:27.500 --> 00:31:30.400
and handle the main thing was some VE processes
00:31:30.600 --> 00:31:33.980
and reaping the processes and handling the stuff.
00:31:34.300 --> 00:31:35.360
Now it can't just do that.
00:31:35.380 --> 00:31:37.460
So you can just run plain Uvicorn.
00:31:37.740 --> 00:31:40.100
So if you're using FastAPI and you say FastAPI run,
00:31:40.560 --> 00:31:41.520
that already does that.
00:31:41.660 --> 00:31:42.920
So if you're deploying on your own,
00:31:42.960 --> 00:31:44.580
you can just use the FastAPI run command.
00:31:44.940 --> 00:31:46.940
Then, of course, you have to deal with the scaling
00:31:47.160 --> 00:31:48.920
and HTTPS and a lot of balancing
00:31:48.970 --> 00:31:49.600
and all the stuff,
00:31:49.700 --> 00:31:51.620
but the core server,
00:31:51.750 --> 00:31:53.260
you can just run it directly.
00:31:53.640 --> 00:31:54.980
If going beyond that,
00:31:55.080 --> 00:31:56.040
then there will probably be
00:31:56.290 --> 00:31:57.820
some cluster Kubernetes
00:31:58.560 --> 00:31:59.760
and trying to scale things,
00:32:00.120 --> 00:32:01.760
figure out the ways to scale things
00:32:02.420 --> 00:32:04.460
based on the load of the requests,
00:32:05.950 --> 00:32:06.940
like scaling automatically.
00:32:08.020 --> 00:32:10.580
Having normally one container per process
00:32:10.900 --> 00:32:12.720
to be able to scale that more dynamically
00:32:13.000 --> 00:32:14.260
without depending on the local memory
00:32:14.400 --> 00:32:15.200
for each one of the servers
00:32:15.270 --> 00:32:15.960
and things like that,
00:32:16.260 --> 00:32:17.300
I'm probably saying too much.
00:32:17.640 --> 00:32:20.840
But yeah, actually, you know, like if I didn't have a CPI cloud,
00:32:20.980 --> 00:32:26.940
I will probably use one of the providers that abstract those things a little bit away,
00:32:27.060 --> 00:32:31.100
you know, like render, railway, fly, like, I don't know.
00:32:31.260 --> 00:32:36.280
Like, I don't really think that a regular developer should be dealing with,
00:32:36.280 --> 00:32:39.140
you know, like the big hyperscalers and like Kubernetes
00:32:39.360 --> 00:32:42.560
and like all that complexity for a common app.
00:32:42.760 --> 00:32:47.360
Most of the cases, I think it's just really too much complexity to real with.
00:32:47.440 --> 00:32:51.940
It's kind of eye-watering to open up the AWS console or Azure or something.
00:32:52.310 --> 00:32:52.440
Whoa.
00:32:52.980 --> 00:32:59.000
Oh, the other day, you know, like the other day I had to, in one of the AWS accounts, I had to change the account email.
00:32:59.500 --> 00:33:00.840
I think I spent four hours.
00:33:01.080 --> 00:33:01.420
I know.
00:33:01.640 --> 00:33:05.820
Because I had to create the delegate account that has the right permissions to roll.
00:33:05.960 --> 00:33:08.140
And they're like, oh, no, this is, you know, like,
00:33:08.920 --> 00:33:11.520
sometimes it's just overwhelming the amount of complexity
00:33:11.820 --> 00:33:12.920
that needs to be dealt with.
00:33:13.660 --> 00:33:16.280
And, yeah, I mean, it's great to really have, like,
00:33:16.440 --> 00:33:20.300
you know, like the infra people that I have working with me
00:33:20.300 --> 00:33:22.780
at the company that I can deal with all that mess
00:33:22.920 --> 00:33:26.020
and, like, can make sure that everything is just running perfectly
00:33:26.220 --> 00:33:27.280
and it just works.
00:33:27.440 --> 00:33:30.100
So it's like, you know, like, sort of SRE as a service,
00:33:30.520 --> 00:33:32.440
DevOps as a service for everyone.
00:33:32.860 --> 00:33:35.600
It's like a cloud product that provides DevOps as a service,
00:33:36.620 --> 00:33:41.700
I spent a number of years doing nothing but cloud migrations to these hyperscalers for enterprises.
00:33:42.420 --> 00:33:47.460
And I can tell you that when you mentioned the eye-watering comment about the network and all that stuff,
00:33:48.260 --> 00:33:49.760
it's so incredibly complicated now, right?
00:33:49.940 --> 00:33:55.000
There's literally every kind of concept that you need to know to deploy these enterprises now,
00:33:55.180 --> 00:33:56.700
move them from on-prem to the cloud.
00:33:56.900 --> 00:33:58.640
So it does get incredibly complicated.
00:33:58.770 --> 00:34:02.680
Having something simple like what Sebastian is talking about, I think, is super helpful
00:34:02.700 --> 00:34:04.380
when you're just trying to get started
00:34:04.380 --> 00:34:06.060
and get something up and running quickly.
00:34:06.240 --> 00:34:07.140
I've got a lot of questions
00:34:07.340 --> 00:34:09.899
and I realize that we will not be getting through all of them.
00:34:10.040 --> 00:34:12.440
So I want to pick carefully.
00:34:12.720 --> 00:34:14.840
So let's do this one next.
00:34:15.220 --> 00:34:18.820
Performance, what's your best low effort tip?
00:34:18.960 --> 00:34:20.899
Not like something super complicated,
00:34:21.360 --> 00:34:23.679
but I know there's a bunch of low hanging fruit
00:34:23.840 --> 00:34:25.540
that people maybe missed out on.
00:34:26.020 --> 00:34:27.760
And this time let's start with Litestar.
00:34:28.159 --> 00:34:29.240
Cody, back at you.
00:34:29.480 --> 00:34:30.760
I'm going to stick to what I know,
00:34:30.820 --> 00:34:32.520
which is databases because I deal with that.
00:34:32.580 --> 00:34:38.060
every single day. There's a couple of things that I see as like gotchas that I constantly see over
00:34:38.060 --> 00:34:45.240
and over. One, SQLAlchemy kind of obfuscates the way it's going to execute things and what kind of
00:34:45.379 --> 00:34:49.899
queries it's going to actually execute. So it's really easy if you're not kind of fluent in how
00:34:49.909 --> 00:34:55.360
it works to create N plus one types of issues. And so when people start talking about sync or async,
00:34:55.500 --> 00:34:59.280
it's really, in my mind, it's less of that because you're going to spend more time waiting on the
00:34:59.300 --> 00:35:04.200
network and database and those kind of things, then you're going to spend serializing just
00:35:04.500 --> 00:35:09.920
generally, right? And or processing things on the web framework. And so, one, making sure that you,
00:35:10.060 --> 00:35:15.340
your relationships dialed in correctly so that you don't have N plus one queries. The other thing is
00:35:15.560 --> 00:35:21.260
oversized connection pooling into Postgres and just databases in general, because what people don't
00:35:21.380 --> 00:35:26.240
tend to know is that each of those connections takes up CPU cycles and RAM of the database.
00:35:26.520 --> 00:35:33.360
And so when you slam the database with hundreds of connections, you're just taking away processing power that can be done for other things, right?
00:35:33.490 --> 00:35:35.740
And so you end up ultimately slowing things down.
00:35:35.830 --> 00:35:44.100
So I've seen databases that have had so many connections that all of the CPU and all the stuff is actually doing things, just managing connections and can't actually do any database work.
00:35:44.270 --> 00:35:45.460
And so what about this socket?
00:35:45.620 --> 00:35:46.180
Is it busy?
00:35:46.260 --> 00:35:46.800
What about this socket?
00:35:46.880 --> 00:35:47.260
Is it busy?
00:35:47.340 --> 00:35:48.580
It's just round robin that, right?
00:35:48.940 --> 00:35:51.980
Paying attention to the database is kind of my first kind of rule of thumb.
00:35:52.120 --> 00:35:52.480
100%.
00:35:52.610 --> 00:35:53.660
I like that one a lot.
00:35:53.700 --> 00:36:01.660
I'll throw in putting stuff or identifying work that doesn't need to be done immediately for the user and putting in a background task.
00:36:02.090 --> 00:36:04.700
Having a background worker defer things till later.
00:36:05.190 --> 00:36:10.060
So sending email is an example, although there's nuances there about knowing that it's sent and everything.
00:36:10.380 --> 00:36:20.460
But yeah, if you user kicks off some process and then you wait to do that process in the worker, you're holding that worker up, which is more relevant in WSGI than ASGI.
00:36:20.540 --> 00:36:26.300
but and you're making them wait for their page to load again versus record what they wanted to do
00:36:26.500 --> 00:36:30.680
send it off to the background let them see the status of it but let the background worker handle
00:36:30.700 --> 00:36:35.260
it all right yeah like i said as you guys go for it i'm not sure if that's some sort of
00:36:35.420 --> 00:36:41.380
it's not really a trick or a tip or more more like a i think the most common mistake i see when i
00:36:41.560 --> 00:36:46.200
is ASGI specific but when i look at ASGI apps that people have written who are maybe not as
00:36:46.220 --> 00:36:52.760
familiar with ASGI or async Python at all, if you make something an async function, you should be
00:36:53.260 --> 00:36:59.220
absolutely sure that it's non-blocking. Because if you're running an ASGI app and you're blocking
00:36:59.920 --> 00:37:04.660
anywhere, your whole application server is blocked completely. It doesn't handle any other requests
00:37:04.680 --> 00:37:11.839
at the same time. It's blocked. I don't think I've had any mistake more times when I've looked through
00:37:11.840 --> 00:37:18.820
some apps that someone has written or that i've came across somewhere so this is really it's super
00:37:18.980 --> 00:37:26.280
super common and it has such a such a big impact on the overall performance in every every metric
00:37:26.880 --> 00:37:33.200
imaginable so i would say unless and that's nowadays what i tell people unless you're 100
00:37:33.440 --> 00:37:39.280
sure that you know what you're doing and you know it's it's non-blocking don't make it async put it
00:37:39.120 --> 00:37:41.700
in a thread pool, execute it in a thread, whatever.
00:37:42.290 --> 00:37:44.580
All of the ASGI frameworks and Django
00:37:45.130 --> 00:37:48.840
give you a lot of tools at hand to translate your stuff
00:37:49.520 --> 00:37:52.080
to from sync to async so you can still run it.
00:37:52.240 --> 00:37:56.600
Do that unless you're very sure that it actually fully supports
00:37:57.080 --> 00:37:57.260
async.
00:37:57.290 --> 00:37:58.340
MARK MANDEL: Yeah, that's good advice.
00:37:58.700 --> 00:37:59.120
Sebastian.
00:37:59.650 --> 00:38:02.120
SEBASTIAN BASTIAN: Hey, I'm actually going to second, Yannick.
00:38:02.800 --> 00:38:04.620
I think, yeah, like it's--
00:38:04.960 --> 00:38:08.020
and it's maybe counterintuitive that one
00:38:08.040 --> 00:38:12.880
of the tips of performance is to try to not optimize that much performance at the beginning.
00:38:13.340 --> 00:38:17.740
You know, like, I think the idea with async is like, oh, you can get so much performance
00:38:17.980 --> 00:38:19.960
and throughput in terms of accuracy, whatever.
00:38:20.360 --> 00:38:26.720
But the thing is, in most of the cases, you know, like, till apps grow so large, they
00:38:26.900 --> 00:38:30.420
actually don't need that much extra throughput, that much extra performance.
00:38:30.920 --> 00:38:35.860
And in a framework like, you know, like, as Yannick was saying, well, in my case, I know
00:38:35.880 --> 00:38:38.080
FastAPI, but like, you know, like also many others.
00:38:38.520 --> 00:38:41.320
If you define the function with async, it's going to be run async.
00:38:41.400 --> 00:38:43.840
If you define it non-async and regular def,
00:38:44.140 --> 00:38:46.400
it's going to be run on a thread worker automatically.
00:38:46.960 --> 00:38:50.440
So it's just going to do the smart thing automatically.
00:38:50.700 --> 00:38:53.880
So it's like fair, you know, like it's going to be good enough.
00:38:54.340 --> 00:38:58.300
And then you can just start with that and just keep blocking code everywhere.
00:38:58.380 --> 00:39:01.340
You know, like just not use async until you actually know
00:39:01.480 --> 00:39:03.280
that you really need to use async.
00:39:03.900 --> 00:39:05.720
And once you do, you have to be, as Yannick was saying,
00:39:05.780 --> 00:39:10.900
you know, like 100% sure that you are not running blocking code inside of it.
00:39:11.220 --> 00:39:14.420
And if you need to run blocking code inside of Async code,
00:39:14.650 --> 00:39:17.360
then make sure that you are sending it to a thread worker.
00:39:17.680 --> 00:39:20.420
Sending it to a thread worker sounds the own thing,
00:39:20.620 --> 00:39:22.420
but yeah, like, you know, like Django has tools,
00:39:23.000 --> 00:39:23.720
any IO has tools.
00:39:23.960 --> 00:39:27.000
I also built something on top of any IO called asyncer,
00:39:27.200 --> 00:39:29.100
that is just to simplify these things,
00:39:29.300 --> 00:39:31.000
to asyncify a blocking function,
00:39:31.440 --> 00:39:33.160
keeping all the type information
00:39:33.360 --> 00:39:35.460
so that you get autocompletion and inline errors and everything.
00:39:35.860 --> 00:39:37.600
even though it's actually doing all the stuff
00:39:37.760 --> 00:39:40.320
of sending the thing to the thread work.
00:39:40.760 --> 00:39:41.980
So the code is super simple.
00:39:42.070 --> 00:39:43.380
You keep very simple code,
00:39:43.410 --> 00:39:44.940
but then underneath it's just like doing
00:39:45.340 --> 00:39:46.360
all the stuff that should be done.
00:39:46.700 --> 00:39:48.000
But you know, like that's normally
00:39:48.160 --> 00:39:50.720
when you actually need to hyper-optimize things.
00:39:51.040 --> 00:39:51.800
In most of the cases,
00:39:51.910 --> 00:39:54.940
you can just start with just not using async at first.
00:39:55.400 --> 00:39:57.800
Also, now that you're going to have Python multi-threaded,
00:39:58.120 --> 00:39:59.760
then suddenly you're going to have
00:40:00.000 --> 00:40:02.800
just so much more performance out of the blue
00:40:02.910 --> 00:40:04.840
without even having to do much more.
00:40:05.180 --> 00:40:12.220
So, yeah, actually that's, you know, like, sorry, I kept speaking so much, but here's a tip for improving performance.
00:40:12.860 --> 00:40:14.220
Upgrade your Python version.
00:40:14.910 --> 00:40:17.220
I was just chatting today with Savannah.
00:40:17.760 --> 00:40:28.440
She was adding the benchmarks to the, you know, like Python benchmark, the official Python benchmarks that they run for the CPython, the faster CPython program.
00:40:29.140 --> 00:40:35.540
And the change from Python 3.10 to Python 3.14
00:40:36.060 --> 00:40:39.800
when running FastAPI is like almost double the performance
00:40:40.020 --> 00:40:40.660
or something like that.
00:40:40.940 --> 00:40:42.060
It was like, it was crazy.
00:40:42.220 --> 00:40:44.640
It was just crazy improvement in performance.
00:40:44.840 --> 00:40:46.520
So you can just upgrade your Python version.
00:40:46.660 --> 00:40:50.320
You're gonna get so much better performance just out of that.
00:40:50.400 --> 00:40:52.000
- Yeah, that's an awesome piece of advice
00:40:52.140 --> 00:40:53.540
that I think is often overlooked.
00:40:53.980 --> 00:40:55.480
And it's not only CPU speed,
00:40:55.680 --> 00:40:57.180
it's also memory gets a lot lower.
00:40:57.600 --> 00:40:58.420
Whoever's gonna jump in, go ahead.
00:40:58.580 --> 00:41:03.840
Last year, I was looking at MarkupSafe, which is an HTML escaping library that we use and
00:41:04.230 --> 00:41:05.700
has a C extension for speedups.
00:41:05.770 --> 00:41:11.460
And I almost convinced myself that I can stop maintaining the C extension because just Python
00:41:11.720 --> 00:41:12.780
itself got way faster.
00:41:13.050 --> 00:41:16.700
But then it turned out that I could do something to the C extension to make it faster also.
00:41:17.110 --> 00:41:17.780
So I'm still maintaining.
00:41:18.100 --> 00:41:23.140
But just the fact that I almost convinced myself like, oh, I can drop a C extension for just
00:41:23.140 --> 00:41:26.160
a Python upgrade instead was pretty impressive.
00:41:26.740 --> 00:41:29.320
They've done a lot, especially with like string handling
00:41:29.400 --> 00:41:30.840
and, you know, which you're going to use
00:41:30.920 --> 00:41:32.360
for templating for web apps.
00:41:32.660 --> 00:41:32.780
Phil.
00:41:32.920 --> 00:41:36.020
Yeah, well, I definitely echo looking at your DB queries
00:41:36.300 --> 00:41:38.260
because by far and large, that's always where
00:41:38.640 --> 00:41:39.720
our performance issues have been.
00:41:39.800 --> 00:41:41.920
It's either badly written query or we're returning
00:41:42.180 --> 00:41:43.920
most of the database when the user just wants to know
00:41:44.020 --> 00:41:45.800
about one thing or something silly like that.
00:41:46.240 --> 00:41:47.520
I was thinking about low-hanging ones,
00:41:47.620 --> 00:41:48.440
which I think you asked about.
00:41:48.740 --> 00:41:52.560
So I'd say uvloop, which is still a noticeable improvement.
00:41:53.740 --> 00:41:58.720
And also, because I think it's likely a lot of us are returning JSON, often changing the
00:41:59.100 --> 00:42:04.080
JSON serializer to one of the faster ones can be noticeable as well and obviously quite easy to do.
00:42:04.330 --> 00:42:05.640
So yeah, that's my key.
00:42:05.760 --> 00:42:06.460
That's really good advice.
00:42:06.570 --> 00:42:08.360
I didn't think about the JSON serializer.
00:42:08.820 --> 00:42:09.560
What one do you recommend?
00:42:09.900 --> 00:42:11.040
I think, is it ujson?
00:42:11.420 --> 00:42:12.280
Or is it orjson?
00:42:12.860 --> 00:42:14.200
I can't remember which one was deprecated.
00:42:15.880 --> 00:42:21.180
But yeah, if you look at the TechEmpower benchmarks, everyone's changing the JSON serializer
00:42:21.310 --> 00:42:22.540
to get that bit extra speed.
00:42:22.860 --> 00:42:27.440
But yeah, you're like, our framework looks bad because our JSON serializer is like third
00:42:27.470 --> 00:42:28.020
of the performance.
00:42:28.270 --> 00:42:31.680
We changed, well, David added a JSON provider to Flask.
00:42:31.880 --> 00:42:35.120
And yeah, you could see it make a difference in the tech and power benchmarks.
00:42:35.400 --> 00:42:35.960
So that was really good.
00:42:36.060 --> 00:42:36.360
Yeah, cool.
00:42:36.540 --> 00:42:37.600
Yeah, it's pluggable now.
00:42:37.860 --> 00:42:42.880
But if you're not installing Flask or orjson, I mean, I don't know what other JSON library
00:42:43.000 --> 00:42:44.780
you'd be using at this point, unless you're already using one.
00:42:45.140 --> 00:42:47.240
But orjson is very, very fast.
00:42:47.520 --> 00:42:49.200
Okay, this is something I'm going to be looking at you later.
00:42:49.660 --> 00:42:57.440
So over to Django, Jeff, David talked about running stuff in the background and was it Django 5 or Django 6 that got the background task thing?
00:42:57.640 --> 00:42:59.640
Yeah, Django 6 just came out a couple of weeks ago.
00:43:00.200 --> 00:43:07.220
And I'll hand that off to Carlton in a second because I think Carlton's had more to do with the actual plumbing being on the steering council.
00:43:07.620 --> 00:43:11.860
My advice to people is the best way to scale something is just to not do it, avoid the process completely.
00:43:12.040 --> 00:43:16.000
So like I mentioned to CDN earlier, it's content heavy sites, cache the crap out of stuff.
00:43:16.500 --> 00:43:17.540
It doesn't even have to hit your servers.
00:43:17.960 --> 00:43:22.020
You can go a lot, as we mentioned earlier, too, just by doubling the amount of resources a project has.
00:43:22.400 --> 00:43:24.840
Django is pretty efficient these days, especially with async views.
00:43:25.290 --> 00:43:31.500
Like everybody else has said, too, any blocking code, move off to threads, move off to a background queue.
00:43:31.820 --> 00:43:35.140
Django Q2 is my favorite one to use because you can use a database.
00:43:35.780 --> 00:43:39.580
So for those little side projects where you just want to run one or two processes, you can use it.
00:43:39.580 --> 00:43:40.040
It works great.
00:43:40.580 --> 00:43:43.160
And Carlton, if you want to talk about Django internals.
00:43:43.260 --> 00:43:43.740
Yeah, OK.
00:43:43.900 --> 00:43:51.120
So the new task framework I just mentioned, the main thing, the main sort of bit about it is that it's, again, this pluggable Django API.
00:43:51.360 --> 00:43:53.080
So it gives a standard task API.
00:43:53.220 --> 00:43:57.120
So if you're writing a third-party library and you, I know, you need to send an email.
00:43:57.540 --> 00:43:58.820
It's the canonical example, right?
00:43:58.820 --> 00:44:00.880
You need to send an email in your third-party library.
00:44:01.320 --> 00:44:07.780
Before, you'd have had to tie yourself to a specific queue implementation, whereas now Django is providing a kind of like an ORM of tasks.
00:44:07.820 --> 00:44:08.180
Right, right.
00:44:08.240 --> 00:44:11.780
You got to do Redis, you got to do Celery, and you got to manage things and all that.
00:44:11.840 --> 00:44:14.820
You don't have to pick that now as the third-party package author.
00:44:14.900 --> 00:44:16.420
You can just say, right, just use Django,
00:44:17.080 --> 00:44:18.640
wrap this as a Django task and queue it.
00:44:18.940 --> 00:44:22.780
And then the developer, when they come to choose their backend,
00:44:22.880 --> 00:44:24.940
if they want to use Celery or they want to use Django Q2
00:44:25.060 --> 00:44:26.720
or they want to use the Django task backend,
00:44:26.900 --> 00:44:30.440
which Jake Howard, who wrote this for Django provided as well,
00:44:30.800 --> 00:44:32.040
you can just plug that in.
00:44:32.300 --> 00:44:34.700
So it's a pluggable interface for tasks,
00:44:34.980 --> 00:44:37.240
which is, I think, the really nice thing about it.
00:44:37.600 --> 00:44:40.700
In terms of quick wins, everybody's mentioned almost all of mine.
00:44:40.840 --> 00:44:43.420
I'm going to, Cody and Phil, they mentioned the database.
00:44:43.920 --> 00:44:44.600
That's the big one.
00:44:44.800 --> 00:44:48.280
Django, the ORM, because it does lazy related lookups,
00:44:48.420 --> 00:44:51.180
it's very easy to trigger in M plus one where, you know,
00:44:51.860 --> 00:44:55.000
the book has multiple authors and suddenly you're iterating through the books
00:44:55.160 --> 00:44:57.120
and you're iterating through the authors and it's a lookup.
00:44:57.640 --> 00:45:00.360
So you need to do things like prefetch related, select related.
00:45:00.470 --> 00:45:02.060
You need to just check that you've got those.
00:45:02.240 --> 00:45:04.880
Django debug toolbars are a great thing to run in development
00:45:05.040 --> 00:45:07.700
where you can see the queries and it'll tell you where you've got the duplicates.
00:45:08.280 --> 00:45:11.100
And then the slightly bigger one is to just check your indexes.
00:45:11.350 --> 00:45:14.300
The ORM will create the right indexes if you're leaning,
00:45:14.570 --> 00:45:16.520
if you're going through primary keys or unique fields.
00:45:16.590 --> 00:45:19.220
But sometimes you're doing a filter on some field,
00:45:19.800 --> 00:45:21.460
and then there's not the right index there,
00:45:21.460 --> 00:45:22.540
and that can really slow you down.
00:45:22.590 --> 00:45:26.460
So again, you can do the SQL explain on that and find that.
00:45:26.860 --> 00:45:29.820
And then the thing I was going to say originally was caching,
00:45:30.160 --> 00:45:34.480
is get a Redis instance, stick it next to your Django app,
00:45:34.530 --> 00:45:36.620
and as Jeff said, don't do the work.
00:45:36.740 --> 00:45:40.000
If you're continually rendering the same page and it never changes,
00:45:40.620 --> 00:45:42.980
cache it and pull it from the cache rather than rendering.
00:45:43.120 --> 00:45:45.460
Because template DB queries are one of your biggest things.
00:45:45.560 --> 00:45:47.500
The second one's always going to be serialization.
00:45:47.620 --> 00:45:49.300
It's either serialization or template rendering.
00:45:49.620 --> 00:45:53.760
So if you can avoid that by caching, you can save an awful lot of time on your account.
00:45:53.780 --> 00:45:53.900
Yeah.
00:45:54.360 --> 00:45:56.740
I was wondering if somebody would come back with database indexes,
00:45:56.900 --> 00:46:00.900
because that's like a 100x multiplier for free almost.
00:46:01.200 --> 00:46:03.040
It's such a big deal.
00:46:03.160 --> 00:46:03.860
It really can be.
00:46:03.900 --> 00:46:06.700
If you're making a particular query and it's doing a full table scan
00:46:06.720 --> 00:46:11.400
all of a sudden you put the index in, it's instant. It's like, oh, wow. You don't have to be a DBA or
00:46:12.619 --> 00:46:16.400
master information architect sort of thing. I don't know about Postgres. I'm sure it has it.
00:46:16.540 --> 00:46:21.720
Somebody can tell me. But with Mongo, you can turn on in the database, I want you to log all
00:46:21.940 --> 00:46:27.060
slow queries and slow for me means 20 millisecond or whatever. Like you put a number in and then
00:46:27.660 --> 00:46:31.620
you run your app for a while and you go, look at what's slow by slowest. And then you can see,
00:46:31.640 --> 00:46:35.220
well, maybe that needs an index, right? Like just let your app tell you what you got to do.
00:46:35.500 --> 00:46:36.840
Yeah, there is a post.
00:46:37.040 --> 00:46:38.740
I'm just trying to see if I can quickly look it up now.
00:46:38.860 --> 00:46:40.060
There's a Postgres extension,
00:46:40.390 --> 00:46:42.000
which will automatically run explain
00:46:42.680 --> 00:46:44.540
on the slow queries and log them for you.
00:46:44.680 --> 00:46:45.360
So it'll...
00:46:45.400 --> 00:46:45.660
There you go.
00:46:46.400 --> 00:46:47.080
See if I can find...
00:46:47.080 --> 00:46:48.240
It's pg_stat_statements, I think,
00:46:48.240 --> 00:46:49.020
is what you're thinking about.
00:46:49.160 --> 00:46:49.500
Right, okay.
00:46:49.700 --> 00:46:52.280
If you're unsure about your database indexes,
00:46:52.620 --> 00:46:55.340
do this, or at least go back and review your queries.
00:46:55.640 --> 00:46:56.240
Yeah, I agree.
00:46:56.590 --> 00:46:56.780
Very good.
00:46:57.240 --> 00:46:59.220
All right, I can see we're blazing through these questions.
00:47:00.060 --> 00:47:00.820
I had one more.
00:47:01.120 --> 00:47:01.960
If I can mention one.
00:47:01.960 --> 00:47:02.540
No, please go ahead.
00:47:02.730 --> 00:47:03.260
Yeah, go ahead, David.
00:47:03.420 --> 00:47:27.740
If you want to like get some more responsive parts of your website, like make your website a little more responsive or interactive with the user, HTMX or Datastar, especially like if you're using Quart or another ASGI where you can do SSE, server sent events or WebSockets, streaming little bits of changes to the web front end and then rendering them with the same HTML you're already writing can make things a lot more responsive.
00:47:28.240 --> 00:47:33.240
We had talk about that from Chris May at FlaskCon last year, which you can find on YouTube.
00:47:33.540 --> 00:47:38.280
This is not one of the questions, but let me just start out for a quick riff on this, folks.
00:47:38.980 --> 00:47:41.380
Out in the audience, someone was asking, what about HTMX?
00:47:41.800 --> 00:47:47.860
And I think more broadly, I am actually a huge fan of server-side-based, template-based apps.
00:47:48.060 --> 00:47:51.380
I think it just keeps things simpler in a lot of ways, unless you need a lot of interactivity.
00:47:51.740 --> 00:47:57.520
But things like HTMX or a little bit of JavaScript can reduce a lot of the traffic and stuff.
00:47:57.580 --> 00:47:59.160
Where do people land on those kinds of things?
00:47:59.360 --> 00:48:05.760
I absolutely love HTMX, not just because you don't have to write a lot of JavaScript or whatever,
00:48:06.300 --> 00:48:14.040
but mostly because if I'm just building a simple app that needs a bit more than just be a static HTML page,
00:48:14.040 --> 00:48:17.840
it needs some interactivity, a little bit of reactivity.
00:48:18.380 --> 00:48:34.160
I feel like having the whole overhead of building an SPA or whatever tools you need for the whole JavaScript, TypeScript, whatever stack, it's just so much work to get a little bit to make a simple thing a little bit nicer, a little bit more reactive.
00:48:34.740 --> 00:48:37.540
And I feel like HTMX just fits right in there.
00:48:37.700 --> 00:48:39.420
It's super great.
00:48:39.520 --> 00:48:41.340
I've built a couple of things with it now,
00:48:41.960 --> 00:48:44.760
a few of my own projects, a few things that work.
00:48:45.280 --> 00:48:47.460
And it makes things so much easier
00:48:47.720 --> 00:48:50.480
where the work probably wouldn't have been done
00:48:50.570 --> 00:48:52.320
if it was just because it's too much.
00:48:52.420 --> 00:48:55.000
If you're doing a whole front end thing
00:48:55.140 --> 00:48:57.120
that you have then to deploy and build and whatever,
00:48:57.520 --> 00:48:59.180
or it would have been less nice.
00:48:59.640 --> 00:49:02.420
So it's an amazing, really amazing thing.
00:49:02.520 --> 00:49:10.460
As the maintainer and author, though, one of the things that is not frustrating, but it's understandable is that HTMX is not for everybody, right?
00:49:10.660 --> 00:49:15.020
There's not like you can't use HTMX in all occasions or Datastar, right?
00:49:15.090 --> 00:49:20.180
And so there are people that are always going to want to use React and there's going to be people that want to use all these other frameworks.
00:49:20.290 --> 00:49:24.120
And so having some cohesive way to make them all talk together, I think, is important.
00:49:24.520 --> 00:49:29.800
I don't have that answer yet, but I just know that like I can't always say HTMX is it, right?
00:49:29.940 --> 00:49:33.840
And then you'll have a great time because I'll inevitably meet somebody that says I need to do this.
00:49:33.910 --> 00:49:37.900
And it's right. And it's in a single page application or something is more appropriate for that.
00:49:37.950 --> 00:49:41.020
And so it's obviously the right tool for the right job when you need it.
00:49:41.100 --> 00:49:45.560
But, you know, I want to make something that is cohesive depending on whatever library you want to use.
00:49:45.730 --> 00:49:46.960
I would throw one thing in there, though.
00:49:47.080 --> 00:49:51.160
I would rather somebody start with HTMX than I would start with React if you don't need it.
00:49:51.320 --> 00:49:54.140
Because React can be total overkill. It can be great for some applications.
00:49:54.720 --> 00:49:59.040
But oftentimes the consultant, we see people like having an about page and they throw a React at it.
00:49:59.160 --> 00:49:59.940
Like, why do you need that?
00:50:00.340 --> 00:50:02.040
Like, especially for small things with partials.
00:50:02.240 --> 00:50:03.500
Do you mean you don't want to start with Angular?
00:50:03.780 --> 00:50:05.800
You know, it's fine if you need it,
00:50:06.080 --> 00:50:07.740
but I don't think you really need it.
00:50:07.920 --> 00:50:09.680
Like, introduce tools as you need them.
00:50:10.000 --> 00:50:11.840
Django 6.0 just added template partials,
00:50:12.010 --> 00:50:13.940
and I guess my job here is to hand off to Carlton
00:50:14.080 --> 00:50:15.160
because this is his feature.
00:50:15.320 --> 00:50:16.900
Yeah, I was happy to see that come in there, Carlton.
00:50:17.020 --> 00:50:17.540
Nice job.
00:50:17.860 --> 00:50:18.620
No, it's okay.
00:50:19.230 --> 00:50:19.920
Plug the new feature.
00:50:20.100 --> 00:50:23.180
So, I mean, I stepped down as a fellow in 2023
00:50:24.550 --> 00:50:25.160
into a new business,
00:50:25.560 --> 00:50:28.299
and I read the essay about template fragments
00:50:28.320 --> 00:50:34.180
on the htmx website where it's this like named reusable bits in the in the templates and i was
00:50:34.200 --> 00:50:38.600
like i need that so i built django template partials released as a third-party package and it's now just
00:50:38.630 --> 00:50:44.460
been merged into core for django 6.0 and i have to say about htms it's really changed the way i write
00:50:44.580 --> 00:50:49.020
websites before i was the fellow i used to write mobile applications and do the back do the front
00:50:49.120 --> 00:50:52.600
end of the mobile application then the back end in django using django rest framework and i was
00:50:52.940 --> 00:50:58.280
that's how i got into you know open source was by django rest framework and since starting the
00:50:58.300 --> 00:51:02.780
we're three years in we've hardly got a json endpoint in sight it's like two three four of
00:51:02.780 --> 00:51:08.000
them in the whole application and it's oh it's just a delight again you know you asked me at the
00:51:08.140 --> 00:51:12.440
beginning michael am i having fun yeah i really am having fun and htmx is the reason i do grant
00:51:12.590 --> 00:51:18.400
there are you know these use cases awesome all right let's talk about our last topic and we have
00:51:18.740 --> 00:51:24.300
five minutes ish to do that so we gotta we gotta stay on target quick but let's just go around
00:51:24.540 --> 00:51:29.780
run real quick here we talked about upgrading the python version getting better performance out of
00:51:29.900 --> 00:51:36.660
it i mentioned the lower memory side but i think one of the underappreciated aspects of this you
00:51:36.660 --> 00:51:43.360
know the instagram team did a huge talk you know on it a while ago is the memory that you run into
00:51:43.560 --> 00:51:47.380
when you start to scale out your stuff on the server because you're like oh i want to have four
00:51:47.580 --> 00:51:51.520
workers so i can have more concurrency because of the gills so now you've got four copies of
00:51:51.540 --> 00:51:56.500
everything that you cache in memory and just like the runtime and now you need eight gigs instead of
00:51:56.500 --> 00:52:02.380
what would have been one or who knows right but with free threaded python coming on which i've
00:52:02.380 --> 00:52:07.920
seen a couple of comments in the chat like hey tell us about this like it's we could have true
00:52:08.380 --> 00:52:13.420
concurrency and we wouldn't need to scale as much on the process side i think giving us both better
00:52:13.600 --> 00:52:17.480
performance and the ability to say well you actually have four times less memory so you could
00:52:17.440 --> 00:52:22.660
run smaller servers or whatever what's the free threaded story for all the frameworks carlton
00:52:22.780 --> 00:52:26.860
let's go back to you for do it in reverse i'm really excited about it i don't know how it's
00:52:26.860 --> 00:52:30.940
going to play out but i'm really excited about it all it can do is help django the async story in
00:52:31.040 --> 00:52:36.460
django is nice and mature now uh but still most of it's sync like you know you're still going to
00:52:36.560 --> 00:52:39.960
default the sync you're still going to write your sync views you still got template rendering you
00:52:39.960 --> 00:52:43.880
know django's template template based kind of framework really you're still going to want to
00:52:43.880 --> 00:52:50.180
run things synchronously, concurrently, and proper threads are going to be, yeah, they can't but help.
00:52:50.270 --> 00:52:53.920
I don't know how it's going to roll out. I'll let someone else go because I'm getting locked up.
00:52:53.920 --> 00:52:59.520
Yeah, I just like elaborated that for people out there before we move on is you could set up your
00:52:59.720 --> 00:53:05.100
worker process to say, I want you to actually run eight threads in this one worker process.
00:53:05.760 --> 00:53:10.600
And when multiple requests come in, they could both be sent off to the same worker to be processed.
00:53:10.740 --> 00:53:15.240
And that allows that worker to do more unless the GIL comes along and says, stop, you only
00:53:15.380 --> 00:53:17.060
get to do one thing in threads in Python.
00:53:17.640 --> 00:53:18.960
And all of a sudden, a lot of that falls down.
00:53:19.100 --> 00:53:22.080
This basically uncorks that and makes that easy all of a sudden.
00:53:22.210 --> 00:53:26.120
Even if you yourself are not writing async, your server can be more async.
00:53:26.220 --> 00:53:26.420
Yeah.
00:53:26.450 --> 00:53:31.220
And this is the thing that we found with ASCII is that you dispatch to a, you know, using
00:53:31.420 --> 00:53:36.220
to async or you dispatch it to a thread, a thread pool executor, but Python doesn't run
00:53:36.380 --> 00:53:37.180
that concurrently.
00:53:37.320 --> 00:53:38.880
And so it's like, or in parallel.
00:53:39.100 --> 00:53:42.340
So it's like, ah, it doesn't actually go as fast as you want it to.
00:53:42.580 --> 00:53:44.880
And so you end up wanting multiple processes still.
00:53:45.080 --> 00:53:45.800
All right, let's keep it with Django.
00:53:46.040 --> 00:53:46.680
Jeff, what do you think?
00:53:47.160 --> 00:53:48.520
I'm going to defer to the others on this.
00:53:48.580 --> 00:53:49.480
I have the least thoughts.
00:53:49.760 --> 00:53:51.120
All right, write down the stack, Sebastian.
00:53:51.620 --> 00:53:54.540
Write down the video, not website, web framework.
00:53:55.760 --> 00:53:56.700
I think it's going to be awesome.
00:53:56.880 --> 00:53:58.940
This is going to help so much, so many things.
00:53:59.560 --> 00:54:04.780
The challenge is going to be third-party libraries used by each individual application
00:54:05.140 --> 00:54:06.740
and if they are compatible or not.
00:54:07.020 --> 00:54:08.440
That's where the challenge is going to be.
00:54:08.580 --> 00:54:12.760
But other than that, it's just going to be free extra performance for everyone.
00:54:13.160 --> 00:54:15.540
Just, you know, like just upgrading the version of Python.
00:54:15.720 --> 00:54:16.420
So that's going to be us.
00:54:16.480 --> 00:54:16.620
Cody.
00:54:16.860 --> 00:54:18.400
Yeah, I'm going to echo what Sebastian just said.
00:54:18.460 --> 00:54:21.520
The third party libraries, I think, are going to be the big kind of sticky point here.
00:54:21.720 --> 00:54:23.120
I'm looking forward to seeing what we can do.
00:54:23.280 --> 00:54:24.880
I'm going to kind of hold my thoughts on that.
00:54:24.960 --> 00:54:28.660
Yannick kind of speak a little bit on it because I know that he's looked at msgspec specifically
00:54:28.920 --> 00:54:31.720
and some of the other things that might, you know, give some better context here.
00:54:31.880 --> 00:54:35.880
But yes, the third party libraries are going to be the kind of the sticky issue.
00:54:36.160 --> 00:54:38.720
but I'm looking forward to seeing what we can make happen.
00:54:38.880 --> 00:54:43.200
I'm super excited, actually, specifically about async stuff,
00:54:43.440 --> 00:54:45.580
because for most of the time, it's like,
00:54:46.600 --> 00:54:49.100
if you can already saturate your CPU,
00:54:50.260 --> 00:54:51.240
async doesn't help you much.
00:54:51.760 --> 00:54:53.500
Well, now, if you have proper threads,
00:54:53.560 --> 00:54:56.180
you can actually do that in async as well.
00:54:56.840 --> 00:54:59.880
And I think it's going to speed up a lot of applications
00:55:00.260 --> 00:55:01.140
just by default,
00:55:01.520 --> 00:55:06.120
because almost all async applications out there
00:55:06.260 --> 00:55:11.500
use threads in some capacity because, well, most of things aren't async by nature.
00:55:12.160 --> 00:55:16.300
So they will use a thread pool and it will run more concurrently.
00:55:16.300 --> 00:55:18.360
And so that's going to be better.
00:55:18.840 --> 00:55:25.820
But I'm also a bit scared about a few things that mainly, as a few others have said now,
00:55:26.500 --> 00:55:32.400
third-party libraries extension is specifically those that are Python C extensions.
00:55:33.320 --> 00:55:35.640
just recently, I think like three weeks ago,
00:55:36.140 --> 00:55:40.200
so got a msgspec released for Python 3.14
00:55:40.410 --> 00:55:41.960
and proper free threading support.
00:55:42.620 --> 00:55:44.000
And that took a lot of work.
00:55:44.540 --> 00:55:47.760
Fortunately, a few of the Python core devs
00:55:47.800 --> 00:55:49.840
chimed in and contributed to PRs
00:55:49.920 --> 00:55:51.580
and helped out with that.
00:55:52.300 --> 00:55:54.380
And all around the ecosystem,
00:55:54.490 --> 00:55:55.240
the last few years,
00:55:55.340 --> 00:55:56.560
there's been a lot of work going on.
00:55:57.020 --> 00:56:00.720
But especially for more niche libraries
00:56:01.000 --> 00:56:02.660
that are still here and there,
00:56:02.880 --> 00:56:09.720
I think there's still a lot to do and possibly also quite a few bugs lurking here and there
00:56:09.860 --> 00:56:13.020
that haven't been found or are really hard to track down.
00:56:13.560 --> 00:56:18.380
I'm curious and a bit maybe scarce, too hard of work, but I'm cautious.
00:56:18.820 --> 00:56:21.880
It's going to be a little bit of a bumpy ride as people turn that on
00:56:22.080 --> 00:56:23.840
and then the reality of what's happening.
00:56:24.420 --> 00:56:27.920
However, I want to take Cody's warning and turn it on its head
00:56:28.300 --> 00:56:29.420
about these third-party libraries,
00:56:29.760 --> 00:56:38.820
Because I think it's also an opportunity for regular Python developers who are not async fanatics to actually capture some of that capability.
00:56:39.400 --> 00:56:47.880
Say if some library says, hey, I realize that if we actually implement this lower level thing, you don't actually see the implementation of in true threading.
00:56:48.220 --> 00:56:50.160
And then you use it, but you don't actually do threading.
00:56:50.160 --> 00:56:52.400
You just call even a blocking function.
00:56:52.880 --> 00:56:57.320
You might get a huge performance boost, a little bit like David was talking about with markup safe.
00:56:57.840 --> 00:57:00.040
And you could just all of a sudden, with doing nothing
00:57:00.600 --> 00:57:03.980
with your code, goes five times faster on an eight core
00:57:04.200 --> 00:57:06.680
machine or something in little places where it used to matter.
00:57:06.840 --> 00:57:08.960
I'm super excited for--
00:57:09.410 --> 00:57:12.720
we currently focused on the things that are out there
00:57:12.900 --> 00:57:15.180
right now and that might need to be updated.
00:57:15.270 --> 00:57:19.260
But I'm super excited for what else might come of this,
00:57:19.620 --> 00:57:23.520
new things that will be developed or stuff
00:57:23.530 --> 00:57:25.940
that we are currently not thinking about
00:57:25.960 --> 00:57:29.620
or have that hadn't been considered for the past 30 years or so,
00:57:29.720 --> 00:57:33.240
because it just wasn't feasible or wasn't possible or didn't make sense at all.
00:57:33.720 --> 00:57:35.720
I think it would pay off definitely.
00:57:36.100 --> 00:57:36.940
All right. Team Flask.
00:57:37.420 --> 00:57:39.180
You guys got the final word.
00:57:39.440 --> 00:57:42.320
I think it would probably be more advantageous to WSGI apps
00:57:42.520 --> 00:57:43.900
than it will for ASGI apps.
00:57:44.140 --> 00:57:47.180
And when I've been playing with it, it's mostly on the WSGI Flask side
00:57:47.280 --> 00:57:48.680
where I'm quite excited about it.
00:57:48.920 --> 00:57:52.220
At the same time, like the others are a bit worried because not clear to me,
00:57:52.380 --> 00:57:54.980
for example, that green threading is going to work that well
00:57:55.000 --> 00:57:56.040
with free threading.
00:57:56.400 --> 00:57:57.340
And that may have been fixed,
00:57:57.380 --> 00:57:58.520
but I don't think it has yet.
00:57:58.740 --> 00:58:01.480
And that might then break a lot of WSGI apps.
00:58:02.080 --> 00:58:03.240
So next, I think.
00:58:03.300 --> 00:58:05.760
But yeah, very excited for Flask in particular.
00:58:06.000 --> 00:58:07.260
Thanks for bringing up green threading.
00:58:07.260 --> 00:58:08.460
I added that to my notes
00:58:09.600 --> 00:58:11.020
of mention right now.
00:58:11.300 --> 00:58:14.360
So Flask already has emphasized
00:58:14.800 --> 00:58:15.980
for years and years and years
00:58:16.340 --> 00:58:17.840
that don't store stuff globally,
00:58:18.260 --> 00:58:19.020
don't have global state,
00:58:19.360 --> 00:58:21.220
bind stuff to the request response cycle
00:58:21.420 --> 00:58:22.880
if you need to store stuff,
00:58:23.100 --> 00:58:24.540
look stuff up from a cache otherwise.
00:58:24.600 --> 00:58:27.320
And my impression is that that emphasis is pretty successful.
00:58:27.330 --> 00:58:31.560
I don't, there's any well-known extensions using like global state or anything like that.
00:58:31.960 --> 00:58:35.580
It's helped that the dev server that we have is threaded by default.
00:58:36.160 --> 00:58:39.340
Like it's not going for performance, obviously, it's just running on your local machine, but
00:58:39.580 --> 00:58:42.680
it's already like running in a threaded environment, running your application in a
00:58:42.800 --> 00:58:45.160
threaded environment, not a process-based one by default.
00:58:45.740 --> 00:58:49.360
I don't know if anybody even knows that you can run the dev server as process-based.
00:58:49.720 --> 00:58:53.460
And we also already had for a decade or more than a decade,
00:58:53.960 --> 00:58:59.380
Gevent to enable the exact same thing that free threading is enabling for Flask,
00:58:59.520 --> 00:59:02.700
which is concurrent work and connections.
00:59:03.740 --> 00:59:06.600
And so plenty of applications are already deployed that way
00:59:06.880 --> 00:59:09.800
using Gevent to do what kind of ASGI is enabling.
00:59:10.400 --> 00:59:14.240
I've run all the test suites with pytest FreeThreaded,
00:59:14.560 --> 00:59:18.460
which checks that your tests can run concurrently in the free threaded builds.
00:59:18.920 --> 00:59:20.580
so go check that out by Anthony Shaw.
00:59:20.980 --> 00:59:23.160
And I'm pretty sure Granian already supports free-threading.
00:59:23.620 --> 00:59:25.400
Not sure though, I haven't looked into Granian enough.
00:59:25.480 --> 00:59:26.040
But like-
00:59:26.040 --> 00:59:27.040
You know, I'm not sure either.
00:59:27.040 --> 00:59:30.280
It does have a runtime threaded mode
00:59:30.280 --> 00:59:32.440
but I don't know if that's truly free-threaded or not.
00:59:32.440 --> 00:59:35.820
All of those things combined make me pretty optimistic
00:59:35.820 --> 00:59:38.840
that Flask will be able to take advantage of this
00:59:38.840 --> 00:59:40.540
without much work from us.
00:59:40.540 --> 00:59:42.240
I mean, I know that's a big statement right there
00:59:42.240 --> 00:59:43.480
and I haven't tested it
00:59:43.480 --> 00:59:45.160
but the fact that we've emphasized
00:59:45.160 --> 00:59:47.540
all these different parts for so long already
00:59:47.540 --> 00:59:48.340
makes me confident about it.
00:59:48.460 --> 00:59:49.660
I'm also super excited about it.
00:59:49.660 --> 00:59:51.680
And just one final thought I'll throw out there
00:59:51.790 --> 00:59:52.720
before we call it a show,
00:59:52.920 --> 00:59:54.640
because we could go on for much longer,
00:59:54.690 --> 00:59:55.260
but we're out of time.
00:59:55.580 --> 00:59:57.380
I think once this comes along,
00:59:57.860 --> 01:00:00.700
whatever framework out of this choice you're using out there,
01:00:01.080 --> 01:00:03.420
there's a bunch of inner working pieces.
01:00:04.400 --> 01:00:05.940
One of them may have some kind of issue.
01:00:06.130 --> 01:00:08.340
And I think it's worth doing some proper load testing
01:00:08.830 --> 01:00:12.040
on your app, you know, point something like locust.io at it
01:00:12.200 --> 01:00:14.900
and just say, well, what if we gave it 10,000 concurrent users
01:00:14.970 --> 01:00:15.360
for an hour?
01:00:15.390 --> 01:00:16.120
Does it stop working?
01:00:16.600 --> 01:00:17.160
Does it crash?
01:00:17.290 --> 01:00:18.140
Or does it just keep going?
01:00:18.360 --> 01:00:21.960
You're like, so that seems like a pretty good thing to do the first time before you deploy
01:00:22.100 --> 01:00:23.320
your first free threaded version.
01:00:23.840 --> 01:00:23.900
Yeah.
01:00:24.220 --> 01:00:24.620
All right, everyone.
01:00:25.440 --> 01:00:26.480
I would love to talk somewhere.
01:00:26.600 --> 01:00:30.020
This is such a good conversation, but I also want to respect your time and all that.
01:00:31.140 --> 01:00:32.220
So thank you for being here.
01:00:32.660 --> 01:00:35.320
It's been an honor to get you all together and have this conversation.
01:00:35.580 --> 01:00:37.000
Thank you very much for having us.
01:00:37.140 --> 01:00:37.400
Thank you.
01:00:37.640 --> 01:00:37.740
Yeah.
01:00:37.900 --> 01:00:38.600
Thanks for having us all.
01:00:38.760 --> 01:00:39.060
Thanks, everybody.
01:00:39.400 --> 01:00:39.480
Yeah.
01:00:39.700 --> 01:00:40.360
It's nice being here.
01:00:40.440 --> 01:00:40.580
Yeah.
01:00:40.700 --> 01:00:41.240
Thanks for having us.
01:00:41.480 --> 01:00:42.240
Thanks for having us all.
01:00:42.380 --> 01:00:42.540
Bye.
01:00:42.700 --> 01:00:43.060
Bye-bye.
01:00:44.380 --> 01:00:46.720
This has been another episode of Talk Python To Me.
01:00:47.020 --> 01:00:47.820
Thank you to our sponsors.
01:00:48.120 --> 01:00:49.320
Be sure to check out what they're offering.
01:00:49.510 --> 01:00:50.880
It really helps support the show.
01:00:51.300 --> 01:00:53.160
If you or your team needs to learn Python,
01:00:53.390 --> 01:00:56.780
we have over 270 hours of beginner and advanced courses
01:00:57.100 --> 01:01:00.520
on topics ranging from complete beginners to async code,
01:01:00.660 --> 01:01:03.440
Flask, Django, HTMX, and even LLMs.
01:01:03.660 --> 01:01:05.940
Best of all, there's no subscription in sight.
01:01:06.480 --> 01:01:08.260
Browse the catalog at talkpython.fm.
01:01:09.000 --> 01:01:10.940
And if you're not already subscribed to the show
01:01:11.160 --> 01:01:13.620
on your favorite podcast player, what are you waiting for?
01:01:14.240 --> 01:01:16.060
Just search for Python in your podcast player.
01:01:16.150 --> 01:01:17.040
We should be right at the top.
01:01:17.260 --> 01:01:20.260
If you enjoyed that geeky rap song, you can download the full track.
01:01:20.450 --> 01:01:22.280
The link is actually in your podcast blog or share notes.
01:01:23.180 --> 01:01:24.500
This is your host, Michael Kennedy.
01:01:24.900 --> 01:01:25.960
Thank you so much for listening.
01:01:26.170 --> 01:01:26.980
I really appreciate it.
01:01:27.400 --> 01:01:28.100
I'll see you next time.
01:01:38.260 --> 01:01:39.540
I started to meet.
01:01:40.900 --> 01:01:42.000
And we're ready to roll.
01:01:43.780 --> 01:01:44.820
Upgrade the code.
01:01:45.700 --> 01:01:47.320
No fear of getting whole.
01:01:48.820 --> 01:01:51.960
We tapped into that modern vibe over King Storm.
01:01:53.020 --> 01:01:55.700
Talk Python To Me, async is the norm.
01:02:24.880 --> 01:02:24.900
Редактор субтитров А.Семкин Корректор А.Егорова
00:00:00.020 --> 00:00:04.940
Today on Talk Python, the creators behind FastAPI, Flask, Django, Quart, and Litestar
00:00:05.500 --> 00:00:09.740
get practical about running apps based on their frameworks in production.
00:00:10.580 --> 00:00:15.060
Deployment patterns, async gotchas, servers, scalings, and the stuff that you only learn
00:00:15.060 --> 00:00:16.900
at 2 a.m. when the pager starts going off.
00:00:17.460 --> 00:00:20.700
For Django, we have Carlton Gibson and Jeff Triplich.
00:00:21.140 --> 00:00:23.220
For Flask, we have David Lord and Phil Jones.
00:00:23.860 --> 00:00:27.660
And on Team Litestar, we have Janek Nouvertné and Cody Fincher.
00:00:28.140 --> 00:00:31.700
And finally, Sebastian Ramirez from FastAPI is here as well.
00:00:32.279 --> 00:00:32.860
Let's jump in.
00:00:33.240 --> 00:00:38.400
This is Talk Python To Me, episode 533, recorded December 17th, 2025.
00:00:55.900 --> 00:00:57.260
Welcome to Talk Python To Me.
00:00:57.380 --> 00:01:00.620
the number one Python podcast for developers and data scientists.
00:01:01.300 --> 00:01:02.520
This is your host, Michael Kennedy.
00:01:02.900 --> 00:01:06.440
I'm a PSF fellow who's been coding for over 25 years.
00:01:07.100 --> 00:01:08.200
Let's connect on social media.
00:01:08.540 --> 00:01:11.680
You'll find me and Talk Python on Mastodon, Bluesky, and X.
00:01:11.870 --> 00:01:13.800
The social links are all in your show notes.
00:01:14.560 --> 00:01:18.080
You can find over 10 years of past episodes at talkpython.fm.
00:01:18.260 --> 00:01:21.560
And if you want to be part of the show, you can join our recording live streams.
00:01:21.760 --> 00:01:22.280
That's right.
00:01:22.470 --> 00:01:25.740
We live stream the raw uncut version of each episode on YouTube.
00:01:26.140 --> 00:01:30.740
Just visit talkpython.fm/youtube to see the schedule of upcoming events.
00:01:30.950 --> 00:01:34.620
Be sure to subscribe there and press the bell so you'll get notified anytime we're recording.
00:01:35.400 --> 00:01:39.500
Hey, before we jump into the interview, I just want to send a little message to all the companies
00:01:39.800 --> 00:01:43.420
out there with products and services trying to reach developers.
00:01:44.260 --> 00:01:45.680
That is the listeners of this show.
00:01:46.160 --> 00:01:50.000
As we're rolling into 2026, I have a bunch of spots open.
00:01:50.130 --> 00:01:55.980
So please reach out to me if you're looking to sponsor a podcast or just generally sponsor
00:01:56.040 --> 00:01:59.820
things in the community and you haven't necessarily considered podcasts, you really should.
00:02:00.720 --> 00:02:04.120
Reach out to me and I'll help you connect with the Talk Python audience.
00:02:05.080 --> 00:02:10.220
Thanks everyone for listening all of 2025. And here we go into 2026. Cheers.
00:02:11.640 --> 00:02:19.140
Hello, hello, Carlton, Sebastian, David, Cody, Yannick, Phil, Jeff, welcome back to Talk Python,
00:02:19.300 --> 00:02:22.920
all of you. Thanks for having us. Thank you for having us. Happy to be here again. We're here for
00:02:22.940 --> 00:02:30.260
what may be my favorite topic for sure. Something I spend most of my time on is web API stuff,
00:02:30.600 --> 00:02:36.320
which is awesome. So excited to have you here to give your inside look at how people should
00:02:36.320 --> 00:02:41.400
be running your framework, at least the one that you significantly contribute to, depending on
00:02:42.040 --> 00:02:47.500
which framework we're talking about, right? It's going to be a lot of fun, and I'm really excited
00:02:47.500 --> 00:02:51.739
to talk about it. However, there's an interesting fact that I've been throwing out a lot lately is
00:02:51.760 --> 00:02:56.680
that fully half of the people doing professional Python development have only been doing it for two
00:02:56.820 --> 00:03:01.760
years or less. And some of you been on the show, it was maybe two years longer than that. Let's just
00:03:01.760 --> 00:03:06.740
do a quick round of introductions for people who don't necessarily know you. We'll go around the
00:03:06.880 --> 00:03:11.280
squares here in the screen sharing. So Carlton, you're up first. Oh, I get to go first. Brilliant.
00:03:11.520 --> 00:03:15.980
Well, I'm Carlton. I work on the Django Red framework mostly. I'm a former Django fellow.
00:03:16.080 --> 00:03:20.220
I maintain a number of packages in the ecosystem. And the last few years I've been back to building
00:03:20.240 --> 00:03:25.420
stuff with Django rather than working on it. So I run a build startup that's, well, we're still
00:03:25.600 --> 00:03:30.680
going. So I'm quite excited about that. Awesome. How is it to be building with Django than building
00:03:30.900 --> 00:03:36.060
Django? Oh, I'm literally having the time of my life. Like I spent five years as a Django fellow
00:03:36.200 --> 00:03:42.000
working on Django and I just built up this backlog of things I wanted to do and I had no time and no
00:03:42.200 --> 00:03:46.740
capacity and no, no sort of nothing to work on with them. And it's just, it's just a delight.
00:03:46.980 --> 00:03:50.460
And every day I sit down on my computer thinking, oh, what's today?
00:03:50.610 --> 00:03:51.420
I look at the background.
00:03:51.780 --> 00:03:52.100
Oh, yes.
00:03:52.680 --> 00:03:54.260
And every day, a delight.
00:03:54.400 --> 00:03:56.640
So I'm still just loving it.
00:03:56.700 --> 00:03:57.100
That's awesome.
00:03:57.250 --> 00:04:01.500
So more often you're appreciating your former self than cursing your former self
00:04:01.680 --> 00:04:02.300
for the way you built.
00:04:04.040 --> 00:04:05.840
Yeah, that's an interesting one.
00:04:05.840 --> 00:04:07.100
I think we should move on before.
00:04:07.960 --> 00:04:08.300
All right.
00:04:08.420 --> 00:04:08.620
All right.
00:04:09.040 --> 00:04:14.100
Speaking of building with and for Sebastian, FastAPI.
00:04:14.230 --> 00:04:14.340
Hello.
00:04:14.700 --> 00:04:14.900
Hello.
00:04:15.980 --> 00:04:18.100
So, okay, intro for the ones that don't know me.
00:04:18.100 --> 00:04:19.060
I'm Sebastian Ramirez.
00:04:19.579 --> 00:04:21.100
I created FastAPI.
00:04:21.620 --> 00:04:23.000
Yeah, that's pretty much it.
00:04:23.340 --> 00:04:27.780
And now I have been building a company since the last two years, FastAPI Cloud, to deploy
00:04:27.980 --> 00:04:28.360
FastAPI.
00:04:28.400 --> 00:04:33.060
So, I get to drink from funny cups, as you can see.
00:04:33.520 --> 00:04:34.720
The world's best boss.
00:04:35.220 --> 00:04:35.400
Amazing.
00:04:36.180 --> 00:04:39.520
So, I think you deserve to give a bit of a shout out to FastAPI Cloud.
00:04:39.680 --> 00:04:40.200
That's a big deal.
00:04:40.360 --> 00:04:40.660
Thank you.
00:04:40.860 --> 00:04:41.480
Thank you very much.
00:04:41.620 --> 00:04:42.880
Yeah, it's super fun.
00:04:42.900 --> 00:04:47.080
And the idea is to make it super simple to deploy FastAPI applications.
00:04:47.540 --> 00:04:52.120
The idea with FastAPI was to make it very simple to build applications, build APIs,
00:04:52.940 --> 00:04:57.700
and get the idea from idea to product in record time.
00:04:57.800 --> 00:04:59.060
That was the idea with FastAPI.
00:04:59.140 --> 00:05:02.820
But then deploying that, in many cases, is just too cumbersome.
00:05:02.880 --> 00:05:03.500
It's too complicated.
00:05:03.820 --> 00:05:05.260
There are just so many things to that.
00:05:05.700 --> 00:05:09.200
So I wanted to bring something for people to be able to say, like,
00:05:09.360 --> 00:05:12.360
hey, just one command FastAPI deploy, and we take care of the rest.
00:05:12.700 --> 00:05:15.660
And then we and the team, I have an amazing thing
00:05:15.660 --> 00:05:16.980
that I've been able to work with.
00:05:17.320 --> 00:05:20.200
We suffer all the cloud pains so that people
00:05:20.380 --> 00:05:21.400
don't have to deal with that.
00:05:21.520 --> 00:05:25.840
And yeah, it's painful to build, but it's so cool to use it.
00:05:25.860 --> 00:05:27.780
You know, like that's the part when I say like, yes,
00:05:28.220 --> 00:05:29.400
this was worth it.
00:05:29.480 --> 00:05:32.240
When I get to use the thing myself, that is super cool.
00:05:32.240 --> 00:05:34.240
MARK MANDEL: Yeah, I'm assuming you build FastAPI
00:05:34.400 --> 00:05:35.560
Cloud with FastAPI somewhat.
00:05:35.720 --> 00:05:37.440
FRANCISCO MOLIN: Yes, yes, yes, exactly.
00:05:37.820 --> 00:05:39.720
FastAPI Cloud runs on FastAPI Cloud.
00:05:40.420 --> 00:05:44.200
And I get just like now random things in there and like, yes.
00:05:44.480 --> 00:05:45.260
Congrats to that again.
00:05:45.440 --> 00:05:45.980
That's super cool.
00:05:46.520 --> 00:05:47.220
David Lord, welcome.
00:05:47.600 --> 00:05:47.880
Welcome back.
00:05:48.040 --> 00:05:48.260
Yeah.
00:05:48.460 --> 00:05:48.580
Hello.
00:05:49.080 --> 00:05:49.640
I'm David Lord.
00:05:49.960 --> 00:05:54.820
I'm the lead maintainer of Pallets, which is Flask, Jinja, Click, Werkzeug.
00:05:55.020 --> 00:05:56.020
ItsDangerous, MarkupSafe.
00:05:56.560 --> 00:06:02.540
And now Pallets Eco, which is a bunch of the famous extensions for Flask that are getting
00:06:02.800 --> 00:06:03.720
community maintenance now.
00:06:04.100 --> 00:06:08.200
I've been doing that since, I think I've been the lead maintainer since like 2019, but a
00:06:08.400 --> 00:06:09.460
maintainer since like 2017.
00:06:09.920 --> 00:06:10.560
So it's been a while.
00:06:10.660 --> 00:06:11.380
That's been a while.
00:06:11.560 --> 00:06:13.860
We're coming up on seven, eight years.
00:06:14.160 --> 00:06:14.520
That's crazy.
00:06:15.200 --> 00:06:15.540
Time flies.
00:06:15.580 --> 00:06:18.280
It's always funny because I always feel like I've been doing it for way, way longer.
00:06:18.600 --> 00:06:21.340
And then I look at the actual date that I got added as a maintainer.
00:06:21.340 --> 00:06:22.660
I'm like, well, it couldn't have been that late.
00:06:22.800 --> 00:06:23.860
I was doing stuff before that, right?
00:06:24.360 --> 00:06:27.720
Well, I'm sure you were deep in flask before you got added as a maintainer of it, right?
00:06:28.140 --> 00:06:28.200
Yeah.
00:06:28.320 --> 00:06:31.780
Phil Jones, since you are also on the same org, next.
00:06:32.240 --> 00:06:32.900
Hey, welcome back.
00:06:32.980 --> 00:06:33.080
Hello.
00:06:33.440 --> 00:06:34.500
Yeah, I'm Phil Jones.
00:06:34.720 --> 00:06:37.760
I am the author of Quart, which is also part of Pallets.
00:06:37.960 --> 00:06:41.800
I also work on Werkzeug and Flask and help out there.
00:06:42.220 --> 00:06:44.660
And I've done a server called Hypercorn as well.
00:06:44.820 --> 00:06:47.680
So a bit of interest in that part of the ecosystem.
00:06:47.860 --> 00:06:49.300
What is Quart for people who don't know?
00:06:50.000 --> 00:06:52.840
Quart is basically Flask with async await.
00:06:53.340 --> 00:06:56.880
And that was the idea behind it really to make it possible to do async await.
00:06:57.240 --> 00:06:58.800
So yeah, that's pretty much it.
00:06:58.800 --> 00:07:00.820
If we, when we manage to merge them, we will.
00:07:00.920 --> 00:07:07.240
And the goal now with Quart as part of palettes is to eventually have it be one code base with Flask.
00:07:07.600 --> 00:07:11.760
But given that we both have small children now, we're moving a lot slower.
00:07:13.060 --> 00:07:13.900
Having kids is great.
00:07:14.040 --> 00:07:14.820
I have three kids.
00:07:15.480 --> 00:07:19.920
Productivity is not a thing that they are known to imbue on the parents, right?
00:07:20.060 --> 00:07:21.320
Especially in the early days.
00:07:21.780 --> 00:07:23.080
I want to say, Phil, thank you.
00:07:23.200 --> 00:07:26.820
I've been running Quart for a couple of my websites lately, and it's been amazing.
00:07:26.980 --> 00:07:27.260
Nice.
00:07:27.580 --> 00:07:29.080
Yeah, I also use it at work.
00:07:29.200 --> 00:07:31.840
We've got all our stuff in Quart, which is, yeah, it's really good fun.
00:07:31.960 --> 00:07:32.660
A bit like Carlton.
00:07:32.800 --> 00:07:39.240
So when people, if they get, if they listen to the show or they go to the website of the show and they're not on YouTube, then that somehow involves Quart.
00:07:39.640 --> 00:07:40.160
Janek, welcome.
00:07:40.560 --> 00:07:40.700
Hey.
00:07:41.160 --> 00:07:42.300
Yeah, I'm Janek Nouvertné.
00:07:42.400 --> 00:07:44.260
I work on Litestar.
00:07:44.360 --> 00:07:47.300
I just looked up how long it's been because I was curious myself.
00:07:48.020 --> 00:07:52.780
I also had the same feeling that it's been a lot longer than, it's actually only been three years.
00:07:53.380 --> 00:07:53.460
Yeah.
00:07:53.820 --> 00:07:57.060
And I also, I noticed something with all you guys here in the room.
00:07:57.200 --> 00:08:00.580
I use almost all of the projects you maintain at work,
00:08:01.960 --> 00:08:04.360
which is quite nice.
00:08:04.420 --> 00:08:06.660
We have a very big Django deployment.
00:08:06.840 --> 00:08:08.180
We have some Flask deployments.
00:08:08.180 --> 00:08:09.760
We have a few FastAPI deployments.
00:08:10.100 --> 00:08:12.360
I think we have one Quart deployment
00:08:12.700 --> 00:08:14.560
and we also have two Litestar deployments,
00:08:15.280 --> 00:08:17.620
which obviously is a lot of fun to work with.
00:08:17.800 --> 00:08:20.420
And I find it really, really nice actually
00:08:20.560 --> 00:08:23.240
to work with all these different things.
00:08:23.400 --> 00:08:26.039
It's super interesting also because like everything
00:08:26.060 --> 00:08:29.540
has its own niche that it's really good at.
00:08:29.760 --> 00:08:31.600
And even, you know, you think
00:08:31.910 --> 00:08:33.260
if you maintain a framework yourself,
00:08:33.539 --> 00:08:36.099
you tend to always recommend it for everything.
00:08:36.440 --> 00:08:38.440
But I noticed it's not actually true.
00:08:38.640 --> 00:08:40.360
There's actually quite a few cases
00:08:40.640 --> 00:08:42.479
where I don't recommend Litestar.
00:08:42.580 --> 00:08:44.480
I recommend, you know, just, you know,
00:08:44.680 --> 00:08:46.740
use Django for this or, you know,
00:08:47.120 --> 00:08:49.340
use Flask for that or use FastAPI for this
00:08:49.430 --> 00:08:52.200
because, well, they are quite different after all.
00:08:52.340 --> 00:08:54.999
And I find that really, really interesting
00:08:55.020 --> 00:09:00.480
and nice. And I think it's a good sign of a healthy ecosystem if it's not just, you know,
00:09:00.740 --> 00:09:04.560
the same thing, but different, but it actually brings something very unique and different to
00:09:04.560 --> 00:09:08.760
the table. I think that's a great attitude. And it's very interesting. You know, I feel like
00:09:08.880 --> 00:09:13.280
there's a lot of people who feel like they've kind of got to pick their tech team for everything.
00:09:13.820 --> 00:09:17.260
I'm going to build a static site. Like, well, I've got to have a Python-based static site builder.
00:09:17.300 --> 00:09:21.220
Like, well, it's a static site. Who cares what technology makes it turn? You're writing Markdown,
00:09:21.260 --> 00:09:22.080
and out comes HTML.
00:09:22.350 --> 00:09:24.000
Who cares what's in the middle, for example, right?
00:09:24.540 --> 00:09:27.600
And, you know, I feel like that's kind of a life lessons learned.
00:09:28.060 --> 00:09:28.980
Absolutely, yeah.
00:09:29.140 --> 00:09:29.760
Yeah, that's awesome.
00:09:30.290 --> 00:09:31.140
Cody, hello, hello.
00:09:31.340 --> 00:09:32.780
Yeah, hey guys, I'm Cody Fincher.
00:09:32.900 --> 00:09:34.560
I'm also one of the maintainers of Litestar.
00:09:34.840 --> 00:09:37.400
I've been there just a little bit longer than Yannick.
00:09:37.940 --> 00:09:39.700
And so it's been about four years now.
00:09:40.080 --> 00:09:41.880
And Yannick actually teed this up perfectly
00:09:42.000 --> 00:09:43.380
because I was going to say something very similar.
00:09:43.500 --> 00:09:44.820
I currently work for Google.
00:09:44.960 --> 00:09:46.460
I've been there for about three and a half years now.
00:09:46.760 --> 00:09:49.240
And we literally have every one of the frameworks
00:09:49.370 --> 00:09:50.220
you guys just mentioned,
00:09:50.240 --> 00:09:51.240
and they're all in production.
00:09:51.480 --> 00:09:54.080
And so one of the things that you'll see
00:09:54.380 --> 00:09:56.420
on the Litestar org and part of the projects
00:09:56.440 --> 00:09:59.040
that we maintain are that we have these optional batteries
00:09:59.320 --> 00:10:01.060
and most of the batteries that we have
00:10:01.260 --> 00:10:03.120
all work with the frameworks for you guys.
00:10:03.220 --> 00:10:05.800
And so it's nice to be able to use that stuff,
00:10:06.080 --> 00:10:08.580
you know, regardless of what tooling you've got
00:10:08.720 --> 00:10:10.120
or what project it is.
00:10:10.180 --> 00:10:12.140
And so, yeah, having that interoperability
00:10:12.400 --> 00:10:14.120
and the ability to kind of go between the frameworks
00:10:14.380 --> 00:10:16.800
that work the best for the right situation is crucial.
00:10:16.920 --> 00:10:18.500
And so I'm glad you mentioned that, Yannick.
00:10:18.520 --> 00:10:20.200
But yeah, nice to see you guys
00:10:20.220 --> 00:10:25.140
on the show. Cody, tell people what Litestar is. I know I had both you guys and Jacob on a while
00:10:25.260 --> 00:10:29.900
ago, but it's been a couple of years, I think. Litestar at its core is really a web framework
00:10:30.010 --> 00:10:36.260
that kind of sits somewhere in between, I'd say, Flask and FastAPI and Django. So whereas, you know,
00:10:36.300 --> 00:10:41.040
Flask doesn't really, you know, bundle a lot of batteries. There's a huge amount of, you know,
00:10:41.180 --> 00:10:44.540
third-party libraries and ecosystem that's built around it that people can add into it, but there's
00:10:44.560 --> 00:10:50.000
not really like, for instance, a database adapter or a database plugin or plugins for
00:10:50.180 --> 00:10:54.100
Vite or something like that, right, for front end development. And so what we have been doing
00:10:54.130 --> 00:10:59.820
is building a API framework that is very similar in concept to FastAPI that is also extensible.
00:10:59.930 --> 00:11:03.280
So if you want to use the batteries, they're there for you. But if you don't want to use
00:11:03.380 --> 00:11:07.560
them, you don't have to, right? And so a lot of the tooling that we built for Litestar
00:11:07.600 --> 00:11:12.920
was birthed out of a startup that I was in prior to joining Google. And so having all
00:11:12.940 --> 00:11:15.160
this boilerplate, really, it needed somewhere to go.
00:11:15.320 --> 00:11:17.620
And so a lot of this stuff ended up being plugins,
00:11:17.960 --> 00:11:19.800
which is what we bundled into Litestar
00:11:19.940 --> 00:11:22.460
so that you can kind of add in this extra functionality.
00:11:22.960 --> 00:11:24.360
And so I know I'm getting long-winded.
00:11:24.480 --> 00:11:27.340
It's somewhere between Django and Flask,
00:11:27.400 --> 00:11:29.280
if you were to think about it in terms of a spectrum,
00:11:29.340 --> 00:11:32.520
in terms of what it gives you in terms of a web framework.
00:11:32.820 --> 00:11:34.960
But in short, it does everything that all the other guys do.
00:11:35.120 --> 00:11:37.520
Very neat. It's definitely a framework I admire.
00:11:37.820 --> 00:11:39.760
Jeff Triplett, so glad you could make it.
00:11:39.860 --> 00:11:40.720
Yeah, thanks for having me.
00:11:40.860 --> 00:11:42.560
Yeah, I'm Jeff Triplett. I'm out of Lawrence, Kansas.
00:11:42.840 --> 00:11:45.100
I'm a consultant at a company called Revolution Systems.
00:11:45.550 --> 00:11:48.320
I was on, some people know me from being on the Python Software Foundation board.
00:11:48.680 --> 00:11:49.920
I've been off that for a few years.
00:11:50.240 --> 00:11:52.880
As of last week, I'm the president of the Django Software Foundation.
00:11:53.290 --> 00:11:54.620
So I've been on that board for a year.
00:11:54.860 --> 00:11:56.620
I'm kind of a Django power user, I guess.
00:11:56.720 --> 00:11:57.900
I've used it for about 20 years.
00:11:58.420 --> 00:12:02.040
And I've kind of not really worked on, I don't even think I have a patch anymore in Django.
00:12:02.460 --> 00:12:04.280
But I've done a lot with the community.
00:12:04.720 --> 00:12:08.660
I've done a lot with contributing through conferences and using utilities.
00:12:09.260 --> 00:12:12.180
I try to promote Carleton's applications like Neapolitan.
00:12:12.260 --> 00:12:15.780
And if I like tools, Python tools in general, I try to advocate for it.
00:12:16.040 --> 00:12:18.620
I've also used all of these applications.
00:12:18.900 --> 00:12:21.020
Litestar, I haven't, but I have a friend who talks about it a lot.
00:12:21.320 --> 00:12:22.860
And so I feel like I know a lot from it.
00:12:23.100 --> 00:12:25.440
As a consultant, we tend to go with the best tool for the job.
00:12:25.600 --> 00:12:26.840
So I've done a little bit of FastAPI.
00:12:27.340 --> 00:12:31.020
I worked with Flask a lot over the years, even though we're primarily a Django shop.
00:12:31.360 --> 00:12:32.500
It just depends on what the client needs.
00:12:32.580 --> 00:12:35.940
And you see a lot of different sizes of web app deployments.
00:12:36.100 --> 00:12:38.140
So I think that's going to be an interesting angle for sure.
00:12:38.320 --> 00:12:38.860
Yeah, absolutely.
00:12:39.220 --> 00:12:41.560
Small ones to hundreds of servers.
00:12:42.100 --> 00:12:46.400
We don't see it as much anymore the last four or five years, especially with like CDNs and caching.
00:12:46.800 --> 00:12:49.720
We just don't see load like we did, you know, 10 years ago or so.
00:12:50.060 --> 00:12:55.160
And then I also do a lot of like small, I kind of call them some of them little dumb projects, but some are just fun.
00:12:55.480 --> 00:12:59.800
Like I've got a FastAPI web ring that I wrote a year ago for April Fool's Day.
00:13:00.100 --> 00:13:03.440
And for some reason that kind of took off and people liked it, even though it was kind of a joke.
00:13:03.780 --> 00:13:07.620
So I started like peppering it on a bunch of sites and I maintain like Django packages.
00:13:08.000 --> 00:13:11.020
I do a newsletter, Django News newsletter, just kind of lots of fun stuff.
00:13:11.340 --> 00:13:13.360
Definitely looking forward to hearing all of your opinions.
00:13:14.000 --> 00:13:17.460
So I've got a bunch of different your app in production topics
00:13:17.580 --> 00:13:20.560
I thought we could just work around or talk over.
00:13:20.900 --> 00:13:24.180
So I thought maybe the first one is what would you recommend,
00:13:24.540 --> 00:13:26.340
or if you don't really have a strong recommendation,
00:13:26.620 --> 00:13:31.900
what would you choose for yourself to put your app in your framework in production?
00:13:32.080 --> 00:13:35.820
I'm thinking app servers, reverse proxies like Nginx or Caddy.
00:13:36.360 --> 00:13:37.320
Do you go for threaded?
00:13:37.860 --> 00:13:39.180
Try to scale out with threads.
00:13:39.340 --> 00:13:44.820
you try to scale out with processes, Docker, no Docker, Kubernetes. What are we doing here,
00:13:44.940 --> 00:13:49.560
folks? Carlton. I think we'll just keep going around the circle here. So you may get the first
00:13:49.880 --> 00:13:52.280
round of everyone. No, I'll try to mix it up, but let's do it this time.
00:13:52.360 --> 00:14:00.020
I do the oldest school thing in the book. I run Nginx as my front end. I'll stick a
00:14:00.100 --> 00:14:05.839
WSGI server behind it with a pre-fork, a few workers, depending on CPU size, depending on
00:14:05.860 --> 00:14:10.900
the kind of requests I'm handling. These days, in order to handle long-lived requests,
00:14:10.960 --> 00:14:14.860
like server-sent events, that kind of, or WebSocket type things, I'll run an ASGI server as a kind
00:14:14.860 --> 00:14:18.040
of sidecar. I've been thinking about this a lot, actually. But yeah, this is interesting.
00:14:18.280 --> 00:14:22.120
If you're running a small site and you want long-lived requests, just run ASGI. Just use
00:14:22.240 --> 00:14:29.360
ASGI. Because any of the servers, Hypercorn, Uvicorn, Daphne, Granian is the new hot kid on
00:14:29.360 --> 00:14:34.279
the bot, right? All of those will handle your traffic, no problem. But for me, the scaling
00:14:34.300 --> 00:14:39.720
Pallets and WSGI are so well known and just like i can do the maths on the back of the pencil i know
00:14:39.900 --> 00:14:45.740
exactly how to scale it up having done it for so long for me for my core application i would still
00:14:45.880 --> 00:14:51.080
rather use the wsgi server and then limit the async stuff to just to the use cases where it's
00:14:51.180 --> 00:14:58.040
particularly suited so i'll do that um process manager i deploy using systemd if i want to if i
00:14:58.120 --> 00:15:03.719
want a container i'll use podman by systemd it's as old school as it gets i'll very often run a
00:15:03.740 --> 00:15:05.740
a Redis instance on localhost for caching,
00:15:05.960 --> 00:15:07.780
and that will be it.
00:15:08.100 --> 00:15:09.300
And that will get me an awful long way.
00:15:09.340 --> 00:15:10.020
If I have to schedule,
00:15:10.880 --> 00:15:11.940
I just get a bigger box.
00:15:12.260 --> 00:15:12.960
And a bigger box.
00:15:13.020 --> 00:15:13.460
Yeah, yeah, yeah.
00:15:13.560 --> 00:15:16.040
I really, really, really need multiple boxes.
00:15:16.260 --> 00:15:16.720
Well, then we'll talk.
00:15:16.820 --> 00:15:18.400
I feel like you and I are in a similar vibe.
00:15:18.840 --> 00:15:20.680
But one thing I want to sort of throw out there to you,
00:15:20.760 --> 00:15:22.360
but also sort of the others is,
00:15:22.720 --> 00:15:23.780
what are we talking with databases?
00:15:24.300 --> 00:15:26.460
Like, who is bold enough to go SQLite?
00:15:26.740 --> 00:15:28.100
Anyone's going SQLite out there?
00:15:28.900 --> 00:15:30.020
Yeah, it depends, right?
00:15:30.100 --> 00:15:31.580
It just depends on what you're doing, right?
00:15:31.580 --> 00:15:33.460
And how many concurrent users you're going to have.
00:15:33.600 --> 00:15:34.640
It really is amazing there.
00:15:34.800 --> 00:15:36.860
The Palette's website is running on Flask,
00:15:37.160 --> 00:15:38.080
which I wasn't doing for a while.
00:15:38.080 --> 00:15:39.400
I was doing a static site generator.
00:15:39.770 --> 00:15:43.340
Then I got inspired by Andrew Godwin's static dynamic sites.
00:15:43.860 --> 00:15:46.520
And so it loads up all these markdown files,
00:15:46.730 --> 00:15:49.900
static markdown files into a SQLite database at runtime
00:15:50.480 --> 00:15:51.720
and then serves off of that
00:15:51.750 --> 00:15:52.920
because you can query really fast.
00:15:53.100 --> 00:15:54.160
Oh, that's awesome. I love it.
00:15:54.260 --> 00:15:56.140
So I am using SQLite for the Palette's website.
00:15:56.480 --> 00:16:00.220
Yeah, I also do have a few small apps that use SQLite.
00:16:00.340 --> 00:16:02.980
And one recently that's Cody's fault
00:16:03.000 --> 00:16:04.580
because he put me on that track
00:16:05.240 --> 00:16:08.740
where it's running a SQLite database in the browser
00:16:08.960 --> 00:16:12.300
because nowadays it's quite easy to do that.
00:16:12.860 --> 00:16:15.200
And then you can do all sorts of stuff with it,
00:16:15.300 --> 00:16:19.080
like hook into it with DuckDB and perform some analysis.
00:16:19.320 --> 00:16:23.020
So you don't actually need to run any sort of server at all.
00:16:23.060 --> 00:16:26.780
You can just throw some files into Nginx and serve your data.
00:16:26.980 --> 00:16:28.240
And as long as that's static,
00:16:28.300 --> 00:16:30.160
you have a super, super simple deployment.
00:16:30.680 --> 00:16:32.200
So yeah, definitely SQLite.
00:16:32.580 --> 00:16:34.280
If you can, I like it.
00:16:34.360 --> 00:16:35.000
I agree.
00:16:35.100 --> 00:16:35.460
It's interesting.
00:16:36.080 --> 00:16:38.100
The database probably won't go down with that, probably.
00:16:38.500 --> 00:16:40.020
Let's do this by framework.
00:16:40.180 --> 00:16:42.340
So we'll do vertical slices in the visual here.
00:16:42.600 --> 00:16:43.140
So Jeff.
00:16:43.560 --> 00:16:46.000
Yeah, Django, Postgres, pretty old school stack.
00:16:46.320 --> 00:16:49.120
I think putting a CD in front of anything is just a win.
00:16:49.210 --> 00:16:51.000
So whether you like Fastly or Cloudflare,
00:16:51.150 --> 00:16:52.380
you get a lot of mileage out of it.
00:16:52.660 --> 00:16:53.760
You learn a lot about caching
00:16:53.920 --> 00:16:55.880
because it's kind of hard to cache Django by default.
00:16:56.200 --> 00:16:57.200
So you get to play with curl
00:16:57.430 --> 00:16:59.260
and kind of figure out why very headers are there.
00:16:59.670 --> 00:17:02.180
And it's a good learning experience to get through that.
00:17:02.240 --> 00:17:06.600
I also like Coolify, which is kind of new, at least new to me and new to Michael.
00:17:06.689 --> 00:17:08.240
We talk about this in our spare time a lot.
00:17:08.520 --> 00:17:11.800
It's just kind of a boring service that'll launch a bunch of containers for you.
00:17:12.280 --> 00:17:15.040
There's a bunch of one-click installs, so Postgres is a one-click.
00:17:15.270 --> 00:17:18.240
It also does backups for you, which is really nice to have for free.
00:17:18.520 --> 00:17:21.640
I run a couple dozen sites through it and really like it.
00:17:21.900 --> 00:17:26.140
You can either do a hosted forum, I don't get any money from it, or you can run the open-source version.
00:17:26.560 --> 00:17:27.140
I do both.
00:17:27.310 --> 00:17:30.500
I've got like a home lab that I just run stuff using the open-source version.
00:17:30.520 --> 00:17:32.860
And for five bucks a month, it's worth it to run a couple servers.
00:17:33.300 --> 00:17:35.400
And like Carlton said, you can just scale up.
00:17:35.880 --> 00:17:40.080
Yeah, it's got a bunch of one-click deploy for self-hosted SaaS things as well.
00:17:40.260 --> 00:17:44.860
Like I want an analytics stack of containers that run in its own isolated bit.
00:17:44.880 --> 00:17:45.960
Just click here and go.
00:17:46.280 --> 00:17:48.520
Yeah, one-click, it's installed and you're up.
00:17:48.740 --> 00:17:52.760
And then once you get one Django, Flask, FastAPI site working with it,
00:17:53.020 --> 00:17:54.440
and it uses like a Docker container.
00:17:54.780 --> 00:17:58.500
Once you get that set up, it's really easy to just kind of duplicate that site,
00:17:58.960 --> 00:18:01.160
plug it in to GitHub or whatever your Git provider is.
00:18:01.260 --> 00:18:05.460
And it's a nice experience for what normally is just our syncing files
00:18:05.820 --> 00:18:06.860
and life's too short for that.
00:18:06.960 --> 00:18:09.500
Sebastian, I want to have you go last on this one
00:18:09.500 --> 00:18:12.160
because I think you've got something pretty interesting
00:18:12.280 --> 00:18:14.260
with FastAPI Cloud to dive into.
00:18:14.480 --> 00:18:16.520
But let's do Litestar next. Cody.
00:18:16.840 --> 00:18:19.580
I have actually bought all the way in on Granian.
00:18:19.840 --> 00:18:22.420
So for the ASGI server, I've actually been running Granian now
00:18:22.560 --> 00:18:24.060
for I'd say a year in production.
00:18:24.420 --> 00:18:25.660
It's worked pretty well.
00:18:26.180 --> 00:18:28.920
There's a couple of new things that I'm actually kind of
00:18:28.940 --> 00:18:31.460
with. I don't know how well they're going to work out. So I'm going to go ahead and throw this out
00:18:31.540 --> 00:18:37.900
there. But Granian is one of the few ASGI servers that supports HTTP2. And it actually can do HTTP2
00:18:38.040 --> 00:18:42.100
clear text. And so this is part of the next thing I'm going to say. Because I work for Google, I'm
00:18:42.480 --> 00:18:47.440
actively using lots of Kubernetes and Cloud Run mainly. And so most of the things that I deploy
00:18:47.520 --> 00:18:52.860
are containerized on Cloud Run. And I typically would suggest if you're not using something like
00:18:53.100 --> 00:18:57.140
SystemD and deploying it directly on bare metal, then you are going to want to let the container
00:18:57.160 --> 00:18:59.740
or whatever you're using to manage your processes,
00:19:00.340 --> 00:19:01.640
manage that and spin that up.
00:19:01.670 --> 00:19:03.900
And so I typically try to allocate,
00:19:04.070 --> 00:19:05.780
you know, like one CPU for the container
00:19:05.920 --> 00:19:08.660
and let the actual framework scale it up and down as needed.
00:19:09.080 --> 00:19:11.600
Cloud Run itself has a, like an ingress,
00:19:11.830 --> 00:19:13.320
like a load balancer that sits in front
00:19:13.480 --> 00:19:14.480
that it automatically configures.
00:19:14.510 --> 00:19:16.980
And you're required to basically serve up
00:19:17.160 --> 00:19:19.300
clear text traffic in when you run Cloud Run.
00:19:19.740 --> 00:19:22.580
And because now Granian supports HTTP2
00:19:22.820 --> 00:19:25.340
and Cloud Run supports HTTP2 clear text,
00:19:25.500 --> 00:19:28.140
you can now serve Granian as HTTP2 traffic.
00:19:28.520 --> 00:19:30.960
The good thing about that is that you get an unlimited upload size.
00:19:31.060 --> 00:19:33.380
And so there are max thresholds to what you can upload
00:19:33.660 --> 00:19:34.720
into the various cloud environments.
00:19:35.500 --> 00:19:37.820
HTTP2 usually circumvents that or gets around it
00:19:37.900 --> 00:19:39.120
because of the way the protocol works.
00:19:39.200 --> 00:19:42.020
And so you git additional features and functionality because of that.
00:19:42.200 --> 00:19:43.780
So anyway, that's what I typically do.
00:19:44.000 --> 00:19:46.280
And most of my databases are usually Postgres,
00:19:47.020 --> 00:19:50.180
AlloyDB if it needs to be something that's on the analytical side.
00:19:50.600 --> 00:19:52.160
Yeah, I'm on Team Granian as well.
00:19:52.160 --> 00:19:53.600
I think that's a super neat framework.
00:19:53.840 --> 00:19:56.660
I had Giovanni on who's behind it a while ago.
00:19:57.050 --> 00:19:59.820
It seems like it's not as popular,
00:20:00.220 --> 00:20:02.000
but it's based on Hyper from the Rust world,
00:20:02.220 --> 00:20:05.500
which has like 130,000 projects based on it or something.
00:20:05.530 --> 00:20:09.420
So, you know, at its core, it's still pretty battle-tested.
00:20:11.420 --> 00:20:16.140
this portion of talk python to me is brought to you by our course just enough python for data
00:20:16.380 --> 00:20:21.160
scientists if you live in notebooks but need your work to hold up in the real world check out just
00:20:21.220 --> 00:20:26.580
enough python for data scientists it's a focused code first course that tightens the python you
00:20:26.840 --> 00:20:33.080
actually use and adds the habits that make results repeatable we refactor messy cells into functions
00:20:33.100 --> 00:20:38.700
and packages, use Git on easy mode, lock environments with uv, and even ship with Docker.
00:20:39.380 --> 00:20:42.020
Keep your notebook speed, add engineering reliability.
00:20:42.720 --> 00:20:43.900
Find it at Talk Python Training.
00:20:44.140 --> 00:20:46.900
Just click courses in the navbar at talkpython.fm.
00:20:47.959 --> 00:20:49.260
Yannick, how about you?
00:20:49.560 --> 00:20:50.800
You've got a variety, it sounds like.
00:20:50.860 --> 00:20:51.680
Yeah, definitely.
00:20:53.000 --> 00:20:59.340
There's a pretty clear split between what I do at work and what I do outside of that.
00:20:59.440 --> 00:21:01.720
So at work, it's Kubernetes deployments.
00:21:01.900 --> 00:21:05.360
And we managed that pretty much the same way that Cody described.
00:21:05.540 --> 00:21:09.700
So it's one or two processes per pod max.
00:21:10.060 --> 00:21:14.660
So you can have Kubernetes scaled or even manually easily scale that up.
00:21:14.760 --> 00:21:19.540
You can just go into Kubernetes and say, OK, do me one to five more pods or whatever.
00:21:20.060 --> 00:21:20.680
And don't have to worry.
00:21:21.040 --> 00:21:22.620
Don't have to start calculating whatever.
00:21:23.280 --> 00:21:28.580
Most of the stuff we run nowadays with uvicorn or Django deployment
00:21:28.900 --> 00:21:34.080
up until I think three months ago or so was running under Gunicorn,
00:21:34.160 --> 00:21:35.240
but we switched that actually.
00:21:35.760 --> 00:21:37.800
And it's been a really great experience.
00:21:38.380 --> 00:21:42.880
I think we tried that a year ago and it didn't work out quite so well.
00:21:42.900 --> 00:21:47.440
There was some things that didn't work as expected or didn't perform great
00:21:47.700 --> 00:21:52.220
or Django was throwing some errors or Uvicorn was throwing some errors.
00:21:52.480 --> 00:21:55.040
And then apparently all of that got fixed
00:21:55.360 --> 00:21:58.380
because now it runs without any issue for the production.
00:21:58.560 --> 00:22:06.400
Yeah, for people who don't know, the vibe used to be run Gunicorn, but then with uvicorn workers, if you're doing async stuff.
00:22:06.510 --> 00:22:12.620
And then uvicorn kind of stepped up its game and said, you can actually treat us as our own app server.
00:22:12.940 --> 00:22:14.580
We'll manage lifecycle and stuff.
00:22:14.590 --> 00:22:16.120
And so that's the path you took, right?
00:22:16.240 --> 00:22:16.740
Yeah, exactly.
00:22:16.970 --> 00:22:17.340
Before that.
00:22:17.630 --> 00:22:21.980
Well, no, actually, before that, we didn't because our Django is fully synchronous.
00:22:22.170 --> 00:22:24.020
It doesn't do any async.
00:22:24.220 --> 00:22:26.820
So it was just bare metal Gunicorn.
00:22:26.960 --> 00:22:30.100
And it's still synchronous with just running it under Uvicorn.
00:22:30.540 --> 00:22:34.200
But interestingly, still quite a bit faster in a few cases.
00:22:34.800 --> 00:22:38.520
We tried that out and we low tested it in a couple of scenarios
00:22:38.800 --> 00:22:41.100
and we found that it makes a lot of sense.
00:22:41.560 --> 00:22:44.200
But outside of that, I do have a lot of, well,
00:22:44.780 --> 00:22:48.100
very simplistic deployments that are also just systemd
00:22:48.380 --> 00:22:53.339
and a couple of Docker compose files and containers
00:22:53.360 --> 00:22:58.620
that are managed through some old coupled together Ansible things.
00:22:59.160 --> 00:23:02.620
But I think the oldest one that I have still running is from 2017.
00:23:03.500 --> 00:23:07.040
And it's been running without a change for like four or five years.
00:23:07.220 --> 00:23:07.920
That is awesome.
00:23:08.180 --> 00:23:11.680
I don't see a reason to do anything about it because the app works.
00:23:11.920 --> 00:23:13.500
It's being used productively.
00:23:14.100 --> 00:23:16.060
So why change anything about that?
00:23:16.060 --> 00:23:17.000
No need to introduce.
00:23:17.380 --> 00:23:18.180
Just don't touch it.
00:23:18.560 --> 00:23:22.340
Yeah, I was actually looking into Coolify that you two guys mentioned.
00:23:22.760 --> 00:23:28.040
I was thinking about, you know, maybe upgrading it to that, but I played around with it and I thought, well, why?
00:23:28.220 --> 00:23:31.520
You know, if I have to look into that deployment maybe once a year.
00:23:31.960 --> 00:23:36.080
So that's really nothing to gain for me to make it more complicated.
00:23:36.560 --> 00:23:37.960
David, Team Flask.
00:23:38.320 --> 00:23:45.000
I mentioned this before the show started, but I think I'm pretty sure I've said this the last time I was on Talk Python,
00:23:45.400 --> 00:23:50.760
but the projects I do for work typically have less than 100 users.
00:23:51.700 --> 00:23:54.500
And so my deployment is usually really simple.
00:23:54.680 --> 00:23:57.780
And usually they've chosen like Azure or AWS already.
00:23:58.180 --> 00:24:00.020
So we just have a Docker container
00:24:00.360 --> 00:24:03.020
and we put it on the relevant Docker container host
00:24:03.080 --> 00:24:05.480
in that service and it just works for them.
00:24:05.740 --> 00:24:08.400
We have a Postgres database and we have like Redis.
00:24:08.880 --> 00:24:11.280
But I never really had to deal with like scaling
00:24:11.820 --> 00:24:13.380
or that sort of stuff.
00:24:13.740 --> 00:24:16.780
But the funny thing is like, at least for my work,
00:24:16.940 --> 00:24:19.360
I'm always, we're often replacing older systems.
00:24:19.840 --> 00:24:32.620
And so even a single Docker container running a Flask application is way more performant and responsive than anything they're used to from like some 20 year old or 30 year old Java system.
00:24:32.880 --> 00:24:38.420
Right. And it can just respond on a small container with like a little bit of CPU and a little bit of memory.
00:24:38.740 --> 00:24:41.380
They're always shocked at like, how much do we need to pay for?
00:24:41.520 --> 00:24:43.280
Oh, just like it'll run a potato.
00:24:44.160 --> 00:24:46.680
You know, there's only 100 users and they're like, that's a lot of users.
00:24:48.400 --> 00:24:52.740
So my recommendation is always start small and then scale up from there.
00:24:52.900 --> 00:24:54.980
Don't try to overthink it ahead of time.
00:24:55.440 --> 00:24:58.860
Yeah, for my personal stuff, I'm using like Docker containers now and fly.io.
00:24:59.070 --> 00:25:00.160
I haven't gotten in.
00:25:00.550 --> 00:25:04.420
So I do want to look into Granian and Coolify, but I haven't gotten there yet.
00:25:04.920 --> 00:25:09.160
And for the Docker container, I can definitely recommend pythonspeed.org.
00:25:09.480 --> 00:25:13.260
I don't remember off the top of my head who writes that, but it's somebody in the Python
00:25:13.860 --> 00:25:14.240
ecosystem.
00:25:14.720 --> 00:25:18.480
And they have a whole series of articles on how to optimize your Docker container.
00:25:18.960 --> 00:25:22.440
And that sounds really complicated, but you end up with a Docker file that's like 20 lines
00:25:22.530 --> 00:25:23.260
long or something.
00:25:23.450 --> 00:25:26.280
So it's not like there's crazy things.
00:25:26.290 --> 00:25:28.140
It's just you have to know how to structure it.
00:25:28.330 --> 00:25:30.100
And then I just copy and paste that to the next project.
00:25:30.380 --> 00:25:30.560
Nice.
00:25:30.820 --> 00:25:30.900
Yeah.
00:25:31.300 --> 00:25:34.360
I resisted doing Docker for a long time because I'm like, I don't want that extra complexity.
00:25:34.550 --> 00:25:38.420
But then I realized the stuff you put in the Docker file is really what you just type in
00:25:38.420 --> 00:25:40.260
the terminal once and then you forget.
00:25:41.140 --> 00:25:44.580
I mean, always using Postgres, Redis, probably if I need some background.
00:25:44.680 --> 00:25:51.120
tasks, just plain SMTP server for email. I wrote for all three of those things. I wrote new
00:25:51.440 --> 00:25:55.920
extensions in the Flask ecosystem that I'm trying to get more people to know about now. So Flask
00:25:56.060 --> 00:26:01.920
SQLAlchemy Lite, L-I-T-E, instead of Flask SQLAlchemy, takes a much more lightweight approach to
00:26:02.360 --> 00:26:08.100
integrating SQLAlchemy with Flask. And then Flask Redis, I revived from like 10 years of
00:26:08.420 --> 00:26:12.699
non-maintenance. And then I wrote this whole system, this whole pluggable email system called
00:26:12.720 --> 00:26:18.560
email simplified, kind of inspired by Django, Django's pluggable system, except, and so there's
00:26:18.560 --> 00:26:23.560
like Flask email simplified to integrate that with Flask. But unlike Django, you can use email
00:26:23.780 --> 00:26:27.580
simplified in any library you're writing, in any Python application you're writing. It doesn't have
00:26:27.580 --> 00:26:32.780
to be a Flask web framework. It's pluggable as the library itself. And then you can also integrate
00:26:32.830 --> 00:26:38.640
it with Flask or something else. So Flask email simplified. I get like three downloads a month
00:26:38.640 --> 00:26:43.240
right now. So it needs some popularity. Awesome. I've been doing the non-simplified email lately.
00:26:43.440 --> 00:26:48.620
So I'm happy to hear that there might be a better way. Yeah. I think people do underappreciate just
00:26:48.720 --> 00:26:53.780
how much performance you got out of Python web apps. You know, they're like, oh, we're going to
00:26:53.800 --> 00:26:59.340
need to rewrite this and something else because the GIL or whatever. Like I decided just to make
00:26:59.340 --> 00:27:04.499
a point to pull up the tail till my log running court, by the way. And each one of these requests
00:27:04.520 --> 00:27:10.120
doing like multiple db calls and it's like 23 milliseconds six milliseconds three milliseconds
00:27:10.600 --> 00:27:15.840
you know nine milliseconds it's like that's good enough for that's a lot of requests per second
00:27:16.320 --> 00:27:21.800
per worker until you gotta you gotta have a lot of traffic speaking of court phil what's your take
00:27:21.940 --> 00:27:27.860
on this one i think it's very similar i also build docker containers and uh with a postgres database
00:27:27.980 --> 00:27:34.480
on the back end and i run hypercorn as the ascii server and put them behind a aws load balancer
00:27:34.560 --> 00:27:35.840
and just run them in ECS.
00:27:36.080 --> 00:27:37.840
And I think it's pretty simple,
00:27:38.120 --> 00:27:39.680
but I guess it depends on your biases.
00:27:39.960 --> 00:27:41.840
But yeah, that's all we do really.
00:27:41.880 --> 00:27:42.760
And it goes a long way.
00:27:42.940 --> 00:27:44.800
There are multiple ECS tasks,
00:27:45.240 --> 00:27:47.480
mostly because if one falls over rather than scaling,
00:27:47.760 --> 00:27:50.240
it's usually the database that you need to scale, I find.
00:27:50.520 --> 00:27:51.940
But yeah, that's how we run it.
00:27:52.180 --> 00:27:53.940
The nice thing for me about Hypercorn
00:27:54.280 --> 00:27:55.700
is that I can play with HTTP 3.
00:27:56.040 --> 00:27:57.320
So that's what we're doing at times.
00:27:57.640 --> 00:27:58.780
Oh, HTTP 3, okay.
00:27:59.380 --> 00:28:01.240
I've just been getting my HTTP 2 game down,
00:28:01.780 --> 00:28:02.980
so I'm already behind the game.
00:28:03.860 --> 00:28:04.820
What's the deal with HTTP/3?
00:28:05.080 --> 00:28:08.400
It's obviously a totally new way of doing it over UDP now
00:28:08.720 --> 00:28:09.520
rather than TCP.
00:28:10.340 --> 00:28:11.720
Although at the application level,
00:28:11.960 --> 00:28:13.180
you can't tell any difference really.
00:28:13.460 --> 00:28:15.480
But I mean, I just find it interesting.
00:28:15.660 --> 00:28:17.580
I'm not really sure it will help too much.
00:28:17.920 --> 00:28:21.140
And it's probably best if you've got users who have not that
00:28:21.340 --> 00:28:22.100
great a network connection.
00:28:22.600 --> 00:28:25.840
But for most other cases, I don't think it matters too much.
00:28:25.880 --> 00:28:29.140
Just keep blasting packets until some of them get through.
00:28:29.940 --> 00:28:30.320
OK, fine.
00:28:30.320 --> 00:28:31.200
We'll give you a page eventually.
00:28:31.380 --> 00:28:32.260
There's three pages, actually.
00:28:32.620 --> 00:28:43.300
All right, Sebastian, you are running not just FastAPI from your experience, but you're running FastAPI for a ton of people through FastAPI Cloud at, I'm sure, many different levels.
00:28:43.580 --> 00:28:47.660
This probably sounds like a shameless plug, and it kind of is, but it's sort of expected.
00:28:48.120 --> 00:28:50.980
I will deploy FastAPI or FastAPI Cloud.
00:28:51.520 --> 00:28:54.760
Just because, well, the idea is just to make it super simple to do that.
00:28:54.960 --> 00:28:58.440
You know, like if you are being able to run the command FastAPI run.
00:28:58.820 --> 00:29:01.540
So FastAPI run has like the production server
00:29:01.600 --> 00:29:03.020
that is using Uvicorn underneath.
00:29:03.440 --> 00:29:04.680
And if you can run that,
00:29:04.800 --> 00:29:06.420
then you can run also FastAPI deploy.
00:29:06.740 --> 00:29:10.160
And then like, you know, like it will most probably just work.
00:29:10.600 --> 00:29:13.260
And, you know, we just wrap everything and like deploy,
00:29:13.740 --> 00:29:16.580
build, install, deploy, handle HTTPS, all the stuff
00:29:17.020 --> 00:29:19.100
without needing any Docker file or anything like that.
00:29:19.320 --> 00:29:20.720
And I think for many use cases,
00:29:20.900 --> 00:29:22.880
it's just like simpler being able just to do that.
00:29:23.100 --> 00:29:25.280
There are so many projects that I have been now building,
00:29:25.520 --> 00:29:29.660
like random stuff that is not really important, but now I can.
00:29:30.220 --> 00:29:34.580
And before it was like, yeah, well, I know how to deploy this thing like fully with like
00:29:34.700 --> 00:29:37.380
all the bells and whistles, but it's just so much work that yeah, I know later.
00:29:38.000 --> 00:29:40.700
So for that, I would end up just like going with that.
00:29:41.160 --> 00:29:42.140
Now if I didn't...
00:29:42.140 --> 00:29:46.580
Well, what I was going to ask is how much are you willing to tell us how things run inside
00:29:47.140 --> 00:29:47.940
FastAPI Cloud?
00:29:48.280 --> 00:29:52.180
Oh, I can't, it's just so much stuff that is going on.
00:29:52.200 --> 00:29:56.780
And it's also, it's fun that nowadays that they're like,
00:29:57.340 --> 00:30:00.380
we have Docker and we have Docker Swarm and there was Nomad
00:30:00.540 --> 00:30:02.720
and Kubernetes and oh, Kubernetes won.
00:30:02.810 --> 00:30:05.940
And then we have the cloud providers and there's AWS
00:30:06.460 --> 00:30:07.840
and Google and Azure.
00:30:08.290 --> 00:30:10.800
And you will expect that all these things
00:30:11.120 --> 00:30:14.360
and all this complexity is like, now that it's like, okay,
00:30:14.540 --> 00:30:15.420
these are the clear winners.
00:30:15.640 --> 00:30:17.400
So it's like a lot of complexity to take on,
00:30:17.470 --> 00:30:20.980
but once you do it all works, but it doesn't.
00:30:21.200 --> 00:30:27.360
And it's just like so much work to get things to work together, to work correctly.
00:30:27.920 --> 00:30:32.380
And the official resources from the different providers and things,
00:30:32.750 --> 00:30:36.540
in many cases, it's like, oh, the solution is hidden in this issue somewhere in GitHub
00:30:36.700 --> 00:30:38.240
because the previous version was obsolete,
00:30:38.460 --> 00:30:43.180
but now the new version of this package or whatever is like, it's just, it's crazy.
00:30:43.620 --> 00:30:50.400
But like, yeah, so if I didn't have FastAPI Cloud, I will probably use containers.
00:30:50.660 --> 00:30:51.460
I will probably use Docker.
00:30:51.940 --> 00:30:52.980
If it's like something simple,
00:30:53.030 --> 00:30:54.860
I will deploy with Docker Compose,
00:30:55.000 --> 00:30:57.400
probably try to scale minimum replicas.
00:30:57.920 --> 00:30:59.840
I don't remember Docker Compose has that.
00:30:59.940 --> 00:31:01.840
I remember that Docker Swarm had that,
00:31:01.940 --> 00:31:04.900
but then Docker Swarm sort of lost against Kubernetes.
00:31:05.420 --> 00:31:08.320
I will put a traffic load balancer in front
00:31:08.390 --> 00:31:11.840
to handle HTTPS and, yeah, well, like regular load balancing.
00:31:12.970 --> 00:31:14.700
And, yeah, just regular Uvicorn.
00:31:14.860 --> 00:31:17.800
What some of the folks we were talking about before,
00:31:18.440 --> 00:31:22.200
At some point, we needed to have Gunicorn on top of Uvicorn
00:31:22.300 --> 00:31:24.580
because Uvicorn wouldn't be able to handle workers.
00:31:25.040 --> 00:31:27.240
But now Uvicorn can handle its workers like everything
00:31:27.500 --> 00:31:30.400
and handle the main thing was some VE processes
00:31:30.600 --> 00:31:33.980
and reaping the processes and handling the stuff.
00:31:34.300 --> 00:31:35.360
Now it can't just do that.
00:31:35.380 --> 00:31:37.460
So you can just run plain Uvicorn.
00:31:37.740 --> 00:31:40.100
So if you're using FastAPI and you say FastAPI run,
00:31:40.560 --> 00:31:41.520
that already does that.
00:31:41.660 --> 00:31:42.920
So if you're deploying on your own,
00:31:42.960 --> 00:31:44.580
you can just use the FastAPI run command.
00:31:44.940 --> 00:31:46.940
Then, of course, you have to deal with the scaling
00:31:47.160 --> 00:31:48.920
and HTTPS and a lot of balancing
00:31:48.970 --> 00:31:49.600
and all the stuff,
00:31:49.700 --> 00:31:51.620
but the core server,
00:31:51.750 --> 00:31:53.260
you can just run it directly.
00:31:53.640 --> 00:31:54.980
If going beyond that,
00:31:55.080 --> 00:31:56.040
then there will probably be
00:31:56.290 --> 00:31:57.820
some cluster Kubernetes
00:31:58.560 --> 00:31:59.760
and trying to scale things,
00:32:00.120 --> 00:32:01.760
figure out the ways to scale things
00:32:02.420 --> 00:32:04.460
based on the load of the requests,
00:32:05.950 --> 00:32:06.940
like scaling automatically.
00:32:08.020 --> 00:32:10.580
Having normally one container per process
00:32:10.900 --> 00:32:12.720
to be able to scale that more dynamically
00:32:13.000 --> 00:32:14.260
without depending on the local memory
00:32:14.400 --> 00:32:15.200
for each one of the servers
00:32:15.270 --> 00:32:15.960
and things like that,
00:32:16.260 --> 00:32:17.300
I'm probably saying too much.
00:32:17.640 --> 00:32:20.840
But yeah, actually, you know, like if I didn't have a CPI cloud,
00:32:20.980 --> 00:32:26.940
I will probably use one of the providers that abstract those things a little bit away,
00:32:27.060 --> 00:32:31.100
you know, like render, railway, fly, like, I don't know.
00:32:31.260 --> 00:32:36.280
Like, I don't really think that a regular developer should be dealing with,
00:32:36.280 --> 00:32:39.140
you know, like the big hyperscalers and like Kubernetes
00:32:39.360 --> 00:32:42.560
and like all that complexity for a common app.
00:32:42.760 --> 00:32:47.360
Most of the cases, I think it's just really too much complexity to real with.
00:32:47.440 --> 00:32:51.940
It's kind of eye-watering to open up the AWS console or Azure or something.
00:32:52.310 --> 00:32:52.440
Whoa.
00:32:52.980 --> 00:32:59.000
Oh, the other day, you know, like the other day I had to, in one of the AWS accounts, I had to change the account email.
00:32:59.500 --> 00:33:00.840
I think I spent four hours.
00:33:01.080 --> 00:33:01.420
I know.
00:33:01.640 --> 00:33:05.820
Because I had to create the delegate account that has the right permissions to roll.
00:33:05.960 --> 00:33:08.140
And they're like, oh, no, this is, you know, like,
00:33:08.920 --> 00:33:11.520
sometimes it's just overwhelming the amount of complexity
00:33:11.820 --> 00:33:12.920
that needs to be dealt with.
00:33:13.660 --> 00:33:16.280
And, yeah, I mean, it's great to really have, like,
00:33:16.440 --> 00:33:20.300
you know, like the infra people that I have working with me
00:33:20.300 --> 00:33:22.780
at the company that I can deal with all that mess
00:33:22.920 --> 00:33:26.020
and, like, can make sure that everything is just running perfectly
00:33:26.220 --> 00:33:27.280
and it just works.
00:33:27.440 --> 00:33:30.100
So it's like, you know, like, sort of SRE as a service,
00:33:30.520 --> 00:33:32.440
DevOps as a service for everyone.
00:33:32.860 --> 00:33:35.600
It's like a cloud product that provides DevOps as a service,
00:33:36.620 --> 00:33:41.700
I spent a number of years doing nothing but cloud migrations to these hyperscalers for enterprises.
00:33:42.420 --> 00:33:47.460
And I can tell you that when you mentioned the eye-watering comment about the network and all that stuff,
00:33:48.260 --> 00:33:49.760
it's so incredibly complicated now, right?
00:33:49.940 --> 00:33:55.000
There's literally every kind of concept that you need to know to deploy these enterprises now,
00:33:55.180 --> 00:33:56.700
move them from on-prem to the cloud.
00:33:56.900 --> 00:33:58.640
So it does get incredibly complicated.
00:33:58.770 --> 00:34:02.680
Having something simple like what Sebastian is talking about, I think, is super helpful
00:34:02.700 --> 00:34:04.380
when you're just trying to get started
00:34:04.380 --> 00:34:06.060
and get something up and running quickly.
00:34:06.240 --> 00:34:07.140
I've got a lot of questions
00:34:07.340 --> 00:34:09.899
and I realize that we will not be getting through all of them.
00:34:10.040 --> 00:34:12.440
So I want to pick carefully.
00:34:12.720 --> 00:34:14.840
So let's do this one next.
00:34:15.220 --> 00:34:18.820
Performance, what's your best low effort tip?
00:34:18.960 --> 00:34:20.899
Not like something super complicated,
00:34:21.360 --> 00:34:23.679
but I know there's a bunch of low hanging fruit
00:34:23.840 --> 00:34:25.540
that people maybe missed out on.
00:34:26.020 --> 00:34:27.760
And this time let's start with Litestar.
00:34:28.159 --> 00:34:29.240
Cody, back at you.
00:34:29.480 --> 00:34:30.760
I'm going to stick to what I know,
00:34:30.820 --> 00:34:32.520
which is databases because I deal with that.
00:34:32.580 --> 00:34:38.060
every single day. There's a couple of things that I see as like gotchas that I constantly see over
00:34:38.060 --> 00:34:45.240
and over. One, SQLAlchemy kind of obfuscates the way it's going to execute things and what kind of
00:34:45.379 --> 00:34:49.899
queries it's going to actually execute. So it's really easy if you're not kind of fluent in how
00:34:49.909 --> 00:34:55.360
it works to create N plus one types of issues. And so when people start talking about sync or async,
00:34:55.500 --> 00:34:59.280
it's really, in my mind, it's less of that because you're going to spend more time waiting on the
00:34:59.300 --> 00:35:04.200
network and database and those kind of things, then you're going to spend serializing just
00:35:04.500 --> 00:35:09.920
generally, right? And or processing things on the web framework. And so, one, making sure that you,
00:35:10.060 --> 00:35:15.340
your relationships dialed in correctly so that you don't have N plus one queries. The other thing is
00:35:15.560 --> 00:35:21.260
oversized connection pooling into Postgres and just databases in general, because what people don't
00:35:21.380 --> 00:35:26.240
tend to know is that each of those connections takes up CPU cycles and RAM of the database.
00:35:26.520 --> 00:35:33.360
And so when you slam the database with hundreds of connections, you're just taking away processing power that can be done for other things, right?
00:35:33.490 --> 00:35:35.740
And so you end up ultimately slowing things down.
00:35:35.830 --> 00:35:44.100
So I've seen databases that have had so many connections that all of the CPU and all the stuff is actually doing things, just managing connections and can't actually do any database work.
00:35:44.270 --> 00:35:45.460
And so what about this socket?
00:35:45.620 --> 00:35:46.180
Is it busy?
00:35:46.260 --> 00:35:46.800
What about this socket?
00:35:46.880 --> 00:35:47.260
Is it busy?
00:35:47.340 --> 00:35:48.580
It's just round robin that, right?
00:35:48.940 --> 00:35:51.980
Paying attention to the database is kind of my first kind of rule of thumb.
00:35:52.120 --> 00:35:52.480
100%.
00:35:52.610 --> 00:35:53.660
I like that one a lot.
00:35:53.700 --> 00:36:01.660
I'll throw in putting stuff or identifying work that doesn't need to be done immediately for the user and putting in a background task.
00:36:02.090 --> 00:36:04.700
Having a background worker defer things till later.
00:36:05.190 --> 00:36:10.060
So sending email is an example, although there's nuances there about knowing that it's sent and everything.
00:36:10.380 --> 00:36:20.460
But yeah, if you user kicks off some process and then you wait to do that process in the worker, you're holding that worker up, which is more relevant in WSGI than ASGI.
00:36:20.540 --> 00:36:26.300
but and you're making them wait for their page to load again versus record what they wanted to do
00:36:26.500 --> 00:36:30.680
send it off to the background let them see the status of it but let the background worker handle
00:36:30.700 --> 00:36:35.260
it all right yeah like i said as you guys go for it i'm not sure if that's some sort of
00:36:35.420 --> 00:36:41.380
it's not really a trick or a tip or more more like a i think the most common mistake i see when i
00:36:41.560 --> 00:36:46.200
is ASGI specific but when i look at ASGI apps that people have written who are maybe not as
00:36:46.220 --> 00:36:52.760
familiar with ASGI or async Python at all, if you make something an async function, you should be
00:36:53.260 --> 00:36:59.220
absolutely sure that it's non-blocking. Because if you're running an ASGI app and you're blocking
00:36:59.920 --> 00:37:04.660
anywhere, your whole application server is blocked completely. It doesn't handle any other requests
00:37:04.680 --> 00:37:11.839
at the same time. It's blocked. I don't think I've had any mistake more times when I've looked through
00:37:11.840 --> 00:37:18.820
some apps that someone has written or that i've came across somewhere so this is really it's super
00:37:18.980 --> 00:37:26.280
super common and it has such a such a big impact on the overall performance in every every metric
00:37:26.880 --> 00:37:33.200
imaginable so i would say unless and that's nowadays what i tell people unless you're 100
00:37:33.440 --> 00:37:39.280
sure that you know what you're doing and you know it's it's non-blocking don't make it async put it
00:37:39.120 --> 00:37:41.700
in a thread pool, execute it in a thread, whatever.
00:37:42.290 --> 00:37:44.580
All of the ASGI frameworks and Django
00:37:45.130 --> 00:37:48.840
give you a lot of tools at hand to translate your stuff
00:37:49.520 --> 00:37:52.080
to from sync to async so you can still run it.
00:37:52.240 --> 00:37:56.600
Do that unless you're very sure that it actually fully supports
00:37:57.080 --> 00:37:57.260
async.
00:37:57.290 --> 00:37:58.340
MARK MANDEL: Yeah, that's good advice.
00:37:58.700 --> 00:37:59.120
Sebastian.
00:37:59.650 --> 00:38:02.120
SEBASTIAN BASTIAN: Hey, I'm actually going to second, Yannick.
00:38:02.800 --> 00:38:04.620
I think, yeah, like it's--
00:38:04.960 --> 00:38:08.020
and it's maybe counterintuitive that one
00:38:08.040 --> 00:38:12.880
of the tips of performance is to try to not optimize that much performance at the beginning.
00:38:13.340 --> 00:38:17.740
You know, like, I think the idea with async is like, oh, you can get so much performance
00:38:17.980 --> 00:38:19.960
and throughput in terms of accuracy, whatever.
00:38:20.360 --> 00:38:26.720
But the thing is, in most of the cases, you know, like, till apps grow so large, they
00:38:26.900 --> 00:38:30.420
actually don't need that much extra throughput, that much extra performance.
00:38:30.920 --> 00:38:35.860
And in a framework like, you know, like, as Yannick was saying, well, in my case, I know
00:38:35.880 --> 00:38:38.080
FastAPI, but like, you know, like also many others.
00:38:38.520 --> 00:38:41.320
If you define the function with async, it's going to be run async.
00:38:41.400 --> 00:38:43.840
If you define it non-async and regular def,
00:38:44.140 --> 00:38:46.400
it's going to be run on a thread worker automatically.
00:38:46.960 --> 00:38:50.440
So it's just going to do the smart thing automatically.
00:38:50.700 --> 00:38:53.880
So it's like fair, you know, like it's going to be good enough.
00:38:54.340 --> 00:38:58.300
And then you can just start with that and just keep blocking code everywhere.
00:38:58.380 --> 00:39:01.340
You know, like just not use async until you actually know
00:39:01.480 --> 00:39:03.280
that you really need to use async.
00:39:03.900 --> 00:39:05.720
And once you do, you have to be, as Yannick was saying,
00:39:05.780 --> 00:39:10.900
you know, like 100% sure that you are not running blocking code inside of it.
00:39:11.220 --> 00:39:14.420
And if you need to run blocking code inside of Async code,
00:39:14.650 --> 00:39:17.360
then make sure that you are sending it to a thread worker.
00:39:17.680 --> 00:39:20.420
Sending it to a thread worker sounds the own thing,
00:39:20.620 --> 00:39:22.420
but yeah, like, you know, like Django has tools,
00:39:23.000 --> 00:39:23.720
any IO has tools.
00:39:23.960 --> 00:39:27.000
I also built something on top of any IO called asyncer,
00:39:27.200 --> 00:39:29.100
that is just to simplify these things,
00:39:29.300 --> 00:39:31.000
to asyncify a blocking function,
00:39:31.440 --> 00:39:33.160
keeping all the type information
00:39:33.360 --> 00:39:35.460
so that you get autocompletion and inline errors and everything.
00:39:35.860 --> 00:39:37.600
even though it's actually doing all the stuff
00:39:37.760 --> 00:39:40.320
of sending the thing to the thread work.
00:39:40.760 --> 00:39:41.980
So the code is super simple.
00:39:42.070 --> 00:39:43.380
You keep very simple code,
00:39:43.410 --> 00:39:44.940
but then underneath it's just like doing
00:39:45.340 --> 00:39:46.360
all the stuff that should be done.
00:39:46.700 --> 00:39:48.000
But you know, like that's normally
00:39:48.160 --> 00:39:50.720
when you actually need to hyper-optimize things.
00:39:51.040 --> 00:39:51.800
In most of the cases,
00:39:51.910 --> 00:39:54.940
you can just start with just not using async at first.
00:39:55.400 --> 00:39:57.800
Also, now that you're going to have Python multi-threaded,
00:39:58.120 --> 00:39:59.760
then suddenly you're going to have
00:40:00.000 --> 00:40:02.800
just so much more performance out of the blue
00:40:02.910 --> 00:40:04.840
without even having to do much more.
00:40:05.180 --> 00:40:12.220
So, yeah, actually that's, you know, like, sorry, I kept speaking so much, but here's a tip for improving performance.
00:40:12.860 --> 00:40:14.220
Upgrade your Python version.
00:40:14.910 --> 00:40:17.220
I was just chatting today with Savannah.
00:40:17.760 --> 00:40:28.440
She was adding the benchmarks to the, you know, like Python benchmark, the official Python benchmarks that they run for the CPython, the faster CPython program.
00:40:29.140 --> 00:40:35.540
And the change from Python 3.10 to Python 3.14
00:40:36.060 --> 00:40:39.800
when running FastAPI is like almost double the performance
00:40:40.020 --> 00:40:40.660
or something like that.
00:40:40.940 --> 00:40:42.060
It was like, it was crazy.
00:40:42.220 --> 00:40:44.640
It was just crazy improvement in performance.
00:40:44.840 --> 00:40:46.520
So you can just upgrade your Python version.
00:40:46.660 --> 00:40:50.320
You're gonna get so much better performance just out of that.
00:40:50.400 --> 00:40:52.000
- Yeah, that's an awesome piece of advice
00:40:52.140 --> 00:40:53.540
that I think is often overlooked.
00:40:53.980 --> 00:40:55.480
And it's not only CPU speed,
00:40:55.680 --> 00:40:57.180
it's also memory gets a lot lower.
00:40:57.600 --> 00:40:58.420
Whoever's gonna jump in, go ahead.
00:40:58.580 --> 00:41:03.840
Last year, I was looking at MarkupSafe, which is an HTML escaping library that we use and
00:41:04.230 --> 00:41:05.700
has a C extension for speedups.
00:41:05.770 --> 00:41:11.460
And I almost convinced myself that I can stop maintaining the C extension because just Python
00:41:11.720 --> 00:41:12.780
itself got way faster.
00:41:13.050 --> 00:41:16.700
But then it turned out that I could do something to the C extension to make it faster also.
00:41:17.110 --> 00:41:17.780
So I'm still maintaining.
00:41:18.100 --> 00:41:23.140
But just the fact that I almost convinced myself like, oh, I can drop a C extension for just
00:41:23.140 --> 00:41:26.160
a Python upgrade instead was pretty impressive.
00:41:26.740 --> 00:41:29.320
They've done a lot, especially with like string handling
00:41:29.400 --> 00:41:30.840
and, you know, which you're going to use
00:41:30.920 --> 00:41:32.360
for templating for web apps.
00:41:32.660 --> 00:41:32.780
Phil.
00:41:32.920 --> 00:41:36.020
Yeah, well, I definitely echo looking at your DB queries
00:41:36.300 --> 00:41:38.260
because by far and large, that's always where
00:41:38.640 --> 00:41:39.720
our performance issues have been.
00:41:39.800 --> 00:41:41.920
It's either badly written query or we're returning
00:41:42.180 --> 00:41:43.920
most of the database when the user just wants to know
00:41:44.020 --> 00:41:45.800
about one thing or something silly like that.
00:41:46.240 --> 00:41:47.520
I was thinking about low-hanging ones,
00:41:47.620 --> 00:41:48.440
which I think you asked about.
00:41:48.740 --> 00:41:52.560
So I'd say uvloop, which is still a noticeable improvement.
00:41:53.740 --> 00:41:58.720
And also, because I think it's likely a lot of us are returning JSON, often changing the
00:41:59.100 --> 00:42:04.080
JSON serializer to one of the faster ones can be noticeable as well and obviously quite easy to do.
00:42:04.330 --> 00:42:05.640
So yeah, that's my key.
00:42:05.760 --> 00:42:06.460
That's really good advice.
00:42:06.570 --> 00:42:08.360
I didn't think about the JSON serializer.
00:42:08.820 --> 00:42:09.560
What one do you recommend?
00:42:09.900 --> 00:42:11.040
I think, is it ujson?
00:42:11.420 --> 00:42:12.280
Or is it orjson?
00:42:12.860 --> 00:42:14.200
I can't remember which one was deprecated.
00:42:15.880 --> 00:42:21.180
But yeah, if you look at the TechEmpower benchmarks, everyone's changing the JSON serializer
00:42:21.310 --> 00:42:22.540
to get that bit extra speed.
00:42:22.860 --> 00:42:27.440
But yeah, you're like, our framework looks bad because our JSON serializer is like third
00:42:27.470 --> 00:42:28.020
of the performance.
00:42:28.270 --> 00:42:31.680
We changed, well, David added a JSON provider to Flask.
00:42:31.880 --> 00:42:35.120
And yeah, you could see it make a difference in the tech and power benchmarks.
00:42:35.400 --> 00:42:35.960
So that was really good.
00:42:36.060 --> 00:42:36.360
Yeah, cool.
00:42:36.540 --> 00:42:37.600
Yeah, it's pluggable now.
00:42:37.860 --> 00:42:42.880
But if you're not installing Flask or orjson, I mean, I don't know what other JSON library
00:42:43.000 --> 00:42:44.780
you'd be using at this point, unless you're already using one.
00:42:45.140 --> 00:42:47.240
But orjson is very, very fast.
00:42:47.520 --> 00:42:49.200
Okay, this is something I'm going to be looking at you later.
00:42:49.660 --> 00:42:57.440
So over to Django, Jeff, David talked about running stuff in the background and was it Django 5 or Django 6 that got the background task thing?
00:42:57.640 --> 00:42:59.640
Yeah, Django 6 just came out a couple of weeks ago.
00:43:00.200 --> 00:43:07.220
And I'll hand that off to Carlton in a second because I think Carlton's had more to do with the actual plumbing being on the steering council.
00:43:07.620 --> 00:43:11.860
My advice to people is the best way to scale something is just to not do it, avoid the process completely.
00:43:12.040 --> 00:43:16.000
So like I mentioned to CDN earlier, it's content heavy sites, cache the crap out of stuff.
00:43:16.500 --> 00:43:17.540
It doesn't even have to hit your servers.
00:43:17.960 --> 00:43:22.020
You can go a lot, as we mentioned earlier, too, just by doubling the amount of resources a project has.
00:43:22.400 --> 00:43:24.840
Django is pretty efficient these days, especially with async views.
00:43:25.290 --> 00:43:31.500
Like everybody else has said, too, any blocking code, move off to threads, move off to a background queue.
00:43:31.820 --> 00:43:35.140
Django Q2 is my favorite one to use because you can use a database.
00:43:35.780 --> 00:43:39.580
So for those little side projects where you just want to run one or two processes, you can use it.
00:43:39.580 --> 00:43:40.040
It works great.
00:43:40.580 --> 00:43:43.160
And Carlton, if you want to talk about Django internals.
00:43:43.260 --> 00:43:43.740
Yeah, OK.
00:43:43.900 --> 00:43:51.120
So the new task framework I just mentioned, the main thing, the main sort of bit about it is that it's, again, this pluggable Django API.
00:43:51.360 --> 00:43:53.080
So it gives a standard task API.
00:43:53.220 --> 00:43:57.120
So if you're writing a third-party library and you, I know, you need to send an email.
00:43:57.540 --> 00:43:58.820
It's the canonical example, right?
00:43:58.820 --> 00:44:00.880
You need to send an email in your third-party library.
00:44:01.320 --> 00:44:07.780
Before, you'd have had to tie yourself to a specific queue implementation, whereas now Django is providing a kind of like an ORM of tasks.
00:44:07.820 --> 00:44:08.180
Right, right.
00:44:08.240 --> 00:44:11.780
You got to do Redis, you got to do Celery, and you got to manage things and all that.
00:44:11.840 --> 00:44:14.820
You don't have to pick that now as the third-party package author.
00:44:14.900 --> 00:44:16.420
You can just say, right, just use Django,
00:44:17.080 --> 00:44:18.640
wrap this as a Django task and queue it.
00:44:18.940 --> 00:44:22.780
And then the developer, when they come to choose their backend,
00:44:22.880 --> 00:44:24.940
if they want to use Celery or they want to use Django Q2
00:44:25.060 --> 00:44:26.720
or they want to use the Django task backend,
00:44:26.900 --> 00:44:30.440
which Jake Howard, who wrote this for Django provided as well,
00:44:30.800 --> 00:44:32.040
you can just plug that in.
00:44:32.300 --> 00:44:34.700
So it's a pluggable interface for tasks,
00:44:34.980 --> 00:44:37.240
which is, I think, the really nice thing about it.
00:44:37.600 --> 00:44:40.700
In terms of quick wins, everybody's mentioned almost all of mine.
00:44:40.840 --> 00:44:43.420
I'm going to, Cody and Phil, they mentioned the database.
00:44:43.920 --> 00:44:44.600
That's the big one.
00:44:44.800 --> 00:44:48.280
Django, the ORM, because it does lazy related lookups,
00:44:48.420 --> 00:44:51.180
it's very easy to trigger in M plus one where, you know,
00:44:51.860 --> 00:44:55.000
the book has multiple authors and suddenly you're iterating through the books
00:44:55.160 --> 00:44:57.120
and you're iterating through the authors and it's a lookup.
00:44:57.640 --> 00:45:00.360
So you need to do things like prefetch related, select related.
00:45:00.470 --> 00:45:02.060
You need to just check that you've got those.
00:45:02.240 --> 00:45:04.880
Django debug toolbars are a great thing to run in development
00:45:05.040 --> 00:45:07.700
where you can see the queries and it'll tell you where you've got the duplicates.
00:45:08.280 --> 00:45:11.100
And then the slightly bigger one is to just check your indexes.
00:45:11.350 --> 00:45:14.300
The ORM will create the right indexes if you're leaning,
00:45:14.570 --> 00:45:16.520
if you're going through primary keys or unique fields.
00:45:16.590 --> 00:45:19.220
But sometimes you're doing a filter on some field,
00:45:19.800 --> 00:45:21.460
and then there's not the right index there,
00:45:21.460 --> 00:45:22.540
and that can really slow you down.
00:45:22.590 --> 00:45:26.460
So again, you can do the SQL explain on that and find that.
00:45:26.860 --> 00:45:29.820
And then the thing I was going to say originally was caching,
00:45:30.160 --> 00:45:34.480
is get a Redis instance, stick it next to your Django app,
00:45:34.530 --> 00:45:36.620
and as Jeff said, don't do the work.
00:45:36.740 --> 00:45:40.000
If you're continually rendering the same page and it never changes,
00:45:40.620 --> 00:45:42.980
cache it and pull it from the cache rather than rendering.
00:45:43.120 --> 00:45:45.460
Because template DB queries are one of your biggest things.
00:45:45.560 --> 00:45:47.500
The second one's always going to be serialization.
00:45:47.620 --> 00:45:49.300
It's either serialization or template rendering.
00:45:49.620 --> 00:45:53.760
So if you can avoid that by caching, you can save an awful lot of time on your account.
00:45:53.780 --> 00:45:53.900
Yeah.
00:45:54.360 --> 00:45:56.740
I was wondering if somebody would come back with database indexes,
00:45:56.900 --> 00:46:00.900
because that's like a 100x multiplier for free almost.
00:46:01.200 --> 00:46:03.040
It's such a big deal.
00:46:03.160 --> 00:46:03.860
It really can be.
00:46:03.900 --> 00:46:06.700
If you're making a particular query and it's doing a full table scan
00:46:06.720 --> 00:46:11.400
all of a sudden you put the index in, it's instant. It's like, oh, wow. You don't have to be a DBA or
00:46:12.619 --> 00:46:16.400
master information architect sort of thing. I don't know about Postgres. I'm sure it has it.
00:46:16.540 --> 00:46:21.720
Somebody can tell me. But with Mongo, you can turn on in the database, I want you to log all
00:46:21.940 --> 00:46:27.060
slow queries and slow for me means 20 millisecond or whatever. Like you put a number in and then
00:46:27.660 --> 00:46:31.620
you run your app for a while and you go, look at what's slow by slowest. And then you can see,
00:46:31.640 --> 00:46:35.220
well, maybe that needs an index, right? Like just let your app tell you what you got to do.
00:46:35.500 --> 00:46:36.840
Yeah, there is a post.
00:46:37.040 --> 00:46:38.740
I'm just trying to see if I can quickly look it up now.
00:46:38.860 --> 00:46:40.060
There's a Postgres extension,
00:46:40.390 --> 00:46:42.000
which will automatically run explain
00:46:42.680 --> 00:46:44.540
on the slow queries and log them for you.
00:46:44.680 --> 00:46:45.360
So it'll...
00:46:45.400 --> 00:46:45.660
There you go.
00:46:46.400 --> 00:46:47.080
See if I can find...
00:46:47.080 --> 00:46:48.240
It's pg_stat_statements, I think,
00:46:48.240 --> 00:46:49.020
is what you're thinking about.
00:46:49.160 --> 00:46:49.500
Right, okay.
00:46:49.700 --> 00:46:52.280
If you're unsure about your database indexes,
00:46:52.620 --> 00:46:55.340
do this, or at least go back and review your queries.
00:46:55.640 --> 00:46:56.240
Yeah, I agree.
00:46:56.590 --> 00:46:56.780
Very good.
00:46:57.240 --> 00:46:59.220
All right, I can see we're blazing through these questions.
00:47:00.060 --> 00:47:00.820
I had one more.
00:47:01.120 --> 00:47:01.960
If I can mention one.
00:47:01.960 --> 00:47:02.540
No, please go ahead.
00:47:02.730 --> 00:47:03.260
Yeah, go ahead, David.
00:47:03.420 --> 00:47:27.740
If you want to like get some more responsive parts of your website, like make your website a little more responsive or interactive with the user, HTMX or Datastar, especially like if you're using Quart or another ASGI where you can do SSE, server sent events or WebSockets, streaming little bits of changes to the web front end and then rendering them with the same HTML you're already writing can make things a lot more responsive.
00:47:28.240 --> 00:47:33.240
We had talk about that from Chris May at FlaskCon last year, which you can find on YouTube.
00:47:33.540 --> 00:47:38.280
This is not one of the questions, but let me just start out for a quick riff on this, folks.
00:47:38.980 --> 00:47:41.380
Out in the audience, someone was asking, what about HTMX?
00:47:41.800 --> 00:47:47.860
And I think more broadly, I am actually a huge fan of server-side-based, template-based apps.
00:47:48.060 --> 00:47:51.380
I think it just keeps things simpler in a lot of ways, unless you need a lot of interactivity.
00:47:51.740 --> 00:47:57.520
But things like HTMX or a little bit of JavaScript can reduce a lot of the traffic and stuff.
00:47:57.580 --> 00:47:59.160
Where do people land on those kinds of things?
00:47:59.360 --> 00:48:05.760
I absolutely love HTMX, not just because you don't have to write a lot of JavaScript or whatever,
00:48:06.300 --> 00:48:14.040
but mostly because if I'm just building a simple app that needs a bit more than just be a static HTML page,
00:48:14.040 --> 00:48:17.840
it needs some interactivity, a little bit of reactivity.
00:48:18.380 --> 00:48:34.160
I feel like having the whole overhead of building an SPA or whatever tools you need for the whole JavaScript, TypeScript, whatever stack, it's just so much work to get a little bit to make a simple thing a little bit nicer, a little bit more reactive.
00:48:34.740 --> 00:48:37.540
And I feel like HTMX just fits right in there.
00:48:37.700 --> 00:48:39.420
It's super great.
00:48:39.520 --> 00:48:41.340
I've built a couple of things with it now,
00:48:41.960 --> 00:48:44.760
a few of my own projects, a few things that work.
00:48:45.280 --> 00:48:47.460
And it makes things so much easier
00:48:47.720 --> 00:48:50.480
where the work probably wouldn't have been done
00:48:50.570 --> 00:48:52.320
if it was just because it's too much.
00:48:52.420 --> 00:48:55.000
If you're doing a whole front end thing
00:48:55.140 --> 00:48:57.120
that you have then to deploy and build and whatever,
00:48:57.520 --> 00:48:59.180
or it would have been less nice.
00:48:59.640 --> 00:49:02.420
So it's an amazing, really amazing thing.
00:49:02.520 --> 00:49:10.460
As the maintainer and author, though, one of the things that is not frustrating, but it's understandable is that HTMX is not for everybody, right?
00:49:10.660 --> 00:49:15.020
There's not like you can't use HTMX in all occasions or Datastar, right?
00:49:15.090 --> 00:49:20.180
And so there are people that are always going to want to use React and there's going to be people that want to use all these other frameworks.
00:49:20.290 --> 00:49:24.120
And so having some cohesive way to make them all talk together, I think, is important.
00:49:24.520 --> 00:49:29.800
I don't have that answer yet, but I just know that like I can't always say HTMX is it, right?
00:49:29.940 --> 00:49:33.840
And then you'll have a great time because I'll inevitably meet somebody that says I need to do this.
00:49:33.910 --> 00:49:37.900
And it's right. And it's in a single page application or something is more appropriate for that.
00:49:37.950 --> 00:49:41.020
And so it's obviously the right tool for the right job when you need it.
00:49:41.100 --> 00:49:45.560
But, you know, I want to make something that is cohesive depending on whatever library you want to use.
00:49:45.730 --> 00:49:46.960
I would throw one thing in there, though.
00:49:47.080 --> 00:49:51.160
I would rather somebody start with HTMX than I would start with React if you don't need it.
00:49:51.320 --> 00:49:54.140
Because React can be total overkill. It can be great for some applications.
00:49:54.720 --> 00:49:59.040
But oftentimes the consultant, we see people like having an about page and they throw a React at it.
00:49:59.160 --> 00:49:59.940
Like, why do you need that?
00:50:00.340 --> 00:50:02.040
Like, especially for small things with partials.
00:50:02.240 --> 00:50:03.500
Do you mean you don't want to start with Angular?
00:50:03.780 --> 00:50:05.800
You know, it's fine if you need it,
00:50:06.080 --> 00:50:07.740
but I don't think you really need it.
00:50:07.920 --> 00:50:09.680
Like, introduce tools as you need them.
00:50:10.000 --> 00:50:11.840
Django 6.0 just added template partials,
00:50:12.010 --> 00:50:13.940
and I guess my job here is to hand off to Carlton
00:50:14.080 --> 00:50:15.160
because this is his feature.
00:50:15.320 --> 00:50:16.900
Yeah, I was happy to see that come in there, Carlton.
00:50:17.020 --> 00:50:17.540
Nice job.
00:50:17.860 --> 00:50:18.620
No, it's okay.
00:50:19.230 --> 00:50:19.920
Plug the new feature.
00:50:20.100 --> 00:50:23.180
So, I mean, I stepped down as a fellow in 2023
00:50:24.550 --> 00:50:25.160
into a new business,
00:50:25.560 --> 00:50:28.299
and I read the essay about template fragments
00:50:28.320 --> 00:50:34.180
on the htmx website where it's this like named reusable bits in the in the templates and i was
00:50:34.200 --> 00:50:38.600
like i need that so i built django template partials released as a third-party package and it's now just
00:50:38.630 --> 00:50:44.460
been merged into core for django 6.0 and i have to say about htms it's really changed the way i write
00:50:44.580 --> 00:50:49.020
websites before i was the fellow i used to write mobile applications and do the back do the front
00:50:49.120 --> 00:50:52.600
end of the mobile application then the back end in django using django rest framework and i was
00:50:52.940 --> 00:50:58.280
that's how i got into you know open source was by django rest framework and since starting the
00:50:58.300 --> 00:51:02.780
we're three years in we've hardly got a json endpoint in sight it's like two three four of
00:51:02.780 --> 00:51:08.000
them in the whole application and it's oh it's just a delight again you know you asked me at the
00:51:08.140 --> 00:51:12.440
beginning michael am i having fun yeah i really am having fun and htmx is the reason i do grant
00:51:12.590 --> 00:51:18.400
there are you know these use cases awesome all right let's talk about our last topic and we have
00:51:18.740 --> 00:51:24.300
five minutes ish to do that so we gotta we gotta stay on target quick but let's just go around
00:51:24.540 --> 00:51:29.780
run real quick here we talked about upgrading the python version getting better performance out of
00:51:29.900 --> 00:51:36.660
it i mentioned the lower memory side but i think one of the underappreciated aspects of this you
00:51:36.660 --> 00:51:43.360
know the instagram team did a huge talk you know on it a while ago is the memory that you run into
00:51:43.560 --> 00:51:47.380
when you start to scale out your stuff on the server because you're like oh i want to have four
00:51:47.580 --> 00:51:51.520
workers so i can have more concurrency because of the gills so now you've got four copies of
00:51:51.540 --> 00:51:56.500
everything that you cache in memory and just like the runtime and now you need eight gigs instead of
00:51:56.500 --> 00:52:02.380
what would have been one or who knows right but with free threaded python coming on which i've
00:52:02.380 --> 00:52:07.920
seen a couple of comments in the chat like hey tell us about this like it's we could have true
00:52:08.380 --> 00:52:13.420
concurrency and we wouldn't need to scale as much on the process side i think giving us both better
00:52:13.600 --> 00:52:17.480
performance and the ability to say well you actually have four times less memory so you could
00:52:17.440 --> 00:52:22.660
run smaller servers or whatever what's the free threaded story for all the frameworks carlton
00:52:22.780 --> 00:52:26.860
let's go back to you for do it in reverse i'm really excited about it i don't know how it's
00:52:26.860 --> 00:52:30.940
going to play out but i'm really excited about it all it can do is help django the async story in
00:52:31.040 --> 00:52:36.460
django is nice and mature now uh but still most of it's sync like you know you're still going to
00:52:36.560 --> 00:52:39.960
default the sync you're still going to write your sync views you still got template rendering you
00:52:39.960 --> 00:52:43.880
know django's template template based kind of framework really you're still going to want to
00:52:43.880 --> 00:52:50.180
run things synchronously, concurrently, and proper threads are going to be, yeah, they can't but help.
00:52:50.270 --> 00:52:53.920
I don't know how it's going to roll out. I'll let someone else go because I'm getting locked up.
00:52:53.920 --> 00:52:59.520
Yeah, I just like elaborated that for people out there before we move on is you could set up your
00:52:59.720 --> 00:53:05.100
worker process to say, I want you to actually run eight threads in this one worker process.
00:53:05.760 --> 00:53:10.600
And when multiple requests come in, they could both be sent off to the same worker to be processed.
00:53:10.740 --> 00:53:15.240
And that allows that worker to do more unless the GIL comes along and says, stop, you only
00:53:15.380 --> 00:53:17.060
get to do one thing in threads in Python.
00:53:17.640 --> 00:53:18.960
And all of a sudden, a lot of that falls down.
00:53:19.100 --> 00:53:22.080
This basically uncorks that and makes that easy all of a sudden.
00:53:22.210 --> 00:53:26.120
Even if you yourself are not writing async, your server can be more async.
00:53:26.220 --> 00:53:26.420
Yeah.
00:53:26.450 --> 00:53:31.220
And this is the thing that we found with ASCII is that you dispatch to a, you know, using
00:53:31.420 --> 00:53:36.220
to async or you dispatch it to a thread, a thread pool executor, but Python doesn't run
00:53:36.380 --> 00:53:37.180
that concurrently.
00:53:37.320 --> 00:53:38.880
And so it's like, or in parallel.
00:53:39.100 --> 00:53:42.340
So it's like, ah, it doesn't actually go as fast as you want it to.
00:53:42.580 --> 00:53:44.880
And so you end up wanting multiple processes still.
00:53:45.080 --> 00:53:45.800
All right, let's keep it with Django.
00:53:46.040 --> 00:53:46.680
Jeff, what do you think?
00:53:47.160 --> 00:53:48.520
I'm going to defer to the others on this.
00:53:48.580 --> 00:53:49.480
I have the least thoughts.
00:53:49.760 --> 00:53:51.120
All right, write down the stack, Sebastian.
00:53:51.620 --> 00:53:54.540
Write down the video, not website, web framework.
00:53:55.760 --> 00:53:56.700
I think it's going to be awesome.
00:53:56.880 --> 00:53:58.940
This is going to help so much, so many things.
00:53:59.560 --> 00:54:04.780
The challenge is going to be third-party libraries used by each individual application
00:54:05.140 --> 00:54:06.740
and if they are compatible or not.
00:54:07.020 --> 00:54:08.440
That's where the challenge is going to be.
00:54:08.580 --> 00:54:12.760
But other than that, it's just going to be free extra performance for everyone.
00:54:13.160 --> 00:54:15.540
Just, you know, like just upgrading the version of Python.
00:54:15.720 --> 00:54:16.420
So that's going to be us.
00:54:16.480 --> 00:54:16.620
Cody.
00:54:16.860 --> 00:54:18.400
Yeah, I'm going to echo what Sebastian just said.
00:54:18.460 --> 00:54:21.520
The third party libraries, I think, are going to be the big kind of sticky point here.
00:54:21.720 --> 00:54:23.120
I'm looking forward to seeing what we can do.
00:54:23.280 --> 00:54:24.880
I'm going to kind of hold my thoughts on that.
00:54:24.960 --> 00:54:28.660
Yannick kind of speak a little bit on it because I know that he's looked at msgspec specifically
00:54:28.920 --> 00:54:31.720
and some of the other things that might, you know, give some better context here.
00:54:31.880 --> 00:54:35.880
But yes, the third party libraries are going to be the kind of the sticky issue.
00:54:36.160 --> 00:54:38.720
but I'm looking forward to seeing what we can make happen.
00:54:38.880 --> 00:54:43.200
I'm super excited, actually, specifically about async stuff,
00:54:43.440 --> 00:54:45.580
because for most of the time, it's like,
00:54:46.600 --> 00:54:49.100
if you can already saturate your CPU,
00:54:50.260 --> 00:54:51.240
async doesn't help you much.
00:54:51.760 --> 00:54:53.500
Well, now, if you have proper threads,
00:54:53.560 --> 00:54:56.180
you can actually do that in async as well.
00:54:56.840 --> 00:54:59.880
And I think it's going to speed up a lot of applications
00:55:00.260 --> 00:55:01.140
just by default,
00:55:01.520 --> 00:55:06.120
because almost all async applications out there
00:55:06.260 --> 00:55:11.500
use threads in some capacity because, well, most of things aren't async by nature.
00:55:12.160 --> 00:55:16.300
So they will use a thread pool and it will run more concurrently.
00:55:16.300 --> 00:55:18.360
And so that's going to be better.
00:55:18.840 --> 00:55:25.820
But I'm also a bit scared about a few things that mainly, as a few others have said now,
00:55:26.500 --> 00:55:32.400
third-party libraries extension is specifically those that are Python C extensions.
00:55:33.320 --> 00:55:35.640
just recently, I think like three weeks ago,
00:55:36.140 --> 00:55:40.200
so got a msgspec released for Python 3.14
00:55:40.410 --> 00:55:41.960
and proper free threading support.
00:55:42.620 --> 00:55:44.000
And that took a lot of work.
00:55:44.540 --> 00:55:47.760
Fortunately, a few of the Python core devs
00:55:47.800 --> 00:55:49.840
chimed in and contributed to PRs
00:55:49.920 --> 00:55:51.580
and helped out with that.
00:55:52.300 --> 00:55:54.380
And all around the ecosystem,
00:55:54.490 --> 00:55:55.240
the last few years,
00:55:55.340 --> 00:55:56.560
there's been a lot of work going on.
00:55:57.020 --> 00:56:00.720
But especially for more niche libraries
00:56:01.000 --> 00:56:02.660
that are still here and there,
00:56:02.880 --> 00:56:09.720
I think there's still a lot to do and possibly also quite a few bugs lurking here and there
00:56:09.860 --> 00:56:13.020
that haven't been found or are really hard to track down.
00:56:13.560 --> 00:56:18.380
I'm curious and a bit maybe scarce, too hard of work, but I'm cautious.
00:56:18.820 --> 00:56:21.880
It's going to be a little bit of a bumpy ride as people turn that on
00:56:22.080 --> 00:56:23.840
and then the reality of what's happening.
00:56:24.420 --> 00:56:27.920
However, I want to take Cody's warning and turn it on its head
00:56:28.300 --> 00:56:29.420
about these third-party libraries,
00:56:29.760 --> 00:56:38.820
Because I think it's also an opportunity for regular Python developers who are not async fanatics to actually capture some of that capability.
00:56:39.400 --> 00:56:47.880
Say if some library says, hey, I realize that if we actually implement this lower level thing, you don't actually see the implementation of in true threading.
00:56:48.220 --> 00:56:50.160
And then you use it, but you don't actually do threading.
00:56:50.160 --> 00:56:52.400
You just call even a blocking function.
00:56:52.880 --> 00:56:57.320
You might get a huge performance boost, a little bit like David was talking about with markup safe.
00:56:57.840 --> 00:57:00.040
And you could just all of a sudden, with doing nothing
00:57:00.600 --> 00:57:03.980
with your code, goes five times faster on an eight core
00:57:04.200 --> 00:57:06.680
machine or something in little places where it used to matter.
00:57:06.840 --> 00:57:08.960
I'm super excited for--
00:57:09.410 --> 00:57:12.720
we currently focused on the things that are out there
00:57:12.900 --> 00:57:15.180
right now and that might need to be updated.
00:57:15.270 --> 00:57:19.260
But I'm super excited for what else might come of this,
00:57:19.620 --> 00:57:23.520
new things that will be developed or stuff
00:57:23.530 --> 00:57:25.940
that we are currently not thinking about
00:57:25.960 --> 00:57:29.620
or have that hadn't been considered for the past 30 years or so,
00:57:29.720 --> 00:57:33.240
because it just wasn't feasible or wasn't possible or didn't make sense at all.
00:57:33.720 --> 00:57:35.720
I think it would pay off definitely.
00:57:36.100 --> 00:57:36.940
All right. Team Flask.
00:57:37.420 --> 00:57:39.180
You guys got the final word.
00:57:39.440 --> 00:57:42.320
I think it would probably be more advantageous to WSGI apps
00:57:42.520 --> 00:57:43.900
than it will for ASGI apps.
00:57:44.140 --> 00:57:47.180
And when I've been playing with it, it's mostly on the WSGI Flask side
00:57:47.280 --> 00:57:48.680
where I'm quite excited about it.
00:57:48.920 --> 00:57:52.220
At the same time, like the others are a bit worried because not clear to me,
00:57:52.380 --> 00:57:54.980
for example, that green threading is going to work that well
00:57:55.000 --> 00:57:56.040
with free threading.
00:57:56.400 --> 00:57:57.340
And that may have been fixed,
00:57:57.380 --> 00:57:58.520
but I don't think it has yet.
00:57:58.740 --> 00:58:01.480
And that might then break a lot of WSGI apps.
00:58:02.080 --> 00:58:03.240
So next, I think.
00:58:03.300 --> 00:58:05.760
But yeah, very excited for Flask in particular.
00:58:06.000 --> 00:58:07.260
Thanks for bringing up green threading.
00:58:07.260 --> 00:58:08.460
I added that to my notes
00:58:09.600 --> 00:58:11.020
of mention right now.
00:58:11.300 --> 00:58:14.360
So Flask already has emphasized
00:58:14.800 --> 00:58:15.980
for years and years and years
00:58:16.340 --> 00:58:17.840
that don't store stuff globally,
00:58:18.260 --> 00:58:19.020
don't have global state,
00:58:19.360 --> 00:58:21.220
bind stuff to the request response cycle
00:58:21.420 --> 00:58:22.880
if you need to store stuff,
00:58:23.100 --> 00:58:24.540
look stuff up from a cache otherwise.
00:58:24.600 --> 00:58:27.320
And my impression is that that emphasis is pretty successful.
00:58:27.330 --> 00:58:31.560
I don't, there's any well-known extensions using like global state or anything like that.
00:58:31.960 --> 00:58:35.580
It's helped that the dev server that we have is threaded by default.
00:58:36.160 --> 00:58:39.340
Like it's not going for performance, obviously, it's just running on your local machine, but
00:58:39.580 --> 00:58:42.680
it's already like running in a threaded environment, running your application in a
00:58:42.800 --> 00:58:45.160
threaded environment, not a process-based one by default.
00:58:45.740 --> 00:58:49.360
I don't know if anybody even knows that you can run the dev server as process-based.
00:58:49.720 --> 00:58:53.460
And we also already had for a decade or more than a decade,
00:58:53.960 --> 00:58:59.380
Gevent to enable the exact same thing that free threading is enabling for Flask,
00:58:59.520 --> 00:59:02.700
which is concurrent work and connections.
00:59:03.740 --> 00:59:06.600
And so plenty of applications are already deployed that way
00:59:06.880 --> 00:59:09.800
using Gevent to do what kind of ASGI is enabling.
00:59:10.400 --> 00:59:14.240
I've run all the test suites with pytest FreeThreaded,
00:59:14.560 --> 00:59:18.460
which checks that your tests can run concurrently in the free threaded builds.
00:59:18.920 --> 00:59:20.580
so go check that out by Anthony Shaw.
00:59:20.980 --> 00:59:23.160
And I'm pretty sure Granian already supports free-threading.
00:59:23.620 --> 00:59:25.400
Not sure though, I haven't looked into Granian enough.
00:59:25.480 --> 00:59:26.040
But like-
00:59:26.040 --> 00:59:27.040
You know, I'm not sure either.
00:59:27.040 --> 00:59:30.280
It does have a runtime threaded mode
00:59:30.280 --> 00:59:32.440
but I don't know if that's truly free-threaded or not.
00:59:32.440 --> 00:59:35.820
All of those things combined make me pretty optimistic
00:59:35.820 --> 00:59:38.840
that Flask will be able to take advantage of this
00:59:38.840 --> 00:59:40.540
without much work from us.
00:59:40.540 --> 00:59:42.240
I mean, I know that's a big statement right there
00:59:42.240 --> 00:59:43.480
and I haven't tested it
00:59:43.480 --> 00:59:45.160
but the fact that we've emphasized
00:59:45.160 --> 00:59:47.540
all these different parts for so long already
00:59:47.540 --> 00:59:48.340
makes me confident about it.
00:59:48.460 --> 00:59:49.660
I'm also super excited about it.
00:59:49.660 --> 00:59:51.680
And just one final thought I'll throw out there
00:59:51.790 --> 00:59:52.720
before we call it a show,
00:59:52.920 --> 00:59:54.640
because we could go on for much longer,
00:59:54.690 --> 00:59:55.260
but we're out of time.
00:59:55.580 --> 00:59:57.380
I think once this comes along,
00:59:57.860 --> 01:00:00.700
whatever framework out of this choice you're using out there,
01:00:01.080 --> 01:00:03.420
there's a bunch of inner working pieces.
01:00:04.400 --> 01:00:05.940
One of them may have some kind of issue.
01:00:06.130 --> 01:00:08.340
And I think it's worth doing some proper load testing
01:00:08.830 --> 01:00:12.040
on your app, you know, point something like locust.io at it
01:00:12.200 --> 01:00:14.900
and just say, well, what if we gave it 10,000 concurrent users
01:00:14.970 --> 01:00:15.360
for an hour?
01:00:15.390 --> 01:00:16.120
Does it stop working?
01:00:16.600 --> 01:00:17.160
Does it crash?
01:00:17.290 --> 01:00:18.140
Or does it just keep going?
01:00:18.360 --> 01:00:21.960
You're like, so that seems like a pretty good thing to do the first time before you deploy
01:00:22.100 --> 01:00:23.320
your first free threaded version.
01:00:23.840 --> 01:00:23.900
Yeah.
01:00:24.220 --> 01:00:24.620
All right, everyone.
01:00:25.440 --> 01:00:26.480
I would love to talk somewhere.
01:00:26.600 --> 01:00:30.020
This is such a good conversation, but I also want to respect your time and all that.
01:00:31.140 --> 01:00:32.220
So thank you for being here.
01:00:32.660 --> 01:00:35.320
It's been an honor to get you all together and have this conversation.
01:00:35.580 --> 01:00:37.000
Thank you very much for having us.
01:00:37.140 --> 01:00:37.400
Thank you.
01:00:37.640 --> 01:00:37.740
Yeah.
01:00:37.900 --> 01:00:38.600
Thanks for having us all.
01:00:38.760 --> 01:00:39.060
Thanks, everybody.
01:00:39.400 --> 01:00:39.480
Yeah.
01:00:39.700 --> 01:00:40.360
It's nice being here.
01:00:40.440 --> 01:00:40.580
Yeah.
01:00:40.700 --> 01:00:41.240
Thanks for having us.
01:00:41.480 --> 01:00:42.240
Thanks for having us all.
01:00:42.380 --> 01:00:42.540
Bye.
01:00:42.700 --> 01:00:43.060
Bye-bye.
01:00:44.380 --> 01:00:46.720
This has been another episode of Talk Python To Me.
01:00:47.020 --> 01:00:47.820
Thank you to our sponsors.
01:00:48.120 --> 01:00:49.320
Be sure to check out what they're offering.
01:00:49.510 --> 01:00:50.880
It really helps support the show.
01:00:51.300 --> 01:00:53.160
If you or your team needs to learn Python,
01:00:53.390 --> 01:00:56.780
we have over 270 hours of beginner and advanced courses
01:00:57.100 --> 01:01:00.520
on topics ranging from complete beginners to async code,
01:01:00.660 --> 01:01:03.440
Flask, Django, HTMX, and even LLMs.
01:01:03.660 --> 01:01:05.940
Best of all, there's no subscription in sight.
01:01:06.480 --> 01:01:08.260
Browse the catalog at talkpython.fm.
01:01:09.000 --> 01:01:10.940
And if you're not already subscribed to the show
01:01:11.160 --> 01:01:13.620
on your favorite podcast player, what are you waiting for?
01:01:14.240 --> 01:01:16.060
Just search for Python in your podcast player.
01:01:16.150 --> 01:01:17.040
We should be right at the top.
01:01:17.260 --> 01:01:20.260
If you enjoyed that geeky rap song, you can download the full track.
01:01:20.450 --> 01:01:22.280
The link is actually in your podcast blog or share notes.
01:01:23.180 --> 01:01:24.500
This is your host, Michael Kennedy.
01:01:24.900 --> 01:01:25.960
Thank you so much for listening.
01:01:26.170 --> 01:01:26.980
I really appreciate it.
01:01:27.400 --> 01:01:28.100
I'll see you next time.
01:01:38.260 --> 01:01:39.540
I started to meet.
01:01:40.900 --> 01:01:42.000
And we're ready to roll.
01:01:43.780 --> 01:01:44.820
Upgrade the code.
01:01:45.700 --> 01:01:47.320
No fear of getting whole.
01:01:48.820 --> 01:01:51.960
We tapped into that modern vibe over King Storm.
01:01:53.020 --> 01:01:55.700
Talk Python To Me, async is the norm.
01:02:24.880 --> 01:02:24.900
Редактор субтитров А.Семкин Корректор А.Егорова