Skip to content

WithClientConfig overrides default ClientConfig values with zero values. #220

@iv-anas

Description

@iv-anas

While using the Go Typesense client (v3.2.0), I noticed a potential configuration issue related to the functional options pattern and default configuration handling.

In NewClient, a default ClientConfig is initialized with sensible default values:

c := &Client{apiConfig: &ClientConfig{
    RetryInterval:             defaultRetryInterval,
    HealthcheckInterval:       defaultHealthcheckInterval,
    ConnectionTimeout:         defaultConnectionTimeout,
    CircuitBreakerName:        defaultCircuitBreakerName,
    CircuitBreakerMaxRequests: circuit.DefaultGoBreakerMaxRequests,
    CircuitBreakerInterval:    circuit.DefaultGoBreakerInterval,
    CircuitBreakerTimeout:     circuit.DefaultGoBreakerTimeout,
    CircuitBreakerReadyToTrip: circuit.DefaultReadyToTrip,
}}

However, when using the WithClientConfig option:

client := typesense.NewClient(
    typesense.WithClientConfig(&typesense.ClientConfig{
        ServerURL: cfg.ServerURL,
        APIKey:    cfg.APIKey,
    }),
)

The implementation of WithClientConfig assigns all fields from the provided config directly:

func WithClientConfig(config *ClientConfig) ClientOption {
    return func(c *Client) {
        c.apiConfig.ServerURL = config.ServerURL
        c.apiConfig.NearestNode = config.NearestNode
        c.apiConfig.Nodes = config.Nodes
        c.apiConfig.NumRetries = config.NumRetries
        c.apiConfig.RetryInterval = config.RetryInterval
        c.apiConfig.HealthcheckInterval = config.HealthcheckInterval
        c.apiConfig.APIKey = config.APIKey
        c.apiConfig.ConnectionTimeout = config.ConnectionTimeout
        c.apiConfig.CircuitBreakerName = config.CircuitBreakerName
        c.apiConfig.CircuitBreakerMaxRequests = config.CircuitBreakerMaxRequests
        c.apiConfig.CircuitBreakerInterval = config.CircuitBreakerInterval
        c.apiConfig.CircuitBreakerTimeout = config.CircuitBreakerTimeout
        c.apiConfig.CircuitBreakerReadyToTrip = config.CircuitBreakerReadyToTrip
        c.apiConfig.CircuitBreakerOnStateChange = config.CircuitBreakerOnStateChange
    }
}

If only a subset of fields is provided (e.g., only ServerURL and APIKey), the remaining fields are Go zero values and overwrite the defaults set in NewClient.

// implement option pattern
	for _, opt := range opts {
		opt(c)
	}

Question / Clarification

1. Is it the intended behavior that WithClientConfig fully overrides all defaults?
2. Specifically, should NumRetries (and other retry-related fields) have a non-zero default if not explicitly provided?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions