Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Go docs for PR#1424 #1474

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions wasp/benchspy/basic.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type BasicData struct {
GeneratorConfigs map[string]*wasp.Config `json:"generator_configs"`
}

// MustNewBasicData creates a new BasicData instance from a commit or tag.
// It panics if the creation fails, ensuring that the caller receives a valid instance.
func MustNewBasicData(commitOrTag string, generators ...*wasp.Generator) BasicData {
b, err := NewBasicData(commitOrTag, generators...)
if err != nil {
Expand All @@ -31,6 +33,9 @@ func MustNewBasicData(commitOrTag string, generators ...*wasp.Generator) BasicDa
return *b
}

// NewBasicData creates a new BasicData instance using the provided commit or tag and a list of generators.
// It ensures that at least one generator is provided and that it is associated with a testing.T instance.
// This function is essential for initializing test data configurations in a structured manner.
func NewBasicData(commitOrTag string, generators ...*wasp.Generator) (*BasicData, error) {
if len(generators) == 0 {
return nil, errors.New("at least one generator is required")
Expand Down Expand Up @@ -58,6 +63,8 @@ func NewBasicData(commitOrTag string, generators ...*wasp.Generator) (*BasicData
return b, nil
}

// FillStartEndTimes calculates the earliest start time and latest end time from generator schedules.
// It updates the BasicData instance with these times, ensuring all segments have valid start and end times.
func (b *BasicData) FillStartEndTimes() error {
earliestTime := time.Now()
var latestTime time.Time
Expand Down Expand Up @@ -89,6 +96,8 @@ func (b *BasicData) FillStartEndTimes() error {
return nil
}

// Validate checks the integrity of the BasicData fields, ensuring that the test start and end times are set,
// and that at least one generator configuration is provided. It returns an error if any of these conditions are not met.
func (b *BasicData) Validate() error {
if b.TestStart.IsZero() {
return errors.New("test start time is missing. We cannot query Loki without a time range. Please set it and try again")
Expand All @@ -104,6 +113,10 @@ func (b *BasicData) Validate() error {
return nil
}

// IsComparable checks if two BasicData instances have the same configuration settings.
// It validates the count, presence, and equivalence of generator configurations,
// returning an error if any discrepancies are found. This function is useful for ensuring
// consistency between data reports before processing or comparison.
func (b *BasicData) IsComparable(otherData BasicData) error {
// are all configs present? do they have the same schedule type? do they have the same segments? is call timeout the same? is rate limit timeout the same?
if len(b.GeneratorConfigs) != len(otherData.GeneratorConfigs) {
Expand Down
24 changes: 24 additions & 0 deletions wasp/benchspy/direct.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type DirectQueryExecutor struct {
QueryResults map[string]interface{} `json:"query_results"`
}

// NewStandardDirectQueryExecutor creates a new DirectQueryExecutor configured for standard queries.
// It initializes the executor and generates the necessary queries, returning the executor or an error if the process fails.
func NewStandardDirectQueryExecutor(generator *wasp.Generator) (*DirectQueryExecutor, error) {
g := &DirectQueryExecutor{
KindName: string(StandardQueryExecutor_Direct),
Expand All @@ -33,6 +35,8 @@ func NewStandardDirectQueryExecutor(generator *wasp.Generator) (*DirectQueryExec
return NewDirectQueryExecutor(generator, queries)
}

// NewDirectQueryExecutor creates a new DirectQueryExecutor with the specified generator and query functions.
// It initializes the executor with a kind name and prepares a map for query results, enabling efficient query execution.
func NewDirectQueryExecutor(generator *wasp.Generator, queries map[string]DirectQueryFn) (*DirectQueryExecutor, error) {
g := &DirectQueryExecutor{
KindName: string(StandardQueryExecutor_Direct),
Expand All @@ -44,14 +48,20 @@ func NewDirectQueryExecutor(generator *wasp.Generator, queries map[string]Direct
return g, nil
}

// Results returns the query results as a map of string keys to interface{} values.
// It allows users to access the outcomes of executed queries, facilitating further processing or type assertions.
func (g *DirectQueryExecutor) Results() map[string]interface{} {
return g.QueryResults
}

// Kind returns the type of the query executor as a string.
// It is useful for identifying the specific implementation of a query executor in a collection.
func (l *DirectQueryExecutor) Kind() string {
return l.KindName
}

// IsComparable checks if the given QueryExecutor is of the same type and has comparable configurations.
// It returns an error if the types do not match or if the configurations are not comparable.
func (g *DirectQueryExecutor) IsComparable(otherQueryExecutor QueryExecutor) error {
otherType := reflect.TypeOf(otherQueryExecutor)

Expand Down Expand Up @@ -83,6 +93,9 @@ func (l *DirectQueryExecutor) compareQueries(other map[string]DirectQueryFn) err
return nil
}

// Validate checks if the query executor is properly configured.
// It ensures that a generator is set and at least one query is provided.
// Returns an error if validation fails, helping to prevent execution issues.
func (g *DirectQueryExecutor) Validate() error {
if g.Generator == nil {
return errors.New("generator is not set")
Expand All @@ -95,6 +108,9 @@ func (g *DirectQueryExecutor) Validate() error {
return nil
}

// Execute runs the defined queries using the data from the generator.
// It validates the generator's data and aggregates responses before executing each query.
// This function is essential for processing and retrieving results from multiple queries concurrently.
func (g *DirectQueryExecutor) Execute(_ context.Context) error {
if g.Generator == nil {
return errors.New("generator is not set")
Expand Down Expand Up @@ -130,6 +146,8 @@ func (g *DirectQueryExecutor) Execute(_ context.Context) error {
return nil
}

// TimeRange ensures that the query executor operates within the specified time range.
// It is a no-op for executors that already have responses stored in the correct time range.
func (g *DirectQueryExecutor) TimeRange(_, _ time.Time) {
// nothing to do here, since all responses stored in the generator are already in the right time range
}
Expand Down Expand Up @@ -198,6 +216,9 @@ func (g *DirectQueryExecutor) standardQuery(standardMetric StandardLoadMetric) (
}
}

// MarshalJSON customizes the JSON representation of the DirectQueryExecutor.
// It serializes only the relevant fields, including query names and results,
// making it suitable for efficient data transmission and storage.
func (g *DirectQueryExecutor) MarshalJSON() ([]byte, error) {
// we need custom marshalling to only include query names, since the functions are not serializable
type QueryExecutor struct {
Expand Down Expand Up @@ -226,6 +247,9 @@ func (g *DirectQueryExecutor) MarshalJSON() ([]byte, error) {
})
}

// UnmarshalJSON decodes JSON data into a DirectQueryExecutor instance.
// It populates the executor's fields, including queries and results,
// enabling seamless integration of JSON configurations into the executor's structure.
func (g *DirectQueryExecutor) UnmarshalJSON(data []byte) error {
// helper struct with QueryExecutors as json.RawMessage and QueryResults as map[string]interface{}
// and as actual types
Expand Down
22 changes: 22 additions & 0 deletions wasp/benchspy/loki.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import (
"golang.org/x/sync/errgroup"
)

// NewLokiQueryExecutor creates a new LokiQueryExecutor instance.
// It initializes the executor with provided queries and Loki configuration,
// enabling efficient querying of logs from Loki in a structured manner.
func NewLokiQueryExecutor(queries map[string]string, lokiConfig *wasp.LokiConfig) *LokiQueryExecutor {
return &LokiQueryExecutor{
KindName: string(StandardQueryExecutor_Loki),
Expand All @@ -40,14 +43,21 @@ type LokiQueryExecutor struct {
Config *wasp.LokiConfig `json:"-"`
}

// Results returns the query results as a map of string to interface{}.
// It allows users to access the outcomes of executed queries, facilitating further processing or type assertions.
func (l *LokiQueryExecutor) Results() map[string]interface{} {
return l.QueryResults
}

// Kind returns the type of the query executor as a string.
// It is used to identify the specific kind of query executor in various operations.
func (l *LokiQueryExecutor) Kind() string {
return l.KindName
}

// IsComparable checks if the given QueryExecutor is of the same type as the current instance.
// It compares the queries of both executors to ensure they are equivalent in structure and content.
// This function is useful for validating compatibility between different query executors.
func (l *LokiQueryExecutor) IsComparable(otherQueryExecutor QueryExecutor) error {
otherType := reflect.TypeOf(otherQueryExecutor)

Expand All @@ -58,6 +68,9 @@ func (l *LokiQueryExecutor) IsComparable(otherQueryExecutor QueryExecutor) error
return l.compareQueries(otherQueryExecutor.(*LokiQueryExecutor).Queries)
}

// Validate checks if the LokiQueryExecutor has valid queries and configuration.
// It returns an error if no queries are set or if the configuration is missing,
// ensuring that the executor is ready for execution.
func (l *LokiQueryExecutor) Validate() error {
if len(l.Queries) == 0 {
return errors.New("there are no Loki queries, there's nothing to fetch. Please set them and try again")
Expand All @@ -69,6 +82,9 @@ func (l *LokiQueryExecutor) Validate() error {
return nil
}

// Execute runs the configured Loki queries concurrently and collects the results.
// It requires a valid configuration and handles basic authentication if provided.
// The function returns an error if any query execution fails or if the configuration is missing.
func (l *LokiQueryExecutor) Execute(ctx context.Context) error {
var basicAuth client.LokiBasicAuth

Expand Down Expand Up @@ -159,11 +175,15 @@ func (l *LokiQueryExecutor) compareQueries(other map[string]string) error {
return nil
}

// TimeRange sets the start and end time for the Loki query execution.
// This function is essential for defining the time window of the data to be fetched.
func (l *LokiQueryExecutor) TimeRange(start, end time.Time) {
l.StartTime = start
l.EndTime = end
}

// UnmarshalJSON parses the JSON-encoded data and populates the LokiQueryExecutor fields.
// It converts the query results from a generic map to a specific type map, enabling type-safe access to the results.
func (l *LokiQueryExecutor) UnmarshalJSON(data []byte) error {
// helper struct with QueryResults map[string]interface{}
type Alias LokiQueryExecutor
Expand All @@ -188,6 +208,8 @@ func (l *LokiQueryExecutor) UnmarshalJSON(data []byte) error {
return nil
}

// NewStandardMetricsLokiExecutor creates a LokiQueryExecutor configured with standard metrics queries.
// It generates queries based on provided test parameters and time range, returning the executor or an error if query generation fails.
func NewStandardMetricsLokiExecutor(lokiConfig *wasp.LokiConfig, testName, generatorName, branch, commit string, startTime, endTime time.Time) (*LokiQueryExecutor, error) {
lq := &LokiQueryExecutor{
KindName: string(StandardQueryExecutor_Loki),
Expand Down
5 changes: 5 additions & 0 deletions wasp/benchspy/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"strconv"
)

// CalculatePercentile computes the specified percentile of a slice of numbers.
// It is useful for statistical analysis, allowing users to understand data distributions
// by retrieving values at specific percentiles, such as median or 95th percentile.
func CalculatePercentile(numbers []float64, percentile float64) float64 {
// Sort the slice
sort.Float64s(numbers)
Expand All @@ -31,6 +34,8 @@ func CalculatePercentile(numbers []float64, percentile float64) float64 {
return numbers[lowerIndex]*(1-weight) + numbers[upperIndex]*weight
}

// StringSliceToFloat64Slice converts a slice of strings to a slice of float64 values.
// It returns an error if any string cannot be parsed as a float64, making it useful for data conversion tasks.
func StringSliceToFloat64Slice(s []string) ([]float64, error) {
numbers := make([]float64, len(s))
for i, str := range s {
Expand Down
28 changes: 28 additions & 0 deletions wasp/benchspy/prometheus.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type PrometheusConfig struct {

const PrometheusUrlEnvVar = "PROMETHEUS_URL"

// NewPrometheusConfig creates a new PrometheusConfig instance with the specified name regex patterns.
// It retrieves the Prometheus URL from the environment and is used to configure query execution for Prometheus data sources.
func NewPrometheusConfig(nameRegexPatterns ...string) *PrometheusConfig {
return &PrometheusConfig{
Url: os.Getenv(PrometheusUrlEnvVar),
Expand Down Expand Up @@ -56,6 +58,9 @@ func NewPrometheusQueryExecutor(queries map[string]string, config *PrometheusCon
}, nil
}

// NewStandardPrometheusQueryExecutor creates a PrometheusQueryExecutor with standard queries
// based on the provided time range and configuration. It simplifies the process of generating
// queries for Prometheus, making it easier to integrate Prometheus data into reports.
func NewStandardPrometheusQueryExecutor(startTime, endTime time.Time, config *PrometheusConfig) (*PrometheusQueryExecutor, error) {
p := &PrometheusQueryExecutor{}

Expand All @@ -74,6 +79,8 @@ func NewStandardPrometheusQueryExecutor(startTime, endTime time.Time, config *Pr
return NewPrometheusQueryExecutor(standardQueries, config)
}

// Execute runs the defined Prometheus queries concurrently, collecting results and warnings.
// It returns an error if any query fails, allowing for efficient data retrieval in reporting tasks.
func (r *PrometheusQueryExecutor) Execute(ctx context.Context) error {
for name, query := range r.Queries {
result, warnings, queryErr := r.client.Query(ctx, query, r.EndTime)
Expand All @@ -91,14 +98,20 @@ func (r *PrometheusQueryExecutor) Execute(ctx context.Context) error {
return nil
}

// Results returns the query results as a map of string to interface{}.
// It allows users to access the results of executed queries, facilitating data retrieval and manipulation.
func (r *PrometheusQueryExecutor) Results() map[string]interface{} {
return r.QueryResults
}

// Kind returns the type of the query executor as a string.
// It is used to identify the specific kind of executor in a collection of query executors.
func (l *PrometheusQueryExecutor) Kind() string {
return l.KindName
}

// Validate checks the PrometheusQueryExecutor for a valid client and ensures that at least one query is provided.
// It returns an error if the client is nil or no queries are specified, helping to ensure proper configuration before execution.
func (r *PrometheusQueryExecutor) Validate() error {
if r.client == nil {
return errors.New("prometheus client is nil")
Expand All @@ -111,6 +124,8 @@ func (r *PrometheusQueryExecutor) Validate() error {
return nil
}

// IsComparable checks if the provided QueryExecutor is of the same type as the receiver.
// It returns an error if the types do not match, ensuring type safety for query comparisons.
func (r *PrometheusQueryExecutor) IsComparable(other QueryExecutor) error {
otherType := reflect.TypeOf(other)
if otherType != reflect.TypeOf(r) {
Expand Down Expand Up @@ -141,10 +156,15 @@ func (r *PrometheusQueryExecutor) compareQueries(other map[string]string) error
return nil
}

// Warnings returns a map of warnings encountered during query execution.
// This function is useful for retrieving any issues that may have arisen,
// allowing users to handle or log them appropriately.
func (r *PrometheusQueryExecutor) Warnings() map[string]v1.Warnings {
return r.warnings
}

// MustResultsAsValue retrieves the query results as a map of metric names to their corresponding values.
// It ensures that the results are in a consistent format, making it easier to work with metrics in subsequent operations.
func (r *PrometheusQueryExecutor) MustResultsAsValue() map[string]model.Value {
results := make(map[string]model.Value)
for name, result := range r.QueryResults {
Expand Down Expand Up @@ -180,6 +200,8 @@ func (r *PrometheusQueryExecutor) MustResultsAsValue() map[string]model.Value {
return results
}

// TimeRange sets the start and end time for the Prometheus query execution.
// This function is essential for defining the time window for data retrieval, ensuring accurate and relevant results.
func (r *PrometheusQueryExecutor) TimeRange(startTime, endTime time.Time) {
r.StartTime = startTime
r.EndTime = endTime
Expand Down Expand Up @@ -222,6 +244,9 @@ type TypedMetric struct {
MetricType string `json:"metric_type"`
}

// MarshalJSON customizes the JSON representation of PrometheusQueryExecutor.
// It includes only essential fields: Kind, Queries, and simplified QueryResults.
// This function is useful for serializing the executor's state in a concise format.
func (g *PrometheusQueryExecutor) MarshalJSON() ([]byte, error) {
// we need custom marshalling to only include some parts of the metrics
type QueryExecutor struct {
Expand All @@ -248,6 +273,9 @@ func (g *PrometheusQueryExecutor) MarshalJSON() ([]byte, error) {
return json.Marshal(q)
}

// UnmarshalJSON decodes JSON data into a PrometheusQueryExecutor instance.
// It populates the QueryResults field with appropriately typed metrics,
// enabling easy access to the results of Prometheus queries.
func (r *PrometheusQueryExecutor) UnmarshalJSON(data []byte) error {
// helper struct with QueryResults map[string]interface{}
type Alias PrometheusQueryExecutor
Expand Down
Loading
Loading