Enabling Server Ping on a Rails App

Paul Lemus
4 min readAug 22, 2022

--

Performance is User Experience

Our web app needs to be as fast as our users are. Competitive games such as League of Legends or CS:GO often have an option to display the current latency (or “lag”) directly on the HUD, typically aiming for <100ms response times.

On a web application, poor latency times can result in an userbase abandoning the site and never returning. So, how can we make sure if we’re performing at a competitive level ourselves?

Let’s code our own lag meter using Hotwire.

Setup

All we need for our server ping is a non-redirecting controller action and a route. We’ll be using the ping_path route helper for the form_tag that will be starting the response cycle.

#config/routes.rb
get '/ping', to: 'ping#pong', as: 'ping'

And the controller action to send back a basic response.

#app/controllers/ping_controller.rb
def pong
render status: :ok, body: ‘PONG’
end

Sending the request

Preparation

  • Create a _ping.html.haml (or .erb) partial in the shared directory in your views folder.
  • Add render "shared/ping" to a view.
  • In our _ping partial, create a form to send a request (using ping-path).
Console displaying response from server

Attach Behavior

Turbo Drive makes this easy for us by allowing us to attach behavior at different points in the response cycle with Turbo Drive Events.

Turbo Events and Corresponding Behaviors

Start the timer

To our form_tag, we'll attach the first behavior using a Turbo Drive Event as a trigger.

= form_tag ping_path ... data: { action: "turbo:before-fetch-request->ping#pauseRequest"} do |f|

In our data-action we can see the trigger before-fetch-request triggering ping#pauseRequest behavior

  1. Setup Data Controller
  2. Create the Stimulus controller.

rails g stimulus PingController

Next, we’ll establish the data-controller. Let’s embed the form_tag in a div with the data-controller attribute.

%div{ :data=>{"controller": "ping"} }
form_tag_here

Setup #pauseRequest in the Stimulus ping-controller.

//app/javascript/controllers/ping_controller.rb
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="ping"
export default class extends Controller {
pauseRequest(event){}
}

Recording the time of request

Three steps. Pause, record time, continue.
//app/javascript/controllers/ping_controller.rb
pauseRequest(event) {
event.preventDefault()
setTimeout(() => this.saveRequestTime() )event.detail.resume()
}
saveRequestTime() {
this.requestTime = new Date().getTime()
}

Stopping the timer

turbo:submit-end will stop the timer when the response cycle is complete.

= form_tag ping_path ... data: { action:
"... turbo:submit-end->ping#stopTimer"} do |f|

In the same Ping data-controller, we'll attach a new series of behaviors.

stopTimer() {
this.saveResponseTime()
this.latency = this.responseTime - this.requestTime
}

Within the div with the data-controller attribute, we'll add a tag with a data-ping-target so the controller can display the current ping.

%p{ :data=>{"ping-target": "latencyValue"} }

And adding the target and display behavior to the controller.

export default class extends Controller {
static targets = ["pingForm", "latencyValue"]
//...
stopTimer() {
//...
this.valueTarget.textContent = `${this.latency} ms`
}
//...

Looping it

Now, we’ll have Stimulus trigger a loop by attaching a data-target to the form_tag itself, causing it to resubmit.

Let’s add the behavior first.

rerun() {
this.pingResubmitTarget.requestSubmit()
}

And the data-target to the form_tag.

= form_tag ping_path ... data: { action: "...ping#stopTimer", "ping-target": "pingResubmit"} do |f|

In our stopTimer behavior, let's add a 1s timeout before triggering the form submission rerun(). And with that, our server ping feature will now be looping.

stopTimer() {
this.saveResponseTime()
this.latency = this.responseTime - this.requestTimethis.latencyValueTarget.textContent = `${this.latency} ms`
setTimeout(() => this.rerun), 1000)
}

Now, we’re all done. Try out the feature and let’s see if the latency is below threshold.

What We Covered

We learned how to use Stimulus + Turbo Events to add a server ping feature on our Rails app.

This number not just indicates the speed of our app, but also the quality of it. We’re telling our users that their actions matter. A seamless experience where tech does not impair thought.

If you have any suggestions to improve this article, please leave a comment down below.

--

--