Live data from Hacker News

The Synchrony Budget

morling.dev

1–10 of 10 posts

Re: The Synchrony Budget

#2
You might as well have an “asynchrony budget” since each point of disorder (where events could happen in a different sequence) is a place where an error could happen and the trouble scales worse than O(N) where N is the number of asynchronous processes.

Re: The Synchrony Budget

#3

You might as well have an “asynchrony budget” since each point of disorder (where events could happen in a different sequence) is a place where an error could happen and the trouble scales worse than O(N) where N is the number of asynchronous processes.

Agreed, I feel like the article didn't address the other major benefit of sync calls, namely their simplicity.

A system built on purely synchronous calls is infinitely easier to reason about and design in all aspects other than e2e latency.

I'd almost approach from the opposite direction, defaulting to sync calls till you have concrete performance reasons not to.

Re: The Synchrony Budget

#4
I'm sorry but some calls are synchronous. Even if all the calls between are async it doesn't matter. It won't make things faster for your service to be async when the process is fundamentally async. In fact if there are any bumps along the way and you have to requeue the request it will take longer.

Users don't live at the edges of your tiny microservice they live at the edges of your whole system.

Re: The Synchrony Budget

#5
post #3

You might as well have an “asynchrony budget” since each point of disorder (where events could happen in a different sequence) is a place where an error could happen and the trouble scales worse than O(N) where N is the number of asynchronous processes.

Agreed, I feel like the article didn't address the other major benefit of sync calls, namely their simplicity. A system built on purely synchronous calls is infinitely easier to reason about and design in all aspects other than e2e latency. I'd almost approach from the opposite direction, defaulting to sync calls till you have concrete performance reasons not to.

Every time you make an async call there is a whole state machine that has to be instantiated and managed. The only situations I would argue justify async over sync:

1. You are making an I/O bounded call and most of the CPU time would otherwise be spent idle.

2. You are making a CPU bounded call that the UI thread cannot directly await.

3. You need to propagate an asynchronous call context for one of the 2 above scenarios.

There are situations where you can put yourself in a position where it seems async needs to be everywhere (i.e., sharding a monolith to a bunch of separate networked elements).

Re: The Synchrony Budget

#6
> synchronous requests impact the availability of your service, because all the invoked services must be up and running in order for your service to work. The more services you rely on in a synchronous manner, the lower the availability of your service will be.

Hear, hear. And the knock-on effect is that as a developer you end up with more support tickets (because downstream services are always screwing up in some way), and your users will make it your problem.

Worse yet, once you've diagnosed the problem (partner company returned 500 for a while) and fixed it (well you probably can't fix it! The codebase belongs to a different company!), you might be stuck sheepishly asking the user to try again, because the sync control flow (including auth) starts from the user.

Had the system been built async, then the user would have created an (auth'd) request in your system, at which point you and your system can decide on your own retry policy, since you own the control flow.

If the request is reified in a queue/ledger as data (rather than being represented implicitly by services invoking one others' methods directly) you can start automating/batching your support actions. You can query "how many Requests created more than 24h ago are not married to a successful Response" and bulk-fix by re-attempting fulfillment of those Requests.

Furthermore, you might start discovering your system is a little crashier than you thought. Maybe for every Karen who calls into customer support and tells you to fix her issue, there are 19 other users who got a 500 and just got on with their day, (but probably won't renew their subscription to your service 3 months from now.)

Re: The Synchrony Budget

#8
post #6

> synchronous requests impact the availability of your service, because all the invoked services must be up and running in order for your service to work. The more services you rely on in a synchronous manner, the lower the availability of your service will be. Hear, hear. And the knock-on effect is that as a developer you end up with more support tickets (because downstream services are always screwing up in some wa…

> you might be stuck sheepishly asking the user to try again, because the sync control flow (including auth) starts from the user.

How does this actually work in practice? At some point you need to communicate the results of a successful or failed login attempt to the user (more generally, the initiating party) which means they're inherently waiting for something and will either see a success or a failure. A login as a precursor event to other kinds of actions feels inherently synchronous, because it's not a fire-and-forget action.

Re: The Synchrony Budget

#9
> a service should minimize the number of synchronous requests

This doesn't feel like a goal for system design that should be persued without careful consideration. Aside from the universal 'it depends', every technical decision has a trade-off, and the trade-off with making system-level events asynchronous is a system with more moving parts that need more monitoring, has more potential states, and seems harder to debug than one with necessary waiting just built in.

> but by design they block progression until complete

Isn't this often a good thing? If a result is required then chaining events that are independently failable but required to complete seems to create more work.

> That being said, for an e-commerce application it may be actually feasible to make synchronous calls to the payment service by default, but fall back to asynchronous processing in case of failures. As the contract to sell typically only gets accepted when an item gets shipped, you still have the room to cancel an order if a payment falls through on the asynchronous processing path.

Choosing to defer the requirement for payment acceptance is a very specific design choice that complicates the overall system by introducing new state(s). Maybe it's needed for your specific e-commerce application, but maybe just requiring payment at point-of-sale is simple and effective?

> synchrony budget

I'd argue that the budget should be for _asynchrony_, and operations should be kept synchronous until a genuine requirement for asynchronicity arises where the cost of adding that complexity justifies the result.

Incidentally, it's interesting that broad system design concepts always seem to be based around strawman e-commerce applications.

Re: The Synchrony Budget

#10
post #6

> synchronous requests impact the availability of your service, because all the invoked services must be up and running in order for your service to work. The more services you rely on in a synchronous manner, the lower the availability of your service will be. Hear, hear. And the knock-on effect is that as a developer you end up with more support tickets (because downstream services are always screwing up in some wa…

> you might be stuck sheepishly asking the user to try again, because the sync control flow (including auth) starts from the user. How does this actually work in practice? At some point you need to communicate the results of a successful or failed login attempt to the user (more generally, the initiating party) which means they're inherently waiting for something and will either see a success or a failure. A login as…

What I said about batched handling of support cases probably doesn't apply to logins - you're not going to 'fix' a login in time for it to be useful.

Yes, the user is waiting for their login to return, so it will feel synchronous to them.

( As some background, BankID is the de-facto auth service here. In its previous form, you typed your personal number in, and you'd get a popup on your phone where you'd enter your pin. They made the flow more secure by displaying an animated qr instead of typing in your personal number.

Here's the new flow as used by the Tax Office: https://m01-mg-local.auth.funktionstjanster.se/mg-local/auth...

Swedbank didn't get the memo and is still using the old flow: https://online.swedbank.se/app/ib/logga-in )

Check out the BankID flow charts: https://developers.bankid.com/getting-started/use-cases. If you're a small business integrating against BankID, it probably doesn't matter if each iteration of the backendbankid /collect loop uses its own thread (sleeping for 1-2s each time). But at BankIds scale there's no way they'd be maintaining a separate thread of execution for each user, it would need to be batched.