This is an automated email from the ASF dual-hosted git repository.

laskoviymishka pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new f403a02c7 fix(rest): include container in prefix-scoped IO cache key 
(#2174)
f403a02c7 is described below

commit f403a02c7ae3b7e3d3ea135ee67473cc4afc3f73
Author: Revanth <[email protected]>
AuthorDate: Sun Oct 11 08:39:41 2026 -0500

    fix(rest): include container in prefix-scoped IO cache key (#2174)
---
 catalog/rest/vended_creds.go      |  9 ++++++-
 catalog/rest/vended_creds_test.go | 51 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/catalog/rest/vended_creds.go b/catalog/rest/vended_creds.go
index 96ca6658f..0c8f3359e 100644
--- a/catalog/rest/vended_creds.go
+++ b/catalog/rest/vended_creds.go
@@ -482,7 +482,14 @@ func scopedFilesystemKey(credentialIndex int, location 
string) string {
                return fmt.Sprintf("%d:%s", credentialIndex, location)
        }
 
-       return fmt.Sprintf("%d:%s://%s", credentialIndex, parsed.Scheme, 
parsed.Host)
+       // The key must name everything a filesystem is bound to. An Azure IO 
serves
+       // one container@host authority, and url.URL.Host leaves out the 
container.
+       authority := parsed.Host
+       if parsed.User != nil {
+               authority = parsed.User.Username() + "@" + authority
+       }
+
+       return fmt.Sprintf("%d:%s://%s", credentialIndex, parsed.Scheme, 
authority)
 }
 
 func closeOptionalIO(fs iceio.IO) error {
diff --git a/catalog/rest/vended_creds_test.go 
b/catalog/rest/vended_creds_test.go
index 76c9a9e89..6a4ac6a1b 100644
--- a/catalog/rest/vended_creds_test.go
+++ b/catalog/rest/vended_creds_test.go
@@ -902,6 +902,57 @@ func 
TestPrefixScopedIODoesNotHoldLockDuringFilesystemClose(t *testing.T) {
        require.NoError(t, <-closeDone)
 }
 
+// An Azure IO is bound to one container@host authority, so two containers in
+// the same account need their own filesystems, whether or not a credential
+// matches them.
+func TestPrefixScopedIOKeysFilesystemsByContainer(t *testing.T) {
+       const scheme = "prefix-scoped-container-test"
+
+       var (
+               mu     sync.Mutex
+               opened []string
+       )
+       iceio.Register(scheme, func(_ context.Context, parsed *url.URL, _ 
map[string]string) (iceio.IO, error) {
+               mu.Lock()
+               defer mu.Unlock()
+               opened = append(opened, parsed.User.Username())
+
+               return iceio.LocalFS{}, nil
+       })
+       t.Cleanup(func() { iceio.Unregister(scheme) })
+
+       for _, tc := range []struct {
+               name        string
+               credentials []StorageCredential
+       }{
+               {name: "no matching credential"},
+               {name: "one credential covering both containers", credentials: 
[]StorageCredential{{
+                       Prefix: scheme + "://",
+                       Config: iceberg.Properties{iceio.ADLSSasTokenPrefix + 
"acct.dfs.core.windows.net": "sig=sas"},
+               }}},
+       } {
+               t.Run(tc.name, func(t *testing.T) {
+                       mu.Lock()
+                       opened = nil
+                       mu.Unlock()
+
+                       p := newPrefixScopedIO(context.Background(), nil, 
tc.credentials)
+                       for _, location := range []string{
+                               scheme + 
"://[email protected]/a.parquet",
+                               scheme + 
"://[email protected]/b.parquet",
+                               scheme + 
"://[email protected]/c.parquet",
+                       } {
+                               _, err := p.filesystemFor(location)
+                               require.NoError(t, err)
+                       }
+
+                       mu.Lock()
+                       defer mu.Unlock()
+                       assert.Equal(t, []string{"c1", "c2"}, opened)
+               })
+       }
+}
+
 func TestPrefixScopedIODoesNotApplyCredentialOutsidePrefix(t *testing.T) {
        t.Parallel()
 

Reply via email to