QuickDID is a high-performance AT Protocol identity resolution service written in Rust. It provides handle-to-DID resolution with Redis-backed caching and queue processing.

feature: add configurable bind address for StatsD UDP socket

+41 -8
+1
CLAUDE.md
··· 142 142 ### Optional - Metrics 143 143 - `METRICS_ADAPTER`: Metrics adapter type - 'noop' or 'statsd' (default: noop) 144 144 - `METRICS_STATSD_HOST`: StatsD host and port (required when METRICS_ADAPTER=statsd, e.g., localhost:8125) 145 + - `METRICS_STATSD_BIND`: Bind address for StatsD UDP socket (default: [::]:0 for IPv6, can use 0.0.0.0:0 for IPv4) 145 146 - `METRICS_PREFIX`: Prefix for all metrics (default: quickdid) 146 147 - `METRICS_TAGS`: Comma-separated tags (e.g., env:prod,service:quickdid) 147 148
+1
README.md
··· 135 135 #### Metrics 136 136 - `METRICS_ADAPTER`: Metrics adapter type - 'noop' or 'statsd' (default: noop) 137 137 - `METRICS_STATSD_HOST`: StatsD host and port (required when METRICS_ADAPTER=statsd) 138 + - `METRICS_STATSD_BIND`: Bind address for StatsD UDP socket (default: [::]:0 for IPv6, can use 0.0.0.0:0 for IPv4) 138 139 - `METRICS_PREFIX`: Prefix for all metrics (default: quickdid) 139 140 - `METRICS_TAGS`: Comma-separated tags (e.g., env:prod,service:quickdid) 140 141
+3
src/bin/quickdid.rs
··· 135 135 println!( 136 136 " METRICS_STATSD_HOST StatsD host when using statsd adapter (e.g., localhost:8125)" 137 137 ); 138 + println!( 139 + " METRICS_STATSD_BIND Bind address for StatsD UDP socket (default: [::]:0)" 140 + ); 138 141 println!(" METRICS_PREFIX Prefix for all metrics (default: quickdid)"); 139 142 println!( 140 143 " METRICS_TAGS Default tags for metrics (comma-separated key:value pairs)"
+5
src/config.rs
··· 220 220 /// Required when metrics_adapter is "statsd" 221 221 pub metrics_statsd_host: Option<String>, 222 222 223 + /// Bind address for StatsD UDP socket (e.g., "0.0.0.0:0" for IPv4 or "[::]:0" for IPv6) 224 + /// Default: "[::]:0" (IPv6 any address, random port) 225 + pub metrics_statsd_bind: String, 226 + 223 227 /// Metrics prefix for all metrics (e.g., "quickdid") 224 228 /// Default: "quickdid" 225 229 pub metrics_prefix: String, ··· 331 335 cache_control_header: None, // Will be calculated below 332 336 metrics_adapter: get_env_or_default("METRICS_ADAPTER", Some("noop")).unwrap(), 333 337 metrics_statsd_host: get_env_or_default("METRICS_STATSD_HOST", None), 338 + metrics_statsd_bind: get_env_or_default("METRICS_STATSD_BIND", Some("[::]:0")).unwrap(), 334 339 metrics_prefix: get_env_or_default("METRICS_PREFIX", Some("quickdid")).unwrap(), 335 340 metrics_tags: get_env_or_default("METRICS_TAGS", None), 336 341 proactive_refresh_enabled: parse_env("PROACTIVE_REFRESH_ENABLED", false)?,
+31 -8
src/metrics.rs
··· 92 92 impl StatsdMetricsPublisher { 93 93 /// Create a new StatsdMetricsPublisher with default configuration 94 94 pub fn new(host: &str, prefix: &str) -> Result<Self, Box<dyn std::error::Error>> { 95 - Self::new_with_tags(host, prefix, vec![]) 95 + Self::new_with_bind(host, prefix, "[::]:0") 96 + } 97 + 98 + /// Create a new StatsdMetricsPublisher with custom bind address 99 + pub fn new_with_bind( 100 + host: &str, 101 + prefix: &str, 102 + bind_addr: &str, 103 + ) -> Result<Self, Box<dyn std::error::Error>> { 104 + Self::new_with_bind_and_tags(host, prefix, bind_addr, vec![]) 96 105 } 97 106 98 107 /// Create a new StatsdMetricsPublisher with default tags ··· 101 110 prefix: &str, 102 111 default_tags: Vec<(String, String)>, 103 112 ) -> Result<Self, Box<dyn std::error::Error>> { 113 + Self::new_with_bind_and_tags(host, prefix, "[::]:0", default_tags) 114 + } 115 + 116 + /// Create a new StatsdMetricsPublisher with custom bind address and tags 117 + pub fn new_with_bind_and_tags( 118 + host: &str, 119 + prefix: &str, 120 + bind_addr: &str, 121 + default_tags: Vec<(String, String)>, 122 + ) -> Result<Self, Box<dyn std::error::Error>> { 104 123 tracing::info!( 105 - "Creating StatsdMetricsPublisher: host={}, prefix={}, tags={:?}", 124 + "Creating StatsdMetricsPublisher: host={}, prefix={}, bind={}, tags={:?}", 106 125 host, 107 126 prefix, 127 + bind_addr, 108 128 default_tags 109 129 ); 110 130 111 - // let socket = UdpSocket::bind("0.0.0.0:0")?; 112 - let socket = UdpSocket::bind("[::0]:0")?; 131 + let socket = UdpSocket::bind(bind_addr)?; 113 132 socket.set_nonblocking(true)?; 114 133 115 134 let buffered_sink = BufferedUdpMetricSink::from(host, socket)?; ··· 120 139 .build(buffered_sink); 121 140 let client = StatsdClient::from_sink(prefix, queuing_sink); 122 141 123 - tracing::info!("StatsdMetricsPublisher created successfully"); 142 + tracing::info!("StatsdMetricsPublisher created successfully with bind address: {}", bind_addr); 124 143 Ok(Self { 125 144 client, 126 145 default_tags, ··· 310 329 vec![] 311 330 }; 312 331 313 - let publisher = 314 - StatsdMetricsPublisher::new_with_tags(host, &config.metrics_prefix, default_tags) 315 - .map_err(|e| MetricsError::CreationFailed(e.to_string()))?; 332 + let publisher = StatsdMetricsPublisher::new_with_bind_and_tags( 333 + host, 334 + &config.metrics_prefix, 335 + &config.metrics_statsd_bind, 336 + default_tags, 337 + ) 338 + .map_err(|e| MetricsError::CreationFailed(e.to_string()))?; 316 339 317 340 Ok(Arc::new(publisher)) 318 341 }