feat: gh-ost 平台配置开关键 + 云数据库参数支持
- ghost_enabled: 总开关, 关闭时返回403禁止dry-run/execute - ghost_path: gh-ost二进制路径, 从system_settings读取 - ghost_default_chunk_size: 每批拷贝行数 - ghost_max_load: 负载阈值限流 - ghost_allow_on_master: 单机MySQL允许直接执行 - handler改为从DB读配置, 不再依赖config包 - 5个配置项已插入system_settings(id=26-30)
This commit is contained in:
1 parent
8aaf2271b8
commit
5d59ca427e
1 file changed
+50
-9
@@ -3,17 +3,41 @@ package handler
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"sql-platform/config"
|
|
||||||
"sql-platform/internal/ghost"
|
"sql-platform/internal/ghost"
|
||||||
"sql-platform/internal/model"
|
"sql-platform/internal/model"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// getSetting reads a system_setting value by key
|
||||||
|
func getSetting(key, fallback string) string {
|
||||||
|
var s model.SystemSetting
|
||||||
|
if model.DB.Where("`key` = ?", key).Take(&s).Error == nil && s.Value != "" {
|
||||||
|
return s.Value
|
||||||
|
}
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
// isGhostEnabled checks if gh-ost is enabled in system settings
|
||||||
|
func isGhostEnabled() bool {
|
||||||
|
return getSetting("ghost_enabled", "true") == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGhostPath returns the gh-ost binary path from settings
|
||||||
|
func getGhostPath() string {
|
||||||
|
return getSetting("ghost_path", "gh-ost")
|
||||||
|
}
|
||||||
|
|
||||||
// POST /api/v1/ghost/dry-run
|
// POST /api/v1/ghost/dry-run
|
||||||
func GhOstDryRun(c *gin.Context) {
|
func GhOstDryRun(c *gin.Context) {
|
||||||
|
if !isGhostEnabled() {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"detail": "gh-ost 功能已禁用,请在系统设置中开启"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var req struct {
|
var req struct {
|
||||||
InstanceID int `json:"instance_id"`
|
InstanceID int `json:"instance_id"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
@@ -25,7 +49,6 @@ func GhOstDryRun(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Look up instance connection info
|
|
||||||
task, err := buildGhOstTask(req.InstanceID, req.Database, req.Table, req.Alter)
|
task, err := buildGhOstTask(req.InstanceID, req.Database, req.Table, req.Alter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
|
||||||
@@ -33,8 +56,9 @@ func GhOstDryRun(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
task.DryRun = true
|
task.DryRun = true
|
||||||
task.Execute = false
|
task.Execute = false
|
||||||
|
applyGhostSettings(task)
|
||||||
|
|
||||||
result, err := ghost.RunGhOst(c.Request.Context(), *task, config.C.GhOstPath)
|
result, err := ghost.RunGhOst(c.Request.Context(), *task, getGhostPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusInternalServerError, result)
|
c.JSON(http.StatusInternalServerError, result)
|
||||||
return
|
return
|
||||||
@@ -51,12 +75,9 @@ func buildGhOstTask(instanceID int, database, table, alter string) (*ghost.GhOst
|
|||||||
if inst.DBType != "mysql" {
|
if inst.DBType != "mysql" {
|
||||||
return nil, fmt.Errorf("gh-ost only supports MySQL instances, got %s", inst.DBType)
|
return nil, fmt.Errorf("gh-ost only supports MySQL instances, got %s", inst.DBType)
|
||||||
}
|
}
|
||||||
// gh-ost needs separate host and port
|
|
||||||
host := inst.Host
|
|
||||||
port := inst.Port
|
|
||||||
return &ghost.GhOstTask{
|
return &ghost.GhOstTask{
|
||||||
Host: host,
|
Host: inst.Host,
|
||||||
Port: port,
|
Port: inst.Port,
|
||||||
User: inst.Username,
|
User: inst.Username,
|
||||||
Password: inst.Password,
|
Password: inst.Password,
|
||||||
Database: database,
|
Database: database,
|
||||||
@@ -65,8 +86,27 @@ func buildGhOstTask(instanceID int, database, table, alter string) (*ghost.GhOst
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// applyGhostSettings applies system_settings to the task (chunk-size, max-load, cloud flags)
|
||||||
|
func applyGhostSettings(task *ghost.GhOstTask) {
|
||||||
|
// Chunk size
|
||||||
|
if cs, err := strconv.Atoi(getSetting("ghost_default_chunk_size", "1000")); err == nil && cs > 0 {
|
||||||
|
task.ChunkSize = cs
|
||||||
|
}
|
||||||
|
// Max load
|
||||||
|
if ml := getSetting("ghost_max_load", ""); ml != "" {
|
||||||
|
task.MaxLoad = ml
|
||||||
|
}
|
||||||
|
// Allow on master
|
||||||
|
task.AllowOnMaster = getSetting("ghost_allow_on_master", "true") == "true"
|
||||||
|
}
|
||||||
|
|
||||||
// POST /api/v1/ghost/execute
|
// POST /api/v1/ghost/execute
|
||||||
func GhOstExecute(c *gin.Context) {
|
func GhOstExecute(c *gin.Context) {
|
||||||
|
if !isGhostEnabled() {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"detail": "gh-ost 功能已禁用,请在系统设置中开启"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var req struct {
|
var req struct {
|
||||||
InstanceID int `json:"instance_id"`
|
InstanceID int `json:"instance_id"`
|
||||||
Database string `json:"database"`
|
Database string `json:"database"`
|
||||||
@@ -85,8 +125,9 @@ func GhOstExecute(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
task.DryRun = false
|
task.DryRun = false
|
||||||
task.Execute = true
|
task.Execute = true
|
||||||
|
applyGhostSettings(task)
|
||||||
|
|
||||||
taskID := ghost.RunGhOstAsync(*task, config.C.GhOstPath, nil)
|
taskID := ghost.RunGhOstAsync(*task, getGhostPath(), nil)
|
||||||
c.JSON(http.StatusAccepted, gin.H{
|
c.JSON(http.StatusAccepted, gin.H{
|
||||||
"task_id": taskID,
|
"task_id": taskID,
|
||||||
"message": "gh-ost execution started",
|
"message": "gh-ost execution started",
|
||||||
|
|||||||
Reference in new issue
Block a user