carlos / platform Public

lease: one S3 client per region and inputs, not one per wake

Every lease built its S3 client from a fresh AWS config load and
credentials lookup (part of a 130 ms lease step on a dev wake). The client
is now cached, keyed by everything a fresh load reads: the region, the
AWS_* and CARLOS_S3_* environment, uid and home, and the identity of every
credentials or config file the load could open. Expiring credentials are
refreshed by the SDK's cache inside the client.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 4e85d529e44b3adfb0773bb88eb4f90dc69c2ce8 parent e3fd2e0
3 files changed, +190 −0
  • internal/lease/clientcache.go +66 −0
  • internal/lease/clientcache_test.go +106 −0
  • internal/lease/endpoint.go +18 −0
diff --git a/internal/lease/clientcache.go b/internal/lease/clientcache.go
new file mode 100644
index 000000000..d5f2d4601
--- /dev/null
+++ b/internal/lease/clientcache.go
@@ -0,0 +1,66 @@
+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
new file mode 100644
index 000000000..2cc6725db
--- /dev/null
+++ b/internal/lease/clientcache_test.go
@@ -0,0 +1,106 @@
+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 4ec0156a9..58047f041 100644
--- a/internal/lease/endpoint.go
+++ b/internal/lease/endpoint.go
@@ -101,6 +101,24 @@ 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))