package main import ( "context" "log/slog" "os" "os/signal" "syscall" "time" "aigateway.local/core/internal/audit" "aigateway.local/core/internal/platform/config" "aigateway.local/core/internal/platform/database" ) func main() { logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo})) cfg, err := config.Load() if err != nil { logger.Error("invalid configuration", "error", err) os.Exit(1) } if cfg.Database.URL == "" { logger.Error("DATABASE_URL is required") os.Exit(1) } ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() db, err := database.Open(ctx, cfg.Database) if err != nil { logger.Error("database initialization failed", "error", err) os.Exit(1) } defer db.Close() maintenance := audit.NewMaintenance(db, cfg.Audit.Retention, cfg.Audit.UsageRetention, cfg.Audit.TraceRetention, cfg.Audit.PartitionMonthsAhead) ticker := time.NewTicker(cfg.Audit.MaintenanceInterval) defer ticker.Stop() for { result, runErr := maintenance.Run(ctx, time.Now()) if runErr != nil && ctx.Err() == nil { logger.Error("audit maintenance failed", "error", runErr) } else if runErr == nil { logger.Info("audit maintenance complete", "created_partitions", result.CreatedPartitions, "dropped_partitions", result.DroppedPartitions, "deleted_audit_rows", result.DeletedAuditRows, "deleted_usage_rows", result.DeletedUsageRows, "deleted_trace_rows", result.DeletedTraceRows) } // 清理工具限流固定窗口:保留 2 天,防止 tool_rate_usage 无限膨胀。 if tag, cleanupErr := db.Exec(ctx, `DELETE FROM gateway.tool_rate_usage WHERE window_start < clock_timestamp()-interval '2 days'`); cleanupErr != nil { logger.Warn("tool rate window cleanup failed", "error", cleanupErr) } else if tag.RowsAffected() > 0 { logger.Info("tool rate windows cleaned", "rows", tag.RowsAffected()) } select { case <-ctx.Done(): logger.Info("maintenance worker stopped") return case <-ticker.C: } } }