Repository navigation
feat: make feature store same-project registration deterministic and … - #270
Conversation
…warn on conflicts
|
|
bruceding
left a comment
There was a problem hiding this comment.
Thanks for the fix — sorting the conf names does make the cold-start winner deterministic. However, the firstConfs bookkeeping breaks on config hot reloads (see inline comment). Since fs.Load is re-invoked by configloader.reloadConfig while the SDK's connection-pool registry is process-level and first-registration-wins, the warning can credit the opposite config of the one actually in effect. Suggested fix + test case in the inline comment.
| conf := config.FeatureStoreConfs[name] | ||
| if firstName, ok := firstConfs[conf.ProjectName]; ok { | ||
| if diffFields := diffFeatureStoreConfs(config.FeatureStoreConfs[firstName], conf); len(diffFields) > 0 { | ||
| log.Warning(fmt.Sprintf("module=FeatureStore\tname=%q\tmsg=project %s is registered by multiple FeatureStoreConfs, the config of %q is used", |
There was a problem hiding this comment.
Bug: this warning reports the wrong winning config after a hot reload.
firstConfs is a local variable rebuilt on every Load call, but the state it models — the SDK's connection-pool registry (per the comment at L65-70) — is process-level with first-registration-wins semantics, and fs.Load is invoked repeatedly on config hot reloads (configloader.reloadConfig → fs.Load). Already-loaded confs short-circuit at L94-97 via ReloadProject(), which only re-fetches the project and never re-applies WithHologresLogin / WithHologresPort / WithFeatureDBLogin — so the pool parameters registered at first client creation stay in effect for the lifetime of the process.
Concrete failure timeline:
- Cold start with only
b = {ProjectName: p, HologresUsername: u2}.bcreates the client → the SDK registers projectp's pool with u2's credentials. - Hot reload adds
a = {ProjectName: p, HologresUsername: u1}("a"sorts before"b"). In thisLoadrun, the freshfirstConfsrecordsaas the first registrant;a's client creation silently getsb's existing pool (u1 discarded). Then forb, this warning fires:
project p is registered by multiple FeatureStoreConfs, the config of "a" is used
— the exact opposite of reality: u2 (registered bybat cold start) is what's actually in effect. - Variant: if the originally-registering conf is later deleted from the config, its credentials silently keep working while the warning credits a conf that never registered anything in this process.
Because the warning concerns credential/port attribution, an operator following it would rotate/inspect the wrong account. It also contradicts the PR's stated goal ("stable and reproducible") which then only holds at cold start.
Suggested fix: track the first actual registration per project at package level (same lifetime as fsInstances, guarded by the already-held fsLoadMu), recording it only in the branch that truly creates a client (L99-148), and compare/warn against that record instead of this run's sort order:
// package-level, guarded by fsLoadMu (held for the whole Load):
// var firstRegisteredPerProject = make(map[string]string)
for _, name := range names {
conf := config.FeatureStoreConfs[name]
if firstName, ok := firstRegisteredPerProject[conf.ProjectName]; ok && firstName != name {
if diffFields := diffFeatureStoreConfs(config.FeatureStoreConfs[firstName], conf); len(diffFields) > 0 {
log.Warning(... "the config of %q is used" ... firstName)
}
}
// ... existing fsInstances short-circuit ...
// ... create client ...
fsInstancesMu.Lock()
fsInstances[name] = m
fsInstancesMu.Unlock()
if _, ok := firstRegisteredPerProject[conf.ProjectName]; !ok {
firstRegisteredPerProject[conf.ProjectName] = name // recorded only on real registration
}
}Test suggestion: load z first, then hot-reload with an added conflicting conf a that sorts earlier; assert the warning still names z as the config in use.
Minor (non-blocking, same lines): diffFields is computed but discarded — the helper's doc comment confirms field names are safe to log, so consider including them in the message (differing fields %v) to make the warning actionable, or simplify the helper to return a bool.
…lict warning Drop the sorted-key deterministic registration: Load() iterates config.FeatureStoreConfs in its natural map order again. Keep the same-project duplicate detection (diffFeatureStoreConfs) and log.Warning, reworded to state the effective config is non-deterministic across restarts and advise converging the configs, since registration order is no longer forced. Removes the now-unused sort import.
feat: FeatureStore 数据源确定性注册 + 同项目冲突告警
概述
让 FeatureStore 数据源注册结果确定化,并在多份
FeatureStoreConfs指向同一 project 且字段冲突时打警告,消除冷重启生效配置随机翻转。动机
fs.Load()原先按 Go map 随机序遍历配置;底层 SDK 连接池注册表按服务端 datasource 名 first-wins,同 project 多份配置时谁先注册谁生效,随机序导致生效配置跨重启不可复现。改动(
persist/fs/featurestore.go)sort.Strings后按字典序注册:同 project 固定由字典序最靠前的配置名生效,跨重启稳定。firstConfs[ProjectName]记录每个 project 的实际生效配置;后续同 project 配置经diffFeatureStoreConfs(反射逐字段reflect.DeepEqual)比对,有差异则log.Warning指明生效配置名。只比对/打印字段名,绝不打印值(含凭证)。说明
ReloadProject路径未改,确定性由首次Load的排序注册保证。测试
GOOS=linux go build ./persist/fs/与go vet通过。diffFeatureStoreConfs经临时程序核实:相同配置→空 diff,冲突配置→正确返回差异字段名。