Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/operations"
"github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

Expand Down Expand Up @@ -75,11 +76,91 @@ func (s *disabledNegativeStatCacheTest) TestNegativeStatCacheDisabled() {
f, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))

//Assert File is found
assert.NoError(s.T(), err)
require.NoError(s.T(), err)
assert.Contains(s.T(), f.Name(), "explicit_dir/file1.txt")
assert.Nil(s.T(), f.Close())
}

func (s *disabledNegativeStatCacheTest) TestNegativeStatCacheDisabled_ImplicitDirectory() {
if !isImplicitDirsEnabled(s.flags) {
s.T().Skip("Skipping implicit directory test as --implicit-dirs flag is not enabled.")
}

implicitDir := path.Join(testEnv.testDirPath, "implicit_dir")
targetFile := path.Join(implicitDir, "file1.txt")

// Stat of non-existent implicit dir should fail.
_, err := os.Stat(implicitDir)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Open of non-existent file in implicit dir should fail.
_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create object in GCS directly under implicit_dir path.
client.CreateObjectInGCSTestDir(testEnv.ctx, testEnv.storageClient, s.testDir, "implicit_dir/file1.txt", "some-content", s.T())

// Since negative stat cache is disabled (TTL = 0), GCSFuse should not serve from negative cache.
// Stat on implicit dir should now succeed.
fi, err := os.Stat(implicitDir)
require.NoError(s.T(), err)
assert.True(s.T(), fi.IsDir())
Comment thread
alleaditya marked this conversation as resolved.

// File should be found and readable.
f, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
require.NoError(s.T(), err)
assert.Contains(s.T(), f.Name(), "implicit_dir/file1.txt")
assert.Nil(s.T(), f.Close())
Comment thread
alleaditya marked this conversation as resolved.
}

func (s *disabledNegativeStatCacheTest) TestNegativeStatCacheDisabled_ImplicitDirsDisabled() {
if isImplicitDirsEnabled(s.flags) || isHNSBucket() {
s.T().Skip("Skipping test: requires flat bucket with implicit-dirs disabled.")
}

targetFile := path.Join(testEnv.testDirPath, "file1.txt")

// Open of non-existent file should fail.
_, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create object in GCS directly.
client.CreateObjectInGCSTestDir(testEnv.ctx, testEnv.storageClient, s.testDir, "file1.txt", "some-content", s.T())

// Opening the file directly should succeed because file negative cache is disabled (TTL = 0).
f, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
require.NoError(s.T(), err)
assert.Contains(s.T(), f.Name(), "file1.txt")
assert.Nil(s.T(), f.Close())
}

func (s *disabledNegativeStatCacheTest) TestNegativeStatCacheDisabled_HNSFolder() {
if !isHNSBucket() {
s.T().Skip("Skipping test: requires HNS bucket.")
}

hnsDirName := "hns_dir"
hnsDirPath := path.Join(testEnv.testDirPath, hnsDirName)
hnsDirPathOnBucket := path.Join(s.testDir, hnsDirName)

// Stat of non-existent HNS folder should fail.
_, err := os.Stat(hnsDirPath)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create folder out-of-band on HNS bucket using control client.
_, err = client.CreateFolderInBucket(testEnv.ctx, testEnv.storageControlClient, hnsDirPathOnBucket)
require.NoError(s.T(), err)

// Since negative stat cache is disabled (TTL = 0), stat on HNS folder should succeed immediately.
fi, err := os.Stat(hnsDirPath)
require.NoError(s.T(), err)
assert.True(s.T(), fi.IsDir())
Comment thread
alleaditya marked this conversation as resolved.
}

////////////////////////////////////////////////////////////////////////
// Test Function (Runs once before all tests)
////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/operations"
"github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
)

Expand Down Expand Up @@ -86,11 +87,118 @@ func (s *finiteNegativeStatCacheTest) TestFiniteNegativeStatCache() {
f, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))

//Assert File is found
assert.NoError(s.T(), err)
require.NoError(s.T(), err)
assert.Contains(s.T(), f.Name(), "explicit_dir/file1.txt")
assert.Nil(s.T(), f.Close())
}

func (s *finiteNegativeStatCacheTest) TestFiniteNegativeStatCache_ImplicitDirectory() {
if !isImplicitDirsEnabled(s.flags) {
s.T().Skip("Skipping implicit directory test as --implicit-dirs flag is not enabled.")
}

implicitDir := path.Join(testEnv.testDirPath, "implicit_dir")
targetFile := path.Join(implicitDir, "file1.txt")

// Stat of non-existent implicit dir should fail, populating the negative stat cache for implicit_dir.
_, err := os.Stat(implicitDir)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Open of non-existent file in implicit dir should fail.
_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create object in GCS directly under implicit_dir path.
client.CreateObjectInGCSTestDir(testEnv.ctx, testEnv.storageClient, s.testDir, "implicit_dir/file1.txt", "some-content", s.T())

// Call should be served from negative cache (error returned) before cache expires.
_, err = os.Stat(implicitDir)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Wait for cache to expire (TTL = 5s).
time.Sleep(5 * time.Second)

// Stat on implicit dir should now succeed after cache expiration.
fi, err := os.Stat(implicitDir)
require.NoError(s.T(), err)
assert.True(s.T(), fi.IsDir())
Comment thread
alleaditya marked this conversation as resolved.

// File should now be found and readable.
f, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
require.NoError(s.T(), err)
assert.Contains(s.T(), f.Name(), "implicit_dir/file1.txt")
assert.Nil(s.T(), f.Close())
Comment thread
alleaditya marked this conversation as resolved.
}

func (s *finiteNegativeStatCacheTest) TestFiniteNegativeStatCache_ImplicitDirsDisabled() {
if isImplicitDirsEnabled(s.flags) || isHNSBucket() {
s.T().Skip("Skipping test: requires flat bucket with implicit-dirs disabled.")
}

targetFile := path.Join(testEnv.testDirPath, "file1.txt")

// Open of non-existent file should fail and populate negative cache for file1.txt.
_, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create object in GCS directly.
client.CreateObjectInGCSTestDir(testEnv.ctx, testEnv.storageClient, s.testDir, "file1.txt", "some-content", s.T())

// Call to open file before TTL expires should fail (negative cache hit for file).
_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Wait for cache to expire (TTL = 5s).
time.Sleep(5 * time.Second)

// Opening the file directly should now succeed after cache expiration.
f, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
require.NoError(s.T(), err)
assert.Contains(s.T(), f.Name(), "file1.txt")
assert.Nil(s.T(), f.Close())
}

func (s *finiteNegativeStatCacheTest) TestFiniteNegativeStatCache_HNSFolder() {
if !isHNSBucket() {
s.T().Skip("Skipping test: requires HNS bucket.")
}

hnsDirName := "hns_dir"
hnsDirPath := path.Join(testEnv.testDirPath, hnsDirName)
hnsDirPathOnBucket := path.Join(s.testDir, hnsDirName)

// Stat of non-existent HNS folder should fail, populating negative cache.
_, err := os.Stat(hnsDirPath)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create folder out-of-band on HNS bucket using control client.
_, err = client.CreateFolderInBucket(testEnv.ctx, testEnv.storageControlClient, hnsDirPathOnBucket)
require.NoError(s.T(), err)

// Stat before TTL expires should fail due to active negative cache.
_, err = os.Stat(hnsDirPath)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Wait for cache to expire (TTL = 5s).
time.Sleep(5 * time.Second)

// Stat on HNS folder should now succeed after cache expiration.
fi, err := os.Stat(hnsDirPath)
require.NoError(s.T(), err)
assert.True(s.T(), fi.IsDir())
}

////////////////////////////////////////////////////////////////////////
// Test Function (Runs once before all tests)
////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,82 @@ func (s *infiniteNegativeStatCacheTest) TestAlreadyExistFolder() {
assert.ErrorIs(s.T(), err, syscall.EEXIST)
}

func (s *infiniteNegativeStatCacheTest) TestInfiniteNegativeStatCache_ImplicitDirectory() {
if !isImplicitDirsEnabled(s.flags) {
s.T().Skip("Skipping implicit directory test as --implicit-dirs flag is not enabled.")
}

implicitDir := path.Join(testEnv.testDirPath, "implicit_dir")
targetFile := path.Join(implicitDir, "file1.txt")

// Stat of non-existent implicit dir should fail, populating negative stat cache for implicit_dir infinitely.
_, err := os.Stat(implicitDir)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Open of non-existent file in implicit dir should fail.
_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create object in GCS directly under implicit_dir path.
client.CreateObjectInGCSTestDir(testEnv.ctx, testEnv.storageClient, s.testDir, "implicit_dir/file1.txt", "some-content", s.T())

// Calls should continue to return error due to infinite negative stat cache.
_, err = os.Stat(implicitDir)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))
}

func (s *infiniteNegativeStatCacheTest) TestInfiniteNegativeStatCache_ImplicitDirsDisabled() {
if isImplicitDirsEnabled(s.flags) || isHNSBucket() {
s.T().Skip("Skipping test: requires flat bucket with implicit-dirs disabled.")
}

targetFile := path.Join(testEnv.testDirPath, "file1.txt")

// Open of non-existent file should fail, caching file non-existence infinitely.
_, err := os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create object in GCS directly.
client.CreateObjectInGCSTestDir(testEnv.ctx, testEnv.storageClient, s.testDir, "file1.txt", "some-content", s.T())

// Call to open file should continue to fail infinitely.
_, err = os.OpenFile(targetFile, os.O_RDONLY, os.FileMode(0600))
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))
}

func (s *infiniteNegativeStatCacheTest) TestInfiniteNegativeStatCache_HNSFolder() {
if !isHNSBucket() {
s.T().Skip("Skipping test: requires HNS bucket.")
}

hnsDirName := "hns_dir"
hnsDirPath := path.Join(testEnv.testDirPath, hnsDirName)
hnsDirPathOnBucket := path.Join(s.testDir, hnsDirName)

// Stat of non-existent HNS folder should fail, populating negative cache infinitely.
_, err := os.Stat(hnsDirPath)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))

// Create folder out-of-band on HNS bucket using control client.
_, err = client.CreateFolderInBucket(testEnv.ctx, testEnv.storageControlClient, hnsDirPathOnBucket)
require.NoError(s.T(), err)

// Stat should continue to fail infinitely due to infinite negative cache.
_, err = os.Stat(hnsDirPath)
assert.Error(s.T(), err)
assert.True(s.T(), os.IsNotExist(err))
}

////////////////////////////////////////////////////////////////////////
// Test Function (Runs once before all tests)
////////////////////////////////////////////////////////////////////////
Expand Down
14 changes: 14 additions & 0 deletions tools/integration_tests/negative_stat_cache/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"log"
"os"
"path"
"strings"
"testing"

"cloud.google.com/go/storage"
Expand Down Expand Up @@ -56,6 +57,19 @@ type env struct {

var testEnv env

func isImplicitDirsEnabled(flags []string) bool {
for _, flagSet := range flags {
if strings.Contains(flagSet, "implicit-dirs") && !strings.Contains(flagSet, "implicit-dirs=false") {
return true
}
}
return false
}
Comment thread
alleaditya marked this conversation as resolved.

func isHNSBucket() bool {
return setup.ResolveIsHierarchicalBucket(testEnv.ctx, setup.TestBucket(), testEnv.storageClient)
}

////////////////////////////////////////////////////////////////////////
// TestMain
////////////////////////////////////////////////////////////////////////
Expand Down
9 changes: 9 additions & 0 deletions tools/integration_tests/test_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1863,6 +1863,8 @@ negative_stat_cache:
- flags:
- "--metadata-cache-negative-ttl-secs=0,--client-protocol=http1"
- "--metadata-cache-negative-ttl-secs=0,--client-protocol=grpc"
- "--metadata-cache-negative-ttl-secs=0,--implicit-dirs,--client-protocol=http1"
- "--metadata-cache-negative-ttl-secs=0,--implicit-dirs,--client-protocol=grpc"
compatible:
flat: true
hns: true
Expand All @@ -1871,6 +1873,7 @@ negative_stat_cache:
run_on_gke: true
- flags:
- "--experimental-enable-pirlo,--metadata-cache-negative-ttl-secs=0,--client-protocol=http1"
- "--experimental-enable-pirlo,--metadata-cache-negative-ttl-secs=0,--implicit-dirs,--client-protocol=http1"
run_on_pirlo:
hns:
same_zone: true
Expand All @@ -1880,6 +1883,8 @@ negative_stat_cache:
- flags:
- "--metadata-cache-negative-ttl-secs=5,--client-protocol=http1"
- "--metadata-cache-negative-ttl-secs=5,--client-protocol=grpc"
- "--metadata-cache-negative-ttl-secs=5,--implicit-dirs,--client-protocol=http1"
- "--metadata-cache-negative-ttl-secs=5,--implicit-dirs,--client-protocol=grpc"
compatible:
flat: true
hns: true
Expand All @@ -1888,6 +1893,7 @@ negative_stat_cache:
run_on_gke: true
- flags:
- "--experimental-enable-pirlo,--metadata-cache-negative-ttl-secs=5,--client-protocol=http1"
- "--experimental-enable-pirlo,--metadata-cache-negative-ttl-secs=5,--implicit-dirs,--client-protocol=http1"
run_on_pirlo:
hns:
same_zone: true
Expand All @@ -1897,6 +1903,8 @@ negative_stat_cache:
- flags:
- "--metadata-cache-negative-ttl-secs=-1,--client-protocol=http1"
- "--metadata-cache-negative-ttl-secs=-1,--client-protocol=grpc"
- "--metadata-cache-negative-ttl-secs=-1,--implicit-dirs,--client-protocol=http1"
- "--metadata-cache-negative-ttl-secs=-1,--implicit-dirs,--client-protocol=grpc"
compatible:
flat: true
hns: true
Expand All @@ -1905,6 +1913,7 @@ negative_stat_cache:
run_on_gke: true
- flags:
- "--experimental-enable-pirlo,--metadata-cache-negative-ttl-secs=-1,--client-protocol=http1"
- "--experimental-enable-pirlo,--metadata-cache-negative-ttl-secs=-1,--implicit-dirs,--client-protocol=http1"
run_on_pirlo:
hns:
same_zone: true
Expand Down
Loading