mirror of
https://github.com/xxnuo/MTranServer.git
synced 2026-09-03 06:35:20 +08:00
fix: auto restart
This commit is contained in:
@@ -3,6 +3,7 @@ package manager
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
@@ -201,13 +202,77 @@ func (m *Manager) Ready(ctx context.Context) (bool, error) {
|
||||
|
||||
func (m *Manager) Compute(ctx context.Context, req ComputeRequest) (string, error) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
client := m.client
|
||||
m.mu.RUnlock()
|
||||
|
||||
if m.client == nil {
|
||||
if client == nil {
|
||||
return "", fmt.Errorf("client not initialized")
|
||||
}
|
||||
|
||||
return m.client.Compute(ctx, req)
|
||||
result, err := client.Compute(ctx, req)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
errMsg := err.Error()
|
||||
isConnectionError := !client.IsConnected() ||
|
||||
strings.Contains(errMsg, "not connected") ||
|
||||
strings.Contains(errMsg, "failed to send message") ||
|
||||
strings.Contains(errMsg, "failed to read response") ||
|
||||
strings.Contains(errMsg, "module closed") ||
|
||||
strings.Contains(errMsg, "exit_code")
|
||||
|
||||
if !isConnectionError {
|
||||
return "", err
|
||||
}
|
||||
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.client != client {
|
||||
return m.client.Compute(ctx, req)
|
||||
}
|
||||
|
||||
if !m.worker.IsRunning() || !m.client.IsConnected() {
|
||||
if m.client != nil {
|
||||
m.client.Close()
|
||||
m.client = nil
|
||||
}
|
||||
|
||||
if m.worker.IsRunning() {
|
||||
m.worker.Stop()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
if err := m.worker.Start(); err != nil {
|
||||
return "", fmt.Errorf("failed to restart worker: %w", err)
|
||||
}
|
||||
|
||||
timeout := time.After(10 * time.Second)
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
return "", fmt.Errorf("worker restart timeout")
|
||||
case <-ticker.C:
|
||||
if m.worker.IsRunning() {
|
||||
m.client = NewClient(m.url)
|
||||
if err := m.client.Connect(); err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
|
||||
return m.client.Compute(ctx, req)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
func (m *Manager) Translate(ctx context.Context, text string) (string, error) {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -62,7 +63,7 @@ func getOrCreateSingleEngine(fromLang, toLang string) (*manager.Manager, error)
|
||||
key := fmt.Sprintf("%s-%s", fromLang, toLang)
|
||||
|
||||
engMu.RLock()
|
||||
if info, ok := engines[key]; ok {
|
||||
if info, ok := engines[key]; ok && info != nil && info.Manager != nil {
|
||||
if info.Manager.IsRunning() {
|
||||
engMu.RUnlock()
|
||||
|
||||
@@ -75,7 +76,7 @@ func getOrCreateSingleEngine(fromLang, toLang string) (*manager.Manager, error)
|
||||
engMu.Lock()
|
||||
defer engMu.Unlock()
|
||||
|
||||
if info, ok := engines[key]; ok {
|
||||
if info, ok := engines[key]; ok && info != nil && info.Manager != nil {
|
||||
if info.Manager.IsRunning() {
|
||||
info.resetIdleTimer()
|
||||
return info.Manager, nil
|
||||
@@ -195,10 +196,39 @@ func TranslateWithPivot(ctx context.Context, fromLang, toLang, text string, isHT
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var result string
|
||||
if isHTML {
|
||||
return m.TranslateHTML(ctx, text)
|
||||
result, err = m.TranslateHTML(ctx, text)
|
||||
} else {
|
||||
result, err = m.Translate(ctx, text)
|
||||
}
|
||||
return m.Translate(ctx, text)
|
||||
if err != nil && isFatalError(err) {
|
||||
key := fmt.Sprintf("%s-%s", fromLang, toLang)
|
||||
logger.Warn("Fatal error detected for engine %s, recreating...", key)
|
||||
engMu.Lock()
|
||||
info, ok := engines[key]
|
||||
if ok && info != nil && info.Manager == m {
|
||||
info.mu.Lock()
|
||||
if info.stopTimer != nil {
|
||||
info.stopTimer.Stop()
|
||||
}
|
||||
info.mu.Unlock()
|
||||
if info.Manager != nil {
|
||||
info.Manager.Cleanup()
|
||||
}
|
||||
delete(engines, key)
|
||||
}
|
||||
engMu.Unlock()
|
||||
m, err = getOrCreateSingleEngine(fromLang, toLang)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isHTML {
|
||||
return m.TranslateHTML(ctx, text)
|
||||
}
|
||||
return m.Translate(ctx, text)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
logger.Debug("Step 1: Translating %s -> en", fromLang)
|
||||
@@ -214,7 +244,36 @@ func TranslateWithPivot(ctx context.Context, fromLang, toLang, text string, isHT
|
||||
intermediateText, err = m1.Translate(ctx, text)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed in first step (%s -> en): %w", fromLang, err)
|
||||
if isFatalError(err) {
|
||||
key := fmt.Sprintf("%s-en", fromLang)
|
||||
logger.Warn("Fatal error detected for engine %s, recreating...", key)
|
||||
engMu.Lock()
|
||||
info, ok := engines[key]
|
||||
if ok && info != nil && info.Manager == m1 {
|
||||
info.mu.Lock()
|
||||
if info.stopTimer != nil {
|
||||
info.stopTimer.Stop()
|
||||
}
|
||||
info.mu.Unlock()
|
||||
if info.Manager != nil {
|
||||
info.Manager.Cleanup()
|
||||
}
|
||||
delete(engines, key)
|
||||
}
|
||||
engMu.Unlock()
|
||||
m1, err = getOrCreateSingleEngine(fromLang, "en")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to recreate first engine (%s -> en): %w", fromLang, err)
|
||||
}
|
||||
if isHTML {
|
||||
intermediateText, err = m1.TranslateHTML(ctx, text)
|
||||
} else {
|
||||
intermediateText, err = m1.Translate(ctx, text)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed in first step (%s -> en): %w", fromLang, err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug("Step 2: Translating en -> %s", toLang)
|
||||
@@ -230,7 +289,36 @@ func TranslateWithPivot(ctx context.Context, fromLang, toLang, text string, isHT
|
||||
finalText, err = m2.Translate(ctx, intermediateText)
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed in second step (en -> %s): %w", toLang, err)
|
||||
if isFatalError(err) {
|
||||
key := fmt.Sprintf("en-%s", toLang)
|
||||
logger.Warn("Fatal error detected for engine %s, recreating...", key)
|
||||
engMu.Lock()
|
||||
info, ok := engines[key]
|
||||
if ok && info != nil && info.Manager == m2 {
|
||||
info.mu.Lock()
|
||||
if info.stopTimer != nil {
|
||||
info.stopTimer.Stop()
|
||||
}
|
||||
info.mu.Unlock()
|
||||
if info.Manager != nil {
|
||||
info.Manager.Cleanup()
|
||||
}
|
||||
delete(engines, key)
|
||||
}
|
||||
engMu.Unlock()
|
||||
m2, err = getOrCreateSingleEngine("en", toLang)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to recreate second engine (en -> %s): %w", toLang, err)
|
||||
}
|
||||
if isHTML {
|
||||
finalText, err = m2.TranslateHTML(ctx, intermediateText)
|
||||
} else {
|
||||
finalText, err = m2.Translate(ctx, intermediateText)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed in second step (en -> %s): %w", toLang, err)
|
||||
}
|
||||
}
|
||||
|
||||
return finalText, nil
|
||||
@@ -289,3 +377,17 @@ func CleanupAllEngines() {
|
||||
|
||||
engines = make(map[string]*EngineInfo)
|
||||
}
|
||||
|
||||
func isFatalError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
errMsg := err.Error()
|
||||
return strings.Contains(errMsg, "module closed") ||
|
||||
strings.Contains(errMsg, "exit_code") ||
|
||||
strings.Contains(errMsg, "not connected") ||
|
||||
strings.Contains(errMsg, "failed to send message") ||
|
||||
strings.Contains(errMsg, "failed to read response") ||
|
||||
strings.Contains(errMsg, "wasm error") ||
|
||||
strings.Contains(errMsg, "invalid table access")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user