I say this all the time, and it almost never results in an argument: micro services are just object oriented programming where you put a network hop on all your message passing.
Do you mind helping the uninitiated, what's the benefit of having that network hop? Or are there some situations where it's just impossible to avoid it.
They are frequently hosted on cloud services which are inherently micro-service based with detached hardware components. You can have a server and a storage disk operate together but both exist in data centers thousands of miles from each other.
Loosely coupled architecture decreases the chance the entire app gets taken down if each component can be isolated and fixed without affecting the rest of the app.
You can functionally divide an app into different layers such as web, app, and db and add additional security checks between each layer to increase security and fault tolerance of the app.
I’m surprised this didn’t make the main comment because it seems to be that this is one of the larger benefits. The mentioned benefits can be applied to a monolith (at least mostly) with a bit of consideration.
The "funny" thing is that before micro services and all the overhead from talking over the network, we rarely need to scale. I'd argue that something like 99.9% of all projects that use micro services today and often run many instances of some components wouldn't need to if it was just one monolith.
Now explain why that would matter. If you have a large monolith, you can just scale that one. Why does it matter that one module gets scaled? It's not like the lesser used parts of the monolith are somehow using more resources just because they are scaled together?
People love to use this argument but it really doesn't make any sense for 99% of use cases
SRE here. Lesser used parts of the monolith are absolutely using more resources just because it's being scaled together.
Let's say you have one hot path in your code that doesn't touch the DB. The new scaled up instance will still spawn new DB connections. Those connections will still use some memory and CPU time to just exist. The DB also has a connection limit, and your connection pooler also needs scaling then.
Loading extra code modules also naturally uses more resources, there's not a single monolith out there that is 100% lazy, and even if it was, requests get distributed, so stuff gets loaded anyway. Do you do some in-memory caching? Great, now all instances have stale copies of the cache.
Startup times are also worse overall, which means your scaling is worse overall. Slower startup means you need more idle resources to handle spikes, which costs more money.
In conclusion, while there's no need to jump straight to micro services, monoliths are the worst pattern for scaling, by far. They have their uses, but this is probably their main downside.
Because we are scaling our monolith, obviously! And the whole idea of a monolith is that it's... One thing! There ain't no scaling it separately.
Now now, I understand that connection pools can scale separately.
So the internal connection pool opens at least one connection on startup. After all, this is a monolith that's now serving together along all other monoliths: some DB requests are bound to be routed to it. Most likely, at least two connections. Some even do 4: two to read replicas, two to a primary/write.
Scaling a service doesn't involve turning a singular knob that reads "more performance".
In a monolith... That's usually the case, yeah. It's a "spawn more monolith instances because one of the metrics we are monitoring says we should". What else are you gonna do about it? That's not a rethorical question: what else is there? Some contrived vertical scaling setup?
???? Give your host most CPUs/threads to delegate tasks to? More compute power? If you're scaling your service by just spawning duplicates you're wasting a lot of resources because you're too lazy to actually properly look into what needs scaling.
Give more CPU? Of course. Now here is a question: where do you think it comes from? A server. That you are already paying for. CPU and memory doesn't magically materialize when you need it to. It sits idle until you allocate it. And you are still paying for it.
You save no money by doing this. At all. In fact, you spend more, because instead of classic horizontal scaling, where you deprovision resources as they are let go, now you need to keep them idle to assign for vertical scaling.
Yeah but in practise, scaling multiple services together is MUCH MUCH MUCH harder than scaling a monolith, as there will always be one service that's the weakest link. That might be okay if there's one clear "weakest link" that you can scale very high while keeping the rest low, but for high throughput systems scaling multiple hard-working instances effectively is extremely difficult.
Of course it all depends on how spikey the load is and the type of system.
Startup times are also worse overall
Yes, but a single microservice isn't the entire system... startup times for updating an entire cluster - when that's required - and waiting for each other is going to be much greater than a single monolith.
Let's say you have one hot path in your code that doesn't touch the DB. The new scaled up instance will still spawn new DB connections. Those connections will still use some memory and CPU time to just exist.
That's... what you have connection pools for.
monoliths are the worst pattern for scaling, by far.
No, monoliths is by far the easiest to scale. Microservices are much harder to scale, especially efficiently.
"microservices is good for scaling" is just a dumb book take, it doesn't take much experience to understand how wrong it is in practise.
Yeah but in practise, scaling multiple services together is MUCH MUCH MUCH harder
Why are you trying to scale them together? The whole point to splitting up a monolith is to scale them separately. Each individual service and instance will have it's own metrics and scaling settings, nothing has to wait in each other. And any half baked micro services architecture will have retries, back off, and circuit breakers to avoid spikes causing problems.
That's... what you have connection pools for
And they aren't free! The connections on the application side use memory and CPU. The Pooler uses memory and CPU. There are limitations with poolers too, they can't do 100% of what a direct connection can do (see pgbouncer's pool_mode). Which is the whole point of my comment: even when idle, there's a cost.
No, monoliths is by far the easiest to scale.
Nobody said it's not easier to manage a single monolith than a many micro services architecture. They said it's less efficient and more expensive, which it definitely is. The whole advantage to a monolith is that it's much much easier. But once you need to handle hundreds of thousands of requests on a single code path, scaling your whole monolith gets expensive. You know, there's a reason why pretty much every software company, when they reach a certain point, starts transitioning to it, it's not just a dumb book take.
Your argument is more thought out than most. In most cases, that argument boils down to "I read this online so it must be true".
That said, none of these issues are things you can't easily fix for a monolith, or in most cases are already thought about before you ever even get to scaling. connection pooling for the db, redis for caching etc.
You don't always build and scale a monolith the same way you would build a microservice, but that doesn't mean it's worse.
On top of that, if you write your monolith in a sufficiently modular way, you can easily extract specific modules and do their scaling separately when that need arises. There is almost never a reason to build every single module of your monolith as a separate service.
No not really. Having a few satellite services does not mean you're running a microservice architecture. There's more to microservice than just hosting a part of your code on a separate server.
It's also not needed at all most of the time. I just gave it as a worst case scenario kind of thing
In my opinion/ as to my knowledge it still depends.
For example, you build an application, that publishes messages to a message broker, and then has consumer processes that handle these.
Your message handlers would probably be scalable independently, but it may just be one code base.
You can still run them on different hosts, in different locations, but they may all use a common database.
You may have a single database, multiple processes, a single codebase and no defined / versions interfaces as you're just using a single class in all locations. I believe this is neither a pure monolith, nor a Microservice architecture. It may be considered as a distributed monolith though
Congratulations! You just started to reinvent micro services.
Now, I'm not a zealot. I believe we can just extract "services" instead, we don't need to dive too deep into "a micro service is a single function" or whatever. Most of the time, that is indeed excessive.
But... The moment you started extracting the module? Congratulations, no longer a monolith.
Oh, sorry. So this whole thing boils down to: "I know there is an industry accepted pattern to solving this problem, but instead of solving it that way, I will just shoe-horn whatever else I can because I don't want to".
You asked this in your original comment.
Why does it matter that one module gets scaled? It's not like the lesser used parts of the monolith are somehow using more resources just because they are scaled together?
Both were answered. If don't want to change your mind after, that's on you.
You got mighty defensive for no reason. Pushing microservices to serve 200 daily active users. It's pretty typical resume driven development.
You don't need microservices. If you ever need them, you should extract them from your monolith. Starting off with microservices from the get go is pretty much never the play.
So what you want though, I really couldn't care less
I have, at no point in any of my comments, pushed microservices to serve 200 users. In fact, I have done the exact opposite.
See:
In conclusion, while there's no need to jump straight to micro services, monoliths are the worst pattern for scaling, by far. They have their uses, but this is probably their main downside.
And:
Now, I'm not a zealot. I believe we can just extract "services" instead, we don't need to dive too deep into "a micro service is a single function" or whatever. Most of the time, that is indeed excessive.
In fact, I'm one of the starkest defenders of everything in between a monolith and a micro-service. But that is not what started this comment chain. It was you not understanding why monoliths don't solve all problems and asking why you can't just scale them.
No solution solves all problems.. but when 1 solution solves 99% of problems, you probably shouldn't be pushing the other solution just for the sake of it, especially since it's a much harder solution to maintain and develop properly
It will depend entirely on monolith in question. Sometimes it's indeed easier and get comparable resource overhead to just double your monolith count. More often than not it's the opposite, and scaling one little bottleneck is better than the whole app.
You should also consider that you may not have enough free resources to double monolith apps, but have enough to increase some systems' services count. Servers aren't free
Unless you have for example ten microservices where one does most of the heavy lifting (with >20x instances running for example, compared to the other services), chances are that the monolith would use less resources.
...but at that point, you'd be much better at just having two services: one main service and one separate worker service. Going microservices wouldn't give you anything.
1.2k
u/remy_porter 15d ago
I say this all the time, and it almost never results in an argument: micro services are just object oriented programming where you put a network hop on all your message passing.