Skip to content

Commit 6514d29

Browse files
committed
span: make MultiFrontier.Frontiers return read-only frontiers
Previously, `MultiFrontier`'s `Frontiers` method returned an iterator over the actual backing frontiers. This was unsafe because it meant callers could modify the underlying frontiers and cause the internal heap invariant to be violated. This change restricts the frontiers returned by the `Frontiers` iterator to be read-only. Release note: None
1 parent 8a15c13 commit 6514d29

File tree

3 files changed

+22
-9
lines changed

3 files changed

+22
-9
lines changed

pkg/ccl/changefeedccl/resolvedspan/frontier.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -437,8 +437,8 @@ type maybeTablePartitionedFrontier interface {
437437
// Frontiers returns an iterator over the table ID and sub-frontiers
438438
// being tracked by the frontier. If the frontier is not tracking
439439
// on a per-table basis, the iterator will return a single frontier
440-
// with an ID of 0.
441-
Frontiers() iter.Seq2[descpb.ID, span.Frontier]
440+
// with descpb.InvalidID.
441+
Frontiers() iter.Seq2[descpb.ID, span.ReadOnlyFrontier]
442442
}
443443

444444
var _ maybeTablePartitionedFrontier = (*span.MultiFrontier[descpb.ID])(nil)
@@ -456,9 +456,9 @@ type notTablePartitionedFrontier struct {
456456
var _ maybeTablePartitionedFrontier = notTablePartitionedFrontier{}
457457

458458
// Frontiers implements maybeTablePartitionedFrontier.
459-
func (f notTablePartitionedFrontier) Frontiers() iter.Seq2[descpb.ID, span.Frontier] {
460-
return func(yield func(descpb.ID, span.Frontier) bool) {
461-
yield(0, f.spanFrontier)
459+
func (f notTablePartitionedFrontier) Frontiers() iter.Seq2[descpb.ID, span.ReadOnlyFrontier] {
460+
return func(yield func(descpb.ID, span.ReadOnlyFrontier) bool) {
461+
yield(descpb.InvalidID, f.spanFrontier)
462462
}
463463
}
464464

pkg/ccl/changefeedccl/resolvedspan/frontier_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ type frontier interface {
176176
InBackfill(jobspb.ResolvedSpan) bool
177177
AtBoundary() (bool, jobspb.ResolvedSpan_BoundaryType, hlc.Timestamp)
178178
All() iter.Seq[jobspb.ResolvedSpan]
179-
Frontiers() iter.Seq2[descpb.ID, span.Frontier]
179+
Frontiers() iter.Seq2[descpb.ID, span.ReadOnlyFrontier]
180180
}
181181

182182
func testBackfillSpan(

pkg/util/span/multi_frontier.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,22 @@ func (f *MultiFrontier[P]) String() string {
241241
return buf.String()
242242
}
243243

244-
// Frontiers returns an iterator over the sub-frontiers.
245-
func (f *MultiFrontier[P]) Frontiers() iter.Seq2[P, Frontier] {
246-
return func(yield func(P, Frontier) bool) {
244+
// ReadOnlyFrontier is a subset of Frontier with only the methods
245+
// that are read-only.
246+
type ReadOnlyFrontier interface {
247+
Frontier() hlc.Timestamp
248+
PeekFrontierSpan() roachpb.Span
249+
Entries() iter.Seq2[roachpb.Span, hlc.Timestamp]
250+
SpanEntries(span roachpb.Span) iter.Seq2[roachpb.Span, hlc.Timestamp]
251+
Len() int
252+
String() string
253+
}
254+
255+
var _ ReadOnlyFrontier = Frontier(nil)
256+
257+
// Frontiers returns an iterator over the sub-frontiers (with read-only access).
258+
func (f *MultiFrontier[P]) Frontiers() iter.Seq2[P, ReadOnlyFrontier] {
259+
return func(yield func(P, ReadOnlyFrontier) bool) {
247260
f.mu.Lock()
248261
defer f.mu.Unlock()
249262

0 commit comments

Comments
 (0)