55 "os"
66 "strconv"
77 "strings"
8+ "time"
89)
910
1011// Config holds application configuration
@@ -25,6 +26,44 @@ type Config struct {
2526 Notifications NotificationsConfig
2627 MQTT MQTTConfig
2728 SES SESConfig
29+ Monitoring MonitoringConfig
30+ Minting MintingConfig
31+ }
32+
33+ // MonitoringConfig holds monitoring-module settings.
34+ type MonitoringConfig struct {
35+ SLA SLAConfig
36+ }
37+
38+ // SLAConfig holds the SLA thresholds the performance analytics service
39+ // benchmarks observed monitoring data against. A zero threshold means
40+ // "not configured": that metric is reported but not judged, so an unset SLA
41+ // can never manufacture a breach.
42+ type SLAConfig struct {
43+ // Latency ceilings, in milliseconds.
44+ LatencyP50Ms float64
45+ LatencyP95Ms float64
46+ LatencyP99Ms float64
47+ // MaxErrorRate is a ceiling expressed as a fraction in [0,1] (0.01 = 1%).
48+ MaxErrorRate float64
49+ // MinUptime is a floor expressed as a fraction in [0,1] (0.999 = 99.9%).
50+ MinUptime float64
51+ // Names of the stored metrics the observed latency and error rate are
52+ // read from.
53+ LatencyMetricName string
54+ ErrorRateMetricName string
55+ }
56+
57+ // MintingConfig holds the retry policy for Soroban carbon-credit minting.
58+ // Backoff grows exponentially from BaseBackoff, doubling per attempt, capped
59+ // at MaxBackoff, with jitter applied to each delay.
60+ type MintingConfig struct {
61+ MaxAttempts int
62+ BaseBackoff time.Duration
63+ MaxBackoff time.Duration
64+ // JitterFactor is the fraction of the computed delay that is randomised,
65+ // in [0,1]. 0.2 means the actual delay lands in [0.8d, 1.0d].
66+ JitterFactor float64
2867}
2968
3069// ElasticsearchConfig holds configuration for Elasticsearch
@@ -295,6 +334,23 @@ func Load() (*Config, error) {
295334 TwilioAuthToken : os .Getenv ("TWILIO_AUTH_TOKEN" ),
296335 TwilioFromNumber : os .Getenv ("TWILIO_FROM_NUMBER" ),
297336 },
337+ Monitoring : MonitoringConfig {
338+ SLA : SLAConfig {
339+ LatencyP50Ms : getFloatOrDefault ("SLA_LATENCY_P50_MS" , 100 ),
340+ LatencyP95Ms : getFloatOrDefault ("SLA_LATENCY_P95_MS" , 300 ),
341+ LatencyP99Ms : getFloatOrDefault ("SLA_LATENCY_P99_MS" , 750 ),
342+ MaxErrorRate : getFloatOrDefault ("SLA_MAX_ERROR_RATE" , 0.01 ),
343+ MinUptime : getFloatOrDefault ("SLA_MIN_UPTIME" , 0.99 ),
344+ LatencyMetricName : getEnvOrDefault ("SLA_LATENCY_METRIC" , "api_request_latency_ms" ),
345+ ErrorRateMetricName : getEnvOrDefault ("SLA_ERROR_RATE_METRIC" , "api_error_rate" ),
346+ },
347+ },
348+ Minting : MintingConfig {
349+ MaxAttempts : getIntOrDefault ("MINTING_MAX_ATTEMPTS" , 3 ),
350+ BaseBackoff : getDurationOrDefault ("MINTING_BACKOFF_BASE" , 2 * time .Second ),
351+ MaxBackoff : getDurationOrDefault ("MINTING_BACKOFF_MAX" , 60 * time .Second ),
352+ JitterFactor : getFloatOrDefault ("MINTING_BACKOFF_JITTER" , 0.2 ),
353+ },
298354 }, nil
299355}
300356
@@ -316,3 +372,31 @@ func getIntOrDefault(key string, defaultVal int) int {
316372 }
317373 return parsed
318374}
375+
376+ // getFloatOrDefault reads a float64 from the environment, falling back to
377+ // defaultVal when unset or unparsable.
378+ func getFloatOrDefault (key string , defaultVal float64 ) float64 {
379+ v := os .Getenv (key )
380+ if v == "" {
381+ return defaultVal
382+ }
383+ parsed , err := strconv .ParseFloat (v , 64 )
384+ if err != nil {
385+ return defaultVal
386+ }
387+ return parsed
388+ }
389+
390+ // getDurationOrDefault reads a Go duration string (e.g. "2s", "500ms") from
391+ // the environment, falling back to defaultVal when unset or unparsable.
392+ func getDurationOrDefault (key string , defaultVal time.Duration ) time.Duration {
393+ v := os .Getenv (key )
394+ if v == "" {
395+ return defaultVal
396+ }
397+ parsed , err := time .ParseDuration (v )
398+ if err != nil {
399+ return defaultVal
400+ }
401+ return parsed
402+ }
0 commit comments