diff --git a/weed/wdclient/filer_client.go b/weed/wdclient/filer_client.go index 98f1150144..55b3204f8f 100644 --- a/weed/wdclient/filer_client.go +++ b/weed/wdclient/filer_client.go @@ -792,7 +792,7 @@ func (p *filerVolumeProvider) LookupVolumeIds(ctx context.Context, volumeIds []s // surface its own DeadlineExceeded a tick before ctx.Err() // becomes visible, so reinterpret only deadline errors this way ctxErr := ctx.Err() - if ctxErr == nil && status.Code(err) == codes.DeadlineExceeded { + if ctxErr == nil && isDeadlineExceededErr(err) { if dl, ok := ctx.Deadline(); ok && !dl.After(time.Now()) { ctxErr = context.DeadlineExceeded } diff --git a/weed/wdclient/filer_client_test.go b/weed/wdclient/filer_client_test.go index f1ccc59ec4..1bfa56223f 100644 --- a/weed/wdclient/filer_client_test.go +++ b/weed/wdclient/filer_client_test.go @@ -309,6 +309,14 @@ func startStallingFiler(t *testing.T) pb.ServerAddress { return pb.ServerAddress(fmt.Sprintf("127.0.0.1:0.%s", port)) } +// lapsedDeadlineCtx mimics the moment after a caller's deadline has passed but +// before the runtime marks the context done. +type lapsedDeadlineCtx struct{ context.Context } + +func (lapsedDeadlineCtx) Deadline() (time.Time, bool) { return time.Now().Add(-time.Second), true } +func (lapsedDeadlineCtx) Done() <-chan struct{} { return nil } +func (lapsedDeadlineCtx) Err() error { return nil } + // A caller giving up must not count as a filer failure, the filer's own timeout must. func TestLookupVolumeIdsCountsOnlyFilerFailures(t *testing.T) { tests := []struct { @@ -338,6 +346,15 @@ func TestLookupVolumeIdsCountsOnlyFilerFailures(t *testing.T) { wantErr: context.DeadlineExceeded, wantFailures: 0, }, + { + name: "caller deadline already elapsed", + callerCtx: func() (context.Context, context.CancelFunc) { + return lapsedDeadlineCtx{context.Background()}, func() {} + }, + grpcTimeout: 5 * time.Second, + wantErr: context.DeadlineExceeded, + wantFailures: 0, + }, { name: "filer timeout", callerCtx: func() (context.Context, context.CancelFunc) { diff --git a/weed/wdclient/masterclient.go b/weed/wdclient/masterclient.go index 5e0e0134e2..91421d203c 100644 --- a/weed/wdclient/masterclient.go +++ b/weed/wdclient/masterclient.go @@ -44,6 +44,17 @@ func isCanceledErr(err error) bool { return false } +func isDeadlineExceededErr(err error) bool { + if err == nil { + return false + } + if errors.Is(err, context.DeadlineExceeded) { + return true + } + statusErr, ok := status.FromError(err) + return ok && statusErr.Code() == codes.DeadlineExceeded +} + // LookupVolumeIds queries the master for volume locations (fallback when cache misses). // Returns partial results with aggregated errors for volumes that failed. // Retries on codes.Unavailable (e.g. master warming up after restart) with backoff.