diff --git a/.gitignore b/.gitignore index 19b2b37929ae..bc3b01561e73 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ test*.bak /juicefs.lite dist/ juicefs.egg-info/ -pkg/meta/jfs-load-dump/ \ No newline at end of file +pkg/meta/jfs-load-dump/juicefs-fork +juicefs-fork diff --git a/cmd/mount_unix.go b/cmd/mount_unix.go index 4e248559c149..55e120d815c6 100644 --- a/cmd/mount_unix.go +++ b/cmd/mount_unix.go @@ -937,6 +937,16 @@ func installHandler(m meta.Meta, mp string, v *vfs.VFS, blob object.ObjectStorag sig := <-signalChan logger.Infof("Received signal %s, exiting...", sig.String()) if sig == syscall.SIGHUP { + // A passthrough open's data lives only in this process's + // staging files; the successor inherits the FUSE session but + // has no record of them, so their release-time reconciles + // would never run and every byte written through them would + // be lost. Drain first; if live opens remain, refuse the + // handover and keep serving. + if !fuse.DrainPassthrough(time.Second * 10) { + logger.Warnf("passthrough opens still in flight, don't restart") + continue + } path := fmt.Sprintf("/tmp/state%d.json", os.Getppid()) if err := v.FlushAll(""); err == nil { fuse.Shutdown() @@ -955,6 +965,19 @@ func installHandler(m meta.Meta, mp string, v *vfs.VFS, blob object.ObjectStorag } go func() { time.Sleep(time.Second * 30) + // The umount is stuck, but a passthrough reconcile may still + // be copying staging data whose close(2) already returned; + // FlushAll now would commit the mid-copy prefix as a durable + // short file. Extend the deadline while reconciles are in + // flight, with a hard cap so a wedged reconcile can't hold + // the exit hostage. + hardStop := time.Now().Add(time.Minute * 10) + for v.ExternalFlushes() > 0 && time.Now().Before(hardStop) { + time.Sleep(time.Millisecond * 100) + } + if n := v.ExternalFlushes(); n > 0 { + logger.Errorf("force exit with %d unfinished passthrough reconcile(s); their files may be committed incomplete", n) + } if err := v.FlushAll(""); err != nil { logger.Errorf("flush all: %s", err) } diff --git a/go.mod b/go.mod index 6efb9de9aafc..fc623c7d6179 100644 --- a/go.mod +++ b/go.mod @@ -14,11 +14,11 @@ require ( github.com/aliyun/alibabacloud-oss-go-sdk-v2 v1.4.1 github.com/aliyun/credentials-go v1.4.5 github.com/apple/foundationdb/bindings/go v0.0.0-20211207225159-47b9a81d1c10 - github.com/aws/aws-sdk-go-v2 v1.41.5 + github.com/aws/aws-sdk-go-v2 v1.42.0 github.com/aws/aws-sdk-go-v2/config v1.32.14 github.com/aws/aws-sdk-go-v2/credentials v1.19.14 github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 - github.com/aws/smithy-go v1.24.2 + github.com/aws/smithy-go v1.27.1 github.com/baidubce/bce-sdk-go v0.9.263 github.com/bytedance/mockey v1.2.14 github.com/ceph/go-ceph v0.18.0 @@ -34,7 +34,7 @@ require ( github.com/go-sql-driver/mysql v1.9.3 github.com/goccy/go-json v0.10.5 github.com/gofrs/flock v0.8.1 - github.com/golang/snappy v0.0.4 + github.com/golang/snappy v1.0.0 github.com/google/btree v1.1.2 github.com/google/uuid v1.6.0 github.com/grafana/pyroscope-go v1.2.1 @@ -71,7 +71,7 @@ require ( github.com/qingstor/qingstor-sdk-go/v4 v4.4.1 github.com/qiniu/go-sdk/v7 v7.26.8 github.com/redis/go-redis/v9 v9.18.0 - github.com/sirupsen/logrus v1.9.3 + github.com/sirupsen/logrus v1.9.4 github.com/smartystreets/goconvey v1.7.2 github.com/spf13/cast v1.7.1 github.com/stretchr/testify v1.11.1 @@ -88,16 +88,16 @@ require ( github.com/volcengine/ve-tos-golang-sdk/v2 v2.9.0 github.com/winfsp/cgofuse v1.6.0 go.etcd.io/etcd v3.3.27+incompatible - go.etcd.io/etcd/client/v3 v3.5.9 + go.etcd.io/etcd/client/v3 v3.6.12 go.uber.org/automaxprocs v1.6.0 - go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.49.0 - golang.org/x/net v0.52.0 - golang.org/x/oauth2 v0.34.0 - golang.org/x/sync v0.20.0 - golang.org/x/sys v0.42.0 - golang.org/x/term v0.41.0 - golang.org/x/text v0.35.0 + go.uber.org/zap v1.28.0 + golang.org/x/crypto v0.51.0 + golang.org/x/net v0.54.0 + golang.org/x/oauth2 v0.36.0 + golang.org/x/sync v0.21.0 + golang.org/x/sys v0.45.0 + golang.org/x/term v0.43.0 + golang.org/x/text v0.38.0 google.golang.org/api v0.210.0 google.golang.org/protobuf v1.36.11 gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216 @@ -113,13 +113,13 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect cloud.google.com/go/iam v1.2.2 // indirect cloud.google.com/go/monitoring v1.21.2 // indirect - filippo.io/edwards25519 v1.1.0 // indirect + filippo.io/edwards25519 v1.2.0 // indirect git.apache.org/thrift.git v0.13.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 // indirect github.com/BurntSushi/toml v1.3.2 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect github.com/IBM/go-sdk-core/v5 v5.21.2 // indirect @@ -133,8 +133,8 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect @@ -152,13 +152,14 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheggaaa/pb v1.0.29 // indirect github.com/clbanning/mxj v1.8.4 // indirect + github.com/clipperhouse/uax29/v2 v2.2.0 // indirect github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc // indirect - github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect + github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/coredns/coredns v1.4.0 // indirect github.com/coreos/etcd v3.3.27+incompatible // indirect - github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/coreos/go-systemd/v22 v22.7.0 // indirect github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect @@ -170,8 +171,8 @@ require ( github.com/djherbis/atime v1.0.0 // indirect github.com/dswarbrick/smart v0.0.0-20190505152634-909a45200d6d // indirect github.com/elastic/gosigar v0.14.2 // indirect - github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect - github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -194,14 +195,14 @@ require ( github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/glog v1.2.5 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/flatbuffers v25.2.10+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/readahead v0.0.0-20161222183148-eaceba169032 // indirect - github.com/google/s2a-go v0.1.8 // indirect + github.com/google/s2a-go v0.1.9 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/gopherjs/gopherjs v1.12.80 // indirect @@ -209,6 +210,7 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect @@ -229,7 +231,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/jtolio/noiseconn v0.0.0-20230301220541-88105e6c8ac6 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.18.6 // indirect github.com/klauspost/cpuid v1.3.1 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/klauspost/pgzip v1.2.5 // indirect @@ -240,7 +242,7 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect github.com/miekg/dns v1.1.61 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/minio/md5-simd v1.1.1 // indirect @@ -269,7 +271,7 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/pquerna/ffjson v0.0.0-20190930134022-aa0246cd15f7 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/procfs v0.20.1 // indirect github.com/rasky/go-xdr v0.0.0-20170124162913-1a41d1a06c93 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect @@ -283,7 +285,7 @@ require ( github.com/smartystreets/assertions v1.2.0 // indirect github.com/spacemonkeygo/monkit/v3 v3.0.25-0.20251022131615-eb24eb109368 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.8.1 // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a // indirect github.com/tidwall/gjson v1.6.7 // indirect @@ -302,33 +304,33 @@ require ( github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/errs v1.4.0 // indirect - go.etcd.io/etcd/api/v3 v3.5.9 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect + go.etcd.io/etcd/api/v3 v3.6.12 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.12 // indirect go.mongodb.org/mongo-driver v1.17.6 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/otel v1.44.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect + go.opentelemetry.io/otel/trace v1.44.0 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/mod v0.33.0 // indirect + golang.org/x/mod v0.36.0 // indirect golang.org/x/time v0.8.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/tools v0.45.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect - google.golang.org/grpc v1.80.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 // indirect + google.golang.org/grpc v1.81.1 // indirect + gopkg.in/ini.v1 v1.67.3 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect @@ -344,7 +346,7 @@ require ( replace github.com/minio/minio v0.0.0-20210206053228-97fe57bba92c => github.com/juicedata/minio v0.0.0-20260515071949-69a6cfc9da65 -replace github.com/hanwen/go-fuse/v2 v2.1.1-0.20210611132105-24a1dfe6b4f8 => github.com/juicedata/go-fuse/v2 v2.1.1-0.20260610024748-b44a81936922 +replace github.com/hanwen/go-fuse/v2 => github.com/unionai/go-fuse/v2 v2.1.1-0.20260624053043-ef6e8dc3c14e replace github.com/dgrijalva/jwt-go v3.2.0+incompatible => github.com/golang-jwt/jwt v3.2.1+incompatible diff --git a/go.sum b/go.sum index ba397af27758..97d0d0614a89 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,8 @@ cloud.google.com/go/trace v1.11.2 h1:4ZmaBdL8Ng/ajrgKqY5jfvzqMXbrDcBsUGXOT9aqTtI cloud.google.com/go/trace v1.11.2/go.mod h1:bn7OwXd4pd5rFuAnTrzBuoZ4ax2XQeG3qNgYmfCy0Io= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= git.apache.org/thrift.git v0.13.0 h1:/3bz5WZ+sqYArk7MBBBbDufMxKKOA56/6JO6psDpUDY= git.apache.org/thrift.git v0.13.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= gitea.com/davies/xorm v1.0.8-0.20220528043536-552d84d1b34a h1:awR9qREIs6qSnKr/cmSewVwDo74/kQ32x0CDEXUtiB8= @@ -37,6 +39,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.1/go.mod h1:Ng3urmn6dYe8gnbCMoHHVl5APYz2txho3koEkV2o2HA= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.4 h1:jWQK1GI+LeGGUKBADtcH2rRqPxYB1Ljwms5gFA2LqrM= @@ -47,6 +51,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 h1:RHK7bS+HQMslb1sZpAokUt+zTVmue0hKSs2C791hhzU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= @@ -55,6 +61,8 @@ github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 h1:l7+6kwRMJNwdCvYdDl7Eax+wzEYHSnNY7zrrfbhDdTA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.48.1 h1:oTX4vsorBZo/Zdum6OKPA4o7544hm6smoRv1QjpTwGo= @@ -102,6 +110,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= +github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA= +github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI= @@ -112,8 +122,12 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeD github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= @@ -138,6 +152,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBU github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.27.1 h1:4T340VFndXtADGF52gYa1POyL7s9E4Z1OeZ1hCscIw8= +github.com/aws/smithy-go v1.27.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/baidubce/bce-sdk-go v0.9.263 h1:tSO+CMS9h+wm+itbyoVy1SUxKO2iNibXn4gtBSC/qeY= github.com/baidubce/bce-sdk-go v0.9.263/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck= @@ -173,11 +189,15 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= +github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc h1:0xCWmFKBmarCqqqLeM7jFBSw/Or81UEElFqO8MY+GDs= github.com/cloudsoda/sddl v0.0.0-20250224235906-926454e91efc/go.mod h1:uvR42Hb/t52HQd7x5/ZLzZEK8oihrFpgnodIJ1vte2E= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w= github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/colinmarc/hdfs/v2 v2.4.0 h1:v6R8oBx/Wu9fHpdPoJJjpGSUxo8NhHIwrwsfhFvU9W0= github.com/colinmarc/hdfs/v2 v2.4.0/go.mod h1:0NAO+/3knbMx6+5pCv+Hcbaz4xn/Zzbn9+WIib2rKVI= github.com/coredns/coredns v1.4.0 h1:RubBkYmkByUqZWWkjRHvNLnUHgkRVqAWgSMmRFvpE1A= @@ -186,10 +206,14 @@ github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9Q github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= +github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb h1:GIzvVQ9UkUlOhSDlqmrQAAAUd6R3E+caIisNEyWXvNE= github.com/coreos/pkg v0.0.0-20240122114842-bbd7aa9bf6fb/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= @@ -234,11 +258,15 @@ github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwB github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g= github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98= +github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= github.com/erikdubbelboer/gspt v0.0.0-20210805194459-ce36a5128377 h1:gT+RM6gdTIAzMT7HUvmT5mL8SyG8Wx7iS3+L0V34Km4= github.com/erikdubbelboer/gspt v0.0.0-20210805194459-ce36a5128377/go.mod h1:v6o7m/E9bfvm79dE1iFiF+3T7zLBnrjYjkWMa1J+Hv0= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -321,6 +349,8 @@ github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I= @@ -344,6 +374,8 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= @@ -373,6 +405,8 @@ github.com/google/readahead v0.0.0-20161222183148-eaceba169032 h1:6Be3nkuJFyRfCg github.com/google/readahead v0.0.0-20161222183148-eaceba169032/go.mod h1:qYysrqQXuV4tzsizt4oOQ6mrBZQ0xnQXP3ylXX8Jk5Y= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -400,6 +434,10 @@ github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrR github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/consul/api v1.29.2 h1:aYyRn8EdE2mSfG14S1+L9Qkjtz8RzmaWh6AcNGRNwPw= github.com/hashicorp/consul/api v1.29.2/go.mod h1:0YObcaLNDSbtlgzIRtmRXI1ZkeuK0trCBxwZQ4MYnIk= github.com/hashicorp/consul/proto-public v0.6.2 h1:+DA/3g/IiKlJZb88NBn0ZgXrxJp2NlvCZdEyl+qxvL0= @@ -519,6 +557,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao= +github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s= github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4= @@ -567,6 +607,8 @@ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= @@ -706,6 +748,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/prometheus/prometheus v0.54.1 h1:vKuwQNjnYN2/mDoWfHXDhAsz/68q/dQDb+YbcEqU7MQ= github.com/prometheus/prometheus v0.54.1/go.mod h1:xlLByHhk2g3ycakQGrMaU8K7OySZx98BzeCR99991NY= github.com/qingstor/qingstor-sdk-go/v4 v4.4.1 h1:DeoKecl+Ls8GhfXVWB0mQ0Qiy1HtmzgFCkeAi9v7ydw= @@ -756,6 +800,8 @@ github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fy github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= @@ -772,6 +818,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/spiffe/go-spiffe/v2 v2.8.1 h1:eXZMLsu+3MLEPJyGJkolqtVrteZfQdUpOWj6LTiDl/E= +github.com/spiffe/go-spiffe/v2 v2.8.1/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -822,6 +870,8 @@ github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9f github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg= github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ= +github.com/unionai/go-fuse/v2 v2.1.1-0.20260624053043-ef6e8dc3c14e h1:3DXRSotPxDCHSKY9FGChQSgInim3tFEPjtexoTxx2ps= +github.com/unionai/go-fuse/v2 v2.1.1-0.20260624053043-ef6e8dc3c14e/go.mod h1:xKwi1cF7nXAOBCXujD5ie0ZKsxc8GGSA1rlMJc+8IJs= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.67.0 h1:tqKlJMUP6iuNG8hGjK/s9J4kadH7HLV4ijEcPGsezac= @@ -867,10 +917,16 @@ go.etcd.io/etcd v3.3.27+incompatible h1:5hMrpf6REqTHV2LW2OclNpRtxI0k9ZplMemJsMSW go.etcd.io/etcd v3.3.27+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs= go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/api/v3 v3.6.12 h1:OLOZUKEuAA36TR48F0cIaa8FdzrWygjyfrJxXg4iDgs= +go.etcd.io/etcd/api/v3 v3.6.12/go.mod h1:p14EIQXHbuOQbVvL/WEes5uqKnxP9AgKJgpjbMVvzvE= go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE= go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/pkg/v3 v3.6.12 h1:36zzB+pQOdHbhN+kH2iJz/K8bJn0ZLtLfPPO7jozTDo= +go.etcd.io/etcd/client/pkg/v3 v3.6.12/go.mod h1:hh2+ZXtfLzs3o6mn92ntgNPBrTJJOvXqICM5g3L3DMY= go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E= go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= +go.etcd.io/etcd/client/v3 v3.6.12 h1:kMSP6JcPZMqSJiX+TXdUIBU/4eXEZWBAaui4VihMbIc= +go.etcd.io/etcd/client/v3 v3.6.12/go.mod h1:CMs6fJWYiZQk4ytFjd4lE1diOvvRMmtbbn/alZXd3dQ= go.mongodb.org/mongo-driver v1.17.6 h1:87JUG1wZfWsr6rIz3ZmpH90rL5tea7O3IHuSwHUpsss= go.mongodb.org/mongo-driver v1.17.6/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= @@ -879,22 +935,34 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk= +go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ= +go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -915,8 +983,12 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo= +go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= @@ -937,6 +1009,8 @@ golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58 golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= @@ -950,6 +1024,7 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -977,9 +1052,13 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -992,6 +1071,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180807162357-acbc56fc7007/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1037,6 +1118,8 @@ golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -1044,6 +1127,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1054,6 +1139,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1075,6 +1162,7 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1094,8 +1182,12 @@ google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516 h1:vmC/ws+pLzWjj/gzApyoZuSVrDtF1aod4u/+bbj8hgM= google.golang.org/genproto/googleapis/api v0.0.0-20260120221211-b8f7ae30c516/go.mod h1:p3MLuOwURrGBRoEyFHBT3GjUwaCQVKeNqqWxlcISGdw= +google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec= +google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc= google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA= google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478 h1:RmoJA1ujG+/lRGNfUnOMfhCy5EipVMyvUE+KNbPbTlw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260414002931-afd174a4e478/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -1103,6 +1195,10 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1126,6 +1222,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw= +gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216 h1:2TSTkQ8PMvGOD5eeqqRVv6Z9+BYI+bowK97RCr3W+9M= gopkg.in/kothar/go-backblaze.v0 v0.0.0-20210124194846-35409b867216/go.mod h1:zJ2QpyDCYo1KvLXlmdnFlQAyF/Qfth0fB8239Qg7BIE= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= diff --git a/pkg/fuse/fuse.go b/pkg/fuse/fuse.go index 33a20875e768..ad585bfada76 100644 --- a/pkg/fuse/fuse.go +++ b/pkg/fuse/fuse.go @@ -40,6 +40,12 @@ type fileSystem struct { fuse.RawFileSystem conf *vfs.Config v *vfs.VFS + + // passthrough write-path acceleration (experimental). When enabled and + // the kernel supports FUSE passthrough, newly written files are backed by + // a local staging file the kernel reads/writes directly (no daemon upcall), + // reconciled into JuiceFS slices on release. See passthrough.go. + pt *passthroughState } func newFileSystem(conf *vfs.Config, v *vfs.VFS) *fileSystem { @@ -119,6 +125,12 @@ func (fs *fileSystem) SetAttr(cancel <-chan struct{}, in *fuse.SetAttrIn, out *f if err != 0 { return fuse.Status(err) } + if in.Valid&fuse.FATTR_SIZE != 0 { + // truncate(2)/ftruncate(2) never reach the backing file (the kernel + // only diverts read/write/mmap); mirror the new size onto any live + // passthrough backing so it doesn't diverge from the metadata. + fs.pt.truncate(Ino(in.NodeId), in.Size) + } fs.replyAttr(ctx, entry, &out.Attr, out.SetTimeout) return 0 } @@ -235,17 +247,36 @@ func (fs *fileSystem) Create(cancel <-chan struct{}, in *fuse.CreateIn, name str return fuse.Status(err) } out.Fh = fh + // A freshly Create'd file is always empty. + if id, ok := fs.pt.tryOpen(entry.Inode, fh, in.Flags, true); ok { + out.OpenFlags |= fuse.FOPEN_PASSTHROUGH + out.BackingID = id + } return fs.replyEntry(ctx, &out.EntryOut, entry) } func (fs *fileSystem) Open(cancel <-chan struct{}, in *fuse.OpenIn, out *fuse.OpenOut) (status fuse.Status) { ctx := fs.newContext(cancel, &in.InHeader) defer releaseContext(ctx) + // If this inode has a passthrough write still reconciling, wait for it so + // this open sees the file's real (post-reconcile) size and content rather + // than the transient empty state — otherwise a write here would race the + // reconcile's copy and lose data. + fs.pt.waitInode(Ino(in.NodeId)) entry, fh, err := fs.v.Open(ctx, Ino(in.NodeId), in.Flags) if err != 0 { return fuse.Status(err) } out.Fh = fh + // Passthrough only when this open observes an empty file: O_TRUNC (about + // to be emptied) or an already zero-length file. Otherwise the backing + // (which starts empty) would shadow / overwrite real content. + emptyAtOpen := in.Flags&uint32(syscall.O_TRUNC) != 0 || entry.Attr.Length == 0 + if id, ok := fs.pt.tryOpen(Ino(in.NodeId), fh, in.Flags, emptyAtOpen); ok { + out.OpenFlags |= fuse.FOPEN_PASSTHROUGH + out.BackingID = id + return 0 + } if vfs.IsSpecialNode(Ino(in.NodeId)) { out.OpenFlags |= fuse.FOPEN_DIRECT_IO } else if entry.Attr.KeepCache { @@ -273,6 +304,7 @@ func (fs *fileSystem) Read(cancel <-chan struct{}, in *fuse.ReadIn, buf []byte) func (fs *fileSystem) Release(cancel <-chan struct{}, in *fuse.ReleaseIn) { ctx := fs.newContext(cancel, &in.InHeader) defer releaseContext(ctx) + fs.pt.reconcile(ctx, fs.v, in.Fh) fs.v.Release(ctx, Ino(in.NodeId), in.Fh) } @@ -296,6 +328,12 @@ func (fs *fileSystem) Flush(cancel <-chan struct{}, in *fuse.FlushIn) fuse.Statu func (fs *fileSystem) Fsync(cancel <-chan struct{}, in *fuse.FsyncIn) (code fuse.Status) { ctx := fs.newContext(cancel, &in.InHeader) defer releaseContext(ctx) + // For a passthrough open the daemon's writer holds none of the data (it + // lives in the kernel backing file), so vfs.Fsync alone would succeed + // without making anything durable; reconcile the staging content first. + if handled, errno := fs.pt.fsync(ctx, fs.v, in.Fh); handled { + return fuse.Status(errno) + } err := fs.v.Fsync(ctx, Ino(in.NodeId), int(in.FsyncFlags), in.Fh) return fuse.Status(err) } @@ -516,6 +554,14 @@ func Serve(v *vfs.VFS, options string, xattrs, ioctl bool) error { opt.Options = append(opt.Options, "volname="+conf.Format.Name) opt.Options = append(opt.Options, "daemon_timeout=60", "iosize=65536", "novncache") } + // Experimental: opt into FUSE passthrough write acceleration via env var + // (avoids CLI surface while prototyping). JUICEFS_PASSTHROUGH_DIR must point + // at a non-stacked fs (tmpfs/ext4/xfs), not overlayfs/fuse. + ptEnabled := os.Getenv("JUICEFS_PASSTHROUGH") == "1" + if ptEnabled { + opt.EnablePassthrough = true + opt.MaxStackDepth = 2 + } fssrv, err := fuse.NewServer(imp, conf.Meta.MountPoint, &opt) if err != nil { if execErr, ok := err.(*exec.Error); ok { @@ -539,8 +585,30 @@ func Serve(v *vfs.VFS, options string, xattrs, ioctl bool) error { } } + if ptEnabled { + imp.pt = newPassthroughState(fssrv, os.Getenv("JUICEFS_PASSTHROUGH_DIR")) + ptState = imp.pt + logger.Infof("FUSE passthrough enabled (experimental); staging dir=%s", imp.pt.dir) + } + fsserv = fssrv fssrv.Serve() + // The kernel delivers queued RELEASEs before tearing the session down, + // but their passthrough reconciles may still be copying staging data + // when Serve returns; exiting now would lose bytes of files whose + // close(2) long since succeeded. Wait the reconciles out so anything + // that snapshots the metadata store after the daemon exits + // (commit/finalize) sees every closed file in full. + if imp.pt != nil { + deadline := time.Now().Add(time.Minute) + for v.ExternalFlushes() > 0 { + if time.Now().After(deadline) { + logger.Errorf("passthrough: exiting with %d unfinished reconcile(s)", v.ExternalFlushes()) + break + } + time.Sleep(50 * time.Millisecond) + } + } return nil } @@ -585,6 +653,7 @@ func GenFuseOpt(conf *vfs.Config, options string, mt int, noxattr, noacl bool, m } var fsserv *fuse.Server +var ptState *passthroughState func Shutdown() bool { if fsserv != nil { @@ -592,3 +661,16 @@ func Shutdown() bool { } return false } + +// DrainPassthrough blocks new passthrough opens and waits (bounded) for all +// live passthrough opens and in-flight reconciles to finish. Returns false — +// with passthrough re-enabled — if any remain; the caller must then refuse a +// session handover, because passthrough data exists only in this process's +// staging files and a successor has no record of them. Returns true +// immediately when passthrough is not enabled. +func DrainPassthrough(timeout time.Duration) bool { + if ptState == nil { + return true + } + return ptState.drain(timeout) +} diff --git a/pkg/fuse/passthrough.go b/pkg/fuse/passthrough.go new file mode 100644 index 000000000000..15dd75742796 --- /dev/null +++ b/pkg/fuse/passthrough.go @@ -0,0 +1,512 @@ +/* + * JuiceFS, Copyright 2026 Juicedata, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fuse + +import ( + "fmt" + "io" + "os" + "path/filepath" + "sync" + "syscall" + "time" + + "github.com/hanwen/go-fuse/v2/fuse" + "github.com/juicedata/juicefs/pkg/utils" + "github.com/juicedata/juicefs/pkg/vfs" +) + +// passthroughState manages per-open backing files used for FUSE passthrough +// write acceleration. EXPERIMENTAL: scoped to the write path. A file opened +// for write gets a local staging file (on a non-stacked fs) that the kernel +// reads/writes directly via FUSE_PASSTHROUGH, bypassing the daemon per-op. On +// release the staging file is reconciled into JuiceFS slices via the normal +// writer path. Durability is therefore deferred to release (commit-style), +// with one exception: fsync(2)/fdatasync(2) reconcile immediately (see +// fsync below), so an application's explicit sync point means what it says. +// +// Backing registrations are NOT reused across opens: each write-open gets a +// fresh registration (a new ioctl or, on unprivileged broker mounts, a new +// RPC round trip to the node broker, plus a fresh staging-file create), and +// reconcile() unregisters and removes it — for a DIFFERENT inode's next open, +// never the same one. This was originally pooled (checked-in backings parked +// for reuse by whichever inode opened next), which is measurably cheaper for +// small-file-heavy workloads, but pooling handed a backing file across +// inodes: once mmap(2) has been called on a passthrough fd, the kernel's +// fuse_passthrough_mmap() repoints the VMA directly at the backing file +// (vma_set_file), fully decoupling the mapping's lifetime from the fd — a +// process can close(2) (triggering reconcile+recycle) while a still-live, +// still-dirty mapping keeps writing to what is now, after recycling, a +// DIFFERENT inode's staging file. There is no FUSE opcode or other userspace +// signal that observes mmap activity on a passthrough-diverted file (the +// entire point of passthrough is to bypass such upcalls), so there is no way +// to detect "this backing might still be mmap'd" and only pool the ones that +// aren't. Reusing a backing across inodes is a real, silent cross-tenant +// data-corruption vector; not reusing it is not. +type passthroughState struct { + server *fuse.Server + dir string + + mu sync.Mutex + files map[uint64]*ptFile // keyed by fh + busy map[Ino]int // inodes with a passthrough open or pending reconcile + seq int // monotonic counter for unique staging file names + disabled bool // registration failed with a permanent error; stop trying + paused bool // draining for handover/shutdown; refuse new passthrough opens + warnOne sync.Once +} + +// ptBacking is one registered kernel backing: a staging file plus the +// backing ID the kernel handed back for it. It outlives individual opens. +type ptBacking struct { + path string + f *os.File + backingID int32 +} + +type ptFile struct { + ino Ino + fh uint64 + b *ptBacking + // mu serializes staging-content copies for this open: fsync-time copies + // against each other and against the final release-time reconcile. + mu sync.Mutex +} + +func newPassthroughState(server *fuse.Server, dir string) *passthroughState { + base := dir + if base == "" { + base = filepath.Join(os.TempDir(), "juicefs-passthrough") + } + _ = os.MkdirAll(base, 0700) + // Isolate this mount's staging in a per-process subdir. Several juicefs + // mounts (multiple volumes on a node, or a graceful-restart successor) + // can share `base`; a flat shared dir would let one mount's pool-N.tmp + // collide with another's, and — worse — a startup sweep of the shared dir + // would unlink another live mount's in-use staging files, silently losing + // the data of files whose close(2) already returned. A private subdir + // removes both hazards, so we deliberately do NOT garbage-collect the + // shared root here (a crashed predecessor's subdir is inert and can be + // reaped out of band). + sub, err := os.MkdirTemp(base, fmt.Sprintf("m%d-", os.Getpid())) + if err != nil { + logger.Warnf("passthrough: per-process staging dir under %s: %s; using base", base, err) + sub = base + } + return &passthroughState{ + server: server, + dir: sub, + files: make(map[uint64]*ptFile), + busy: make(map[Ino]int), + } +} + +// checkout registers a fresh backing for a new passthrough open. Always a new +// registration — see the passthroughState doc comment for why a backing is +// never reused across inodes. +func (p *passthroughState) checkout() (*ptBacking, bool) { + p.mu.Lock() + if p.disabled { + p.mu.Unlock() + return nil, false + } + p.seq++ + seq := p.seq + p.mu.Unlock() + + path := filepath.Join(p.dir, fmt.Sprintf("pool-%d.tmp", seq)) + // O_EXCL: never open a staging file that already exists. The dir is + // per-process and seq is monotonic, so a collision would signal a bug + // (or a shared-dir fallback), and truncating a pre-existing file could + // clobber another mount's in-use backing. + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if err != nil { + logger.Warnf("passthrough: open staging %s: %s", path, err) + return nil, false + } + id, errno := p.server.RegisterBackingFd(&fuse.BackingMap{Fd: int32(f.Fd())}) + if errno != 0 { + _ = f.Close() + _ = os.Remove(path) + // EPERM is permanent: the backing-registration ioctl needs + // CAP_SYS_ADMIN in the init user namespace, and a process that + // lacks it now will lack it for every open (e.g. a non-root + // container whose added capabilities are bounding-set only). + // Without this latch every write-open pays a doomed ioctl, a + // staging create/remove, and a warning line (ENG26-869 saw 2000+ + // per run). Other errnos may be transient; keep trying those. + if errno == syscall.EPERM { + p.mu.Lock() + p.disabled = true + p.mu.Unlock() + logger.Warnf("passthrough: RegisterBackingFd(%s): %s; disabling passthrough for this mount "+ + "(the ioctl needs CAP_SYS_ADMIN in the init user namespace — run the mount as root, "+ + "or use a mount broker that performs registrations node-side)", path, errno) + return nil, false + } + logger.Warnf("passthrough: RegisterBackingFd(%s): %s", path, errno) + return nil, false + } + return &ptBacking{path: path, f: f, backingID: id}, true +} + +// retire tears a backing down for good: unregisters the kernel registration, +// closes the local fd, and removes the staging file. Never reused by another +// inode — see the passthroughState doc comment. +func (p *passthroughState) retire(b *ptBacking) { + if errno := p.server.UnregisterBackingFd(b.backingID); errno != 0 { + logger.Warnf("passthrough: UnregisterBackingFd(%d): %s", b.backingID, errno) + } + _ = b.f.Close() + _ = os.Remove(b.path) +} + +func isWriteOpen(flags uint32) bool { + acc := flags & uint32(syscall.O_ACCMODE) + return acc == syscall.O_WRONLY || acc == syscall.O_RDWR +} + +// tryOpen sets up passthrough for a write-opened file that is EMPTY at open. +// It returns the kernel backing ID and true on success; callers then set +// FOPEN_PASSTHROUGH and OpenOut.BackingID. On any failure it returns false and +// the caller falls back to the normal (daemon) path — passthrough is purely an +// optimization. +// +// emptyAtOpen MUST be true only when the file has no pre-existing content the +// open can observe or extend: a fresh Create, or an Open with O_TRUNC, or a +// zero-length file. This is a correctness gate, not a heuristic. The backing +// staging file always starts empty and FOPEN_PASSTHROUGH diverts reads, writes +// AND mmap to it, so enabling it on a non-empty file would (a) serve reads as +// zeros/EOF instead of real content (read-modify-write corruption), and (b) +// make reconcile — which copies staging linearly from offset 0 — overwrite the +// file's real prefix with holes for O_APPEND / sparse / seek-write patterns. +func (p *passthroughState) tryOpen(ino Ino, fh uint64, flags uint32, emptyAtOpen bool) (int32, bool) { + if p == nil || !isWriteOpen(flags) || !emptyAtOpen { + return 0, false + } + // Never hand JuiceFS's internal/control files (.control, .stats, .config, + // ...) to the kernel via a backing file. Those inodes are served by the + // daemon's in-process handlers; passthrough would silently divert their + // I/O to a plain temp file, breaking the control protocol — e.g. the + // `juicefs checkpoint` verb, whose write/read on .control would go to the + // backing file and never reach the handler, hanging the command. + if vfs.IsSpecialNode(ino) { + return 0, false + } + // One passthrough writer per inode at a time. A second write-open while an + // earlier one is still open — or while its reconcile is in flight (during + // which the metadata size still reads 0, so emptyAtOpen looks true again) — + // would get its own empty backing whose linear reconcile overwrites the + // first's data. Reserve the inode; overlapping opens fall to the daemon + // path, which serializes correctly. Checked before any server call so the + // disabled latch and a drain-for-handover pause short-circuit cheaply. + p.mu.Lock() + if p.disabled || p.paused || p.busy[ino] > 0 { + p.mu.Unlock() + return 0, false + } + p.busy[ino]++ + p.mu.Unlock() + release := func() { + p.mu.Lock() + p.releaseBusyLocked(ino) + p.mu.Unlock() + } + + if !p.server.SupportsPassthrough() { + p.warnOne.Do(func() { + logger.Warnf("FUSE passthrough requested but not supported by the kernel; falling back") + }) + release() + return 0, false + } + b, ok := p.checkout() + if !ok { + release() + return 0, false + } + p.mu.Lock() + p.files[fh] = &ptFile{ino: ino, fh: fh, b: b} + p.mu.Unlock() + return b.backingID, true +} + +// releaseBusyLocked drops one busy reference for ino. Caller holds p.mu. +func (p *passthroughState) releaseBusyLocked(ino Ino) { + if n := p.busy[ino]; n <= 1 { + delete(p.busy, ino) + } else { + p.busy[ino] = n - 1 + } +} + +// waitInode blocks (bounded) until ino has no passthrough open or in-flight +// reconcile. Called at the start of Open so a reopen of a file this session +// just wrote via passthrough observes the reconciled state: until reconcile +// lands, the metadata size still reads 0 and a new write (passthrough OR +// daemon) would race the reconcile's linear copy and silently lose data. +// After the wait the file's size is authoritative, so tryOpen's emptyAtOpen +// gate correctly sends the reopen down the daemon path. +func (p *passthroughState) waitInode(ino Ino) { + if p == nil { + return + } + deadline := time.Now().Add(30 * time.Second) + for { + p.mu.Lock() + busy := p.busy[ino] > 0 + p.mu.Unlock() + if !busy { + return + } + if time.Now().After(deadline) { + logger.Warnf("passthrough: waited 30s for in-flight reconcile of ino %d; proceeding", ino) + return + } + time.Sleep(2 * time.Millisecond) + } +} + +// reconcile flushes a passthrough staging file back into JuiceFS slices, then +// tears down the backing registration. Called on release, before vfs.Release. +func (p *passthroughState) reconcile(ctx vfs.Context, v *vfs.VFS, fh uint64) { + if p == nil { + return + } + p.mu.Lock() + pf := p.files[fh] + if pf != nil { + delete(p.files, fh) + } + p.mu.Unlock() + if pf == nil { + return + } + // Free the inode for a new passthrough open only once its data has fully + // landed (or been preserved on failure) — held across the whole reconcile. + defer func() { + p.mu.Lock() + p.releaseBusyLocked(pf.ino) + p.mu.Unlock() + }() + // Fence for consistency points now that we know this is a passthrough + // release with data to land: the application's close(2) has already + // returned, but the data still lives only in the staging file until the + // copy below finishes. A checkpoint or commit that ran concurrently would + // flush+snapshot without these writes and publish a mid-copy (short) file. + // The external-flush counter lets those paths wait for in-flight + // reconciles first. Scoped past the pf==nil check so plain (non- + // passthrough) releases don't add spurious fence contention. + v.BeginExternalFlush() + defer v.EndExternalFlush() + // The kernel stops issuing passthrough I/O for this open once its release + // is processed, and reconcile runs from the RELEASE handler — after the + // application's last close, so no writes are in flight. The registration + // is retired for good once reconciled (see retire) rather than reused by + // a future open, which may belong to a different inode; read the staging + // content through a fresh path-open fd for a clean sequential pass + // (reading via the registered backing fd can return stale/partial data). + b := pf.b + done := false + defer func() { + if !done { + // Reconcile failed: the staging file is the ONLY copy of data whose + // close(2) already returned 0, so do NOT delete it — preserve it as + // an .orphan sibling for manual recovery and log loudly. Drop the + // kernel registration and the live fd so the backing isn't reused + // with stale data. + if errno := p.server.UnregisterBackingFd(b.backingID); errno != 0 { + logger.Warnf("passthrough: UnregisterBackingFd(%d): %s", b.backingID, errno) + } + _ = b.f.Close() + orphan := b.path + fmt.Sprintf(".orphan-%d", pf.ino) + if err := os.Rename(b.path, orphan); err != nil { + logger.Errorf("passthrough: reconcile of ino %d FAILED and staging %s could not be preserved: %s", pf.ino, b.path, err) + } else { + logger.Errorf("passthrough: reconcile of ino %d FAILED; unreconciled data preserved at %s", pf.ino, orphan) + } + } + }() + + pf.mu.Lock() + defer pf.mu.Unlock() + off, ok := p.copyStagingLocked(ctx, v, pf) + if !ok { + return + } + if e := v.Flush(ctx, pf.ino, fh, 0); e != 0 { + logger.Errorf("passthrough: reconcile flush ino %d: %s", pf.ino, e) + return + } + // Passthrough writes bypassed the daemon, so the kernel's cached size and + // page data for this inode are stale (size is still 0 from the empty + // create). Now that the slices + metadata are committed, invalidate both so + // readers in this mount session see the reconciled file (read-your-writes). + p.server.InodeNotify(uint64(pf.ino), -1, 0) // attributes (size/mtime) + p.server.InodeNotify(uint64(pf.ino), 0, int64(off)) // data range + // Data is safely in JuiceFS: retire the registration and staging file for + // good. No truncate needed — retire removes the file outright. + done = true + p.retire(b) +} + +// copyStagingLocked copies the full staging content of pf into JuiceFS slices +// via the normal writer path. Returns the byte count and false on any read or +// write error — a read error mid-copy must NOT be mistaken for end-of-file, +// or the caller would flush+commit a truncated file as if complete. Always a +// FULL copy from offset 0: passthrough writes land in the backing at +// arbitrary offsets, so an incremental "since last copy" scheme would miss +// overwrites of already-copied ranges. Caller holds pf.mu. +func (p *passthroughState) copyStagingLocked(ctx vfs.Context, v *vfs.VFS, pf *ptFile) (uint64, bool) { + rf, err := os.Open(pf.b.path) + if err != nil { + logger.Errorf("passthrough: reopen staging %s: %s", pf.b.path, err) + return 0, false + } + defer rf.Close() + // Pooled/accounted allocation: vfs.writer's and vfs/compact's memory + // backpressure (utils.AllocMemory()-store.UsedMemory()) only sees bytes + // allocated through utils.Alloc. A raw make() here would be invisible to + // that accounting, so concurrent reconciles could balloon RSS well past + // the configured buffer budget without the compactor/writer ever + // throttling in response. + buf := utils.Alloc(4 << 20) + defer utils.Free(buf) + var off uint64 + for { + n, err := rf.Read(buf) + if n > 0 { + // vfs.Write may retain the buffer until flush; give each chunk its + // own backing array so the next Read doesn't corrupt a pending slice. + chunk := make([]byte, n) + copy(chunk, buf[:n]) + if e := v.Write(ctx, pf.ino, chunk, off, pf.fh); e != 0 { + logger.Errorf("passthrough: staging copy write ino %d off %d: %s", pf.ino, off, e) + return off, false + } + off += uint64(n) + } + if err != nil { + if err != io.EOF { + logger.Errorf("passthrough: read staging %s at off %d: %s", pf.b.path, off, err) + return off, false + } + break + } + } + return off, true +} + +// fsync makes fsync(2)/fdatasync(2) honest for a passthrough open. The +// kernel diverts only read/write/mmap to the backing file — FSYNC still +// reaches the daemon, whose writer has no data for this fh, so plain +// vfs.Fsync would report success while every byte still sits in a local +// staging file that a crash before release would lose. Instead, copy the +// staging content into JuiceFS slices now (the open stays live and the +// backing stays registered; the release-time reconcile recopies and remains +// the authority on final content) and then flush the writer, giving the +// caller exactly the durability a non-passthrough fsync provides. +// +// Returns handled=false when fh is not a passthrough open (caller proceeds +// with the normal path). +func (p *passthroughState) fsync(ctx vfs.Context, v *vfs.VFS, fh uint64) (bool, syscall.Errno) { + if p == nil { + return false, 0 + } + p.mu.Lock() + pf := p.files[fh] + p.mu.Unlock() + if pf == nil { + return false, 0 + } + pf.mu.Lock() + defer pf.mu.Unlock() + // Fence: a checkpoint/commit racing this copy must wait for it, exactly + // as for a release-time reconcile, or it could snapshot a mid-copy state + // of a file the application believes it just made durable. + v.BeginExternalFlush() + defer v.EndExternalFlush() + if _, ok := p.copyStagingLocked(ctx, v, pf); !ok { + // The open is still live and release will retry the copy; report the + // failure so the application does not trust this fsync. + return true, syscall.EIO + } + if e := v.Fsync(ctx, pf.ino, 0, fh); e != 0 { + return true, e + } + return true, 0 +} + +// truncate mirrors a successful SETATTR size change onto the inode's live +// passthrough backing, if any. truncate(2)/ftruncate(2) reach the daemon — +// the kernel never diverts SETATTR to the backing — so without this the +// backing keeps its old length and diverges from the size the caller just +// set: reads through the passthrough fd see the old data/EOF, and the +// release-time reconcile (linear copy of the staging, authority on final +// content) would silently undo the truncate. +func (p *passthroughState) truncate(ino Ino, size uint64) { + if p == nil { + return + } + p.mu.Lock() + var pf *ptFile + for _, f := range p.files { + if f.ino == ino { + pf = f + break + } + } + p.mu.Unlock() + if pf == nil { + return + } + pf.mu.Lock() + defer pf.mu.Unlock() + if err := pf.b.f.Truncate(int64(size)); err != nil { + logger.Errorf("passthrough: mirror truncate ino %d to %d on %s: %s", ino, size, pf.b.path, err) + } +} + +// drain blocks new passthrough opens and waits (bounded) until no inode has +// a live passthrough open or in-flight reconcile. Used before a smooth +// upgrade (SIGHUP handover): a passthrough open's data exists only in this +// process's staging files, which the successor has no record of, so handing +// over while any are live silently loses every byte written through them. +// On timeout it re-enables passthrough and returns false — the caller must +// refuse the handover and keep serving. +func (p *passthroughState) drain(timeout time.Duration) bool { + p.mu.Lock() + p.paused = true + p.mu.Unlock() + deadline := time.Now().Add(timeout) + for { + p.mu.Lock() + n := len(p.busy) + p.mu.Unlock() + if n == 0 { + return true + } + if time.Now().After(deadline) { + p.mu.Lock() + p.paused = false + p.mu.Unlock() + logger.Warnf("passthrough: %d inode(s) still have live passthrough opens or reconciles after %s", n, timeout) + return false + } + time.Sleep(10 * time.Millisecond) + } +} diff --git a/pkg/fuse/passthrough_drain_test.go b/pkg/fuse/passthrough_drain_test.go new file mode 100644 index 000000000000..b2328e44e852 --- /dev/null +++ b/pkg/fuse/passthrough_drain_test.go @@ -0,0 +1,104 @@ +//go:build linux + +/* + * JuiceFS, Copyright 2026 Juicedata, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"). + */ + +package fuse + +import ( + "os" + "path/filepath" + "testing" + "time" +) + +// A paused state (drain for smooth-upgrade handover) must refuse new +// passthrough opens before touching the server — handing out a backing +// during a handover would strand its data in a staging file the successor +// has no record of. +func TestTryOpenRefusedWhilePaused(t *testing.T) { + p := &passthroughState{dir: t.TempDir(), files: make(map[uint64]*ptFile), busy: make(map[Ino]int), paused: true} + // nil server: reaching SupportsPassthrough/checkout would panic, proving + // the pause gate short-circuits first. + if id, ok := p.tryOpen(Ino(2), 1, 0x8002 /* O_RDWR|... */, true); ok || id != 0 { + t.Fatalf("tryOpen while paused = (%d,%v), want (0,false)", id, ok) + } + if len(p.busy) != 0 { + t.Fatalf("paused tryOpen leaked a busy reservation: %v", p.busy) + } +} + +// drain returns true immediately when nothing is in flight and leaves the +// state paused (the caller is about to hand the session over); with a live +// open it must time out, RE-ENABLE passthrough, and return false so the +// caller refuses the handover instead of losing the open's staging data. +func TestDrainForHandover(t *testing.T) { + p := &passthroughState{dir: t.TempDir(), files: make(map[uint64]*ptFile), busy: make(map[Ino]int)} + if !p.drain(time.Millisecond) { + t.Fatalf("drain with no passthrough opens should succeed") + } + if !p.paused { + t.Fatalf("successful drain must leave the state paused for the handover") + } + + p2 := &passthroughState{dir: t.TempDir(), files: make(map[uint64]*ptFile), busy: map[Ino]int{Ino(7): 1}} + start := time.Now() + if p2.drain(50 * time.Millisecond) { + t.Fatalf("drain with a live passthrough open must fail") + } + if time.Since(start) < 50*time.Millisecond { + t.Fatalf("drain returned before its deadline") + } + if p2.paused { + t.Fatalf("failed drain must re-enable passthrough (handover was refused)") + } +} + +// truncate must mirror a SETATTR size change onto the inode's live backing: +// the kernel never diverts SETATTR to the backing, so without the mirror the +// staging keeps its old length and the release-time reconcile would undo the +// truncate (and reads through the passthrough fd would see stale bytes). +func TestTruncateMirrorsOntoBacking(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "pool-1.tmp") + f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + t.Fatal(err) + } + defer f.Close() + if _, err := f.Write([]byte("0123456789")); err != nil { + t.Fatal(err) + } + p := &passthroughState{dir: dir, files: make(map[uint64]*ptFile), busy: make(map[Ino]int)} + pf := &ptFile{ino: Ino(42), fh: 9, b: &ptBacking{path: path, f: f}} + p.files[9] = pf + + p.truncate(Ino(42), 4) // shrink + if st, _ := os.Stat(path); st.Size() != 4 { + t.Fatalf("backing size after shrink = %d, want 4", st.Size()) + } + p.truncate(Ino(42), 16) // extend (sparse zeros) + if st, _ := os.Stat(path); st.Size() != 16 { + t.Fatalf("backing size after extend = %d, want 16", st.Size()) + } + // A size change for an inode with no live passthrough open is a no-op. + p.truncate(Ino(43), 1) + if st, _ := os.Stat(path); st.Size() != 16 { + t.Fatalf("truncate of unrelated inode disturbed the backing: %d", st.Size()) + } +} + +// fsync on a non-passthrough fh is not handled (the caller proceeds with the +// plain vfs path), including on a nil state. +func TestFsyncPassesThroughUnknownFh(t *testing.T) { + var nilState *passthroughState + if handled, _ := nilState.fsync(nil, nil, 1); handled { + t.Fatalf("nil state must not handle fsync") + } + p := &passthroughState{dir: t.TempDir(), files: make(map[uint64]*ptFile), busy: make(map[Ino]int)} + if handled, _ := p.fsync(nil, nil, 99); handled { + t.Fatalf("unknown fh must not be handled") + } +} diff --git a/pkg/fuse/passthrough_pool_test.go b/pkg/fuse/passthrough_pool_test.go new file mode 100644 index 000000000000..9392a8dee5cd --- /dev/null +++ b/pkg/fuse/passthrough_pool_test.go @@ -0,0 +1,66 @@ +/* + * JuiceFS, Copyright 2026 Juicedata, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fuse + +import ( + "os" + "path/filepath" + "testing" +) + +// TestPassthroughDisabledLatch: once registration hit a permanent error the +// state stops attempting registrations entirely — checkout must return false +// before touching the (nil) server or the filesystem. +func TestPassthroughDisabledLatch(t *testing.T) { + p := &passthroughState{dir: t.TempDir(), files: make(map[uint64]*ptFile), disabled: true} + if b, ok := p.checkout(); ok || b != nil { + t.Fatalf("checkout on disabled state = %v, %v; want nil, false", b, ok) + } + if p.seq != 0 { + t.Fatalf("disabled checkout still allocated a staging sequence") + } +} + +// TestPassthroughStagingIsolation: a fresh state carves a private per-process +// subdir under base and MUST NOT touch files belonging to another mount that +// shares base — the old shared-dir sweep silently deleted a live mount's +// in-use staging (data loss). It also gives distinct states distinct dirs. +func TestPassthroughStagingIsolation(t *testing.T) { + base := t.TempDir() + // A sibling mount's live staging file sitting directly in base. + sibling := filepath.Join(base, "pool-9.tmp") + if err := os.WriteFile(sibling, []byte("live data"), 0600); err != nil { + t.Fatal(err) + } + p1 := newPassthroughState(nil, base) + p2 := newPassthroughState(nil, base) + + if _, err := os.Stat(sibling); err != nil { + t.Fatalf("sibling mount's staging was disturbed: %v", err) + } + if p1.dir == base || p2.dir == base { + t.Fatalf("state did not isolate into a subdir: p1=%q p2=%q base=%q", p1.dir, p2.dir, base) + } + if filepath.Dir(p1.dir) != base { + t.Fatalf("subdir %q not under base %q", p1.dir, base) + } + // Even within one process, two states must not share a dir (else their + // pool-N.tmp names would collide). + if p1.dir == p2.dir { + t.Fatalf("two states shared staging dir %q", p1.dir) + } +} diff --git a/pkg/fuse/passthrough_special_test.go b/pkg/fuse/passthrough_special_test.go new file mode 100644 index 000000000000..ce1a20c2302c --- /dev/null +++ b/pkg/fuse/passthrough_special_test.go @@ -0,0 +1,34 @@ +//go:build linux + +/* + * JuiceFS, Copyright 2026 Juicedata, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"). + */ + +package fuse + +import ( + "testing" + + "github.com/juicedata/juicefs/pkg/vfs" +) + +// tryOpen must refuse passthrough for JuiceFS internal/control inodes: a +// backing file on .control would divert the control protocol (e.g. the +// checkpoint verb) and hang. A nil passthroughState short-circuits before +// any kernel calls, so this exercises the special-node guard in isolation. +func TestTryOpenSkipsSpecialNodes(t *testing.T) { + var p *passthroughState // nil: guard order still returns (0,false) + // Sanity: control inode is classified special. + control := vfs.Ino(0x7FFFFFFF00000002) + if !vfs.IsSpecialNode(control) { + t.Fatalf("expected %d to be a special node", control) + } + if id, ok := p.tryOpen(control, 1, 0x8002, true); ok || id != 0 { + t.Fatalf("tryOpen(special) = (%d,%v), want (0,false)", id, ok) + } + // A regular inode with a read-only open is also refused (not a write). + if _, ok := p.tryOpen(vfs.Ino(2), 1, 0, true); ok { + t.Fatalf("tryOpen(regular, read-only) should be false") + } +} diff --git a/pkg/vfs/vfs.go b/pkg/vfs/vfs.go index 45b557c53de6..9c62e62cd4e1 100644 --- a/pkg/vfs/vfs.go +++ b/pkg/vfs/vfs.go @@ -24,6 +24,7 @@ import ( "runtime" "sort" "sync" + "sync/atomic" "syscall" "time" @@ -79,6 +80,8 @@ type FuseOptions struct { DisableReadDirPlus bool `json:",omitempty"` EnableReadDirPlusAuto bool EnableWriteback bool + EnablePassthrough bool `json:",omitempty"` + MaxStackDepth int `json:",omitempty"` EnableIoctl bool `json:",omitempty"` DontUmask bool OtherCaps uint32 @@ -1239,9 +1242,27 @@ type VFS struct { modM sync.Mutex modifiedAt map[Ino]time.Time + // externalFlushes counts out-of-band write flows (e.g. FUSE passthrough + // reconcile at release) whose Write calls are not yet visible to + // FlushAll. Consistency points (checkpoint, commit) must wait for it to + // reach zero before flushing, or a file whose close(2) already returned + // can be snapshotted mid-copy. See ExternalFlushes. + externalFlushes int64 + registry *prometheus.Registry } +// BeginExternalFlush marks an out-of-band write flow (such as a passthrough +// staging-file reconcile) as in flight. Call before the flow's first Write so +// a concurrent consistency point cannot observe zero while data is pending. +func (v *VFS) BeginExternalFlush() { atomic.AddInt64(&v.externalFlushes, 1) } + +// EndExternalFlush marks the flow complete (its writes flushed or failed). +func (v *VFS) EndExternalFlush() { atomic.AddInt64(&v.externalFlushes, -1) } + +// ExternalFlushes reports the number of out-of-band write flows in flight. +func (v *VFS) ExternalFlushes() int64 { return atomic.LoadInt64(&v.externalFlushes) } + func NewVFS(conf *Config, m meta.Meta, store chunk.ChunkStore, registerer prometheus.Registerer, registry *prometheus.Registry) *VFS { reader := NewDataReader(conf, m, store) writer := NewDataWriter(conf, m, store, reader) @@ -1349,6 +1370,18 @@ func initVFSMetrics(v *VFS, writer DataWriter, reader DataReader, registerer pro return float64(len(v.handles)) }) _ = registerer.Register(handlersGause) + // Named to contain "staging_blocks" ON PURPOSE: durability watchers that + // sum the staging gauges from .stats (e.g. drain-before-commit clients) + // then automatically cover in-flight passthrough reconciles too — a + // closed file's data is "staged" in its backing file until the reconcile + // lands it in the writer, invisible to the writeback gauges. + externalGauge := prometheus.NewGaugeFunc(prometheus.GaugeOpts{ + Name: "passthrough_staging_blocks", + Help: "out-of-band flush flows (passthrough staging reconciles) not yet visible to FlushAll.", + }, func() float64 { + return float64(v.ExternalFlushes()) + }) + _ = registerer.Register(externalGauge) InitMemoryBufferMetrics(writer, reader, registerer) }