fix: auto restart

This commit is contained in:
xxnuo
2025-12-07 16:49:57 +08:00
parent 0c7be920bb
commit 1d21978799
2 changed files with 176 additions and 9 deletions

View File

@@ -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) {

View File

@@ -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")
}