History

fix(auth): preserve and restore ready view cursors during index rebuilds

Luis Pater committed 7 months ago

Commit 730809d1 parent 5e81b651 file · +83 −1Browse files

Unified · Split

sdk/cliproxy/auth/scheduler.go+83−1
@@ -97,6 +97,72 @@ type childBucket struct { · 96 unmodified lines
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 · 721 unmodified lines
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() { · 14 unmodified lines
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 {