Gatling’s pacing and think time aren’t just about slowing down your tests; they’re how you simulate realistic user behavior, differentiating between a bot hammering your server and a human browsing.

Let’s see this in action. Imagine a simple scenario where a user logs in, views a product, and adds it to their cart.

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class PacingSimulation extends Simulation {

  val httpProtocol = http
    .baseUrl("http://localhost:8080") // Replace with your actual base URL

  val scn = scenario("User Journey")
    .exec(http("Login")
      .post("/login")
      .formParam("username", "testuser")
      .formParam("password", "password123")
      .check(status.is(200))
    )
    .pause(5 seconds, 10 seconds) // Think time between login and viewing product
    .exec(http("View Product")
      .get("/products/123")
      .check(status.is(200))
    )
    .pause(2 seconds, 5 seconds) // Think time between viewing product and adding to cart
    .exec(http("Add to Cart")
      .post("/cart/add")
      .formParam("productId", "123")
      .formParam("quantity", "1")
      .check(status.is(200))
    )

  setUp(scn.inject(
    rampUsersPerSec(10) to 100 during (30 seconds) // Pacing: Users per second ramp-up
  ).protocols(httpProtocol))
}

In this example, pause(5 seconds, 10 seconds) injects random "think time" between requests, mimicking a user pausing to read or decide. The rampUsersPerSec in setUp controls the rate at which new virtual users start their scenarios, which is your pacing. It means Gatling will ramp up from 10 users starting per second to 100 users starting per second over 30 seconds. This isn’t about how fast requests are made, but how quickly new users begin their journey.

The core problem these features solve is the "load generator effect." Without pacing and think time, your load generator becomes part of the bottleneck, hammering the system with requests as fast as possible, which doesn’t reflect real-world user interaction. Realistic think times ensure that the load on your system is spread out over time, and the rate at which users start their sessions (pacing) dictates the overall concurrency.

Internally, Gatling manages these delays by scheduling the next action for a virtual user after a specified duration. pause introduces a random delay within the given range for a single user’s session. Pacing, controlled in setUp, influences the Simulation’s injection profile, determining how many new users are injected into the system at specific time intervals. If you wanted to simulate a constant stream of users arriving, you’d use constantUsersPerSec(100) during (1 minute).

The one thing most people don’t realize is that pause applies between exec blocks. If you have multiple http requests within a single exec block (e.g., using group), the pause after that exec applies after all requests in the group have completed, not between individual requests within the group. This means a pause after a group might feel much longer than expected if the requests within the group themselves are slow.

Understanding the interplay between the rate of user injection (pacing) and the delays within a user’s session (think time) is crucial for accurate performance testing.

Want structured learning?

Take the full Gatling course →