{"id":22546297,"url":"https://github.com/infinilabs/loadgen","last_synced_at":"2025-04-09T16:43:38.627Z","repository":{"id":266846743,"uuid":"896685292","full_name":"infinilabs/loadgen","owner":"infinilabs","description":"💣 INFINI Loadgen for API Benchmark \u0026 API Testing.","archived":false,"fork":false,"pushed_at":"2025-03-31T14:43:20.000Z","size":660,"stargazers_count":13,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-31T15:36:17.399Z","etag":null,"topics":["api","benchmark","benchmarking","ci-cd","continue-testing","elasticsearch","opensearch","pizza","testing"],"latest_commit_sha":null,"homepage":"https://docs.infinilabs.com/loadgen/main/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/infinilabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-01T02:59:02.000Z","updated_at":"2025-03-31T14:07:38.000Z","dependencies_parsed_at":"2024-12-29T09:17:37.217Z","dependency_job_id":"5abb5e1a-3112-423d-bef7-3aa7de8bf4cd","html_url":"https://github.com/infinilabs/loadgen","commit_stats":null,"previous_names":["infinilabs/loadgen"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinilabs%2Floadgen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinilabs%2Floadgen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinilabs%2Floadgen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/infinilabs%2Floadgen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/infinilabs","download_url":"https://codeload.github.com/infinilabs/loadgen/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248069568,"owners_count":21042666,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","benchmark","benchmarking","ci-cd","continue-testing","elasticsearch","opensearch","pizza","testing"],"created_at":"2024-12-07T15:06:51.499Z","updated_at":"2025-04-09T16:43:38.600Z","avatar_url":"https://github.com/infinilabs.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# INFINI Loadgen\n\n\nHighlights of Loadgen:\n\n- Robust performance\n- Lightweight and dependency-free\n- Random selection of template-based parameters\n- High concurrency\n- Balanced traffic control at the benchmark end\n- Validate server responses.\n\nInstall with script:\n\n```\ncurl -sSL http://get.infini.cloud | bash -s -- -p loadgen\n```\n\n\u003e Or download from here: [http://release.infinilabs.com/loadgen/](http://release.infinilabs.com/loadgen/)\n\n```\n➜  /tmp mkdir loadgen\n➜  /tmp curl -sSL http://get.infini.cloud | bash -s -- -p loadgen -d /tmp/loadgen\n\n                                 @@@@@@@@@@@\n                                @@@@@@@@@@@@\n                                @@@@@@@@@@@@\n                               @@@@@@@@@\u0026@@@\n                              #@@@@@@@@@@@@@\n        @@@                   @@@@@@@@@@@@@\n       \u0026@@@@@@@              \u0026@@@@@@@@@@@@@\n       @\u0026@@@@@@@\u0026@           @@@\u0026@@@@@@@\u0026@\n      @@@@@@@@@@@@@@@@      @@@@@@@@@@@@@@\n      @@@@@@@@@@@@@@@@@@\u0026   @@@@@@@@@@@@@\n        %@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n            @@@@@@@@@@@@\u0026@@@@@@@@@@@@@@@\n    @@         ,@@@@@@@@@@@@@@@@@@@@@@@\u0026\n    @@@@@.         @@@@@\u0026@@@@@@@@@@@@@@\n   @@@@@@@@@@          @@@@@@@@@@@@@@@#\n   @\u0026@@@\u0026@@@\u0026@@@          \u0026@\u0026@@@\u0026@@@\u0026@\n  @@@@@@@@@@@@@.              @@@@@@@*\n  @@@@@@@@@@@@@                  %@@@\n @@@@@@@@@@@@@\n/@@@@@@@\u0026@@@@@\n@@@@@@@@@@@@@\n@@@@@@@@@@@@@\n@@@@@@@@@@@@        Welcome to INFINI Labs!\n\n\nNow attempting the installation...\n\nName: [loadgen], Version: [1.26.1-598], Path: [/tmp/loadgen]\nFile: [https://release.infinilabs.com/loadgen/stable/loadgen-1.26.1-598-mac-arm64.zip]\n##=O#- #\n\nInstallation complete. [loadgen] is ready to use!\n\n\n----------------------------------------------------------------\ncd /tmp/loadgen \u0026\u0026 ./loadgen-mac-arm64\n----------------------------------------------------------------\n\n\n   __ _  __ ____ __ _  __ __\n  / // |/ // __// // |/ // /\n / // || // _/ / // || // /\n/_//_/|_//_/  /_//_/|_//_/\n\n©INFINI.LTD, All Rights Reserved.\n```\n\n## Loadgen\n\nLoadgen is easy to use. After the tool is downloaded and decompressed, two files are obtained: one executable program and one configuration file `loadgen.yml`. An example of the configuration file is as follows:\n\n```\nenv:\n  ES_USERNAME: elastic\n  ES_PASSWORD: elastic\nrunner:\n  # total_rounds: 1\n  no_warm: false\n  log_requests: false\n  assert_invalid: false\n  assert_error: false\nvariables:\n  - name: ip\n    type: file\n    path: test/ip.txt\n  - name: user\n    type: file\n    path: test/user.txt\n  - name: id\n    type: sequence\n  - name: uuid\n    type: uuid\n  - name: now_local\n    type: now_local\n  - name: now_utc\n    type: now_utc\n  - name: now_unix\n    type: now_unix\nrequests:\n  - request:\n      method: GET\n      basic_auth:\n        username: $[[env.ES_USERNAME]]\n        password: $[[env.ES_PASSWORD]]\n      url: http://localhost:8000/medcl/_search\n      body: '{  \"query\": {\"match\": {    \"name\": \"$[[user]]\"  }}}'\n```\n\n### Runner Configurations\n\nBy default, `loadgen` will run under the benchmarking mode, repeating through all the `requests` during the specified duration (`-d`). If you only need to test the responses, setting `runner.total_rounds: 1` will let `loadgen` run for only once.\n\n### HTTP Headers Canonization\n\nBy default, `loadgen` will canonilize the HTTP response header keys received from the server side (`user-agent: xxx` -\u003e `User-Agent: xxx`). If you need to assert the header keys exactly, you can set `runner.disable_header_names_normalizing: true` to disable this behavior.\n\n## Usage of Variables\n\nIn the above configuration, `variables` is used to define variable parameters and variables are identified by `name`. In a constructed request, `$[[Variable name]]` can be used to access the value of the variable. Supported variable types are as follows:\n\n| Type              | Description                                                                                              | Parameters                                                                                                                                                                                                                                     |\n| ----------------- | -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `file`            | Load variables from file                                                                                 | `path`: the path of the data files\u003cbr\u003e`data`: a list of values, will get appended to the end of the data specified by `path` file                                                                                                              |\n| `list`            | Defined variables inline                                                                                 | use `data` to define a string array                                                                                                                                                                                                            |\n| `sequence`        | 32-bit Variable of the auto incremental numeric type                                                     | `from`: the minimum of the values\u003cbr\u003e`to`: the maximum of the values                                                                                                                                                                           |\n| `sequence64`      | 64-bit Variable of the auto incremental numeric type                                                     | `from`: the minimum of the values\u003cbr\u003e`to`: the maximum of the values                                                                                                                                                                           |\n| `range`           | Variable of the range numbers, support parameters `from` and `to` to define the range                    | `from`: the minimum of the values\u003cbr\u003e`to`: the maximum of the values                                                                                                                                                                           |\n| `random_array`    | Generate a random array from the variable specified by `variable_key`                                    | `variable_key`: the variable name for the source of array values\u003cbr\u003e`size`: the size of array\u003cbr\u003e`square_bracket`: `true/false`, whether to add `[]` for the outputed array\u003cbr\u003e`string_bracket`: the string to surround the outputed elements. |\n| `uuid`            | Variable of the UUID character type                                                                      |                                                                                                                                                                                                                                                |\n| `now_local`       | Current time and local time zone                                                                         |                                                                                                                                                                                                                                                |\n| `now_utc`         | Current time and UTC time zone                                                                           |                                                                                                                                                                                                                                                |\n| `now_unix`        | Current time and Unix timestamp                                                                          |                                                                                                                                                                                                                                                |\n| `now_with_format` | Current time，support parameter `format` to customize the output format， eg: `2006-01-02T15:04:05-0700` | `format`: the format of the time output ([Example](https://www.geeksforgeeks.org/time-formatting-in-golang/))                                                                                                                                  |\n\n### Examples\n\nVariable parameters of the `file` type are loaded from an external text file. One variable parameter occupies one line. When one variable of the file type is accessed, one variable value is taken randomly. An example of the variable format is as follows:\n\n```\n➜  loadgen git:(master) ✗ cat test/user.txt\nmedcl\nelastic\n```\n\nTips about how to generate a random string of fixed length, such as 1024 per line:\n\n```\nLC_CTYPE=C tr -dc A-Za-z0-9_\\!\\@\\#\\$\\%\\^\\\u0026\\*\\(\\)-+= \u003c /dev/random | head -c 1024 \u003e\u003e 1k.txt\n```\n\n### Environment Variables\n\n`loadgen` supporting loading and using environment variables in `loadgen.yml`, you can specify the default values in `env` configuration. `loadgen` will overwrite the variables at runtime if they're also specified by the command-line environment.\n\nThe environment variables can be access by `$[[env.ENV_KEY]]`:\n\n```\n# Default values for the environment variables.\nenv:\n  ES_USERNAME: elastic\n  ES_PASSWORD: elastic\n  ES_ENDPOINT: http://localhost:8000\nrequests:\n  - request:\n      method: GET\n      basic_auth:\n        username: $[[env.ES_USERNAME]] # Use environment variables\n        password: $[[env.ES_PASSWORD]] # Use environment variables\n      url: $[[env.ES_ENDPOINT]]/medcl/_search # Use environment variables\n      body: '{  \"query\": {\"match\": {    \"name\": \"$[[user]]\"  }}}'\n```\n\n## Request Definition\n\nThe `requests` node is used to set requests to be executed by Loadgen in sequence. Loadgen supports fixed-parameter requests and requests constructed using template-based variable parameters. The following is an example of a common query request.\n\n```\nrequests:\n  - request:\n      method: GET\n      basic_auth:\n        username: elastic\n        password: pass\n      url: http://localhost:8000/medcl/_search?q=name:$[[user]]\n```\n\nIn the above query, Loadgen conducts queries based on the `medcl` index and executes one query based on the `name` field. The value of each request is from the random variable `user`.\n\n### Simulating Bulk Ingestion\n\nIt is very easy to use Loadgen to simulate bulk ingestion. Configure one index operation in the request body and then use the `body_repeat_times` parameter to randomly replicate several parameterized requests to complete the preparation of a batch of requests. See the following example.\n\n```\n  - request:\n      method: POST\n      basic_auth:\n        username: test\n        password: testtest\n      url: http://localhost:8000/_bulk\n      body_repeat_times: 1000\n      body: |\n        { \"index\" : { \"_index\" : \"medcl-y4\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" } }\n        { \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n```\n\n### Response Assertions\n\nYou can use the `assert` configuration to check the response values. `assert` now supports most of all the [condition checkers](https://docs.infinilabs.com/gateway/main/docs/references/flow/#condition-type) of INFINI Gateway.\n\n```\nrequests:\n  - request:\n      method: GET\n      basic_auth:\n        username: elastic\n        password: pass\n      url: http://localhost:8000/medcl/_search?q=name:$[[user]]\n    assert:\n      equals:\n        _ctx.response.status: 201\n```\n\nThe response value can be accessed from the `_ctx` value, currently it contains these values:\n\n| Parameter                 | Description                                                                                     |\n| ------------------------- | ----------------------------------------------------------------------------------------------- |\n| `_ctx.response.status`    | HTTP response status code                                                                       |\n| `_ctx.response.header`    | HTTP response headers                                                                           |\n| `_ctx.response.body`      | HTTP response body text                                                                         |\n| `_ctx.response.body_json` | If the HTTP response body is a valid JSON string, you can access the JSON fields by `body_json` |\n| `_ctx.elapsed`            | The time elapsed since request sent to the server (milliseconds)                                |\n\nIf the request failed (e.g. the host is not reachable), `loadgen` will record it under `Number of Errors` as part of the testing output. If you configured `runner.assert_error: true`, `loadgen` will exit as `exit(2)` when there're any requests failed.\n\nIf the assertion failed, `loadgen` will record it under `Number of Invalid` as part of the testing output and skip the subsequent requests in this round. If you configured `runner.assert_invalid: true`, `loadgen` will exit as `exit(1)` when there're any assertions failed.\n\n### Dynamic Variable Registration\n\nEach request can use `register` to dynamically set the variables based on the response value, a common usage is to update the parameters of the later requests based on the previous responses.\n\nIn the below example, we're registering the response value `_ctx.response.body_json.test.settings.index.uuid` of the `$[[env.ES_ENDPOINT]]/test` to the `index_id` variable, then we can access it by `$[[index_id]]`.\n\n```\nrequests:\n  - request:\n      method: GET\n      url: $[[env.ES_ENDPOINT]]/test\n    assert:\n      equals:\n        _ctx.response.status: 200\n    register:\n      - index_id: _ctx.response.body_json.test.settings.index.uuid\n```\n\n### Benchmark Test\n\nRun Loadgen to perform the benchmark test as follows:\n\n```\n➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -compress\n   __   ___  _      ___  ___   __    __\n  / /  /___\\/_\\    /   \\/ _ \\ /__\\/\\ \\ \\\n / /  //  ///_\\\\  / /\\ / /_\\//_\\ /  \\/ /\n/ /__/ \\_//  _  \\/ /_// /_\\\\//__/ /\\  /\n\\____|___/\\_/ \\_/___,'\\____/\\__/\\_\\ \\/\n\n[LOADGEN] A http load generator and testing suit.\n[LOADGEN] 1.0.0_SNAPSHOT, 83f2cb9, Sun Jul 4 13:52:42 2021 +0800, medcl, support single item in dict files\n[07-19 16:15:00] [INF] [instance.go:24] workspace: data/loadgen/nodes/0\n[07-19 16:15:00] [INF] [loader.go:312] warmup started\n[07-19 16:15:00] [INF] [app.go:306] loadgen now started.\n[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search\n[07-19 16:15:00] [INF] [loader.go:317] status: 200,\u003cnil\u003e,{\"took\":1,\"timed_out\":false,\"_shards\":{\"total\":1,\"successful\":1,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":0,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[]}}\n[07-19 16:15:00] [INF] [loader.go:316] [GET] http://localhost:8000/medcl/_search?q=name:medcl\n[07-19 16:15:00] [INF] [loader.go:317] status: 200,\u003cnil\u003e,{\"took\":1,\"timed_out\":false,\"_shards\":{\"total\":1,\"successful\":1,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":{\"value\":0,\"relation\":\"eq\"},\"max_score\":null,\"hits\":[]}}\n[07-19 16:15:01] [INF] [loader.go:316] [POST] http://localhost:8000/_bulk\n[07-19 16:15:01] [INF] [loader.go:317] status: 200,\u003cnil\u003e,{\"took\":120,\"errors\":false,\"items\":[{\"index\":{\"_index\":\"medcl-y4\",\"_type\":\"doc\",\"_id\":\"c3qj9123r0okahraiej0\",\"_version\":1,\"result\":\"created\",\"_shards\":{\"total\":2,\"successful\":1,\"failed\":0},\"_seq_no\":5735852,\"_primary_term\":3,\"status\":201}}]}\n[07-19 16:15:01] [INF] [loader.go:325] warmup finished\n\n209 requests finished in 10.031365126s, 0.00bytes sent, 32.86KB received\n\n[Loadgen Client Metrics]\nRequests/sec:\t\t20.82\nRequest Traffic/sec:\t0.00bytes\nTotal Transfer/sec:\t3.27KB\nFastest Request:\t1ms\nSlowest Request:\t182.437792ms\nStatus 302:\t\t209\n\n[Latency Metrics]\n209 samples of 209 events\nCumulative:\t10.031365126s\nHMean:\t\t46.31664ms\nAvg.:\t\t47.996962ms\np50: \t\t45.712292ms\np75:\t\t51.6065ms\np95:\t\t53.05475ms\np99:\t\t118.162416ms\np999:\t\t182.437792ms\nLong 5%:\t87.678145ms\nShort 5%:\t39.11217ms\nMax:\t\t182.437792ms\nMin:\t\t38.257791ms\nRange:\t\t144.180001ms\nStdDev:\t\t14.407579ms\nRate/sec.:\t20.82\n\n[Latency Distribution]\n   38.257ms - 52.675ms ------------------------------\n   52.675ms - 67.093ms --\n   67.093ms - 81.511ms -\n   81.511ms - 95.929ms -\n  95.929ms - 110.347ms -\n 110.347ms - 124.765ms -\n\n\n[Estimated Server Metrics]\nRequests/sec:\t\t20.83\nAvg Req Time:\t\t47.996962ms\nTransfer/sec:\t\t3.28KB\n```\n\nLoadgen executes all requests once to warm up before the formal benchmark test. If an error occurs, a prompt is displayed, asking you whether to continue.\nThe warm-up request results are also output to the terminal. After execution, an execution summary is output.\nYou can set `runner.no_warm: true` to skip the warm-up stage.\n\n\u003e The final results of Loadgen are the cumulative statistics after all requests are executed, and they may be inaccurate. You are advised to start the Kibana dashboard to check all operating indicators of Elasticsearch in real time.\n\n### CLI Parameters\n\nLoadgen cyclically executes requests defined in the configuration file. By default, Loadgen runs for `5s` and then automatically exits. If you want to prolong the running time or increase the concurrency, you can set the tool's startup parameters. The help commands are as follows:\n\n```\n➜  loadgen git:(master) ✗ ./bin/loadgen --help\nUsage of ./bin/loadgen:\n  -c int\n    \tNumber of concurrent threads (default 1)\n  -compress\n    \tCompress requests with gzip\n  -config string\n    \tthe location of config file, default: loadgen.yml (default \"loadgen.yml\")\n  -d int\n    \tDuration of tests in seconds (default 5)\n  -debug\n    \trun in debug mode, loadgen will quit with panic error\n  -l int\n    \tLimit total requests (default -1)\n  -log string\n    \tthe log level,options:trace,debug,info,warn,error (default \"info\")\n  -r int\n    \tMax requests per second (fixed QPS) (default -1)\n  -v\tversion\n```\n\n### Limiting the Client Workload\n\nYou can use Loadgen and set the CLI parameter `-r` to restrict the number of requests that can be sent by the client per second, so as to evaluate the response time and load of Elasticsearch under fixed pressure. See the following example.\n\n```\n➜  loadgen git:(master) ✗ ./bin/loadgen -d 30 -c 100 -r 100\n```\n\n\u003e Note: The client throughput limit may not be accurate enough in the case of massive concurrencies.\n\n### Limiting the Total Number of Requests\n\nYou can set the `-l` parameter to control the total number of requests that can be sent by the client, so as to generate a fixed number of documents. Modify the configuration as follows:\n\n```\nrequests:\n  - request:\n      method: POST\n      basic_auth:\n        username: test\n        password: testtest\n      url: http://localhost:8000/medcl-test/doc2/_bulk\n      body_repeat_times: 1\n      body: |\n        { \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[uuid]]\" } }\n        { \"id\" : \"$[[id]]\",\"field1\" : \"$[[user]]\",\"ip\" : \"$[[ip]]\" }\n```\n\nConfigured parameters use the content of only one document for each request. Then, the system executes Loadgen.\n\n```\n./bin/loadgen -config loadgen-gw.yml -d 600 -c 100 -l 50000\n```\n\nAfter execution, `50000` records are added for the Elasticsearch index `medcl-test`.\n\n### Using Auto Incremental IDs to Ensure the Document Sequence\n\nIf the IDs of generated documents need to increase regularly to facilitate comparison, you can use the auto incremental IDs of the `sequence` type as the primary key and avoid using random numbers in the content. See the following example.\n\n```\nrequests:\n  - request:\n      method: POST\n      basic_auth:\n        username: test\n        password: testtest\n      url: http://localhost:8000/medcl-test/doc2/_bulk\n      body_repeat_times: 1\n      body: |\n        { \"index\" : { \"_index\" : \"medcl-test\", \"_id\" : \"$[[id]]\" } }\n        { \"id\" : \"$[[id]]\" }\n```\n\n### Reuse variables in Request Context\n\nIn a request, we might want use the same variable value, such as the `routing` parameter to control the shard destination, also store the field in the JSON document.\nYou can use `runtime_variables` to set request-level variables, or `runtime_body_line_variables` to define request-body-level variables.\nIf the request body set `body_repeat_times`, each line will be different, as shown in the following example:\n\n```\nvariables:\n  - name: id\n    type: sequence\n  - name: uuid\n    type: uuid\n  - name: now_local\n    type: now_local\n  - name: now_utc\n    type: now_utc\n  - name: now_unix\n    type: now_unix\n  - name: suffix\n    type: range\n    from: 10\n    to: 15\nrequests:\n  - request:\n      method: POST\n      runtime_variables:\n        batch_no: id\n      runtime_body_line_variables:\n        routing_no: uuid\n      basic_auth:\n        username: ingest\n        password: password\n      #url: http://localhost:8000/_search?q=$[[id]]\n      url: http://192.168.3.188:9206/_bulk\n      body_repeat_times: 10\n      body: |\n        { \"create\" : { \"_index\" : \"test-$[[suffix]]\",\"_type\":\"doc\", \"_id\" : \"$[[uuid]]\" , \"routing\" : \"$[[routing_no]]\" } }\n        { \"id\" : \"$[[uuid]]\",\"routing_no\" : \"$[[routing_no]]\",\"batch_number\" : \"$[[batch_no]]\", \"random_no\" : \"$[[suffix]]\",\"ip\" : \"$[[ip]]\",\"now_local\" : \"$[[now_local]]\",\"now_unix\" : \"$[[now_unix]]\" }\n```\n\nWe defined the `batch_no` variable to represent the same batch number in a batch of documents, and the `routing_no` variable to represent the routing value at each document level.\n\n### Customize Header\n\n```\nrequests:\n  - request:\n      method: GET\n      url: http://localhost:8000/test/_search\n      headers:\n        - Agent: \"Loadgen-1\"\n      disable_header_names_normalizing: false\n```\n\nBy default, `loadgen` will canonilize the HTTP header keys before sending the request (`user-agent: xxx` -\u003e `User-Agent: xxx`), if you need to set the header keys exactly as is, set `disable_header_names_normalizing: true`.\n\n### Work with DSL\n\nLoadgen also support simply the requests called DSL,for example, prepare a dsl file for loadgen, save as `bulk.dsl`:\n\n```\nPOST /_bulk\n{\"index\": {\"_index\": \"$[[env.INDEX_NAME]]\", \"_type\": \"_doc\", \"_id\": \"$[[uuid]]\"}}\n{\"id\": \"$[[id]]\", \"routing\": \"$[[routing_no]]\", \"batch\": \"$[[batch_no]]\", \"now_local\": \"$[[now_local]]\", \"now_unix\": \"$[[now_unix]]\"}\n```\nAnd specify the dsl file with parameter `run`:\n\n```\n$ INDEX_NAME=medcl123 ES_ENDPOINT=https://localhost:9200 ES_USERNAME=admin  ES_PASSWORD=b14612393da0d4e7a70b ./bin/loadgen -run bulk.dsl\n```\n\nNow you should ready to rock~","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinilabs%2Floadgen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finfinilabs%2Floadgen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finfinilabs%2Floadgen/lists"}