hotswap: compare config bytes exactly and check the marshal error - #1471
hotswap: compare config bytes exactly and check the marshal error#1471dwin-gharibi wants to merge 1 commit into
Conversation
…load Signed-off-by: Dwin Gharibi <dwin.gharibi@email.kntu.ac.ir>
| } | ||
| new, err := yaml.Marshal(config) | ||
| if len(o.backupOldCfg) == len(new) && bytes.EqualFold(o.backupOldCfg, new) { | ||
| if err != nil { |
There was a problem hiding this comment.
Good catch checking the marshal error — previously a failed yaml.Marshal returned nil bytes, which wiped backupOldCfg to nil and still fired notify().
One suggestion: this is the only error path in this file that returns silently. load logs read/unmarshal failures and listenFile logs rewatch/add failures, but here a config that unmarshals yet fails to marshal will be silently ignored on every reload (the ticker retries forever with no signal). Consider logging before returning, consistent with the surrounding code:
if err != nil {
o.Errorf(context.Background(), "Marshal file:%s fail:%v", path, err)
return false
}(Applies identically to the Cubelet/pkg/hotswap/file.go copy.)
|
AI-generated review — not a human approval. Verdict: Approve (with a minor suggestion)This is a small, correct fix applied identically to the two vendored copies of the hotswap package. I reviewed the diff against the base branch ( What the change doesIn new, err := yaml.Marshal(config)
if err != nil {
return false
}
if bytes.Equal(o.backupOldCfg, new) {
return false
}Two fixes in one:
Verification against the base tree
Minor suggestions
Testing assessmentThe four tests directly target the behavior change ( No security or concurrency concerns: |
Closes #1470.
Motivation
FileOperator.reloadusedbytes.EqualFoldto decide whether the config had changed.EqualFoldis a case-insensitive comparison, so any edit that only changed ASCII letter case —level: INFO→level: info,host: Foo.internal→host: foo.internal,driver: MySQL→driver: mysql— was reported as "no change" and never pushed to listeners.The
yaml.Marshalerror on the same line was also unchecked. On failurenewis nil, so thebaseline
backupOldCfgwas wiped to nil andnotify()still fired with the unmarshalable config.Init()at:71-74already checks the same call, so this was an inconsistency within one file.What this changes
Both copies of the package —
CubeMaster/pkg/base/hotswap/file.goandCubelet/pkg/hotswap/file.go— get the same two-line change:
bytes.EqualreplacesEqualFold, and thelen(...) == len(...)pre-check goes away becausebytes.Equalalready compares lengths first. A marshal failure now returnsfalsewithoutcorrupting the baseline or notifying listeners.
No comment changes.
Per CONTRIBUTING's "one component per commit", this should land as two commits — one for CubeMaster,
one for Cubelet — since the two packages are separate copies. The diff is identical in both.
Testing
New:
CubeMaster/pkg/base/hotswap/file_reload_test.goandCubelet/pkg/hotswap/file_reload_test.go(identical).
TestReloadDetectsCaseOnlyKeyValueChange—INFO→infois detected.TestReloadDetectsCaseOnlyHostChange—Foo.internal→foo.internalis detected.TestReloadReportsNoChangeForIdenticalConfig— an unchanged file still reports no change, so thefix does not turn every watcher tick into a spurious notify.
TestReloadDetectsOrdinaryChange— regression guard for the normal path.The tests drive
reload()directly viaNewWatcherrather thanInit(), so no fsnotify goroutine isstarted.
Red/green verified — with
file.goreverted to master:and with the fix applied:
CI gates checked locally:
gofmt -lon both packages — clean (fmt-check).GOOS=linux go buildon both packages — clean.Risk / rollout
Low. The change makes reload strictly more sensitive, so the only behavioural difference is that
case-only edits now take effect — which is the intent. Listeners already had to tolerate being
notified on any change.