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()