feat: expression-based concurrency keys (#889)

* feat: expression-based concurrency keys

* fix: build

* fix: typos

* fix: gen

* fix: migration

* fix: remove print statements

* fix: reassignment bugs, retries on closed transport, pr review
This commit is contained in:
abelanger5
2024-09-19 10:32:22 -04:00
committed by GitHub
parent 44d03af852
commit d23e5d9963
54 changed files with 3044 additions and 2366 deletions
+1
View File
@@ -14,3 +14,4 @@ extend-ignore-re = [
[default.extend-words]
datas = "datas"
strat = "strat"
@@ -395,6 +395,8 @@ StepRunEventReason:
- TIMED_OUT
- SLOT_RELEASED
- RETRIED_BY_USER
- WORKFLOW_RUN_GROUP_KEY_SUCCEEDED
- WORKFLOW_RUN_GROUP_KEY_FAILED
StepRunEventSeverity:
type: string
@@ -416,6 +418,8 @@ StepRunEvent:
format: date-time
stepRunId:
type: string
workflowRunId:
type: string
reason:
$ref: "#/StepRunEventReason"
severity:
@@ -430,7 +434,6 @@ StepRunEvent:
- id
- timeFirstSeen
- timeLastSeen
- stepRunId
- reason
- severity
- message
+4 -3
View File
@@ -53,9 +53,10 @@ enum ConcurrencyLimitStrategy {
}
message WorkflowConcurrencyOpts {
string action = 1; // (required) the action id for getting the concurrency group
int32 max_runs = 2; // (optional) the maximum number of concurrent workflow runs, default 1
ConcurrencyLimitStrategy limit_strategy = 3; // (optional) the strategy to use when the concurrency limit is reached, default CANCEL_IN_PROGRESS
optional string action = 1; // (optional) the action id for getting the concurrency group
optional int32 max_runs = 2; // (optional) the maximum number of concurrent workflow runs, default 1
optional ConcurrencyLimitStrategy limit_strategy = 3; // (optional) the strategy to use when the concurrency limit is reached, default CANCEL_IN_PROGRESS
optional string expression = 4; // (optional) the expression to use for concurrency
}
// CreateWorkflowJobOpts represents options to create a workflow job.
@@ -8,7 +8,7 @@ import (
func (t *WorkflowRunsService) WorkflowRunGetInput(ctx echo.Context, request gen.WorkflowRunGetInputRequestObject) (gen.WorkflowRunGetInputResponseObject, error) {
input, err := t.config.APIRepository.WorkflowRun().GetWorkflowRunInputData(request.Tenant.String(), request.WorkflowRun.String())
input, err := t.config.EngineRepository.WorkflowRun().GetWorkflowRunInputData(request.Tenant.String(), request.WorkflowRun.String())
if err != nil {
return nil, err
+189 -185
View File
@@ -67,20 +67,22 @@ const (
// Defines values for StepRunEventReason.
const (
StepRunEventReasonASSIGNED StepRunEventReason = "ASSIGNED"
StepRunEventReasonCANCELLED StepRunEventReason = "CANCELLED"
StepRunEventReasonFAILED StepRunEventReason = "FAILED"
StepRunEventReasonFINISHED StepRunEventReason = "FINISHED"
StepRunEventReasonREASSIGNED StepRunEventReason = "REASSIGNED"
StepRunEventReasonREQUEUEDNOWORKER StepRunEventReason = "REQUEUED_NO_WORKER"
StepRunEventReasonREQUEUEDRATELIMIT StepRunEventReason = "REQUEUED_RATE_LIMIT"
StepRunEventReasonRETRIEDBYUSER StepRunEventReason = "RETRIED_BY_USER"
StepRunEventReasonRETRYING StepRunEventReason = "RETRYING"
StepRunEventReasonSCHEDULINGTIMEDOUT StepRunEventReason = "SCHEDULING_TIMED_OUT"
StepRunEventReasonSLOTRELEASED StepRunEventReason = "SLOT_RELEASED"
StepRunEventReasonSTARTED StepRunEventReason = "STARTED"
StepRunEventReasonTIMEDOUT StepRunEventReason = "TIMED_OUT"
StepRunEventReasonTIMEOUTREFRESHED StepRunEventReason = "TIMEOUT_REFRESHED"
StepRunEventReasonASSIGNED StepRunEventReason = "ASSIGNED"
StepRunEventReasonCANCELLED StepRunEventReason = "CANCELLED"
StepRunEventReasonFAILED StepRunEventReason = "FAILED"
StepRunEventReasonFINISHED StepRunEventReason = "FINISHED"
StepRunEventReasonREASSIGNED StepRunEventReason = "REASSIGNED"
StepRunEventReasonREQUEUEDNOWORKER StepRunEventReason = "REQUEUED_NO_WORKER"
StepRunEventReasonREQUEUEDRATELIMIT StepRunEventReason = "REQUEUED_RATE_LIMIT"
StepRunEventReasonRETRIEDBYUSER StepRunEventReason = "RETRIED_BY_USER"
StepRunEventReasonRETRYING StepRunEventReason = "RETRYING"
StepRunEventReasonSCHEDULINGTIMEDOUT StepRunEventReason = "SCHEDULING_TIMED_OUT"
StepRunEventReasonSLOTRELEASED StepRunEventReason = "SLOT_RELEASED"
StepRunEventReasonSTARTED StepRunEventReason = "STARTED"
StepRunEventReasonTIMEDOUT StepRunEventReason = "TIMED_OUT"
StepRunEventReasonTIMEOUTREFRESHED StepRunEventReason = "TIMEOUT_REFRESHED"
StepRunEventReasonWORKFLOWRUNGROUPKEYFAILED StepRunEventReason = "WORKFLOW_RUN_GROUP_KEY_FAILED"
StepRunEventReasonWORKFLOWRUNGROUPKEYSUCCEEDED StepRunEventReason = "WORKFLOW_RUN_GROUP_KEY_SUCCEEDED"
)
// Defines values for StepRunEventSeverity.
@@ -669,9 +671,10 @@ type StepRunEvent struct {
Message string `json:"message"`
Reason StepRunEventReason `json:"reason"`
Severity StepRunEventSeverity `json:"severity"`
StepRunId string `json:"stepRunId"`
StepRunId *string `json:"stepRunId,omitempty"`
TimeFirstSeen time.Time `json:"timeFirstSeen"`
TimeLastSeen time.Time `json:"timeLastSeen"`
WorkflowRunId *string `json:"workflowRunId,omitempty"`
}
// StepRunEventList defines model for StepRunEventList.
@@ -8886,176 +8889,177 @@ func (sh *strictHandler) WorkflowVersionGet(ctx echo.Context, workflow openapi_t
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"/w8AAP//Zv+ztY2oAQA=",
}
// GetSwagger returns the content of the embedded swagger specification file
+10 -1
View File
@@ -310,13 +310,22 @@ func ToStepRunEvent(stepRunEvent *dbsqlc.StepRunEvent) *gen.StepRunEvent {
Id: int(stepRunEvent.ID),
TimeFirstSeen: stepRunEvent.TimeFirstSeen.Time,
TimeLastSeen: stepRunEvent.TimeLastSeen.Time,
StepRunId: sqlchelpers.UUIDToStr(stepRunEvent.StepRunId),
Severity: gen.StepRunEventSeverity(stepRunEvent.Severity),
Reason: gen.StepRunEventReason(stepRunEvent.Reason),
Message: stepRunEvent.Message,
Count: int(stepRunEvent.Count),
}
if stepRunEvent.StepRunId.Valid {
srId := sqlchelpers.UUIDToStr(stepRunEvent.StepRunId)
res.StepRunId = &srId
}
if stepRunEvent.WorkflowRunId.Valid {
wrId := sqlchelpers.UUIDToStr(stepRunEvent.WorkflowRunId)
res.WorkflowRunId = &wrId
}
if stepRunEvent.Data != nil {
data := make(map[string]interface{})
+1 -1
View File
@@ -150,7 +150,7 @@ func seedDev(repo repository.EngineRepository, tenantId string) error {
},
},
Concurrency: &repository.CreateWorkflowConcurrencyOpts{
Action: "test:concurrency",
Action: repository.StringPtr("test:concurrency"),
},
Jobs: []repository.CreateWorkflowJobOpts{
{
+1 -1
View File
@@ -70,7 +70,7 @@ func run(events chan<- string) (func() error, error) {
On: worker.Events("user:create:simple"),
Name: "simple",
Description: "This runs after an update to the user model.",
Concurrency: worker.Concurrency(getConcurrencyKey),
Concurrency: worker.Expression("input.user_id"),
Steps: []*worker.WorkflowStep{
worker.Fn(func(ctx worker.HatchetContext) (result *stepOneOutput, err error) {
input := &userCreateEvent{}
@@ -843,6 +843,8 @@ export enum StepRunEventReason {
TIMED_OUT = 'TIMED_OUT',
SLOT_RELEASED = 'SLOT_RELEASED',
RETRIED_BY_USER = 'RETRIED_BY_USER',
WORKFLOW_RUN_GROUP_KEY_SUCCEEDED = 'WORKFLOW_RUN_GROUP_KEY_SUCCEEDED',
WORKFLOW_RUN_GROUP_KEY_FAILED = 'WORKFLOW_RUN_GROUP_KEY_FAILED',
}
export enum StepRunEventSeverity {
@@ -857,7 +859,8 @@ export interface StepRunEvent {
timeFirstSeen: string;
/** @format date-time */
timeLastSeen: string;
stepRunId: string;
stepRunId?: string;
workflowRunId?: string;
reason: StepRunEventReason;
severity: StepRunEventSeverity;
message: string;
@@ -205,6 +205,9 @@ const REASON_TO_TITLE: Record<StepRunEventReason, string> = {
[StepRunEventReason.TIMED_OUT]: 'Execution timed out',
[StepRunEventReason.SLOT_RELEASED]: 'Slot released',
[StepRunEventReason.RETRIED_BY_USER]: 'Replayed by user',
[StepRunEventReason.WORKFLOW_RUN_GROUP_KEY_SUCCEEDED]:
'Successfully got group key',
[StepRunEventReason.WORKFLOW_RUN_GROUP_KEY_FAILED]: 'Failed to get group key',
};
function getTitleFromReason(reason: StepRunEventReason, message: string) {
@@ -52,20 +52,25 @@ export const columns = ({
header: ({ column }) => (
<DataTableColumnHeader column={column} title="Task" />
),
cell: ({ row }) => (
<div className="min-w-[120px] max-w-[180px]">
<Badge
className="cursor-pointer text-xs font-mono py-1 bg-[#ffffff] dark:bg-[#050c1c] border-[#050c1c] dark:border-gray-400"
variant="outline"
onClick={() => onRowClick(row.original)}
>
<ArrowLeftEndOnRectangleIcon className="w-4 h-4 mr-1" />
<div className="truncate max-w-[150px]">
{row.original.step?.readableId}
</div>
</Badge>
</div>
),
cell: ({ row }) => {
if (!row.original.stepRun) {
return null;
}
return (
<div className="min-w-[120px] max-w-[180px]">
<Badge
className="cursor-pointer text-xs font-mono py-1 bg-[#ffffff] dark:bg-[#050c1c] border-[#050c1c] dark:border-gray-400"
variant="outline"
onClick={() => onRowClick(row.original)}
>
<ArrowLeftEndOnRectangleIcon className="w-4 h-4 mr-1" />
<div className="truncate max-w-[150px]">
{row.original.step?.readableId}
</div>
</Badge>
</div>
);
},
enableSorting: false,
enableHiding: false,
});
@@ -171,6 +176,9 @@ const REASON_TO_TITLE: Record<StepRunEventReason, string> = {
[StepRunEventReason.TIMED_OUT]: 'Execution timed out',
[StepRunEventReason.SLOT_RELEASED]: 'Slot released',
[StepRunEventReason.RETRIED_BY_USER]: 'Replayed by user',
[StepRunEventReason.WORKFLOW_RUN_GROUP_KEY_SUCCEEDED]:
'Successfully got group key',
[StepRunEventReason.WORKFLOW_RUN_GROUP_KEY_FAILED]: 'Failed to get group key',
};
function getTitleFromReason(reason: StepRunEventReason, message: string) {
@@ -100,8 +100,12 @@ export function StepRunEvents({
updatedAt: item.timeLastSeen,
},
event: item,
stepRun: normalizedStepRunsByStepRunId[item.stepRunId],
step: normalizedStepsByStepRunId[item.stepRunId],
stepRun: item.stepRunId
? normalizedStepRunsByStepRunId[item.stepRunId]
: undefined,
step: item.stepRunId
? normalizedStepsByStepRunId[item.stepRunId]
: undefined,
};
}) || [];
+3
View File
@@ -49,6 +49,7 @@ require (
cloud.google.com/go/auth v0.9.3 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
@@ -96,6 +97,7 @@ require (
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
@@ -127,6 +129,7 @@ require (
github.com/go-playground/validator/v10 v10.22.1
github.com/go-test/deep v1.1.0 // indirect
github.com/goccy/go-json v0.10.3
github.com/google/cel-go v0.21.0
github.com/google/uuid v1.6.0
github.com/gorilla/schema v1.4.1
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0
+6
View File
@@ -9,6 +9,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
@@ -109,6 +111,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@@ -282,6 +286,8 @@ github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
github.com/steebchen/prisma-client-go v0.41.0 h1:j5ORS7kweZLkvJByy8xBkSSltjQVCkES3VuuWPgfPNk=
github.com/steebchen/prisma-client-go v0.41.0/go.mod h1:wp2xU9HO5WIefc65vcl1HOiFUzaHKyOhHw5atrzs8hc=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+115
View File
@@ -0,0 +1,115 @@
package cel
import (
"crypto/sha256"
"fmt"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/checker/decls"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
expr "google.golang.org/genproto/googleapis/api/expr/v1alpha1"
)
type CELParser struct {
workflowStrEnv *cel.Env
}
var checksumDecl = decls.NewFunction("checksum",
decls.NewOverload("checksum_string",
[]*expr.Type{decls.String},
decls.String,
),
)
var checksum = cel.Function("checksum",
cel.MemberOverload(
"checksum_string_impl",
[]*cel.Type{cel.StringType},
cel.StringType,
cel.FunctionBinding(func(args ...ref.Val) ref.Val {
if len(args) != 1 {
return types.NewErr("checksum requires 1 argument")
}
str, ok := args[0].(types.String)
if !ok {
return types.NewErr("argument must be a string")
}
hash := sha256.Sum256([]byte(str))
return types.String(fmt.Sprintf("%x", hash))
})),
)
func NewCELParser() *CELParser {
workflowStrEnv, _ := cel.NewEnv(
cel.Declarations(
decls.NewVar("input", decls.NewMapType(decls.String, decls.Dyn)),
decls.NewVar("additional_metadata", decls.NewMapType(decls.String, decls.Dyn)),
decls.NewVar("workflow_run_id", decls.String),
checksumDecl,
),
checksum,
)
return &CELParser{
workflowStrEnv: workflowStrEnv,
}
}
type WorkflowStringInput map[string]interface{}
type WorkflowStringInputOpts func(WorkflowStringInput)
func WithInput(input map[string]interface{}) WorkflowStringInputOpts {
return func(w WorkflowStringInput) {
w["input"] = input
}
}
func WithAdditionalMetadata(metadata map[string]interface{}) WorkflowStringInputOpts {
return func(w WorkflowStringInput) {
w["additional_metadata"] = metadata
}
}
func WithWorkflowRunID(workflowRunID string) WorkflowStringInputOpts {
return func(w WorkflowStringInput) {
w["workflow_run_id"] = workflowRunID
}
}
func NewWorkflowStringInput(opts ...WorkflowStringInputOpts) WorkflowStringInput {
res := make(map[string]interface{})
for _, opt := range opts {
opt(res)
}
return res
}
func (p *CELParser) ParseWorkflowString(workflowExp string) (cel.Program, error) {
ast, issues := p.workflowStrEnv.Compile(workflowExp)
if issues != nil && issues.Err() != nil {
return nil, issues.Err()
}
return p.workflowStrEnv.Program(ast)
}
func (p *CELParser) ParseAndEvalWorkflowString(workflowExp string, in WorkflowStringInput) (string, error) {
prg, err := p.ParseWorkflowString(workflowExp)
if err != nil {
return "", err
}
var inMap map[string]interface{} = in
out, _, err := prg.Eval(inMap)
if err != nil {
return "", err
}
return out.Value().(string), nil
}
+93
View File
@@ -0,0 +1,93 @@
package cel_test
import (
"testing"
"github.com/google/cel-go/common/types"
"github.com/hatchet-dev/hatchet/internal/cel"
"github.com/stretchr/testify/assert"
)
func TestCELParser(t *testing.T) {
parser := cel.NewCELParser()
tests := []struct {
expression string
input cel.WorkflowStringInput
expected string
expectError bool
}{
{
expression: `has(input.custom.value) ? input.custom.value : "default"`,
input: cel.NewWorkflowStringInput(
cel.WithInput(map[string]interface{}{
"custom": map[string]interface{}{
"value": "actual value",
},
}),
),
expected: "actual value",
expectError: false,
},
{
expression: `has(input.custom) ? input.custom.value : "default"`,
input: cel.NewWorkflowStringInput(
cel.WithInput(map[string]interface{}{}),
),
expected: "default",
expectError: false,
},
{
expression: `checksum(input.custom.value)`,
input: cel.NewWorkflowStringInput(
cel.WithInput(map[string]interface{}{
"custom": map[string]interface{}{
"value": "checksum this",
},
}),
),
expected: types.String("97e9269cd0514f864e6be9157998464c94776ebc7f669b449f581abdad4035f5").Value().(string), // Precomputed checksum
expectError: false,
},
{
expression: `input.custom.value + workflow_run_id`,
input: cel.NewWorkflowStringInput(
cel.WithInput(map[string]interface{}{
"custom": map[string]interface{}{
"value": "concatenate ",
},
}),
cel.WithWorkflowRunID("1234"),
),
expected: "concatenate 1234",
expectError: false,
},
{
expression: `checksum(input.missing_key)`, // Should throw an error due to missing key
input: cel.NewWorkflowStringInput(),
expected: "",
expectError: true,
},
{
expression: `input.custom.value + 1234`, // Invalid expression (mismatched types), expecting error
input: cel.NewWorkflowStringInput(),
expected: "",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.expression, func(t *testing.T) {
result, err := parser.ParseAndEvalWorkflowString(tt.expression, tt.input)
if tt.expectError {
assert.Error(t, err, "Expected error but got none")
} else {
assert.NoError(t, err, "Did not expect error but got one")
assert.Equal(t, tt.expected, result, "Unexpected result")
}
})
}
}
+105
View File
@@ -0,0 +1,105 @@
package queueutils
import (
"context"
"sync"
"time"
"github.com/rs/zerolog"
)
type OpMethod func(ctx context.Context, id string) (bool, error)
// SerialOperation represents a method that can only run serially.
type SerialOperation struct {
mu sync.RWMutex
shouldContinue bool
isRunning bool
id string
lastRun time.Time
description string
timeout time.Duration
method OpMethod
}
func (o *SerialOperation) RunOrContinue(ql *zerolog.Logger) {
o.setContinue(true)
o.Run(ql)
}
func (o *SerialOperation) Run(ql *zerolog.Logger) {
if !o.setRunning(true, ql) {
return
}
go func() {
defer func() {
o.setRunning(false, ql)
}()
f := func() {
o.setContinue(false)
ctx, cancel := context.WithTimeout(context.Background(), o.timeout)
defer cancel()
shouldContinue, err := o.method(ctx, o.id)
if err != nil {
ql.Err(err).Msgf("could not %s", o.description)
return
}
// if a continue was set during execution of the scheduler, we'd like to continue no matter what.
// if a continue was not set, we'd like to set it to the value returned by the scheduler.
if !o.getContinue() {
o.setContinue(shouldContinue)
}
}
f()
for o.getContinue() {
f()
}
}()
}
// setRunning sets the running state of the operation and returns true if the state was changed,
// false if the state was not changed.
func (o *SerialOperation) setRunning(isRunning bool, ql *zerolog.Logger) bool {
o.mu.Lock()
defer o.mu.Unlock()
if isRunning == o.isRunning {
return false
}
if isRunning {
ql.Info().Str("tenant_id", o.id).TimeDiff("last_run", time.Now(), o.lastRun).Msg(o.description)
o.lastRun = time.Now()
}
o.isRunning = isRunning
return true
}
func (o *SerialOperation) setContinue(shouldContinue bool) {
o.mu.Lock()
defer o.mu.Unlock()
o.shouldContinue = shouldContinue
}
func (o *SerialOperation) getContinue() bool {
o.mu.RLock()
defer o.mu.RUnlock()
return o.shouldContinue
}
+116
View File
@@ -0,0 +1,116 @@
package queueutils
import (
"context"
"sync"
"testing"
"time"
"github.com/rs/zerolog"
"github.com/stretchr/testify/assert"
)
var l = zerolog.Nop()
func TestSerialOperation_RunOrContinue_NoConcurrentExecution(t *testing.T) {
var runCount int
var mu sync.Mutex
mockMethod := func(ctx context.Context, id string) (bool, error) {
if !mu.TryLock() {
panic("Concurrent execution detected")
}
runCount++
mu.Unlock()
time.Sleep(100 * time.Millisecond) // Simulate method execution time
return false, nil
}
operation := &SerialOperation{
id: "1234",
description: "Test operation",
timeout: 2 * time.Second,
method: mockMethod,
}
// First run
operation.RunOrContinue(&l)
// Try to trigger a set of runs concurrently, it should not start until the first finishes
time.Sleep(10 * time.Millisecond)
operation.RunOrContinue(&l)
operation.RunOrContinue(&l)
operation.RunOrContinue(&l)
operation.RunOrContinue(&l)
// Wait for both to finish
time.Sleep(250 * time.Millisecond)
mu.Lock()
assert.Equal(t, 2, runCount, "The method should not run concurrently")
mu.Unlock()
}
func TestSerialOperation_RunOrContinue_ShouldRunAfterCompletion(t *testing.T) {
var runCount int
var mu sync.Mutex
mockMethod := func(ctx context.Context, id string) (bool, error) {
mu.Lock()
runCount++
mu.Unlock()
time.Sleep(50 * time.Millisecond) // Simulate method execution time
return false, nil
}
operation := &SerialOperation{
id: "1234",
description: "Test operation",
timeout: 2 * time.Second,
method: mockMethod,
}
// First run
operation.RunOrContinue(&l)
time.Sleep(110 * time.Millisecond)
// Second run after first finishes
operation.RunOrContinue(&l)
time.Sleep(110 * time.Millisecond)
mu.Lock()
assert.Equal(t, 2, runCount, "The method should run twice after completion")
mu.Unlock()
}
func TestSerialOperation_RunOrContinue_ShouldRunOnContinues(t *testing.T) {
var runCount int
var mu sync.Mutex
mockMethod := func(ctx context.Context, id string) (bool, error) {
mu.Lock()
runCount++
mu.Unlock()
time.Sleep(25 * time.Millisecond) // Simulate method execution time
return runCount < 5, nil
}
operation := &SerialOperation{
id: "1234",
description: "Test operation",
timeout: 2 * time.Second,
method: mockMethod,
}
// First run
operation.RunOrContinue(&l)
time.Sleep(200 * time.Millisecond)
mu.Lock()
assert.Equal(t, 5, runCount, "The method should run five times on continue")
mu.Unlock()
}
+47
View File
@@ -0,0 +1,47 @@
package queueutils
import (
"sync"
"time"
"github.com/rs/zerolog"
)
type OperationPool struct {
ops sync.Map
timeout time.Duration
description string
method OpMethod
ql *zerolog.Logger
}
func NewOperationPool(ql *zerolog.Logger, timeout time.Duration, description string, method OpMethod) *OperationPool {
return &OperationPool{
timeout: timeout,
description: description,
method: method,
ql: ql,
}
}
func (p *OperationPool) RunOrContinue(id string) {
p.GetOperation(id).RunOrContinue(p.ql)
}
func (p *OperationPool) GetOperation(id string) *SerialOperation {
op, ok := p.ops.Load(id)
if !ok {
op = &SerialOperation{
id: id,
lastRun: time.Now(),
description: p.description,
timeout: p.timeout,
method: p.method,
}
p.ops.Store(id, op)
}
return op.(*SerialOperation)
}
+229 -213
View File
@@ -491,9 +491,10 @@ type WorkflowConcurrencyOpts struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Action string `protobuf:"bytes,1,opt,name=action,proto3" json:"action,omitempty"` // (required) the action id for getting the concurrency group
MaxRuns int32 `protobuf:"varint,2,opt,name=max_runs,json=maxRuns,proto3" json:"max_runs,omitempty"` // (optional) the maximum number of concurrent workflow runs, default 1
LimitStrategy ConcurrencyLimitStrategy `protobuf:"varint,3,opt,name=limit_strategy,json=limitStrategy,proto3,enum=ConcurrencyLimitStrategy" json:"limit_strategy,omitempty"` // (optional) the strategy to use when the concurrency limit is reached, default CANCEL_IN_PROGRESS
Action *string `protobuf:"bytes,1,opt,name=action,proto3,oneof" json:"action,omitempty"` // (optional) the action id for getting the concurrency group
MaxRuns *int32 `protobuf:"varint,2,opt,name=max_runs,json=maxRuns,proto3,oneof" json:"max_runs,omitempty"` // (optional) the maximum number of concurrent workflow runs, default 1
LimitStrategy *ConcurrencyLimitStrategy `protobuf:"varint,3,opt,name=limit_strategy,json=limitStrategy,proto3,enum=ConcurrencyLimitStrategy,oneof" json:"limit_strategy,omitempty"` // (optional) the strategy to use when the concurrency limit is reached, default CANCEL_IN_PROGRESS
Expression *string `protobuf:"bytes,4,opt,name=expression,proto3,oneof" json:"expression,omitempty"` // (optional) the expression to use for concurrency
}
func (x *WorkflowConcurrencyOpts) Reset() {
@@ -529,26 +530,33 @@ func (*WorkflowConcurrencyOpts) Descriptor() ([]byte, []int) {
}
func (x *WorkflowConcurrencyOpts) GetAction() string {
if x != nil {
return x.Action
if x != nil && x.Action != nil {
return *x.Action
}
return ""
}
func (x *WorkflowConcurrencyOpts) GetMaxRuns() int32 {
if x != nil {
return x.MaxRuns
if x != nil && x.MaxRuns != nil {
return *x.MaxRuns
}
return 0
}
func (x *WorkflowConcurrencyOpts) GetLimitStrategy() ConcurrencyLimitStrategy {
if x != nil {
return x.LimitStrategy
if x != nil && x.LimitStrategy != nil {
return *x.LimitStrategy
}
return ConcurrencyLimitStrategy_CANCEL_IN_PROGRESS
}
func (x *WorkflowConcurrencyOpts) GetExpression() string {
if x != nil && x.Expression != nil {
return *x.Expression
}
return ""
}
// CreateWorkflowJobOpts represents options to create a workflow job.
type CreateWorkflowJobOpts struct {
state protoimpl.MessageState
@@ -1544,212 +1552,219 @@ var file_workflows_proto_rawDesc = []byte{
0x69, 0x6c, 0x75, 0x72, 0x65, 0x5f, 0x6a, 0x6f, 0x62, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74,
0x69, 0x63, 0x6b, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x42, 0x13, 0x0a,
0x11, 0x5f, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69,
0x74, 0x79, 0x22, 0x8e, 0x01, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x43,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x16,
0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x75,
0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x52, 0x75, 0x6e,
0x73, 0x12, 0x40, 0x0a, 0x0e, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x43, 0x6f, 0x6e, 0x63,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61,
0x74, 0x65, 0x67, 0x79, 0x52, 0x0d, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x22, 0x82, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, 0x0a,
0x74, 0x79, 0x22, 0xfc, 0x01, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x43,
0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1b,
0x0a, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x1e, 0x0a, 0x08, 0x6d,
0x61, 0x78, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52,
0x07, 0x6d, 0x61, 0x78, 0x52, 0x75, 0x6e, 0x73, 0x88, 0x01, 0x01, 0x12, 0x45, 0x0a, 0x0e, 0x6c,
0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x03, 0x20,
0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63,
0x79, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x48, 0x02,
0x52, 0x0d, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x88,
0x01, 0x01, 0x12, 0x23, 0x0a, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x0a, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x42,
0x11, 0x0a, 0x0f, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65,
0x67, 0x79, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x22, 0x82, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x4a, 0x6f, 0x62, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f,
0x6e, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x17, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73,
0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x93, 0x02, 0x0a, 0x13, 0x44, 0x65, 0x73, 0x69, 0x72,
0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x1f,
0x0a, 0x08, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x48, 0x00, 0x52, 0x08, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01, 0x12,
0x1f, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x05, 0x48, 0x01, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01, 0x01,
0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01,
0x28, 0x08, 0x48, 0x02, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x88, 0x01,
0x01, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61,
0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48, 0x03, 0x52,
0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x1b,
0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x48, 0x04,
0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a, 0x09, 0x5f,
0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x69, 0x6e, 0x74,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f,
0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xb2, 0x03, 0x0a,
0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53,
0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x61,
0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x72, 0x65,
0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x6e,
0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x6e, 0x70, 0x75,
0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x05, 0x20,
0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1b, 0x0a, 0x09,
0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x74,
0x72, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x72, 0x65, 0x74, 0x72,
0x69, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, 0x6d, 0x69,
0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x0a,
0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x77, 0x6f,
0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x29, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65,
0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c, 0x77, 0x6f,
0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x11, 0x57, 0x6f,
0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12,
0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x14, 0x2e, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38,
0x01, 0x22, 0x3d, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52,
0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18,
0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x75, 0x6e,
0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x75, 0x6e, 0x69, 0x74, 0x73,
0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xdc, 0x02, 0x0a, 0x17, 0x53, 0x63, 0x68,
0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x73, 0x63, 0x68, 0x65,
0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69,
0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c,
0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65,
0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x70,
0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x12, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64,
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x53, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b,
0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28,
0x05, 0x48, 0x02, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88,
0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4b, 0x65,
0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f,
0x69, 0x64, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74,
0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68,
0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x68,
0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0xe8, 0x01, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a, 0x0a, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d,
0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41,
0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6f,
0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6f, 0x72, 0x64, 0x65,
0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64,
0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x49, 0x64, 0x22, 0x53, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72,
0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a,
0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65,
0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x49, 0x0a, 0x16, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x72, 0x6f, 0x6e, 0x52, 0x65,
0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x12,
0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72,
0x6f, 0x6e, 0x22, 0xe4, 0x03, 0x0a, 0x16, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a, 0x05, 0x73, 0x74, 0x65, 0x70, 0x73, 0x18, 0x04, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x17, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x65,
0x70, 0x73, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x93, 0x02, 0x0a, 0x13, 0x44, 0x65, 0x73,
0x69, 0x72, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73,
0x12, 0x1f, 0x0a, 0x08, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88, 0x01,
0x01, 0x12, 0x1f, 0x0a, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08, 0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x88,
0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x03,
0x20, 0x01, 0x28, 0x08, 0x48, 0x02, 0x52, 0x08, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
0x88, 0x01, 0x01, 0x12, 0x3b, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f,
0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72,
0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x48,
0x03, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01,
0x12, 0x1b, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05,
0x48, 0x04, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0b, 0x0a,
0x09, 0x5f, 0x73, 0x74, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x69,
0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x72, 0x65, 0x71, 0x75,
0x69, 0x72, 0x65, 0x64, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x61,
0x74, 0x6f, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xb2,
0x03, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x72, 0x65, 0x61,
0x64, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a,
0x72, 0x65, 0x61, 0x64, 0x61, 0x62, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63,
0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x74, 0x69,
0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x16, 0x0a, 0x06,
0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x6e,
0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x18,
0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1b,
0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a, 0x07, 0x72,
0x65, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x72, 0x65,
0x74, 0x72, 0x69, 0x65, 0x73, 0x12, 0x35, 0x0a, 0x0b, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x43, 0x72, 0x65,
0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74,
0x52, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x73, 0x12, 0x4e, 0x0a, 0x0d,
0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x09, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b,
0x66, 0x6c, 0x6f, 0x77, 0x53, 0x74, 0x65, 0x70, 0x4f, 0x70, 0x74, 0x73, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0c,
0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x55, 0x0a, 0x11,
0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72,
0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03,
0x6b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0b, 0x32, 0x14, 0x2e, 0x44, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x57, 0x6f, 0x72, 0x6b,
0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a,
0x02, 0x38, 0x01, 0x22, 0x3d, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65,
0x70, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65,
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05,
0x75, 0x6e, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x75, 0x6e, 0x69,
0x74, 0x73, 0x22, 0x16, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xdc, 0x02, 0x0a, 0x17, 0x53,
0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x73, 0x63,
0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x63, 0x68, 0x65, 0x64,
0x75, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x03, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52,
0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x12,
0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65,
0x6e, 0x74, 0x53, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24,
0x0a, 0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20,
0x01, 0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65,
0x78, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65,
0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64,
0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f,
0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f,
0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, 0x5f,
0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x22, 0xe8, 0x01, 0x0a, 0x0f, 0x57, 0x6f,
0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a,
0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x39, 0x0a,
0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63,
0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54,
0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65,
0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a,
0x05, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6f, 0x72,
0x64, 0x65, 0x72, 0x12, 0x1f, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f,
0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x49, 0x64, 0x22, 0x53, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x66, 0x12,
0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09,
0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4b, 0x65, 0x79, 0x22, 0x49, 0x0a, 0x16, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x43, 0x72, 0x6f, 0x6e,
0x52, 0x65, 0x66, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64,
0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x63, 0x72, 0x6f, 0x6e, 0x22, 0xe4, 0x03, 0x0a, 0x16, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72,
0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72,
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08,
0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x12, 0x70,
0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69,
0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x53, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a,
0x0b, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01,
0x28, 0x05, 0x48, 0x02, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78,
0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79,
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4b,
0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f,
0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01,
0x28, 0x09, 0x48, 0x04, 0x52, 0x12, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c,
0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x64,
0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64,
0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65,
0x64, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08,
0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x06,
0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a,
0x0a, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x15, 0x0a, 0x13, 0x5f,
0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f,
0x69, 0x64, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64,
0x65, 0x78, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79,
0x42, 0x16, 0x0a, 0x14, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f,
0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x64, 0x65, 0x73,
0x69, 0x72, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x0b,
0x0a, 0x09, 0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x41, 0x0a, 0x17, 0x54,
0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x6d,
0x0a, 0x13, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2e, 0x0a,
0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x12, 0x2e, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a,
0x14, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x24, 0x0a, 0x0e, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x53,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10,
0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x41, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x0c, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x46,
0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x55, 0x52,
0x41, 0x42, 0x4c, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x47, 0x10, 0x02, 0x2a,
0x6c, 0x0a, 0x18, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x16, 0x0a, 0x12, 0x43,
0x41, 0x4e, 0x43, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53,
0x53, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x4e, 0x45, 0x57, 0x45,
0x53, 0x54, 0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x51, 0x55, 0x45, 0x55, 0x45, 0x5f, 0x4e, 0x45,
0x57, 0x45, 0x53, 0x54, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f,
0x52, 0x4f, 0x55, 0x4e, 0x44, 0x5f, 0x52, 0x4f, 0x42, 0x49, 0x4e, 0x10, 0x03, 0x2a, 0x85, 0x01,
0x0a, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d,
0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x51, 0x55, 0x41, 0x4c,
0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10,
0x01, 0x12, 0x10, 0x0a, 0x0c, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41,
0x4e, 0x10, 0x02, 0x12, 0x19, 0x0a, 0x15, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54,
0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0d,
0x0a, 0x09, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x04, 0x12, 0x16, 0x0a,
0x12, 0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51,
0x55, 0x41, 0x4c, 0x10, 0x05, 0x2a, 0x5d, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45,
0x43, 0x4f, 0x4e, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45,
0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03,
0x44, 0x41, 0x59, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x45, 0x45, 0x4b, 0x10, 0x04, 0x12,
0x09, 0x0a, 0x05, 0x4d, 0x4f, 0x4e, 0x54, 0x48, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x59, 0x45,
0x41, 0x52, 0x10, 0x06, 0x32, 0x8a, 0x02, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0b, 0x50, 0x75, 0x74, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e,
0x0a, 0x10, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c,
0x6f, 0x77, 0x12, 0x18, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44,
0x0a, 0x0f, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x12, 0x17, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x54, 0x72, 0x69,
0x65, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x05, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x30, 0x0a, 0x12, 0x70, 0x61, 0x72,
0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18,
0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x53,
0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x24, 0x0a, 0x0b, 0x63,
0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05,
0x48, 0x02, 0x52, 0x0a, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x01,
0x01, 0x12, 0x20, 0x0a, 0x09, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06,
0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x4b, 0x65, 0x79,
0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x13, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61,
0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09,
0x48, 0x04, 0x52, 0x12, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x2f, 0x0a, 0x11, 0x64, 0x65, 0x73,
0x69, 0x72, 0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x08,
0x20, 0x01, 0x28, 0x09, 0x48, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x73, 0x69, 0x72, 0x65, 0x64, 0x57,
0x6f, 0x72, 0x6b, 0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1f, 0x0a, 0x08, 0x70, 0x72,
0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x48, 0x06, 0x52, 0x08,
0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f,
0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x15, 0x0a, 0x13, 0x5f, 0x70, 0x61,
0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64,
0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78,
0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x63, 0x68, 0x69, 0x6c, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x42, 0x16,
0x0a, 0x14, 0x5f, 0x61, 0x64, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x5f, 0x6d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x14, 0x0a, 0x12, 0x5f, 0x64, 0x65, 0x73, 0x69, 0x72,
0x65, 0x64, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x42, 0x0b, 0x0a, 0x09,
0x5f, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, 0x41, 0x0a, 0x17, 0x54, 0x72, 0x69,
0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c,
0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69,
0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x50, 0x75, 0x74,
0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x42, 0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
0x68, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63,
0x68, 0x65, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74,
0x72, 0x61, 0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77,
0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x22, 0x6d, 0x0a, 0x13,
0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02,
0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x64,
0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e,
0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x08, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x16, 0x0a, 0x14, 0x50,
0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x2a, 0x24, 0x0a, 0x0e, 0x53, 0x74, 0x69, 0x63, 0x6b, 0x79, 0x53, 0x74, 0x72,
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x4f, 0x46, 0x54, 0x10, 0x00, 0x12,
0x08, 0x0a, 0x04, 0x48, 0x41, 0x52, 0x44, 0x10, 0x01, 0x2a, 0x32, 0x0a, 0x0c, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4b, 0x69, 0x6e, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x55, 0x4e,
0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x55, 0x52, 0x41, 0x42,
0x4c, 0x45, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41, 0x47, 0x10, 0x02, 0x2a, 0x6c, 0x0a,
0x18, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x63, 0x79, 0x4c, 0x69, 0x6d, 0x69,
0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x16, 0x0a, 0x12, 0x43, 0x41, 0x4e,
0x43, 0x45, 0x4c, 0x5f, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10,
0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x44, 0x52, 0x4f, 0x50, 0x5f, 0x4e, 0x45, 0x57, 0x45, 0x53, 0x54,
0x10, 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x51, 0x55, 0x45, 0x55, 0x45, 0x5f, 0x4e, 0x45, 0x57, 0x45,
0x53, 0x54, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x47, 0x52, 0x4f, 0x55, 0x50, 0x5f, 0x52, 0x4f,
0x55, 0x4e, 0x44, 0x5f, 0x52, 0x4f, 0x42, 0x49, 0x4e, 0x10, 0x03, 0x2a, 0x85, 0x01, 0x0a, 0x15,
0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x43, 0x6f, 0x6d, 0x70, 0x61,
0x72, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x00,
0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x54, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x01, 0x12,
0x10, 0x0a, 0x0c, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10,
0x02, 0x12, 0x19, 0x0a, 0x15, 0x47, 0x52, 0x45, 0x41, 0x54, 0x45, 0x52, 0x5f, 0x54, 0x48, 0x41,
0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0d, 0x0a, 0x09,
0x4c, 0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x10, 0x04, 0x12, 0x16, 0x0a, 0x12, 0x4c,
0x45, 0x53, 0x53, 0x5f, 0x54, 0x48, 0x41, 0x4e, 0x5f, 0x4f, 0x52, 0x5f, 0x45, 0x51, 0x55, 0x41,
0x4c, 0x10, 0x05, 0x2a, 0x5d, 0x0a, 0x11, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74,
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x45, 0x43, 0x4f,
0x4e, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x4e, 0x55, 0x54, 0x45, 0x10, 0x01,
0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x55, 0x52, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x44, 0x41,
0x59, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x57, 0x45, 0x45, 0x4b, 0x10, 0x04, 0x12, 0x09, 0x0a,
0x05, 0x4d, 0x4f, 0x4e, 0x54, 0x48, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x59, 0x45, 0x41, 0x52,
0x10, 0x06, 0x32, 0x8a, 0x02, 0x0a, 0x0f, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x34, 0x0a, 0x0b, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x13, 0x2e, 0x50, 0x75, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x10,
0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
0x12, 0x18, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x66,
0x6c, 0x6f, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x57, 0x6f, 0x72,
0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x44, 0x0a, 0x0f,
0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12,
0x17, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x54, 0x72, 0x69, 0x67, 0x67,
0x65, 0x72, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x3b, 0x0a, 0x0c, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d,
0x69, 0x74, 0x12, 0x14, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x50, 0x75, 0x74, 0x52, 0x61,
0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42,
0x42, 0x5a, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61,
0x74, 0x63, 0x68, 0x65, 0x74, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x68, 0x61, 0x74, 0x63, 0x68, 0x65,
0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69,
0x63, 0x65, 0x73, 0x2f, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61,
0x63, 0x74, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -2024,6 +2039,7 @@ func file_workflows_proto_init() {
}
}
file_workflows_proto_msgTypes[1].OneofWrappers = []interface{}{}
file_workflows_proto_msgTypes[2].OneofWrappers = []interface{}{}
file_workflows_proto_msgTypes[4].OneofWrappers = []interface{}{}
file_workflows_proto_msgTypes[8].OneofWrappers = []interface{}{}
file_workflows_proto_msgTypes[12].OneofWrappers = []interface{}{}
+9 -4
View File
@@ -457,6 +457,13 @@ func getCreateWorkflowOpts(req *contracts.PutWorkflowRequest) (*repository.Creat
var concurrency *repository.CreateWorkflowConcurrencyOpts
if req.Opts.Concurrency != nil {
if req.Opts.Concurrency.Action == nil && req.Opts.Concurrency.Expression == nil {
return nil, status.Error(
codes.InvalidArgument,
"concurrency action or expression is required",
)
}
var limitStrategy *string
if req.Opts.Concurrency.LimitStrategy.String() != "" {
@@ -466,10 +473,8 @@ func getCreateWorkflowOpts(req *contracts.PutWorkflowRequest) (*repository.Creat
concurrency = &repository.CreateWorkflowConcurrencyOpts{
Action: req.Opts.Concurrency.Action,
LimitStrategy: limitStrategy,
}
if req.Opts.Concurrency.MaxRuns != 0 {
concurrency.MaxRuns = &req.Opts.Concurrency.MaxRuns
Expression: req.Opts.Concurrency.Expression,
MaxRuns: req.Opts.Concurrency.MaxRuns,
}
}
@@ -864,8 +864,8 @@ func (ec *JobsControllerImpl) queueStepRun(ctx context.Context, tenantId, stepId
_, err = ec.repo.StepRun().QueueStepRun(ctx, tenantId, stepRunId, queueOpts)
if err != nil {
if errors.Is(err, repository.ErrStepRunIsNotPending) {
ec.l.Debug().Msgf("step run %s is not pending, skipping scheduling", stepRunId)
if errors.Is(err, repository.ErrAlreadyQueued) {
ec.l.Debug().Msgf("step run %s is already queued, skipping scheduling", stepRunId)
return nil
}
+11 -136
View File
@@ -12,6 +12,7 @@ import (
"github.com/hatchet-dev/hatchet/internal/datautils"
"github.com/hatchet-dev/hatchet/internal/msgqueue"
"github.com/hatchet-dev/hatchet/internal/queueutils"
"github.com/hatchet-dev/hatchet/internal/services/controllers/partition"
"github.com/hatchet-dev/hatchet/internal/services/shared/recoveryutils"
"github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes"
@@ -34,8 +35,8 @@ type queue struct {
// a custom queue logger
ql *zerolog.Logger
tenantQueueOperations sync.Map
updateStepRunOperations sync.Map
tenantQueueOperations *queueutils.OperationPool
updateStepRunOperations *queueutils.OperationPool
}
func newQueue(
@@ -53,7 +54,7 @@ func newQueue(
return nil, fmt.Errorf("could not create scheduler: %w", err)
}
return &queue{
q := &queue{
mq: mq,
l: l,
repo: repo,
@@ -62,95 +63,12 @@ func newQueue(
a: a,
p: p,
ql: ql,
}, nil
}
type operation struct {
mu sync.RWMutex
shouldContinue bool
isRunning bool
tenantId string
lastRun time.Time
description string
timeout time.Duration
}
func (o *operation) runOrContinue(l *zerolog.Logger, ql *zerolog.Logger, scheduler func(context.Context, string) (bool, error)) {
o.setContinue(true)
o.run(l, ql, scheduler)
}
func (o *operation) run(l *zerolog.Logger, ql *zerolog.Logger, scheduler func(context.Context, string) (bool, error)) {
if !o.setRunning(true, ql) {
return
}
go func() {
defer func() {
o.setRunning(false, ql)
}()
q.tenantQueueOperations = queueutils.NewOperationPool(ql, time.Second*5, "check tenant queue", q.scheduleStepRuns)
q.updateStepRunOperations = queueutils.NewOperationPool(ql, time.Second*30, "update step runs", q.processStepRunUpdates)
f := func() {
o.setContinue(false)
ctx, cancel := context.WithTimeout(context.Background(), o.timeout)
defer cancel()
shouldContinue, err := scheduler(ctx, o.tenantId)
if err != nil {
l.Err(err).Msgf("could not %s", o.description)
return
}
// if a continue was set during execution of the scheduler, we'd like to continue no matter what.
// if a continue was not set, we'd like to set it to the value returned by the scheduler.
if !o.getContinue() {
o.setContinue(shouldContinue)
}
}
f()
for o.getContinue() {
f()
}
}()
}
// setRunning sets the running state of the operation and returns true if the state was changed,
// false if the state was not changed.
func (o *operation) setRunning(isRunning bool, ql *zerolog.Logger) bool {
o.mu.Lock()
defer o.mu.Unlock()
if isRunning == o.isRunning {
return false
}
if isRunning {
ql.Info().Str("tenant_id", o.tenantId).TimeDiff("last_run", time.Now(), o.lastRun).Msg(o.description)
o.lastRun = time.Now()
}
o.isRunning = isRunning
return true
}
func (o *operation) setContinue(shouldContinue bool) {
o.mu.Lock()
defer o.mu.Unlock()
o.shouldContinue = shouldContinue
}
func (o *operation) getContinue() bool {
o.mu.RLock()
defer o.mu.RUnlock()
return o.shouldContinue
return q, nil
}
func (q *queue) Start() (func() error, error) {
@@ -254,17 +172,8 @@ func (q *queue) handleCheckQueue(ctx context.Context, task *msgqueue.Message) er
}
// if this tenant is registered, then we should check the queue
if opInt, ok := q.tenantQueueOperations.Load(metadata.TenantId); ok {
op := opInt.(*operation)
op.runOrContinue(q.l, q.ql, q.scheduleStepRuns)
}
if opInt, ok := q.updateStepRunOperations.Load(metadata.TenantId); ok {
op := opInt.(*operation)
op.runOrContinue(q.l, q.ql, q.processStepRunUpdates)
}
q.tenantQueueOperations.RunOrContinue(metadata.TenantId)
q.updateStepRunOperations.RunOrContinue(metadata.TenantId)
return nil
}
@@ -284,7 +193,7 @@ func (q *queue) runTenantQueues(ctx context.Context) func() {
for i := range tenants {
tenantId := sqlchelpers.UUIDToStr(tenants[i].ID)
q.getQueueOperation(tenantId).run(q.l, q.ql, q.scheduleStepRuns)
q.tenantQueueOperations.RunOrContinue(tenantId)
}
}
}
@@ -334,40 +243,6 @@ func (q *queue) scheduleStepRuns(ctx context.Context, tenantId string) (bool, er
return queueResults.Continue, err
}
func (q *queue) getQueueOperation(tenantId string) *operation {
op, ok := q.tenantQueueOperations.Load(tenantId)
if !ok {
op = &operation{
tenantId: tenantId,
lastRun: time.Now(),
description: "scheduling step runs",
timeout: 30 * time.Second,
}
q.tenantQueueOperations.Store(tenantId, op)
}
return op.(*operation)
}
func (q *queue) getUpdateStepRunOperation(tenantId string) *operation {
op, ok := q.updateStepRunOperations.Load(tenantId)
if !ok {
op = &operation{
tenantId: tenantId,
lastRun: time.Now(),
description: "updating step runs",
timeout: 300 * time.Second,
}
q.updateStepRunOperations.Store(tenantId, op)
}
return op.(*operation)
}
func (q *queue) runTenantUpdateStepRuns(ctx context.Context) func() {
return func() {
q.l.Debug().Msgf("partition: updating step run statuses")
@@ -383,7 +258,7 @@ func (q *queue) runTenantUpdateStepRuns(ctx context.Context) func() {
for i := range tenants {
tenantId := sqlchelpers.UUIDToStr(tenants[i].ID)
q.getUpdateStepRunOperation(tenantId).run(q.l, q.ql, q.processStepRunUpdates)
q.updateStepRunOperations.RunOrContinue(tenantId)
}
}
}
@@ -10,9 +10,11 @@ import (
"github.com/rs/zerolog"
"golang.org/x/sync/errgroup"
"github.com/hatchet-dev/hatchet/internal/cel"
"github.com/hatchet-dev/hatchet/internal/datautils"
"github.com/hatchet-dev/hatchet/internal/integrations/alerting"
"github.com/hatchet-dev/hatchet/internal/msgqueue"
"github.com/hatchet-dev/hatchet/internal/queueutils"
"github.com/hatchet-dev/hatchet/internal/services/controllers/partition"
"github.com/hatchet-dev/hatchet/internal/services/shared/recoveryutils"
"github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes"
@@ -30,14 +32,16 @@ type WorkflowsController interface {
}
type WorkflowsControllerImpl struct {
mq msgqueue.MessageQueue
l *zerolog.Logger
repo repository.EngineRepository
dv datautils.DataDecoderValidator
s gocron.Scheduler
tenantAlerter *alerting.TenantAlertManager
a *hatcheterrors.Wrapped
p *partition.Partition
mq msgqueue.MessageQueue
l *zerolog.Logger
repo repository.EngineRepository
dv datautils.DataDecoderValidator
s gocron.Scheduler
tenantAlerter *alerting.TenantAlertManager
a *hatcheterrors.Wrapped
p *partition.Partition
celParser *cel.CELParser
processWorkflowEventsOps *queueutils.OperationPool
}
type WorkflowsControllerOpt func(*WorkflowsControllerOpts)
@@ -140,7 +144,7 @@ func New(fs ...WorkflowsControllerOpt) (*WorkflowsControllerImpl, error) {
a := hatcheterrors.NewWrapped(opts.alerter)
a.WithData(map[string]interface{}{"service": "workflows-controller"})
return &WorkflowsControllerImpl{
w := &WorkflowsControllerImpl{
mq: opts.mq,
l: opts.l,
repo: opts.repo,
@@ -149,7 +153,12 @@ func New(fs ...WorkflowsControllerOpt) (*WorkflowsControllerImpl, error) {
tenantAlerter: opts.ta,
a: a,
p: opts.p,
}, nil
celParser: cel.NewCELParser(),
}
w.processWorkflowEventsOps = queueutils.NewOperationPool(w.l, time.Second*5, "process workflow events", w.processWorkflowEvents)
return w, nil
}
func (wc *WorkflowsControllerImpl) Start() (func() error, error) {
@@ -195,6 +204,18 @@ func (wc *WorkflowsControllerImpl) Start() (func() error, error) {
return nil, fmt.Errorf("could not poll active queues: %w", err)
}
_, err = wc.s.NewJob(
gocron.DurationJob(time.Second*1),
gocron.NewTask(
wc.runTenantProcessWorkflowRunEvents(ctx),
),
)
if err != nil {
cancel()
return nil, fmt.Errorf("could not schedule process workflow run events: %w", err)
}
wc.s.Start()
f := func(task *msgqueue.Message) error {
@@ -531,3 +552,39 @@ func (wc *WorkflowsControllerImpl) cancelGetGroupKeyRun(ctx context.Context, ten
return wc.cancelWorkflowRunJobs(ctx, workflowRun)
}
func (wc *WorkflowsControllerImpl) runTenantProcessWorkflowRunEvents(ctx context.Context) func() {
return func() {
wc.l.Debug().Msgf("partition: processing workflow run events")
// list all tenants
tenants, err := wc.repo.Tenant().ListTenantsByControllerPartition(ctx, wc.p.GetControllerPartitionId())
if err != nil {
wc.l.Err(err).Msg("could not list tenants")
return
}
for i := range tenants {
tenantId := sqlchelpers.UUIDToStr(tenants[i].ID)
wc.processWorkflowEventsOps.RunOrContinue(tenantId)
}
}
}
func (wc *WorkflowsControllerImpl) processWorkflowEvents(ctx context.Context, tenantId string) (bool, error) {
ctx, span := telemetry.NewSpan(ctx, "process-workflow-events")
defer span.End()
dbCtx, cancel := context.WithTimeout(ctx, 300*time.Second)
defer cancel()
res, err := wc.repo.WorkflowRun().ProcessWorkflowRunUpdates(dbCtx, tenantId)
if err != nil {
return false, fmt.Errorf("could not process step run updates: %w", err)
}
return res, nil
}
@@ -2,6 +2,7 @@ package workflows
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
@@ -9,6 +10,7 @@ import (
"github.com/hashicorp/go-multierror"
"golang.org/x/sync/errgroup"
"github.com/hatchet-dev/hatchet/internal/cel"
"github.com/hatchet-dev/hatchet/internal/datautils"
"github.com/hatchet-dev/hatchet/internal/msgqueue"
"github.com/hatchet-dev/hatchet/internal/services/shared/tasktypes"
@@ -36,6 +38,7 @@ func (wc *WorkflowsControllerImpl) handleWorkflowRunQueued(ctx context.Context,
err = wc.dv.DecodeAndValidate(task.Metadata, &metadata)
if err != nil {
return fmt.Errorf("could not decode job task metadata: %w", err)
}
@@ -76,7 +79,30 @@ func (wc *WorkflowsControllerImpl) handleWorkflowRunQueued(ctx context.Context,
}
return nil
} else if workflowRun.ConcurrencyLimitStrategy.Valid && !workflowRun.GetGroupKeyRunId.Valid {
} else if workflowRun.ConcurrencyLimitStrategy.Valid && workflowRun.ConcurrencyGroupExpression.Valid {
// if the workflow has a concurrency group expression, then we need to evaluate it
// and see if we can start the workflow run
wc.l.Info().Msgf("workflow %s has concurrency settings", workflowRunId)
groupKey, err := wc.evalWorkflowRunConcurrency(ctx, metadata.TenantId, workflowRunId, workflowRun.ConcurrencyGroupExpression.String)
if err != nil {
return fmt.Errorf("could not evaluate concurrency group expression: %w", err)
}
if groupKey == nil {
// fail the workflow run
workflowRun, err := wc.repo.WorkflowRun().GetWorkflowRunById(ctx, metadata.TenantId, workflowRunId)
if err != nil {
return fmt.Errorf("could not get workflow run: %w", err)
}
return wc.cancelWorkflowRunJobs(ctx, workflowRun)
}
return wc.bumpQueue(ctx, metadata.TenantId, sqlchelpers.UUIDToStr(workflowRun.WorkflowVersion.ID), groupKey)
} else if workflowRun.ConcurrencyLimitStrategy.Valid {
return fmt.Errorf("workflow run %s has concurrency settings but no group key run", workflowRunId)
}
@@ -89,6 +115,54 @@ func (wc *WorkflowsControllerImpl) handleWorkflowRunQueued(ctx context.Context,
return nil
}
func (wc *WorkflowsControllerImpl) evalWorkflowRunConcurrency(ctx context.Context, tenantId, workflowRunId, expr string) (*string, error) {
input, err := wc.repo.WorkflowRun().GetWorkflowRunInputData(tenantId, workflowRunId)
if err != nil {
return nil, fmt.Errorf("could not get workflow run input data: %w", err)
}
addMetaRes, err := wc.repo.WorkflowRun().GetWorkflowRunAdditionalMeta(ctx, tenantId, workflowRunId)
if err != nil {
return nil, fmt.Errorf("could not get workflow run additional meta: %w", err)
}
addMeta := map[string]interface{}{}
if addMetaRes.AdditionalMetadata != nil {
err = json.Unmarshal(addMetaRes.AdditionalMetadata, &addMeta)
if err != nil {
return nil, fmt.Errorf("could not decode additional metadata: %w", err)
}
}
concurrencyGroupId, err := wc.celParser.ParseAndEvalWorkflowString(expr, cel.NewWorkflowStringInput(
cel.WithInput(input),
cel.WithAdditionalMetadata(addMeta),
cel.WithWorkflowRunID(workflowRunId),
))
opts := &repository.UpdateWorkflowRunFromGroupKeyEvalOpts{}
if err != nil {
opts.Error = repository.StringPtr(fmt.Sprintf("could not evaluate concurrency group expression %s: %s", expr, err.Error()))
wc.l.Err(err).Msgf("could not evaluate concurrency group expression for tenant %s and workflow run %s", tenantId, workflowRunId)
} else {
opts.GroupKey = repository.StringPtr(concurrencyGroupId)
}
err = wc.repo.WorkflowRun().UpdateWorkflowRunFromGroupKeyEval(ctx, tenantId, workflowRunId, opts)
if err != nil {
return nil, fmt.Errorf("could not update workflow run from group key eval: %w", err)
}
return opts.GroupKey, nil
}
func (wc *WorkflowsControllerImpl) handleWorkflowRunFinished(ctx context.Context, task *msgqueue.Message) error {
ctx, span := telemetry.NewSpan(ctx, "handle-workflow-run-finished")
defer span.End()
+7 -1
View File
@@ -500,6 +500,8 @@ func (d *DispatcherImpl) handleStepRunAssignedTask(ctx context.Context, task *ms
}
}
now := time.Now().UTC()
if success {
defer d.repo.StepRun().DeferredStepRunEvent(
metadata.TenantId,
@@ -508,6 +510,8 @@ func (d *DispatcherImpl) handleStepRunAssignedTask(ctx context.Context, task *ms
EventMessage: repository.StringPtr("Sent step run to the assigned worker"),
EventReason: repository.StepRunEventReasonPtr(dbsqlc.StepRunEventReasonSENTTOWORKER),
EventSeverity: repository.StepRunEventSeverityPtr(dbsqlc.StepRunEventSeverityINFO),
Timestamp: &now,
EventData: map[string]interface{}{"worker_id": payload.WorkerId},
},
)
@@ -521,6 +525,8 @@ func (d *DispatcherImpl) handleStepRunAssignedTask(ctx context.Context, task *ms
EventMessage: repository.StringPtr("Could not send step run to assigned worker"),
EventReason: repository.StepRunEventReasonPtr(dbsqlc.StepRunEventReasonREASSIGNED),
EventSeverity: repository.StepRunEventSeverityPtr(dbsqlc.StepRunEventSeverityWARNING),
Timestamp: &now,
EventData: map[string]interface{}{"worker_id": payload.WorkerId},
},
)
@@ -529,7 +535,7 @@ func (d *DispatcherImpl) handleStepRunAssignedTask(ctx context.Context, task *ms
IsInternalRetry: true,
})
if err != nil {
if err != nil && !errors.Is(err, repository.ErrAlreadyRunning) {
multiErr = multierror.Append(multiErr, fmt.Errorf("💥 could not requeue step run in dispatcher: %w", err))
}
+11 -5
View File
@@ -303,21 +303,27 @@ func (a *adminClientImpl) getPutRequest(workflow *types.Workflow) (*admincontrac
if workflow.Concurrency != nil {
opts.Concurrency = &admincontracts.WorkflowConcurrencyOpts{
Action: workflow.Concurrency.ActionID,
Action: workflow.Concurrency.ActionID,
Expression: workflow.Concurrency.Expression,
}
var limitStrat admincontracts.ConcurrencyLimitStrategy
switch workflow.Concurrency.LimitStrategy {
case types.CancelInProgress:
opts.Concurrency.LimitStrategy = admincontracts.ConcurrencyLimitStrategy_CANCEL_IN_PROGRESS
limitStrat = admincontracts.ConcurrencyLimitStrategy_CANCEL_IN_PROGRESS
case types.GroupRoundRobin:
opts.Concurrency.LimitStrategy = admincontracts.ConcurrencyLimitStrategy_GROUP_ROUND_ROBIN
limitStrat = admincontracts.ConcurrencyLimitStrategy_GROUP_ROUND_ROBIN
default:
opts.Concurrency.LimitStrategy = admincontracts.ConcurrencyLimitStrategy_CANCEL_IN_PROGRESS
limitStrat = admincontracts.ConcurrencyLimitStrategy_CANCEL_IN_PROGRESS
}
opts.Concurrency.LimitStrategy = &limitStrat
// TODO: should be a pointer because users might want to set maxRuns temporarily for disabling
if workflow.Concurrency.MaxRuns != 0 {
opts.Concurrency.MaxRuns = workflow.Concurrency.MaxRuns
maxRuns := workflow.Concurrency.MaxRuns
opts.Concurrency.MaxRuns = &maxRuns
}
}
+18 -15
View File
@@ -64,20 +64,22 @@ const (
// Defines values for StepRunEventReason.
const (
StepRunEventReasonASSIGNED StepRunEventReason = "ASSIGNED"
StepRunEventReasonCANCELLED StepRunEventReason = "CANCELLED"
StepRunEventReasonFAILED StepRunEventReason = "FAILED"
StepRunEventReasonFINISHED StepRunEventReason = "FINISHED"
StepRunEventReasonREASSIGNED StepRunEventReason = "REASSIGNED"
StepRunEventReasonREQUEUEDNOWORKER StepRunEventReason = "REQUEUED_NO_WORKER"
StepRunEventReasonREQUEUEDRATELIMIT StepRunEventReason = "REQUEUED_RATE_LIMIT"
StepRunEventReasonRETRIEDBYUSER StepRunEventReason = "RETRIED_BY_USER"
StepRunEventReasonRETRYING StepRunEventReason = "RETRYING"
StepRunEventReasonSCHEDULINGTIMEDOUT StepRunEventReason = "SCHEDULING_TIMED_OUT"
StepRunEventReasonSLOTRELEASED StepRunEventReason = "SLOT_RELEASED"
StepRunEventReasonSTARTED StepRunEventReason = "STARTED"
StepRunEventReasonTIMEDOUT StepRunEventReason = "TIMED_OUT"
StepRunEventReasonTIMEOUTREFRESHED StepRunEventReason = "TIMEOUT_REFRESHED"
StepRunEventReasonASSIGNED StepRunEventReason = "ASSIGNED"
StepRunEventReasonCANCELLED StepRunEventReason = "CANCELLED"
StepRunEventReasonFAILED StepRunEventReason = "FAILED"
StepRunEventReasonFINISHED StepRunEventReason = "FINISHED"
StepRunEventReasonREASSIGNED StepRunEventReason = "REASSIGNED"
StepRunEventReasonREQUEUEDNOWORKER StepRunEventReason = "REQUEUED_NO_WORKER"
StepRunEventReasonREQUEUEDRATELIMIT StepRunEventReason = "REQUEUED_RATE_LIMIT"
StepRunEventReasonRETRIEDBYUSER StepRunEventReason = "RETRIED_BY_USER"
StepRunEventReasonRETRYING StepRunEventReason = "RETRYING"
StepRunEventReasonSCHEDULINGTIMEDOUT StepRunEventReason = "SCHEDULING_TIMED_OUT"
StepRunEventReasonSLOTRELEASED StepRunEventReason = "SLOT_RELEASED"
StepRunEventReasonSTARTED StepRunEventReason = "STARTED"
StepRunEventReasonTIMEDOUT StepRunEventReason = "TIMED_OUT"
StepRunEventReasonTIMEOUTREFRESHED StepRunEventReason = "TIMEOUT_REFRESHED"
StepRunEventReasonWORKFLOWRUNGROUPKEYFAILED StepRunEventReason = "WORKFLOW_RUN_GROUP_KEY_FAILED"
StepRunEventReasonWORKFLOWRUNGROUPKEYSUCCEEDED StepRunEventReason = "WORKFLOW_RUN_GROUP_KEY_SUCCEEDED"
)
// Defines values for StepRunEventSeverity.
@@ -666,9 +668,10 @@ type StepRunEvent struct {
Message string `json:"message"`
Reason StepRunEventReason `json:"reason"`
Severity StepRunEventSeverity `json:"severity"`
StepRunId string `json:"stepRunId"`
StepRunId *string `json:"stepRunId,omitempty"`
TimeFirstSeen time.Time `json:"timeFirstSeen"`
TimeLastSeen time.Time `json:"timeLastSeen"`
WorkflowRunId *string `json:"workflowRunId,omitempty"`
}
// StepRunEventList defines model for StepRunEventList.
+3 -1
View File
@@ -48,7 +48,9 @@ const (
)
type WorkflowConcurrency struct {
ActionID string `yaml:"action,omitempty"`
ActionID *string `yaml:"action,omitempty"`
Expression *string `yaml:"expression,omitempty"`
MaxRuns int32 `yaml:"maxRuns,omitempty"`
File diff suppressed because it is too large Load Diff
+27 -22
View File
@@ -60,6 +60,7 @@ type InternalQueue string
const (
InternalQueueWORKERSEMAPHORECOUNT InternalQueue = "WORKER_SEMAPHORE_COUNT"
InternalQueueSTEPRUNUPDATE InternalQueue = "STEP_RUN_UPDATE"
InternalQueueWORKFLOWRUNUPDATE InternalQueue = "WORKFLOW_RUN_UPDATE"
)
func (e *InternalQueue) Scan(src interface{}) error {
@@ -319,21 +320,23 @@ func (ns NullLogLineLevel) Value() (driver.Value, error) {
type StepRunEventReason string
const (
StepRunEventReasonREQUEUEDNOWORKER StepRunEventReason = "REQUEUED_NO_WORKER"
StepRunEventReasonREQUEUEDRATELIMIT StepRunEventReason = "REQUEUED_RATE_LIMIT"
StepRunEventReasonSCHEDULINGTIMEDOUT StepRunEventReason = "SCHEDULING_TIMED_OUT"
StepRunEventReasonASSIGNED StepRunEventReason = "ASSIGNED"
StepRunEventReasonSTARTED StepRunEventReason = "STARTED"
StepRunEventReasonFINISHED StepRunEventReason = "FINISHED"
StepRunEventReasonFAILED StepRunEventReason = "FAILED"
StepRunEventReasonRETRYING StepRunEventReason = "RETRYING"
StepRunEventReasonCANCELLED StepRunEventReason = "CANCELLED"
StepRunEventReasonTIMEDOUT StepRunEventReason = "TIMED_OUT"
StepRunEventReasonREASSIGNED StepRunEventReason = "REASSIGNED"
StepRunEventReasonSLOTRELEASED StepRunEventReason = "SLOT_RELEASED"
StepRunEventReasonTIMEOUTREFRESHED StepRunEventReason = "TIMEOUT_REFRESHED"
StepRunEventReasonRETRIEDBYUSER StepRunEventReason = "RETRIED_BY_USER"
StepRunEventReasonSENTTOWORKER StepRunEventReason = "SENT_TO_WORKER"
StepRunEventReasonREQUEUEDNOWORKER StepRunEventReason = "REQUEUED_NO_WORKER"
StepRunEventReasonREQUEUEDRATELIMIT StepRunEventReason = "REQUEUED_RATE_LIMIT"
StepRunEventReasonSCHEDULINGTIMEDOUT StepRunEventReason = "SCHEDULING_TIMED_OUT"
StepRunEventReasonASSIGNED StepRunEventReason = "ASSIGNED"
StepRunEventReasonSTARTED StepRunEventReason = "STARTED"
StepRunEventReasonFINISHED StepRunEventReason = "FINISHED"
StepRunEventReasonFAILED StepRunEventReason = "FAILED"
StepRunEventReasonRETRYING StepRunEventReason = "RETRYING"
StepRunEventReasonCANCELLED StepRunEventReason = "CANCELLED"
StepRunEventReasonTIMEDOUT StepRunEventReason = "TIMED_OUT"
StepRunEventReasonREASSIGNED StepRunEventReason = "REASSIGNED"
StepRunEventReasonSLOTRELEASED StepRunEventReason = "SLOT_RELEASED"
StepRunEventReasonTIMEOUTREFRESHED StepRunEventReason = "TIMEOUT_REFRESHED"
StepRunEventReasonRETRIEDBYUSER StepRunEventReason = "RETRIED_BY_USER"
StepRunEventReasonSENTTOWORKER StepRunEventReason = "SENT_TO_WORKER"
StepRunEventReasonWORKFLOWRUNGROUPKEYSUCCEEDED StepRunEventReason = "WORKFLOW_RUN_GROUP_KEY_SUCCEEDED"
StepRunEventReasonWORKFLOWRUNGROUPKEYFAILED StepRunEventReason = "WORKFLOW_RUN_GROUP_KEY_FAILED"
)
func (e *StepRunEventReason) Scan(src interface{}) error {
@@ -1146,6 +1149,7 @@ type StepRunEvent struct {
Message string `json:"message"`
Count int32 `json:"count"`
Data []byte `json:"data"`
WorkflowRunId pgtype.UUID `json:"workflowRunId"`
}
type StepRunOrder struct {
@@ -1407,13 +1411,14 @@ type Workflow struct {
}
type WorkflowConcurrency struct {
ID pgtype.UUID `json:"id"`
CreatedAt pgtype.Timestamp `json:"createdAt"`
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
WorkflowVersionId pgtype.UUID `json:"workflowVersionId"`
GetConcurrencyGroupId pgtype.UUID `json:"getConcurrencyGroupId"`
MaxRuns int32 `json:"maxRuns"`
LimitStrategy ConcurrencyLimitStrategy `json:"limitStrategy"`
ID pgtype.UUID `json:"id"`
CreatedAt pgtype.Timestamp `json:"createdAt"`
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
WorkflowVersionId pgtype.UUID `json:"workflowVersionId"`
GetConcurrencyGroupId pgtype.UUID `json:"getConcurrencyGroupId"`
MaxRuns int32 `json:"maxRuns"`
LimitStrategy ConcurrencyLimitStrategy `json:"limitStrategy"`
ConcurrencyGroupExpression pgtype.Text `json:"concurrencyGroupExpression"`
}
type WorkflowRun struct {
+9 -7
View File
@@ -2,7 +2,7 @@
CREATE TYPE "ConcurrencyLimitStrategy" AS ENUM ('CANCEL_IN_PROGRESS', 'DROP_NEWEST', 'QUEUE_NEWEST', 'GROUP_ROUND_ROBIN');
-- CreateEnum
CREATE TYPE "InternalQueue" AS ENUM ('WORKER_SEMAPHORE_COUNT', 'STEP_RUN_UPDATE');
CREATE TYPE "InternalQueue" AS ENUM ('WORKER_SEMAPHORE_COUNT', 'STEP_RUN_UPDATE', 'WORKFLOW_RUN_UPDATE');
-- CreateEnum
CREATE TYPE "InviteLinkStatus" AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');
@@ -20,7 +20,7 @@ CREATE TYPE "LimitResource" AS ENUM ('WORKFLOW_RUN', 'EVENT', 'WORKER', 'CRON',
CREATE TYPE "LogLineLevel" AS ENUM ('DEBUG', 'INFO', 'WARN', 'ERROR');
-- CreateEnum
CREATE TYPE "StepRunEventReason" AS ENUM ('REQUEUED_NO_WORKER', 'REQUEUED_RATE_LIMIT', 'SCHEDULING_TIMED_OUT', 'ASSIGNED', 'STARTED', 'FINISHED', 'FAILED', 'RETRYING', 'CANCELLED', 'TIMED_OUT', 'REASSIGNED', 'SLOT_RELEASED', 'TIMEOUT_REFRESHED', 'RETRIED_BY_USER', 'SENT_TO_WORKER');
CREATE TYPE "StepRunEventReason" AS ENUM ('REQUEUED_NO_WORKER', 'REQUEUED_RATE_LIMIT', 'SCHEDULING_TIMED_OUT', 'ASSIGNED', 'STARTED', 'FINISHED', 'FAILED', 'RETRYING', 'CANCELLED', 'TIMED_OUT', 'REASSIGNED', 'SLOT_RELEASED', 'TIMEOUT_REFRESHED', 'RETRIED_BY_USER', 'SENT_TO_WORKER', 'WORKFLOW_RUN_GROUP_KEY_SUCCEEDED', 'WORKFLOW_RUN_GROUP_KEY_FAILED');
-- CreateEnum
CREATE TYPE "StepRunEventSeverity" AS ENUM ('INFO', 'WARNING', 'CRITICAL');
@@ -396,12 +396,13 @@ CREATE TABLE "StepRunEvent" (
"id" BIGSERIAL NOT NULL,
"timeFirstSeen" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"timeLastSeen" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"stepRunId" UUID NOT NULL,
"stepRunId" UUID,
"reason" "StepRunEventReason" NOT NULL,
"severity" "StepRunEventSeverity" NOT NULL,
"message" TEXT NOT NULL,
"count" INTEGER NOT NULL,
"data" JSONB
"data" JSONB,
"workflowRunId" UUID
);
-- CreateTable
@@ -736,6 +737,7 @@ CREATE TABLE "WorkflowConcurrency" (
"getConcurrencyGroupId" UUID,
"maxRuns" INTEGER NOT NULL DEFAULT 1,
"limitStrategy" "ConcurrencyLimitStrategy" NOT NULL DEFAULT 'CANCEL_IN_PROGRESS',
"concurrencyGroupExpression" TEXT,
CONSTRAINT "WorkflowConcurrency_pkey" PRIMARY KEY ("id")
);
@@ -1074,6 +1076,9 @@ CREATE UNIQUE INDEX "StepRunEvent_id_key" ON "StepRunEvent"("id" ASC);
-- CreateIndex
CREATE INDEX "StepRunEvent_stepRunId_idx" ON "StepRunEvent"("stepRunId" ASC);
-- CreateIndex
CREATE INDEX "StepRunEvent_workflowRunId_idx" ON "StepRunEvent"("workflowRunId" ASC);
-- CreateIndex
CREATE UNIQUE INDEX "StepRunResultArchive_id_key" ON "StepRunResultArchive"("id" ASC);
@@ -1422,9 +1427,6 @@ ALTER TABLE "StepRun" ADD CONSTRAINT "StepRun_tickerId_fkey" FOREIGN KEY ("ticke
-- AddForeignKey
ALTER TABLE "StepRun" ADD CONSTRAINT "StepRun_workerId_fkey" FOREIGN KEY ("workerId") REFERENCES "Worker"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "StepRunEvent" ADD CONSTRAINT "StepRunEvent_stepRunId_fkey" FOREIGN KEY ("stepRunId") REFERENCES "StepRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "StepRunResultArchive" ADD CONSTRAINT "StepRunResultArchive_stepRunId_fkey" FOREIGN KEY ("stepRunId") REFERENCES "StepRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+10 -1
View File
@@ -474,6 +474,16 @@ deleted_sqis AS (
sqi."stepRunId" = srs."id"
AND sqi."workerId" = srs."workerId"
),
deleted_tqis AS (
DELETE FROM
"TimeoutQueueItem" tqi
-- delete when step run id AND retry count tuples match
USING
step_runs_to_reassign srs
WHERE
tqi."stepRunId" = srs."id"
AND tqi."retryCount" = srs."retryCount"
),
inserted_queue_items AS (
INSERT INTO "QueueItem" (
"stepRunId",
@@ -546,7 +556,6 @@ RETURNING *;
WITH oldsr AS (
SELECT
"id",
"workerId",
"retryCount"
FROM
"StepRun"
+16 -5
View File
@@ -183,7 +183,7 @@ updated AS (
ORDER BY "id" DESC
LIMIT 1
)
RETURNING "StepRunEvent".id, "StepRunEvent"."timeFirstSeen", "StepRunEvent"."timeLastSeen", "StepRunEvent"."stepRunId", "StepRunEvent".reason, "StepRunEvent".severity, "StepRunEvent".message, "StepRunEvent".count, "StepRunEvent".data
RETURNING "StepRunEvent".id, "StepRunEvent"."timeFirstSeen", "StepRunEvent"."timeLastSeen", "StepRunEvent"."stepRunId", "StepRunEvent".reason, "StepRunEvent".severity, "StepRunEvent".message, "StepRunEvent".count, "StepRunEvent".data, "StepRunEvent"."workflowRunId"
)
INSERT INTO "StepRunEvent" (
"timeFirstSeen",
@@ -514,7 +514,7 @@ updated AS (
ORDER BY "id" DESC
LIMIT 1
)
RETURNING "StepRunEvent".id, "StepRunEvent"."timeFirstSeen", "StepRunEvent"."timeLastSeen", "StepRunEvent"."stepRunId", "StepRunEvent".reason, "StepRunEvent".severity, "StepRunEvent".message, "StepRunEvent".count, "StepRunEvent".data
RETURNING "StepRunEvent".id, "StepRunEvent"."timeFirstSeen", "StepRunEvent"."timeLastSeen", "StepRunEvent"."stepRunId", "StepRunEvent".reason, "StepRunEvent".severity, "StepRunEvent".message, "StepRunEvent".count, "StepRunEvent".data, "StepRunEvent"."workflowRunId"
)
INSERT INTO "StepRunEvent" (
"timeFirstSeen",
@@ -1525,7 +1525,7 @@ func (q *Queries) ListStepRunArchives(ctx context.Context, db DBTX, arg ListStep
const listStepRunEvents = `-- name: ListStepRunEvents :many
SELECT
id, "timeFirstSeen", "timeLastSeen", "stepRunId", reason, severity, message, count, data
id, "timeFirstSeen", "timeLastSeen", "stepRunId", reason, severity, message, count, data, "workflowRunId"
FROM
"StepRunEvent"
WHERE
@@ -1563,6 +1563,7 @@ func (q *Queries) ListStepRunEvents(ctx context.Context, db DBTX, arg ListStepRu
&i.Message,
&i.Count,
&i.Data,
&i.WorkflowRunId,
); err != nil {
return nil, err
}
@@ -1576,7 +1577,7 @@ func (q *Queries) ListStepRunEvents(ctx context.Context, db DBTX, arg ListStepRu
const listStepRunEventsByWorkflowRunId = `-- name: ListStepRunEventsByWorkflowRunId :many
SELECT
sre.id, sre."timeFirstSeen", sre."timeLastSeen", sre."stepRunId", sre.reason, sre.severity, sre.message, sre.count, sre.data
sre.id, sre."timeFirstSeen", sre."timeLastSeen", sre."stepRunId", sre.reason, sre.severity, sre.message, sre.count, sre.data, sre."workflowRunId"
FROM
"StepRunEvent" sre
JOIN
@@ -1617,6 +1618,7 @@ func (q *Queries) ListStepRunEventsByWorkflowRunId(ctx context.Context, db DBTX,
&i.Message,
&i.Count,
&i.Data,
&i.WorkflowRunId,
); err != nil {
return nil, err
}
@@ -1729,6 +1731,16 @@ deleted_sqis AS (
sqi."stepRunId" = srs."id"
AND sqi."workerId" = srs."workerId"
),
deleted_tqis AS (
DELETE FROM
"TimeoutQueueItem" tqi
-- delete when step run id AND retry count tuples match
USING
step_runs_to_reassign srs
WHERE
tqi."stepRunId" = srs."id"
AND tqi."retryCount" = srs."retryCount"
),
inserted_queue_items AS (
INSERT INTO "QueueItem" (
"stepRunId",
@@ -2425,7 +2437,6 @@ const updateStepRunUnsetWorkerId = `-- name: UpdateStepRunUnsetWorkerId :one
WITH oldsr AS (
SELECT
"id",
"workerId",
"retryCount"
FROM
"StepRun"
+93 -1
View File
@@ -294,7 +294,31 @@ WHERE
RETURNING
"WorkflowRun".*;
-- name: UpdateWorkflowRunGroupKey :one
-- name: UpdateWorkflowRunGroupKeyFromExpr :one
UPDATE "WorkflowRun" wr
SET "error" = CASE
-- Final states are final, cannot be updated. We also can't move out of a queued state
WHEN "status" IN ('SUCCEEDED', 'FAILED', 'QUEUED') THEN "error"
WHEN sqlc.narg('error')::text IS NOT NULL THEN sqlc.narg('error')::text
ELSE "error"
END,
"status" = CASE
-- Final states are final, cannot be updated. We also can't move out of a queued state
WHEN "status" IN ('SUCCEEDED', 'FAILED', 'QUEUED') THEN "status"
-- When the concurrency expression errored, then the workflow is failed
WHEN sqlc.narg('error')::text IS NOT NULL THEN 'FAILED'
-- When the expression evaluated successfully, then queue the workflow run
ELSE 'QUEUED'
END,
"concurrencyGroupId" = CASE
WHEN sqlc.narg('concurrencyGroupId')::text IS NOT NULL THEN sqlc.narg('concurrencyGroupId')::text
ELSE "concurrencyGroupId"
END
WHERE
wr."id" = @workflowRunId::uuid
RETURNING wr."id";
-- name: UpdateWorkflowRunGroupKeyFromRun :one
WITH groupKeyRun AS (
SELECT "id", "status" as groupKeyRunStatus, "output", "workflowRunId"
FROM "GetGroupKeyRun" as groupKeyRun
@@ -739,6 +763,7 @@ SELECT
-- waiting on https://github.com/sqlc-dev/sqlc/pull/2858 for nullable fields
wc."limitStrategy" as "concurrencyLimitStrategy",
wc."maxRuns" as "concurrencyMaxRuns",
wc."concurrencyGroupExpression" as "concurrencyGroupExpression",
groupKeyRun."id" as "getGroupKeyRunId"
FROM
"WorkflowRun" as runs
@@ -986,3 +1011,70 @@ WHERE
AND sr."tenantId" = @tenantId::uuid
AND sr."deletedAt" IS NULL
ORDER BY sr."order" DESC;
-- name: BulkCreateWorkflowRunEvent :exec
WITH input_values AS (
SELECT
unnest(@timeSeen::timestamp[]) AS "timeFirstSeen",
unnest(@timeSeen::timestamp[]) AS "timeLastSeen",
unnest(@workflowRunIds::uuid[]) AS "workflowRunId",
unnest(cast(@reasons::text[] as"StepRunEventReason"[])) AS "reason",
unnest(cast(@severities::text[] as "StepRunEventSeverity"[])) AS "severity",
unnest(@messages::text[]) AS "message",
1 AS "count",
unnest(@data::jsonb[]) AS "data"
),
updated AS (
UPDATE "StepRunEvent"
SET
"timeLastSeen" = input_values."timeLastSeen",
"message" = input_values."message",
"count" = "StepRunEvent"."count" + 1,
"data" = input_values."data"
FROM input_values
WHERE
"StepRunEvent"."workflowRunId" = input_values."workflowRunId"
AND "StepRunEvent"."reason" = input_values."reason"
AND "StepRunEvent"."severity" = input_values."severity"
AND "StepRunEvent"."id" = (
SELECT "id"
FROM "StepRunEvent"
WHERE "workflowRunId" = input_values."workflowRunId"
ORDER BY "id" DESC
LIMIT 1
)
RETURNING "StepRunEvent".*
)
INSERT INTO "StepRunEvent" (
"timeFirstSeen",
"timeLastSeen",
"workflowRunId",
"reason",
"severity",
"message",
"count",
"data"
)
SELECT
"timeFirstSeen",
"timeLastSeen",
"workflowRunId",
"reason",
"severity",
"message",
"count",
"data"
FROM input_values
WHERE NOT EXISTS (
SELECT 1 FROM updated WHERE "workflowRunId" = input_values."workflowRunId"
);
-- name: ListWorkflowRunEventsByWorkflowRunId :many
SELECT
sre.*
FROM
"StepRunEvent" sre
WHERE
sre."workflowRunId" = @workflowRunId::uuid
ORDER BY
sre."id" DESC;
+173 -11
View File
@@ -11,6 +11,85 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
const bulkCreateWorkflowRunEvent = `-- name: BulkCreateWorkflowRunEvent :exec
WITH input_values AS (
SELECT
unnest($1::timestamp[]) AS "timeFirstSeen",
unnest($1::timestamp[]) AS "timeLastSeen",
unnest($2::uuid[]) AS "workflowRunId",
unnest(cast($3::text[] as"StepRunEventReason"[])) AS "reason",
unnest(cast($4::text[] as "StepRunEventSeverity"[])) AS "severity",
unnest($5::text[]) AS "message",
1 AS "count",
unnest($6::jsonb[]) AS "data"
),
updated AS (
UPDATE "StepRunEvent"
SET
"timeLastSeen" = input_values."timeLastSeen",
"message" = input_values."message",
"count" = "StepRunEvent"."count" + 1,
"data" = input_values."data"
FROM input_values
WHERE
"StepRunEvent"."workflowRunId" = input_values."workflowRunId"
AND "StepRunEvent"."reason" = input_values."reason"
AND "StepRunEvent"."severity" = input_values."severity"
AND "StepRunEvent"."id" = (
SELECT "id"
FROM "StepRunEvent"
WHERE "workflowRunId" = input_values."workflowRunId"
ORDER BY "id" DESC
LIMIT 1
)
RETURNING "StepRunEvent".id, "StepRunEvent"."timeFirstSeen", "StepRunEvent"."timeLastSeen", "StepRunEvent"."stepRunId", "StepRunEvent".reason, "StepRunEvent".severity, "StepRunEvent".message, "StepRunEvent".count, "StepRunEvent".data, "StepRunEvent"."workflowRunId"
)
INSERT INTO "StepRunEvent" (
"timeFirstSeen",
"timeLastSeen",
"workflowRunId",
"reason",
"severity",
"message",
"count",
"data"
)
SELECT
"timeFirstSeen",
"timeLastSeen",
"workflowRunId",
"reason",
"severity",
"message",
"count",
"data"
FROM input_values
WHERE NOT EXISTS (
SELECT 1 FROM updated WHERE "workflowRunId" = input_values."workflowRunId"
)
`
type BulkCreateWorkflowRunEventParams struct {
Timeseen []pgtype.Timestamp `json:"timeseen"`
Workflowrunids []pgtype.UUID `json:"workflowrunids"`
Reasons []string `json:"reasons"`
Severities []string `json:"severities"`
Messages []string `json:"messages"`
Data [][]byte `json:"data"`
}
func (q *Queries) BulkCreateWorkflowRunEvent(ctx context.Context, db DBTX, arg BulkCreateWorkflowRunEventParams) error {
_, err := db.Exec(ctx, bulkCreateWorkflowRunEvent,
arg.Timeseen,
arg.Workflowrunids,
arg.Reasons,
arg.Severities,
arg.Messages,
arg.Data,
)
return err
}
const countWorkflowRuns = `-- name: CountWorkflowRuns :one
WITH runs AS (
SELECT runs."id", runs."createdAt"
@@ -889,6 +968,7 @@ SELECT
-- waiting on https://github.com/sqlc-dev/sqlc/pull/2858 for nullable fields
wc."limitStrategy" as "concurrencyLimitStrategy",
wc."maxRuns" as "concurrencyMaxRuns",
wc."concurrencyGroupExpression" as "concurrencyGroupExpression",
groupKeyRun."id" as "getGroupKeyRunId"
FROM
"WorkflowRun" as runs
@@ -916,13 +996,14 @@ type GetWorkflowRunParams struct {
}
type GetWorkflowRunRow struct {
WorkflowRun WorkflowRun `json:"workflow_run"`
WorkflowRunTriggeredBy WorkflowRunTriggeredBy `json:"workflow_run_triggered_by"`
WorkflowVersion WorkflowVersion `json:"workflow_version"`
WorkflowName pgtype.Text `json:"workflowName"`
ConcurrencyLimitStrategy NullConcurrencyLimitStrategy `json:"concurrencyLimitStrategy"`
ConcurrencyMaxRuns pgtype.Int4 `json:"concurrencyMaxRuns"`
GetGroupKeyRunId pgtype.UUID `json:"getGroupKeyRunId"`
WorkflowRun WorkflowRun `json:"workflow_run"`
WorkflowRunTriggeredBy WorkflowRunTriggeredBy `json:"workflow_run_triggered_by"`
WorkflowVersion WorkflowVersion `json:"workflow_version"`
WorkflowName pgtype.Text `json:"workflowName"`
ConcurrencyLimitStrategy NullConcurrencyLimitStrategy `json:"concurrencyLimitStrategy"`
ConcurrencyMaxRuns pgtype.Int4 `json:"concurrencyMaxRuns"`
ConcurrencyGroupExpression pgtype.Text `json:"concurrencyGroupExpression"`
GetGroupKeyRunId pgtype.UUID `json:"getGroupKeyRunId"`
}
func (q *Queries) GetWorkflowRun(ctx context.Context, db DBTX, arg GetWorkflowRunParams) ([]*GetWorkflowRunRow, error) {
@@ -981,6 +1062,7 @@ func (q *Queries) GetWorkflowRun(ctx context.Context, db DBTX, arg GetWorkflowRu
&i.WorkflowName,
&i.ConcurrencyLimitStrategy,
&i.ConcurrencyMaxRuns,
&i.ConcurrencyGroupExpression,
&i.GetGroupKeyRunId,
); err != nil {
return nil, err
@@ -1367,6 +1449,48 @@ func (q *Queries) ListStepsForJob(ctx context.Context, db DBTX, jobrunid pgtype.
return items, nil
}
const listWorkflowRunEventsByWorkflowRunId = `-- name: ListWorkflowRunEventsByWorkflowRunId :many
SELECT
sre.id, sre."timeFirstSeen", sre."timeLastSeen", sre."stepRunId", sre.reason, sre.severity, sre.message, sre.count, sre.data, sre."workflowRunId"
FROM
"StepRunEvent" sre
WHERE
sre."workflowRunId" = $1::uuid
ORDER BY
sre."id" DESC
`
func (q *Queries) ListWorkflowRunEventsByWorkflowRunId(ctx context.Context, db DBTX, workflowrunid pgtype.UUID) ([]*StepRunEvent, error) {
rows, err := db.Query(ctx, listWorkflowRunEventsByWorkflowRunId, workflowrunid)
if err != nil {
return nil, err
}
defer rows.Close()
var items []*StepRunEvent
for rows.Next() {
var i StepRunEvent
if err := rows.Scan(
&i.ID,
&i.TimeFirstSeen,
&i.TimeLastSeen,
&i.StepRunId,
&i.Reason,
&i.Severity,
&i.Message,
&i.Count,
&i.Data,
&i.WorkflowRunId,
); err != nil {
return nil, err
}
items = append(items, &i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listWorkflowRuns = `-- name: ListWorkflowRuns :many
SELECT
runs."createdAt", runs."updatedAt", runs."deletedAt", runs."tenantId", runs."workflowVersionId", runs.status, runs.error, runs."startedAt", runs."finishedAt", runs."concurrencyGroupId", runs."displayName", runs.id, runs."childIndex", runs."childKey", runs."parentId", runs."parentStepRunId", runs."additionalMetadata", runs.duration, runs.priority,
@@ -2070,7 +2194,45 @@ func (q *Queries) UpdateWorkflowRun(ctx context.Context, db DBTX, arg UpdateWork
return &i, err
}
const updateWorkflowRunGroupKey = `-- name: UpdateWorkflowRunGroupKey :one
const updateWorkflowRunGroupKeyFromExpr = `-- name: UpdateWorkflowRunGroupKeyFromExpr :one
UPDATE "WorkflowRun" wr
SET "error" = CASE
-- Final states are final, cannot be updated. We also can't move out of a queued state
WHEN "status" IN ('SUCCEEDED', 'FAILED', 'QUEUED') THEN "error"
WHEN $1::text IS NOT NULL THEN $1::text
ELSE "error"
END,
"status" = CASE
-- Final states are final, cannot be updated. We also can't move out of a queued state
WHEN "status" IN ('SUCCEEDED', 'FAILED', 'QUEUED') THEN "status"
-- When the concurrency expression errored, then the workflow is failed
WHEN $1::text IS NOT NULL THEN 'FAILED'
-- When the expression evaluated successfully, then queue the workflow run
ELSE 'QUEUED'
END,
"concurrencyGroupId" = CASE
WHEN $2::text IS NOT NULL THEN $2::text
ELSE "concurrencyGroupId"
END
WHERE
wr."id" = $3::uuid
RETURNING wr."id"
`
type UpdateWorkflowRunGroupKeyFromExprParams struct {
Error pgtype.Text `json:"error"`
ConcurrencyGroupId pgtype.Text `json:"concurrencyGroupId"`
Workflowrunid pgtype.UUID `json:"workflowrunid"`
}
func (q *Queries) UpdateWorkflowRunGroupKeyFromExpr(ctx context.Context, db DBTX, arg UpdateWorkflowRunGroupKeyFromExprParams) (pgtype.UUID, error) {
row := db.QueryRow(ctx, updateWorkflowRunGroupKeyFromExpr, arg.Error, arg.ConcurrencyGroupId, arg.Workflowrunid)
var id pgtype.UUID
err := row.Scan(&id)
return id, err
}
const updateWorkflowRunGroupKeyFromRun = `-- name: UpdateWorkflowRunGroupKeyFromRun :one
WITH groupKeyRun AS (
SELECT "id", "status" as groupKeyRunStatus, "output", "workflowRunId"
FROM "GetGroupKeyRun" as groupKeyRun
@@ -2111,13 +2273,13 @@ workflowRun."tenantId" = $1::uuid
RETURNING workflowrun."createdAt", workflowrun."updatedAt", workflowrun."deletedAt", workflowrun."tenantId", workflowrun."workflowVersionId", workflowrun.status, workflowrun.error, workflowrun."startedAt", workflowrun."finishedAt", workflowrun."concurrencyGroupId", workflowrun."displayName", workflowrun.id, workflowrun."childIndex", workflowrun."childKey", workflowrun."parentId", workflowrun."parentStepRunId", workflowrun."additionalMetadata", workflowrun.duration, workflowrun.priority
`
type UpdateWorkflowRunGroupKeyParams struct {
type UpdateWorkflowRunGroupKeyFromRunParams struct {
Tenantid pgtype.UUID `json:"tenantid"`
Groupkeyrunid pgtype.UUID `json:"groupkeyrunid"`
}
func (q *Queries) UpdateWorkflowRunGroupKey(ctx context.Context, db DBTX, arg UpdateWorkflowRunGroupKeyParams) (*WorkflowRun, error) {
row := db.QueryRow(ctx, updateWorkflowRunGroupKey, arg.Tenantid, arg.Groupkeyrunid)
func (q *Queries) UpdateWorkflowRunGroupKeyFromRun(ctx context.Context, db DBTX, arg UpdateWorkflowRunGroupKeyFromRunParams) (*WorkflowRun, error) {
row := db.QueryRow(ctx, updateWorkflowRunGroupKeyFromRun, arg.Tenantid, arg.Groupkeyrunid)
var i WorkflowRun
err := row.Scan(
&i.CreatedAt,
+9 -5
View File
@@ -146,15 +146,17 @@ INSERT INTO "WorkflowConcurrency" (
"workflowVersionId",
"getConcurrencyGroupId",
"maxRuns",
"limitStrategy"
"limitStrategy",
"concurrencyGroupExpression"
) VALUES (
@id::uuid,
gen_random_uuid(),
coalesce(sqlc.narg('createdAt')::timestamp, CURRENT_TIMESTAMP),
coalesce(sqlc.narg('updatedAt')::timestamp, CURRENT_TIMESTAMP),
@workflowVersionId::uuid,
@getConcurrencyGroupId::uuid,
sqlc.narg('getConcurrencyGroupId')::uuid,
coalesce(sqlc.narg('maxRuns')::integer, 1),
coalesce(sqlc.narg('limitStrategy')::"ConcurrencyLimitStrategy", 'CANCEL_IN_PROGRESS')
coalesce(sqlc.narg('limitStrategy')::"ConcurrencyLimitStrategy", 'CANCEL_IN_PROGRESS'),
sqlc.narg('concurrencyGroupExpression')::text
) RETURNING *;
-- name: CreateJob :one
@@ -364,7 +366,9 @@ SELECT
sqlc.embed(workflowVersions),
w."name" as "workflowName",
wc."limitStrategy" as "concurrencyLimitStrategy",
wc."maxRuns" as "concurrencyMaxRuns"
wc."maxRuns" as "concurrencyMaxRuns",
wc."getConcurrencyGroupId" as "concurrencyGroupId",
wc."concurrencyGroupExpression" as "concurrencyGroupExpression"
FROM
"WorkflowVersion" as workflowVersions
JOIN
+30 -21
View File
@@ -464,37 +464,39 @@ INSERT INTO "WorkflowConcurrency" (
"workflowVersionId",
"getConcurrencyGroupId",
"maxRuns",
"limitStrategy"
"limitStrategy",
"concurrencyGroupExpression"
) VALUES (
$1::uuid,
gen_random_uuid(),
coalesce($1::timestamp, CURRENT_TIMESTAMP),
coalesce($2::timestamp, CURRENT_TIMESTAMP),
coalesce($3::timestamp, CURRENT_TIMESTAMP),
$3::uuid,
$4::uuid,
$5::uuid,
coalesce($6::integer, 1),
coalesce($7::"ConcurrencyLimitStrategy", 'CANCEL_IN_PROGRESS')
) RETURNING id, "createdAt", "updatedAt", "workflowVersionId", "getConcurrencyGroupId", "maxRuns", "limitStrategy"
coalesce($5::integer, 1),
coalesce($6::"ConcurrencyLimitStrategy", 'CANCEL_IN_PROGRESS'),
$7::text
) RETURNING id, "createdAt", "updatedAt", "workflowVersionId", "getConcurrencyGroupId", "maxRuns", "limitStrategy", "concurrencyGroupExpression"
`
type CreateWorkflowConcurrencyParams struct {
ID pgtype.UUID `json:"id"`
CreatedAt pgtype.Timestamp `json:"createdAt"`
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
Workflowversionid pgtype.UUID `json:"workflowversionid"`
Getconcurrencygroupid pgtype.UUID `json:"getconcurrencygroupid"`
MaxRuns pgtype.Int4 `json:"maxRuns"`
LimitStrategy NullConcurrencyLimitStrategy `json:"limitStrategy"`
CreatedAt pgtype.Timestamp `json:"createdAt"`
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
Workflowversionid pgtype.UUID `json:"workflowversionid"`
GetConcurrencyGroupId pgtype.UUID `json:"getConcurrencyGroupId"`
MaxRuns pgtype.Int4 `json:"maxRuns"`
LimitStrategy NullConcurrencyLimitStrategy `json:"limitStrategy"`
ConcurrencyGroupExpression pgtype.Text `json:"concurrencyGroupExpression"`
}
func (q *Queries) CreateWorkflowConcurrency(ctx context.Context, db DBTX, arg CreateWorkflowConcurrencyParams) (*WorkflowConcurrency, error) {
row := db.QueryRow(ctx, createWorkflowConcurrency,
arg.ID,
arg.CreatedAt,
arg.UpdatedAt,
arg.Workflowversionid,
arg.Getconcurrencygroupid,
arg.GetConcurrencyGroupId,
arg.MaxRuns,
arg.LimitStrategy,
arg.ConcurrencyGroupExpression,
)
var i WorkflowConcurrency
err := row.Scan(
@@ -505,6 +507,7 @@ func (q *Queries) CreateWorkflowConcurrency(ctx context.Context, db DBTX, arg Cr
&i.GetConcurrencyGroupId,
&i.MaxRuns,
&i.LimitStrategy,
&i.ConcurrencyGroupExpression,
)
return &i, err
}
@@ -930,7 +933,9 @@ SELECT
workflowversions.id, workflowversions."createdAt", workflowversions."updatedAt", workflowversions."deletedAt", workflowversions.version, workflowversions."order", workflowversions."workflowId", workflowversions.checksum, workflowversions."scheduleTimeout", workflowversions."onFailureJobId", workflowversions.sticky, workflowversions.kind, workflowversions."defaultPriority",
w."name" as "workflowName",
wc."limitStrategy" as "concurrencyLimitStrategy",
wc."maxRuns" as "concurrencyMaxRuns"
wc."maxRuns" as "concurrencyMaxRuns",
wc."getConcurrencyGroupId" as "concurrencyGroupId",
wc."concurrencyGroupExpression" as "concurrencyGroupExpression"
FROM
"WorkflowVersion" as workflowVersions
JOIN
@@ -950,10 +955,12 @@ type GetWorkflowVersionForEngineParams struct {
}
type GetWorkflowVersionForEngineRow struct {
WorkflowVersion WorkflowVersion `json:"workflow_version"`
WorkflowName string `json:"workflowName"`
ConcurrencyLimitStrategy NullConcurrencyLimitStrategy `json:"concurrencyLimitStrategy"`
ConcurrencyMaxRuns pgtype.Int4 `json:"concurrencyMaxRuns"`
WorkflowVersion WorkflowVersion `json:"workflow_version"`
WorkflowName string `json:"workflowName"`
ConcurrencyLimitStrategy NullConcurrencyLimitStrategy `json:"concurrencyLimitStrategy"`
ConcurrencyMaxRuns pgtype.Int4 `json:"concurrencyMaxRuns"`
ConcurrencyGroupId pgtype.UUID `json:"concurrencyGroupId"`
ConcurrencyGroupExpression pgtype.Text `json:"concurrencyGroupExpression"`
}
func (q *Queries) GetWorkflowVersionForEngine(ctx context.Context, db DBTX, arg GetWorkflowVersionForEngineParams) ([]*GetWorkflowVersionForEngineRow, error) {
@@ -982,6 +989,8 @@ func (q *Queries) GetWorkflowVersionForEngine(ctx context.Context, db DBTX, arg
&i.WorkflowName,
&i.ConcurrencyLimitStrategy,
&i.ConcurrencyMaxRuns,
&i.ConcurrencyGroupId,
&i.ConcurrencyGroupExpression,
); err != nil {
return nil, err
}
+2 -2
View File
@@ -111,7 +111,7 @@ func (s *getGroupKeyRunRepository) UpdateGetGroupKeyRun(ctx context.Context, ten
Tenantid: pgTenantId,
}
updateWorkflowRunParams := dbsqlc.UpdateWorkflowRunGroupKeyParams{
updateWorkflowRunParams := dbsqlc.UpdateWorkflowRunGroupKeyFromRunParams{
Tenantid: pgTenantId,
Groupkeyrunid: sqlchelpers.UUIDFromStr(getGroupKeyRunId),
}
@@ -174,7 +174,7 @@ func (s *getGroupKeyRunRepository) UpdateGetGroupKeyRun(ctx context.Context, ten
// only update workflow run if status or output has changed
if opts.Status != nil || opts.Output != nil {
_, err = s.queries.UpdateWorkflowRunGroupKey(ctx, tx, updateWorkflowRunParams)
_, err = s.queries.UpdateWorkflowRunGroupKeyFromRun(ctx, tx, updateWorkflowRunParams)
if err != nil {
return nil, fmt.Errorf("could not resolve workflow run status from get group key run: %w", err)
+41 -11
View File
@@ -155,11 +155,21 @@ func (s *stepRunAPIRepository) ListStepRunEventsByWorkflowRunId(ctx context.Cont
}
}
events, err := s.queries.ListStepRunEventsByWorkflowRunId(context.Background(), tx, listParams)
allEvents, err := s.queries.ListWorkflowRunEventsByWorkflowRunId(ctx, tx, sqlchelpers.UUIDFromStr(workflowRunId))
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
events = make([]*dbsqlc.StepRunEvent, 0)
allEvents = make([]*dbsqlc.StepRunEvent, 0)
} else {
return nil, fmt.Errorf("could not list workflow run events: %w", err)
}
}
srEvents, err := s.queries.ListStepRunEventsByWorkflowRunId(context.Background(), tx, listParams)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
srEvents = make([]*dbsqlc.StepRunEvent, 0)
} else {
return nil, fmt.Errorf("could not list step run events: %w", err)
}
@@ -171,8 +181,15 @@ func (s *stepRunAPIRepository) ListStepRunEventsByWorkflowRunId(ctx context.Cont
return nil, fmt.Errorf("could not commit transaction: %w", err)
}
allEvents = append(allEvents, srEvents...)
// sort all events by id asc
sort.Slice(allEvents, func(i, j int) bool {
return allEvents[i].ID > allEvents[j].ID
})
return &repository.ListStepRunEventResult{
Rows: events,
Rows: allEvents,
}, nil
}
@@ -412,7 +429,7 @@ func (s *stepRunEngineRepository) ListStepRunsToReassign(ctx context.Context, te
data[i] = map[string]interface{}{"worker_id": workerId}
}
deferredBulkStepRunEvents(
bulkStepRunEvents(
ctx,
s.l,
s.pool,
@@ -629,6 +646,7 @@ func deferredStepRunEvent(
}
func (s *stepRunEngineRepository) bulkStepRunsAssigned(
assignedAt time.Time,
stepRunIds []pgtype.UUID,
workerIds []pgtype.UUID,
) {
@@ -651,7 +669,7 @@ func (s *stepRunEngineRepository) bulkStepRunsAssigned(
workerIdToStepRunIds[workerId] = append(workerIdToStepRunIds[workerId], sqlchelpers.UUIDToStr(stepRunIds[i]))
messages[i] = fmt.Sprintf("Assigned to worker %s", workerId)
timeSeen[i] = sqlchelpers.TimestampFromTime(time.Now().UTC())
timeSeen[i] = sqlchelpers.TimestampFromTime(assignedAt)
reasons[i] = dbsqlc.StepRunEventReasonASSIGNED
severities[i] = dbsqlc.StepRunEventSeverityINFO
data[i] = map[string]interface{}{"worker_id": workerId}
@@ -675,7 +693,7 @@ func (s *stepRunEngineRepository) bulkStepRunsAssigned(
s.l.Err(err).Msg("could not create worker assign events")
}
deferredBulkStepRunEvents(
bulkStepRunEvents(
ctx,
s.l,
s.pool,
@@ -710,7 +728,7 @@ func (s *stepRunEngineRepository) bulkStepRunsUnassigned(
data[i] = map[string]interface{}{}
}
deferredBulkStepRunEvents(
bulkStepRunEvents(
ctx,
s.l,
s.pool,
@@ -745,7 +763,7 @@ func (s *stepRunEngineRepository) bulkStepRunsRateLimited(
data[i] = map[string]interface{}{}
}
deferredBulkStepRunEvents(
bulkStepRunEvents(
ctx,
s.l,
s.pool,
@@ -759,7 +777,7 @@ func (s *stepRunEngineRepository) bulkStepRunsRateLimited(
)
}
func deferredBulkStepRunEvents(
func bulkStepRunEvents(
ctx context.Context,
l *zerolog.Logger,
dbtx dbsqlc.DBTX,
@@ -1286,7 +1304,7 @@ func (s *stepRunEngineRepository) QueueStepRuns(ctx context.Context, qlp *zerolo
return emptyRes, fmt.Errorf("could not commit transaction: %w", err)
}
defer s.bulkStepRunsAssigned(plan.StepRunIds, plan.WorkerIds)
defer s.bulkStepRunsAssigned(time.Now().UTC(), plan.StepRunIds, plan.WorkerIds)
defer s.bulkStepRunsUnassigned(plan.UnassignedStepRunIds)
defer s.bulkStepRunsRateLimited(plan.RateLimitedStepRuns)
@@ -1587,7 +1605,7 @@ func (s *stepRunEngineRepository) ProcessStepRunUpdates(ctx context.Context, qlp
startRunEvents := time.Now()
// NOTE: actually not deferred
deferredBulkStepRunEvents(ctx, s.l, tx, s.queries, eventStepRunIds, eventTimeSeen, eventReasons, eventSeverities, eventMessages, eventData)
bulkStepRunEvents(ctx, s.l, tx, s.queries, eventStepRunIds, eventTimeSeen, eventReasons, eventSeverities, eventMessages, eventData)
durationRunEvents := time.Since(startRunEvents)
@@ -2269,6 +2287,18 @@ func (s *stepRunEngineRepository) QueueStepRun(ctx context.Context, tenantId, st
return nil, err
}
// if this is an internal retry, and the step run is in a running or final state, this is a no-op. The internal retry
// may have be delayed (for example, timing out when sending the action to the worker), while the system
// may have already reassigned the step run to another.
if opts.IsInternalRetry && (repository.IsFinalStepRunStatus(innerStepRun.SRStatus) || innerStepRun.SRStatus == dbsqlc.StepRunStatusRUNNING) {
return nil, repository.ErrAlreadyRunning
}
// if this is not a retry, and the step run is already in a pending assignment state, this is a no-op
if !opts.IsRetry && innerStepRun.SRStatus == dbsqlc.StepRunStatusPENDINGASSIGNMENT {
return nil, repository.ErrAlreadyQueued
}
err = s.queries.QueueStepRun(ctx, tx, queueParams)
if err != nil {
+22 -16
View File
@@ -593,24 +593,30 @@ func (r *workflowEngineRepository) createWorkflowVersionTxs(ctx context.Context,
// create concurrency group
if opts.Concurrency != nil {
// upsert the action
action, err := r.queries.UpsertAction(
ctx,
tx,
dbsqlc.UpsertActionParams{
Action: opts.Concurrency.Action,
Tenantid: tenantId,
},
)
if err != nil {
return "", fmt.Errorf("could not upsert action: %w", err)
params := dbsqlc.CreateWorkflowConcurrencyParams{
Workflowversionid: sqlcWorkflowVersion.ID,
}
params := dbsqlc.CreateWorkflowConcurrencyParams{
ID: sqlchelpers.UUIDFromStr(uuid.New().String()),
Workflowversionid: sqlcWorkflowVersion.ID,
Getconcurrencygroupid: action.ID,
// upsert the action
if opts.Concurrency.Action != nil {
action, err := r.queries.UpsertAction(
ctx,
tx,
dbsqlc.UpsertActionParams{
Action: *opts.Concurrency.Action,
Tenantid: tenantId,
},
)
if err != nil {
return "", fmt.Errorf("could not upsert action: %w", err)
}
params.GetConcurrencyGroupId = action.ID
}
if opts.Concurrency.Expression != nil {
params.ConcurrencyGroupExpression = sqlchelpers.TextFromStr(*opts.Concurrency.Expression)
}
if opts.Concurrency.MaxRuns != nil {
+238 -42
View File
@@ -73,7 +73,7 @@ func (w *workflowRunAPIRepository) WorkflowRunMetricsCount(ctx context.Context,
return workflowRunMetricsCount(context.Background(), w.pool, w.queries, tenantId, opts)
}
func (w *workflowRunAPIRepository) GetWorkflowRunInputData(tenantId, workflowRunId string) (map[string]interface{}, error) {
func (w *workflowRunEngineRepository) GetWorkflowRunInputData(tenantId, workflowRunId string) (map[string]interface{}, error) {
lookupData := datautils.JobRunLookupData{}
jsonBytes, err := w.queries.GetWorkflowRunInput(
@@ -107,16 +107,6 @@ func (w *workflowRunAPIRepository) CreateNewWorkflowRun(ctx context.Context, ten
id := sqlchelpers.UUIDToStr(workflowRun.ID)
// res, err := w.client.WorkflowRun.FindUnique(
// db.WorkflowRun.ID.Equals(id),
// ).With(
// defaultWorkflowRunPopulator()...,
// ).Exec(context.Background())
// if err != nil {
// return nil, nil, err
// }
for _, cb := range w.callbacks {
cb.Do(workflowRun) // nolint: errcheck
}
@@ -125,6 +115,119 @@ func (w *workflowRunAPIRepository) CreateNewWorkflowRun(ctx context.Context, ten
})
}
type updateWorkflowRunQueueData struct {
WorkflowRunId string `json:"workflow_run_id"`
Event *repository.CreateStepRunEventOpts `json:"event,omitempty"`
}
func (w *workflowRunEngineRepository) ProcessWorkflowRunUpdates(ctx context.Context, tenantId string) (bool, error) {
ctx, span := telemetry.NewSpan(ctx, "process-workflow-run-updates-database")
defer span.End()
pgTenantId := sqlchelpers.UUIDFromStr(tenantId)
limit := 100
tx, commit, rollback, err := prepareTx(ctx, w.pool, w.l, 25000)
if err != nil {
return false, err
}
defer rollback()
// list queues
queueItems, err := w.queries.ListInternalQueueItems(ctx, tx, dbsqlc.ListInternalQueueItemsParams{
Tenantid: pgTenantId,
Queue: dbsqlc.InternalQueueWORKFLOWRUNUPDATE,
Limit: pgtype.Int4{
Int32: int32(limit),
Valid: true,
},
})
if err != nil {
return false, fmt.Errorf("could not list internal queue items: %w", err)
}
data, err := toQueueItemData[updateWorkflowRunQueueData](queueItems)
if err != nil {
return false, fmt.Errorf("could not convert internal queue item data to worker semaphore queue data: %w", err)
}
eventTimeSeen := make([]pgtype.Timestamp, 0, len(data))
eventReasons := make([]dbsqlc.StepRunEventReason, 0, len(data))
eventWorkflowRunIds := make([]pgtype.UUID, 0, len(data))
eventSeverities := make([]dbsqlc.StepRunEventSeverity, 0, len(data))
eventMessages := make([]string, 0, len(data))
eventData := make([]map[string]interface{}, 0, len(data))
dedupe := make(map[string]bool)
for _, item := range data {
workflowRunId := sqlchelpers.UUIDFromStr(item.WorkflowRunId)
if item.Event.EventMessage == nil || item.Event.EventReason == nil {
continue
}
dedupeKey := fmt.Sprintf("EVENT-%s-%s", item.WorkflowRunId, *item.Event.EventReason)
if _, ok := dedupe[dedupeKey]; ok {
continue
}
dedupe[dedupeKey] = true
eventWorkflowRunIds = append(eventWorkflowRunIds, workflowRunId)
eventMessages = append(eventMessages, *item.Event.EventMessage)
eventReasons = append(eventReasons, *item.Event.EventReason)
if item.Event.EventSeverity != nil {
eventSeverities = append(eventSeverities, *item.Event.EventSeverity)
} else {
eventSeverities = append(eventSeverities, dbsqlc.StepRunEventSeverityINFO)
}
if item.Event.EventData != nil {
eventData = append(eventData, item.Event.EventData)
} else {
eventData = append(eventData, map[string]interface{}{})
}
if item.Event.Timestamp != nil {
eventTimeSeen = append(eventTimeSeen, sqlchelpers.TimestampFromTime(*item.Event.Timestamp))
} else {
eventTimeSeen = append(eventTimeSeen, sqlchelpers.TimestampFromTime(time.Now().UTC()))
}
}
qiIds := make([]int64, 0, len(data))
for _, item := range queueItems {
qiIds = append(qiIds, item.ID)
}
// update the processed semaphore queue items
err = w.queries.MarkInternalQueueItemsProcessed(ctx, tx, qiIds)
if err != nil {
return false, fmt.Errorf("could not mark worker semaphore queue items processed: %w", err)
}
// NOTE: actually not deferred
bulkWorkflowRunEvents(ctx, w.l, tx, w.queries, eventWorkflowRunIds, eventTimeSeen, eventReasons, eventSeverities, eventMessages, eventData)
err = commit(ctx)
if err != nil {
return false, fmt.Errorf("could not commit transaction: %w", err)
}
return len(queueItems) == limit, nil
}
func (w *workflowRunAPIRepository) GetWorkflowRunById(ctx context.Context, tenantId, id string) (*dbsqlc.GetWorkflowRunByIdRow, error) {
return w.queries.GetWorkflowRunById(ctx, w.pool, dbsqlc.GetWorkflowRunByIdParams{
Tenantid: sqlchelpers.UUIDFromStr(tenantId),
@@ -494,6 +597,61 @@ func (s *workflowRunEngineRepository) ReplayWorkflowRun(ctx context.Context, ten
return workflowRuns[0], nil
}
func (s *workflowRunEngineRepository) UpdateWorkflowRunFromGroupKeyEval(ctx context.Context, tenantId, workflowRunId string, opts *repository.UpdateWorkflowRunFromGroupKeyEvalOpts) error {
if err := s.v.Validate(opts); err != nil {
return err
}
pgWorkflowRunId := sqlchelpers.UUIDFromStr(workflowRunId)
updateParams := dbsqlc.UpdateWorkflowRunGroupKeyFromExprParams{
Workflowrunid: pgWorkflowRunId,
}
eventParams := repository.CreateStepRunEventOpts{}
if opts.GroupKey != nil {
updateParams.ConcurrencyGroupId = sqlchelpers.TextFromStr(*opts.GroupKey)
now := time.Now().UTC()
eventParams.EventReason = repository.StepRunEventReasonPtr(dbsqlc.StepRunEventReasonWORKFLOWRUNGROUPKEYSUCCEEDED)
eventParams.EventSeverity = repository.StepRunEventSeverityPtr(dbsqlc.StepRunEventSeverityINFO)
eventParams.EventMessage = repository.StringPtr(fmt.Sprintf("Workflow run group key evaluated as %s", *opts.GroupKey))
eventParams.Timestamp = &now
}
if opts.Error != nil {
updateParams.Error = sqlchelpers.TextFromStr(*opts.Error)
now := time.Now().UTC()
eventParams.EventReason = repository.StepRunEventReasonPtr(dbsqlc.StepRunEventReasonWORKFLOWRUNGROUPKEYFAILED)
eventParams.EventSeverity = repository.StepRunEventSeverityPtr(dbsqlc.StepRunEventSeverityCRITICAL)
eventParams.EventMessage = repository.StringPtr(fmt.Sprintf("Error evaluating workflow run group key: %s", *opts.Error))
eventParams.Timestamp = &now
}
_, err := s.queries.UpdateWorkflowRunGroupKeyFromExpr(ctx, s.pool, updateParams)
if err != nil {
return fmt.Errorf("could not update workflow run group key from expr: %w", err)
}
defer insertWorkflowRunQueueItem( // nolint: errcheck
ctx,
s.pool,
s.queries,
tenantId,
updateWorkflowRunQueueData{
WorkflowRunId: workflowRunId,
Event: &eventParams,
},
)
return nil
}
func listWorkflowRuns(ctx context.Context, pool *pgxpool.Pool, queries *dbsqlc.Queries, l *zerolog.Logger, tenantId string, opts *repository.ListWorkflowRunsOpts) (*repository.ListWorkflowRunsResult, error) {
res := &repository.ListWorkflowRunsResult{}
@@ -1044,37 +1202,6 @@ func createNewWorkflowRun(ctx context.Context, pool *pgxpool.Pool, queries *dbsq
return sqlcWorkflowRun, nil
}
func defaultWorkflowRunPopulator() []db.WorkflowRunRelationWith {
return []db.WorkflowRunRelationWith{
db.WorkflowRun.WorkflowVersion.Fetch().With(
db.WorkflowVersion.Workflow.Fetch(),
db.WorkflowVersion.Concurrency.Fetch().With(
db.WorkflowConcurrency.GetConcurrencyGroup.Fetch(),
),
),
db.WorkflowRun.GetGroupKeyRun.Fetch(),
db.WorkflowRun.TriggeredBy.Fetch().With(
db.WorkflowRunTriggeredBy.Event.Fetch(),
db.WorkflowRunTriggeredBy.Cron.Fetch(),
),
db.WorkflowRun.JobRuns.Fetch().With(
db.JobRun.Job.Fetch().With(
db.Job.Steps.Fetch().With(
db.Step.Action.Fetch(),
db.Step.Parents.Fetch(),
),
),
db.JobRun.StepRuns.Fetch().With(
db.StepRun.ChildWorkflowRuns.Fetch(),
db.StepRun.Step.Fetch().With(
db.Step.Action.Fetch(),
db.Step.Parents.Fetch(),
),
),
),
}
}
func isUniqueViolationOnDedupe(err error) bool {
if err == nil {
return false
@@ -1083,3 +1210,72 @@ func isUniqueViolationOnDedupe(err error) bool {
return strings.Contains(err.Error(), "WorkflowRunDedupe_tenantId_workflowId_value_key") &&
strings.Contains(err.Error(), "SQLSTATE 23505")
}
func insertWorkflowRunQueueItem(
ctx context.Context,
dbtx dbsqlc.DBTX,
queries *dbsqlc.Queries,
tenantId string,
data updateWorkflowRunQueueData,
) error {
insertData := make([]any, 1)
insertData[0] = data
return bulkInsertInternalQueueItem(
ctx,
dbtx,
queries,
tenantId,
dbsqlc.InternalQueueWORKFLOWRUNUPDATE,
insertData,
)
}
func bulkWorkflowRunEvents(
ctx context.Context,
l *zerolog.Logger,
dbtx dbsqlc.DBTX,
queries *dbsqlc.Queries,
workflowRunIds []pgtype.UUID,
timeSeen []pgtype.Timestamp,
reasons []dbsqlc.StepRunEventReason,
severities []dbsqlc.StepRunEventSeverity,
messages []string,
data []map[string]interface{},
) {
inputData := [][]byte{}
inputReasons := []string{}
inputSeverities := []string{}
for _, d := range data {
dataBytes, err := json.Marshal(d)
if err != nil {
l.Err(err).Msg("could not marshal deferred step run event data")
return
}
inputData = append(inputData, dataBytes)
}
for _, r := range reasons {
inputReasons = append(inputReasons, string(r))
}
for _, s := range severities {
inputSeverities = append(inputSeverities, string(s))
}
err := queries.BulkCreateWorkflowRunEvent(ctx, dbtx, dbsqlc.BulkCreateWorkflowRunEventParams{
Workflowrunids: workflowRunIds,
Reasons: inputReasons,
Severities: inputSeverities,
Messages: messages,
Data: inputData,
Timeseen: timeSeen,
})
if err != nil {
l.Err(err).Msg("could not create deferred step run event")
}
}
+2 -1
View File
@@ -78,10 +78,11 @@ func StepRunEventSeverityPtr(severity dbsqlc.StepRunEventSeverity) *dbsqlc.StepR
return &severity
}
var ErrStepRunIsNotPending = fmt.Errorf("step run is not pending")
var ErrNoWorkerAvailable = fmt.Errorf("no worker available")
var ErrRateLimitExceeded = fmt.Errorf("rate limit exceeded")
var ErrStepRunIsNotAssigned = fmt.Errorf("step run is not assigned")
var ErrAlreadyQueued = fmt.Errorf("step run is already queued")
var ErrAlreadyRunning = fmt.Errorf("step run is already running")
type StepRunUpdateInfo struct {
WorkflowRunFinalState bool
+5 -2
View File
@@ -56,14 +56,17 @@ type CreateWorkflowVersionOpts struct {
}
type CreateWorkflowConcurrencyOpts struct {
// (required) the action id for getting the concurrency group
Action string `validate:"required,actionId"`
// (optional) the action id for getting the concurrency group
Action *string `validate:"omitempty,actionId"`
// (optional) the maximum number of concurrent workflow runs, default 1
MaxRuns *int32
// (optional) the strategy to use when the concurrency limit is reached, default CANCEL_IN_PROGRESS
LimitStrategy *string `validate:"omitnil,oneof=CANCEL_IN_PROGRESS DROP_NEWEST QUEUE_NEWEST GROUP_ROUND_ROBIN"`
// (optional) a concurrency expression for evaluating the concurrency key
Expression *string `validate:"omitempty,celworkflowrunstr"`
}
func (o *CreateWorkflowVersionOpts) Checksum() (string, error) {
+17 -7
View File
@@ -113,7 +113,7 @@ func GetCreateWorkflowRunOptsFromManual(
AdditionalMetadata: additionalMetadata,
}
if workflowVersion.ConcurrencyLimitStrategy.Valid {
if workflowVersion.ConcurrencyLimitStrategy.Valid && workflowVersion.ConcurrencyGroupId.Valid {
opts.GetGroupKeyRun = &CreateGroupKeyRunOpts{
Input: input,
}
@@ -145,7 +145,7 @@ func GetCreateWorkflowRunOptsFromParent(
WithParent(parentId, parentStepRunId, childIndex, childKey, additionalMetadata, parentAdditionalMetadata)(opts)
if workflowVersion.ConcurrencyLimitStrategy.Valid {
if workflowVersion.ConcurrencyLimitStrategy.Valid && workflowVersion.ConcurrencyGroupId.Valid {
opts.GetGroupKeyRun = &CreateGroupKeyRunOpts{
Input: input,
}
@@ -173,7 +173,7 @@ func GetCreateWorkflowRunOptsFromEvent(
AdditionalMetadata: additionalMetadata,
}
if workflowVersion.ConcurrencyLimitStrategy.Valid {
if workflowVersion.ConcurrencyLimitStrategy.Valid && workflowVersion.ConcurrencyGroupId.Valid {
opts.GetGroupKeyRun = &CreateGroupKeyRunOpts{
Input: input,
}
@@ -203,7 +203,7 @@ func GetCreateWorkflowRunOptsFromCron(
AdditionalMetadata: additionalMetadata,
}
if workflowVersion.ConcurrencyLimitStrategy.Valid {
if workflowVersion.ConcurrencyLimitStrategy.Valid && workflowVersion.ConcurrencyGroupId.Valid {
opts.GetGroupKeyRun = &CreateGroupKeyRunOpts{
Input: input,
}
@@ -232,7 +232,7 @@ func GetCreateWorkflowRunOptsFromSchedule(
AdditionalMetadata: additionalMetadata,
}
if workflowVersion.ConcurrencyLimitStrategy.Valid {
if workflowVersion.ConcurrencyLimitStrategy.Valid && workflowVersion.ConcurrencyGroupId.Valid {
opts.GetGroupKeyRun = &CreateGroupKeyRunOpts{
Input: input,
}
@@ -389,8 +389,6 @@ type WorkflowRunAPIRepository interface {
// Counts by status
WorkflowRunMetricsCount(ctx context.Context, tenantId string, opts *WorkflowRunsMetricsOpts) (*dbsqlc.WorkflowRunsMetricsCountRow, error)
GetWorkflowRunInputData(tenantId, workflowRunId string) (map[string]interface{}, error)
// CreateNewWorkflowRun creates a new workflow run for a workflow version.
CreateNewWorkflowRun(ctx context.Context, tenantId string, opts *CreateWorkflowRunOpts) (*dbsqlc.WorkflowRun, error)
@@ -414,6 +412,12 @@ func (e ErrDedupeValueExists) Error() string {
return fmt.Sprintf("workflow run with dedupe value %s already exists", e.DedupeValue)
}
type UpdateWorkflowRunFromGroupKeyEvalOpts struct {
GroupKey *string
Error *string
}
type WorkflowRunEngineRepository interface {
RegisterCreateCallback(callback Callback[*dbsqlc.WorkflowRun])
@@ -429,6 +433,12 @@ type WorkflowRunEngineRepository interface {
// CreateNewWorkflowRun creates a new workflow run for a workflow version.
CreateNewWorkflowRun(ctx context.Context, tenantId string, opts *CreateWorkflowRunOpts) (string, error)
GetWorkflowRunInputData(tenantId, workflowRunId string) (map[string]interface{}, error)
ProcessWorkflowRunUpdates(ctx context.Context, tenantId string) (bool, error)
UpdateWorkflowRunFromGroupKeyEval(ctx context.Context, tenantId, workflowRunId string, opts *UpdateWorkflowRunFromGroupKeyEvalOpts) error
// GetWorkflowRunById returns a workflow run by id.
GetWorkflowRunById(ctx context.Context, tenantId, runId string) (*dbsqlc.GetWorkflowRunRow, error)
+3
View File
@@ -19,6 +19,7 @@ const (
ActionIDErr = "Invalid action ID. Action IDs must be in the format <integrationId>:<verb>"
CronErr = "Invalid cron expression"
DurationErr = "Invalid duration. Durations must be in the format <number><unit>, where unit is one of: 's', 'm', 'h'"
CELExprErr = "Invalid CEL expression"
)
type APIErrors gen.APIErrors
@@ -132,6 +133,8 @@ func getErrorStr(errObj *ValidationErrObject) string {
return errObj.SafeExternalError(CronErr)
case "duration":
return errObj.SafeExternalError(DurationErr)
case "celworkflowrunstr":
return errObj.SafeExternalError(CELExprErr)
default:
return errObj.SafeExternalError("")
}
+9
View File
@@ -10,6 +10,7 @@ import (
"github.com/go-playground/validator/v10"
"github.com/google/uuid"
"github.com/hatchet-dev/hatchet/internal/cel"
"github.com/hatchet-dev/hatchet/pkg/client/types"
)
@@ -20,6 +21,8 @@ var CronRegex = regexp.MustCompile(`(@(annually|yearly|monthly|weekly|daily|hour
func newValidator() *validator.Validate {
validate := validator.New()
celParser := cel.NewCELParser()
_ = validate.RegisterValidation("hatchetName", func(fl validator.FieldLevel) bool {
return NameRegex.MatchString(fl.Field().String())
})
@@ -62,6 +65,12 @@ func newValidator() *validator.Validate {
return err == nil
})
_ = validate.RegisterValidation("celworkflowrunstr", func(fl validator.FieldLevel) bool {
_, err := celParser.ParseWorkflowString(fl.Field().String())
return err == nil
})
return validate
}
+17 -3
View File
@@ -157,10 +157,17 @@ type WorkflowJob struct {
type WorkflowConcurrency struct {
fn GetWorkflowConcurrencyGroupFn
expr *string
maxRuns *int32
limitStrategy *types.WorkflowConcurrencyLimitStrategy
}
func Expression(expr string) *WorkflowConcurrency {
return &WorkflowConcurrency{
expr: &expr,
}
}
func Concurrency(fn GetWorkflowConcurrencyGroupFn) *WorkflowConcurrency {
return &WorkflowConcurrency{
fn: fn,
@@ -206,8 +213,15 @@ func (j *WorkflowJob) ToWorkflow(svcName string, namespace string) types.Workflo
}
if j.Concurrency != nil {
w.Concurrency = &types.WorkflowConcurrency{
ActionID: "concurrency:" + getFnName(j.Concurrency.fn), // TODO this should also be namespaced
w.Concurrency = &types.WorkflowConcurrency{}
if j.Concurrency.fn != nil {
actionId := "concurrency:" + getFnName(j.Concurrency.fn)
w.Concurrency.ActionID = &actionId // TODO this should also be namespaced
}
if j.Concurrency.expr != nil {
w.Concurrency.Expression = j.Concurrency.expr
}
if j.Concurrency.maxRuns != nil {
@@ -259,7 +273,7 @@ func (j *WorkflowJob) ToActionMap(svcName string) map[string]any {
res[actionId] = step.Function
}
if j.Concurrency != nil {
if j.Concurrency != nil && j.Concurrency.fn != nil {
res["concurrency:"+getFnName(j.Concurrency.fn)] = j.Concurrency.fn
}
@@ -0,0 +1,26 @@
-- AlterEnum
ALTER TYPE "InternalQueue" ADD VALUE 'WORKFLOW_RUN_UPDATE';
-- AlterEnum
-- This migration adds more than one value to an enum.
-- With PostgreSQL versions 11 and earlier, this is not possible
-- in a single migration. This can be worked around by creating
-- multiple migrations, each migration adding only one value to
-- the enum.
ALTER TYPE "StepRunEventReason" ADD VALUE 'WORKFLOW_RUN_GROUP_KEY_SUCCEEDED';
ALTER TYPE "StepRunEventReason" ADD VALUE 'WORKFLOW_RUN_GROUP_KEY_FAILED';
-- DropForeignKey
ALTER TABLE "StepRunEvent" DROP CONSTRAINT "StepRunEvent_stepRunId_fkey";
-- AlterTable
ALTER TABLE "StepRunEvent" ADD COLUMN "workflowRunId" UUID,
ALTER COLUMN "stepRunId" DROP NOT NULL;
-- AlterTable
ALTER TABLE "WorkflowConcurrency" ADD COLUMN "concurrencyGroupExpression" TEXT;
-- CreateIndex
CREATE INDEX "StepRunEvent_workflowRunId_idx" ON "StepRunEvent"("workflowRunId");
+14 -7
View File
@@ -593,6 +593,9 @@ model WorkflowConcurrency {
getConcurrencyGroup Action? @relation(fields: [getConcurrencyGroupId], references: [id])
getConcurrencyGroupId String? @db.Uuid
// A CEL expression for getting the concurrency group based on the input to the workflow.
concurrencyGroupExpression String?
// the maximum number of concurrent workflow runs
maxRuns Int @default(1)
@@ -1299,8 +1302,6 @@ model StepRun {
childWorkflowRuns WorkflowRun[]
childSchedules WorkflowTriggerScheduledRef[]
events StepRunEvent[]
@@index([tenantId])
@@index([workerId])
@@index([createdAt])
@@ -1352,6 +1353,7 @@ model QueueItem {
enum InternalQueue {
WORKER_SEMAPHORE_COUNT
STEP_RUN_UPDATE
WORKFLOW_RUN_UPDATE
}
model InternalQueueItem {
@@ -1419,6 +1421,10 @@ enum StepRunEventReason {
CANCELLED
TIMEOUT_REFRESHED
SLOT_RELEASED
// NOTE: not actually step run events, but the table is currently named StepRunEvent
WORKFLOW_RUN_GROUP_KEY_SUCCEEDED
WORKFLOW_RUN_GROUP_KEY_FAILED
}
enum StepRunEventSeverity {
@@ -1433,8 +1439,10 @@ model StepRunEvent {
timeLastSeen DateTime @default(now())
// the parent step run
stepRun StepRun @relation(fields: [stepRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
stepRunId String @db.Uuid
stepRunId String? @db.Uuid
// the parent workflow run
workflowRunId String? @db.Uuid
// the event reason
reason StepRunEventReason
@@ -1448,9 +1456,8 @@ model StepRunEvent {
data Json?
// id - timeFirstSeen uniue index
@@index([stepRunId]) // only on wfr id
@@index([stepRunId])
@@index([workflowRunId])
}
model StepRunResultArchive {
+2
View File
@@ -10,3 +10,5 @@ ALTER TABLE "InternalQueueItem" ADD CONSTRAINT "InternalQueueItem_priority_check
CREATE INDEX CONCURRENTLY IF NOT EXISTS "StepRun_jobRunId_status_tenantId_idx"
ON "StepRun" ("jobRunId", "status", "tenantId")
WHERE "status" = 'PENDING';
CREATE INDEX CONCURRENTLY IF NOT EXISTS "WorkflowRun_parentStepRunId" ON "WorkflowRun"("parentStepRunId" ASC);
+16
View File
@@ -0,0 +1,16 @@
-- atlas:txmode none
CREATE INDEX CONCURRENTLY IF NOT EXISTS "WorkflowRun_parentStepRunId" ON "WorkflowRun"("parentStepRunId" ASC);
-- Add value to enum type: "StepRunEventReason"
ALTER TYPE "StepRunEventReason" ADD VALUE 'WORKFLOW_RUN_GROUP_KEY_SUCCEEDED';
-- Add value to enum type: "StepRunEventReason"
ALTER TYPE "StepRunEventReason" ADD VALUE 'WORKFLOW_RUN_GROUP_KEY_FAILED';
-- Add value to enum type: "InternalQueue"
ALTER TYPE "InternalQueue" ADD VALUE 'WORKFLOW_RUN_UPDATE';
-- Modify "StepRunEvent" table
ALTER TABLE "StepRunEvent" DROP CONSTRAINT "StepRunEvent_stepRunId_fkey", ALTER COLUMN "stepRunId" DROP NOT NULL, ADD COLUMN "workflowRunId" uuid NULL;
-- Create index "StepRunEvent_workflowRunId_idx" to table: "StepRunEvent"
CREATE INDEX "StepRunEvent_workflowRunId_idx" ON "StepRunEvent" ("workflowRunId");
-- Modify "WorkflowConcurrency" table
ALTER TABLE "WorkflowConcurrency" ADD COLUMN "concurrencyGroupExpression" text NULL;
+2 -1
View File
@@ -1,4 +1,4 @@
h1:GONXnb4KX2gX139WgjSkVzSS2yGkT+MXqmCtgO3ulMg=
h1:TwaCvaPgsVfs199JWuoIa/1ipf3RIgAGfH76Wzm/4dw=
20240115180414_init.sql h1:Ef3ZyjAHkmJPdGF/dEWCahbwgcg6uGJKnDxW2JCRi2k=
20240122014727_v0_6_0.sql h1:o/LdlteAeFgoHJ3e/M4Xnghqt9826IE/Y/h0q95Acuo=
20240126235456_v0_7_0.sql h1:KiVzt/hXgQ6esbdC6OMJOOWuYEXmy1yeCpmsVAHTFKs=
@@ -56,3 +56,4 @@ h1:GONXnb4KX2gX139WgjSkVzSS2yGkT+MXqmCtgO3ulMg=
20240911124017_v0.44.6.sql h1:7MlkRZpRUra7GUPSYfFzqDSZGM1SKlP1j81xMHyptqc=
20240911201831_v0.44.7.sql h1:rBiOx3qNLvl9lCedK6dlf6yTX3y+xCcmUJNEXPG8YYc=
20240916115647_v0.44.8.sql h1:1D5fmikdME61lwN1/EbBTmGcKzpqESZUmfoHvmOeZl4=
20240918162532_v0.45.0.sql h1:pOhBg/58SnD8qBpDx9OdxTFfjRxEanJrJF7rLEvUFSY=
+11 -7
View File
@@ -2,7 +2,7 @@
CREATE TYPE "ConcurrencyLimitStrategy" AS ENUM ('CANCEL_IN_PROGRESS', 'DROP_NEWEST', 'QUEUE_NEWEST', 'GROUP_ROUND_ROBIN');
-- CreateEnum
CREATE TYPE "InternalQueue" AS ENUM ('WORKER_SEMAPHORE_COUNT', 'STEP_RUN_UPDATE');
CREATE TYPE "InternalQueue" AS ENUM ('WORKER_SEMAPHORE_COUNT', 'STEP_RUN_UPDATE', 'WORKFLOW_RUN_UPDATE');
-- CreateEnum
CREATE TYPE "InviteLinkStatus" AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');
@@ -20,7 +20,7 @@ CREATE TYPE "LimitResource" AS ENUM ('WORKFLOW_RUN', 'EVENT', 'WORKER', 'CRON',
CREATE TYPE "LogLineLevel" AS ENUM ('DEBUG', 'INFO', 'WARN', 'ERROR');
-- CreateEnum
CREATE TYPE "StepRunEventReason" AS ENUM ('REQUEUED_NO_WORKER', 'REQUEUED_RATE_LIMIT', 'SCHEDULING_TIMED_OUT', 'ASSIGNED', 'STARTED', 'FINISHED', 'FAILED', 'RETRYING', 'CANCELLED', 'TIMED_OUT', 'REASSIGNED', 'SLOT_RELEASED', 'TIMEOUT_REFRESHED', 'RETRIED_BY_USER', 'SENT_TO_WORKER');
CREATE TYPE "StepRunEventReason" AS ENUM ('REQUEUED_NO_WORKER', 'REQUEUED_RATE_LIMIT', 'SCHEDULING_TIMED_OUT', 'ASSIGNED', 'STARTED', 'FINISHED', 'FAILED', 'RETRYING', 'CANCELLED', 'TIMED_OUT', 'REASSIGNED', 'SLOT_RELEASED', 'TIMEOUT_REFRESHED', 'RETRIED_BY_USER', 'SENT_TO_WORKER', 'WORKFLOW_RUN_GROUP_KEY_SUCCEEDED', 'WORKFLOW_RUN_GROUP_KEY_FAILED');
-- CreateEnum
CREATE TYPE "StepRunEventSeverity" AS ENUM ('INFO', 'WARNING', 'CRITICAL');
@@ -396,12 +396,13 @@ CREATE TABLE "StepRunEvent" (
"id" BIGSERIAL NOT NULL,
"timeFirstSeen" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"timeLastSeen" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"stepRunId" UUID NOT NULL,
"stepRunId" UUID,
"reason" "StepRunEventReason" NOT NULL,
"severity" "StepRunEventSeverity" NOT NULL,
"message" TEXT NOT NULL,
"count" INTEGER NOT NULL,
"data" JSONB
"data" JSONB,
"workflowRunId" UUID
);
-- CreateTable
@@ -736,6 +737,7 @@ CREATE TABLE "WorkflowConcurrency" (
"getConcurrencyGroupId" UUID,
"maxRuns" INTEGER NOT NULL DEFAULT 1,
"limitStrategy" "ConcurrencyLimitStrategy" NOT NULL DEFAULT 'CANCEL_IN_PROGRESS',
"concurrencyGroupExpression" TEXT,
CONSTRAINT "WorkflowConcurrency_pkey" PRIMARY KEY ("id")
);
@@ -1074,6 +1076,9 @@ CREATE UNIQUE INDEX "StepRunEvent_id_key" ON "StepRunEvent"("id" ASC);
-- CreateIndex
CREATE INDEX "StepRunEvent_stepRunId_idx" ON "StepRunEvent"("stepRunId" ASC);
-- CreateIndex
CREATE INDEX "StepRunEvent_workflowRunId_idx" ON "StepRunEvent"("workflowRunId" ASC);
-- CreateIndex
CREATE UNIQUE INDEX "StepRunResultArchive_id_key" ON "StepRunResultArchive"("id" ASC);
@@ -1422,9 +1427,6 @@ ALTER TABLE "StepRun" ADD CONSTRAINT "StepRun_tickerId_fkey" FOREIGN KEY ("ticke
-- AddForeignKey
ALTER TABLE "StepRun" ADD CONSTRAINT "StepRun_workerId_fkey" FOREIGN KEY ("workerId") REFERENCES "Worker"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "StepRunEvent" ADD CONSTRAINT "StepRunEvent_stepRunId_fkey" FOREIGN KEY ("stepRunId") REFERENCES "StepRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "StepRunResultArchive" ADD CONSTRAINT "StepRunResultArchive_stepRunId_fkey" FOREIGN KEY ("stepRunId") REFERENCES "StepRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
@@ -1629,3 +1631,5 @@ ALTER TABLE "InternalQueueItem" ADD CONSTRAINT "InternalQueueItem_priority_check
CREATE INDEX CONCURRENTLY IF NOT EXISTS "StepRun_jobRunId_status_tenantId_idx"
ON "StepRun" ("jobRunId", "status", "tenantId")
WHERE "status" = 'PENDING';
CREATE INDEX CONCURRENTLY IF NOT EXISTS "WorkflowRun_parentStepRunId" ON "WorkflowRun"("parentStepRunId" ASC);