refacrot: 优化目录结构
This commit is contained in:
@@ -30,9 +30,9 @@ type App struct {
|
|||||||
groupManager *services.GroupManager
|
groupManager *services.GroupManager
|
||||||
logCleanupService *services.LogCleanupService
|
logCleanupService *services.LogCleanupService
|
||||||
requestLogService *services.RequestLogService
|
requestLogService *services.RequestLogService
|
||||||
keyCronService *services.KeyCronService
|
cronChecker *keypool.CronChecker
|
||||||
keyPoolProvider *keypool.KeyProvider
|
keyPoolProvider *keypool.KeyProvider
|
||||||
leaderService *services.LeaderService
|
leaderLock *store.LeaderLock
|
||||||
proxyServer *proxy.ProxyServer
|
proxyServer *proxy.ProxyServer
|
||||||
storage store.Store
|
storage store.Store
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
@@ -48,9 +48,9 @@ type AppParams struct {
|
|||||||
GroupManager *services.GroupManager
|
GroupManager *services.GroupManager
|
||||||
LogCleanupService *services.LogCleanupService
|
LogCleanupService *services.LogCleanupService
|
||||||
RequestLogService *services.RequestLogService
|
RequestLogService *services.RequestLogService
|
||||||
KeyCronService *services.KeyCronService
|
CronChecker *keypool.CronChecker
|
||||||
KeyPoolProvider *keypool.KeyProvider
|
KeyPoolProvider *keypool.KeyProvider
|
||||||
LeaderService *services.LeaderService
|
LeaderLock *store.LeaderLock
|
||||||
ProxyServer *proxy.ProxyServer
|
ProxyServer *proxy.ProxyServer
|
||||||
Storage store.Store
|
Storage store.Store
|
||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
@@ -65,9 +65,9 @@ func NewApp(params AppParams) *App {
|
|||||||
groupManager: params.GroupManager,
|
groupManager: params.GroupManager,
|
||||||
logCleanupService: params.LogCleanupService,
|
logCleanupService: params.LogCleanupService,
|
||||||
requestLogService: params.RequestLogService,
|
requestLogService: params.RequestLogService,
|
||||||
keyCronService: params.KeyCronService,
|
cronChecker: params.CronChecker,
|
||||||
keyPoolProvider: params.KeyPoolProvider,
|
keyPoolProvider: params.KeyPoolProvider,
|
||||||
leaderService: params.LeaderService,
|
leaderLock: params.LeaderLock,
|
||||||
proxyServer: params.ProxyServer,
|
proxyServer: params.ProxyServer,
|
||||||
storage: params.Storage,
|
storage: params.Storage,
|
||||||
db: params.DB,
|
db: params.DB,
|
||||||
@@ -77,25 +77,25 @@ func NewApp(params AppParams) *App {
|
|||||||
// Start runs the application, it is a non-blocking call.
|
// Start runs the application, it is a non-blocking call.
|
||||||
func (a *App) Start() error {
|
func (a *App) Start() error {
|
||||||
|
|
||||||
// 启动 Leader Service 并等待选举结果
|
// 启动 Leader Lock 服务并等待选举结果
|
||||||
if err := a.leaderService.Start(); err != nil {
|
if err := a.leaderLock.Start(); err != nil {
|
||||||
return fmt.Errorf("leader service failed to start: %w", err)
|
return fmt.Errorf("leader service failed to start: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leader 节点执行初始化,Follower 节点等待
|
// Leader 节点执行初始化,Follower 节点等待
|
||||||
if a.leaderService.IsLeader() {
|
if a.leaderLock.IsLeader() {
|
||||||
logrus.Info("Leader mode. Performing initial one-time tasks...")
|
logrus.Info("Leader mode. Performing initial one-time tasks...")
|
||||||
acquired, err := a.leaderService.AcquireInitializingLock()
|
acquired, err := a.leaderLock.AcquireInitializingLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to acquire initializing lock: %w", err)
|
return fmt.Errorf("failed to acquire initializing lock: %w", err)
|
||||||
}
|
}
|
||||||
if !acquired {
|
if !acquired {
|
||||||
logrus.Warn("Could not acquire initializing lock, another leader might be active. Switching to follower mode for initialization.")
|
logrus.Warn("Could not acquire initializing lock, another leader might be active. Switching to follower mode for initialization.")
|
||||||
if err := a.leaderService.WaitForInitializationToComplete(); err != nil {
|
if err := a.leaderLock.WaitForInitializationToComplete(); err != nil {
|
||||||
return fmt.Errorf("failed to wait for initialization as a fallback follower: %w", err)
|
return fmt.Errorf("failed to wait for initialization as a fallback follower: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
defer a.leaderService.ReleaseInitializingLock()
|
defer a.leaderLock.ReleaseInitializingLock()
|
||||||
|
|
||||||
// 数据库迁移
|
// 数据库迁移
|
||||||
if err := a.db.AutoMigrate(
|
if err := a.db.AutoMigrate(
|
||||||
@@ -115,7 +115,7 @@ func (a *App) Start() error {
|
|||||||
}
|
}
|
||||||
logrus.Info("System settings initialized in DB.")
|
logrus.Info("System settings initialized in DB.")
|
||||||
|
|
||||||
a.settingsManager.Initialize(a.storage, a.groupManager, a.leaderService)
|
a.settingsManager.Initialize(a.storage, a.groupManager, a.leaderLock)
|
||||||
|
|
||||||
// 从数据库加载密钥到 Redis
|
// 从数据库加载密钥到 Redis
|
||||||
if err := a.keyPoolProvider.LoadKeysFromDB(); err != nil {
|
if err := a.keyPoolProvider.LoadKeysFromDB(); err != nil {
|
||||||
@@ -125,10 +125,10 @@ func (a *App) Start() error {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logrus.Info("Follower Mode. Waiting for leader to complete initialization.")
|
logrus.Info("Follower Mode. Waiting for leader to complete initialization.")
|
||||||
if err := a.leaderService.WaitForInitializationToComplete(); err != nil {
|
if err := a.leaderLock.WaitForInitializationToComplete(); err != nil {
|
||||||
return fmt.Errorf("follower failed to start: %w", err)
|
return fmt.Errorf("follower failed to start: %w", err)
|
||||||
}
|
}
|
||||||
a.settingsManager.Initialize(a.storage, a.groupManager, a.leaderService)
|
a.settingsManager.Initialize(a.storage, a.groupManager, a.leaderLock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 显示配置并启动所有后台服务
|
// 显示配置并启动所有后台服务
|
||||||
@@ -138,7 +138,7 @@ func (a *App) Start() error {
|
|||||||
|
|
||||||
a.requestLogService.Start()
|
a.requestLogService.Start()
|
||||||
a.logCleanupService.Start()
|
a.logCleanupService.Start()
|
||||||
a.keyCronService.Start()
|
a.cronChecker.Start()
|
||||||
|
|
||||||
// Create HTTP server
|
// Create HTTP server
|
||||||
serverConfig := a.configManager.GetEffectiveServerConfig()
|
serverConfig := a.configManager.GetEffectiveServerConfig()
|
||||||
@@ -174,8 +174,8 @@ func (a *App) Stop(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop background services
|
// Stop background services
|
||||||
a.keyCronService.Stop()
|
a.cronChecker.Stop()
|
||||||
a.leaderService.Stop()
|
a.leaderLock.Stop()
|
||||||
a.logCleanupService.Stop()
|
a.logCleanupService.Stop()
|
||||||
a.requestLogService.Stop()
|
a.requestLogService.Stop()
|
||||||
a.groupManager.Stop()
|
a.groupManager.Stop()
|
||||||
|
@@ -35,12 +35,12 @@ type groupManager interface {
|
|||||||
Invalidate() error
|
Invalidate() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type leaderService interface {
|
type leaderLock interface {
|
||||||
IsLeader() bool
|
IsLeader() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes the SystemSettingsManager with database and store dependencies.
|
// Initialize initializes the SystemSettingsManager with database and store dependencies.
|
||||||
func (sm *SystemSettingsManager) Initialize(store store.Store, gm groupManager, leader leaderService) error {
|
func (sm *SystemSettingsManager) Initialize(store store.Store, gm groupManager, leaderLock leaderLock) error {
|
||||||
settingsLoader := func() (types.SystemSettings, error) {
|
settingsLoader := func() (types.SystemSettings, error) {
|
||||||
var dbSettings []models.SystemSetting
|
var dbSettings []models.SystemSetting
|
||||||
if err := db.DB.Find(&dbSettings).Error; err != nil {
|
if err := db.DB.Find(&dbSettings).Error; err != nil {
|
||||||
@@ -82,7 +82,7 @@ func (sm *SystemSettingsManager) Initialize(store store.Store, gm groupManager,
|
|||||||
}
|
}
|
||||||
|
|
||||||
afterLoader := func(newData types.SystemSettings) {
|
afterLoader := func(newData types.SystemSettings) {
|
||||||
if !leader.IsLeader() {
|
if !leaderLock.IsLeader() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := gm.Invalidate(); err != nil {
|
if err := gm.Invalidate(); err != nil {
|
||||||
|
@@ -25,9 +25,6 @@ func BuildContainer() (*dig.Container, error) {
|
|||||||
if err := container.Provide(config.NewManager); err != nil {
|
if err := container.Provide(config.NewManager); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := container.Provide(services.NewLeaderService); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Provide(db.NewDB); err != nil {
|
if err := container.Provide(db.NewDB); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -37,6 +34,9 @@ func BuildContainer() (*dig.Container, error) {
|
|||||||
if err := container.Provide(store.NewStore); err != nil {
|
if err := container.Provide(store.NewStore); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := container.Provide(store.NewLeaderLock); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if err := container.Provide(httpclient.NewHTTPClientManager); err != nil {
|
if err := container.Provide(httpclient.NewHTTPClientManager); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -51,9 +51,6 @@ func BuildContainer() (*dig.Container, error) {
|
|||||||
if err := container.Provide(services.NewKeyManualValidationService); err != nil {
|
if err := container.Provide(services.NewKeyManualValidationService); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := container.Provide(services.NewKeyCronService); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := container.Provide(services.NewKeyService); err != nil {
|
if err := container.Provide(services.NewKeyService); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -72,6 +69,9 @@ func BuildContainer() (*dig.Container, error) {
|
|||||||
if err := container.Provide(keypool.NewKeyValidator); err != nil {
|
if err := container.Provide(keypool.NewKeyValidator); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := container.Provide(keypool.NewCronChecker); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
if err := container.Provide(handler.NewServer); err != nil {
|
if err := container.Provide(handler.NewServer); err != nil {
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
package services
|
package keypool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gpt-load/internal/config"
|
"gpt-load/internal/config"
|
||||||
"gpt-load/internal/keypool"
|
|
||||||
"gpt-load/internal/models"
|
"gpt-load/internal/models"
|
||||||
|
"gpt-load/internal/store"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -11,51 +11,51 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// KeyCronService is responsible for periodically validating invalid keys.
|
// NewCronChecker is responsible for periodically validating invalid keys.
|
||||||
type KeyCronService struct {
|
type CronChecker struct {
|
||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
SettingsManager *config.SystemSettingsManager
|
SettingsManager *config.SystemSettingsManager
|
||||||
Validator *keypool.KeyValidator
|
Validator *KeyValidator
|
||||||
LeaderService *LeaderService
|
LeaderLock *store.LeaderLock
|
||||||
stopChan chan struct{}
|
stopChan chan struct{}
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeyCronService creates a new KeyCronService.
|
// NewCronChecker creates a new CronChecker.
|
||||||
func NewKeyCronService(
|
func NewCronChecker(
|
||||||
db *gorm.DB,
|
db *gorm.DB,
|
||||||
settingsManager *config.SystemSettingsManager,
|
settingsManager *config.SystemSettingsManager,
|
||||||
validator *keypool.KeyValidator,
|
validator *KeyValidator,
|
||||||
leaderService *LeaderService,
|
leaderLock *store.LeaderLock,
|
||||||
) *KeyCronService {
|
) *CronChecker {
|
||||||
return &KeyCronService{
|
return &CronChecker{
|
||||||
DB: db,
|
DB: db,
|
||||||
SettingsManager: settingsManager,
|
SettingsManager: settingsManager,
|
||||||
Validator: validator,
|
Validator: validator,
|
||||||
LeaderService: leaderService,
|
LeaderLock: leaderLock,
|
||||||
stopChan: make(chan struct{}),
|
stopChan: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start begins the cron job execution.
|
// Start begins the cron job execution.
|
||||||
func (s *KeyCronService) Start() {
|
func (s *CronChecker) Start() {
|
||||||
logrus.Debug("Starting KeyCronService...")
|
logrus.Debug("Starting CronChecker...")
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
go s.runLoop()
|
go s.runLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the cron job.
|
// Stop stops the cron job.
|
||||||
func (s *KeyCronService) Stop() {
|
func (s *CronChecker) Stop() {
|
||||||
logrus.Info("Stopping KeyCronService...")
|
logrus.Info("Stopping CronChecker...")
|
||||||
close(s.stopChan)
|
close(s.stopChan)
|
||||||
s.wg.Wait()
|
s.wg.Wait()
|
||||||
logrus.Info("KeyCronService stopped.")
|
logrus.Info("CronChecker stopped.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *KeyCronService) runLoop() {
|
func (s *CronChecker) runLoop() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
|
|
||||||
if s.LeaderService.IsLeader() {
|
if s.LeaderLock.IsLeader() {
|
||||||
s.submitValidationJobs()
|
s.submitValidationJobs()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,11 +65,11 @@ func (s *KeyCronService) runLoop() {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if s.LeaderService.IsLeader() {
|
if s.LeaderLock.IsLeader() {
|
||||||
logrus.Debug("KeyCronService: Running as leader, submitting validation jobs.")
|
logrus.Debug("CronChecker: Running as leader, submitting validation jobs.")
|
||||||
s.submitValidationJobs()
|
s.submitValidationJobs()
|
||||||
} else {
|
} else {
|
||||||
logrus.Debug("KeyCronService: Not the leader. Standing by.")
|
logrus.Debug("CronChecker: Not the leader. Standing by.")
|
||||||
}
|
}
|
||||||
case <-s.stopChan:
|
case <-s.stopChan:
|
||||||
return
|
return
|
||||||
@@ -78,10 +78,10 @@ func (s *KeyCronService) runLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// submitValidationJobs finds groups whose keys need validation and validates them.
|
// submitValidationJobs finds groups whose keys need validation and validates them.
|
||||||
func (s *KeyCronService) submitValidationJobs() {
|
func (s *CronChecker) submitValidationJobs() {
|
||||||
var groups []models.Group
|
var groups []models.Group
|
||||||
if err := s.DB.Find(&groups).Error; err != nil {
|
if err := s.DB.Find(&groups).Error; err != nil {
|
||||||
logrus.Errorf("KeyCronService: Failed to get groups: %v", err)
|
logrus.Errorf("CronChecker: Failed to get groups: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,14 +97,14 @@ func (s *KeyCronService) submitValidationJobs() {
|
|||||||
var invalidKeys []models.APIKey
|
var invalidKeys []models.APIKey
|
||||||
err := s.DB.Where("group_id = ? AND status = ?", group.ID, models.KeyStatusInvalid).Find(&invalidKeys).Error
|
err := s.DB.Where("group_id = ? AND status = ?", group.ID, models.KeyStatusInvalid).Find(&invalidKeys).Error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("KeyCronService: Failed to get invalid keys for group %s: %v", group.Name, err)
|
logrus.Errorf("CronChecker: Failed to get invalid keys for group %s: %v", group.Name, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
validatedCount := len(invalidKeys)
|
validatedCount := len(invalidKeys)
|
||||||
becameValidCount := 0
|
becameValidCount := 0
|
||||||
if validatedCount > 0 {
|
if validatedCount > 0 {
|
||||||
logrus.Debugf("KeyCronService: Found %d invalid keys to validate for group %s.", validatedCount, group.Name)
|
logrus.Debugf("CronChecker: Found %d invalid keys to validate for group %s.", validatedCount, group.Name)
|
||||||
for j := range invalidKeys {
|
for j := range invalidKeys {
|
||||||
key := &invalidKeys[j]
|
key := &invalidKeys[j]
|
||||||
isValid, _ := s.Validator.ValidateSingleKey(key, group)
|
isValid, _ := s.Validator.ValidateSingleKey(key, group)
|
||||||
@@ -116,12 +116,12 @@ func (s *KeyCronService) submitValidationJobs() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.DB.Model(group).Update("last_validated_at", time.Now()).Error; err != nil {
|
if err := s.DB.Model(group).Update("last_validated_at", time.Now()).Error; err != nil {
|
||||||
logrus.Errorf("KeyCronService: Failed to update last_validated_at for group %s: %v", group.Name, err)
|
logrus.Errorf("CronChecker: Failed to update last_validated_at for group %s: %v", group.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
duration := time.Since(groupProcessStart)
|
duration := time.Since(groupProcessStart)
|
||||||
logrus.Infof(
|
logrus.Infof(
|
||||||
"KeyCronService: Group '%s' validation finished. Total checked: %d, became valid: %d. Duration: %s.",
|
"CronChecker: Group '%s' validation finished. Total checked: %d, became valid: %d. Duration: %s.",
|
||||||
group.Name,
|
group.Name,
|
||||||
validatedCount,
|
validatedCount,
|
||||||
becameValidCount,
|
becameValidCount,
|
@@ -3,6 +3,7 @@ package services
|
|||||||
import (
|
import (
|
||||||
"gpt-load/internal/config"
|
"gpt-load/internal/config"
|
||||||
"gpt-load/internal/models"
|
"gpt-load/internal/models"
|
||||||
|
"gpt-load/internal/store"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@@ -13,16 +14,16 @@ import (
|
|||||||
type LogCleanupService struct {
|
type LogCleanupService struct {
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
settingsManager *config.SystemSettingsManager
|
settingsManager *config.SystemSettingsManager
|
||||||
leaderService *LeaderService
|
leaderLock *store.LeaderLock
|
||||||
stopCh chan struct{}
|
stopCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogCleanupService 创建新的日志清理服务
|
// NewLogCleanupService 创建新的日志清理服务
|
||||||
func NewLogCleanupService(db *gorm.DB, settingsManager *config.SystemSettingsManager, leaderService *LeaderService) *LogCleanupService {
|
func NewLogCleanupService(db *gorm.DB, settingsManager *config.SystemSettingsManager, leaderLock *store.LeaderLock) *LogCleanupService {
|
||||||
return &LogCleanupService{
|
return &LogCleanupService{
|
||||||
db: db,
|
db: db,
|
||||||
settingsManager: settingsManager,
|
settingsManager: settingsManager,
|
||||||
leaderService: leaderService,
|
leaderLock: leaderLock,
|
||||||
stopCh: make(chan struct{}),
|
stopCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,7 @@ func (s *LogCleanupService) run() {
|
|||||||
|
|
||||||
// cleanupExpiredLogs 清理过期的请求日志
|
// cleanupExpiredLogs 清理过期的请求日志
|
||||||
func (s *LogCleanupService) cleanupExpiredLogs() {
|
func (s *LogCleanupService) cleanupExpiredLogs() {
|
||||||
if !s.leaderService.IsLeader() {
|
if !s.leaderLock.IsLeader() {
|
||||||
logrus.Debug("Not the leader, skipping log cleanup.")
|
logrus.Debug("Not the leader, skipping log cleanup.")
|
||||||
return
|
return
|
||||||
}
|
}
|
@@ -27,20 +27,20 @@ type RequestLogService struct {
|
|||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
store store.Store
|
store store.Store
|
||||||
settingsManager *config.SystemSettingsManager
|
settingsManager *config.SystemSettingsManager
|
||||||
leaderService *LeaderService
|
leaderLock *store.LeaderLock
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
ticker *time.Ticker
|
ticker *time.Ticker
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRequestLogService creates a new RequestLogService instance
|
// NewRequestLogService creates a new RequestLogService instance
|
||||||
func NewRequestLogService(db *gorm.DB, store store.Store, sm *config.SystemSettingsManager, ls *LeaderService) *RequestLogService {
|
func NewRequestLogService(db *gorm.DB, store store.Store, sm *config.SystemSettingsManager, ls *store.LeaderLock) *RequestLogService {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
return &RequestLogService{
|
return &RequestLogService{
|
||||||
db: db,
|
db: db,
|
||||||
store: store,
|
store: store,
|
||||||
settingsManager: sm,
|
settingsManager: sm,
|
||||||
leaderService: ls,
|
leaderLock: ls,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cancel: cancel,
|
cancel: cancel,
|
||||||
}
|
}
|
||||||
@@ -116,7 +116,7 @@ func (s *RequestLogService) flush() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.leaderService.IsLeader() {
|
if !s.leaderLock.IsLeader() {
|
||||||
logrus.Debug("Not a leader, skipping log flush.")
|
logrus.Debug("Not a leader, skipping log flush.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package services
|
package store
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -9,8 +9,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gpt-load/internal/store"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,9 +34,9 @@ else
|
|||||||
return 0
|
return 0
|
||||||
end`
|
end`
|
||||||
|
|
||||||
// LeaderService provides a mechanism for electing a single leader in a cluster.
|
// LeaderLock provides a mechanism for electing a single leader in a cluster.
|
||||||
type LeaderService struct {
|
type LeaderLock struct {
|
||||||
store store.Store
|
store Store
|
||||||
nodeID string
|
nodeID string
|
||||||
isLeader atomic.Bool
|
isLeader atomic.Bool
|
||||||
stopChan chan struct{}
|
stopChan chan struct{}
|
||||||
@@ -46,10 +44,10 @@ type LeaderService struct {
|
|||||||
isSingleNode bool
|
isSingleNode bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLeaderService creates a new LeaderService.
|
// NewLeaderLock creates a new LeaderLock.
|
||||||
func NewLeaderService(s store.Store) *LeaderService {
|
func NewLeaderLock(s Store) *LeaderLock {
|
||||||
_, isDistributed := s.(store.LuaScripter)
|
_, isDistributed := s.(LuaScripter)
|
||||||
service := &LeaderService{
|
service := &LeaderLock{
|
||||||
store: s,
|
store: s,
|
||||||
nodeID: generateNodeID(),
|
nodeID: generateNodeID(),
|
||||||
stopChan: make(chan struct{}),
|
stopChan: make(chan struct{}),
|
||||||
@@ -65,7 +63,7 @@ func NewLeaderService(s store.Store) *LeaderService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start performs an initial leader election and starts the background leadership maintenance loop.
|
// Start performs an initial leader election and starts the background leadership maintenance loop.
|
||||||
func (s *LeaderService) Start() error {
|
func (s *LeaderLock) Start() error {
|
||||||
if s.isSingleNode {
|
if s.isSingleNode {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -81,7 +79,7 @@ func (s *LeaderService) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Stop gracefully stops the leadership maintenance process.
|
// Stop gracefully stops the leadership maintenance process.
|
||||||
func (s *LeaderService) Stop() {
|
func (s *LeaderLock) Stop() {
|
||||||
if s.isSingleNode {
|
if s.isSingleNode {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -96,12 +94,12 @@ func (s *LeaderService) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsLeader returns true if the current node is the leader.
|
// IsLeader returns true if the current node is the leader.
|
||||||
func (s *LeaderService) IsLeader() bool {
|
func (s *LeaderLock) IsLeader() bool {
|
||||||
return s.isLeader.Load()
|
return s.isLeader.Load()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcquireInitializingLock sets a temporary lock to indicate that initialization is in progress.
|
// AcquireInitializingLock sets a temporary lock to indicate that initialization is in progress.
|
||||||
func (s *LeaderService) AcquireInitializingLock() (bool, error) {
|
func (s *LeaderLock) AcquireInitializingLock() (bool, error) {
|
||||||
if !s.IsLeader() {
|
if !s.IsLeader() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@@ -110,7 +108,7 @@ func (s *LeaderService) AcquireInitializingLock() (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReleaseInitializingLock removes the initialization lock.
|
// ReleaseInitializingLock removes the initialization lock.
|
||||||
func (s *LeaderService) ReleaseInitializingLock() {
|
func (s *LeaderLock) ReleaseInitializingLock() {
|
||||||
if !s.IsLeader() {
|
if !s.IsLeader() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -121,7 +119,7 @@ func (s *LeaderService) ReleaseInitializingLock() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WaitForInitializationToComplete waits until the initialization lock is released.
|
// WaitForInitializationToComplete waits until the initialization lock is released.
|
||||||
func (s *LeaderService) WaitForInitializationToComplete() error {
|
func (s *LeaderLock) WaitForInitializationToComplete() error {
|
||||||
if s.isSingleNode || s.IsLeader() {
|
if s.isSingleNode || s.IsLeader() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -165,7 +163,7 @@ func (s *LeaderService) WaitForInitializationToComplete() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// maintainLeadershipLoop is the background process that keeps trying to acquire or renew the lock.
|
// maintainLeadershipLoop is the background process that keeps trying to acquire or renew the lock.
|
||||||
func (s *LeaderService) maintainLeadershipLoop() {
|
func (s *LeaderLock) maintainLeadershipLoop() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
ticker := time.NewTicker(leaderRenewalInterval)
|
ticker := time.NewTicker(leaderRenewalInterval)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -185,7 +183,7 @@ func (s *LeaderService) maintainLeadershipLoop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// tryToBeLeader is an idempotent function that attempts to acquire or renew the lock.
|
// tryToBeLeader is an idempotent function that attempts to acquire or renew the lock.
|
||||||
func (s *LeaderService) tryToBeLeader() error {
|
func (s *LeaderLock) tryToBeLeader() error {
|
||||||
if s.isLeader.Load() {
|
if s.isLeader.Load() {
|
||||||
err := s.renewLock()
|
err := s.renewLock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -206,12 +204,12 @@ func (s *LeaderService) tryToBeLeader() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeaderService) acquireLock() (bool, error) {
|
func (s *LeaderLock) acquireLock() (bool, error) {
|
||||||
return s.store.SetNX(leaderLockKey, []byte(s.nodeID), leaderLockTTL)
|
return s.store.SetNX(leaderLockKey, []byte(s.nodeID), leaderLockTTL)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeaderService) renewLock() error {
|
func (s *LeaderLock) renewLock() error {
|
||||||
luaStore := s.store.(store.LuaScripter)
|
luaStore := s.store.(LuaScripter)
|
||||||
ttlSeconds := int(leaderLockTTL.Seconds())
|
ttlSeconds := int(leaderLockTTL.Seconds())
|
||||||
res, err := luaStore.Eval(renewLockScript, []string{leaderLockKey}, s.nodeID, ttlSeconds)
|
res, err := luaStore.Eval(renewLockScript, []string{leaderLockKey}, s.nodeID, ttlSeconds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -223,8 +221,8 @@ func (s *LeaderService) renewLock() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *LeaderService) releaseLock() {
|
func (s *LeaderLock) releaseLock() {
|
||||||
luaStore := s.store.(store.LuaScripter)
|
luaStore := s.store.(LuaScripter)
|
||||||
if _, err := luaStore.Eval(releaseLockScript, []string{leaderLockKey}, s.nodeID); err != nil {
|
if _, err := luaStore.Eval(releaseLockScript, []string{leaderLockKey}, s.nodeID); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to release leader lock on shutdown.")
|
logrus.WithError(err).Error("Failed to release leader lock on shutdown.")
|
||||||
} else {
|
} else {
|
Reference in New Issue
Block a user