Guide to Lock Convoys
davekilian.com
Guide to Lock Convoys
1–10 of 15 posts
Re: Guide to Lock Convoys
#2Re: Guide to Lock Convoys
#3The best pattern I've found for inter-thread communication involves CAS for a ring buffer slot on the producer (you can skip CAS if only 1 producer thread), with either busy wait or yield/sleep on the consumer(s). If you keep your sync primitive separated between consumer & producer, the worst bits of contention can be avoided. CAS is very fast when un-contended or lightly-contended. Busy waiting can be used for late…
Re: Guide to Lock Convoys
#4Specifically for the request case, I recall seeing a talk suggestion that processing requests last-in-first-out resolves this failure mode as well as reduces the 99th percentile latency. The intuition is if a request is going to be late, might as well abandon it and process it later because it's already late instead of doing a slow job and making everything else slow in the process.
Maybe that's a strange metaphor for work too.
Re: Guide to Lock Convoys
#5> Maybe we can’t eliminate them, but that doesn’t mean there’s nothing we can do about them. Specifically for the request case, I recall seeing a talk suggestion that processing requests last-in-first-out resolves this failure mode as well as reduces the 99th percentile latency. The intuition is if a request is going to be late, might as well abandon it and process it later because it's already late instead of doing…
LIFO processing in the steady state is not a great idea because it will stochastically starve some requests for no reason.
Re: Guide to Lock Convoys
#6> Maybe we can’t eliminate them, but that doesn’t mean there’s nothing we can do about them. Specifically for the request case, I recall seeing a talk suggestion that processing requests last-in-first-out resolves this failure mode as well as reduces the 99th percentile latency. The intuition is if a request is going to be late, might as well abandon it and process it later because it's already late instead of doing…
If you have a long standing queue, switching to LIFO makes sense. It requires you to know how long the requests have been waiting, however, and many servers don't even have this basic information. Every request needs to come with a deadline and a timestamp, so the request processor can make rational decisions about processing or dropping it. If a service finds a request that arrived a long time ago and sat in the que…
Re: Guide to Lock Convoys
#7The best pattern I've found for inter-thread communication involves CAS for a ring buffer slot on the producer (you can skip CAS if only 1 producer thread), with either busy wait or yield/sleep on the consumer(s). If you keep your sync primitive separated between consumer & producer, the worst bits of contention can be avoided. CAS is very fast when un-contended or lightly-contended. Busy waiting can be used for late…
Re: Guide to Lock Convoys
#8Re: Guide to Lock Convoys
#9> Maybe we can’t eliminate them, but that doesn’t mean there’s nothing we can do about them. Specifically for the request case, I recall seeing a talk suggestion that processing requests last-in-first-out resolves this failure mode as well as reduces the 99th percentile latency. The intuition is if a request is going to be late, might as well abandon it and process it later because it's already late instead of doing…
How does it manage to do that?
When you're getting close to your limits and requests are actually waiting in the queue, I would expect FIFO to slow down everything to provide backpressure, while LIFO keeps a very nice median speed but has an increasing percentage of requests that timeout and retry once or even twice.
Are there significant dynamics I'm not thinking of?
Maybe if you have bursts that are just big enough for FIFO to delay >1% of total requests, but small enough for LIFO to drop <1%? But in that situation giving a single percentile paints a misleading picture; 99th would do better on LIFO but it comes at the cost of trashing higher percentiles.