7 comments

  • CraigJPerry1 hour ago
    There are so many footguns[1] in async python but it really has stolen the zeitgeist of modern python web. Synchronous django has a lot to commend it. If you deploy it reasonably well (workers, behind a caching reverse proxy etc) it&#x27;s easy to operate in production even under duress.<p>It&#x27;s not going to be the right stack for long lived websocket connections or whatever but for a CRUD-ish or enterprise app, often very productive.<p>[1] buffer bloat is too easy to sleep walk into<p><pre><code> queue = asyncio.Queue() # oops </code></pre> unbounded concurrency<p><pre><code> await asyncio.gather(*(fetch(item) for item in items)) # look mum! no outbound sockets left or maybe even no file descriptors </code></pre> accidental blocking<p><pre><code> data = requests.get(url).json() # oops! we&#x27;re blocking the event loop </code></pre> and sure you can setup a separate task to measure the event loop latency and alert on that but this is the kinda thing you&#x27;ll never find in a tutorial, you just need to experience this stuff and figure out a solution you like<p>I can go on and on about async python (cancellation bugs, leaking a task - that has a cataclysmic failure mode where if you forget to hold a reference to your asyncio.create_task() then it&#x27;s all weak refs in that machinery so your task can get garbage collected before it ran or completed in production! super tricky forensics. Then there&#x27;s all the obvious stuff like race conditions, new ways of creating deadlocks, blah blah i really can go on for days.
  • echoangle3 hours ago
    &gt; Some light load testing (with (ab -n 1000 -c 1) shows that right now we can serve about 2-3 requests per second (on a ~$10&#x2F;month VM).<p>&gt; After turning on template caching, it seems like the site can now pretty easily handle 12 requests per second or so without using all of the CPU. I have not carefully benchmarked the before and after but it seems like it’s made a pretty big difference.<p>That seems crazy low, I think there has to be something else going on here.
    • shakna2 hours ago
      Those numbers sound... Single-threaded. Like they&#x27;re using the development runserver instead of uwsgi or gunicorn.
      • jonatron2 hours ago
        A $10&#x2F;month VM might only have a single thread anyway. Some providers like lightsail are really slow too.
        • echoangle2 hours ago
          You can run multiple OS threads (gunicorn workers) on one VM thread so the workers don’t have to wait for each others request to finish though, right?<p>And if you pay $10&#x2F;month for a single threaded machine, you’re overpaying by a lot.
          • jonatron2 hours ago
            True, but in this case with SQLite, there&#x27;s unlikely to be much of a difference because there isn&#x27;t the spare time available when waiting for a separate database server. I don&#x27;t know what providers are good for a $10&#x2F;month instance these days.
            • echoangle51 minutes ago
              How are you spending any non-negligible time reading from SQLite at 12 requests per second though? That would mean you’re spending something like 50 ms per request on reading from SQLite.
            • ErroneousBosh1 hour ago
              Most of the time you&#x27;re hitting SQLite, you&#x27;re just reading from it, and so it doesn&#x27;t hold anything up.
        • shakna1 hour ago
          For a cheap VM, I&#x27;d still be expecting in the range of 500-1000 connections a second. Green threads are cheap, even with a single processor.<p>For a half-decent VM, I&#x27;d be expecting multi-thousand.<p>Single figures a second, is choked to a single connection at a time.
      • BiteCode_dev1 hour ago
        Also, they may be serving static files through Django. Otherwise, it&#x27;s really, really low.
  • ranedk2 hours ago
    I have been using Django since 0.95 and I haven&#x27;t seen anything which is so flexible with amazing DSLs while also making it easy to understand the magic behind it.<p>For the last 10 years, even in a Golang stack or Java stack, I still use Django for models and migration. I even have generators which generate Gorm (or other framework) DAO or Java hibernate classes using Django models.<p>With LLMs, it becomes easier since I can now write all the model, custom querysets in Django, ask the LLM to generate Golang DAO, setters and getters... and test the query against the Django generated queries for completeness.<p>Atlas, sqlx, sqlc and all other ORM like things in golang cannot do migrations the way Django does.
  • altbdoor3 hours ago
    Good ole Django. Worked with a number of frameworks (tm), but nothing really quite scratches my itch like Django does. I still find the ORM and database migration system unmatched.
    • hahahaa2 hours ago
      I found Django a bit hard to get on with vs. other frameworks and I&#x27;ve used Rails, .NET MVC and Express (and friends). I just found more friction trying to achieve X for any given X for some reason. Not sure why.
  • faangguyindia2 hours ago
    I mostly use Go + SQLite for all the things I used to use Rails, JavaScript, or Python for.<p>I find python django wastes too much resources, just look at memory usage.<p>One of my web app backend (go) is serving approx 100 req&#x2F;s right now and i look at pprof i see it&#x27;s not bottlenecked by CPU but mostly IO and i love this.<p>Writing concurrent code in Go is easy, the code i wrote 10yrs ago still compiles with no issue! This is why i am never gonna switch.<p>My go apps use very little memory, so we can scale to many users for very cheap.<p>For larger apps i use postgres (why? replication is easy using pgfailover, high demand apps need multiple api servers so it&#x27;s out of process db like postgres is fine) but most of my web app use HTMX and if we need some reactivity, i use react (simply due to react experience from work)<p>For our maintenance calorie tracking app, which is free and has no ads, we have to use as few resources as possible as we scale to thousands of users: macrocodex (which figures out maintenance calories from weight and calorie intake). We initially used Haskell.<p>Later, it became slow and cumbersome to develop in (developing on an Apple Silicon Mac and deploying to x64 is a pain), even though I liked writing Haskell code. I even tried nix and wasted a day on that! I had a choice between OCaml and Rust. I picked Rust and never looked back.<p>The algorithm serves in 0.1 ms on Rust. In Haskell, it was 0.2 ms, and memory usage was twice that of Rust. There are many optimization possible in Rust which i didn&#x27;t do (for sake of simplicity) yet i received good performance.<p>Yeah, I use Docker to compile Rust, but it&#x27;s pretty fast, much faster than what I had with Haskell, so the developer experience is great.<p>By switching to Rust, the LOC dropped to half of what we had in Haskell.<p>project turned out to be successful. It has already produced guaranteed weight loss or weight gain for many people.<p>So I set out to create an algorithmic workout app, for which I am using Rust and Go. The mobile app is in Flutter.
    • nargek1 hour ago
      Appart from the fact that you find that Python wastes too much memory, what is your point ? I think that any person choosing Django knows that Python by nature will not be the most efficient language. Apart from that Django is battle-tested and can help bring a stable &quot;product&quot; quite quickly.
      • faangguyindia1 hour ago
        if &quot;2-3 requests per second&quot; per author is what you wanna do on $10 server go ahead&quot;. My server does 100 RPS on $16 instance<p>neither you are saving any time, nor money.<p>&gt;part from that Django is battle-tested and can help bring a stable &quot;product&quot; quite quickly.<p>this is a myth, you&#x27;ll not save anytime. Only way you can save time is if you&#x27;ve experience in this but same is true if you write your app from scratch in Go from your learned patterns.
        • echoangle16 minutes ago
          &gt; if &quot;2-3 requests per second&quot; per author is what you wanna do on $10 server go ahead<p>That&#x27;s not a Django limit and there&#x27;s something going on with the authors setup. 100 RPS on a $16 instance would be easily doable with Django too.<p>&gt; neither you are saving any time, nor money.<p>How do you know? I&#x27;m pretty sure I can set up the same webapp in Django much faster than in go, so I&#x27;m saving both.<p>&gt; this is a myth, you&#x27;ll not save anytime. Only way you can save time is if you&#x27;ve experience in this but same is true if you write your app from scratch in Go from your learned patterns.<p>Why do you think all the built in stuff in Django does not save time? Any argument for that?
  • sakjur1 hour ago
    &gt; as a meta comment: I’ve been working on talking about my programming opinions by just saying “THING does not feel good to me, I prefer OTHER THING instead”. That post I linked to says that function-based views are the “right way”. I’m not very invested in whether it’s “right”, but it’s validating to know that other people feel similarly to me about inheritance<p>I admire this about Julia a lot. Her texts and zines are exploring software in a way that encourages curiosity rather than promoting a singular point of view.
  • BrenBarn2 hours ago
    The Django filter syntax with the double underscores is like fingernails on a chalkboard to me. I find it insane that they didn&#x27;t just use operator overloading to create a real query expression language.
    • ezst1 hour ago
      Or now that python has ~types, this is really an area where things could be improved. Filtering would just be lambda predicate with fields auto complete as seen in .NET, scala, etc
    • 7bit50 minutes ago
      There may be many reason other than rejecting that suggestion leading to what it is know. Your statement somehow suggests that it was deliberately decided against what you propose. I don&#x27;t think we know that.<p>I can&#x27;t quite picture how operator overloading would look like, could you give an example?
      • echoangle9 minutes ago
        &gt; I can&#x27;t quite picture how operator overloading would look like, could you give an example?<p>Instead of this:<p>self.filter(end__gt=self._midnight(today))<p>You could write a &quot;Field&quot; class that implements __getattr__ and __gt__ so you could do<p>self.filter(Field.end &gt; self._midnight(today))<p>The &quot;Field.end &gt; self._midnight(today)&quot; would evaluate to an object that would just store &quot;my field name is end and my value needs to be larger than xyz&quot;.<p>filter() can then look into its argument list and construct the filter criteria from the passed Field objects instead of the key value pairs as it does now.