<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Concepts on Grafana Labs</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/</link><description>Recent content in Concepts on Grafana Labs</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="/docs/k6/v2.3.x/using-k6/scenarios/concepts/index.xml" rel="self" type="application/rss+xml"/><item><title>Graceful stop</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/graceful-stop/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/graceful-stop/</guid><content><![CDATA[&lt;h1 id=&#34;graceful-stop&#34;&gt;Graceful stop&lt;/h1&gt;
&lt;p&gt;The &lt;code&gt;gracefulStop&lt;/code&gt; is a period at the end of the test in which k6 lets iterations in progress finish.&lt;/p&gt;
&lt;p&gt;If a test has a set duration or ramp down, its possible that k6 could interrupt iterations in progress.
These interruptions can lead to skewed metrics and unexpected test results.
To deal with this, k6 scenarios have a &lt;code&gt;gracefulStop&lt;/code&gt;.
For the &lt;code&gt;ramping-vus&lt;/code&gt; executor, a related option, &lt;code&gt;gracefulRampDown&lt;/code&gt;, exists to let VUs finish as their number ramps down.&lt;/p&gt;
&lt;h2 id=&#34;graceful-stop-1&#34;&gt;Graceful stop&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;gracefulStop&lt;/code&gt; option is available for all executors.
It specifies a duration that k6 will wait before forcefully interrupting an iteration.
The default value is &lt;code&gt;30s&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;graceful-stop-example&#34;&gt;Graceful stop example&lt;/h3&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;

export const options = {
  discardResponseBodies: true,
  scenarios: {
    contacts: {
      executor: &amp;#39;constant-vus&amp;#39;,
      vus: 100,
      duration: &amp;#39;10s&amp;#39;,
      gracefulStop: &amp;#39;3s&amp;#39;,
    },
  },
};

export default function () {
  const delay = Math.floor(Math.random() * 5) &amp;#43; 1;
  http.get(`https://quickpizza.grafana.com/api/delay/${delay}`);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Running this script would result in something like:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;running (13.0s), 000/100 VUs, 349 complete and 23 interrupted iterations
contacts ✓ [======================================] 100 VUs  10s&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Notice that even though the total test duration is 10s, the actual execution time was 13s
because of &lt;code&gt;gracefulStop&lt;/code&gt;, giving the VUs a 3s additional time to complete iterations in progress. 23
of the iterations currently in progress did not complete within this window and was therefore interrupted.&lt;/p&gt;
&lt;h2 id=&#34;the-gracefulrampdown&#34;&gt;The &lt;code&gt;gracefulRampDown&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In addition to &lt;code&gt;gracefulStop&lt;/code&gt;, the 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/executors/ramping-vus/&#34;&gt;ramping-vus&lt;/a&gt; executor also has a &lt;code&gt;gracefulRampDown&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When the target value for a stage is lower than the target for the previous stage, k6 might need to stop some VUs that were started during the previous stages.
The &lt;code&gt;gracefulRampDown&lt;/code&gt; option controls how long these VUs have to finish currently before k6 interrupts them.&lt;/p&gt;
&lt;p&gt;To get an idea of how &lt;code&gt;gracefulRampDown&lt;/code&gt; works, you can run the following script with
&lt;code&gt;k6 run --verbose&lt;/code&gt;.
In this script, the iteration &lt;code&gt;sleep&lt;/code&gt; time exceeds the &lt;code&gt;gracefulRampdown&lt;/code&gt; time.
So, as k6 ramps down to reach the target of the second stage, it must forcibly interrupt VUs.
The &lt;code&gt;--verbose&lt;/code&gt; flag will log to your console when VUs start, enter the grace period, and are forcibly interrupted.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;
import { sleep } from &amp;#39;k6&amp;#39;;

export const options = {
  discardresponsebodies: true,
  scenarios: {
    contacts: {
      executor: &amp;#39;ramping-vus&amp;#39;,
      startvus: 0,
      stages: [
        { duration: &amp;#39;10s&amp;#39;, target: 10 },
        { duration: &amp;#39;10s&amp;#39;, target: 0 },
      ],
      gracefulRampDown: &amp;#39;1s&amp;#39;,
    },
  },
};

export default function () {
  http.get(&amp;#39;https://test.k6.io/contacts.php&amp;#39;);
  // adding sleep beyond so that iterations are longer than rampdown
  sleep(5);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="graceful-stop">Graceful stop&lt;/h1>
&lt;p>The &lt;code>gracefulStop&lt;/code> is a period at the end of the test in which k6 lets iterations in progress finish.&lt;/p>
&lt;p>If a test has a set duration or ramp down, its possible that k6 could interrupt iterations in progress.
These interruptions can lead to skewed metrics and unexpected test results.
To deal with this, k6 scenarios have a &lt;code>gracefulStop&lt;/code>.
For the &lt;code>ramping-vus&lt;/code> executor, a related option, &lt;code>gracefulRampDown&lt;/code>, exists to let VUs finish as their number ramps down.&lt;/p></description></item><item><title>Arrival-rate VU allocation</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/arrival-rate-vu-allocation/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/arrival-rate-vu-allocation/</guid><content><![CDATA[&lt;h1 id=&#34;arrival-rate-vu-allocation&#34;&gt;Arrival-rate VU allocation&lt;/h1&gt;
&lt;p&gt;In arrival-rate executors, as long as k6 has VUs available, it starts iterations according to your target rate.
The ability to set iteration rate comes with a bit more configuration complexity: you must pre-allocate a sufficient number of VUs.
In other words, before the tests runs, you must both:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configure load (as new iterations per unit of time)&lt;/li&gt;
&lt;li&gt;Ensure that you&amp;rsquo;ve allocated enough VUs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Read on to learn about how k6 allocates VUs in the arrival-rate executors.&lt;/p&gt;


&lt;div class=&#34;admonition admonition-caution&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Caution&lt;/p&gt;&lt;p&gt;In cloud tests, &lt;strong&gt;&lt;code&gt;preAllocatedVUs&lt;/code&gt; count against your subscription.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When planning a test, consider doing a trial initialization on a local machine to ensure you&amp;rsquo;re allocating VUs efficiently.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;h2 id=&#34;pre-allocation-in-arrival-rate-executors&#34;&gt;Pre-allocation in arrival-rate executors&lt;/h2&gt;
&lt;p&gt;As 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/concepts/open-vs-closed/#open-model&#34;&gt;open-model&lt;/a&gt; scenarios, arrival-rate executors start iterations according to a configured rate.
For example, you can configure arrival-rate executors to start 10 iterations each second, or minute, or hour.
This behavior is opposed to the closed-model scenarios, in which VUs wait for one iteration to finish before starting another&lt;/p&gt;
&lt;p&gt;Each iteration needs a VU to run it.
Because k6 VUs are single threaded, like other JavaScript runtimes, a VU can only run a single iteration (and its event loop) at a time.
To ensure you have enough, you must pre-allocate a sufficient number.&lt;/p&gt;
&lt;p&gt;In your arrival-rate configuration, three properties determine the iteration rate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;k6 starts &lt;code&gt;rate&lt;/code&gt; number of iterations evenly across the &lt;code&gt;timeUnit&lt;/code&gt; (default 1s).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;preAllocatedVUs&lt;/code&gt; sets the number of VUs to initialize to meet the target iteration rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, with a &lt;code&gt;constant-arrival-rate&lt;/code&gt; executor and &lt;code&gt;rate: 10&lt;/code&gt;, k6 tries to start a new iteration every 100 milliseconds. If the scenario has &lt;code&gt;rate: 10, timeUnit: &#39;1m&#39;&lt;/code&gt;, k6 tries to start a new iteration every 6 seconds.
Whether it &lt;em&gt;can&lt;/em&gt; start the target iteration rate depends on whether the number of allocated VUs is sufficient.&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;export const options = {
  scenarios: {
    constant_load: {
      executor: &amp;#39;constant-arrival-rate&amp;#39;,
      preAllocatedVUs: 10,
      rate: 10,
      timeUnit: &amp;#39;1m&amp;#39;,
    },
  },
};&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In practice, determining the right number of VUs to be able to reach the target iteration rate might take some trial and error,
as the necessary VUs entirely depends on what your iteration code looks like and how quickly the system under test can process requests.&lt;/p&gt;
&lt;h2 id=&#34;how-k6-uses-allocated-vus&#34;&gt;How k6 uses allocated VUs&lt;/h2&gt;
&lt;p&gt;Before an arrival-rate scenario starts, k6 first initializes the number of &lt;code&gt;preAllocatedVUs&lt;/code&gt;.
When the test runs,
the number of available &lt;code&gt;preAllocatedVUs&lt;/code&gt; determines how many iterations k6 can start.
k6 tries to reach the target iterations per second,
and one of two things can happen:&lt;/p&gt;
&lt;section class=&#34;expand-table-wrapper&#34;&gt;&lt;div class=&#34;button-div&#34;&gt;
      &lt;button class=&#34;expand-table-btn&#34;&gt;Expand table&lt;/button&gt;
    &lt;/div&gt;&lt;div class=&#34;responsive-table-wrapper&#34;&gt;
    &lt;table&gt;
      &lt;thead&gt;
          &lt;tr&gt;
              &lt;th&gt;If the executor&lt;/th&gt;
              &lt;th&gt;Then..&lt;/th&gt;
          &lt;/tr&gt;
      &lt;/thead&gt;
      &lt;tbody&gt;
          &lt;tr&gt;
              &lt;td&gt;Has enough VUs&lt;/td&gt;
              &lt;td&gt;the extra VUs are &amp;ldquo;idle,&amp;rdquo; ready to be used when needed.&lt;/td&gt;
          &lt;/tr&gt;
          &lt;tr&gt;
              &lt;td&gt;Has insufficient VUs.&lt;/td&gt;
              &lt;td&gt;k6 emits a 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/concepts/dropped-iterations/&#34;&gt;&lt;code&gt;dropped_iterations&lt;/code&gt; metric&lt;/a&gt; for each iteration that it can&amp;rsquo;t run.&lt;/td&gt;
          &lt;/tr&gt;
      &lt;/tbody&gt;
    &lt;/table&gt;
  &lt;/div&gt;
&lt;/section&gt;&lt;h2 id=&#34;iteration-duration-affects-the-necessary-allocation&#34;&gt;Iteration duration affects the necessary allocation&lt;/h2&gt;
&lt;p&gt;The necessary allocation depends on the iteration duration:
longer durations need more VUs.&lt;/p&gt;
&lt;p&gt;In a perfect world, you could estimate the number of pre-allocated VUs with this formula:&lt;/p&gt;

&lt;div class=&#34;code-snippet code-snippet__mini&#34;&gt;&lt;div class=&#34;lang-toolbar__mini&#34;&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet code-snippet__border&#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-none&#34;&gt;preAllocatedVUs = [median_iteration_duration * rate] &amp;#43; constant_for_variance&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;In the real world, if you know &lt;em&gt;exactly&lt;/em&gt; how long an iteration takes, you likely don&amp;rsquo;t need to run a test.
What&amp;rsquo;s more, as the test goes on, iteration duration likely increases.
If response times slow so much that k6 lacks the VUs to start iterations at the desired rate,
the allocation might be insufficient and k6 will drop iterations.&lt;/p&gt;
&lt;p&gt;To determine your strategy, you can run tests locally and gradually add more pre-allocated VUs.
As dropped iterations can also indicate that the system performance is degrading, this early experimentation can provide useful data on its own.&lt;/p&gt;
&lt;h2 id=&#34;you-probably-dont-need-maxvus&#34;&gt;You probably don&amp;rsquo;t need &lt;code&gt;maxVUs&lt;/code&gt;&lt;/h2&gt;


&lt;div class=&#34;admonition admonition-caution&#34;&gt;&lt;blockquote&gt;&lt;p class=&#34;title text-uppercase&#34;&gt;Caution&lt;/p&gt;&lt;p&gt;In cloud tests, the number of &lt;code&gt;maxVUs&lt;/code&gt; counts against your subscription,
&lt;strong&gt;overriding the number set by &lt;code&gt;preAllocatedVUs&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;/div&gt;

&lt;p&gt;The arrival-rate executors also have a &lt;code&gt;maxVUs&lt;/code&gt; property.
If you set it, k6 runs in this sequence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pre-allocate the &lt;code&gt;preAllocatedVUs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run the test, trying to reach the target iteration rate.&lt;/li&gt;
&lt;li&gt;If the target exceeds the available VUs, allocate another VU.&lt;/li&gt;
&lt;li&gt;If the target still exceeds available VUs, continue allocating VUs until reaching the number set by &lt;code&gt;maxVUs&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Though it seems convenient, you should avoid using &lt;code&gt;maxVUs&lt;/code&gt; in most cases.
Allocating VUs has CPU and memory costs, and allocating VUs as the test runs &lt;strong&gt;can overload the load generator and skew results&lt;/strong&gt;.
In Cloud tests, the number of &lt;code&gt;maxVUs&lt;/code&gt; count against your subscription.
This is because k6 must allocate sufficient resources for &lt;code&gt;maxVUs&lt;/code&gt; to be initialized, even if they never are.
In almost all cases, the best thing to do is to pre-allocate the number of VUs you need beforehand.&lt;/p&gt;
&lt;p&gt;Some of the times it might make sense to use &lt;code&gt;maxVUs&lt;/code&gt; include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To determine necessary allocation in first-time tests&lt;/li&gt;
&lt;li&gt;To add a little &amp;ldquo;cushion&amp;rdquo; to the pre-allocated VUs that you expect the test needs&lt;/li&gt;
&lt;li&gt;In huge, highly distributed tests, in which you need to carefully scale load generators as you increment VUs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;you-cant-guarantee-which-vu-runs-an-iteration&#34;&gt;You can&amp;rsquo;t guarantee which VU runs an iteration&lt;/h2&gt;
&lt;p&gt;As with all executors, you can&amp;rsquo;t predict the specific VU that an arrival-rate executor uses for a specific iteration.
As the test runs on, the executor might use some or all of the allocated VUs, even if it never needs the entire allocated number to reach the iteration rate.&lt;/p&gt;
]]></content><description>&lt;h1 id="arrival-rate-vu-allocation">Arrival-rate VU allocation&lt;/h1>
&lt;p>In arrival-rate executors, as long as k6 has VUs available, it starts iterations according to your target rate.
The ability to set iteration rate comes with a bit more configuration complexity: you must pre-allocate a sufficient number of VUs.
In other words, before the tests runs, you must both:&lt;/p></description></item><item><title>Dropped iterations</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/dropped-iterations/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/dropped-iterations/</guid><content><![CDATA[&lt;h1 id=&#34;dropped-iterations&#34;&gt;Dropped iterations&lt;/h1&gt;
&lt;p&gt;Sometimes, a scenario can&amp;rsquo;t run the expected number of iterations.
k6 tracks the number of unsent iterations in a counter metric, &lt;code&gt;dropped_iterations&lt;/code&gt;.
The number of dropped iterations can be valuable data when you debug executors or analyze results.&lt;/p&gt;
&lt;p&gt;Dropped iterations usually happen for one of two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The executor configuration is insufficient.&lt;/li&gt;
&lt;li&gt;The SUT can&amp;rsquo;t handle the configured VU arrival rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;configuration-related-iteration-drops&#34;&gt;Configuration-related iteration drops&lt;/h3&gt;
&lt;p&gt;Dropped iterations happen for different reasons in different types of executors.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;shared-iterations&lt;/code&gt; and &lt;code&gt;per-vu-iterations&lt;/code&gt;, iterations drop if the scenario reaches its &lt;code&gt;maxDuration&lt;/code&gt; before all iterations finish.
To mitigate this, you likely need to increase the value of the duration.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;constant-arrival-rate&lt;/code&gt; and &lt;code&gt;ramping-arrival-rate&lt;/code&gt;, iterations drop if there are no free VUs.
&lt;strong&gt;If it happens at the beginning of the test, you likely just need to allocate more VUs.&lt;/strong&gt;
If this happens later in the test, the dropped iterations might happen because SUT performance is degrading and iterations are taking longer to finish.&lt;/p&gt;
&lt;h3 id=&#34;sut-related-iteration-drops&#34;&gt;SUT-related iteration drops&lt;/h3&gt;
&lt;p&gt;At a certain point of high latency or longer iteration durations, k6 will no longer have free VUs to start iterations with at the configured rate.
As a result, the executor will drop iterations.&lt;/p&gt;
&lt;p&gt;The reasons for these dropped iterations vary:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The SUT response has become so long that k6 starts dropping scheduled iterations from the queue.&lt;/li&gt;
&lt;li&gt;The SUT iteration duration has become so long that k6 needs to schedule more VUs to reach the target arrival rate, exceeding the number of scheduled iterations.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As the causes vary, dropped iterations might mean different things.
A few dropped iterations might indicate a quick network error.
Many dropped iterations might indicate that your SUT has completely stopped responding.&lt;/p&gt;
&lt;p&gt;When you design your test, consider what an acceptable rate of dropped iterations is (the &lt;em&gt;error budget&lt;/em&gt;).
To assert that the SUT responds within this error budget, you can use the &lt;code&gt;dropped_iterations&lt;/code&gt; metric in a 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/thresholds/&#34;&gt;Threshold&lt;/a&gt;.&lt;/p&gt;
]]></content><description>&lt;h1 id="dropped-iterations">Dropped iterations&lt;/h1>
&lt;p>Sometimes, a scenario can&amp;rsquo;t run the expected number of iterations.
k6 tracks the number of unsent iterations in a counter metric, &lt;code>dropped_iterations&lt;/code>.
The number of dropped iterations can be valuable data when you debug executors or analyze results.&lt;/p></description></item><item><title>Open and closed models</title><link>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/open-vs-closed/</link><pubDate>Mon, 21 Sep 2026 15:16:28 +0000</pubDate><guid>https://grafana.com/docs/k6/v2.3.x/using-k6/scenarios/concepts/open-vs-closed/</guid><content><![CDATA[&lt;h1 id=&#34;open-and-closed-models&#34;&gt;Open and closed models&lt;/h1&gt;
&lt;p&gt;Different k6 executors have different ways of scheduling VUs.
Some executors use the &lt;em&gt;closed model&lt;/em&gt;, while the arrival-rate executors use the &lt;em&gt;open model&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;In short, in the closed model, VU iterations start only when the last iteration finishes.
In the open model, on the other hand, VUs arrive independently of iteration completion.
Different models suit different test aims.&lt;/p&gt;
&lt;h2 id=&#34;closed-model&#34;&gt;Closed Model&lt;/h2&gt;
&lt;p&gt;In a closed model, the execution time of each iteration dictates the
number of iterations executed in your test.
The next iteration doesn&amp;rsquo;t start until the previous one finishes.&lt;/p&gt;
&lt;p&gt;Thus, in a closed model, the start or arrival rate of
new VU iterations is tightly coupled with the iteration duration (that is, time from start
to finish of the VU&amp;rsquo;s &lt;code&gt;exec&lt;/code&gt; function, by default the &lt;code&gt;export default function&lt;/code&gt;):&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;

export const options = {
  scenarios: {
    closed_model: {
      executor: &amp;#39;constant-vus&amp;#39;,
      vus: 1,
      duration: &amp;#39;1m&amp;#39;,
    },
  },
};

export default function () {
  // The following request will take roughly 6s to complete,
  // resulting in an iteration duration of 6s.

  // As a result, New VU iterations will start at a rate of 1 per 6s,
  // and we can expect to get 10 iterations completed in total for the
  // full 1m test duration.

  http.get(&amp;#39;https://quickpizza.grafana.com/api/delay/6&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Running this script would result in something like:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;
running (1m01.5s), 0/1 VUs, 10 complete and 0 interrupted iterations
closed_model ✓ [======================================] 1 VUs  1m0s&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;h3 id=&#34;drawbacks-of-using-the-closed-model&#34;&gt;Drawbacks of using the closed model&lt;/h3&gt;
&lt;p&gt;When the duration of the VU iteration is tightly coupled to the start of new VU iterations,
the target system&amp;rsquo;s response time can influence the throughput of the test.
Slower response times means longer iterations and a lower arrival rate of new iterations―and vice versa for faster response times.
In some testing literature, this problem is known as &lt;em&gt;coordinated omission.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In other words, when the target system is stressed and starts to respond more
slowly, a closed model load test will wait, resulting in increased
iteration durations and a tapering off of the arrival rate of new VU iterations.&lt;/p&gt;
&lt;p&gt;This effect is not ideal when the goal is to simulate a certain arrival rate of new VUs,
or more generally throughput (e.g. requests per second).&lt;/p&gt;
&lt;h2 id=&#34;open-model&#34;&gt;Open model&lt;/h2&gt;
&lt;p&gt;Compared to the closed model, the open model decouples VU iterations from
the iteration duration.
The response times of the target system no longer
influence the load on the target system.&lt;/p&gt;
&lt;p&gt;To fix this problem of coordination, you can use an open model,
which decouples the start of new VU iterations from the iteration duration.
This reduces the influence of the target system&amp;rsquo;s response time.&lt;/p&gt;
&lt;p&gt;&lt;img
  class=&#34;lazyload d-inline-block&#34;
  data-src=&#34;/media/docs/k6-oss/arrival-rate-open-closed-model.png&#34;
  alt=&#34;Arrival rate closed/open models&#34; width=&#34;768&#34;
     height=&#34;365&#34;/&gt;&lt;/p&gt;
&lt;p&gt;k6 implements the open model with two &lt;em&gt;arrival rate&lt;/em&gt; executors:

    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/executors/constant-arrival-rate/&#34;&gt;constant-arrival-rate&lt;/a&gt; and 
    &lt;a href=&#34;/docs/k6/v2.3.x/using-k6/scenarios/executors/ramping-arrival-rate/&#34;&gt;ramping-arrival-rate&lt;/a&gt;:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;JavaScript&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-javascript&#34;&gt;import http from &amp;#39;k6/http&amp;#39;;

export const options = {
  scenarios: {
    open_model: {
      executor: &amp;#39;constant-arrival-rate&amp;#39;,
      rate: 1,
      timeUnit: &amp;#39;1s&amp;#39;,
      duration: &amp;#39;1m&amp;#39;,
      preAllocatedVUs: 20,
    },
  },
};

export default function () {
  // With the open model arrival rate executor config above,
  // new VU iterations will start at a rate of 1 every second,
  // and we can thus expect to get 60 iterations completed
  // for the full 1m test duration.
  http.get(&amp;#39;https://quickpizza.grafana.com/api/delay/6&amp;#39;);
}&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Running this script would result in something like:&lt;/p&gt;

&lt;div class=&#34;code-snippet &#34;&gt;&lt;div class=&#34;lang-toolbar&#34;&gt;
    &lt;span class=&#34;lang-toolbar__item lang-toolbar__item-active&#34;&gt;Bash&lt;/span&gt;
    &lt;span class=&#34;code-clipboard&#34;&gt;
      &lt;button x-data=&#34;app_code_snippet()&#34; x-init=&#34;init()&#34; @click=&#34;copy()&#34;&gt;
        &lt;img class=&#34;code-clipboard__icon&#34; src=&#34;/media/images/icons/icon-copy-small-2.svg&#34; alt=&#34;Copy code to clipboard&#34; width=&#34;14&#34; height=&#34;13&#34;&gt;
        &lt;span&gt;Copy&lt;/span&gt;
      &lt;/button&gt;
    &lt;/span&gt;
    &lt;div class=&#34;lang-toolbar__border&#34;&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-snippet &#34;&gt;
    &lt;pre data-expanded=&#34;false&#34;&gt;&lt;code class=&#34;language-bash&#34;&gt;running (1m09.3s), 000/011 VUs, 60 complete and 0 interrupted iterations
open_model ✓ [======================================] 011/011 VUs  1m0s  1 iters/s&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;
&lt;/div&gt;
]]></content><description>&lt;h1 id="open-and-closed-models">Open and closed models&lt;/h1>
&lt;p>Different k6 executors have different ways of scheduling VUs.
Some executors use the &lt;em>closed model&lt;/em>, while the arrival-rate executors use the &lt;em>open model&lt;/em>.&lt;/p></description></item></channel></rss>