github.com/alibaba/ilogtail/pkg@v0.0.0-20250526110833-c53b480d046c/helper/k8smeta/k8s_meta_deferred_deletion_meta_store.go (about)

     1  package k8smeta
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  
     8  	"k8s.io/client-go/tools/cache"
     9  
    10  	"github.com/alibaba/ilogtail/pkg/logger"
    11  )
    12  
    13  type IndexItem struct {
    14  	Keys map[string]struct{} // alternative to set, struct{} is zero memory
    15  }
    16  
    17  func NewIndexItem() IndexItem {
    18  	return IndexItem{
    19  		Keys: make(map[string]struct{}),
    20  	}
    21  }
    22  
    23  func (i IndexItem) Add(key string) {
    24  	i.Keys[key] = struct{}{}
    25  }
    26  
    27  func (i IndexItem) Remove(key string) {
    28  	delete(i.Keys, key)
    29  }
    30  
    31  type DeferredDeletionMetaStore struct {
    32  	keyFunc    cache.KeyFunc
    33  	indexRules []IdxFunc
    34  
    35  	eventCh chan *K8sMetaEvent
    36  	stopCh  <-chan struct{}
    37  
    38  	// cache
    39  	Items map[string]*ObjectWrapper
    40  	Index map[string]IndexItem
    41  	lock  sync.RWMutex
    42  
    43  	// timer
    44  	gracePeriod  int64
    45  	registerLock sync.RWMutex
    46  	sendFuncs    map[string]*SendFuncWithStopCh
    47  }
    48  
    49  type TimerEvent struct {
    50  	ConfigName string
    51  	Interval   int
    52  }
    53  
    54  type SendFuncWithStopCh struct {
    55  	SendFunc SendFunc
    56  	StopCh   chan struct{}
    57  }
    58  
    59  func NewDeferredDeletionMetaStore(eventCh chan *K8sMetaEvent, stopCh <-chan struct{}, gracePeriod int64, keyFunc cache.KeyFunc, indexRules ...IdxFunc) *DeferredDeletionMetaStore {
    60  	m := &DeferredDeletionMetaStore{
    61  		keyFunc:    keyFunc,
    62  		indexRules: indexRules,
    63  
    64  		eventCh: eventCh,
    65  		stopCh:  stopCh,
    66  
    67  		Items: make(map[string]*ObjectWrapper),
    68  		Index: make(map[string]IndexItem),
    69  
    70  		gracePeriod: gracePeriod,
    71  		sendFuncs:   make(map[string]*SendFuncWithStopCh),
    72  	}
    73  	return m
    74  }
    75  
    76  func (m *DeferredDeletionMetaStore) Start() {
    77  	go m.handleEvent()
    78  }
    79  
    80  func (m *DeferredDeletionMetaStore) Get(key []string) map[string][]*ObjectWrapper {
    81  	m.lock.RLock()
    82  	defer m.lock.RUnlock()
    83  	result := make(map[string][]*ObjectWrapper)
    84  	for _, k := range key {
    85  		realKeys, ok := m.Index[k]
    86  		if !ok {
    87  			continue
    88  		}
    89  		for realKey := range realKeys.Keys {
    90  			if obj, ok := m.Items[realKey]; ok {
    91  				if obj.Raw != nil {
    92  					result[k] = append(result[k], obj)
    93  				} else {
    94  					logger.Error(context.Background(), "K8S_META_HANDLE_ALARM", "raw object not found", realKey)
    95  				}
    96  			} else {
    97  				logger.Error(context.Background(), "K8S_META_HANDLE_ALARM", "key not found", realKey)
    98  			}
    99  		}
   100  	}
   101  	return result
   102  }
   103  
   104  func (m *DeferredDeletionMetaStore) List() []*ObjectWrapper {
   105  	m.lock.RLock()
   106  	defer m.lock.RUnlock()
   107  	result := make([]*ObjectWrapper, 0)
   108  	for _, item := range m.Items {
   109  		result = append(result, item)
   110  	}
   111  	return result
   112  }
   113  
   114  func (m *DeferredDeletionMetaStore) Filter(filterFunc func(*ObjectWrapper) bool, limit int) []*ObjectWrapper {
   115  	m.lock.RLock()
   116  	defer m.lock.RUnlock()
   117  	result := make([]*ObjectWrapper, 0)
   118  	for _, item := range m.Items {
   119  		if filterFunc != nil {
   120  			if filterFunc(item) {
   121  				result = append(result, item)
   122  			}
   123  		} else {
   124  			result = append(result, item)
   125  		}
   126  		if limit > 0 && len(result) >= limit {
   127  			break
   128  		}
   129  	}
   130  	return result
   131  }
   132  
   133  func (m *DeferredDeletionMetaStore) RegisterSendFunc(key string, f SendFunc, interval int) {
   134  	sendFuncWithStopCh := &SendFuncWithStopCh{
   135  		SendFunc: f,
   136  		StopCh:   make(chan struct{}),
   137  	}
   138  	m.registerLock.Lock()
   139  	m.sendFuncs[key] = sendFuncWithStopCh
   140  	m.registerLock.Unlock()
   141  	go func() {
   142  		defer panicRecover()
   143  		event := &K8sMetaEvent{
   144  			EventType: EventTypeTimer,
   145  			Object: &ObjectWrapper{
   146  				Raw: &TimerEvent{
   147  					ConfigName: key,
   148  					Interval:   interval,
   149  				},
   150  			},
   151  		}
   152  		manager := GetMetaManagerInstance()
   153  		for {
   154  			if manager.IsReady() {
   155  				break
   156  			}
   157  			time.Sleep(1 * time.Second)
   158  		}
   159  
   160  		m.eventCh <- event
   161  		ticker := time.NewTicker(time.Duration(interval) * time.Second)
   162  		for {
   163  			select {
   164  			case <-ticker.C:
   165  				m.eventCh <- event
   166  			case <-sendFuncWithStopCh.StopCh:
   167  				return
   168  			}
   169  		}
   170  	}()
   171  }
   172  
   173  func (m *DeferredDeletionMetaStore) UnRegisterSendFunc(key string) {
   174  	m.registerLock.Lock()
   175  	if stopCh, ok := m.sendFuncs[key]; ok {
   176  		close(stopCh.StopCh)
   177  	}
   178  	delete(m.sendFuncs, key)
   179  	m.registerLock.Unlock()
   180  }
   181  
   182  // realtime events (add, update, delete) and timer events are handled sequentially
   183  func (m *DeferredDeletionMetaStore) handleEvent() {
   184  	defer panicRecover()
   185  	for {
   186  		select {
   187  		case event := <-m.eventCh:
   188  			switch event.EventType {
   189  			case EventTypeAdd, EventTypeUpdate:
   190  				m.handleAddOrUpdateEvent(event)
   191  			case EventTypeDelete:
   192  				m.handleDeleteEvent(event)
   193  			case EventTypeDeferredDelete:
   194  				m.handleDeferredDeleteEvent(event)
   195  			case EventTypeTimer:
   196  				m.handleTimerEvent(event)
   197  			default:
   198  				logger.Error(context.Background(), "unknown event type", event.EventType)
   199  			}
   200  		case <-m.stopCh:
   201  			m.registerLock.Lock()
   202  			for _, f := range m.sendFuncs {
   203  				close(f.StopCh)
   204  			}
   205  			m.registerLock.Unlock()
   206  			return
   207  		}
   208  	}
   209  }
   210  
   211  func (m *DeferredDeletionMetaStore) handleAddOrUpdateEvent(event *K8sMetaEvent) {
   212  	key, err := m.keyFunc(event.Object.Raw)
   213  	if err != nil {
   214  		logger.Error(context.Background(), "K8S_META_HANDLE_ALARM", "handle k8s meta with keyFunc error", err)
   215  		return
   216  	}
   217  	idxKeys := m.getIdxKeys(event.Object)
   218  	m.lock.Lock()
   219  	// should delete oldIdxKeys in two cases:
   220  	// 1. update event
   221  	// 2. add event when the previous object is between deleted and deferred delete
   222  	if obj, ok := m.Items[key]; ok {
   223  		var oldIdxKeys []string
   224  		event.Object.FirstObservedTime = obj.FirstObservedTime
   225  		oldIdxKeys = m.getIdxKeys(obj)
   226  		for _, idxKey := range oldIdxKeys {
   227  			m.Index[idxKey].Remove(key)
   228  		}
   229  	}
   230  
   231  	m.Items[key] = event.Object
   232  	for _, idxKey := range idxKeys {
   233  		if _, ok := m.Index[idxKey]; !ok {
   234  			m.Index[idxKey] = NewIndexItem()
   235  		}
   236  		m.Index[idxKey].Add(key)
   237  	}
   238  	m.lock.Unlock()
   239  	m.registerLock.RLock()
   240  	for _, f := range m.sendFuncs {
   241  		f.SendFunc([]*K8sMetaEvent{event})
   242  	}
   243  	m.registerLock.RUnlock()
   244  }
   245  
   246  func (m *DeferredDeletionMetaStore) handleDeleteEvent(event *K8sMetaEvent) {
   247  	key, err := m.keyFunc(event.Object.Raw)
   248  	if err != nil {
   249  		logger.Error(context.Background(), "K8S_META_HANDLE_ALARM", "handle k8s meta with keyFunc error", err)
   250  		return
   251  	}
   252  	m.lock.Lock()
   253  	if obj, ok := m.Items[key]; ok {
   254  		obj.Deleted = true
   255  		event.Object.FirstObservedTime = obj.FirstObservedTime
   256  	}
   257  	m.lock.Unlock()
   258  	m.registerLock.RLock()
   259  	for _, f := range m.sendFuncs {
   260  		f.SendFunc([]*K8sMetaEvent{event})
   261  	}
   262  	m.registerLock.RUnlock()
   263  	go func() {
   264  		// wait and add a deferred delete event
   265  		time.Sleep(time.Duration(m.gracePeriod) * time.Second)
   266  		m.eventCh <- &K8sMetaEvent{
   267  			EventType: EventTypeDeferredDelete,
   268  			Object:    event.Object,
   269  		}
   270  	}()
   271  }
   272  
   273  func (m *DeferredDeletionMetaStore) handleDeferredDeleteEvent(event *K8sMetaEvent) {
   274  	key, err := m.keyFunc(event.Object.Raw)
   275  	if err != nil {
   276  		logger.Error(context.Background(), "handleDeferredDeleteEvent keyFunc error", err)
   277  		return
   278  	}
   279  	idxKeys := m.getIdxKeys(event.Object)
   280  	m.lock.Lock()
   281  	defer m.lock.Unlock()
   282  	if obj, ok := m.Items[key]; ok {
   283  		if obj.Deleted {
   284  			delete(m.Items, key)
   285  			for _, idxKey := range idxKeys {
   286  				if _, ok := m.Index[idxKey]; !ok {
   287  					continue
   288  				}
   289  				m.Index[idxKey].Remove(key)
   290  				if len(m.Index[idxKey].Keys) == 0 {
   291  					delete(m.Index, idxKey)
   292  				}
   293  			}
   294  		}
   295  		// if deleted is false, there is a new add event between delete event and deferred delete event
   296  	}
   297  }
   298  
   299  func (m *DeferredDeletionMetaStore) handleTimerEvent(event *K8sMetaEvent) {
   300  	timerEvent := event.Object.Raw.(*TimerEvent)
   301  	m.registerLock.RLock()
   302  	defer m.registerLock.RUnlock()
   303  	if f, ok := m.sendFuncs[timerEvent.ConfigName]; ok {
   304  		allItems := make([]*K8sMetaEvent, 0)
   305  		m.lock.RLock()
   306  		for _, obj := range m.Items {
   307  			if !obj.Deleted {
   308  				obj.LastObservedTime = time.Now().Unix()
   309  				allItems = append(allItems, &K8sMetaEvent{
   310  					EventType: EventTypeUpdate,
   311  					Object:    obj,
   312  				})
   313  			}
   314  		}
   315  		m.lock.RUnlock()
   316  		f.SendFunc(allItems)
   317  	}
   318  }
   319  
   320  func (m *DeferredDeletionMetaStore) getIdxKeys(obj *ObjectWrapper) []string {
   321  	result := make([]string, 0)
   322  	for _, rule := range m.indexRules {
   323  		idxKeys, err := rule(obj.Raw)
   324  		if err != nil {
   325  			logger.Error(context.Background(), "K8S_META_HANDLE_ALARM", "handle k8s meta with idx rules error", err)
   326  			return nil
   327  		}
   328  		result = append(result, idxKeys...)
   329  	}
   330  	return result
   331  }