Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

【提问】Resync 后将 Indexer 本地缓存放入 Delta FIFO 队列中,触发 update 回调,入参的 old 和 new 是一样的吗? #33

Open
lianghao208 opened this issue Aug 21, 2020 · 10 comments

Comments

@lianghao208
Copy link
Member

问题描述

Resync 机制会定时将 Indexer 本地缓存重新放入 Delta FIFO 队列中,然后触发 controller 的 update 回调。update 函数的入参有 old 和 new,此时 old 和 new 都是指 indexer 缓存中的同一个元素吗?相关代码我贴在下面,也注释说明了 Resync 的回调逻辑。

相关代码

// k8s.io/client-go/tools/cache/shared_informer.go
func (s *sharedIndexInformer) HandleDeltas(obj interface{}) error {
	s.blockDeltas.Lock()
	defer s.blockDeltas.Unlock()

	// from oldest to newest
	for _, d := range obj.(Deltas) {
		// 判断事件类型,看事件是通过新增、更新、替换、删除还是 Resync 重新同步产生的
		switch d.Type {
		case Sync, Replaced, Added, Updated:
			s.cacheMutationDetector.AddObject(d.Object)
			if old, exists, err := s.indexer.Get(d.Object); err == nil && exists {
				if err := s.indexer.Update(d.Object); err != nil {
					return err
				}
				
				isSync := false
				switch {
				case d.Type == Sync:
					// 如果是通过 Resync 重新同步得到的事件则做个标记
					isSync = true
				case d.Type == Replaced:
					...
				}
				// 如果是通过 Resync 重新同步得到的事件,则触发 onUpdate 回调
				s.processor.distribute(updateNotification{oldObj: old, newObj: d.Object}, isSync)
			} else {
				if err := s.indexer.Add(d.Object); err != nil {
					return err
				}
                                // 这里触发 controller 的 update 回调,传入 new 和 old 对象,是否为同一个元素?
				s.processor.distribute(addNotification{newObj: d.Object}, false)
			}
		case Deleted:
			if err := s.indexer.Delete(d.Object); err != nil {
				return err
			}
			s.processor.distribute(deleteNotification{oldObj: d.Object}, false)
		}
	}
	return nil
}
@sundongmin
Copy link

传递给oldObj是从indexer取到的
传递给newObj是从deltaFIFOpop出来的

所以不是同一个

@lianghao208
Copy link
Member Author

传递给oldObj是从indexer取到的
传递给newObj是从deltaFIFOpop出来的

所以不是同一个

注意,是 Resync 机制触发的回调,不是通过 Watch apiserver 的 update 事件触发的回调。Resync 机制会将 Indexer 本地缓存重新同步到 Delta FIFO 队列中,如果此时队列存在该对象的 key,则忽略不入队,最后才去触发 update 的回调。

@sundongmin
Copy link

好吧, 没看到resync的前提,
又看了下代码, 看样子, 两个参数是一样的.
其他人觉得呢?

@WisWang
Copy link

WisWang commented Sep 27, 2020

resync 是将最近一次 relist 的结果都传给 deltaFIFO,oldObj 和 newObj 是否一样,主要取决于上次 resync 到这次 resync 的期间发生过 relist 没有。下面就是发生 relist 的场景:

下面引自 programing kubernetes 一书中的第三章 client-go 的 Informers and Caching

Informers also have advanced error behavior: when the long-running watch connection breaks down, they recover from it by trying another watch request, picking up the event stream without losing any events. If the outage is long, and the API server lost events because etcd purged them from its database before the new watch request was successful, the informer will relist all objects.
Next to relists, there is a configurable resync period for reconciliation between the in-memory cache and the business logic: the registered event handlers will be called for all objects each time this period has passed. Common values are in minutes (e.g., 10 or 30 minutes).

@fighterhit
Copy link

传递给oldObj是从indexer取到的
传递给newObj是从deltaFIFOpop出来的
所以不是同一个

注意,是 Resync 机制触发的回调,不是通过 Watch apiserver 的 update 事件触发的回调。Resync 机制会将 Indexer 本地缓存重新同步到 Delta FIFO 队列中,如果此时队列存在该对象的 key,则忽略不入队,最后才去触发 update 的回调。

所以如果队列里不存在该对象的 key (也就是没有 relist 过?),那么 oldObjnewObj 就是一样的吗? @WisWang

@WisWang
Copy link

WisWang commented May 25, 2021

传递给oldObj是从indexer取到的
传递给newObj是从deltaFIFOpop出来的
所以不是同一个

注意,是 Resync 机制触发的回调,不是通过 Watch apiserver 的 update 事件触发的回调。Resync 机制会将 Indexer 本地缓存重新同步到 Delta FIFO 队列中,如果此时队列存在该对象的 key,则忽略不入队,最后才去触发 update 的回调。

所以如果队列里不存在该对象的 key (也就是没有 relist 过?),那么 oldObjnewObj 就是一样的吗? @WisWang

@fighterhit 还是没明白你的问题,就这个问题,我找了好久没找到,我就给 kubernetes 提了一个 PR Add compare ResourceVersion process 这个 PR 就有个人回复了一下,但是还没结论,都快三个月了,relist 在 informer 初始化的时候就会发生一次。提完这个 PR 之后我就基本明白了 resync 的作用。

@FinaleH
Copy link

FinaleH commented Mar 23, 2022

传递给oldObj是从indexer取到的
传递给newObj是从deltaFIFOpop出来的
所以不是同一个

注意,是 Resync 机制触发的回调,不是通过 Watch apiserver 的 update 事件触发的回调。Resync 机制会将 Indexer 本地缓存重新同步到 Delta FIFO 队列中,如果此时队列存在该对象的 key,则忽略不入队,最后才去触发 update 的回调。

所以如果队列里不存在该对象的 key (也就是没有 relist 过?),那么 oldObjnewObj 就是一样的吗? @WisWang

@fighterhit 还是没明白你的问题,就这个问题,我找了好久没找到,我就给 kubernetes 提了一个 PR Add compare ResourceVersion process 这个 PR 就有个人回复了一下,但是还没结论,都快三个月了,relist 在 informer 初始化的时候就会发生一次。提完这个 PR 之后我就基本明白了 resync 的作用。

@WisWang 你好,请教一下,如果reSync的作用只是用来处理最近两次relist的不一致问题,考虑到resourceVersion的存在的话,reSync是否是一个不必要的操作?

@callmevincent
Copy link

callmevincent commented Mar 23, 2022 via email

@WisWang
Copy link

WisWang commented Mar 24, 2022

@FinaleH resourceVersion 解决不了最近两次 relist 的不一致问题吧?因为 informer 是走的边缘出发,不是水平触发,不会轮询状态的,可以看下我的这个博客 kubernetes infomer 中的 resync

resync 是将最近一次 relist 的结果都传给 deltaFIFO,oldObj 和 newObj 是否一样,主要取决于上次 resync 到这次 resync 的期间发生过 relist 没有。下面就是发生 relist 的场景:

下面引自 programing kubernetes 一书中的第三章 client-go 的 Informers and Caching

Informers also have advanced error behavior: when the long-running watch connection breaks down, they recover from it by trying another watch request, picking up the event stream without losing any events. If the outage is long, and the API server lost events because etcd purged them from its database before the new watch request was successful, the informer will relist all objects.
Next to relists, there is a configurable resync period for reconciliation between the in-memory cache and the business logic: the registered event handlers will be called for all objects each time this period has passed. Common values are in minutes (e.g., 10 or 30 minutes).

@FinaleH
Copy link

FinaleH commented Apr 2, 2022 via email

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

7 participants