BenchSpy - Adding a New QueryExecutor

QueryExecutor interface

As mentioned earlier, the StandardReport supports three different data sources:

  • Direct
  • Loki
  • Prometheus

Each of these implements the QueryExecutor interface:

type QueryExecutor interface {
	// Kind returns the type of the QueryExecutor
	Kind() string
	// Validate checks if the QueryExecutor has all the necessary data and configuration to execute the queries
	Validate() error
	// Execute executes the queries and populates the QueryExecutor with the results
	Execute(ctx context.Context) error
	// Results returns the results of the queries, where the key is the query name and the value is the result
	Results() map[string]interface{}
	// IsComparable checks whether both QueryExecutors can be compared (e.g., they have the same type, queries, etc.),
	// and returns an error if any differences are found
	IsComparable(other QueryExecutor) error
	// TimeRange sets the time range for the queries
	TimeRange(startTime, endTime time.Time)
}

When creating a new QueryExecutor, most functions in this interface are straightforward. Below, we focus on two that may require additional explanation.


Kind

The Kind function should return a unique string identifier for your QueryExecutor. This identifier is crucial because StandardReport uses it when unmarshalling JSON files with stored reports.

Additionally, you need to extend the StandardReport.UnmarshalJSON function to support your new executor.

note

If your QueryExecutor includes interfaces, interface{} or any types, or fields that should not or cannot be serialized, ensure you implement custom MarshalJSON and UnmarshalJSON functions. Existing executors can provide useful examples.


TimeRange

The TimeRange method is called by StandardReport just before invoking Execute() for each executor. This method sets the time range for queries (required for Loki and Prometheus).

By default, StandardReport calculates the test time range automatically by analyzing the schedules of all generators and determining the earliest start time and latest end time. This eliminates the need for manual calculations.


With these details in mind, you should have a clear path to implementing your own QueryExecutor and integrating it seamlessly with BenchSpy's StandardReport.

NamedGenerator interface

Executors that query load generation metrics should also implement this simple interface:

type NamedGenerator interface {
	// GeneratorName returns the name of the generator
	GeneratorName() string
}

It is used primarly, when casting results from map[string]interface{} to target type, while splitting them between different generators.

Currently, this interface is implemented by Direct and Loki exectors, but not by Prometheus.