From 817076bdbf9ce1dc94bda55a3f4fe83321325105 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Thu, 16 Jan 2025 12:25:49 +0100 Subject: [PATCH 1/7] initial prototyping --- internal/repository/stats.go | 7 +++++-- web/frontend/src/User.root.svelte | 5 +++-- web/frontend/src/generic/plots/Histogram.svelte | 13 ++++++++++--- web/frontend/src/generic/utils.js | 12 +++++++++--- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/internal/repository/stats.go b/internal/repository/stats.go index 484851d..3245b70 100644 --- a/internal/repository/stats.go +++ b/internal/repository/stats.go @@ -450,12 +450,15 @@ func (r *JobRepository) AddHistograms( ) (*model.JobsStatistics, error) { start := time.Now() + binSeconds := 900 + binMinutes := binSeconds / 60 castType := r.getCastType() var err error - value := fmt.Sprintf(`CAST(ROUND((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / 3600) as %s) as value`, time.Now().Unix(), castType) + // Bin by job duration in sizes of binSeconds, add +1, gives Integers from 1-XX+1, re-multiply by binMinutes to get final bar x-values (logic: Jobs less than duration X in bin) + value := fmt.Sprintf(`CAST((ROUND(((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / %d) + 1) * %d) as %s) as value`, time.Now().Unix(), binSeconds, binMinutes, castType) stat.HistDuration, err = r.jobsStatisticsHistogram(ctx, value, filter) if err != nil { - log.Warn("Error while loading job statistics histogram: running jobs") + log.Warn("Error while loading job statistics histogram: job duration") return nil, err } diff --git a/web/frontend/src/User.root.svelte b/web/frontend/src/User.root.svelte index 57f2f28..8f7ed57 100644 --- a/web/frontend/src/User.root.svelte +++ b/web/frontend/src/User.root.svelte @@ -213,9 +213,10 @@ {#key $stats.data.jobsStatistics[0].histDuration} t.map((v) => formatNumber(v)), + values: (_, t) => t.map((v) => { + if (!usesBins) console.log("X Scale Val", xlabel, v) + return formatNumber(v) + }), }, { stroke: "#000000", @@ -166,7 +170,10 @@ size: 5 / devicePixelRatio, stroke: "#000000", }, - values: (_, t) => t.map((v) => formatNumber(v)), + values: (_, t) => t.map((v) => { + if (!usesBins) console.log("Y Scale Val", ylabel, v) + return formatNumber(v) + }), }, ], series: [ diff --git a/web/frontend/src/generic/utils.js b/web/frontend/src/generic/utils.js index fa357eb..9d63617 100644 --- a/web/frontend/src/generic/utils.js +++ b/web/frontend/src/generic/utils.js @@ -405,7 +405,7 @@ function getMetricConfigDeep(metric, cluster, subCluster) { } } -export function convert2uplot(canvasData) { +export function convert2uplot(canvasData, minutesToHours = false) { // Prep: Uplot Data Structure let uplotData = [[],[]] // [X, Y1, Y2, ...] // Iterate if exists @@ -415,9 +415,15 @@ export function convert2uplot(canvasData) { uplotData[0].push(cd?.max ? cd.max : 0) uplotData[1].push(cd.count) } else { // Default - uplotData[0].push(cd.value) + if (minutesToHours) { + let hours = cd.value / 60 + console.log("x minutes to y hours", cd.value, hours) + uplotData[0].push(hours) + } else { + uplotData[0].push(cd.value) + } uplotData[1].push(cd.count) - } + } }) } return uplotData From 05bfa9b546e8a66f7221ea4858d8504f1b8108c7 Mon Sep 17 00:00:00 2001 From: Christoph Kluge Date: Wed, 22 Jan 2025 12:07:12 +0100 Subject: [PATCH 2/7] Prepare adaptive binCounts in backend --- api/schema.graphqls | 2 +- internal/graph/generated/generated.go | 1433 +++++++++++++++++++------ internal/graph/schema.resolvers.go | 22 +- internal/repository/stats.go | 38 +- web/frontend/src/User.root.svelte | 2 +- web/frontend/src/generic/utils.js | 12 +- 6 files changed, 1171 insertions(+), 338 deletions(-) diff --git a/api/schema.graphqls b/api/schema.graphqls index e62fb0a..60b4446 100644 --- a/api/schema.graphqls +++ b/api/schema.graphqls @@ -236,7 +236,7 @@ type Query { jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList! - jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]! + jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate, numDurationBins: Int, numMetricBins: Int): [JobsStatistics!]! rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]! diff --git a/internal/graph/generated/generated.go b/internal/graph/generated/generated.go index 00609ac..a9e94ce 100644 --- a/internal/graph/generated/generated.go +++ b/internal/graph/generated/generated.go @@ -257,7 +257,7 @@ type ComplexityRoot struct { JobMetrics func(childComplexity int, id string, metrics []string, scopes []schema.MetricScope, resolution *int) int Jobs func(childComplexity int, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) int JobsFootprints func(childComplexity int, filter []*model.JobFilter, metrics []string) int - JobsStatistics func(childComplexity int, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) int + JobsStatistics func(childComplexity int, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate, numDurationBins *int, numMetricBins *int) int NodeMetrics func(childComplexity int, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) int RooflineHeatmap func(childComplexity int, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) int Tags func(childComplexity int) int @@ -382,7 +382,7 @@ type QueryResolver interface { JobMetrics(ctx context.Context, id string, metrics []string, scopes []schema.MetricScope, resolution *int) ([]*model.JobMetricWithName, error) JobsFootprints(ctx context.Context, filter []*model.JobFilter, metrics []string) (*model.Footprints, error) Jobs(ctx context.Context, filter []*model.JobFilter, page *model.PageRequest, order *model.OrderByInput) (*model.JobResultList, error) - JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) + JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate, numDurationBins *int, numMetricBins *int) ([]*model.JobsStatistics, error) RooflineHeatmap(ctx context.Context, filter []*model.JobFilter, rows int, cols int, minX float64, minY float64, maxX float64, maxY float64) ([][]float64, error) NodeMetrics(ctx context.Context, cluster string, nodes []string, scopes []schema.MetricScope, metrics []string, from time.Time, to time.Time) ([]*model.NodeMetrics, error) } @@ -1372,7 +1372,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return 0, false } - return e.complexity.Query.JobsStatistics(childComplexity, args["filter"].([]*model.JobFilter), args["metrics"].([]string), args["page"].(*model.PageRequest), args["sortBy"].(*model.SortByAggregate), args["groupBy"].(*model.Aggregate)), true + return e.complexity.Query.JobsStatistics(childComplexity, args["filter"].([]*model.JobFilter), args["metrics"].([]string), args["page"].(*model.PageRequest), args["sortBy"].(*model.SortByAggregate), args["groupBy"].(*model.Aggregate), args["numDurationBins"].(*int), args["numMetricBins"].(*int)), true case "Query.nodeMetrics": if e.complexity.Query.NodeMetrics == nil { @@ -1786,8 +1786,8 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in } func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { - rc := graphql.GetOperationContext(ctx) - ec := executionContext{rc, e, 0, 0, make(chan graphql.DeferredResult)} + opCtx := graphql.GetOperationContext(ctx) + ec := executionContext{opCtx, e, 0, 0, make(chan graphql.DeferredResult)} inputUnmarshalMap := graphql.BuildUnmarshalerMap( ec.unmarshalInputFloatRange, ec.unmarshalInputIntRange, @@ -1800,7 +1800,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { ) first := true - switch rc.Operation.Operation { + switch opCtx.Operation.Operation { case ast.Query: return func(ctx context.Context) *graphql.Response { var response graphql.Response @@ -1808,7 +1808,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { if first { first = false ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data = ec._Query(ctx, rc.Operation.SelectionSet) + data = ec._Query(ctx, opCtx.Operation.SelectionSet) } else { if atomic.LoadInt32(&ec.pendingDeferred) > 0 { result := <-ec.deferredResults @@ -1838,7 +1838,7 @@ func (e *executableSchema) Exec(ctx context.Context) graphql.ResponseHandler { } first = false ctx = graphql.WithUnmarshalerMap(ctx, inputUnmarshalMap) - data := ec._Mutation(ctx, rc.Operation.SelectionSet) + data := ec._Mutation(ctx, opCtx.Operation.SelectionSet) var buf bytes.Buffer data.MarshalGQL(&buf) @@ -2132,7 +2132,7 @@ type Query { jobsFootprints(filter: [JobFilter!], metrics: [String!]!): Footprints jobs(filter: [JobFilter!], page: PageRequest, order: OrderByInput): JobResultList! - jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate): [JobsStatistics!]! + jobsStatistics(filter: [JobFilter!], metrics: [String!], page: PageRequest, sortBy: SortByAggregate, groupBy: Aggregate, numDurationBins: Int, numMetricBins: Int): [JobsStatistics!]! rooflineHeatmap(filter: [JobFilter!]!, rows: Int!, cols: Int!, minX: Float!, minY: Float!, maxX: Float!, maxY: Float!): [[Float!]!]! @@ -2272,491 +2272,1302 @@ var parsedSchema = gqlparser.MustLoadSchema(sources...) func (ec *executionContext) field_Mutation_addTagsToJob_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["job"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("job")) - arg0, err = ec.unmarshalNID2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Mutation_addTagsToJob_argsJob(ctx, rawArgs) + if err != nil { + return nil, err } args["job"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["tagIds"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) - arg1, err = ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Mutation_addTagsToJob_argsTagIds(ctx, rawArgs) + if err != nil { + return nil, err } args["tagIds"] = arg1 return args, nil } +func (ec *executionContext) field_Mutation_addTagsToJob_argsJob( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["job"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("job")) + if tmp, ok := rawArgs["job"]; ok { + return ec.unmarshalNID2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_addTagsToJob_argsTagIds( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["tagIds"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) + if tmp, ok := rawArgs["tagIds"]; ok { + return ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} func (ec *executionContext) field_Mutation_createTag_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["type"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Mutation_createTag_argsType(ctx, rawArgs) + if err != nil { + return nil, err } args["type"] = arg0 - var arg1 string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg1, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Mutation_createTag_argsName(ctx, rawArgs) + if err != nil { + return nil, err } args["name"] = arg1 - var arg2 string - if tmp, ok := rawArgs["scope"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scope")) - arg2, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Mutation_createTag_argsScope(ctx, rawArgs) + if err != nil { + return nil, err } args["scope"] = arg2 return args, nil } +func (ec *executionContext) field_Mutation_createTag_argsType( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["type"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("type")) + if tmp, ok := rawArgs["type"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_createTag_argsName( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["name"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + if tmp, ok := rawArgs["name"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_createTag_argsScope( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["scope"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scope")) + if tmp, ok := rawArgs["scope"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Mutation_deleteTag_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["id"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - arg0, err = ec.unmarshalNID2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Mutation_deleteTag_argsID(ctx, rawArgs) + if err != nil { + return nil, err } args["id"] = arg0 return args, nil } +func (ec *executionContext) field_Mutation_deleteTag_argsID( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["id"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + if tmp, ok := rawArgs["id"]; ok { + return ec.unmarshalNID2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Mutation_removeTagsFromJob_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["job"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("job")) - arg0, err = ec.unmarshalNID2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Mutation_removeTagsFromJob_argsJob(ctx, rawArgs) + if err != nil { + return nil, err } args["job"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["tagIds"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) - arg1, err = ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Mutation_removeTagsFromJob_argsTagIds(ctx, rawArgs) + if err != nil { + return nil, err } args["tagIds"] = arg1 return args, nil } +func (ec *executionContext) field_Mutation_removeTagsFromJob_argsJob( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["job"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("job")) + if tmp, ok := rawArgs["job"]; ok { + return ec.unmarshalNID2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_removeTagsFromJob_argsTagIds( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["tagIds"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("tagIds")) + if tmp, ok := rawArgs["tagIds"]; ok { + return ec.unmarshalNID2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} func (ec *executionContext) field_Mutation_updateConfiguration_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Mutation_updateConfiguration_argsName(ctx, rawArgs) + if err != nil { + return nil, err } args["name"] = arg0 - var arg1 string - if tmp, ok := rawArgs["value"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) - arg1, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Mutation_updateConfiguration_argsValue(ctx, rawArgs) + if err != nil { + return nil, err } args["value"] = arg1 return args, nil } +func (ec *executionContext) field_Mutation_updateConfiguration_argsName( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["name"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + if tmp, ok := rawArgs["name"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Mutation_updateConfiguration_argsValue( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["value"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("value")) + if tmp, ok := rawArgs["value"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query___type_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["name"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query___type_argsName(ctx, rawArgs) + if err != nil { + return nil, err } args["name"] = arg0 return args, nil } +func (ec *executionContext) field_Query___type_argsName( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["name"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("name")) + if tmp, ok := rawArgs["name"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_allocatedNodes_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["cluster"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_allocatedNodes_argsCluster(ctx, rawArgs) + if err != nil { + return nil, err } args["cluster"] = arg0 return args, nil } +func (ec *executionContext) field_Query_allocatedNodes_argsCluster( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["cluster"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) + if tmp, ok := rawArgs["cluster"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_jobMetrics_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["id"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - arg0, err = ec.unmarshalNID2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_jobMetrics_argsID(ctx, rawArgs) + if err != nil { + return nil, err } args["id"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["metrics"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - arg1, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_jobMetrics_argsMetrics(ctx, rawArgs) + if err != nil { + return nil, err } args["metrics"] = arg1 - var arg2 []schema.MetricScope - if tmp, ok := rawArgs["scopes"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) - arg2, err = ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐMetricScopeᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_jobMetrics_argsScopes(ctx, rawArgs) + if err != nil { + return nil, err } args["scopes"] = arg2 - var arg3 *int - if tmp, ok := rawArgs["resolution"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("resolution")) - arg3, err = ec.unmarshalOInt2ᚖint(ctx, tmp) - if err != nil { - return nil, err - } + arg3, err := ec.field_Query_jobMetrics_argsResolution(ctx, rawArgs) + if err != nil { + return nil, err } args["resolution"] = arg3 return args, nil } +func (ec *executionContext) field_Query_jobMetrics_argsID( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["id"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + if tmp, ok := rawArgs["id"]; ok { + return ec.unmarshalNID2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobMetrics_argsMetrics( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["metrics"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) + if tmp, ok := rawArgs["metrics"]; ok { + return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobMetrics_argsScopes( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]schema.MetricScope, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["scopes"] + if !ok { + var zeroVal []schema.MetricScope + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) + if tmp, ok := rawArgs["scopes"]; ok { + return ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐMetricScopeᚄ(ctx, tmp) + } + + var zeroVal []schema.MetricScope + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobMetrics_argsResolution( + ctx context.Context, + rawArgs map[string]interface{}, +) (*int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["resolution"] + if !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("resolution")) + if tmp, ok := rawArgs["resolution"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} func (ec *executionContext) field_Query_job_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["id"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) - arg0, err = ec.unmarshalNID2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_job_argsID(ctx, rawArgs) + if err != nil { + return nil, err } args["id"] = arg0 return args, nil } +func (ec *executionContext) field_Query_job_argsID( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["id"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("id")) + if tmp, ok := rawArgs["id"]; ok { + return ec.unmarshalNID2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field_Query_jobsFootprints_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []*model.JobFilter - if tmp, ok := rawArgs["filter"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - arg0, err = ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_jobsFootprints_argsFilter(ctx, rawArgs) + if err != nil { + return nil, err } args["filter"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["metrics"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - arg1, err = ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_jobsFootprints_argsMetrics(ctx, rawArgs) + if err != nil { + return nil, err } args["metrics"] = arg1 return args, nil } +func (ec *executionContext) field_Query_jobsFootprints_argsFilter( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]*model.JobFilter, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["filter"] + if !ok { + var zeroVal []*model.JobFilter + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + if tmp, ok := rawArgs["filter"]; ok { + return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) + } + + var zeroVal []*model.JobFilter + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsFootprints_argsMetrics( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["metrics"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) + if tmp, ok := rawArgs["metrics"]; ok { + return ec.unmarshalNString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} func (ec *executionContext) field_Query_jobsStatistics_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []*model.JobFilter - if tmp, ok := rawArgs["filter"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - arg0, err = ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_jobsStatistics_argsFilter(ctx, rawArgs) + if err != nil { + return nil, err } args["filter"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["metrics"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - arg1, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_jobsStatistics_argsMetrics(ctx, rawArgs) + if err != nil { + return nil, err } args["metrics"] = arg1 - var arg2 *model.PageRequest - if tmp, ok := rawArgs["page"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - arg2, err = ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_jobsStatistics_argsPage(ctx, rawArgs) + if err != nil { + return nil, err } args["page"] = arg2 - var arg3 *model.SortByAggregate - if tmp, ok := rawArgs["sortBy"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("sortBy")) - arg3, err = ec.unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐSortByAggregate(ctx, tmp) - if err != nil { - return nil, err - } + arg3, err := ec.field_Query_jobsStatistics_argsSortBy(ctx, rawArgs) + if err != nil { + return nil, err } args["sortBy"] = arg3 - var arg4 *model.Aggregate - if tmp, ok := rawArgs["groupBy"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("groupBy")) - arg4, err = ec.unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐAggregate(ctx, tmp) - if err != nil { - return nil, err - } + arg4, err := ec.field_Query_jobsStatistics_argsGroupBy(ctx, rawArgs) + if err != nil { + return nil, err } args["groupBy"] = arg4 + arg5, err := ec.field_Query_jobsStatistics_argsNumDurationBins(ctx, rawArgs) + if err != nil { + return nil, err + } + args["numDurationBins"] = arg5 + arg6, err := ec.field_Query_jobsStatistics_argsNumMetricBins(ctx, rawArgs) + if err != nil { + return nil, err + } + args["numMetricBins"] = arg6 return args, nil } +func (ec *executionContext) field_Query_jobsStatistics_argsFilter( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]*model.JobFilter, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["filter"] + if !ok { + var zeroVal []*model.JobFilter + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + if tmp, ok := rawArgs["filter"]; ok { + return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) + } + + var zeroVal []*model.JobFilter + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsStatistics_argsMetrics( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["metrics"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) + if tmp, ok := rawArgs["metrics"]; ok { + return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsStatistics_argsPage( + ctx context.Context, + rawArgs map[string]interface{}, +) (*model.PageRequest, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["page"] + if !ok { + var zeroVal *model.PageRequest + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + if tmp, ok := rawArgs["page"]; ok { + return ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) + } + + var zeroVal *model.PageRequest + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsStatistics_argsSortBy( + ctx context.Context, + rawArgs map[string]interface{}, +) (*model.SortByAggregate, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["sortBy"] + if !ok { + var zeroVal *model.SortByAggregate + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("sortBy")) + if tmp, ok := rawArgs["sortBy"]; ok { + return ec.unmarshalOSortByAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐSortByAggregate(ctx, tmp) + } + + var zeroVal *model.SortByAggregate + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsStatistics_argsGroupBy( + ctx context.Context, + rawArgs map[string]interface{}, +) (*model.Aggregate, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["groupBy"] + if !ok { + var zeroVal *model.Aggregate + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("groupBy")) + if tmp, ok := rawArgs["groupBy"]; ok { + return ec.unmarshalOAggregate2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐAggregate(ctx, tmp) + } + + var zeroVal *model.Aggregate + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsStatistics_argsNumDurationBins( + ctx context.Context, + rawArgs map[string]interface{}, +) (*int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["numDurationBins"] + if !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("numDurationBins")) + if tmp, ok := rawArgs["numDurationBins"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobsStatistics_argsNumMetricBins( + ctx context.Context, + rawArgs map[string]interface{}, +) (*int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["numMetricBins"] + if !ok { + var zeroVal *int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("numMetricBins")) + if tmp, ok := rawArgs["numMetricBins"]; ok { + return ec.unmarshalOInt2ᚖint(ctx, tmp) + } + + var zeroVal *int + return zeroVal, nil +} func (ec *executionContext) field_Query_jobs_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []*model.JobFilter - if tmp, ok := rawArgs["filter"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - arg0, err = ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_jobs_argsFilter(ctx, rawArgs) + if err != nil { + return nil, err } args["filter"] = arg0 - var arg1 *model.PageRequest - if tmp, ok := rawArgs["page"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) - arg1, err = ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_jobs_argsPage(ctx, rawArgs) + if err != nil { + return nil, err } args["page"] = arg1 - var arg2 *model.OrderByInput - if tmp, ok := rawArgs["order"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("order")) - arg2, err = ec.unmarshalOOrderByInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐOrderByInput(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_jobs_argsOrder(ctx, rawArgs) + if err != nil { + return nil, err } args["order"] = arg2 return args, nil } +func (ec *executionContext) field_Query_jobs_argsFilter( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]*model.JobFilter, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["filter"] + if !ok { + var zeroVal []*model.JobFilter + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + if tmp, ok := rawArgs["filter"]; ok { + return ec.unmarshalOJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) + } + + var zeroVal []*model.JobFilter + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobs_argsPage( + ctx context.Context, + rawArgs map[string]interface{}, +) (*model.PageRequest, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["page"] + if !ok { + var zeroVal *model.PageRequest + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("page")) + if tmp, ok := rawArgs["page"]; ok { + return ec.unmarshalOPageRequest2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐPageRequest(ctx, tmp) + } + + var zeroVal *model.PageRequest + return zeroVal, nil +} + +func (ec *executionContext) field_Query_jobs_argsOrder( + ctx context.Context, + rawArgs map[string]interface{}, +) (*model.OrderByInput, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["order"] + if !ok { + var zeroVal *model.OrderByInput + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("order")) + if tmp, ok := rawArgs["order"]; ok { + return ec.unmarshalOOrderByInput2ᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐOrderByInput(ctx, tmp) + } + + var zeroVal *model.OrderByInput + return zeroVal, nil +} func (ec *executionContext) field_Query_nodeMetrics_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["cluster"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_nodeMetrics_argsCluster(ctx, rawArgs) + if err != nil { + return nil, err } args["cluster"] = arg0 - var arg1 []string - if tmp, ok := rawArgs["nodes"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("nodes")) - arg1, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_nodeMetrics_argsNodes(ctx, rawArgs) + if err != nil { + return nil, err } args["nodes"] = arg1 - var arg2 []schema.MetricScope - if tmp, ok := rawArgs["scopes"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) - arg2, err = ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐMetricScopeᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_nodeMetrics_argsScopes(ctx, rawArgs) + if err != nil { + return nil, err } args["scopes"] = arg2 - var arg3 []string - if tmp, ok := rawArgs["metrics"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) - arg3, err = ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg3, err := ec.field_Query_nodeMetrics_argsMetrics(ctx, rawArgs) + if err != nil { + return nil, err } args["metrics"] = arg3 - var arg4 time.Time - if tmp, ok := rawArgs["from"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("from")) - arg4, err = ec.unmarshalNTime2timeᚐTime(ctx, tmp) - if err != nil { - return nil, err - } + arg4, err := ec.field_Query_nodeMetrics_argsFrom(ctx, rawArgs) + if err != nil { + return nil, err } args["from"] = arg4 - var arg5 time.Time - if tmp, ok := rawArgs["to"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("to")) - arg5, err = ec.unmarshalNTime2timeᚐTime(ctx, tmp) - if err != nil { - return nil, err - } + arg5, err := ec.field_Query_nodeMetrics_argsTo(ctx, rawArgs) + if err != nil { + return nil, err } args["to"] = arg5 return args, nil } +func (ec *executionContext) field_Query_nodeMetrics_argsCluster( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["cluster"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cluster")) + if tmp, ok := rawArgs["cluster"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_nodeMetrics_argsNodes( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["nodes"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("nodes")) + if tmp, ok := rawArgs["nodes"]; ok { + return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_nodeMetrics_argsScopes( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]schema.MetricScope, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["scopes"] + if !ok { + var zeroVal []schema.MetricScope + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("scopes")) + if tmp, ok := rawArgs["scopes"]; ok { + return ec.unmarshalOMetricScope2ᚕgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋpkgᚋschemaᚐMetricScopeᚄ(ctx, tmp) + } + + var zeroVal []schema.MetricScope + return zeroVal, nil +} + +func (ec *executionContext) field_Query_nodeMetrics_argsMetrics( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["metrics"] + if !ok { + var zeroVal []string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("metrics")) + if tmp, ok := rawArgs["metrics"]; ok { + return ec.unmarshalOString2ᚕstringᚄ(ctx, tmp) + } + + var zeroVal []string + return zeroVal, nil +} + +func (ec *executionContext) field_Query_nodeMetrics_argsFrom( + ctx context.Context, + rawArgs map[string]interface{}, +) (time.Time, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["from"] + if !ok { + var zeroVal time.Time + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("from")) + if tmp, ok := rawArgs["from"]; ok { + return ec.unmarshalNTime2timeᚐTime(ctx, tmp) + } + + var zeroVal time.Time + return zeroVal, nil +} + +func (ec *executionContext) field_Query_nodeMetrics_argsTo( + ctx context.Context, + rawArgs map[string]interface{}, +) (time.Time, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["to"] + if !ok { + var zeroVal time.Time + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("to")) + if tmp, ok := rawArgs["to"]; ok { + return ec.unmarshalNTime2timeᚐTime(ctx, tmp) + } + + var zeroVal time.Time + return zeroVal, nil +} func (ec *executionContext) field_Query_rooflineHeatmap_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 []*model.JobFilter - if tmp, ok := rawArgs["filter"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) - arg0, err = ec.unmarshalNJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_rooflineHeatmap_argsFilter(ctx, rawArgs) + if err != nil { + return nil, err } args["filter"] = arg0 - var arg1 int - if tmp, ok := rawArgs["rows"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("rows")) - arg1, err = ec.unmarshalNInt2int(ctx, tmp) - if err != nil { - return nil, err - } + arg1, err := ec.field_Query_rooflineHeatmap_argsRows(ctx, rawArgs) + if err != nil { + return nil, err } args["rows"] = arg1 - var arg2 int - if tmp, ok := rawArgs["cols"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("cols")) - arg2, err = ec.unmarshalNInt2int(ctx, tmp) - if err != nil { - return nil, err - } + arg2, err := ec.field_Query_rooflineHeatmap_argsCols(ctx, rawArgs) + if err != nil { + return nil, err } args["cols"] = arg2 - var arg3 float64 - if tmp, ok := rawArgs["minX"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("minX")) - arg3, err = ec.unmarshalNFloat2float64(ctx, tmp) - if err != nil { - return nil, err - } + arg3, err := ec.field_Query_rooflineHeatmap_argsMinX(ctx, rawArgs) + if err != nil { + return nil, err } args["minX"] = arg3 - var arg4 float64 - if tmp, ok := rawArgs["minY"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("minY")) - arg4, err = ec.unmarshalNFloat2float64(ctx, tmp) - if err != nil { - return nil, err - } + arg4, err := ec.field_Query_rooflineHeatmap_argsMinY(ctx, rawArgs) + if err != nil { + return nil, err } args["minY"] = arg4 - var arg5 float64 - if tmp, ok := rawArgs["maxX"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("maxX")) - arg5, err = ec.unmarshalNFloat2float64(ctx, tmp) - if err != nil { - return nil, err - } + arg5, err := ec.field_Query_rooflineHeatmap_argsMaxX(ctx, rawArgs) + if err != nil { + return nil, err } args["maxX"] = arg5 - var arg6 float64 - if tmp, ok := rawArgs["maxY"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("maxY")) - arg6, err = ec.unmarshalNFloat2float64(ctx, tmp) - if err != nil { - return nil, err - } + arg6, err := ec.field_Query_rooflineHeatmap_argsMaxY(ctx, rawArgs) + if err != nil { + return nil, err } args["maxY"] = arg6 return args, nil } +func (ec *executionContext) field_Query_rooflineHeatmap_argsFilter( + ctx context.Context, + rawArgs map[string]interface{}, +) ([]*model.JobFilter, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["filter"] + if !ok { + var zeroVal []*model.JobFilter + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("filter")) + if tmp, ok := rawArgs["filter"]; ok { + return ec.unmarshalNJobFilter2ᚕᚖgithubᚗcomᚋClusterCockpitᚋccᚑbackendᚋinternalᚋgraphᚋmodelᚐJobFilterᚄ(ctx, tmp) + } + + var zeroVal []*model.JobFilter + return zeroVal, nil +} + +func (ec *executionContext) field_Query_rooflineHeatmap_argsRows( + ctx context.Context, + rawArgs map[string]interface{}, +) (int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["rows"] + if !ok { + var zeroVal int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("rows")) + if tmp, ok := rawArgs["rows"]; ok { + return ec.unmarshalNInt2int(ctx, tmp) + } + + var zeroVal int + return zeroVal, nil +} + +func (ec *executionContext) field_Query_rooflineHeatmap_argsCols( + ctx context.Context, + rawArgs map[string]interface{}, +) (int, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["cols"] + if !ok { + var zeroVal int + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("cols")) + if tmp, ok := rawArgs["cols"]; ok { + return ec.unmarshalNInt2int(ctx, tmp) + } + + var zeroVal int + return zeroVal, nil +} + +func (ec *executionContext) field_Query_rooflineHeatmap_argsMinX( + ctx context.Context, + rawArgs map[string]interface{}, +) (float64, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["minX"] + if !ok { + var zeroVal float64 + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("minX")) + if tmp, ok := rawArgs["minX"]; ok { + return ec.unmarshalNFloat2float64(ctx, tmp) + } + + var zeroVal float64 + return zeroVal, nil +} + +func (ec *executionContext) field_Query_rooflineHeatmap_argsMinY( + ctx context.Context, + rawArgs map[string]interface{}, +) (float64, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["minY"] + if !ok { + var zeroVal float64 + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("minY")) + if tmp, ok := rawArgs["minY"]; ok { + return ec.unmarshalNFloat2float64(ctx, tmp) + } + + var zeroVal float64 + return zeroVal, nil +} + +func (ec *executionContext) field_Query_rooflineHeatmap_argsMaxX( + ctx context.Context, + rawArgs map[string]interface{}, +) (float64, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["maxX"] + if !ok { + var zeroVal float64 + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("maxX")) + if tmp, ok := rawArgs["maxX"]; ok { + return ec.unmarshalNFloat2float64(ctx, tmp) + } + + var zeroVal float64 + return zeroVal, nil +} + +func (ec *executionContext) field_Query_rooflineHeatmap_argsMaxY( + ctx context.Context, + rawArgs map[string]interface{}, +) (float64, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["maxY"] + if !ok { + var zeroVal float64 + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("maxY")) + if tmp, ok := rawArgs["maxY"]; ok { + return ec.unmarshalNFloat2float64(ctx, tmp) + } + + var zeroVal float64 + return zeroVal, nil +} func (ec *executionContext) field_Query_user_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 string - if tmp, ok := rawArgs["username"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("username")) - arg0, err = ec.unmarshalNString2string(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field_Query_user_argsUsername(ctx, rawArgs) + if err != nil { + return nil, err } args["username"] = arg0 return args, nil } +func (ec *executionContext) field_Query_user_argsUsername( + ctx context.Context, + rawArgs map[string]interface{}, +) (string, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["username"] + if !ok { + var zeroVal string + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("username")) + if tmp, ok := rawArgs["username"]; ok { + return ec.unmarshalNString2string(ctx, tmp) + } + + var zeroVal string + return zeroVal, nil +} func (ec *executionContext) field___Type_enumValues_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field___Type_enumValues_argsIncludeDeprecated(ctx, rawArgs) + if err != nil { + return nil, err } args["includeDeprecated"] = arg0 return args, nil } +func (ec *executionContext) field___Type_enumValues_argsIncludeDeprecated( + ctx context.Context, + rawArgs map[string]interface{}, +) (bool, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["includeDeprecated"] + if !ok { + var zeroVal bool + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + if tmp, ok := rawArgs["includeDeprecated"]; ok { + return ec.unmarshalOBoolean2bool(ctx, tmp) + } + + var zeroVal bool + return zeroVal, nil +} func (ec *executionContext) field___Type_fields_args(ctx context.Context, rawArgs map[string]interface{}) (map[string]interface{}, error) { var err error args := map[string]interface{}{} - var arg0 bool - if tmp, ok := rawArgs["includeDeprecated"]; ok { - ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) - arg0, err = ec.unmarshalOBoolean2bool(ctx, tmp) - if err != nil { - return nil, err - } + arg0, err := ec.field___Type_fields_argsIncludeDeprecated(ctx, rawArgs) + if err != nil { + return nil, err } args["includeDeprecated"] = arg0 return args, nil } +func (ec *executionContext) field___Type_fields_argsIncludeDeprecated( + ctx context.Context, + rawArgs map[string]interface{}, +) (bool, error) { + // We won't call the directive if the argument is null. + // Set call_argument_directives_with_null to true to call directives + // even if the argument is null. + _, ok := rawArgs["includeDeprecated"] + if !ok { + var zeroVal bool + return zeroVal, nil + } + + ctx = graphql.WithPathContext(ctx, graphql.NewPathWithField("includeDeprecated")) + if tmp, ok := rawArgs["includeDeprecated"]; ok { + return ec.unmarshalOBoolean2bool(ctx, tmp) + } + + var zeroVal bool + return zeroVal, nil +} // endregion ***************************** args.gotpl ***************************** @@ -9018,7 +9829,7 @@ func (ec *executionContext) _Query_jobsStatistics(ctx context.Context, field gra }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return ec.resolvers.Query().JobsStatistics(rctx, fc.Args["filter"].([]*model.JobFilter), fc.Args["metrics"].([]string), fc.Args["page"].(*model.PageRequest), fc.Args["sortBy"].(*model.SortByAggregate), fc.Args["groupBy"].(*model.Aggregate)) + return ec.resolvers.Query().JobsStatistics(rctx, fc.Args["filter"].([]*model.JobFilter), fc.Args["metrics"].([]string), fc.Args["page"].(*model.PageRequest), fc.Args["sortBy"].(*model.SortByAggregate), fc.Args["groupBy"].(*model.Aggregate), fc.Args["numDurationBins"].(*int), fc.Args["numMetricBins"].(*int)) }) if err != nil { ec.Error(ctx, err) diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index 9fd7260..aae5812 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -2,7 +2,7 @@ package graph // This file will be automatically regenerated based on the schema, any resolver implementations // will be copied through when generating and any unknown code will be moved to the end. -// Code generated by github.com/99designs/gqlgen version v0.17.49 +// Code generated by github.com/99designs/gqlgen version v0.17.57 import ( "context" @@ -357,7 +357,7 @@ func (r *queryResolver) Jobs(ctx context.Context, filter []*model.JobFilter, pag } // JobsStatistics is the resolver for the jobsStatistics field. -func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate) ([]*model.JobsStatistics, error) { +func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobFilter, metrics []string, page *model.PageRequest, sortBy *model.SortByAggregate, groupBy *model.Aggregate, numDurationBins *int, numMetricBins *int) ([]*model.JobsStatistics, error) { var err error var stats []*model.JobsStatistics @@ -395,7 +395,7 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF if requireField(ctx, "histDuration") || requireField(ctx, "histNumNodes") || requireField(ctx, "histNumCores") || requireField(ctx, "histNumAccs") { if groupBy == nil { - stats[0], err = r.Repo.AddHistograms(ctx, filter, stats[0]) + stats[0], err = r.Repo.AddHistograms(ctx, filter, stats[0], numDurationBins) if err != nil { return nil, err } @@ -406,7 +406,7 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF if requireField(ctx, "histMetrics") { if groupBy == nil { - stats[0], err = r.Repo.AddMetricHistograms(ctx, filter, metrics, stats[0]) + stats[0], err = r.Repo.AddMetricHistograms(ctx, filter, metrics, stats[0], numMetricBins) if err != nil { return nil, err } @@ -493,11 +493,9 @@ func (r *Resolver) Query() generated.QueryResolver { return &queryResolver{r} } // SubCluster returns generated.SubClusterResolver implementation. func (r *Resolver) SubCluster() generated.SubClusterResolver { return &subClusterResolver{r} } -type ( - clusterResolver struct{ *Resolver } - jobResolver struct{ *Resolver } - metricValueResolver struct{ *Resolver } - mutationResolver struct{ *Resolver } - queryResolver struct{ *Resolver } - subClusterResolver struct{ *Resolver } -) +type clusterResolver struct{ *Resolver } +type jobResolver struct{ *Resolver } +type metricValueResolver struct{ *Resolver } +type mutationResolver struct{ *Resolver } +type queryResolver struct{ *Resolver } +type subClusterResolver struct{ *Resolver } diff --git a/internal/repository/stats.go b/internal/repository/stats.go index 3245b70..5a055a4 100644 --- a/internal/repository/stats.go +++ b/internal/repository/stats.go @@ -447,15 +447,29 @@ func (r *JobRepository) AddHistograms( ctx context.Context, filter []*model.JobFilter, stat *model.JobsStatistics, + targetBinCount *int, ) (*model.JobsStatistics, error) { start := time.Now() - binSeconds := 900 - binMinutes := binSeconds / 60 + // targetBinCount : Frontendargument + // -> Min Bins: 24 -> Min Resolution: By Hour + // -> In Between Bins: 48 -> Resolution by Half Hour + // 96 -> Resolution by Quarter Hour (Skip for Now) + // 144 -> Resolution by 10 Minutes + // 288 -> Resolution by 5 Minutes + // 720 -> Resolution by 2 Minutes (SKip for Now) + // -> Max Bins: 1440 -> Max Resolution: By Minute + + if targetBinCount == nil { + binCount := 24 + targetBinCount = &binCount + } + binSizeSeconds := (86400 / *targetBinCount) + castType := r.getCastType() var err error - // Bin by job duration in sizes of binSeconds, add +1, gives Integers from 1-XX+1, re-multiply by binMinutes to get final bar x-values (logic: Jobs less than duration X in bin) - value := fmt.Sprintf(`CAST((ROUND(((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / %d) + 1) * %d) as %s) as value`, time.Now().Unix(), binSeconds, binMinutes, castType) + // Return X-Values always as seconds, will be formatted into minutes and hours in frontend + value := fmt.Sprintf(`CAST(ROUND(((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / %d) + 1) as %s) as value`, time.Now().Unix(), binSizeSeconds, castType) stat.HistDuration, err = r.jobsStatisticsHistogram(ctx, value, filter) if err != nil { log.Warn("Error while loading job statistics histogram: job duration") @@ -490,14 +504,20 @@ func (r *JobRepository) AddMetricHistograms( filter []*model.JobFilter, metrics []string, stat *model.JobsStatistics, + targetBinCount *int, ) (*model.JobsStatistics, error) { start := time.Now() + if targetBinCount == nil { + binCount := 10 + targetBinCount = &binCount + } + // Running Jobs Only: First query jobdata from sqlite, then query data and make bins for _, f := range filter { if f.State != nil { if len(f.State) == 1 && f.State[0] == "running" { - stat.HistMetrics = r.runningJobsMetricStatisticsHistogram(ctx, metrics, filter) + stat.HistMetrics = r.runningJobsMetricStatisticsHistogram(ctx, metrics, filter, targetBinCount) log.Debugf("Timer AddMetricHistograms %s", time.Since(start)) return stat, nil } @@ -506,7 +526,7 @@ func (r *JobRepository) AddMetricHistograms( // All other cases: Query and make bins in sqlite directly for _, m := range metrics { - metricHisto, err := r.jobsMetricStatisticsHistogram(ctx, m, filter) + metricHisto, err := r.jobsMetricStatisticsHistogram(ctx, m, filter, targetBinCount) if err != nil { log.Warnf("Error while loading job metric statistics histogram: %s", m) continue @@ -560,6 +580,7 @@ func (r *JobRepository) jobsMetricStatisticsHistogram( ctx context.Context, metric string, filters []*model.JobFilter, + bins *int, ) (*model.MetricHistoPoints, error) { // Get specific Peak or largest Peak var metricConfig *schema.MetricConfig @@ -627,7 +648,6 @@ func (r *JobRepository) jobsMetricStatisticsHistogram( return nil, sqlerr } - bins := 10 binQuery := fmt.Sprintf(`CAST( (case when %s = value.max then value.max*0.999999999 else %s end - value.min) / (value.max - value.min) * %d as INTEGER )`, jm, jm, bins) @@ -681,6 +701,7 @@ func (r *JobRepository) runningJobsMetricStatisticsHistogram( ctx context.Context, metrics []string, filters []*model.JobFilter, + bins *int, ) []*model.MetricHistoPoints { // Get Jobs jobs, err := r.QueryJobs(ctx, filters, &model.PageRequest{Page: 1, ItemsPerPage: 500 + 1}, nil) @@ -743,8 +764,7 @@ func (r *JobRepository) runningJobsMetricStatisticsHistogram( } // Make and fill bins - bins := 10.0 - peakBin := peak / bins + peakBin := peak / float64(*bins) points := make([]*model.MetricHistoPoint, 0) for b := 0; b < 10; b++ { diff --git a/web/frontend/src/User.root.svelte b/web/frontend/src/User.root.svelte index 8f7ed57..4c958a9 100644 --- a/web/frontend/src/User.root.svelte +++ b/web/frontend/src/User.root.svelte @@ -213,7 +213,7 @@ {#key $stats.data.jobsStatistics[0].histDuration} Date: Thu, 23 Jan 2025 12:23:29 +0100 Subject: [PATCH 3/7] initial duration histogram zoom in frontend - metric zoom todo - keeping last zoomState does not work --- internal/graph/schema.resolvers.go | 12 +++ internal/repository/stats.go | 71 +++++++++++++--- web/frontend/src/User.root.svelte | 55 ++++++++++-- .../src/generic/plots/Histogram.svelte | 84 +++++++++++++++++-- web/frontend/src/generic/utils.js | 2 +- 5 files changed, 200 insertions(+), 24 deletions(-) diff --git a/internal/graph/schema.resolvers.go b/internal/graph/schema.resolvers.go index aae5812..98f9a83 100644 --- a/internal/graph/schema.resolvers.go +++ b/internal/graph/schema.resolvers.go @@ -394,6 +394,12 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF } if requireField(ctx, "histDuration") || requireField(ctx, "histNumNodes") || requireField(ctx, "histNumCores") || requireField(ctx, "histNumAccs") { + + if numDurationBins == nil { + binCount := 24 + numDurationBins = &binCount + } + if groupBy == nil { stats[0], err = r.Repo.AddHistograms(ctx, filter, stats[0], numDurationBins) if err != nil { @@ -405,6 +411,12 @@ func (r *queryResolver) JobsStatistics(ctx context.Context, filter []*model.JobF } if requireField(ctx, "histMetrics") { + + if numMetricBins == nil { + binCount := 10 + numMetricBins = &binCount + } + if groupBy == nil { stats[0], err = r.Repo.AddMetricHistograms(ctx, filter, metrics, stats[0], numMetricBins) if err != nil { diff --git a/internal/repository/stats.go b/internal/repository/stats.go index 5a055a4..12caa6c 100644 --- a/internal/repository/stats.go +++ b/internal/repository/stats.go @@ -454,23 +454,19 @@ func (r *JobRepository) AddHistograms( // targetBinCount : Frontendargument // -> Min Bins: 24 -> Min Resolution: By Hour // -> In Between Bins: 48 -> Resolution by Half Hour - // 96 -> Resolution by Quarter Hour (Skip for Now) + // 96 -> Resolution by Quarter Hour // 144 -> Resolution by 10 Minutes // 288 -> Resolution by 5 Minutes - // 720 -> Resolution by 2 Minutes (SKip for Now) + // 720 -> Resolution by 2 Minutes // -> Max Bins: 1440 -> Max Resolution: By Minute - if targetBinCount == nil { - binCount := 24 - targetBinCount = &binCount - } binSizeSeconds := (86400 / *targetBinCount) castType := r.getCastType() var err error // Return X-Values always as seconds, will be formatted into minutes and hours in frontend value := fmt.Sprintf(`CAST(ROUND(((CASE WHEN job.job_state = "running" THEN %d - job.start_time ELSE job.duration END) / %d) + 1) as %s) as value`, time.Now().Unix(), binSizeSeconds, castType) - stat.HistDuration, err = r.jobsStatisticsHistogram(ctx, value, filter) + stat.HistDuration, err = r.jobsDurationStatisticsHistogram(ctx, value, filter, binSizeSeconds, targetBinCount) if err != nil { log.Warn("Error while loading job statistics histogram: job duration") return nil, err @@ -508,10 +504,7 @@ func (r *JobRepository) AddMetricHistograms( ) (*model.JobsStatistics, error) { start := time.Now() - if targetBinCount == nil { - binCount := 10 - targetBinCount = &binCount - } + log.Debugf(">>> HELLO ADD HISTO Metrics: Target %d", *targetBinCount) // Running Jobs Only: First query jobdata from sqlite, then query data and make bins for _, f := range filter { @@ -563,6 +556,7 @@ func (r *JobRepository) jobsStatisticsHistogram( } points := make([]*model.HistoPoint, 0) + // is it possible to introduce zero values here? requires info about bincount for rows.Next() { point := model.HistoPoint{} if err := rows.Scan(&point.Value, &point.Count); err != nil { @@ -576,6 +570,61 @@ func (r *JobRepository) jobsStatisticsHistogram( return points, nil } +func (r *JobRepository) jobsDurationStatisticsHistogram( + ctx context.Context, + value string, + filters []*model.JobFilter, + binSizeSeconds int, + targetBinCount *int, +) ([]*model.HistoPoint, error) { + start := time.Now() + query, qerr := SecurityCheck(ctx, + sq.Select(value, "COUNT(job.id) AS count").From("job")) + + if qerr != nil { + return nil, qerr + } + + // Setup Array + points := make([]*model.HistoPoint, 0) + for i := 1; i <= *targetBinCount; i++ { + point := model.HistoPoint{Value: i * binSizeSeconds, Count: 0} + points = append(points, &point) + } + + for _, f := range filters { + query = BuildWhereClause(f, query) + } + + rows, err := query.GroupBy("value").RunWith(r.DB).Query() + if err != nil { + log.Error("Error while running query") + return nil, err + } + + // Fill Array at matching $Value + for rows.Next() { + point := model.HistoPoint{} + if err := rows.Scan(&point.Value, &point.Count); err != nil { + log.Warn("Error while scanning rows") + return nil, err + } + + for _, e := range points { + if e.Value == (point.Value * binSizeSeconds) { + // Note: + // Matching on unmodified integer value (and multiplying point.Value by binSizeSeconds after match) + // causes frontend to loop into highest targetBinCount, due to zoom condition instantly being fullfilled (cause unknown) + e.Count = point.Count + break + } + } + } + + log.Debugf("Timer jobsStatisticsHistogram %s", time.Since(start)) + return points, nil +} + func (r *JobRepository) jobsMetricStatisticsHistogram( ctx context.Context, metric string, diff --git a/web/frontend/src/User.root.svelte b/web/frontend/src/User.root.svelte index 4c958a9..853a89b 100644 --- a/web/frontend/src/User.root.svelte +++ b/web/frontend/src/User.root.svelte @@ -59,6 +59,9 @@ let showFootprint = filterPresets.cluster ? !!ccconfig[`plot_list_showFootprint:${filterPresets.cluster}`] : !!ccconfig.plot_list_showFootprint; + let numDurationBins; + let numMetricBins; + $: metricsInHistograms = selectedCluster ? ccconfig[`user_view_histogramMetrics:${selectedCluster}`] || [] @@ -68,8 +71,8 @@ $: stats = queryStore({ client: client, query: gql` - query ($jobFilters: [JobFilter!]!, $metricsInHistograms: [String!]) { - jobsStatistics(filter: $jobFilters, metrics: $metricsInHistograms) { + query ($jobFilters: [JobFilter!]!, $metricsInHistograms: [String!], $numDurationBins: Int, $numMetricBins: Int) { + jobsStatistics(filter: $jobFilters, metrics: $metricsInHistograms, numDurationBins: $numDurationBins , numMetricBins: $numMetricBins ) { totalJobs shortJobs totalWalltime @@ -96,9 +99,41 @@ } } `, - variables: { jobFilters, metricsInHistograms }, + variables: { jobFilters, metricsInHistograms, numDurationBins, numMetricBins }, }); + let durationZoomState = null; + let metricZoomState = null; + let pendingDurationBinCount = null; + let pendingMetricBinCount = null; + let pendingZoomState = null; + function handleZoom(detail) { + if ( // States have to differ, causes deathloop if just set + (pendingZoomState?.x?.min !== detail?.lastZoomState?.x?.min) && + (pendingZoomState?.y?.max !== detail?.lastZoomState?.y?.max) + ) { + pendingZoomState = {...detail.lastZoomState}; + } + + if (detail?.durationBinCount) { // Triggers GQL + pendingDurationBinCount = detail.durationBinCount; + } + + if (detail?.metricBinCount) { // Triggers GQL + pendingMetricBinCount = detail.metricBinCount; + } + }; + + $: if (pendingDurationBinCount !== numDurationBins) { + durationZoomState = {...pendingZoomState}; + numDurationBins = pendingDurationBinCount; + } + + $: if (pendingMetricBinCount !== numMetricBins) { + metricZoomState = {...pendingZoomState}; + numMetricBins = pendingMetricBinCount; + } + onMount(() => filterComponent.updateFilters()); @@ -213,13 +248,17 @@ {#key $stats.data.jobsStatistics[0].histDuration} { handleZoom(detail) }} data={convert2uplot($stats.data.jobsStatistics[0].histDuration)} title="Duration Distribution" - xlabel="Current Runtimes (Hours)" - xtime={true} - xunit="Hours" + xlabel="Job Runtimes" + xunit="Runtime" ylabel="Number of Jobs" yunit="Jobs" + lastBinCount={pendingDurationBinCount} + {durationZoomState} + zoomableHistogram + xtime /> {/key} @@ -273,6 +312,7 @@ itemsPerRow={3} > { handleZoom(detail) }} data={convert2uplot(item.data)} usesBins={true} title="Distribution of '{item.metric} ({item.stat})' footprints" @@ -280,6 +320,9 @@ xunit={item.unit} ylabel="Number of Jobs" yunit="Jobs" + lastBinCount={pendingMetricBinCount} + {metricZoomState} + zoomableHistogram /> {/key} diff --git a/web/frontend/src/generic/plots/Histogram.svelte b/web/frontend/src/generic/plots/Histogram.svelte index fbc8f14..51ca543 100644 --- a/web/frontend/src/generic/plots/Histogram.svelte +++ b/web/frontend/src/generic/plots/Histogram.svelte @@ -15,8 +15,8 @@ @@ -153,8 +126,8 @@ {/if} - - + + - + - + + + + + + + Duration Bin Size + + + {#each durationBinOptions as dbin} + + {/each} + + + + filterComponent.updateFilters(detail)} /> - + { jobList.refreshJobs() jobList.refreshAllMetrics() @@ -248,16 +236,13 @@ {#key $stats.data.jobsStatistics[0].histDuration} { handleZoom(detail) }} data={convert2uplot($stats.data.jobsStatistics[0].histDuration)} title="Duration Distribution" xlabel="Job Runtimes" xunit="Runtime" ylabel="Number of Jobs" yunit="Jobs" - lastBinCount={pendingDurationBinCount} - {durationZoomState} - zoomableHistogram + usesBins xtime /> {/key} @@ -278,16 +263,32 @@ - - + + + + + + + + + Metric Bins + + + {#each metricBinOptions as mbin} + + {/each} + + + {#if metricsInHistograms?.length > 0} {#if $stats.error} @@ -312,17 +313,13 @@ itemsPerRow={3} > { handleZoom(detail) }} data={convert2uplot(item.data)} - usesBins={true} title="Distribution of '{item.metric} ({item.stat})' footprints" xlabel={`${item.metric} bin maximum ${item?.unit ? `[${item.unit}]` : ``}`} xunit={item.unit} ylabel="Number of Jobs" yunit="Jobs" - lastBinCount={pendingMetricBinCount} - {metricZoomState} - zoomableHistogram + usesBins /> {/key} diff --git a/web/frontend/src/generic/plots/Histogram.svelte b/web/frontend/src/generic/plots/Histogram.svelte index 8331a9b..fbc5a33 100644 --- a/web/frontend/src/generic/plots/Histogram.svelte +++ b/web/frontend/src/generic/plots/Histogram.svelte @@ -15,7 +15,7 @@