Skip to content

Commit fb2854c

Browse files
authored
ui: Samples strips in Flamecharts visualization (#6192)
* SamplesStrips now part of the Flamechart visualization * Linter fixes * go test fix * Minor UX improvements * Dragging UX improvement * Added a button to quickly switch ot 1m range and consistent strip interaction fixes
1 parent 79deeec commit fb2854c

File tree

31 files changed

+4096
-12269
lines changed

31 files changed

+4096
-12269
lines changed

gen/proto/go/parca/query/v1alpha1/query.pb.go

Lines changed: 13 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/proto/go/parca/query/v1alpha1/query_vtproto.pb.go

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gen/proto/swagger/parca/query/v1alpha1/query.swagger.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,11 @@
15031503
"type": "string",
15041504
"format": "int64",
15051505
"description": "duration is the normalized aggregated duration the metric samples has been observed over."
1506+
},
1507+
"count": {
1508+
"type": "integer",
1509+
"format": "int32",
1510+
"description": "count is the number of samples aggregated into this data point."
15061511
}
15071512
},
15081513
"title": "MetricsSample is a cumulative value and timestamp of a profile"

pkg/parcacol/querier.go

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -348,11 +348,6 @@ func (q *Querier) QueryRange(
348348
return nil, err
349349
}
350350

351-
// The step cannot be lower than 1s
352-
if step < time.Second {
353-
step = time.Second
354-
}
355-
356351
start := startTime.UnixNano()
357352
end := endTime.UnixNano()
358353

@@ -401,6 +396,8 @@ func (q *Querier) queryRangeDelta(
401396

402397
totalSum := logicalplan.Sum(logicalplan.Col(profile.ColumnValue))
403398
totalSumColumn := totalSum.Name()
399+
totalCount := logicalplan.Count(logicalplan.Col(profile.ColumnValue))
400+
totalCountColumn := totalCount.Name()
404401
durationMin := logicalplan.Min(logicalplan.Col(profile.ColumnDuration))
405402
timestampUnique := logicalplan.Unique(logicalplan.Col(profile.ColumnTimeNanos))
406403

@@ -476,6 +473,7 @@ func (q *Querier) queryRangeDelta(
476473
durationMin,
477474
timestampUnique,
478475
totalSum,
476+
totalCount,
479477
},
480478
append([]logicalplan.Expr{
481479
logicalplan.Col(TimestampBucket),
@@ -489,6 +487,7 @@ func (q *Querier) queryRangeDelta(
489487
durationMin,
490488
).Alias(profile.ColumnDuration),
491489
totalSum,
490+
totalCount,
492491
logicalplan.DynCol(profile.ColumnLabels),
493492
logicalplan.Col(TimestampBucket),
494493
).
@@ -514,11 +513,13 @@ func (q *Querier) queryRangeDelta(
514513
PerSecondValue int
515514
ValueSum int
516515
Duration int
516+
Count int
517517
}{
518518
Timestamp: -1,
519519
PerSecondValue: -1,
520520
ValueSum: -1,
521521
Duration: -1,
522+
Count: -1,
522523
}
523524

524525
labelColumnIndices := []int{}
@@ -541,6 +542,9 @@ func (q *Querier) queryRangeDelta(
541542
continue
542543
case profile.ColumnDuration:
543544
columnIndices.Duration = i
545+
case totalCountColumn:
546+
columnIndices.Count = i
547+
continue
544548
}
545549

546550
if strings.HasPrefix(field.Name, "labels.") {
@@ -606,13 +610,15 @@ func (q *Querier) queryRangeDelta(
606610
valueSum := ar.Column(columnIndices.ValueSum).(*array.Int64).Value(i)
607611
valuePerSecond := ar.Column(columnIndices.PerSecondValue).(*array.Float64).Value(i)
608612
duration := ar.Column(columnIndices.Duration).(*array.Int64).Value(i)
613+
count := ar.Column(columnIndices.Count).(*array.Int64).Value(i)
609614

610615
series := resSeries[index]
611616
series.Samples = append(series.Samples, &pb.MetricsSample{
612617
Timestamp: timestamppb.New(time.Unix(0, ts)),
613618
Value: valueSum,
614619
ValuePerSecond: valuePerSecond,
615620
Duration: duration,
621+
Count: int32(count),
616622
})
617623
}
618624
}
@@ -686,7 +692,7 @@ func (q *Querier) queryRangeNonDelta(ctx context.Context, filterExpr logicalplan
686692
labelColumnIndices := []int{}
687693
labelSet := labels.NewScratchBuilder(64)
688694
resSeries := []*pb.MetricsSeries{}
689-
resSeriesBuckets := map[int]map[int64]struct{}{}
695+
resSeriesBuckets := map[int]map[int64]int{}
690696
labelsetToIndex := map[string]int{}
691697

692698
for _, ar := range records {
@@ -740,7 +746,7 @@ func (q *Querier) queryRangeNonDelta(ctx context.Context, filterExpr logicalplan
740746
resSeries = append(resSeries, &pb.MetricsSeries{Labelset: &profilestorepb.LabelSet{Labels: pbLabelSet}})
741747
index = len(resSeries) - 1
742748
labelsetToIndex[s] = index
743-
resSeriesBuckets[index] = map[int64]struct{}{}
749+
resSeriesBuckets[index] = map[int64]int{}
744750
}
745751

746752
ts := ar.Column(columnIndices[profile.ColumnTimeNanos].index).(*array.Int64).Value(i)
@@ -755,9 +761,13 @@ func (q *Querier) queryRangeNonDelta(ctx context.Context, filterExpr logicalplan
755761
// This needs to be moved to FrostDB to not even query all of this data in the first place.
756762
// With a scrape interval of 10s and a query range of 1d we'd query 8640 samples and at most return 960.
757763
// Even worse for a week, we'd query 60480 samples and only return 1000.
758-
tsBucket := ts / 1000 / int64(step.Seconds())
759-
if _, found := resSeriesBuckets[index][tsBucket]; found {
760-
// We already have a MetricsSample for this timestamp bucket, ignore it.
764+
tsBucket := ts
765+
if stepNanos := step.Nanoseconds(); stepNanos > 0 {
766+
tsBucket = ts / stepNanos
767+
}
768+
if sampleIdx, found := resSeriesBuckets[index][tsBucket]; found {
769+
// We already have a MetricsSample for this timestamp bucket, increment its count.
770+
resSeries[index].Samples[sampleIdx].Count++
761771
continue
762772
}
763773

@@ -766,9 +776,10 @@ func (q *Querier) queryRangeNonDelta(ctx context.Context, filterExpr logicalplan
766776
Timestamp: timestamppb.New(time.Unix(0, ts)),
767777
Value: value,
768778
ValuePerSecond: float64(value),
779+
Count: 1,
769780
})
770-
// Mark the timestamp bucket as filled by the above MetricsSample.
771-
resSeriesBuckets[index][tsBucket] = struct{}{}
781+
// Mark the timestamp bucket as filled by the above MetricsSample, storing its index.
782+
resSeriesBuckets[index][tsBucket] = len(series.Samples) - 1
772783
}
773784
}
774785

proto/parca/query/v1alpha1/query.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ message MetricsSample {
142142

143143
// duration is the normalized aggregated duration the metric samples has been observed over.
144144
int64 duration = 4;
145+
146+
// count is the number of samples aggregated into this data point.
147+
int32 count = 5;
145148
}
146149

147150
// MergeProfile contains parameters for a merge request

ui/packages/shared/client/src/parca/query/v1alpha1/query.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ export interface MetricsSample {
210210
* @generated from protobuf field: int64 duration = 4
211211
*/
212212
duration: bigint;
213+
/**
214+
* count is the number of samples aggregated into this data point.
215+
*
216+
* @generated from protobuf field: int32 count = 5
217+
*/
218+
count: number;
213219
}
214220
/**
215221
* MergeProfile contains parameters for a merge request
@@ -2036,14 +2042,16 @@ class MetricsSample$Type extends MessageType<MetricsSample> {
20362042
{ no: 1, name: "timestamp", kind: "message", T: () => Timestamp },
20372043
{ no: 2, name: "value", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
20382044
{ no: 3, name: "value_per_second", kind: "scalar", T: 1 /*ScalarType.DOUBLE*/ },
2039-
{ no: 4, name: "duration", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ }
2045+
{ no: 4, name: "duration", kind: "scalar", T: 3 /*ScalarType.INT64*/, L: 0 /*LongType.BIGINT*/ },
2046+
{ no: 5, name: "count", kind: "scalar", T: 5 /*ScalarType.INT32*/ }
20402047
]);
20412048
}
20422049
create(value?: PartialMessage<MetricsSample>): MetricsSample {
20432050
const message = globalThis.Object.create((this.messagePrototype!));
20442051
message.value = 0n;
20452052
message.valuePerSecond = 0;
20462053
message.duration = 0n;
2054+
message.count = 0;
20472055
if (value !== undefined)
20482056
reflectionMergePartial<MetricsSample>(this, message, value);
20492057
return message;
@@ -2065,6 +2073,9 @@ class MetricsSample$Type extends MessageType<MetricsSample> {
20652073
case /* int64 duration */ 4:
20662074
message.duration = reader.int64().toBigInt();
20672075
break;
2076+
case /* int32 count */ 5:
2077+
message.count = reader.int32();
2078+
break;
20682079
default:
20692080
let u = options.readUnknownField;
20702081
if (u === "throw")
@@ -2089,6 +2100,9 @@ class MetricsSample$Type extends MessageType<MetricsSample> {
20892100
/* int64 duration = 4; */
20902101
if (message.duration !== 0n)
20912102
writer.tag(4, WireType.Varint).int64(message.duration);
2103+
/* int32 count = 5; */
2104+
if (message.count !== 0)
2105+
writer.tag(5, WireType.Varint).int32(message.count);
20922106
let u = options.writeUnknownFields;
20932107
if (u !== false)
20942108
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);

ui/packages/shared/components/src/hooks/URLState/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,10 +334,13 @@ export const JSONParser = <T,>(val: ParamValue): T => {
334334
return JSON.parse(val as string);
335335
};
336336

337-
export const NumberParser = (val: string): number => {
337+
export const NumberParser = (val: ParamValue): number => {
338338
if (val == null || val === '' || val === 'undefined') {
339339
return 0;
340340
}
341+
if (Array.isArray(val)) {
342+
return val.length > 0 ? Number(val[0]) : 0;
343+
}
341344
return Number(val);
342345
};
343346

ui/packages/shared/profile/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"react-use": "^17.5.0",
5858
"tailwindcss": "3.2.4",
5959
"tsc-watch": "6.3.1",
60+
"usehooks-ts": "^3.1.1",
6061
"with-alpha-hex": "^1.0.6"
6162
},
6263
"devDependencies": {

ui/packages/shared/profile/src/MetricsGraphStrips/AreaGraph/Tooltip.tsx

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)