mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
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.
82 lines
2.0 KiB
Go
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
|
|
}
|