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