mirror of
https://github.com/langgenius/dify-plugin-daemon.git
synced 2026-07-25 21:46:13 -04:00
ca3d00229e
* use slog instead of log package and format to new log schema * update the environment name to LOG_OUTPUT_FORMAT * add the env to .env.example * fix log reference error * change the order of milldlewares * delete unused code * fix the concurrently session potential race condition * fix the log format in tests * update the duplicate code * refactor: convert log functions to slog structured format - Change log.Error/Info/Warn/Debug/Panic to accept msg + key-value pairs - Remove printf-style formatting from log functions - Update log calls in internal/cluster, internal/db, internal/core/session_manager - Remove unused 'initialized' variable from log package - Remaining files will be updated in follow-up commits 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: update all log call sites to use slog structured format Convert all log.Error, log.Info, log.Warn, log.Debug, and log.Panic calls from printf-style formatting to slog key-value pairs. Before: log.Error("failed to do something: %s", err.Error()) After: log.Error("failed to do something", "error", err) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: update cmd/ log calls to use slog structured format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: implement GnetLogger for structured logging in gnet * refactor: remove deprecated log visibility functions and related calls * feat: enhance session management with trace and identity context propagation * feat: implement serverless transaction handler and writer for plugin runtime * refactor: rename context field to traceCtx in RealBackwardsInvocation --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Yeuoly <admin@srmxy.cn>
128 lines
3.4 KiB
Go
128 lines
3.4 KiB
Go
package tasks
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/langgenius/dify-plugin-daemon/internal/cluster"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/db"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/types/app"
|
|
"github.com/langgenius/dify-plugin-daemon/internal/types/models"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/utils/log"
|
|
"github.com/langgenius/dify-plugin-daemon/pkg/utils/mapping"
|
|
)
|
|
|
|
var (
|
|
// reference to running installation tasks
|
|
installingTasks mapping.Map[string, bool]
|
|
)
|
|
|
|
func startTasks(taskIDs []string) {
|
|
for _, taskID := range taskIDs {
|
|
log.Info("start new install task", "task_id", taskID)
|
|
installingTasks.Store(taskID, true)
|
|
}
|
|
}
|
|
|
|
func endTasks(taskIDs []string) {
|
|
for _, taskID := range taskIDs {
|
|
log.Info("install task finished", "task_id", taskID)
|
|
installingTasks.Delete(taskID)
|
|
}
|
|
}
|
|
|
|
// RecycleTasks is a finalizer to update the status of all running installation tasks to failed
|
|
// when the daemon is shutting down
|
|
func RecycleTasks() error {
|
|
var errs []error
|
|
installingTasks.Range(func(taskId string, _ bool) bool {
|
|
log.Info("updating task status to failed", "task_id", taskId)
|
|
// update task status to failed
|
|
task, err := db.GetOne[models.InstallTask](
|
|
db.Equal("id", taskId),
|
|
db.InArray("status", []any{
|
|
string(models.InstallTaskStatusRunning),
|
|
string(models.InstallTaskStatusPending)},
|
|
),
|
|
)
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
return true
|
|
}
|
|
task.Status = models.InstallTaskStatusFailed
|
|
for i := range task.Plugins {
|
|
plugin := &task.Plugins[i]
|
|
plugin.Status = models.InstallTaskStatusFailed
|
|
plugin.Message = "An unexpected daemon shutdown occurred"
|
|
}
|
|
err = db.Update(task)
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
}
|
|
return true
|
|
})
|
|
return errors.Join(errs...)
|
|
}
|
|
|
|
func markTasksAsTimeout(tasks []*models.InstallTask) {
|
|
if len(tasks) == 0 {
|
|
return
|
|
}
|
|
for _, task := range tasks {
|
|
task.Status = models.InstallTaskStatusFailed
|
|
for i := range task.Plugins {
|
|
plugin := &task.Plugins[i]
|
|
plugin.Status = models.InstallTaskStatusFailed
|
|
plugin.Message = "Task timed out but not properly terminated"
|
|
}
|
|
}
|
|
err := db.Update(tasks)
|
|
if err != nil {
|
|
log.Error("failed to update tasks", "error", err)
|
|
}
|
|
}
|
|
|
|
// Just in case some tasks may stuck for some reason that we don't know.
|
|
func MonitorTimeoutTasks(cluster *cluster.Cluster, config *app.Config) {
|
|
go func() {
|
|
|
|
var timeout time.Duration
|
|
if config.Platform == app.PLATFORM_SERVERLESS {
|
|
timeout = time.Duration(config.DifyPluginServerlessConnectorLaunchTimeout) * time.Second
|
|
} else {
|
|
timeout = time.Duration(config.PythonEnvInitTimeout) * time.Second
|
|
}
|
|
// add some tolerance to timeout to avoid race condition
|
|
timeout = timeout + time.Minute
|
|
|
|
ticker := time.NewTicker(time.Minute)
|
|
defer ticker.Stop()
|
|
for range ticker.C {
|
|
if !cluster.IsMaster() {
|
|
continue
|
|
}
|
|
tasksToProcess := []*models.InstallTask{}
|
|
// get all tasks that are pending or running
|
|
tasks, err := db.GetAll[models.InstallTask](
|
|
db.InArray("status", []any{
|
|
string(models.InstallTaskStatusPending),
|
|
string(models.InstallTaskStatusRunning),
|
|
}),
|
|
)
|
|
if err != nil {
|
|
log.Error("failed to get all tasks", "error", err)
|
|
continue
|
|
}
|
|
for i := range tasks {
|
|
task := &tasks[i]
|
|
if time.Since(task.CreatedAt) > timeout {
|
|
log.Info("task timed out", "task_id", task.ID)
|
|
tasksToProcess = append(tasksToProcess, task)
|
|
}
|
|
}
|
|
markTasksAsTimeout(tasksToProcess)
|
|
}
|
|
}()
|
|
|
|
}
|