Rails: Measuring Server Latency/Ping time
Latency/Ping time is the time it takes for data to pass from one point on a network to another. Suppose Server A in New York sends a data packet to Server B in London. Server A sends the packet at 04:38:00.000 GMT and Server B receives it at 04:38:00.145 GMT. The amount of latency on this path is the difference between these two times: 0.145 seconds or 145 milliseconds.
Most often, latency is measured between a user’s device (the “client” device) and a data centre. This measurement helps developers understand how quickly a webpage or application will load for users.
In this blog, we will try to add a ping time measurement tool in rails app. We will do this following a specific “recipe” format (Problem, Solution, Discussion).
Problem
We’re going to add a tool in rails app that will calculate latency/ping time for requests in the app. To achieve this, we will be using
- Rails with webpecker
- Stimulus
- Hotwire
- Trubo Rails
- Redis
Solution
Let’s go step by step as follow
Create a Rails App
If you want to add this tool in en existing rails app, move to the next section.
In this section we will create a simple rails app with following command:
rails new webapp --webpack
In your Gemfile, add the webpecker
gem and then run bundle to download Webpacker into your project
gem 'webpacker'
And then run
bundle
Now, running the following installs Webpacker in your application.
bundle exec rails webpacker:install
That’s it. The Ruby on Rails project is now completely functional with Webpacker.
Add Stimulus
Run this command to add stimulus
yarn add stimulus
Now we need to import stimulus to the application using webpack . First, in javascript directory, create controllers
folder and then add index.js
there. This index.js
file should have the following code.
import { Application } from "stimulus"
import { definitionsFromContext } from "stimulus/webpack-helpers"
const application = Application.start()
const context = require.context(".", true, /\.js$/)
application.load(definitionsFromContext(context))
Then reference this folder inside application.js
. This file should have the following code:
import Rails from "@rails/ujs"import Turbolinks from "turbolinks"import * as ActiveStorage from "@rails/activestorage"import "channels"import "controllers"import "@hotwired/turbo-railsRails.start()Turbolinks.start()ActiveStorage.start()
Add turbo and redis
Add turbo-rails with following command
bundle add turbo-rails
Then install with
rails turbo:install
Now we need to install redis as well
rails turbo:install:redis
We’re done with setup.
Add Ping controller
Now we will add a controller with an action named as ping
. The controller will be named as PingController
. Use the following command for this
rails g controller ping
This controller should have the following code:
class PingController < ApplicationController
def ping
render status: :ok, body: "PONG"
end
end
Let’s add a route for this action in the route file as in below
get “/ping”, to: “ping#ping”
Now create a form in app/views/shared/_ping.html.erb
to hit this rout.
<div data-controller="ping" >
<%= form_tag ping_path, method: :get, data: { action: "turbo:before-fetch-request->ping#pauseRequest turbo:submit-end->ping#measureLatency", "ping-target": "pingForm" } do %>
<%= button_tag "Ping" %>
<% end %>
<span data-ping-target="latency"></span></div>
Here, we need to create a controller
which will display this _ping
partial. We will name this controller as HomeController
and and add a show
action. For this, use the following command:
rails g controller home show
And then we will make this action as root
. Go to the routes.rb
file and add this line
root to: “home#show”
To render ping
partial, add following code in app/views/home/show.html.erb
<%= render "shared/ping" %>
At this point, we can hit the ping
button. Next step is to write a function to calculate the ping time.
Calculating ping time
For this, we need to create a stimulus controller. Use following command for this
rails g stimulus ping
This will create a file app/javascript/controllers/ping_controller.js
. Now place the following cod in this file
import { Controller } from "stimulus";
export default class extends Controller {
static targets = ["pingForm", "latency"]
pauseRequest(event) {
event.preventDefault();
setTimeout(() => this.saveRequestTime());
event.detail.resume();
}
saveRequestTime() {
this.requestTime = new Date().getTime();
}
measureLatency() {
this.saveResponseTime();
this.latency = this.responseTime - this.requestTime;
console.log(`${this.latency} ms`);
this.displayLatency()
setTimeout(() => this.ping(), 1000)
}
displayLatency() {
this.latencyTarget.textContent = this.latency + " ms"
}
saveResponseTime() {
this.responseTime = new Date().getTime();
}
ping() {
this.pingFormTarget.requestSubmit()
}
get requestTime() {
return this._requestTime;
}
set requestTime(requestTime) {
this._requestTime = requestTime;
}
get responseTime() {
return this._responseTime;
}
set responseTime(responseTime) {
this._responseTime = responseTime;
}
get latency() {
return this._latency;
}
set latency(latency) {
this._latency = latency;
}
}
What we’re doing in this code is
- Pause Request
- Save the response time
- Compute the difference between the response and request time
- Display the result
At this point, we’re all set. If you go to http://localhost:3000
and click Ping
button, you will see the response time being displayed on the page after every second as shown below.
Discussion
This is how you can monitor your application’s latency time. This will help you understanding how user friendly your app is and will indicate you if you need to scale your application because time is everything these days. If you’re working on improving your app performance, this simple tool will help you.