mirror of
https://github.com/Dvorinka/Trackeep.git
synced 2026-06-03 20:12:58 +00:00
229 lines
7.9 KiB
Go
229 lines
7.9 KiB
Go
package services
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type PerformanceService struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewPerformanceService(db *gorm.DB) *PerformanceService {
|
|
return &PerformanceService{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
// OptimizeDatabase performs database optimizations
|
|
func (s *PerformanceService) OptimizeDatabase() error {
|
|
// Create indexes for frequently queried fields
|
|
indexes := []string{
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_email ON users(email)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_users_username ON users(username)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_bookmarks_user_id ON bookmarks(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_bookmarks_created_at ON bookmarks(created_at)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_tasks_user_id ON tasks(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_tasks_status ON tasks(status)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_tasks_created_at ON tasks(created_at)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_notes_user_id ON notes(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_notes_is_public ON notes(is_public)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_files_user_id ON files(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_files_created_at ON files(created_at)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_time_entries_user_id ON time_entries(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_time_entries_created_at ON time_entries(created_at)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_audit_logs_created_at ON audit_logs(created_at)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_audit_logs_action ON audit_logs(action)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_marketplace_items_status ON marketplace_items(status)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_marketplace_items_category ON marketplace_items(category)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_challenges_status ON challenges(status)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_challenges_start_date ON challenges(start_date)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_challenge_participants_user_id ON challenge_participants(user_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_challenge_participants_challenge_id ON challenge_participants(challenge_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mentorships_status ON mentorships(status)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mentorships_mentor_id ON mentorships(mentor_id)",
|
|
"CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mentorships_mentee_id ON mentorships(mentee_id)",
|
|
}
|
|
|
|
for _, indexSQL := range indexes {
|
|
if err := s.db.Exec(indexSQL).Error; err != nil {
|
|
// Log error but continue with other indexes
|
|
fmt.Printf("Failed to create index: %s, error: %v\n", indexSQL, err)
|
|
}
|
|
}
|
|
|
|
// Analyze tables to update statistics
|
|
tables := []string{
|
|
"users", "bookmarks", "tasks", "notes", "files",
|
|
"time_entries", "audit_logs", "marketplace_items",
|
|
"challenges", "challenge_participants", "mentorships",
|
|
}
|
|
|
|
for _, table := range tables {
|
|
if err := s.db.Exec(fmt.Sprintf("ANALYZE %s", table)).Error; err != nil {
|
|
fmt.Printf("Failed to analyze table %s: %v\n", table, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CleanupOldAuditLogs removes old audit logs to maintain performance
|
|
func (s *PerformanceService) CleanupOldAuditLogs(retentionDays int) error {
|
|
cutoffDate := time.Now().AddDate(0, 0, -retentionDays)
|
|
|
|
result := s.db.Where("created_at < ?", cutoffDate).Delete(&struct{}{})
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
fmt.Printf("Cleaned up %d old audit log entries\n", result.RowsAffected)
|
|
return nil
|
|
}
|
|
|
|
// GetDatabaseStats returns database performance statistics
|
|
func (s *PerformanceService) GetDatabaseStats() (map[string]interface{}, error) {
|
|
stats := make(map[string]interface{})
|
|
|
|
// Get table sizes
|
|
var tableStats []struct {
|
|
TableName string `json:"table_name"`
|
|
RowCount int64 `json:"row_count"`
|
|
}
|
|
|
|
query := `
|
|
SELECT
|
|
table_name as table_name,
|
|
n_tup_ins - n_tup_del as row_count
|
|
FROM pg_stat_user_tables
|
|
ORDER BY n_tup_ins - n_tup_del DESC
|
|
LIMIT 10
|
|
`
|
|
|
|
if err := s.db.Raw(query).Scan(&tableStats).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
stats["table_sizes"] = tableStats
|
|
|
|
// Get slow queries (if pg_stat_statements is available)
|
|
var slowQueries []struct {
|
|
Query string `json:"query"`
|
|
Calls int64 `json:"calls"`
|
|
TotalTime float64 `json:"total_time"`
|
|
MeanTime float64 `json:"mean_time"`
|
|
}
|
|
|
|
slowQuerySQL := `
|
|
SELECT
|
|
query,
|
|
calls,
|
|
total_time,
|
|
mean_time
|
|
FROM pg_stat_statements
|
|
ORDER BY mean_time DESC
|
|
LIMIT 10
|
|
`
|
|
|
|
if err := s.db.Raw(slowQuerySQL).Scan(&slowQueries).Error; err == nil {
|
|
stats["slow_queries"] = slowQueries
|
|
}
|
|
|
|
// Get cache hit ratio
|
|
var cacheStats struct {
|
|
HitRatio float64 `json:"hit_ratio"`
|
|
}
|
|
|
|
cacheSQL := `
|
|
SELECT
|
|
ROUND(sum(heap_blks_hit)::numeric /
|
|
(sum(heap_blks_hit) + sum(heap_blks_read)), 4) as hit_ratio
|
|
FROM pg_statio_user_tables
|
|
`
|
|
|
|
if err := s.db.Raw(cacheSQL).Scan(&cacheStats).Error; err == nil {
|
|
stats["cache_hit_ratio"] = cacheStats.HitRatio
|
|
}
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// OptimizeQueries optimizes common query patterns
|
|
func (s *PerformanceService) OptimizeQueries() error {
|
|
// Enable query plan caching
|
|
if err := s.db.Exec("SET plan_cache_mode = force_generic_plan").Error; err != nil {
|
|
fmt.Printf("Failed to set plan cache mode: %v\n", err)
|
|
}
|
|
|
|
// Set appropriate work_mem for sorting operations
|
|
if err := s.db.Exec("SET work_mem = '16MB'").Error; err != nil {
|
|
fmt.Printf("Failed to set work_mem: %v\n", err)
|
|
}
|
|
|
|
// Set maintenance_work_mem for index creation
|
|
if err := s.db.Exec("SET maintenance_work_mem = '64MB'").Error; err != nil {
|
|
fmt.Printf("Failed to set maintenance_work_mem: %v\n", err)
|
|
}
|
|
|
|
// Enable parallel query processing
|
|
if err := s.db.Exec("SET max_parallel_workers_per_gather = 2").Error; err != nil {
|
|
fmt.Printf("Failed to set max_parallel_workers_per_gather: %v\n", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MonitorPerformance monitors system performance
|
|
func (s *PerformanceService) MonitorPerformance() (map[string]interface{}, error) {
|
|
stats := make(map[string]interface{})
|
|
|
|
// Database connections
|
|
var dbStats struct {
|
|
ActiveConnections int `json:"active_connections"`
|
|
MaxConnections int `json:"max_connections"`
|
|
}
|
|
|
|
if err := s.db.Raw("SELECT count(*) as active_connections FROM pg_stat_activity WHERE state = 'active'").Scan(&dbStats.ActiveConnections).Error; err == nil {
|
|
s.db.Raw("SHOW max_connections").Scan(&dbStats.MaxConnections)
|
|
stats["database_connections"] = dbStats
|
|
}
|
|
|
|
// Redis stats (if available) - currently not implemented
|
|
stats["redis_info"] = "Redis not configured"
|
|
|
|
// Memory usage
|
|
var memoryStats struct {
|
|
TotalMemory int64 `json:"total_memory"`
|
|
UsedMemory int64 `json:"used_memory"`
|
|
}
|
|
|
|
if err := s.db.Raw("SELECT setting::int * 1024 * 1024 as total_memory FROM pg_settings WHERE name = 'shared_buffers'").Scan(&memoryStats.TotalMemory).Error; err == nil {
|
|
stats["memory_usage"] = memoryStats
|
|
}
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// WarmupCache preloads frequently accessed data into cache
|
|
// Currently not implemented - requires Redis or other caching solution
|
|
func (s *PerformanceService) WarmupCache() error {
|
|
// TODO: Implement caching when Redis is added
|
|
return nil
|
|
}
|
|
|
|
// ClearCache clears all cache entries
|
|
// Currently not implemented - requires Redis or other caching solution
|
|
func (s *PerformanceService) ClearCache() error {
|
|
// TODO: Implement cache clearing when Redis is added
|
|
return nil
|
|
}
|
|
|
|
// GetCacheStats returns cache performance statistics
|
|
// Currently not implemented - requires Redis or other caching solution
|
|
func (s *PerformanceService) GetCacheStats() (map[string]interface{}, error) {
|
|
return map[string]interface{}{"status": "cache_not_implemented"}, nil
|
|
}
|