mirror of
https://github.com/hanzoai/tasks.git
synced 2026-10-04 02:04:03 +00:00
## What changed? Refactor: remove yaml config files from functional tests. ndc and xdc configs are still there and I will do it in following PRs. ## Why? It is much more convenient to have test configuration in code than in yaml files. ## How did you test it? - [ ] built - [ ] run locally and tested manually - [x] covered by existing tests - [ ] added new unit test(s) - [ ] added new functional test(s)
224 lines
6.4 KiB
Go
224 lines
6.4 KiB
Go
package tests
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/suite"
|
|
commandpb "go.temporal.io/api/command/v1"
|
|
commonpb "go.temporal.io/api/common/v1"
|
|
enumspb "go.temporal.io/api/enums/v1"
|
|
"go.temporal.io/api/workflowservice/v1"
|
|
"go.temporal.io/server/common/dynamicconfig"
|
|
"go.temporal.io/server/common/payloads"
|
|
"go.temporal.io/server/common/testing/taskpoller"
|
|
"go.temporal.io/server/common/testing/testvars"
|
|
"go.temporal.io/server/tests/testcore"
|
|
"google.golang.org/protobuf/types/known/durationpb"
|
|
)
|
|
|
|
type PriorityFairnessSuite struct {
|
|
testcore.FunctionalTestBase
|
|
}
|
|
|
|
func TestPriorityFairnessSuite(t *testing.T) {
|
|
t.Parallel()
|
|
suite.Run(t, new(PriorityFairnessSuite))
|
|
}
|
|
|
|
func (s *PriorityFairnessSuite) SetupSuite() {
|
|
dynamicConfigOverrides := map[dynamicconfig.Key]any{
|
|
dynamicconfig.MatchingUseNewMatcher.Key(): true,
|
|
dynamicconfig.MatchingGetTasksBatchSize.Key(): 20,
|
|
}
|
|
s.FunctionalTestBase.SetupSuiteWithCluster(testcore.WithDynamicConfigOverrides(dynamicConfigOverrides))
|
|
}
|
|
|
|
func (s *PriorityFairnessSuite) TestPriority_Activity_Basic() {
|
|
const N = 100
|
|
const Levels = 5
|
|
|
|
tv := testvars.New(s.T())
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
for wfidx := range N {
|
|
_, err := s.FrontendClient().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{
|
|
Namespace: s.Namespace().String(),
|
|
WorkflowId: fmt.Sprintf("wf%d", wfidx),
|
|
WorkflowType: tv.WorkflowType(),
|
|
TaskQueue: tv.TaskQueue(),
|
|
})
|
|
s.NoError(err)
|
|
}
|
|
|
|
// process workflow tasks
|
|
for range N {
|
|
_, err := s.TaskPoller().PollAndHandleWorkflowTask(
|
|
tv,
|
|
func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) {
|
|
s.Equal(3, len(task.History.Events))
|
|
|
|
var wfidx int
|
|
_, err := fmt.Sscanf(task.WorkflowExecution.WorkflowId, "wf%d", &wfidx)
|
|
s.NoError(err)
|
|
|
|
var commands []*commandpb.Command
|
|
|
|
for i, pri := range rand.Perm(Levels) {
|
|
input, err := payloads.Encode(wfidx, pri+1)
|
|
s.NoError(err)
|
|
commands = append(commands, &commandpb.Command{
|
|
CommandType: enumspb.COMMAND_TYPE_SCHEDULE_ACTIVITY_TASK,
|
|
Attributes: &commandpb.Command_ScheduleActivityTaskCommandAttributes{
|
|
ScheduleActivityTaskCommandAttributes: &commandpb.ScheduleActivityTaskCommandAttributes{
|
|
ActivityId: fmt.Sprintf("act%d", i),
|
|
ActivityType: tv.ActivityType(),
|
|
TaskQueue: tv.TaskQueue(),
|
|
ScheduleToCloseTimeout: durationpb.New(time.Minute),
|
|
Priority: &commonpb.Priority{
|
|
PriorityKey: int32(pri + 1),
|
|
},
|
|
Input: input,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
return &workflowservice.RespondWorkflowTaskCompletedRequest{Commands: commands}, nil
|
|
},
|
|
taskpoller.WithContext(ctx),
|
|
)
|
|
s.NoError(err)
|
|
}
|
|
|
|
// process activity tasks
|
|
var runs []int
|
|
for range N * Levels {
|
|
_, err := s.TaskPoller().PollAndHandleActivityTask(
|
|
tv,
|
|
func(task *workflowservice.PollActivityTaskQueueResponse) (*workflowservice.RespondActivityTaskCompletedRequest, error) {
|
|
var wfidx, pri int
|
|
s.NoError(payloads.Decode(task.Input, &wfidx, &pri))
|
|
// s.T().Log("activity", "pri", pri, "wfidx", wfidx)
|
|
runs = append(runs, pri)
|
|
nothing, err := payloads.Encode()
|
|
s.NoError(err)
|
|
return &workflowservice.RespondActivityTaskCompletedRequest{Result: nothing}, nil
|
|
},
|
|
taskpoller.WithContext(ctx),
|
|
)
|
|
s.NoError(err)
|
|
}
|
|
|
|
w := wrongorderness(runs)
|
|
s.T().Log("wrongorderness:", w)
|
|
s.Less(w, 0.15)
|
|
}
|
|
|
|
func (s *PriorityFairnessSuite) TestSubqueue_Migration() {
|
|
tv := testvars.New(s.T())
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
|
defer cancel()
|
|
|
|
// start with old matcher
|
|
s.OverrideDynamicConfig(dynamicconfig.MatchingUseNewMatcher, false)
|
|
|
|
// start 100 workflows
|
|
for range 100 {
|
|
_, err := s.FrontendClient().StartWorkflowExecution(ctx, &workflowservice.StartWorkflowExecutionRequest{
|
|
Namespace: s.Namespace().String(),
|
|
WorkflowId: uuid.NewString(),
|
|
WorkflowType: tv.WorkflowType(),
|
|
TaskQueue: tv.TaskQueue(),
|
|
})
|
|
s.NoError(err)
|
|
}
|
|
|
|
// process workflow tasks and create 300 activities
|
|
for range 100 {
|
|
_, err := s.TaskPoller().PollAndHandleWorkflowTask(
|
|
tv,
|
|
func(task *workflowservice.PollWorkflowTaskQueueResponse) (*workflowservice.RespondWorkflowTaskCompletedRequest, error) {
|
|
s.Equal(3, len(task.History.Events))
|
|
|
|
var commands []*commandpb.Command
|
|
|
|
for i := range 3 {
|
|
input, err := payloads.Encode(i)
|
|
s.NoError(err)
|
|
commands = append(commands, &commandpb.Command{
|
|
CommandType: enumspb.COMMAND_TYPE_SCHEDULE_ACTIVITY_TASK,
|
|
Attributes: &commandpb.Command_ScheduleActivityTaskCommandAttributes{
|
|
ScheduleActivityTaskCommandAttributes: &commandpb.ScheduleActivityTaskCommandAttributes{
|
|
ActivityId: fmt.Sprintf("act%d", i),
|
|
ActivityType: tv.ActivityType(),
|
|
TaskQueue: tv.TaskQueue(),
|
|
ScheduleToCloseTimeout: durationpb.New(time.Minute),
|
|
Input: input,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
return &workflowservice.RespondWorkflowTaskCompletedRequest{Commands: commands}, nil
|
|
},
|
|
taskpoller.WithContext(ctx),
|
|
)
|
|
s.NoError(err)
|
|
}
|
|
|
|
processActivity := func() {
|
|
_, err := s.TaskPoller().PollAndHandleActivityTask(
|
|
tv,
|
|
func(task *workflowservice.PollActivityTaskQueueResponse) (*workflowservice.RespondActivityTaskCompletedRequest, error) {
|
|
nothing, err := payloads.Encode()
|
|
s.NoError(err)
|
|
return &workflowservice.RespondActivityTaskCompletedRequest{Result: nothing}, nil
|
|
},
|
|
taskpoller.WithContext(ctx),
|
|
)
|
|
s.NoError(err)
|
|
}
|
|
|
|
s.T().Log("process first 100 activities")
|
|
for range 100 {
|
|
processActivity()
|
|
}
|
|
|
|
s.T().Log("switching to new matcher")
|
|
s.OverrideDynamicConfig(dynamicconfig.MatchingUseNewMatcher, true)
|
|
|
|
s.T().Log("processing next 100 activities")
|
|
for range 100 {
|
|
processActivity()
|
|
}
|
|
|
|
s.T().Log("switching back to old matcher")
|
|
s.OverrideDynamicConfig(dynamicconfig.MatchingUseNewMatcher, false)
|
|
|
|
s.T().Log("processing last 100 activities")
|
|
for range 100 {
|
|
processActivity()
|
|
}
|
|
}
|
|
|
|
func wrongorderness(vs []int) float64 {
|
|
l := len(vs)
|
|
wrong := 0
|
|
for i, v := range vs[:l-1] {
|
|
for _, w := range vs[i+1:] {
|
|
if v > w {
|
|
wrong++
|
|
}
|
|
}
|
|
}
|
|
return float64(wrong) / float64(l*(l-1)/2)
|
|
}
|