Database migration #3

Merged
robin merged 5 commits from dev into main 2026-07-20 21:58:59 +02:00
8 changed files with 32 additions and 26 deletions
Showing only changes of commit c623e02b6c - Show all commits
+12 -12
View File
@@ -24,7 +24,7 @@ func main() {
serviceVersion := "0.1.0"
if err := run(serviceName, serviceVersion); err != nil {
slog.Error("Service crashed")
slog.Error("service crashed")
os.Exit(1)
}
}
@@ -33,7 +33,7 @@ func run(serviceName string, serviceVersion string) error {
_ = godotenv.Load(".env")
setupLogging()
slog.Info("Starting service", slog.String("service", serviceName), slog.String("version", serviceVersion))
slog.Info("starting service", slog.String("service", serviceName), slog.String("version", serviceVersion))
cfg := config.Load()
@@ -45,7 +45,7 @@ func run(serviceName string, serviceVersion string) error {
}
shutdownTelemetry, err := telemetry.Init(context.Background(), telCfg)
if err != nil {
slog.Error("Failed to initialize telemetry", err)
slog.Error("failed to initialize telemetry", err)
return err
}
defer func() {
@@ -53,31 +53,31 @@ func run(serviceName string, serviceVersion string) error {
defer cancel()
if err := shutdownTelemetry(shutdownCtx); err != nil {
slog.Error("Failed to shutdown telemetry", slog.Any("error", err))
slog.Error("failed to shutdown telemetry", slog.Any("error", err))
}
}()
db, err := database.New(context.Background(), cfg.DatabaseURL)
if err != nil {
slog.Error("Failed to initialize database", slog.Any("error", err))
slog.Error("failed to initialize database", slog.Any("error", err))
return err
}
defer func() {
err := db.Close()
if err != nil {
slog.Error("Failed to close database connection", slog.Any("error", err))
slog.Error("failed to close database connection", slog.Any("error", err))
}
}()
if err := database.RunMigrations(db); err != nil {
slog.Error("Failed to run migrations", slog.Any("error", err))
slog.Error("failed to run migrations", slog.Any("error", err))
}
srv := makeServer(db, cfg.Port)
go func() {
slog.Info("Starting http server", "addr", srv.Addr)
slog.Info("starting http server", "addr", srv.Addr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Error("Failed to start http server", err)
slog.Error("failed to start http server", err)
os.Exit(1)
}
}()
@@ -85,17 +85,17 @@ func run(serviceName string, serviceVersion string) error {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
sig := <-quit
slog.Info("Shutting down server...", slog.String("signal", sig.String()))
slog.Info("shutting down server...", slog.String("signal", sig.String()))
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
slog.Error("Server forced to shutdown", slog.Any("error", err))
slog.Error("server forced to shutdown", slog.Any("error", err))
return err
}
slog.Info("Service shutdown successful!")
slog.Info("service shutdown successful!")
return nil
}
+1 -1
View File
@@ -39,7 +39,7 @@ func DefaultHandler(w http.ResponseWriter, r *http.Request) {
resp.Form[k] = v[0]
}
} else {
slog.ErrorContext(ctx, "Error parsing form", slog.Any("error", err))
slog.ErrorContext(ctx, "error parsing form", slog.Any("error", err))
}
response.JSON(ctx, w, http.StatusOK, resp)
+5 -5
View File
@@ -18,18 +18,18 @@ func (h *ListHandler) CreateList(w http.ResponseWriter, r *http.Request) {
payload, err := request.DecodeCreateList(r)
if err != nil {
slog.ErrorContext(ctx, "Failed to decode create list payload", slog.Any("error", err))
response.Error(ctx, w, http.StatusBadRequest, "Failed to decode request body")
slog.ErrorContext(ctx, "failed to decode create list payload", slog.Any("error", err))
response.Error(ctx, w, http.StatusBadRequest, "failed to decode request body")
return
}
list, err := h.ListService.Create(ctx, payload.Name, payload.UserIDs)
if err != nil {
slog.ErrorContext(ctx, "Failed to create list", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "Failed to create list")
slog.ErrorContext(ctx, "failed to create list", slog.Any("error", err))
response.Error(ctx, w, http.StatusInternalServerError, "failed to create list")
return
}
slog.InfoContext(ctx, "Created list successfully", slog.Any("list_id", list.ID))
slog.InfoContext(ctx, "created list successfully", slog.Any("list_id", list.ID))
response.JSON(ctx, w, http.StatusCreated, list)
}
+2 -2
View File
@@ -14,7 +14,7 @@ func JSON(ctx context.Context, w http.ResponseWriter, status int, data any) {
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write([]byte(`{"error": "internal server error: failed to marshal response"}`))
if err != nil {
slog.ErrorContext(ctx, "Failed to write JSON", slog.Any("error", err))
slog.ErrorContext(ctx, "failed to write json", slog.Any("error", err))
return
}
return
@@ -24,7 +24,7 @@ func JSON(ctx context.Context, w http.ResponseWriter, status int, data any) {
w.WriteHeader(status)
_, err = w.Write(payload)
if err != nil {
slog.ErrorContext(ctx, "Failed to write response", slog.Any("error", err))
slog.ErrorContext(ctx, "failed to write response", slog.Any("error", err))
return
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ func assignIntFromEnv(key string, target *int) {
if val, exists := os.LookupEnv(key); exists {
parsed, err := strconv.Atoi(val)
if err != nil {
slog.Error("Failed to parse environment variable", slog.String("var", key), slog.Any("error", err))
slog.Error("failed to parse environment variable", slog.String("var", key), slog.Any("error", err))
} else {
*target = parsed
}
+8 -2
View File
@@ -20,6 +20,12 @@ func RunMigrations(db *sql.DB) error {
if err != nil {
return fmt.Errorf("failed to load embedded migrations: %w", err)
}
defer func() {
err := sourceDriver.Close()
if err != nil {
slog.Error("failed to close migrations source", slog.Any("error", err))
}
}()
dbDriver, err := postgres.WithInstance(db, &postgres.Config{})
if err != nil {
@@ -31,13 +37,13 @@ func RunMigrations(db *sql.DB) error {
return fmt.Errorf("failed to initialize migrator: %w", err)
}
slog.Info("Running database migrations...")
slog.Info("running database migrations...")
err = m.Up()
if err != nil && !errors.Is(err, migrate.ErrNoChange) {
return fmt.Errorf("failed to run database migrations: %w", err)
}
slog.Info("Database migrations applied successfully")
slog.Info("database migrations applied successfully")
return nil
}
+2 -2
View File
@@ -25,7 +25,7 @@ func (r *ListRepo) CreateList(ctx context.Context, name string, userIDs []string
defer func() {
err := tx.Rollback()
if err != nil {
slog.Error("Failed to rollback transaction", slog.Any("error", err))
slog.Error("failed to rollback transaction", slog.Any("error", err))
}
}()
@@ -46,7 +46,7 @@ func (r *ListRepo) CreateList(ctx context.Context, name string, userIDs []string
defer func() {
err := stmt.Close()
if err != nil {
slog.Error("failed to close user/list association statement", slog.Any("error", err))
}
}()
+1 -1
View File
@@ -53,7 +53,7 @@ func Init(ctx context.Context, cfg Config) (func(context.Context) error, error)
)
metricExporter, err := otlpmetricgrpc.New(ctx,
otlpmetricgrpc.WithInsecure(),
otlpmetricgrpc.WithInsecure(), // TODO: for local development
otlpmetricgrpc.WithEndpoint(cfg.Endpoint),
)
if err != nil {