History

Refine watcher delete suppression and maintenance scans

benjamin committed 7 months agov0.1.0

Commit 768ffe21 parent e509adc3 files · +60 −6Browse files

Unified · Split

3 files
  1. internal/watcher/events.go +1 −1
  2. internal/watcher/watcher_test.go +7 −0
  3. sdk/cliproxy/service.go +52 −5
internal/watcher/events.go+1−1
@@ -90,11 +90,11 @@ func (w *Watcher) handleEvent(event fsnotify.Event) { · 89 unmodified lines
90 90
91 // Handle auth directory changes incrementally (.json only)91 // Handle auth directory changes incrementally (.json only)
92 if event.Op&(fsnotify.Remove|fsnotify.Rename) != 0 {92 if event.Op&(fsnotify.Remove|fsnotify.Rename) != 0 {
93 w.cancelPendingAuthWrite(normalizedName)
93 if w.isAuthPathSuppressed(normalizedName, now) {94 if w.isAuthPathSuppressed(normalizedName, now) {
94 log.Debugf("suppressing auth event for %s", filepath.Base(event.Name))95 log.Debugf("suppressing auth event for %s", filepath.Base(event.Name))
95 return96 return
96 }97 }
97 w.cancelPendingAuthWrite(normalizedName)
98 if w.shouldDebounceRemove(normalizedName, now) {98 if w.shouldDebounceRemove(normalizedName, now) {
99 log.Debugf("debouncing remove event for %s", filepath.Base(event.Name))99 log.Debugf("debouncing remove event for %s", filepath.Base(event.Name))
100 return100 return
internal/watcher/watcher_test.go+7−0
@@ -1258,11 +1258,18 @@ func TestHandleEventSuppressedAuthPathSkipsKnownRemove(t *testing.T) { · 1257 unmodified lines
1258 w.SetConfig(&config.Config{AuthDir: authDir})1258 w.SetConfig(&config.Config{AuthDir: authDir})
1259 w.lastAuthHashes[w.normalizeAuthPath(authFile)] = "hash"1259 w.lastAuthHashes[w.normalizeAuthPath(authFile)] = "hash"
1260 w.SuppressAuthPath(authFile, time.Second)1260 w.SuppressAuthPath(authFile, time.Second)
1261 w.scheduleAuthWrite(w.normalizeAuthPath(authFile), authFile)
1261 1262
1262 w.handleEvent(fsnotify.Event{Name: authFile, Op: fsnotify.Remove})1263 w.handleEvent(fsnotify.Event{Name: authFile, Op: fsnotify.Remove})
1263 if _, ok := w.lastAuthHashes[w.normalizeAuthPath(authFile)]; !ok {1264 if _, ok := w.lastAuthHashes[w.normalizeAuthPath(authFile)]; !ok {
1264 t.Fatal("expected suppressed remove to leave watcher auth cache untouched")1265 t.Fatal("expected suppressed remove to leave watcher auth cache untouched")
1265 }1266 }
1267 w.eventMu.Lock()
1268 pending := len(w.pendingAuthWrites)
1269 w.eventMu.Unlock()
1270 if pending != 0 {
1271 t.Fatalf("expected suppressed remove to cancel pending writes, got %d", pending)
1272 }
1266}1273}
1267 1274
1268func TestFlushPendingAuthWrite_IgnoresStaleGeneration(t *testing.T) {1275func TestFlushPendingAuthWrite_IgnoresStaleGeneration(t *testing.T) {
sdk/cliproxy/service.go+52−5
@@ -798,11 +798,27 @@ func (s *Service) rebuildAuthMaintenanceIndex(snapshot []*coreauth.Auth, authDir · 797 unmodified lines
798}798}
799 799
800func (s *Service) rebuildAuthMaintenanceIndexLocked(snapshot []*coreauth.Auth, authDir string) {800func (s *Service) rebuildAuthMaintenanceIndexLocked(snapshot []*coreauth.Auth, authDir string) {
801 s.maintenanceAuthIDsByPath = make(map[string]map[string]struct{})801 idsByPath := make(map[string]map[string]struct{})
802 s.maintenanceAuthPathByID = make(map[string]string)802 pathByID := make(map[string]string)
803 for _, auth := range snapshot {803 for _, auth := range snapshot {
804 s.indexAuthMaintenanceAuthLocked(auth, authDir)804 if auth == nil {
805 continue
806 }
807 id := strings.TrimSpace(auth.ID)
808 if id == "" {
809 continue
810 }
811 path := resolveAuthFilePath(auth, authDir)
812 if path == "" {
813 continue
814 }
815 if idsByPath[path] == nil {
816 idsByPath[path] = make(map[string]struct{})
817 }
818 idsByPath[path][id] = struct{}{}
819 pathByID[id] = path
805 }820 }
821 s.replaceAuthMaintenanceIndexLocked(idsByPath, pathByID)
806}822}
807 823
808func (s *Service) indexAuthMaintenanceAuth(auth *coreauth.Auth) {824func (s *Service) indexAuthMaintenanceAuth(auth *coreauth.Auth) {
@@ -910,6 +926,27 @@ func (s *Service) authMaintenanceIDsForPath(path, authDir string) []string { · 101 unmodified lines
910 return out926 return out
911}927}
912 928
929func (s *Service) replaceAuthMaintenanceIndex(idsByPath map[string]map[string]struct{}, pathByID map[string]string) {
930 if s == nil {
931 return
932 }
933 s.ensureAuthMaintenanceQueue()
934 s.maintenanceMu.Lock()
935 defer s.maintenanceMu.Unlock()
936 s.replaceAuthMaintenanceIndexLocked(idsByPath, pathByID)
937}
938
939func (s *Service) replaceAuthMaintenanceIndexLocked(idsByPath map[string]map[string]struct{}, pathByID map[string]string) {
940 if idsByPath == nil {
941 idsByPath = make(map[string]map[string]struct{})
942 }
943 if pathByID == nil {
944 pathByID = make(map[string]string)
945 }
946 s.maintenanceAuthIDsByPath = idsByPath
947 s.maintenanceAuthPathByID = pathByID
948}
949
913func (s *Service) snapshotAuthMaintenanceConfig() (config.AuthMaintenanceConfig, string) {950func (s *Service) snapshotAuthMaintenanceConfig() (config.AuthMaintenanceConfig, string) {
914 if s == nil {951 if s == nil {
915 return config.AuthMaintenanceConfig{952 return config.AuthMaintenanceConfig{
@@ -946,8 +983,9 @@ func (s *Service) scanAuthMaintenanceCandidates(now time.Time, cfg config.AuthMa · 30 unmodified lines
946 return nil983 return nil
947 }984 }
948 snapshot := s.coreManager.List()985 snapshot := s.coreManager.List()
949 s.rebuildAuthMaintenanceIndex(snapshot, authDir)
950 grouped := make(map[string]authMaintenanceCandidate)986 grouped := make(map[string]authMaintenanceCandidate)
987 idsByPath := make(map[string]map[string]struct{})
988 pathByID := make(map[string]string)
951 for _, auth := range snapshot {989 for _, auth := range snapshot {
952 path := resolveAuthFilePath(auth, authDir)990 path := resolveAuthFilePath(auth, authDir)
953 if path == "" {991 if path == "" {
@@ -961,7 +999,15 @@ func (s *Service) scanAuthMaintenanceCandidates(now time.Time, cfg config.AuthMa · 7 unmodified lines
961 }999 }
962 }1000 }
963 if auth != nil && strings.TrimSpace(auth.ID) != "" {1001 if auth != nil && strings.TrimSpace(auth.ID) != "" {
964 group.IDs = append(group.IDs, strings.TrimSpace(auth.ID))1002 id := strings.TrimSpace(auth.ID)
1003 if idsByPath[path] == nil {
1004 idsByPath[path] = make(map[string]struct{})
1005 }
1006 if _, exists := idsByPath[path][id]; !exists {
1007 idsByPath[path][id] = struct{}{}
1008 group.IDs = append(group.IDs, id)
1009 }
1010 pathByID[id] = path
965 }1011 }
966 if group.Reason == "" {1012 if group.Reason == "" {
967 if reason, ok := authEligibleForMaintenanceDelete(auth, nil, cfg, now); ok {1013 if reason, ok := authEligibleForMaintenanceDelete(auth, nil, cfg, now); ok {
@@ -970,6 +1016,7 @@ func (s *Service) scanAuthMaintenanceCandidates(now time.Time, cfg config.AuthMa · 2 unmodified lines
970 }1016 }
971 grouped[path] = group1017 grouped[path] = group
972 }1018 }
1019 s.replaceAuthMaintenanceIndex(idsByPath, pathByID)
973 1020
974 candidates := make([]authMaintenanceCandidate, 0, len(grouped))1021 candidates := make([]authMaintenanceCandidate, 0, len(grouped))
975 for _, candidate := range grouped {1022 for _, candidate := range grouped {