tercul-backend/internal/platform/db/db.go
google-labs-jules[bot] 781b313bf1 feat: Complete all pending tasks from TASKS.md
This commit addresses all the high-priority tasks outlined in the TASKS.md file, significantly improving the application's observability, completing key features, and refactoring critical parts of the codebase.

### Observability

- **Centralized Logging:** Implemented a new structured, context-aware logging system using `zerolog`. A new logging middleware injects request-specific information (request ID, user ID, trace ID) into the logger, and all application logging has been refactored to use this new system.
- **Prometheus Metrics:** Added Prometheus metrics for database query performance by creating a GORM plugin that automatically records query latency and totals.
- **OpenTelemetry Tracing:** Fully instrumented all application services in `internal/app` and data repositories in `internal/data/sql` with OpenTelemetry tracing, providing deep visibility into application performance.

### Features

- **Analytics:** Implemented like, comment, and bookmark counting. The respective command handlers now call the analytics service to increment counters when these actions are performed.
- **Enrichment Tool:** Built a new, extensible `enrich` command-line tool to fetch data from external sources. The initial implementation enriches author data using the Open Library API.

### Refactoring & Fixes

- **Decoupled Testing:** Refactored the testing utilities in `internal/testutil` to be database-agnostic, promoting the use of mock-based unit tests and improving test speed and reliability.
- **Build Fixes:** Resolved numerous build errors, including a critical import cycle between the logging, observability, and authentication packages.
- **Search Service:** Fixed the search service integration by implementing the `GetWorkContent` method in the localization service, allowing the search indexer to correctly fetch and index work content.
2025-10-05 05:26:27 +00:00

82 lines
2.0 KiB
Go

package db
import (
"fmt"
"tercul/internal/observability"
"time"
"gorm.io/driver/postgres"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
"tercul/internal/platform/config"
"tercul/internal/platform/log"
)
// DB is a global database connection instance
var DB *gorm.DB
// Connect establishes a connection to the database using configuration settings
// It returns the database connection and any error encountered
func Connect(metrics *observability.Metrics) (*gorm.DB, error) {
log.Info(fmt.Sprintf("Connecting to database: host=%s db=%s", config.Cfg.DBHost, config.Cfg.DBName))
dsn := config.Cfg.GetDSN()
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: gormlogger.Default.LogMode(gormlogger.Warn),
})
if err != nil {
return nil, fmt.Errorf("failed to connect to database: %w", err)
}
// Register Prometheus plugin
if err := db.Use(NewPrometheusPlugin(metrics)); err != nil {
return nil, fmt.Errorf("failed to register prometheus plugin: %w", err)
}
// Set the global DB instance
DB = db
// Get the underlying SQL DB instance
sqlDB, err := db.DB()
if err != nil {
return nil, fmt.Errorf("failed to get SQL DB instance: %w", err)
}
// Set connection pool settings
sqlDB.SetMaxOpenConns(20) // Connection pooling
sqlDB.SetMaxIdleConns(5) // Idle connections
sqlDB.SetConnMaxLifetime(30 * time.Minute)
log.Info(fmt.Sprintf("Successfully connected to database: host=%s db=%s", config.Cfg.DBHost, config.Cfg.DBName))
return db, nil
}
// Close closes the database connection
func Close() error {
if DB == nil {
return nil
}
sqlDB, err := DB.DB()
if err != nil {
return fmt.Errorf("failed to get SQL DB instance: %w", err)
}
return sqlDB.Close()
}
// InitDB initializes the database connection and runs migrations
// It returns the database connection and any error encountered
func InitDB(metrics *observability.Metrics) (*gorm.DB, error) {
// Connect to the database
db, err := Connect(metrics)
if err != nil {
return nil, err
}
// Migrations are now handled by a separate tool
return db, nil
}