From df3bf69ee1d458c193852f6f6d2d1a0789d01eab Mon Sep 17 00:00:00 2001 From: Vedant Das Date: Fri, 10 Jul 2026 19:01:44 +0000 Subject: [PATCH 1/3] used experimental flag to switch to radix cache when flag is set --- cmd/mount.go | 1 + internal/gcsx/bucket_manager.go | 9 ++++++++- tools/integration_tests/test_config.yaml | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/cmd/mount.go b/cmd/mount.go index 8745d76811..b172f7bd1a 100644 --- a/cmd/mount.go +++ b/cmd/mount.go @@ -108,6 +108,7 @@ be interacting with the file system.`) DummyIOCfg: newConfig.DummyIo, IsTypeCacheDeprecated: newConfig.EnableTypeCacheDeprecation, ImplicitDir: newConfig.ImplicitDirs, + EnableOptimizedMetadataCache: newConfig.MetadataCache.ExperimentalEnableOptimizedMetadataCache, } bm := gcsx.NewBucketManager(bucketCfg, storageHandle) diff --git a/internal/gcsx/bucket_manager.go b/internal/gcsx/bucket_manager.go index c523123028..43a6fabe5c 100644 --- a/internal/gcsx/bucket_manager.go +++ b/internal/gcsx/bucket_manager.go @@ -78,6 +78,8 @@ type BucketConfig struct { IsTypeCacheDeprecated bool ImplicitDir bool + + EnableOptimizedMetadataCache bool } // BucketManager manages the lifecycle of buckets. @@ -103,7 +105,12 @@ type bucketManager struct { func NewBucketManager(config BucketConfig, storageHandle storage.StorageHandle) BucketManager { var c lru.Cache if config.StatCacheMaxSizeMB > 0 { - c = lru.NewCache(util.MiBsToBytes(config.StatCacheMaxSizeMB)) + cacheSize := util.MiBsToBytes(config.StatCacheMaxSizeMB) + if config.EnableOptimizedMetadataCache { + c = lru.NewRadixCache(cacheSize) + } else { + c = lru.NewCache(cacheSize) + } } bm := &bucketManager{ diff --git a/tools/integration_tests/test_config.yaml b/tools/integration_tests/test_config.yaml index 2212a40f96..b8ff717164 100644 --- a/tools/integration_tests/test_config.yaml +++ b/tools/integration_tests/test_config.yaml @@ -34,6 +34,13 @@ explicit_dir: same_zone: false different_zone: false run_on_gke: true + - flags: + - "experimental-enable-optimized-metadata-cache=true,--implicit-dirs=false,--client-protocol=http1" + compatible: + flat: true + hns: true + zonal: true + run_on_gke: true implicit_dir: - mounted_directory: "${MOUNTED_DIR}" @@ -63,6 +70,13 @@ implicit_dir: hns: true zonal: false run_on_gke: true + - flags: + - "--experimental-enable-optimized-metadata-cache=true,--implicit-dirs,--client-protocol=http1" + compatible: + flat: true + hns: true + zonal: true + run_on_gke: true list_large_dir: - mounted_directory: "${MOUNTED_DIR}" @@ -145,6 +159,14 @@ operations: zonal: true tpc: true run_on_gke: false + - flags: + - "--experimental-enable-optimized-metadata-cache=true,--client-protocol=http1" + - "--experimental-enable-optimized-metadata-cache=true,--client-protocol=grpc" + compatible: + flat: true + hns: true + zonal: true + run_on_gke: true write_large_files: - mounted_directory: "${MOUNTED_DIR}" From 4420c9b7bdfb0430edee8d1fb8a46aafaa42c125 Mon Sep 17 00:00:00 2001 From: Vedant Das Date: Fri, 10 Jul 2026 19:18:29 +0000 Subject: [PATCH 2/3] modified benchmarks to test radix cache --- internal/cache/lru/lru_memory_test.go | 15 ++++++++++++- .../cache/lru/lru_workload_benchmark_test.go | 15 +++++++++++-- .../cache/metadata/statcache_memory_test.go | 20 +++++++++++++++-- tools/integration_tests/test_config.yaml | 22 ------------------- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/internal/cache/lru/lru_memory_test.go b/internal/cache/lru/lru_memory_test.go index 4c55c7fb2c..ce85584941 100644 --- a/internal/cache/lru/lru_memory_test.go +++ b/internal/cache/lru/lru_memory_test.go @@ -93,8 +93,21 @@ func TestMapCacheWorkloads(t *testing.T) { alloc := getMemStats() pureMem := alloc - baseMem runtime.KeepAlive(pureCache) - runtime.KeepAlive(paths) t.Logf("%-20s Heap Used: %10.2f MB\n", "MapLRU", float64(pureMem)/(1024*1024)) + + // 2. Pure RadixLRU + baseMemRadix := getMemStats() + + radixCache := lru.NewRadixCache(capacity) + for _, p := range paths { + _, _ = radixCache.Insert(p, dummyValue{}) + } + allocRadix := getMemStats() + pureMemRadix := allocRadix - baseMemRadix + runtime.KeepAlive(radixCache) + runtime.KeepAlive(paths) + + t.Logf("%-20s Heap Used: %10.2f MB\n", "RadixLRU", float64(pureMemRadix)/(1024*1024)) } } diff --git a/internal/cache/lru/lru_workload_benchmark_test.go b/internal/cache/lru/lru_workload_benchmark_test.go index 5090faae09..50fb2e9074 100644 --- a/internal/cache/lru/lru_workload_benchmark_test.go +++ b/internal/cache/lru/lru_workload_benchmark_test.go @@ -88,13 +88,14 @@ func benchmarkErasePrefix(b *testing.B, cache lru.Cache, prefixMap map[string][] } i := 0 + b.ResetTimer() for b.Loop() { + b.StopTimer() // Timer must be running when b.Loop() evaluates, stop it for setup prefix := prefixes[i%len(prefixes)] b.StartTimer() cache.EraseEntriesWithGivenPrefix(prefix) - //reset the timer so that we only measure erasure time - b.StopTimer() + b.StopTimer() // Stop timer for cleanup //restore the map keysToRestore := prefixMap[prefix] @@ -103,6 +104,7 @@ func benchmarkErasePrefix(b *testing.B, cache lru.Cache, prefixMap map[string][] } i++ + b.StartTimer() // Restart timer before b.Loop() evaluates } } @@ -116,14 +118,23 @@ func runBenchmarks(b *testing.B, name string, depth int) { b.Run(name+"_MapLRU_Insert", func(b *testing.B) { benchmarkInsert(b, lru.NewCache(capacity), keys) }) + b.Run(name+"_RadixLRU_Insert", func(b *testing.B) { + benchmarkInsert(b, lru.NewRadixCache(capacity), keys) + }) b.Run(name+"_MapLRU_Lookup", func(b *testing.B) { benchmarkLookup(b, lru.NewCache(capacity), keys) }) + b.Run(name+"_RadixLRU_Lookup", func(b *testing.B) { + benchmarkLookup(b, lru.NewRadixCache(capacity), keys) + }) b.Run(name+"_MapLRU_ErasePrefix", func(b *testing.B) { benchmarkErasePrefix(b, lru.NewCache(capacity), prefixMap, prefixes) }) + b.Run(name+"_RadixLRU_ErasePrefix", func(b *testing.B) { + benchmarkErasePrefix(b, lru.NewRadixCache(capacity), prefixMap, prefixes) + }) } func BenchmarkLRU_Flat(b *testing.B) { diff --git a/internal/cache/metadata/statcache_memory_test.go b/internal/cache/metadata/statcache_memory_test.go index 3810bd77aa..67c2f141d5 100644 --- a/internal/cache/metadata/statcache_memory_test.go +++ b/internal/cache/metadata/statcache_memory_test.go @@ -79,7 +79,7 @@ func TestStatCacheMemoryWorkloads(t *testing.T) { for _, w := range workloads { t.Logf("=== WORKLOAD: %s (%d files) ===", w, count) - // 1. StatCache + // 1. StatCache (Map) paths := generatePaths(w, count) baseMem := getMemStats() @@ -93,8 +93,24 @@ func TestStatCacheMemoryWorkloads(t *testing.T) { alloc := getMemStats() pureMem := alloc - baseMem runtime.KeepAlive(statCache) + + t.Logf("%-20s Heap Used: %10.2f MB\n", "Map StatCache", float64(pureMem)/(1024*1024)) + + // 2. StatCache (Radix) + baseMemRadix := getMemStats() + + sharedRadixCache := lru.NewRadixCache(capacity) + statRadixCache := metadata.NewStatCacheBucketView(sharedRadixCache, "") + + for _, p := range paths { + statRadixCache.Insert(&gcs.MinObject{Name: p}, expiration) + } + + allocRadix := getMemStats() + pureMemRadix := allocRadix - baseMemRadix + runtime.KeepAlive(statRadixCache) runtime.KeepAlive(paths) - t.Logf("%-20s Heap Used: %10.2f MB\n", "StatCache", float64(pureMem)/(1024*1024)) + t.Logf("%-20s Heap Used: %10.2f MB\n", "Radix StatCache", float64(pureMemRadix)/(1024*1024)) } } diff --git a/tools/integration_tests/test_config.yaml b/tools/integration_tests/test_config.yaml index b8ff717164..2212a40f96 100644 --- a/tools/integration_tests/test_config.yaml +++ b/tools/integration_tests/test_config.yaml @@ -34,13 +34,6 @@ explicit_dir: same_zone: false different_zone: false run_on_gke: true - - flags: - - "experimental-enable-optimized-metadata-cache=true,--implicit-dirs=false,--client-protocol=http1" - compatible: - flat: true - hns: true - zonal: true - run_on_gke: true implicit_dir: - mounted_directory: "${MOUNTED_DIR}" @@ -70,13 +63,6 @@ implicit_dir: hns: true zonal: false run_on_gke: true - - flags: - - "--experimental-enable-optimized-metadata-cache=true,--implicit-dirs,--client-protocol=http1" - compatible: - flat: true - hns: true - zonal: true - run_on_gke: true list_large_dir: - mounted_directory: "${MOUNTED_DIR}" @@ -159,14 +145,6 @@ operations: zonal: true tpc: true run_on_gke: false - - flags: - - "--experimental-enable-optimized-metadata-cache=true,--client-protocol=http1" - - "--experimental-enable-optimized-metadata-cache=true,--client-protocol=grpc" - compatible: - flat: true - hns: true - zonal: true - run_on_gke: true write_large_files: - mounted_directory: "${MOUNTED_DIR}" From 3a8eaa350752a7caf5de2054265dbc68a3feef33 Mon Sep 17 00:00:00 2001 From: Vedant Das Date: Fri, 17 Jul 2026 22:22:18 +0000 Subject: [PATCH 3/3] changes to table driven tests --- internal/cache/lru/lru_memory_test.go | 71 ++++++++++------- .../cache/lru/lru_workload_benchmark_test.go | 14 +--- .../cache/metadata/statcache_memory_test.go | 78 ++++++++++--------- 3 files changed, 87 insertions(+), 76 deletions(-) diff --git a/internal/cache/lru/lru_memory_test.go b/internal/cache/lru/lru_memory_test.go index ce85584941..5103178666 100644 --- a/internal/cache/lru/lru_memory_test.go +++ b/internal/cache/lru/lru_memory_test.go @@ -77,37 +77,48 @@ func (d dummyValue) Size() uint64 { func TestMapCacheWorkloads(t *testing.T) { count := 1000000 // 1 Million files capacity := uint64(count * 1000) - workloads := []string{"flat", "nested", "deeply_nested"} - for _, w := range workloads { - t.Logf("=== WORKLOAD: %s (%d files) ===", w, count) - - // 1. Pure MapLRU - paths := generatePaths(w, count) - baseMem := getMemStats() - - pureCache := lru.NewCache(capacity) - for _, p := range paths { - _, _ = pureCache.Insert(p, dummyValue{}) - } - alloc := getMemStats() - pureMem := alloc - baseMem - runtime.KeepAlive(pureCache) - - t.Logf("%-20s Heap Used: %10.2f MB\n", "MapLRU", float64(pureMem)/(1024*1024)) - - // 2. Pure RadixLRU - baseMemRadix := getMemStats() - - radixCache := lru.NewRadixCache(capacity) - for _, p := range paths { - _, _ = radixCache.Insert(p, dummyValue{}) - } - allocRadix := getMemStats() - pureMemRadix := allocRadix - baseMemRadix - runtime.KeepAlive(radixCache) - runtime.KeepAlive(paths) + tests := []struct { + name string + workload string + isRadix bool + }{ + {"Flat_Map", "flat", false}, + {"Flat_Radix", "flat", true}, + {"Nested_Map", "nested", false}, + {"Nested_Radix", "nested", true}, + {"DeeplyNested_Map", "deeply_nested", false}, + {"DeeplyNested_Radix", "deeply_nested", true}, + } - t.Logf("%-20s Heap Used: %10.2f MB\n", "RadixLRU", float64(pureMemRadix)/(1024*1024)) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Arrange + paths := generatePaths(tc.workload, count) + baseMem := getMemStats() + var cache lru.Cache + if tc.isRadix { + cache = lru.NewRadixCache(capacity) + } else { + cache = lru.NewCache(capacity) + } + + // Act + for _, p := range paths { + _, _ = cache.Insert(p, dummyValue{}) + } + + // Assert (or Log in this case) + alloc := getMemStats() + pureMem := alloc - baseMem + runtime.KeepAlive(cache) + runtime.KeepAlive(paths) + + cacheType := "MapLRU" + if tc.isRadix { + cacheType = "RadixLRU" + } + t.Logf("%-20s Heap Used: %10.2f MB\n", cacheType, float64(pureMem)/(1024*1024)) + }) } } diff --git a/internal/cache/lru/lru_workload_benchmark_test.go b/internal/cache/lru/lru_workload_benchmark_test.go index 50fb2e9074..9a36afcd41 100644 --- a/internal/cache/lru/lru_workload_benchmark_test.go +++ b/internal/cache/lru/lru_workload_benchmark_test.go @@ -88,23 +88,17 @@ func benchmarkErasePrefix(b *testing.B, cache lru.Cache, prefixMap map[string][] } i := 0 - b.ResetTimer() for b.Loop() { - b.StopTimer() // Timer must be running when b.Loop() evaluates, stop it for setup prefix := prefixes[i%len(prefixes)] - - b.StartTimer() cache.EraseEntriesWithGivenPrefix(prefix) - b.StopTimer() // Stop timer for cleanup - //restore the map - keysToRestore := prefixMap[prefix] - for _, key := range keysToRestore { + // UNTIMED: Pause clock, restore erased keys, and restart timer for next iteration + b.StopTimer() + for _, key := range prefixMap[prefix] { _, _ = cache.Insert(key, data) } - i++ - b.StartTimer() // Restart timer before b.Loop() evaluates + b.StartTimer() // Timer MUST be running when b.Loop() evaluates next! } } diff --git a/internal/cache/metadata/statcache_memory_test.go b/internal/cache/metadata/statcache_memory_test.go index 67c2f141d5..b03845e192 100644 --- a/internal/cache/metadata/statcache_memory_test.go +++ b/internal/cache/metadata/statcache_memory_test.go @@ -73,44 +73,50 @@ func generatePaths(workload string, count int) []string { func TestStatCacheMemoryWorkloads(t *testing.T) { count := 1000000 // 1 Million files capacity := uint64(count * 100000) // high capacity to avoid eviction - workloads := []string{"flat", "nested", "deeply_nested"} expiration := time.Now().Add(time.Hour) - for _, w := range workloads { - t.Logf("=== WORKLOAD: %s (%d files) ===", w, count) - - // 1. StatCache (Map) - paths := generatePaths(w, count) - baseMem := getMemStats() - - sharedCache := lru.NewCache(capacity) - statCache := metadata.NewStatCacheBucketView(sharedCache, "") - - for _, p := range paths { - statCache.Insert(&gcs.MinObject{Name: p}, expiration) - } - - alloc := getMemStats() - pureMem := alloc - baseMem - runtime.KeepAlive(statCache) - - t.Logf("%-20s Heap Used: %10.2f MB\n", "Map StatCache", float64(pureMem)/(1024*1024)) - - // 2. StatCache (Radix) - baseMemRadix := getMemStats() - - sharedRadixCache := lru.NewRadixCache(capacity) - statRadixCache := metadata.NewStatCacheBucketView(sharedRadixCache, "") - - for _, p := range paths { - statRadixCache.Insert(&gcs.MinObject{Name: p}, expiration) - } - - allocRadix := getMemStats() - pureMemRadix := allocRadix - baseMemRadix - runtime.KeepAlive(statRadixCache) - runtime.KeepAlive(paths) + tests := []struct { + name string + workload string + isRadix bool + }{ + {"Flat_Map", "flat", false}, + {"Flat_Radix", "flat", true}, + {"Nested_Map", "nested", false}, + {"Nested_Radix", "nested", true}, + {"DeeplyNested_Map", "deeply_nested", false}, + {"DeeplyNested_Radix", "deeply_nested", true}, + } - t.Logf("%-20s Heap Used: %10.2f MB\n", "Radix StatCache", float64(pureMemRadix)/(1024*1024)) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + // Arrange + paths := generatePaths(tc.workload, count) + baseMem := getMemStats() + var sharedCache lru.Cache + if tc.isRadix { + sharedCache = lru.NewRadixCache(capacity) + } else { + sharedCache = lru.NewCache(capacity) + } + statCache := metadata.NewStatCacheBucketView(sharedCache, "") + + // Act + for _, p := range paths { + statCache.Insert(&gcs.MinObject{Name: p}, expiration) + } + + // Assert (or Log in this case) + alloc := getMemStats() + pureMem := alloc - baseMem + runtime.KeepAlive(statCache) + runtime.KeepAlive(paths) + + cacheType := "Map StatCache" + if tc.isRadix { + cacheType = "Radix StatCache" + } + t.Logf("%-20s Heap Used: %10.2f MB\n", cacheType, float64(pureMem)/(1024*1024)) + }) } }