Hacker News new | past | comments | ask | show | jobs | submit login

I think you are oversimplifying quite a bit. Looks to me like you are viewing things thought the lens of the typical Microservice architecture. But here are a few more dimensions of complexity:

* State between communication. If service (worker/function) A needs to talk to B, A usually needs to keep some state until B answers. Maintaining this state (what if the message gets lost, what if we redeploy A, what if the storage fails) adds a new dimension IMO.

* Circular dependencies/recursion. It's astonishing how things like A communicating with A, directly or indirectly seems to be implicitly missing from that typical architecture.

* Message growth. What happens when you have an O(n^2) or worse growth in messages? How do you track and manage that?

Generally speaking, it looks like microservices behave a lot like the early procedural programming languages by not considering the actually complicated stuff.




I've been thinking a lot about these things lately. I still haven't found solutions to all the problems, but here goes...

The alternative to asynchronous interfaces (which is basically what GP describes) are blocking interfaces - like system calls, or just waits for a specific event. And these are never an answer, unless it is _guaranteed_ that the blocking call will return withing a given timeframe and you also know that you will have absolutely nothing else to do meanwhile.

> State between communication.

There are two ways to keep state - locally on the stack or in an explicit data structure. As always in programming, you have to clean up when you destroy an object/process/stateful thing.

> what if the message gets lost

This absolutely should not happen, unless the sender doesn't expect an answer and the message can clean up itself. The latter is the case for example when sending a message means just copying it towards the destination, like in computer networks. The former is the case in particular in UDP connections.

> what if we redeploy A, what if the storage fails

You absolutely need to answer all messages that require an answer (called "IO completion" elsewhere). Of course, the answer can be "cancelled" or "failed".

Before destruction or reset, you need to synchronize with all users that hold a direct handle to the object being destroyed. That could just be done by having only a single owner who is responsible to wait for a "cleaned up" event and to then destroy the object. Think Unix processes - processes that exited still appear in the process table until their parent has wait()ed for them.

> Message growth. What happens when you have an O(n^2) or worse growth in messages? How do you track and manage that?

In general, asynchronous IO is achieved with queues (which are what GP discussed). With queues you have the choice to limit their size right in the queue (if the queue is full, block sending, or reject it temporarily and notify when there is progress). Or you can allow unlimited queues and push responsibility for memory management (and allocation policies) to the users of the queue. For example, users can allocate messages on their own and just link them into the queue - not additional memory allocation needed.


>Generally speaking, it looks like microservices behave a lot like the early procedural programming languages by not considering the actually complicated stuff.

+1 In my experience “modern distributed systems” is codeword for handwaving away the complex questions like transactionality and consistency and thinking mostly about the happy path behaviour.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: