@@ -16,11 +16,22 @@ import (
16
16
"github.com/cockroachdb/cockroach/pkg/settings"
17
17
"github.com/cockroachdb/cockroach/pkg/sql/catalog/descpb"
18
18
"github.com/cockroachdb/cockroach/pkg/util/hlc"
19
+ "github.com/cockroachdb/cockroach/pkg/util/metamorphic"
19
20
"github.com/cockroachdb/cockroach/pkg/util/span"
20
21
"github.com/cockroachdb/errors"
21
22
"github.com/cockroachdb/redact"
22
23
)
23
24
25
+ // FrontierPerTableTracking controls whether the in-memory frontiers changefeeds
26
+ // use for tracking span progress should do per-table tracking (via partitioning
27
+ // into one sub-frontier per table).
28
+ var FrontierPerTableTracking = settings .RegisterBoolSetting (
29
+ settings .ApplicationLevel ,
30
+ "changefeed.frontier.per_table_tracking.enabled" ,
31
+ "track progress on a per-table basis in-memory" ,
32
+ metamorphic .ConstantWithTestBool ("changefeed.frontier.per_table_tracking.enabled" , true ),
33
+ )
34
+
24
35
// AggregatorFrontier wraps a resolvedSpanFrontier with additional
25
36
// checks specific to how change aggregators process boundaries.
26
37
type AggregatorFrontier struct {
@@ -32,9 +43,10 @@ func NewAggregatorFrontier(
32
43
statementTime hlc.Timestamp ,
33
44
initialHighWater hlc.Timestamp ,
34
45
decoder TablePrefixDecoder ,
46
+ sv * settings.Values ,
35
47
spans ... roachpb.Span ,
36
48
) (* AggregatorFrontier , error ) {
37
- rsf , err := newResolvedSpanFrontier (statementTime , initialHighWater , decoder , spans ... )
49
+ rsf , err := newResolvedSpanFrontier (statementTime , initialHighWater , decoder , sv , spans ... )
38
50
if err != nil {
39
51
return nil , err
40
52
}
@@ -80,9 +92,10 @@ func NewCoordinatorFrontier(
80
92
statementTime hlc.Timestamp ,
81
93
initialHighWater hlc.Timestamp ,
82
94
decoder TablePrefixDecoder ,
95
+ sv * settings.Values ,
83
96
spans ... roachpb.Span ,
84
97
) (* CoordinatorFrontier , error ) {
85
- rsf , err := newResolvedSpanFrontier (statementTime , initialHighWater , decoder , spans ... )
98
+ rsf , err := newResolvedSpanFrontier (statementTime , initialHighWater , decoder , sv , spans ... )
86
99
if err != nil {
87
100
return nil , err
88
101
}
@@ -195,7 +208,7 @@ func (f *CoordinatorFrontier) MakeCheckpoint(
195
208
// used to track resolved spans for a changefeed and methods for computing
196
209
// lagging and checkpoint spans.
197
210
type resolvedSpanFrontier struct {
198
- * span. MultiFrontier [descpb. ID ]
211
+ maybeTablePartitionedFrontier
199
212
200
213
// statementTime is the statement time of the changefeed.
201
214
statementTime hlc.Timestamp
@@ -217,16 +230,26 @@ func newResolvedSpanFrontier(
217
230
statementTime hlc.Timestamp ,
218
231
initialHighWater hlc.Timestamp ,
219
232
decoder TablePrefixDecoder ,
233
+ sv * settings.Values ,
220
234
spans ... roachpb.Span ,
221
235
) (* resolvedSpanFrontier , error ) {
222
- sf , err := span .NewMultiFrontierAt (newTableIDPartitioner (decoder ), initialHighWater , spans ... )
236
+ sf , err := func () (maybeTablePartitionedFrontier , error ) {
237
+ if FrontierPerTableTracking .Get (sv ) {
238
+ return span .NewMultiFrontierAt (newTableIDPartitioner (decoder ), initialHighWater , spans ... )
239
+ }
240
+ f , err := span .MakeFrontierAt (initialHighWater , spans ... )
241
+ if err != nil {
242
+ return nil , err
243
+ }
244
+ return notTablePartitionedFrontier {spanFrontier : span .MakeConcurrentFrontier (f )}, nil
245
+ }()
223
246
if err != nil {
224
247
return nil , err
225
248
}
226
249
return & resolvedSpanFrontier {
227
- MultiFrontier : sf ,
228
- statementTime : statementTime ,
229
- initialHighWater : initialHighWater ,
250
+ maybeTablePartitionedFrontier : sf ,
251
+ statementTime : statementTime ,
252
+ initialHighWater : initialHighWater ,
230
253
}, nil
231
254
}
232
255
@@ -417,6 +440,39 @@ func (b *resolvedSpanBoundary) SafeFormat(s redact.SafePrinter, _ rune) {
417
440
s .Printf ("%v boundary (%v)" , b .typ , b .ts )
418
441
}
419
442
443
+ // A maybeTablePartitionedFrontier is a frontier that might be tracking
444
+ // spans in per-table sub-frontiers.
445
+ type maybeTablePartitionedFrontier interface {
446
+ span.Frontier
447
+
448
+ // Frontiers returns an iterator over the table ID and sub-frontiers
449
+ // being tracked by the frontier. If the frontier is not tracking
450
+ // on a per-table basis, the iterator will return a single frontier
451
+ // with an ID of 0.
452
+ Frontiers () iter.Seq2 [descpb.ID , span.Frontier ]
453
+ }
454
+
455
+ var _ maybeTablePartitionedFrontier = (* span.MultiFrontier [descpb.ID ])(nil )
456
+
457
+ // spanFrontier is a type alias to make it possible to embed and forward calls
458
+ // (e.g. Frontier()) to the underlying span.Frontier.
459
+ type spanFrontier = span.Frontier
460
+
461
+ // notTablePartitionedFrontier is a frontier that does not track spans on
462
+ // a per-table basis.
463
+ type notTablePartitionedFrontier struct {
464
+ spanFrontier
465
+ }
466
+
467
+ var _ maybeTablePartitionedFrontier = notTablePartitionedFrontier {}
468
+
469
+ // Frontiers implements maybeTablePartitionedFrontier.
470
+ func (f notTablePartitionedFrontier ) Frontiers () iter.Seq2 [descpb.ID , span.Frontier ] {
471
+ return func (yield func (descpb.ID , span.Frontier ) bool ) {
472
+ yield (0 , f .spanFrontier )
473
+ }
474
+ }
475
+
420
476
// A TablePrefixDecoder decodes table prefixes from keys.
421
477
// The production implementation is keys.SQLCodec.
422
478
type TablePrefixDecoder interface {
0 commit comments