> OctoPerf load testing - K6 variables and data documentation page. How a K6 script reads the variables, CSV files and project files of an OctoPerf project.

Source: https://api.octoperf.com/doc/design/edit-virtual-user/action-types/k6-actions/k6-variables/

# K6 variables and data

The [variables](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/) and [files](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/files/) of the project reach a [K6 Virtual User](https://api.octoperf.com/doc/design/create-virtual-user/import-k6-scripts/) with nothing to configure: the constants and secrets are environment variables, and a module that OctoPerf generates next to the scripts, `octoperf.js`, gives access to the others.

## Constants and secrets

Every [constant](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/constant/) and [secret](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/secret/) of the project is an `__ENV` entry of the script, under its name:

```js
import http from 'k6/http';

export default function () {
  http.post(`${__ENV.BASE_URL}/login`, { user: __ENV.LOGIN, password: __ENV.PASSWORD });
}
```

The values are handed to K6 through its environment: they never appear on the command line of K6, in the environment of the container or in the logs of OctoPerf. A secret the script prints itself, with `console.log`, still lands in the K6 log as it is.

!!! info
    The [Environment](https://api.octoperf.com/doc/runtime/edit-scenario/edit-user-profile/options/k6/#environment) of a K6 user profile adds `__ENV` entries of its own; an entry named like a project variable wins over the variable.

## The octoperf.js module

```js
import { run, vars, random, csv, row } from './octoperf.js';
```

| Export | What the script gets |
|--------|----------------------|
| `run` | the identity of the test and of this load generator: `benchResultId`, `regionId`, `injectorId`, `virtualUserName`, `userProfileIndex` |
| `vars` | the constants and secrets, the same values as `__ENV`, as an object |
| `random(name)` | a new value of the [random variable](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/random/) `name` at every call, an integer between its min and max, padded with zeros when its output format is a run of `0`s |
| `csv(name)` | every row of the [CSV variable](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/csv/) `name` this load generator holds, as objects keyed by its column names, or by the header line of the file when the variable names no column |
| `row(name)` | the row of the CSV variable `name` for the current iteration |

`octoperf.js` and every file name starting with `__octoperf_` are reserved: OctoPerf generates those files next to the scripts, and a script cannot take one of their names. To run the scripts outside OctoPerf, [download the Virtual User](https://api.octoperf.com/doc/design/edit-virtual-user/action-types/k6-actions/k6-script-action/#run-it-with-k6): the ZIP file carries a local copy of these files.

The [code editor](https://api.octoperf.com/doc/design/edit-virtual-user/action-types/k6-actions/k6-script-action/#auto-completion) completes the variables of the project in these forms: `__ENV.<name>` for constants and secrets, `row('<name>')` for CSV variables, `random('<name>')` for random variables.

![Variables completion](https://api.octoperf.com/doc/design/edit-virtual-user/action-types/k6-actions/img/variables-completion.png "Variables completion")

## CSV data

```js
import http from 'k6/http';
import { row } from './octoperf.js';

export default function () {
  const customer = row('customers');
  http.get(`${__ENV.BASE_URL}/customers/${customer.id}`);
}
```

- **The [sharing mode](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/variables/csv/#sharing-mode) of the variable decides what each load generator gets:**
    - **Shared**: the file is split between the load generators of the test, so two load generators never read the same row. A part keeps the header line when the file has one. Pick it for unique test data, such as accounts or orders. A file holding fewer rows than there are load generators is not split: each one gets it whole.
    - **Private**: every load generator gets the whole file. Pick it for reference data each load generator must walk whole, such as a catalog or a list of pages.
- **`row()` hands out one row per iteration**, in the order of the file, and starts over from the first row once it has read the last one. The rows are counted per load generator, not per virtual user: in Private mode, every load generator reads the same rows in the same order. It never ends an iteration: the **Recycle on EOF** and **Stop thread on EOF** settings of a CSV variable do not apply to K6.
- **`csv()` returns every row of the load generator**, loaded once, when the script starts: pick from it when the script needs its own order.

!!! warning
    A CSV variable is split and sent to the load generators only when a script names it between quotes, like `row('customers')` or `csv("customers")`. A name built at run time, `csv('cust' + 'omers')` or a name read from a variable, leaves the file out of the load generators.

The virtual users of one K6 user profile are also split between its load generators by K6 itself, with `--execution-segment`: `__VU` numbers the virtual users of one load generator. Do not share out the data by hand from `__VU`, `run.injectorId` or the length of the file: the split already does it.

## Project files

The [files](https://api.octoperf.com/doc/design/edit-virtual-user/configuration/files/) of the project sit next to the scripts, so a script opens one by its name:

```js
const invoice = open('invoice.pdf', 'b');
```

The name of a file can come from a variable, like `` open(`batch-${__ENV.BATCH}.json`) ``, as long as the file it names is a file of the project. A file opened this way is sent whole to every load generator, so every load generator reads the same rows: the sanity check warns when a script opens, other than in binary mode, a file that no CSV variable declares. Declare a CSV variable on it and read it with `row()` or `csv()` instead: in Shared mode to split it, in Private mode to keep sending it whole.

## What K6 scripts do not get

- **Counter variables**: use `__VU`, which numbers the virtual user, `__ITER`, which numbers its iteration, or `k6/execution`, with `exec.vu.idInTest` and `exec.scenario.iterationInTest`. The sanity check refuses a script that references a counter variable.
