Files
orbit/internal/storage/mysql/workflow_details.go

80 lines
3.9 KiB
Go

package mysql
import (
"context"
"database/sql"
"errors"
"git.yuntuo.hk/k2safe/orbit/internal/fault"
core "git.yuntuo.hk/k2safe/orbit/model"
"git.yuntuo.hk/k2safe/orbit/platform/auth"
"git.yuntuo.hk/k2safe/orbit/weave/engine"
"git.yuntuo.hk/k2safe/orbit/weave/instance"
)
func (s *WorkflowInstanceStore) Step(ctx context.Context, t auth.Token, id core.ID, key string) (instance.StepDetail, error) {
out := instance.StepDetail{InstanceID: id}
err := s.transaction(ctx, t, instance.ReadAction, func(tx *sql.Tx, p auth.Permissions) error {
v, gate, version, err := readWorkflowForQuery(ctx, tx, t.NamespaceID, id, p)
if err != nil {
return err
}
out.Revision = v.Revision
steps, err := readWorkflowStepViews(ctx, tx, t.NamespaceID, id, gate, version, key)
if err != nil {
return err
}
if len(steps) != 1 {
return fault.NotFound
}
out.Step = steps[0]
a := out.Step.ActivationID
if a == nil {
return nil
}
var input, output []byte
if err = tx.QueryRowContext(ctx, `SELECT input,output,created_at,completed_at FROM weave_activations WHERE namespace_id=? AND workflow_id=? AND step_id=? AND id=?`, t.NamespaceID, id, out.Step.ID, a).Scan(&input, &output, &out.ActivatedAt, &out.CompletedAt); err != nil {
return err
}
out.Input, out.Output = input, output
program, err := engine.Compile(version.Specification)
if err != nil {
return err
}
node, _ := program.Step(key)
if node.Poll != nil {
q := &instance.PollView{}
var owned, queue *core.ID
err = tx.QueryRowContext(ctx, "SELECT p.used_rounds,p.first_round,p.first_round_attempts_used,p.max_rounds,p.max_attempts_per_round,p.interval_ms,p.deadline_at,p.next_poll_at,p.current_task_id,r.task_id,p.resolved_at,t.id,t.queue_id FROM weave_poll_states p JOIN weave_poll_rounds r ON r.namespace_id=p.namespace_id AND r.workflow_id=p.workflow_id AND r.activation_id=p.activation_id AND r.`round`=p.used_rounds LEFT JOIN pulse_tasks t ON t.namespace_id=r.namespace_id AND t.id=r.task_id AND t.owner_kind='workflow_activation' AND t.owner_id=r.activation_id AND t.gate_id=? WHERE p.namespace_id=? AND p.workflow_id=? AND p.activation_id=?", gate, t.NamespaceID, id, a).Scan(&q.UsedRounds, &q.FirstRound, &q.FirstRoundAttemptsUsed, &q.MaxRounds, &q.MaxAttemptsPerRound, &q.IntervalMS, &q.DeadlineAt, &q.NextPollAt, &q.CurrentTaskID, &q.LastTaskID, &q.ResolvedAt, &owned, &queue)
if err != nil {
return err
}
if owned == nil || *owned != q.LastTaskID || queue == nil || *queue != node.Poll.Task.QueueID || q.CurrentTaskID != nil && *q.CurrentTaskID != q.LastTaskID || q.MaxRounds != node.Poll.MaxRounds || q.MaxAttemptsPerRound != node.Poll.Task.Retry.MaxAttempts || q.IntervalMS != node.Poll.IntervalMS {
return errors.New("workflow poll detail ownership or policy invalid")
}
out.Poll = q
}
if node.Signal != nil || node.Timer != nil || node.Poll != nil && node.Poll.FinalSignal != nil {
w := &instance.WaitView{}
if err = tx.QueryRowContext(ctx, `SELECT id,kind,state,signal_name,payload_version,correlation_key,deadline_at,winner_signal_id,resolved_at FROM weave_waits WHERE namespace_id=? AND workflow_id=? AND activation_id=?`, t.NamespaceID, id, a).Scan(&w.ID, &w.Kind, &w.State, &w.Name, &w.PayloadVersion, &w.CorrelationKey, &w.DeadlineAt, &w.WinnerSignalID, &w.ResolvedAt); err != nil {
return err
}
if w.Kind != string(out.Step.Kind) {
return errors.New("workflow wait kind mismatch")
}
if w.WinnerSignalID != nil {
var n int
if err = tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM weave_signals WHERE namespace_id=? AND workflow_id=? AND id=? AND consumed_wait_id=? AND target_step_key=? AND target_activation_sequence=? AND state='CONSUMED'`, t.NamespaceID, id, w.WinnerSignalID, w.ID, key, out.Step.ActivationSequence).Scan(&n); err != nil {
return err
}
if n != 1 {
return errors.New("workflow wait winner association invalid")
}
}
out.Wait = w
}
return nil
})
return out, err
}