File size: 4,112 Bytes
c2b0409
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package database

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"time"

	"restaurant-pos/internal/models"

	"github.com/glebarez/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

var DB *gorm.DB

func Initialize(dbPath string) (*gorm.DB, error) {
	// Ensure directory exists
	dir := filepath.Dir(dbPath)
	if err := os.MkdirAll(dir, 0755); err != nil {
		return nil, fmt.Errorf("failed to create database directory: %w", err)
	}

	newLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags),
		logger.Config{
			SlowThreshold:             200 * time.Millisecond,
			LogLevel:                  logger.Warn,
			IgnoreRecordNotFoundError: true,
			Colorful:                  true,
		},
	)

	db, err := gorm.Open(sqlite.Open(dbPath+"?_journal_mode=WAL&_busy_timeout=5000&_foreign_keys=ON"), &gorm.Config{
		Logger:                 newLogger,
		SkipDefaultTransaction: true, // Performance: skip wrapping single queries in transactions
	})
	if err != nil {
		return nil, fmt.Errorf("failed to connect to database: %w", err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		return nil, fmt.Errorf("failed to get underlying sql.DB: %w", err)
	}

	// SQLite connection pool settings
	sqlDB.SetMaxOpenConns(1) // SQLite only supports one writer
	sqlDB.SetMaxIdleConns(1)
	sqlDB.SetConnMaxLifetime(time.Hour)

	// Enable WAL mode for better concurrent read performance
	db.Exec("PRAGMA journal_mode=WAL")
	db.Exec("PRAGMA synchronous=NORMAL")
	db.Exec("PRAGMA cache_size=-64000") // 64MB cache
	db.Exec("PRAGMA temp_store=MEMORY")
	db.Exec("PRAGMA mmap_size=268435456") // 256MB mmap

	DB = db

	// Run migrations
	if err := runMigrations(db); err != nil {
		return nil, fmt.Errorf("migration failed: %w", err)
	}

	// Create indexes
	createIndexes(db)

	log.Println("[DB] SQLite database initialized successfully at", dbPath)
	return db, nil
}

func runMigrations(db *gorm.DB) error {
	log.Println("[DB] Running auto-migrations...")
	return db.AutoMigrate(models.AllModels()...)
}

func createIndexes(db *gorm.DB) {
	// Order indexes for fast lookups
	db.Exec("CREATE INDEX IF NOT EXISTS idx_orders_source ON orders(source)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_orders_status ON orders(status)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_orders_created_at ON orders(created_at)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_orders_payment_status ON orders(payment_status)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_orders_table_id ON orders(table_id)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_orders_customer_id ON orders(customer_id)")

	// KOT indexes
	db.Exec("CREATE INDEX IF NOT EXISTS idx_kots_order_id ON kots(order_id)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_kots_status ON kots(status)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_kots_kitchen_station ON kots(kitchen_station)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_kots_created_at ON kots(created_at)")

	// Inventory
	db.Exec("CREATE INDEX IF NOT EXISTS idx_stock_movements_item ON stock_movements(inventory_item_id)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_stock_movements_type ON stock_movements(type)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_stock_movements_date ON stock_movements(created_at)")

	// Audit
	db.Exec("CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_audit_logs_module ON audit_logs(module)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_audit_logs_date ON audit_logs(created_at)")

	// Products
	db.Exec("CREATE INDEX IF NOT EXISTS idx_products_category ON products(category_id)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_products_station ON products(kitchen_station)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_products_active ON products(is_active, is_available)")

	// Payments
	db.Exec("CREATE INDEX IF NOT EXISTS idx_payments_order ON payments(order_id)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_payments_method ON payments(method)")

	// Expenses
	db.Exec("CREATE INDEX IF NOT EXISTS idx_expenses_category ON expenses(category)")
	db.Exec("CREATE INDEX IF NOT EXISTS idx_expenses_date ON expenses(date)")

	log.Println("[DB] Indexes created successfully")
}