func (s *Schedules) GetTsk(id string) (Tsk, error) {
	s.mutex.Lock()
	task, ok := s.tasks[id]
	s.mutex.Unlock()
	if ok {
		return task.Render(), nil
	}
	if err := s.daemon.Cluster().Transaction(func(tx *db.ClusterTx) error {
		t, err := tx.TaskByUUID(id)
		if err == nil {
			task = &Task{
				id:       t.UUID,
				query:    t.Query,
				schedule: time.Unix(t.Schedule, 0),
				result:   t.Result,
				status:   Status(t.Status),
			}
		}
		return err
	}); err != nil {
		return Tsk{}, errors.Wrapf(err, "task not found for %q", id)
	}
	return task.Render(), nil
}
GetTsk returns a scheduled task
package schedules
import (
	"context"
	"encoding/json"
	"fmt"
	"strings"
	"sync"
	"time"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
	"github.com/spoke-d/thermionic/internal/clock"
	"github.com/spoke-d/thermionic/internal/cluster"
	"github.com/spoke-d/thermionic/internal/config"
	"github.com/spoke-d/thermionic/internal/db"
	"github.com/spoke-d/thermionic/internal/db/database"
	"github.com/spoke-d/thermionic/internal/node"
	"github.com/spoke-d/task"
	"github.com/spoke-d/thermionic/internal/tomb"
)
type Gateway interface {
	IsDatabaseNode() bool
	LeaderAddress() (string, error)
}
type Cluster interface {
	database.DBAccessor
	db.ClusterOpener
	db.ClusterTransactioner
	db.ClusterExclusiveLocker
	NodeID(int64)
	SchemaVersion() int
	Close() error
}
type Node interface {
	database.DBAccessor
	db.NodeOpener
	db.NodeTransactioner
	Dir() string
	Close() error
}
type Daemon interface {
	Gateway() Gateway
	Cluster() Cluster
	Node() Node
	NodeConfigSchema() config.Schema
}
const Interval = 20
type Schedules struct {
	daemon Daemon
	tasks  map[string]*Task
	mutex  sync.Mutex
	clock  clock.Clock
	logger log.Logger
}
func New(daemon Daemon, options ...Option) *Schedules {
	opts := newOptions()
	for _, option := range options {
		option(opts)
	}
	return &Schedules{
		daemon: daemon,
		tasks:  make(map[string]*Task),
		clock:  opts.clock,
		logger: opts.logger,
	}
}
func (s *Schedules) Add(tsk *Task) error {
	if err := s.daemon.Cluster().Transaction(func(tx *db.ClusterTx) error {
		_, err := tx.TaskAdd(tsk.id, tsk.query, tsk.schedule.UTC().Unix(), tsk.status.Raw())
		return errors.WithStack(err)
	}); err != nil {
		return errors.Wrapf(err, "failed to add task %s to database", tsk.id)
	}
	s.mutex.Lock()
	s.tasks[tsk.id] = tsk
	s.mutex.Unlock()
	return nil
}
func (s *Schedules) Remove(id string) error {
	if _, err := s.GetTsk(id); err != nil {
		return errors.WithStack(err)
	}
	if err := s.daemon.Cluster().Transaction(func(tx *db.ClusterTx) error {
		err := tx.TaskRemove(id)
		return errors.WithStack(err)
	}); err != nil {
		return errors.Wrapf(err, "failed to remove task %s to database", id)
	}
	s.mutex.Lock()
	delete(s.tasks, id)
	s.mutex.Unlock()
	return nil
}