package spacesyncer import ( "context" "fmt" "sync" "gitlink.org.cn/cloudream/common/pkgs/async" "gitlink.org.cn/cloudream/common/pkgs/logger" "gitlink.org.cn/cloudream/jcs-pub/client/internal/db" "gitlink.org.cn/cloudream/jcs-pub/client/internal/metacache" clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types" stgpool "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/storage/pool" ) const ( logMod = "SpaceSyncer" ) type SpaceSyncerEvent interface { IsSpaceSyncerEvent() bool } type SpaceSyncer struct { db *db.DB stgPool *stgpool.Pool spaceMeta *metacache.UserSpaceMeta lock sync.Mutex tasks map[clitypes.SpaceSyncTaskID]*task } func New(db *db.DB, stgPool *stgpool.Pool, spaceMeta *metacache.UserSpaceMeta) *SpaceSyncer { return &SpaceSyncer{ db: db, stgPool: stgPool, spaceMeta: spaceMeta, tasks: make(map[clitypes.SpaceSyncTaskID]*task), } } func (s *SpaceSyncer) Start() *async.UnboundChannel[SpaceSyncerEvent] { s.lock.Lock() defer s.lock.Unlock() log := logger.WithField("Mod", logMod) ch := async.NewUnboundChannel[SpaceSyncerEvent]() allTask, err := db.DoTx01(s.db, s.db.SpaceSyncTask().GetAll) if err != nil { log.Warnf("load task from db: %v", err) } else { var rms []clitypes.SpaceSyncTaskID for _, t := range allTask { ctx, cancel := context.WithCancel(context.Background()) tsk := task{ Task: t, Context: ctx, CancelFn: cancel, } switch tr := t.Trigger.(type) { case *clitypes.SpaceSyncTriggerOnce: // Once类型的任务没有执行完也不执行了 rms = append(rms, t.TaskID) case *clitypes.SpaceSyncTriggerInterval: triggerInterval(s, &tsk, tr) case *clitypes.SpaceSyncTriggerAt: triggerAt(s, &tsk, tr) } log.Infof("load task %v from db", t.TaskID) } if len(rms) > 0 { err := s.db.SpaceSyncTask().BatchDelete(s.db.DefCtx(), rms) if err != nil { log.Warnf("batch delete task: %v", err) } else { log.Infof("%v once task deleted", len(rms)) } } } return ch } func (s *SpaceSyncer) Stop() { s.lock.Lock() defer s.lock.Unlock() for _, t := range s.tasks { t.CancelFn() } s.tasks = make(map[clitypes.SpaceSyncTaskID]*task) } func (s *SpaceSyncer) CreateTask(t clitypes.SpaceSyncTask) (*TaskInfo, error) { log := logger.WithField("Mod", logMod) d := s.db err := d.DoTx(func(tx db.SQLContext) error { err := d.SpaceSyncTask().Create(tx, &t) if err != nil { return err } return nil }) if err != nil { return nil, fmt.Errorf("creating space sync task: %w", err) } ctx, cancel := context.WithCancel(context.Background()) tsk := task{ Task: t, Context: ctx, CancelFn: cancel, } s.lock.Lock() s.tasks[t.TaskID] = &tsk s.lock.Unlock() switch tr := t.Trigger.(type) { case *clitypes.SpaceSyncTriggerOnce: triggerOnce(s, &tsk) case *clitypes.SpaceSyncTriggerInterval: triggerInterval(s, &tsk, tr) case *clitypes.SpaceSyncTriggerAt: triggerAt(s, &tsk, tr) } log.Infof("task %v created", t.TaskID) return &TaskInfo{ Task: t, }, nil } func (s *SpaceSyncer) CancelTask(taskID clitypes.SpaceSyncTaskID) { log := logger.WithField("Mod", logMod) s.lock.Lock() defer s.lock.Unlock() t := s.tasks[taskID] if t == nil { log.Infof("task %v not found, cancel aborted", taskID) return } t.CancelFn() delete(s.tasks, taskID) err := s.db.SpaceSyncTask().Delete(s.db.DefCtx(), taskID) if err != nil { log.Warnf("delete task %v from db: %v", taskID, err) } log.Infof("task %v canceled", taskID) } func (s *SpaceSyncer) GetTask(taskID clitypes.SpaceSyncTaskID) *clitypes.SpaceSyncTask { s.lock.Lock() defer s.lock.Unlock() tsk := s.tasks[taskID] if tsk == nil { return nil } // TODO 考虑复制一份状态,防止修改 t := tsk.Task return &t } type TaskInfo struct { Task clitypes.SpaceSyncTask } type task struct { Task clitypes.SpaceSyncTask Context context.Context CancelFn func() }