(ctx context.Context, cmd Cmder)
| 834 | } |
| 835 | |
| 836 | func (c *baseClient) process(ctx context.Context, cmd Cmder) error { |
| 837 | // Start measuring total operation duration (includes all retries) |
| 838 | // Only call time.Now() if operation duration callback is set to avoid overhead |
| 839 | var operationStart time.Time |
| 840 | opDurationCallback := otel.GetOperationDurationCallback() |
| 841 | if opDurationCallback != nil { |
| 842 | operationStart = time.Now() |
| 843 | } |
| 844 | var lastConn *pool.Conn |
| 845 | |
| 846 | var lastErr error |
| 847 | totalAttempts := 0 |
| 848 | for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ { |
| 849 | totalAttempts++ |
| 850 | attempt := attempt |
| 851 | |
| 852 | retry, cn, err := c._process(ctx, cmd, attempt) |
| 853 | if cn != nil { |
| 854 | lastConn = cn |
| 855 | } |
| 856 | // Don't retry if command explicitly disables retries (e.g., RawWriteToCmd |
| 857 | // which writes directly to an io.Writer and cannot undo partial writes) |
| 858 | if err == nil || !retry || cmd.NoRetry() { |
| 859 | // Record total operation duration |
| 860 | if opDurationCallback != nil { |
| 861 | operationDuration := time.Since(operationStart) |
| 862 | opDurationCallback(ctx, operationDuration, cmd, totalAttempts, err, lastConn, c.opt.DB) |
| 863 | } |
| 864 | |
| 865 | if err != nil { |
| 866 | if errorCallback := pool.GetMetricErrorCallback(); errorCallback != nil { |
| 867 | errorType, statusCode, isInternal := classifyCommandError(err) |
| 868 | errorCallback(ctx, errorType, lastConn, statusCode, isInternal, totalAttempts-1) |
| 869 | } |
| 870 | } |
| 871 | return err |
| 872 | } |
| 873 | |
| 874 | lastErr = err |
| 875 | } |
| 876 | |
| 877 | // Record failed operation after all retries |
| 878 | if opDurationCallback != nil { |
| 879 | operationDuration := time.Since(operationStart) |
| 880 | opDurationCallback(ctx, operationDuration, cmd, totalAttempts, lastErr, lastConn, c.opt.DB) |
| 881 | } |
| 882 | |
| 883 | // Record error metric for exhausted retries |
| 884 | if errorCallback := pool.GetMetricErrorCallback(); errorCallback != nil { |
| 885 | errorType, statusCode, isInternal := classifyCommandError(lastErr) |
| 886 | errorCallback(ctx, errorType, lastConn, statusCode, isInternal, totalAttempts-1) |
| 887 | } |
| 888 | |
| 889 | return lastErr |
| 890 | } |
| 891 | |
| 892 | // classifyCommandError classifies an error for metrics reporting. |
| 893 | // Returns: errorType, statusCode, isInternal |
no test coverage detected