Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
0ff8a5c
feat(caching): implement negative stat cache to optimize polling of m…
alleaditya May 25, 2026
6ed7904
docs(fs): add detailed comment explaining LookUpChild short-circuit l…
alleaditya May 25, 2026
633c027
style: fix formatting in bucket_manager.go
alleaditya May 25, 2026
5afbc7f
fix(caching): address review comments and resolve data races/deadlock…
alleaditya May 25, 2026
3bf09de
style: remove extra blank line in fast_stat_bucket.go
alleaditya May 25, 2026
063c48a
refactor(caching): remove unrelated fixes and speculative listing cac…
alleaditya May 25, 2026
6cd627c
fix(caching): add negative caching for empty directories in fast_stat…
alleaditya Jun 4, 2026
85ef618
Revert changes to internal/fs/inode/dir.go as requested by reviewer
alleaditya Jun 4, 2026
7844a2d
fix(caching): remove speculative negative caching from insertListing
alleaditya Jun 8, 2026
a41f2f7
fix(caching): safely re-add negative caching to insertListing
alleaditya Jun 8, 2026
e4c4072
style: apply go fmt to tests
alleaditya Jun 8, 2026
b04bef2
chore: revert unintended formatting changes in hns_bucket_test.go
alleaditya Jun 8, 2026
0dfc7f6
style(caching): combine nested if conditions in insertListing
alleaditya Jun 8, 2026
855f4f0
fix(caching): move negative caching TTL check to caller
alleaditya Jun 8, 2026
0370a6a
refactor(caching): clean up boilerplate if-else blocks by using a hel…
alleaditya Jun 8, 2026
e70f114
Fix cache bug and combine condition
alleaditya Jun 8, 2026
ef534ee
Add b.implicitDir check to negative cache insertion condition
alleaditya Jun 10, 2026
abf86a8
Address PR comment: Use newline to separate AAA in gcs_metrics_test
alleaditya Jun 11, 2026
b928d18
Merge remote-tracking branch 'upstream/master' into add-negative-stat…
alleaditya Jun 11, 2026
f7e0e0c
test(metrics): use t.TempDir() instead of hardcoded tmp directory in …
alleaditya Jun 11, 2026
1311bb8
test(metrics): update short-circuit test to simulate list call with -…
alleaditya Jun 11, 2026
21c24f0
test(metrics): clean up comments in TestGCSMetrics_RequestCount_Negat…
alleaditya Jun 12, 2026
955c914
Update internal/fs/gcs_metrics_test.go
alleaditya Jun 15, 2026
e5a2c07
refactor(metrics): remove unused type definition in gcs_metrics_test
alleaditya Jun 15, 2026
5abeb0b
Short-circuit ListObjects if negative cache is hit
alleaditya Jun 16, 2026
0e32dbe
test(metrics): advance cacheClock for cross-mount tests
alleaditya Jun 16, 2026
5c2ff9f
fix(cache): restrict implicit dir negative caching and inference to d…
alleaditya Jun 16, 2026
fd86253
style(cache): apply De Morgan's law to satisfy staticcheck QF1001
alleaditya Jun 16, 2026
b7e0bdc
Fix staticcheck QF1001 and allow short-circuit in ListObjects for exp…
alleaditya Jun 16, 2026
fc4f668
Revert "allow short-circuit in ListObjects for explicit-dirs and HNS"
alleaditya Jun 16, 2026
006f3d9
fix(cache): preserve folder in LRU and fix ListObjects short-circuit …
alleaditya Jun 17, 2026
74843d4
Revert "fix(cache): preserve folder in LRU and fix ListObjects short-…
alleaditya Jun 17, 2026
ceb9bb1
fix(cache): fix ListObjects short-circuit for HNS folders
alleaditya Jun 17, 2026
51e42bc
Merge remote-tracking branch 'upstream/master' into add-negative-stat…
alleaditya Jun 17, 2026
c2fae1f
Fix integration test cache issues
alleaditya Jun 18, 2026
76b9fad
Address PR comments: Move negative cache to implicit directories sect…
alleaditya Jun 18, 2026
071d269
Refactor ListObjects negative cache short-circuiting logic
alleaditya Jun 18, 2026
891c487
Fix build error: pass time.Time to statCache LookUp
alleaditya Jun 18, 2026
bf412c2
Simplify ListObjects short-circuiting by removing unreachable HNS buc…
alleaditya Jun 18, 2026
c8ab55a
Revert addition of --metadata-cache-negative-ttl-secs=2 in dentry_cac…
alleaditya Jun 18, 2026
beab398
test: use unique t.TempDir() in file_cache_reader_test to fix cross-s…
alleaditya Jun 18, 2026
27d4998
test: restore --metadata-cache-negative-ttl-secs=2 to dentry_cache tests
alleaditya Jun 18, 2026
e0da02e
test: restore --metadata-cache-negative-ttl-secs=2 to test_config.yaml
alleaditya Jun 19, 2026
b9f8514
Fix integration test failures and restore flags
alleaditya Jun 19, 2026
9b1dd6d
test: Add negative TTL flag to CI integration test config
alleaditya Jun 19, 2026
f430837
Fix dentry_cache test failures due to implicit directory caching
alleaditya Jun 19, 2026
109b209
Revert addition of --metadata-cache-negative-ttl-secs in test_config.…
alleaditya Jun 19, 2026
10e5e13
Revert test changes as requested
alleaditya Jun 19, 2026
d8b9371
Remove negative caching of empty directories in insertListing
alleaditya Jun 19, 2026
6b0387e
Run go fmt
alleaditya Jun 19, 2026
ac90e84
Fix file_cache_reader_test crosstalk
alleaditya Jun 19, 2026
abf58ba
Merge branch 'master' into add-negative-stat-cache
alleaditya Jun 19, 2026
0bc9494
Restore negative stat cache for empty directories
alleaditya Jun 19, 2026
09de3a3
Fix dentry_cache integration tests by clearing test cache
alleaditya Jun 19, 2026
7c18968
Fix dentry_cache integration tests in test_config.yaml
alleaditya Jun 20, 2026
506cb06
Revert unnecessary destroy call
alleaditya Jun 20, 2026
7b3c970
Merge branch 'master' into add-negative-stat-cache
alleaditya Jun 24, 2026
5683653
fix: always add negative cache entry for NotFoundError in StatObjectF…
alleaditya Jun 24, 2026
46b7a9f
Restore test_config.yaml and file_cache_reader_test.go to master version
alleaditya Jun 24, 2026
a03b699
fix: add negative cache TTL to dentry_cache configuration
alleaditya Jun 25, 2026
bae72f6
fix: add negative cache TTL to dentry_cache configuration
alleaditya Jun 25, 2026
d1b8371
fix: enhance negative cache check for implicit directories in ListObj…
alleaditya Jun 25, 2026
afc97c7
fix: enhance negative cache check for implicit directories in ListObj…
alleaditya Jun 25, 2026
8ef2a88
fix: enhance negative cache check for implicit directories in ListObj…
alleaditya Jun 25, 2026
0a110be
fix: enhance negative cache check for implicit directories in ListObj…
alleaditya Jun 25, 2026
6b37509
fix: streamline negative cache handling in directory lookups and simp…
alleaditya Jun 29, 2026
9d39ecb
test: add TestLookUpChild_NegativeCacheHit
alleaditya Jun 29, 2026
7d8aa5f
test: delete irrelevant short-circuit test
alleaditya Jun 29, 2026
45a46d8
Revert "test: delete irrelevant short-circuit test"
alleaditya Jun 30, 2026
7443f67
Merge master into add-negative-stat-cache
alleaditya Jun 30, 2026
853a4c7
fix: update context import to standard library
alleaditya Jun 30, 2026
47a3623
fix: update context import to standard library
alleaditya Jun 30, 2026
f67ae58
Merge branch 'master' into add-negative-stat-cache
alleaditya Jul 1, 2026
0eee10b
fix: safely add negative cache for implicit directories
alleaditya Jun 30, 2026
23b88fe
Revert dir.go changes
alleaditya Jun 30, 2026
5b62465
Fix negative caching for implicit directories without breaking empty …
alleaditya Jun 30, 2026
8b9cd86
Fix negative caching for implicit directories without breaking empty …
alleaditya Jun 30, 2026
68b89c8
Fix negative caching for implicit directories without breaking empty …
alleaditya Jun 30, 2026
a8902f3
feat: add EnableEmptyManagedFolders configuration for Negative cachin…
alleaditya Jul 2, 2026
6bd45e2
feat: add EnableEmptyManagedFolders configuration for Negative cachin…
alleaditya Jul 2, 2026
13b5e4f
Merge branch 'master' into add-negative-stat-cache
alleaditya Jul 3, 2026
0005157
refactor: disable negative caching for empty managed folders and simp…
alleaditya Jul 3, 2026
8d5fe56
refactor: disable negative caching for empty managed folders and simp…
alleaditya Jul 3, 2026
c8a1c74
test: add sleep to TearDownTest to prevent negative cache interferenc…
alleaditya Jul 3, 2026
f36031a
refactor: remove path suffix check to improve implicit directory cach…
alleaditya Jul 3, 2026
c28daa1
trigger kokoro
alleaditya Jul 5, 2026
4d4fc09
test: adjust sleep duration in Dentry test to prevent negative cache …
alleaditya Jul 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ be interacting with the file system.`)
DummyIOCfg: newConfig.DummyIo,
IsTypeCacheDeprecated: newConfig.EnableTypeCacheDeprecation,
ImplicitDir: newConfig.ImplicitDirs,
EnableEmptyManagedFolders: newConfig.List.EnableEmptyManagedFolders,
}
bm := gcsx.NewBucketManager(bucketCfg, storageHandle)

Expand Down
7 changes: 6 additions & 1 deletion docs/semantics.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,12 @@ The behavior of stat cache is controlled by the following flags/config parameter
If this config variable is missing, then the value of ```--stat-cache-ttl``` is used.
* ```--stat-cache-ttl``` commandline flag, which can be set to a value like ```10s``` or ```1.5h```. The default is one minute. This has been deprecated (starting v2.0) and is currently only available for backward compatibility. If ```metadata-cache: ttl-secs``` is set, ```--stat-cache-ttl``` is ignored.

Positive and negative stat results will be cached for the specified amount of time.
Positive stat results (existing files) will be cached for this specified amount of time.

3. **Negative Stat-cache (Non-existent Entry Caching)**: Controls caching of non-existent paths (`ErrNotExist` / 404 results) to optimize workloads that aggressively poll missing files (e.g., JupyterLab).
* `metadata-cache: negative-ttl-secs` in the config-file (integer). Sets the TTL in seconds for negative entries. Default is 5 seconds. Use -1 for infinite TTL. Setting this to 0 disables negative caching.
Comment thread
alleaditya marked this conversation as resolved.
> [!WARNING]
> Using an infinite TTL (`-1`) for negative caching is risky if objects are created externally (by other clients or processes), as those new files/directories will never be visible under the mount until the cache is manually cleared or GCSFuse is restarted.

Warnings:
- Using stat caching breaks the consistency guarantees discussed in this document. It is safe only in the following situations:
Expand Down
8 changes: 8 additions & 0 deletions internal/fs/caching_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (t *cachingTestCommon) SetUpTestSuite() {
negativeCacheTTL,
IsTypeCacheDeprecated,
isImplicitDir,
false,
)

// Enable directory type caching.
Expand Down Expand Up @@ -338,6 +339,9 @@ func (t *CachingWithImplicitDirsTest) ImplicitDirectory_DefinedByFile() {

AssertEq(nil, err)

// Advance time so that any negative cache for "foo" from previous tests expires.
cacheClock.AdvanceTime(negativeCacheTTL + time.Millisecond)

// The directory should appear to exist.
fi, err = os.Stat(path.Join(mntDir, "foo"))
AssertEq(nil, err)
Expand All @@ -359,6 +363,9 @@ func (t *CachingWithImplicitDirsTest) ImplicitDirectory_DefinedByDirectory() {

AssertEq(nil, err)

// Advance time so that any negative cache for "foo" from previous tests expires.
cacheClock.AdvanceTime(negativeCacheTTL + time.Millisecond)

// The directory should appear to exist.
fi, err = os.Stat(path.Join(mntDir, "foo"))
AssertEq(nil, err)
Expand Down Expand Up @@ -479,6 +486,7 @@ func (t *MultiBucketMountCachingTest) SetUpTestSuite() {
negativeCacheTTL,
IsTypeCacheDeprecated,
isImplicitDir,
false,
)
}

Expand Down
91 changes: 91 additions & 0 deletions internal/fs/gcs_metrics_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ import (
"os"
"testing"

"time"

"github.com/googlecloudplatform/gcsfuse/v3/cfg"
"github.com/googlecloudplatform/gcsfuse/v3/internal/cache/lru"
"github.com/googlecloudplatform/gcsfuse/v3/internal/cache/metadata"
"github.com/googlecloudplatform/gcsfuse/v3/internal/fs"
"github.com/googlecloudplatform/gcsfuse/v3/internal/fs/wrappers"
"github.com/googlecloudplatform/gcsfuse/v3/internal/gcsx"
"github.com/googlecloudplatform/gcsfuse/v3/internal/monitor"
"github.com/googlecloudplatform/gcsfuse/v3/internal/storage/caching"
"github.com/googlecloudplatform/gcsfuse/v3/internal/storage/fake"
"github.com/googlecloudplatform/gcsfuse/v3/internal/storage/gcs"
"github.com/googlecloudplatform/gcsfuse/v3/internal/storage/storageutil"
Expand Down Expand Up @@ -463,3 +468,89 @@ func TestGCSMetrics_RetryCount(t *testing.T) {
attribute.NewSet(attribute.String("retry_error_category", "STALLED_READ_REQUEST")),
1)
}

type fakeBucketManagerForShortCircuit struct {
bucket gcs.Bucket
tempDir string
}

func (bm *fakeBucketManagerForShortCircuit) SetUpBucket(ctx context.Context, name string, _ bool, _ metrics.MetricHandle) (gcsx.SyncerBucket, error) {
return gcsx.NewSyncerBucket(0, 120, 10, bm.tempDir, gcsx.NewContentTypeBucket(bm.bucket)), nil
}
func (bm *fakeBucketManagerForShortCircuit) ShutDown() {}

// TestGCSMetrics_RequestCount_NegativeCachingShortCircuit validates that when negative entry caching is enabled,
// repeated lookups for non-existent files short-circuit in memory and do not emit redundant backend GCS requests.
func TestGCSMetrics_RequestCount_NegativeCachingShortCircuit(t *testing.T) {
ctx := context.Background()
origProvider := otel.GetMeterProvider()
t.Cleanup(func() { otel.SetMeterProvider(origProvider) })
reader := metric.NewManualReader()
provider := metric.NewMeterProvider(metric.WithReader(reader))
otel.SetMeterProvider(provider)
mh, err := metrics.NewOTelMetrics(ctx, 1, 100)
require.NoError(t, err, "metrics.NewOTelMetrics")
bucketName := "test-bucket"
rawBucket := fake.NewFakeBucket(timeutil.RealClock(), bucketName, gcs.BucketType{Hierarchical: false})
monitoringInnerBucket := monitor.NewMonitoringBucket(rawBucket, mh)
lruCache := lru.NewCache(1024 * 1024)
statCacheView := metadata.NewStatCacheBucketView(lruCache, "")
cachedOuterBucket := caching.NewFastStatBucket(
time.Minute,
statCacheView,
timeutil.RealClock(),
monitoringInnerBucket,
time.Minute,
true, // isTypeCacheDeprecated
true, // implicitDir
false,
)
serverCfg := &fs.ServerConfig{
NewConfig: &cfg.Config{
EnableNewReader: true,
EnableTypeCacheDeprecation: true,
MetadataCache: cfg.MetadataCacheConfig{
TtlSecs: 60,
},
},
MetricHandle: mh,
TraceHandle: tracing.NewNoopTracer(),
CacheClock: &timeutil.SimulatedClock{},
BucketName: bucketName,
BucketManager: &fakeBucketManagerForShortCircuit{
bucket: cachedOuterBucket,
tempDir: t.TempDir(),
},
ImplicitDirectories: true,
}
server, err := fs.NewFileSystem(ctx, serverCfg)
require.NoError(t, err, "NewFileSystem")
lookupOp := &fuseops.LookUpInodeOp{
Parent: fuseops.RootInodeID,
Name: "missing_file",
}

// First Lookup - Cache Miss
_ = server.LookUpInode(ctx, lookupOp)
waitForMetricsProcessing()

// Probing a missing file checks file (StatObject) and then implicit dir (ListObjects)
metrics.VerifyCounterMetric(t, ctx, reader, "gcs/request_count",
attribute.NewSet(attribute.String("gcs_method", "StatObject")),
1)
metrics.VerifyCounterMetric(t, ctx, reader, "gcs/request_count",
attribute.NewSet(attribute.String("gcs_method", "ListObjects")),
1)

// Second Lookup - Negative Cache Hit
_ = server.LookUpInode(ctx, lookupOp)
waitForMetricsProcessing()

// StatObject is short-circuited (remains 1), and ListObjects is also short-circuited (remains 1)
metrics.VerifyCounterMetric(t, ctx, reader, "gcs/request_count",
attribute.NewSet(attribute.String("gcs_method", "StatObject")),
1)
metrics.VerifyCounterMetric(t, ctx, reader, "gcs/request_count",
attribute.NewSet(attribute.String("gcs_method", "ListObjects")),
1)
}
3 changes: 2 additions & 1 deletion internal/fs/hns_bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ func (t *HNSCachedBucketMountTest) SetupSuite() {
uncachedHNSBucket,
negativeCacheTTL,
IsTypeCacheDeprecated,
isImplicitDir)
isImplicitDir,
false)

// Enable directory type caching.
t.serverCfg.DirTypeCacheTTL = ttl
Expand Down
27 changes: 17 additions & 10 deletions internal/fs/inode/dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,31 +683,38 @@ func (d *dirInode) LookUpChild(ctx context.Context, name string) (*Core, error)

// 1. Try Directory FIRST (since it's the preferred return type)
var dirResult *Core
var err error
var dirErr error
if d.Bucket().BucketType().Hierarchical {
dirResult, err = findExplicitFolder(ctx, d.Bucket(), NewDirName(d.Name(), name), true)
dirResult, dirErr = findExplicitFolder(ctx, d.Bucket(), NewDirName(d.Name(), name), true)
} else {
dirResult, err = findExplicitInode(ctx, d.Bucket(), NewDirName(d.Name(), name), true)
dirResult, dirErr = findExplicitInode(ctx, d.Bucket(), NewDirName(d.Name(), name), true)
}

// If we hit a real error (not a cache miss), exit early.
if dirErr != nil && !errors.As(dirErr, &cacheMissErr) {
Comment thread
alleaditya marked this conversation as resolved.
return nil, dirErr
}

// If we found a directory, we're done. Return it now.
if dirResult != nil {
return dirResult, nil
}
// If we hit a real error (not a cache miss), exit early.
if err != nil && !errors.As(err, &cacheMissErr) {
return nil, err
}

// 2. Try File ONLY if directory wasn't found
fileResult, err := findExplicitInode(ctx, d.Bucket(), NewFileName(d.Name(), name), true)
if err != nil && !errors.As(err, &cacheMissErr) {
return nil, err
fileResult, fileErr := findExplicitInode(ctx, d.Bucket(), NewFileName(d.Name(), name), true)
if fileErr != nil && !errors.As(fileErr, &cacheMissErr) {
return nil, fileErr
}

if fileResult != nil {
return fileResult, nil
}

// 3. Both lookups resulted in cache hits (no cacheMiss errors) with no results found,
// indicating a negative cache entry. Return nil to indicate the entry doesn't exist from cache.
if dirErr == nil && fileErr == nil {
return nil, nil
}
}

// 2. Legacy: If Type Cache is NOT deprecated, fetch the type hint.
Expand Down
51 changes: 50 additions & 1 deletion internal/fs/inode/dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"testing"
"time"

"context"

"github.com/googlecloudplatform/gcsfuse/v3/cfg"
"github.com/googlecloudplatform/gcsfuse/v3/internal/cache/metadata"
"github.com/googlecloudplatform/gcsfuse/v3/internal/contentcache"
Expand All @@ -39,7 +41,6 @@ import (
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"golang.org/x/net/context"
"golang.org/x/sync/semaphore"

"github.com/googlecloudplatform/gcsfuse/v3/internal/gcsx"
Expand Down Expand Up @@ -348,6 +349,54 @@ func (t *DirTest) TestLookUpChild_NonExistent() {
}
}

type mockNegativeCacheBucket struct {
gcs.Bucket
}

func (m *mockNegativeCacheBucket) StatObject(ctx context.Context, req *gcs.StatObjectRequest) (*gcs.MinObject, *gcs.ExtendedObjectAttributes, error) {
return nil, nil, &gcs.NotFoundError{Err: errors.New("negative cache")}
}

func (m *mockNegativeCacheBucket) BucketType() gcs.BucketType {
return gcs.BucketType{}
}

func (t *DirTest) TestLookUpChild_NegativeCacheHit() {
const name = "qux"

mockBucket := &mockNegativeCacheBucket{}
syncerBucket := gcsx.NewSyncerBucket(
1,
chunkRetryDeadlineSecs,
chunkTransferTimeoutSecs,
".gcsfuse_tmp/",
mockBucket)

config := &cfg.Config{
EnableTypeCacheDeprecation: true,
}

in := NewDirInode(
dirInodeID,
NewDirName(NewRootName(""), dirInodeName),
context.Background(),
fuseops.InodeAttributes{},
false,
false,
time.Second, // non-zero TTL to enable the caching logic
&syncerBucket,
&t.clock,
&t.clock,
semaphore.NewWeighted(10),
config,
)

result, err := in.LookUpChild(t.ctx, name)

require.NoError(t.T(), err)
require.Nil(t.T(), result)
}

func (t *DirTest) TestLookUpChild_FileOnly() {
const name = "qux"
objName := path.Join(dirInodeName, name)
Expand Down
5 changes: 4 additions & 1 deletion internal/gcsx/bucket_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ type BucketConfig struct {
IsTypeCacheDeprecated bool

ImplicitDir bool

EnableEmptyManagedFolders bool
}

// BucketManager manages the lifecycle of buckets.
Expand Down Expand Up @@ -241,7 +243,8 @@ func (bm *bucketManager) SetUpBucket(
b,
bm.config.NegativeStatCacheTTL,
bm.config.IsTypeCacheDeprecated,
bm.config.ImplicitDir)
bm.config.ImplicitDir,
bm.config.EnableEmptyManagedFolders)
}

// Enable content type awareness
Expand Down
26 changes: 19 additions & 7 deletions internal/storage/caching/fast_stat_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,17 @@ func NewFastStatBucket(
negativeCacheTTL time.Duration,
isTypeCacheDeprecated bool,
implicitDir bool,
enableEmptyManagedFolders bool,
) (b gcs.Bucket) {
fsb := &fastStatBucket{
cache: cache,
clock: clock,
wrapped: wrapped,
primaryCacheTTL: primaryCacheTTL,
negativeCacheTTL: negativeCacheTTL,
isTypeCacheDeprecated: isTypeCacheDeprecated,
implicitDir: implicitDir,
cache: cache,
clock: clock,
wrapped: wrapped,
primaryCacheTTL: primaryCacheTTL,
negativeCacheTTL: negativeCacheTTL,
isTypeCacheDeprecated: isTypeCacheDeprecated,
implicitDir: implicitDir,
enableEmptyManagedFolders: enableEmptyManagedFolders,
}

b = fsb
Expand Down Expand Up @@ -92,6 +94,8 @@ type fastStatBucket struct {
isTypeCacheDeprecated bool

implicitDir bool

enableEmptyManagedFolders bool
}

////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -145,6 +149,14 @@ func (b *fastStatBucket) insertListing(ctx context.Context, listing *gcs.Listing
if !b.implicitDir {
return
}
// Negative Cache (Only if it's a directory and there are NO contents)
// If enableEmptyManagedFolders is true, do not negatively cache empty directories,
// otherwise existing positive entries for valid empty managed folders get overwritten.
isNegativeCacheEnabled := b.negativeCacheTTL > 0 && !b.enableEmptyManagedFolders
Comment thread
alleaditya marked this conversation as resolved.
isEmptyNonRootDir := !dirHasContents && dirName != ""
if isNegativeCacheEnabled && isEmptyNonRootDir {
b.cache.AddNegativeEntry(dirName, b.clock.Now().Add(b.negativeCacheTTL))
}

// 3. Cache Sub-directories (Collapsed Runs)
// These represent folders discovered via prefixes in the ListObjects response.
Expand Down
Loading
Loading