mirror of
https://github.com/pgsty/minio.git
synced 2026-09-05 18:16:16 +03:00
fix: never record a resident bucket as a CORS load failure
A bucket that is resident keeps its last loaded metadata through a failed refresh, exactly like every other bucket configuration, so the load-failure set only ever holds buckets that were never loaded. This makes the CORS lookup's precedence explicit: resident document first, fail-closed only for a bucket that has no loaded document. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PvgysXDmhPBBimCReYtA8q Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
@@ -711,3 +711,33 @@ func testBucketCorsLoadFailedBucketFailsClosed(obj ObjectLayer, _ string, _ stri
|
||||
t.Fatalf("load-failed CORS lookup performed %d synchronous bucket metadata reads", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBucketCorsResidentConfigSurvivesRefreshFailure: a resident bucket keeps
|
||||
// its last loaded CORS configuration through a failed refresh, like every
|
||||
// other bucket configuration, and the failure set never records a resident
|
||||
// bucket. Only a bucket that was never loaded fails closed.
|
||||
func TestBucketCorsResidentConfigSurvivesRefreshFailure(t *testing.T) {
|
||||
ExecObjectLayerAPITest(ExecObjectLayerAPITestArgs{
|
||||
t: t,
|
||||
objAPITest: testBucketCorsResidentConfigSurvivesRefreshFailure,
|
||||
endpoints: []string{"GetBucketCors"},
|
||||
})
|
||||
}
|
||||
|
||||
func testBucketCorsResidentConfigSurvivesRefreshFailure(obj ObjectLayer, _ string, bucket string, _ http.Handler, _ auth.Credentials, t *testing.T) {
|
||||
if _, err := updateLocalBucketCORSMetadata(t.Context(), obj, bucket, []byte(testSiteReplicationCORSDoc)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sys := globalBucketMetadataSys
|
||||
sys.Lock()
|
||||
sys.noteLoadFailure(bucket)
|
||||
_, marked := sys.loadFailed[bucket]
|
||||
sys.Unlock()
|
||||
if marked {
|
||||
t.Fatal("a resident bucket was recorded as a load failure")
|
||||
}
|
||||
cfg, _, err := sys.GetResidentCorsConfig(bucket)
|
||||
if err != nil || cfg == nil {
|
||||
t.Fatalf("resident CORS configuration lost after a refresh failure: cfg=%v err=%v", cfg, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,17 +51,25 @@ type BucketMetadataSys struct {
|
||||
initialized bool
|
||||
group *singleflight.Group
|
||||
metadataMap map[string]BucketMetadata
|
||||
// loadFailed records real buckets whose metadata could not be loaded at
|
||||
// startup or during a refresh. They are absent from metadataMap even though
|
||||
// the subsystem is initialized, and without this bit a resident-only lookup
|
||||
// could not tell them apart from a name that is not a bucket at all. The
|
||||
// set is bounded by the number of failed loads and empty in normal operation.
|
||||
// loadFailed records real buckets whose metadata has never been loaded
|
||||
// successfully because the startup load or a refresh failed. They are
|
||||
// absent from metadataMap even though the subsystem is initialized, and
|
||||
// without this bit a resident-only lookup could not tell them apart from a
|
||||
// name that is not a bucket at all. It never holds a resident bucket, is
|
||||
// bounded by the number of failed loads, and is empty in normal operation.
|
||||
loadFailed map[string]struct{}
|
||||
}
|
||||
|
||||
// noteLoadFailure and clearLoadFailure maintain loadFailed; both expect the
|
||||
// caller to hold sys.Lock.
|
||||
func (sys *BucketMetadataSys) noteLoadFailure(bucket string) { sys.loadFailed[bucket] = struct{}{} }
|
||||
// caller to hold sys.Lock. A bucket that is resident keeps its last loaded
|
||||
// metadata through a failed refresh, exactly like every other bucket
|
||||
// configuration, so the set only ever holds non-resident buckets.
|
||||
func (sys *BucketMetadataSys) noteLoadFailure(bucket string) {
|
||||
if _, resident := sys.metadataMap[bucket]; !resident {
|
||||
sys.loadFailed[bucket] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
func (sys *BucketMetadataSys) clearLoadFailure(bucket string) { delete(sys.loadFailed, bucket) }
|
||||
|
||||
// Count returns number of bucket metadata map entries.
|
||||
|
||||
Reference in New Issue
Block a user