Simulate Latency, Jitter, and Package Loss in Phoenix LiveView
Hey folks đź‘‹ Before we begin, a little story:
My friend bragged that his 3D printer could print a Gun,but I wasn’t impressed.
I already had a Canon printer for years!
Alright, now to today’s topic: How to simulate latency, jitter, and package loss when developing LiveView apps
The Problem
When we develop LiveView apps, usually two things are true: We develop it locally and we test production using a solid internet connection. But these things are rarely true for our users! They don’t run the app on their computer and they often connect through a mobile - and therefore, unreliable - connection! LiveView apps have to do round-trips to the server for almost everything. Every button click, every site navigation. So, the quality of our user experience is strongly dependent on our internet connection. Users need immediate feedback for their actions. If they click a button and nothing happens, they get confused. If our website “freezes”, they leave it. So, how do you prevent this from happening?
The Solution
You can simulate a bad internet connection locally by adjusting the latency
, jitter
, or package loss
of your connection. Use these levers to test how your website behaves when the internet is bad. You’ll be surprised how confusing it is when you click a button and nothing happens!
Let’s see how you can simulate each:
Simulate Latency
Phoenix LiveView comes with a latency simulator out of the box. When you generate a new project, you’ll find the following documentation in assets/js/app.js
:
// expose liveSocket on window for web console debug logs and latency simulation:// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket;
LiveView makes it trivial to simulate a stable latency. Run your app, open the browsers console and type the following to simulate a one second delay for every user action:
// Browser console
liveSocket.enableLatencySim(1000)
LiveView will store this setting in the browser’s session, so you need to either close the browser tab or run the following command to disable the latency again:
liveSocket.disableLatencySim()
Simulate Jitter
Phoenix LiveView allows us to add a constant latency to our connection. In reality, latency is rarely constant though. You might sit on a moving train and your phone constantly connects to a new cell phone tower. So, to simulate an even more realistic internet connection, we need to add jitter
. Jitter means that the latency changes with almost every user action. Sadly, LiveView does not support this out of the box. It was requested a while ago, but it seems that it wasn’t pursued further.
Luckily, it’s not too hard to add it ourselves. All it takes is a small JavaScript function that uses LiveView’s enableLatencySim
-function to change the latency randomly. Have a look:
// Copy this to a new file at e.g. assets/js/jitter.jsexport default enableJitter = (
minJitter,
maxJitter,
minDelay = 500,
maxDelay = 2000
) => {
let timeout;
const randomInt = (lowerBound, upperBound) => {
return (
Math.floor(Math.random() * (upperBound - lowerBound + 1)) + lowerBound
);
};
const setJitter = () => {
latency = randomInt(minJitter, maxJitter);
// It's important to disable the latency first before
// setting it again. Otherwise, weird things happen.
window.liveSocket.disableLatencySim();
window.liveSocket.enableLatencySim(latency);
};
const runInterval = () => {
const timeoutFunction = () => {
setJitter();
runInterval();
};
const delay = randomInt(minDelay, maxDelay);
timeout = setTimeout(timeoutFunction, delay);
};
runInterval();
return {
disable() {
clearTimeout(timeout);
// Don't forget to clear the latency as well.
window.liveSocket.disableLatencySim();
},
};
};
The enableJitter
-function changes the latency using a random interval. By default, it changes the latency every 500 milliseconds to 2 seconds, which is good enough for local testing. You can use it for your application by adding it to your app.js
like this:
// app.js
import enableJitter from "./jitter";
// ...
window.liveSocket = liveSocket; // this line must exist
window.enableJitter = enableJitter; // add this line
This will expose the enableJitter
-function in your browser. You can enable the jitter in your browser console like this:
// Enable jitterjitter = enableJitter(500, 1500)
// Disable jitter
jitter.disable()
Simulate Package Loss
The last characteristic of a bad internet connection is package loss. Especially on mobile connections, you will lose packages occasionally. LiveView handles this problem pretty well out of the box, but you can still simulate it by using the dummynet utility. Thanks to Chris McCord for pointing this out!
Open a terminal on your computer and run the following code:
# Enable Package Losssudo -i
dnctl pipe 1 config plr 0.3
echo "dummynet out proto tcp from any to localhost port 4000 pipe 1" | pfctl -ef -
# Disable
pfctl -f /etc/pf.conf && pfctl -d && dnctl -q flush
We first open a sudo
session and create a dummynet “pipe” using dnctl. We configure the pipe to drop 30% of packages using the plr 0.3
option. We could also add a constant delay with delay 1000
or limit the available bandwidth with bw 300Kbit/s
.
Then, we add the pipe to the package filter of our computer using pfctl. Open your application locally and have a look at the Network
tab. You’ll see that some packages are dropped!
Once you’re done with testing, don’t forget to disable the package filter again with pfctl -f ...
. And that’s it!
Conclusion
And that’s it! I hope you enjoyed this article! If you have questions or comments, let’s discuss them on BlueSky. Follow me on BlueSky or subscribe to my newsletter below if you want to get notified when I publish the next blog post.