Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

负载均衡策略中的一致性哈希策略存在无法感知已摘除的下游列表问题 #1221

Open
shenhuiyang opened this issue Jan 10, 2024 · 7 comments

Comments

@shenhuiyang
Copy link

Describe the bug

负载均衡的一致性哈希策略,在下游列表发生变更时,有概率性不更新该列表,导致获取到错误的列表

To Reproduce
代码路径:kitex/pkg/loadbalance/consist.go
代码片段:

func (cb *consistBalancer) updateConsistInfo(e discovery.Result) {
	newInfo := cb.newConsistInfo(e)
	infoI, loaded := cb.cachedConsistInfo.LoadOrStore(e.CacheKey, newInfo)
	if !loaded {
		return
	}
	info := infoI.(*consistInfo)
	// Warm up.
	// The reason for not modifying info directly is that there is no guarantee of concurrency security.
	info.cachedConsistResult.Range(func(key, value interface{}) bool {
		cr := buildConsistResult(cb, newInfo, key.(uint64))
		if cb.opt.ExpireDuration > 0 {
			t := value.(*consistResult).Touch.Load().(time.Time)
			if time.Now().After(t.Add(cb.opt.ExpireDuration)) {
				return true
			}
			cr.Touch.Store(t)
		}
		newInfo.cachedConsistResult.Store(key, cr)
		return true
	})
	cb.cachedConsistInfo.Store(e.CacheKey, newInfo)
}

[Steps to reproduce the behavior:

  1. 10:01:00 下游列表发生变更,触发updateConsistInfo更新cachedConsistInfo,但是key1 的Touch时间已经超过2min(cb.opt.ExpireDuration),根据上述逻辑会直接return,不会更新cachedConsistInfo中key1对应的下游列表
  2. 10:01:30 key1对应的数据到达,此时会直接去cachedConsistInfo中找key1对应的下游列表,此时会获取到未更新的脏数据
  3. 10:02:00 定期删除过期key的逻辑开始执行,发现key1 Touch时间在2min(cb.opt.ExpireDuration)内,未过期,不做删除操作,key1的脏数据会继续沿用下去

Expected behavior

每次下游列表发生变更触发updateConsistInfo时都应保障数据是最新的

Screenshots

Kitex version:

0.8.0

Environment:

GO111MODULE="on"
GOARCH="arm64"
GOBIN=""
GOCACHE="/Users/xx/Library/Caches/go-build"
GOENV="/Users/xx/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/xx/go/pkg/mod"
GONOSUMDB="*"
GOOS="darwin"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_arm64"
GOVCS=""
GOVERSION="go1.19.13"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/98/76br_wbx6ngfjx8wt0shr08m0000gp/T/go-build3560284120=/tmp/go-build -gno-record-gcc-switches -fno-common"

Additional context

@jayantxie
Copy link
Member

10:01:00 下游列表发生变更,触发updateConsistInfo更新cachedConsistInfo,但是key1 的Touch时间已经超过2min(cb.opt.ExpireDuration),根据上述逻辑会直接return,不会更新cachedConsistInfo中key1对应的下游列表

这个实际的逻辑是,如果key1过期,会在新的map里丢弃这个key,而不是不更新。

@shenhuiyang
Copy link
Author

shenhuiyang commented Jan 10, 2024 via email

@shenhuiyang
Copy link
Author

10:01:00 下游列表发生变更,触发updateConsistInfo更新cachedConsistInfo,但是key1 的Touch时间已经超过2min(cb.opt.ExpireDuration),根据上述逻辑会直接return,不会更新cachedConsistInfo中key1对应的下游列表

这个实际的逻辑是,如果key1过期,会在新的map里丢弃这个key,而不是不更新。

丢弃key是一个定期执行的,在定期删除过期key之前,如果这个key1再次被使用,对应的Touch就会更新,key1就不属于过期key了,不会丢弃,而是继续使用

@jayantxie
Copy link
Member

是的,但更新的时候如果key1是未过期的,是会被更新掉的,所以还是没有很理解具体的问题是?

@shenhuiyang
Copy link
Author

shenhuiyang commented Jan 10, 2024

是的,但更新的时候如果key1是未过期的,是会被更新掉的,所以还是没有很理解具体的问题是?

这个更新是在下游列表有更新的时候才会触发,在下游变更不是很频繁的场景,这个脏数据会存在很长时间。接上面的例子,一直到11点有变更,那么10点-11点期间key1的数据都是不对的。

  1. 10:01:00 下游列表发生变更,触发updateConsistInfo更新cachedConsistInfo,但是key1 的Touch时间已经超过2min(cb.opt.ExpireDuration),根据上述逻辑会直接return,不会更新cachedConsistInfo中key1对应的下游列表
  2. 10:01:30 key1对应的数据到达,此时会直接去cachedConsistInfo中找key1对应的下游列表,此时会获取到未更新的脏数据,并更新key1对应Touch时间
  3. 10:02:00 定期删除过期key的逻辑开始执行,发现key1 Touch时间在2min(cb.opt.ExpireDuration)内,未过期,不做删除操作,key1的脏数据会继续沿用下去
  4. 11:00:00 下游列表发生变更,触发updateConsistInfo更新cachedConsistInfo,此时才能更正key1的数据

@jayantxie
Copy link
Member

你有可以复现的代码吗?我没理解「此时会直接去cachedConsistInfo中找key1对应的下游列表,此时会获取到未更新的脏数据,并更新key1对应Touch时间」这句话是什么意思?如果key1直接return,这里会拿新的数据重新build

@jizhuozhi
Copy link
Contributor

没有理解这里面的问题是什么,如果可以的话,能提供一个模拟复现场景的最小化单元测试用例以及期望结果的断言么

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

3 participants