Production DBA
Fact-Checking Microsoft’s “PostgreSQL to SQL Field Notes: Connections”
Microsoft has started a series of blog posts helping Postgres developers who are “upgrading” to Microsoft SQL. (Microsoft’s been using that generic “SQL” term to refer to its collection of database products: Azure SQL DB, Hyperscale, Managed Instances, and SQL Server.)
I let the first blog post slide, but the second post explained why Microsoft is so much better than Postgres at handling connections, and it’s so egregiously misleading that I’m gonna have to step in.
The post claims that Microsoft “supports up to 32,767 simultaneous user connections per instance” – and that’s true, but only if those connections aren’t doing anything!
See, Microsoft SQL has a different problem than Postgres does. Microsoft SQL has a concept of “worker threads”, and it’s best explained by thinking about a take-a-number dispenser.
When you walk into a grocery store, and you go to the meat counter, you’ll likely see a take-a-number dispenser. You grab one of those numbers, and you wait for your turn to be called. When it’s your turn, you can go up to the butcher and get the stuff you need. (Vegetarians: I’m sure the same concept applies in the produce aisle, I’ve just … never been over there. If I lived in India, I’d be a vegetarian for sure, because y’all’s vegetarian food tastes AMAZING and I don’t feel like I’m sacrificing anything at all, but here in America, most vegetarian food tastes like shoe sandals. I wonder if American vegetarians all have foot fetishes? Moving on.)
Lots of people can be in the grocery store at once, as long as they don’t need to talk to the butcher.
Worker threads: where Microsoft’s claim starts to fall apart
Microsoft’s claim that they support 32,767 connections means yes, that many people can walk into the grocery store – but they can’t actually do anything. When they wanna run a query, they have to get a number from the take-a-number dispenser.
That’s called a “worker thread”, and it’s way, way more limited, as the docs explain:
- Servers up to 4 CPU cores: 512 worker threads, aka max 512 numbers in the take-a-number dispenser
- 5 to 64 cores: 512 + ((logical CPUs – 4) * 16), so for example an 8-core server gets 576 worker threads
- 65+ cores: 512 + ((logical CPUs – 4) * 32), so a 72-core server gets 2,688
That worker thread number dictates how many queries you can actually run, which is much, much more impactful than the number of connections – especially because of the second problem we have in Microsoft SQL, which is…
Parallelism: where Microsoft’s claim catches fire
Microsoft SQL automatically parallelizes parts of your query where it makes sense, and I love that feature. It’s awesome. It’s not perfect, and if you Google for CXPACKET you’ll find all kinds of database administrators struggling with Microsoft SQL’s inability to evenly parallelize work. It’s completely normal to see a query’s tasks getting “parallelized” across multiple cores, but all of the work ends up on a single core.
However, here’s the part you’re gonna care about: when a query goes parallel, it takes multiple numbers from the take-a-number dispenser. The default parallelism settings make this particularly problematic: in Azure SQL DB and related flavors, it’s 8, and in Microsoft SQL Server, it can be even higher than that.
Even worse, the query doesn’t just take 8 numbers at once: it takes 8, plus another one for a coordinating parallelism manager thread, for each simultaneous parallel branch in the query. The larger your query is, the more likely it is that SQL Server can break it up into branches that can execute simultaneously, and if those things can parallelize, buckle up:

In this particular demo query from my Mastering Server Tuning class – which explains why I have a “take a number” dispenser at my house, because, you know, I need props for my classes – running on a 4-core server, a query goes parallel across 50 branches, spawning 200 tasks, using up 250 worker threads:

So on my 4-core server, which only has 512 numbers in the take-a-number dispenser, this one query grabs 250 numbers from the take-a-number dispenser. If just two of these queries run simultaneously, I’m down to only 12 numbers left! Granted, this is an extreme case – it’s fairly unusual to see a single query with more than 2-3 simultaneous branches – but the same problem exists with those small queries, run simultaneously at scale.
But wait, it gets worse.
Blocking: where Microsoft’s claim has rapid unscheduled disassembly
Your query continues to hang on to those paper numbers the whole time it’s running. If your query happens to get blocked by another query – something that’s very frequent, given that most of Microsoft SQL’s products default to pessimistic concurrency, where readers can block writers and vice versa – then your query is sitting there doing nothing, while still hogging those numbers, preventing other queries from starting.
Can apps CONNECT to the database server? Maybe, and that’s what Microsoft is so apparently proud of, that giant 32,768 connections number. But who cares? If those apps try to actually connect to do anything, and they want a number from the take-a-number dispenser, they’re gonna find it empty.
Including diagnostic queries.
Including monitoring tools.
It’s a common nightmare scenario for database administrators, and if you Google for SQL Server THREADPOOL you’ll find a lot of confused admins saying, “I don’t understand what’s happening – my database server’s CPU is sitting at 0%, but when people try to connect or run queries, they get timeouts! My monitoring system just shows blanks when this is happening!” Yeah, because your monitoring apps also need to connect to the database server to run diagnostic queries, and they can’t do it either.
This week, Louis Tochon uncovered a wild related problem: if you use the new sp_invoke_external_rest_endpoint, it consumes an unlimited amount of worker threads. You end up with worker thread exhaustion, and people unable to connect to the server.
Summary: no, Microsoft SQL isn’t problem-free.
If you’re coming from Postgres, you’re used to reading unbiased blog posts and articles from people who are just trying to help you. You’re going to need to think more critically when you read blog posts that live on Microsoft.com itself, or on Microsoft’s YouTube channels, because they’re usually written by people who are being paid to convert you to Microsoft. These people are often really nice, and they mean well, and they believe in the product they’re selling you, but they are selling you a product. Make no mistake: you’re not reading educational material, you’re reading marketing material.
What’s the solution? Find independent community members who blog about how Microsoft SQL products work. Here are some of my favorites who are currently actively blogging:
- Blog & tools: Erik Darling
- Blog: SQLServerScience.com by Hannah Vernon
- Book: T-SQL Fundamentals by Itzik Ben-Gan – it’s old, but it’s great and relevant
- Book: SQL Server 2025 Tuning by Grant Fritchey
And of course, there’s, uh, me. I’ve published 3-4 blog posts and videos per week across the last couple of decades, I maintain the First Responder Kit repo, I do performance tuning consulting for companies as large as Google, Stack Overflow, and other companies you’ve heard of, and I teach unbiased training classes. I use both Postgres and Microsoft SQL every day.
I really do love the Microsoft data platform – I just don’t want you to think it’s all a bed of roses, because there’s a whole lot of thorns in here, and I can help you avoid those. And if you wanna attend my live classes, hey, take a number.
Free, 3× a week
Get my new posts by email
Three posts a week, plus a Monday roundup of the best database news from around the web.

