From ee6054700f040977184db6e0a500b324f8a6c4cf Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 25 Nov 2025 20:19:01 -0800 Subject: [PATCH] Track current active filer in FilerClient for better HA Add GetCurrentFiler() method to FilerClient that returns the currently active filer based on the filerIndex which is updated on successful operations. This provides better availability than always using the first filer. Changes: - Add FilerClient.GetCurrentFiler() method that returns current active filer - Update S3ApiServer.getFilerAddress() to use FilerClient's current filer - Add fallback to first filer if FilerClient not yet initialized - Document IAM limitation (doesn't have FilerClient access) Benefits: - Single-filer operations (URLs, ReadFilerConf, etc.) now use the currently active/healthy filer - Better distribution and failover behavior - FilerClient's round-robin and health tracking automatically determines which filer to use --- weed/iamapi/iamapi_server.go | 3 ++- weed/s3api/s3api_server.go | 10 +++++++--- weed/wdclient/filer_client.go | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/weed/iamapi/iamapi_server.go b/weed/iamapi/iamapi_server.go index 11005d00c..cb6712f29 100644 --- a/weed/iamapi/iamapi_server.go +++ b/weed/iamapi/iamapi_server.go @@ -39,7 +39,8 @@ type IamS3ApiConfigure struct { } // getFilerAddress returns the current filer address to use -// Returns the first filer for single-address operations +// Note: IAM operations don't have access to FilerClient's current filer tracking +// Returns the first filer as fallback - consider adding FilerClient to IamS3ApiConfigure func (iama *IamS3ApiConfigure) getFilerAddress() pb.ServerAddress { if len(iama.option.Filers) > 0 { return iama.option.Filers[0] diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index 4b658525a..d29bf5ff2 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -185,10 +185,14 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl return s3ApiServer, nil } -// getFilerAddress returns the current filer address to use -// For operations that need a single address, returns the first one -// The underlying FilerClient handles failover automatically +// getFilerAddress returns the current active filer address +// Uses FilerClient's tracked current filer which is updated on successful operations +// This provides better availability than always using the first filer func (s3a *S3ApiServer) getFilerAddress() pb.ServerAddress { + if s3a.filerClient != nil { + return s3a.filerClient.GetCurrentFiler() + } + // Fallback to first filer if filerClient not initialized if len(s3a.option.Filers) > 0 { return s3a.option.Filers[0] } diff --git a/weed/wdclient/filer_client.go b/weed/wdclient/filer_client.go index 820a346c5..7e6b0b07a 100644 --- a/weed/wdclient/filer_client.go +++ b/weed/wdclient/filer_client.go @@ -184,6 +184,26 @@ func NewFilerClient(filerAddresses []pb.ServerAddress, grpcDialOption grpc.DialO return fc } +// GetCurrentFiler returns the currently active filer address +// This is the filer that was last successfully used or the one indicated by round-robin +// Returns empty string if no filers are configured +func (fc *FilerClient) GetCurrentFiler() pb.ServerAddress { + fc.filerAddressesMu.RLock() + defer fc.filerAddressesMu.RUnlock() + + if len(fc.filerAddresses) == 0 { + return "" + } + + // Get current index (atomically updated on successful operations) + index := atomic.LoadInt32(&fc.filerIndex) + if index >= int32(len(fc.filerAddresses)) { + index = 0 + } + + return fc.filerAddresses[index] +} + // Close stops the filer discovery goroutine if running func (fc *FilerClient) Close() { if fc.stopDiscovery != nil {