Skip to content

Commit 730809d

Browse files
committed
fix(auth): preserve and restore ready view cursors during index rebuilds
1 parent 5e81b65 commit 730809d

1 file changed

Lines changed: 83 additions & 1 deletion

File tree

sdk/cliproxy/auth/scheduler.go

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,72 @@ type childBucket struct {
9797
// cooldownQueue is the blocked auth collection ordered by next retry time during rebuilds.
9898
type cooldownQueue []*scheduledAuth
9999

100+
type readyViewCursorState struct {
101+
cursor int
102+
parentCursor int
103+
childCursors map[string]int
104+
}
105+
106+
type readyBucketCursorState struct {
107+
all readyViewCursorState
108+
ws readyViewCursorState
109+
}
110+
111+
func snapshotReadyViewCursors(view readyView) readyViewCursorState {
112+
state := readyViewCursorState{
113+
cursor: view.cursor,
114+
parentCursor: view.parentCursor,
115+
}
116+
if len(view.children) == 0 {
117+
return state
118+
}
119+
state.childCursors = make(map[string]int, len(view.children))
120+
for parent, child := range view.children {
121+
if child == nil {
122+
continue
123+
}
124+
state.childCursors[parent] = child.cursor
125+
}
126+
return state
127+
}
128+
129+
func restoreReadyViewCursors(view *readyView, state readyViewCursorState) {
130+
if view == nil {
131+
return
132+
}
133+
if len(view.flat) > 0 {
134+
view.cursor = normalizeCursor(state.cursor, len(view.flat))
135+
}
136+
if len(view.parentOrder) == 0 || len(view.children) == 0 {
137+
return
138+
}
139+
view.parentCursor = normalizeCursor(state.parentCursor, len(view.parentOrder))
140+
if len(state.childCursors) == 0 {
141+
return
142+
}
143+
for parent, child := range view.children {
144+
if child == nil || len(child.items) == 0 {
145+
continue
146+
}
147+
cursor, ok := state.childCursors[parent]
148+
if !ok {
149+
continue
150+
}
151+
child.cursor = normalizeCursor(cursor, len(child.items))
152+
}
153+
}
154+
155+
func normalizeCursor(cursor, size int) int {
156+
if size <= 0 || cursor <= 0 {
157+
return 0
158+
}
159+
cursor = cursor % size
160+
if cursor < 0 {
161+
cursor += size
162+
}
163+
return cursor
164+
}
165+
100166
// newAuthScheduler constructs an empty scheduler configured for the supplied selector strategy.
101167
func newAuthScheduler(selector Selector) *authScheduler {
102168
return &authScheduler{
@@ -824,6 +890,17 @@ func (m *modelScheduler) availabilitySummaryLocked(predicate func(*scheduledAuth
824890

825891
// rebuildIndexesLocked reconstructs ready and blocked views from the current entry map.
826892
func (m *modelScheduler) rebuildIndexesLocked() {
893+
cursorStates := make(map[int]readyBucketCursorState, len(m.readyByPriority))
894+
for priority, bucket := range m.readyByPriority {
895+
if bucket == nil {
896+
continue
897+
}
898+
cursorStates[priority] = readyBucketCursorState{
899+
all: snapshotReadyViewCursors(bucket.all),
900+
ws: snapshotReadyViewCursors(bucket.ws),
901+
}
902+
}
903+
827904
m.readyByPriority = make(map[int]*readyBucket)
828905
m.priorityOrder = m.priorityOrder[:0]
829906
m.blocked = m.blocked[:0]
@@ -844,7 +921,12 @@ func (m *modelScheduler) rebuildIndexesLocked() {
844921
sort.Slice(entries, func(i, j int) bool {
845922
return entries[i].auth.ID < entries[j].auth.ID
846923
})
847-
m.readyByPriority[priority] = buildReadyBucket(entries)
924+
bucket := buildReadyBucket(entries)
925+
if cursorState, ok := cursorStates[priority]; ok && bucket != nil {
926+
restoreReadyViewCursors(&bucket.all, cursorState.all)
927+
restoreReadyViewCursors(&bucket.ws, cursorState.ws)
928+
}
929+
m.readyByPriority[priority] = bucket
848930
m.priorityOrder = append(m.priorityOrder, priority)
849931
}
850932
sort.Slice(m.priorityOrder, func(i, j int) bool {

0 commit comments

Comments
 (0)