Unified · Split
| @@ -97,6 +97,72 @@ type childBucket struct { · 96 unmodified lines | ||
| 97 | 97 | // cooldownQueue is the blocked auth collection ordered by next retry time during rebuilds. |
| 98 | 98 | type cooldownQueue []*scheduledAuth |
| 99 | 99 | |
| 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 | + | |
| 100 | 166 | // newAuthScheduler constructs an empty scheduler configured for the supplied selector strategy. |
| 101 | 167 | func newAuthScheduler(selector Selector) *authScheduler { |
| 102 | 168 | return &authScheduler{ |
| @@ -824,6 +890,17 @@ func (m *modelScheduler) availabilitySummaryLocked(predicate func(*scheduledAuth · 721 unmodified lines | ||
| 824 | 890 | |
| 825 | 891 | // rebuildIndexesLocked reconstructs ready and blocked views from the current entry map. |
| 826 | 892 | 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 | + | |
| 827 | 904 | m.readyByPriority = make(map[int]*readyBucket) |
| 828 | 905 | m.priorityOrder = m.priorityOrder[:0] |
| 829 | 906 | m.blocked = m.blocked[:0] |
| @@ -844,7 +921,12 @@ func (m *modelScheduler) rebuildIndexesLocked() { · 14 unmodified lines | ||
| 844 | 921 | sort.Slice(entries, func(i, j int) bool { |
| 845 | 922 | return entries[i].auth.ID < entries[j].auth.ID |
| 846 | 923 | }) |
| 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 | |
| 848 | 930 | m.priorityOrder = append(m.priorityOrder, priority) |
| 849 | 931 | } |
| 850 | 932 | sort.Slice(m.priorityOrder, func(i, j int) bool { |