Skip to content

Commit 4704abf

Browse files
craig[bot]kev-cao
andcommitted
Merge #153893
153893: pcr: add initial/catchup scan metrics r=jeffswenson a=kev-cao This commit adds initial and catchup scan metrics to PCR similar to the metrics supplied in steady-state LDR. The two metrics can be found with `physical_replication.scanning_ranges` and `physical_replication.catchup_ranges`, respectively. Fixes: #152272 Release note: Adds initial and catchup scan metrics to PCR under `physical_replication.scanning_ranges` and `physical_replication.catchup_ranges`. Co-authored-by: Kevin Cao <[email protected]>
2 parents 37e0337 + 524c222 commit 4704abf

File tree

7 files changed

+125
-9
lines changed

7 files changed

+125
-9
lines changed

docs/generated/metrics/metrics.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7118,6 +7118,14 @@ layers:
71187118
unit: NANOSECONDS
71197119
aggregation: AVG
71207120
derivative: NONE
7121+
- name: physical_replication.catchup_ranges
7122+
exported_name: physical_replication_catchup_ranges
7123+
description: Source side ranges undergoing catch up scans
7124+
y_axis_label: Ranges
7125+
type: GAUGE
7126+
unit: COUNT
7127+
aggregation: AVG
7128+
derivative: NONE
71217129
- name: physical_replication.commit_latency
71227130
exported_name: physical_replication_commit_latency
71237131
description: 'Event commit latency: a difference between event MVCC timestamp and the time it was flushed into disk. If we batch events, then the difference between the oldest event in the batch and flush is recorded'
@@ -7182,6 +7190,14 @@ layers:
71827190
unit: COUNT
71837191
aggregation: AVG
71847192
derivative: NONE
7193+
- name: physical_replication.scanning_ranges
7194+
exported_name: physical_replication_scanning_ranges
7195+
description: Source side ranges undergoing an initial scan
7196+
y_axis_label: Ranges
7197+
type: GAUGE
7198+
unit: COUNT
7199+
aggregation: AVG
7200+
derivative: NONE
71857201
- name: requests.slow.distsender
71867202
exported_name: requests_slow_distsender
71877203
description: |-

pkg/crosscluster/physical/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ go_library(
9494
"@com_github_cockroachdb_errors//:errors",
9595
"@com_github_cockroachdb_logtags//:logtags",
9696
"@com_github_cockroachdb_redact//:redact",
97+
"@com_github_gogo_protobuf//types",
9798
],
9899
)
99100

pkg/crosscluster/physical/metrics.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,19 @@ var (
101101
Measurement: "Events",
102102
Unit: metric.Unit_COUNT,
103103
}
104+
105+
metaScanningRanges = metric.Metadata{
106+
Name: "physical_replication.scanning_ranges",
107+
Help: "Source side ranges undergoing an initial scan",
108+
Measurement: "Ranges",
109+
Unit: metric.Unit_COUNT,
110+
}
111+
metaCatchupRanges = metric.Metadata{
112+
Name: "physical_replication.catchup_ranges",
113+
Help: "Source side ranges undergoing catch up scans",
114+
Measurement: "Ranges",
115+
Unit: metric.Unit_COUNT,
116+
}
104117
)
105118

106119
// Metrics are for production monitoring of stream ingestion jobs.
@@ -116,6 +129,8 @@ type Metrics struct {
116129
RunningCount *metric.Gauge
117130
ReplicatedTimeSeconds *metric.Gauge
118131
ReplicationCutoverProgress *metric.Gauge
132+
ScanningRanges *metric.Gauge
133+
CatchupRanges *metric.Gauge
119134
}
120135

121136
// MetricStruct implements the metric.Struct interface.
@@ -153,6 +168,8 @@ func MakeMetrics(histogramWindow time.Duration) metric.Struct {
153168
RunningCount: metric.NewGauge(metaStreamsRunning),
154169
ReplicatedTimeSeconds: metric.NewGauge(metaReplicatedTimeSeconds),
155170
ReplicationCutoverProgress: metric.NewGauge(metaReplicationCutoverProgress),
171+
ScanningRanges: metric.NewGauge(metaScanningRanges),
172+
CatchupRanges: metric.NewGauge(metaCatchupRanges),
156173
}
157174
return m
158175
}

pkg/crosscluster/physical/stream_ingestion_dist.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -859,13 +859,14 @@ func constructStreamIngestionPlanSpecs(
859859
// Create a spec for the StreamIngestionFrontier processor on the coordinator
860860
// node.
861861
streamIngestionFrontierSpec := &execinfrapb.StreamIngestionFrontierSpec{
862-
ReplicatedTimeAtStart: previousReplicatedTimestamp,
863-
TrackedSpans: []roachpb.Span{tenantSpan},
864-
JobID: int64(jobID),
865-
StreamID: uint64(streamID),
866-
ConnectionUris: topology.SerializedClusterUris(),
867-
Checkpoint: checkpoint,
868-
PartitionSpecs: repackagePartitionSpecs(streamIngestionSpecs),
862+
ReplicatedTimeAtStart: previousReplicatedTimestamp,
863+
TrackedSpans: []roachpb.Span{tenantSpan},
864+
JobID: int64(jobID),
865+
StreamID: uint64(streamID),
866+
ConnectionUris: topology.SerializedClusterUris(),
867+
Checkpoint: checkpoint,
868+
PartitionSpecs: repackagePartitionSpecs(streamIngestionSpecs),
869+
NumIngestionProcessors: int32(len(topology.Partitions)),
869870
}
870871

871872
return streamIngestionSpecs, streamIngestionFrontierSpec, nil

pkg/crosscluster/physical/stream_ingestion_frontier_processor.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
3232
"github.com/cockroachdb/errors"
3333
"github.com/cockroachdb/redact"
34+
pbtypes "github.com/gogo/protobuf/types"
3435
)
3536

3637
const (
@@ -83,6 +84,14 @@ type streamIngestionFrontier struct {
8384
// replicatedTimeAtLastPositiveLagNodeCheck records the replicated time the
8485
// last time the lagging node checker detected a lagging node.
8586
replicatedTimeAtLastPositiveLagNodeCheck hlc.Timestamp
87+
88+
rangeStats replicationutils.AggregateRangeStatsCollector
89+
90+
// This stores the last aggregate stats we computed. Because stats are only
91+
// updated on a checkpoint event, the stats will be stale until the next
92+
// checkpoint and should not be used to update job statuses. Only on a fresh
93+
// checkpoint should we update job statuses.
94+
lastAggStats streampb.StreamEvent_RangeStats
8695
}
8796

8897
var _ execinfra.Processor = &streamIngestionFrontier{}
@@ -138,6 +147,9 @@ func newStreamIngestionFrontierProcessor(
138147
return crosscluster.StreamReplicationConsumerHeartbeatFrequency.Get(&flowCtx.Cfg.Settings.SV)
139148
}),
140149
persistedReplicatedTime: spec.ReplicatedTimeAtStart,
150+
rangeStats: replicationutils.NewAggregateRangeStatsCollector(
151+
int(spec.NumIngestionProcessors),
152+
),
141153
}
142154
if err := sf.Init(
143155
ctx,
@@ -184,6 +196,10 @@ func (sf *streamIngestionFrontier) Next() (
184196
if meta.Err != nil {
185197
sf.MoveToDrainingAndLogError(nil /* err */)
186198
}
199+
if err := sf.maybeCollectRangeStats(sf.Ctx(), meta); err != nil {
200+
sf.MoveToDrainingAndLogError(err)
201+
break
202+
}
187203
return nil, meta
188204
}
189205
if row == nil {
@@ -328,6 +344,9 @@ func (sf *streamIngestionFrontier) maybeUpdateProgress() error {
328344
replicatedTime := f.Frontier()
329345
sf.lastPartitionUpdate = timeutil.Now()
330346
log.Dev.VInfof(ctx, 2, "persisting replicated time of %s", replicatedTime)
347+
348+
statusByStats := sf.aggregateAndUpdateRangeMetrics()
349+
331350
if err := registry.UpdateJobWithTxn(ctx, jobID, nil /* txn */, func(
332351
txn isql.Txn, md jobs.JobMetadata, ju *jobs.JobUpdater,
333352
) error {
@@ -342,6 +361,8 @@ func (sf *streamIngestionFrontier) maybeUpdateProgress() error {
342361
if replicatedTime.IsSet() && streamProgress.ReplicationStatus == jobspb.InitialScan {
343362
streamProgress.ReplicationStatus = jobspb.Replicating
344363
md.Progress.StatusMessage = streamProgress.ReplicationStatus.String()
364+
} else if statusByStats != "" {
365+
md.Progress.StatusMessage = statusByStats
345366
}
346367

347368
// Keep the recorded replicatedTime empty until some advancement has been made
@@ -408,6 +429,44 @@ func (sf *streamIngestionFrontier) maybeUpdateProgress() error {
408429
return nil
409430
}
410431

432+
func (sf *streamIngestionFrontier) maybeCollectRangeStats(
433+
ctx context.Context, meta *execinfrapb.ProducerMetadata,
434+
) error {
435+
if meta.BulkProcessorProgress == nil {
436+
log.Dev.VInfof(ctx, 2, "received non-progress producer meta: %v", meta)
437+
return nil
438+
}
439+
440+
var stats streampb.StreamEvent_RangeStats
441+
if err := pbtypes.UnmarshalAny(&meta.BulkProcessorProgress.ProgressDetails, &stats); err != nil {
442+
return errors.Wrap(err, "unable to unmarshal progress details")
443+
}
444+
445+
sf.rangeStats.Add(meta.BulkProcessorProgress.ProcessorID, &stats)
446+
return nil
447+
}
448+
449+
// aggregateAndUpdateRangeMetrics aggregates the range stats collected from each
450+
// of the ingestion processors and updates the corresponding metrics. If the
451+
// stats have changed since the last aggregation, it returns a status message
452+
// to update the job status with. We do this to avoid overwriting job statuses
453+
// with stale stats as the stats will be the same until the next checkpoint
454+
// event.
455+
func (sf *streamIngestionFrontier) aggregateAndUpdateRangeMetrics() string {
456+
aggRangeStats, _, statusMsg := sf.rangeStats.RollupStats()
457+
if aggRangeStats.RangeCount != 0 {
458+
sf.metrics.ScanningRanges.Update(aggRangeStats.ScanningRangeCount)
459+
sf.metrics.CatchupRanges.Update(aggRangeStats.LaggingRangeCount)
460+
}
461+
if sf.lastAggStats == aggRangeStats {
462+
// This is the same stats as last time, so we don't need to update the job
463+
// status.
464+
return ""
465+
}
466+
sf.lastAggStats = aggRangeStats
467+
return statusMsg
468+
}
469+
411470
// maybePersistFrontierEntries periodically persists the current state of the
412471
// frontier to the `system.job_info` table. This information is used to hydrate
413472
// the execution details that can be requested for the C2C ingestion job. Note,

pkg/crosscluster/physical/stream_ingestion_processor.go

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ type streamIngestionProcessor struct {
291291
// backupDataProcessors' trace recording.
292292
agg *tracing.TracingAggregator
293293
aggTimer timeutil.Timer
294+
295+
// Pipelines to report range stats down to frontier processor.
296+
rangeStatsCh chan *streampb.StreamEvent_RangeStats
294297
}
295298

296299
// PartitionEvent augments a normal event with the partition it came from.
@@ -347,6 +350,7 @@ func newStreamIngestionDataProcessor(
347350
flushCh: make(chan flushableBuffer),
348351
checkpointCh: make(chan *jobspb.ResolvedSpans),
349352
errCh: make(chan error, 1),
353+
rangeStatsCh: make(chan *streampb.StreamEvent_RangeStats),
350354
rekeyer: rekeyer,
351355
rewriteToDiffKey: spec.TenantRekey.NewID != spec.TenantRekey.OldID,
352356
logBufferEvery: log.Every(30 * time.Second),
@@ -534,6 +538,13 @@ func (sip *streamIngestionProcessor) Next() (rowenc.EncDatumRow, *execinfrapb.Pr
534538
sip.aggTimer.Reset(15 * time.Second)
535539
return nil, bulkutil.ConstructTracingAggregatorProducerMeta(sip.Ctx(),
536540
sip.FlowCtx.NodeID.SQLInstanceID(), sip.FlowCtx.ID, sip.agg)
541+
case stats := <-sip.rangeStatsCh:
542+
meta, err := replicationutils.StreamRangeStatsToProgressMeta(sip.FlowCtx, sip.ProcessorID, stats)
543+
if err != nil {
544+
sip.MoveToDrainingAndLogError(err)
545+
return nil, sip.DrainHelper()
546+
}
547+
return nil, meta
537548
case err := <-sip.errCh:
538549
sip.MoveToDrainingAndLogError(err)
539550
return nil, sip.DrainHelper()
@@ -927,7 +938,8 @@ func (sip *streamIngestionProcessor) bufferCheckpoint(event PartitionEvent) erro
927938
}
928939
}
929940

930-
resolvedSpans := event.GetCheckpoint().ResolvedSpans
941+
checkpointEvent := event.GetCheckpoint()
942+
resolvedSpans := checkpointEvent.ResolvedSpans
931943
if resolvedSpans == nil {
932944
return errors.New("checkpoint event expected to have resolved spans")
933945
}
@@ -957,6 +969,13 @@ func (sip *streamIngestionProcessor) bufferCheckpoint(event PartitionEvent) erro
957969
}
958970
}
959971
sip.metrics.ResolvedEvents.Inc(1)
972+
973+
if checkpointEvent.RangeStats != nil {
974+
select {
975+
case <-sip.stopCh:
976+
case sip.rangeStatsCh <- checkpointEvent.RangeStats:
977+
}
978+
}
960979
return nil
961980
}
962981

pkg/sql/execinfrapb/processors_bulk_io.proto

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,9 @@ message StreamIngestionFrontierSpec {
282282

283283
// PartitionSpecs contains the topology of the physical replication stream.
284284
optional StreamIngestionPartitionSpecs partition_specs = 9 [(gogoproto.nullable) = false];
285+
286+
// NumIngestionProcessors is the number of ingestion processors in the job.
287+
optional int32 num_ingestion_processors = 10 [(gogoproto.nullable) = false];
285288
}
286289

287290
enum ElidePrefix {
@@ -568,7 +571,7 @@ message CompactBackupsSpec {
568571
// if it is only provided the assigned spans, so to be safe we send the entire
569572
// set of spans.
570573
repeated roachpb.Span spans = 8 [(gogoproto.nullable) = false];
571-
// assigned_spans represents the spans assigned to this particular processor
574+
// assigned_spans represents the spans assigned to this particular processor
572575
// to compact.
573576
repeated roachpb.Span assigned_spans = 9 [(gogoproto.nullable) = false];
574577
optional string user_proto = 10 [(gogoproto.nullable) = false, (gogoproto.casttype) = "github.com/cockroachdb/cockroach/pkg/security/username.SQLUsernameProto"];

0 commit comments

Comments
 (0)