Revert "lease: one S3 client per region and inputs, not one per wake"
This reverts commit 4e85d529e44b3adfb0773bb88eb4f90dc69c2ce8.
3 files changed,
+0
−190
internal/lease/clientcache.go+0 −66internal/lease/clientcache_test.go+0 −106internal/lease/endpoint.go+0 −18
diff --git a/internal/lease/clientcache.go b/internal/lease/clientcache.go| deleted file mode 100644 |
| index d5f2d4601..000000000 |
| --- a/internal/lease/clientcache.go |
| +++ /dev/null |
| @@ -1,66 +0,0 @@ |
| -package lease |
| - |
| -import ( |
| - "crypto/sha256" |
| - "encoding/hex" |
| - "fmt" |
| - "os" |
| - "path/filepath" |
| - "sort" |
| - "strings" |
| - "sync" |
| - |
| - awss3 "github.com/aws/aws-sdk-go-v2/service/s3" |
| -) |
| - |
| -// Every wake takes a lease, and building the lease's S3 client was a fresh |
| -// AWS config load and a fresh credentials lookup each time: part of the |
| -// 130 ms the lease step took on a dev wake (2026-10-24). One client per set |
| -// of inputs is kept instead. An S3 client is safe for concurrent use, and |
| -// credentials that expire (an instance profile's) are refreshed by the |
| -// SDK's own cache inside it, so sharing changes nothing a fresh load would |
| -// decide. Anything a fresh load reads is part of the key, so a change to |
| -// it builds a new client: the region asked for, the environment the SDK |
| -// and s3compat read (AWS_*, CARLOS_S3_*), the effective uid and home |
| -// directory, and the identity of every credentials or config file the |
| -// load could open (rootaws's /etc/carlos/credentials for root, the shared |
| -// files otherwise). |
| -type leaserClient struct { |
| - client *awss3.Client |
| - region string |
| -} |
| - |
| -var leaserClients sync.Map // key string -> leaserClient |
| - |
| -func resetLeaserClients() { leaserClients.Clear() } |
| - |
| -func leaserClientKey(region string) string { |
| - var env []string |
| - for _, kv := range os.Environ() { |
| - if strings.HasPrefix(kv, "AWS_") || strings.HasPrefix(kv, "CARLOS_S3_") { |
| - env = append(env, kv) |
| - } |
| - } |
| - sort.Strings(env) |
| - home, _ := os.UserHomeDir() |
| - files := []string{"/etc/carlos/credentials", os.Getenv("AWS_SHARED_CREDENTIALS_FILE"), os.Getenv("AWS_CONFIG_FILE")} |
| - if home != "" { |
| - files = append(files, filepath.Join(home, ".aws", "credentials"), filepath.Join(home, ".aws", "config")) |
| - } |
| - h := sha256.New() |
| - fmt.Fprintf(h, "region=%s\x00uid=%d\x00home=%s\x00", region, os.Geteuid(), home) |
| - for _, kv := range env { |
| - fmt.Fprintf(h, "env=%s\x00", kv) |
| - } |
| - for _, f := range files { |
| - if f == "" { |
| - continue |
| - } |
| - if st, err := os.Stat(f); err == nil { |
| - fmt.Fprintf(h, "file=%s:%d:%d:%v\x00", f, st.Size(), st.ModTime().UnixNano(), st.Mode()) |
| - } else { |
| - fmt.Fprintf(h, "file=%s:absent\x00", f) |
| - } |
| - } |
| - return hex.EncodeToString(h.Sum(nil)) |
| -} |
diff --git a/internal/lease/clientcache_test.go b/internal/lease/clientcache_test.go| deleted file mode 100644 |
| index 2cc6725db..000000000 |
| --- a/internal/lease/clientcache_test.go |
| +++ /dev/null |
| @@ -1,106 +0,0 @@ |
| -package lease |
| - |
| -import ( |
| - "context" |
| - "os" |
| - "path/filepath" |
| - "testing" |
| - "time" |
| -) |
| - |
| -func cacheTestEnv(t *testing.T) { |
| - t.Helper() |
| - t.Setenv("AWS_ACCESS_KEY_ID", "test-key") |
| - t.Setenv("AWS_SECRET_ACCESS_KEY", "test-secret") |
| - t.Setenv("AWS_SESSION_TOKEN", "") |
| - t.Setenv("AWS_EC2_METADATA_DISABLED", "true") |
| - t.Setenv("CARLOS_S3_ENDPOINT", "") |
| - resetLeaserClients() |
| - t.Cleanup(resetLeaserClients) |
| -} |
| - |
| -// A wake builds its lease client once per region and endpoint, not once per |
| -// wake: the config load and the credentials lookup are paid by the first. |
| -func TestLeaserClientIsReusedForTheSameRegion(t *testing.T) { |
| - cacheTestEnv(t) |
| - a, ra, err := newLeaserClient(context.Background(), "eu-west-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - b, rb, err := newLeaserClient(context.Background(), "eu-west-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - if a != b || ra != rb { |
| - t.Fatalf("second call built a new client: %p %p (%q %q)", a, b, ra, rb) |
| - } |
| - c, _, err := newLeaserClient(context.Background(), "us-east-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - if c == a { |
| - t.Fatal("another region shared the client") |
| - } |
| -} |
| - |
| -// Anything that changes what a fresh load would build gives a fresh client: |
| -// the endpoint override, the environment's keys, a shared credentials file. |
| -func TestLeaserClientIsRebuiltWhenItsInputsChange(t *testing.T) { |
| - cacheTestEnv(t) |
| - first, _, err := newLeaserClient(context.Background(), "eu-west-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - for name, change := range map[string]func(t *testing.T){ |
| - "the endpoint": func(t *testing.T) { t.Setenv("CARLOS_S3_ENDPOINT", "https://fsn1.your-objectstorage.com") }, |
| - "the keys": func(t *testing.T) { t.Setenv("AWS_ACCESS_KEY_ID", "other-key") }, |
| - "the s3 quirks": func(t *testing.T) { t.Setenv("CARLOS_S3_QUIRKS", "unquoted-if-match") }, |
| - "the env region": func(t *testing.T) { t.Setenv("AWS_DEFAULT_REGION", "eu-central-1") }, |
| - "the creds file": func(t *testing.T) { |
| - path := filepath.Join(t.TempDir(), "credentials") |
| - if err := os.WriteFile(path, []byte("[default]\n"), 0o600); err != nil { |
| - t.Fatal(err) |
| - } |
| - t.Setenv("AWS_SHARED_CREDENTIALS_FILE", path) |
| - }, |
| - } { |
| - t.Run(name, func(t *testing.T) { |
| - change(t) |
| - c, _, err := newLeaserClient(context.Background(), "eu-west-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - if c == first { |
| - t.Fatalf("%s changed but the client was reused", name) |
| - } |
| - }) |
| - } |
| -} |
| - |
| -// A credentials file rewritten in place (same path) is a new input too. |
| -func TestLeaserClientIsRebuiltWhenTheCredentialsFileChanges(t *testing.T) { |
| - cacheTestEnv(t) |
| - path := filepath.Join(t.TempDir(), "credentials") |
| - if err := os.WriteFile(path, []byte("[default]\n"), 0o600); err != nil { |
| - t.Fatal(err) |
| - } |
| - t.Setenv("AWS_SHARED_CREDENTIALS_FILE", path) |
| - first, _, err := newLeaserClient(context.Background(), "eu-west-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - later := time.Now().Add(time.Minute) |
| - if err := os.WriteFile(path, []byte("[default]\nregion = eu-west-1\n"), 0o600); err != nil { |
| - t.Fatal(err) |
| - } |
| - if err := os.Chtimes(path, later, later); err != nil { |
| - t.Fatal(err) |
| - } |
| - c, _, err := newLeaserClient(context.Background(), "eu-west-1") |
| - if err != nil { |
| - t.Fatal(err) |
| - } |
| - if c == first { |
| - t.Fatal("the credentials file changed but the client was reused") |
| - } |
| -} |
diff --git a/internal/lease/endpoint.go b/internal/lease/endpoint.go| index 58047f041..4ec0156a9 100644 |
| --- a/internal/lease/endpoint.go |
| +++ b/internal/lease/endpoint.go |
| @@ -101,24 +101,6 @@ func regionFor(explicit, chainResolved string) string { |
| // carry that exact value onto the Held for Replicate's replica client rather |
| // than letting litestream substitute a different default of its own. |
| func newLeaserClient(ctx context.Context, region string) (*awss3.Client, string, error) { |
| - // One client per set of inputs (clientcache.go): the config load and |
| - // credentials lookup are a wake's, once, not every wake's. |
| - key := leaserClientKey(region) |
| - if c, ok := leaserClients.Load(key); ok { |
| - lc := c.(leaserClient) |
| - return lc.client, lc.region, nil |
| - } |
| - client, resolved, err := buildLeaserClient(ctx, region) |
| - if err != nil { |
| - return nil, "", err |
| - } |
| - c, _ := leaserClients.LoadOrStore(key, leaserClient{client: client, region: resolved}) |
| - lc := c.(leaserClient) |
| - return lc.client, lc.region, nil |
| -} |
| - |
| -// buildLeaserClient is newLeaserClient's uncached body. |
| -func buildLeaserClient(ctx context.Context, region string) (*awss3.Client, string, error) { |
| var opts []func(*awsconfig.LoadOptions) error |
| if region != "" { |
| opts = append(opts, awsconfig.WithRegion(region)) |