package externalprobe import ( "context" "errors" "regexp" "time" "github.com/jackc/pgx/v5/pgxpool" ) var ( serviceNamePattern = regexp.MustCompile(`^[a-z][a-z0-9-]{1,31}$`) dependencyPattern = regexp.MustCompile(`^[a-z][a-z0-9_]{1,63}$`) ) func PostgresObserver(pool *pgxpool.Pool, serviceName string) (Observer, error) { if pool == nil || !serviceNamePattern.MatchString(serviceName) { return nil, errors.New("external probe database and service name are required") } return func(ctx context.Context, observation Observation) error { if !dependencyPattern.MatchString(observation.Dependency) { return errors.New("external probe dependency name is invalid") } if observation.Interval <= 0 { return errors.New("external probe interval is required") } latencyMS := observation.Latency.Milliseconds() if latencyMS < 0 { latencyMS = 0 } intervalMS := observation.Interval.Milliseconds() if intervalMS < 1 { intervalMS = 1 } checkedAt := observation.CheckedAt.UTC() if checkedAt.IsZero() { checkedAt = time.Now().UTC() } status := "AVAILABLE" if !observation.Available { status = "UNAVAILABLE" if observation.ErrorCode == "" { observation.ErrorCode = "PROBE_FAILED" } } _, err := pool.Exec(ctx, ` INSERT INTO external_dependency_probe_state(service_name,dependency,status,checked_at,last_success_at,last_failure_at,last_error_code,latency_ms,probe_interval_ms,consecutive_failures) VALUES($1,$2,$3::varchar,$4::timestamptz,CASE WHEN $3::varchar='AVAILABLE' THEN $4::timestamptz END,CASE WHEN $3::varchar='UNAVAILABLE' THEN $4::timestamptz END,NULLIF($5::varchar,''),$6,$7,CASE WHEN $3::varchar='UNAVAILABLE' THEN 1 ELSE 0 END) ON CONFLICT(service_name,dependency) DO UPDATE SET status=EXCLUDED.status, checked_at=EXCLUDED.checked_at, last_success_at=CASE WHEN EXCLUDED.status='AVAILABLE' THEN EXCLUDED.checked_at ELSE external_dependency_probe_state.last_success_at END, last_failure_at=CASE WHEN EXCLUDED.status='UNAVAILABLE' THEN EXCLUDED.checked_at ELSE external_dependency_probe_state.last_failure_at END, last_error_code=EXCLUDED.last_error_code, latency_ms=EXCLUDED.latency_ms, probe_interval_ms=EXCLUDED.probe_interval_ms, consecutive_failures=CASE WHEN EXCLUDED.status='AVAILABLE' THEN 0 ELSE external_dependency_probe_state.consecutive_failures+1 END, updated_at=now()`, serviceName, observation.Dependency, status, checkedAt, observation.ErrorCode, latencyMS, intervalMS) return err }, nil } func ReconcilePostgresState(ctx context.Context, pool *pgxpool.Pool, serviceName string, dependencies []string) error { if pool == nil || !serviceNamePattern.MatchString(serviceName) { return errors.New("external probe database and service name are required") } for _, dependency := range dependencies { if !dependencyPattern.MatchString(dependency) { return errors.New("external probe dependency name is invalid") } } _, err := pool.Exec(ctx, `DELETE FROM external_dependency_probe_state WHERE service_name=$1 AND NOT (dependency=ANY(COALESCE($2::text[],ARRAY[]::text[])))`, serviceName, dependencies) return err }