Skip to content

Customization

Two important runtime extension points on a VU are:

  • http: the HTTP client used by steps
  • step_strategy: the scheduler that decides which ready steps run next

In practice:

  • the HTTP client defines how a VU sends requests and how those requests are configured
  • the step strategy defines how the runtime selects ready steps during the execution loop

These two attributes are the main customization points for request behavior and step scheduling.

The http attribute can now be backed by either:

  • ReqwestClient for request-oriented HTTP calls such as get(...) and post(...)
  • JsonRPCClient for JSON-RPC method calls through call(...)

Every VU has an http attribute. In the base runtime, the default is:

http = ReqwestClient()

That means:

  • the default HTTP implementation is based on ReqwestClient
  • the runtime creates a separate client instance per VU
  • the client is wrapped with instrumentation, so HTTP metrics are emitted automatically

In practice, steps call methods like:

await self.http.get("/catalog")
await self.http.post("/auth", json=payload)

The most common override is to replace the class attribute with another ReqwestClient configuration:

from vikhry import ReqwestClient, VU
class DemoVU(VU):
http = ReqwestClient(timeout=5.0)

Then, in on_init(...), you can bind a base_url for that VU:

async def on_init(self, base_url: str) -> None:
self.http = self.http(base_url=base_url)

You can also provide your own HTTP client or factory instead of ReqwestClient. The runtime accepts:

  • an object with async request(...)
  • an object with create(...) returning a client with async request(...)
  • a callable returning a client with async request(...)

So custom transports are possible as long as they match the expected interface.

ReqwestClient is a lazy template, not the final connected client itself.

It is used to define defaults such as:

  • base_url
  • timeout

When the VU starts, the runtime resolves that template into an instrumented per-user HTTP client.

JsonRPCClient is another lazy runtime client template. It is intended for scenarios where the target system exposes a JSON-RPC endpoint instead of classic REST-style routes.

Example:

from vikhry import JsonRPCClient, VU, step
class DemoVU(VU):
http = JsonRPCClient(timeout=5.0)
async def on_init(self, base_url: str) -> None:
self.http = self.http(base_url=base_url)
@step(name="user.get")
async def get_user(self) -> None:
result = await self.http.call("user.get", {"user_id": 42})
if not result:
raise RuntimeError("empty result")

Important details:

  • JsonRPCClient requires a non-empty base_url
  • calls are sent as JSON-RPC 2.0 POST requests
  • the primary API is self.http.call(method, params=None, ...)
  • params can be a list, tuple, dict, or None

At runtime, the client builds requests like:

{
"jsonrpc": "2.0",
"id": 1,
"method": "user.get",
"params": {
"user_id": 42
}
}

The runtime also parses JSON-RPC responses and raises dedicated exceptions for protocol or RPC errors.

JsonRPCClient is instrumented by the runtime in the same way as ReqwestClient, but metrics are emitted with JSON-RPC-specific fields.

Key differences:

  • metric source is jsonrpc
  • metric name is the RPC method name
  • RPC failures use normalized result_code values such as JSONRPC_-32000
  • metric payload may include rpc_error_code and http_status

Every VU also has a step_strategy attribute. The default is:

step_strategy = SequentialWeightedStrategy()

This is the default scheduler for the VU step loop.

The runtime first determines which steps are ready:

  • all step names listed in requires must already be completed
  • if every_s is set, the step must wait until its next allowed execution time

After that:

  • if no steps are ready, the VU sleeps briefly
  • if one step is ready, that step runs
  • if multiple steps are ready, one step is chosen using weight

So the default behavior is sequential execution with weighted random choice among ready steps.

You can replace the default strategy on the VU class:

from vikhry import ParallelReadyStrategy, VU
class DemoVU(VU):
step_strategy = ParallelReadyStrategy()

ParallelReadyStrategy runs all ready steps in the same scheduling tick instead of choosing only one.

You can also provide your own object implementing select(...) with the StepStrategy protocol.