@@ -15,6 +15,7 @@ import (
1515 "go.temporal.io/server/api/matchingservice/v1"
1616 persistencespb "go.temporal.io/server/api/persistence/v1"
1717 taskqueuespb "go.temporal.io/server/api/taskqueue/v1"
18+ "go.temporal.io/server/common"
1819 "go.temporal.io/server/common/cache"
1920 "go.temporal.io/server/common/headers"
2021 "go.temporal.io/server/common/log"
@@ -63,8 +64,11 @@ type (
6364 // TODO(stephanos): move cache out of partition manager
6465 cache cache.Cache // non-nil for root-partition
6566
67+ fairnessState persistencespb.TaskQueueTypeUserData_FairnessState //Set once on initialization and read only after
68+
6669 cancelNewMatcherSub func ()
6770 cancelFairnessSub func ()
71+ cancelAutoEnableSub func ()
6872
6973 // rateLimitManager is used to manage the rate limit for task queues.
7074 rateLimitManager * rateLimitManager
@@ -82,6 +86,7 @@ func (pm *taskQueuePartitionManagerImpl) GetCache(key any) any {
8286var _ taskQueuePartitionManager = (* taskQueuePartitionManagerImpl )(nil )
8387
8488func newTaskQueuePartitionManager (
89+ ctx context.Context ,
8590 e * matchingEngineImpl ,
8691 ns * namespace.Namespace ,
8792 partition tqid.Partition ,
@@ -119,14 +124,40 @@ func newTaskQueuePartitionManager(
119124 pm .unloadFromEngine (unloadCauseConfigChange )
120125 }
121126
122- var fairness bool
123- fairness , pm .cancelFairnessSub = tqConfig .EnableFairnessSub (unload )
124- // Fairness is disabled for sticky queues for now so that we can still use TTLs.
125- tqConfig .EnableFairness = fairness && partition .Kind () != enumspb .TASK_QUEUE_KIND_STICKY
126- if fairness {
127+ pm .userDataManager .Start ()
128+ err := pm .userDataManager .WaitUntilInitialized (ctx )
129+ if err != nil {
130+ return nil , err
131+ }
132+ //todo(moody): do we need to be more cautious loading this? Do we need to ensure that we have PerType()? Check for a nil return?
133+ data , _ , err := pm .getPerTypeUserData ()
134+ if err != nil {
135+ return nil , err
136+ }
137+
138+ tqConfig .AutoEnable , pm .cancelAutoEnableSub = tqConfig .AutoEnableSub (unload )
139+ pm .fairnessState = data .GetFairnessState ()
140+ switch {
141+ case tqConfig .AutoEnable == false || pm .fairnessState == persistencespb .TaskQueueTypeUserData_FAIRNESS_STATE_UNSPECIFIED :
142+ var fairness bool
143+ fairness , pm .cancelFairnessSub = tqConfig .EnableFairnessSub (unload )
144+ // Fairness is disabled for sticky queues for now so that we can still use TTLs.
145+ tqConfig .EnableFairness = fairness && partition .Kind () != enumspb .TASK_QUEUE_KIND_STICKY
146+ if fairness {
147+ tqConfig .NewMatcher = true
148+ } else {
149+ tqConfig .NewMatcher , pm .cancelNewMatcherSub = tqConfig .NewMatcherSub (unload )
150+ }
151+ case pm .fairnessState == persistencespb .TaskQueueTypeUserData_FAIRNESS_STATE_V1 :
127152 tqConfig .NewMatcher = true
128- } else {
129- tqConfig .NewMatcher , pm .cancelNewMatcherSub = tqConfig .NewMatcherSub (unload )
153+ tqConfig .EnableFairness = false
154+ case pm .fairnessState == persistencespb .TaskQueueTypeUserData_FAIRNESS_STATE_V2 :
155+ tqConfig .NewMatcher = true
156+ if partition .Kind () == enumspb .TASK_QUEUE_KIND_STICKY {
157+ tqConfig .EnableFairness = false
158+ } else {
159+ tqConfig .EnableFairness = true
160+ }
130161 }
131162
132163 defaultQ , err := newPhysicalTaskQueueManager (pm , UnversionedQueueKey (partition ))
@@ -139,7 +170,6 @@ func newTaskQueuePartitionManager(
139170
140171func (pm * taskQueuePartitionManagerImpl ) Start () {
141172 pm .engine .updateTaskQueuePartitionGauge (pm .Namespace (), pm .partition , 1 )
142- pm .userDataManager .Start ()
143173 pm .defaultQueue .Start ()
144174}
145175
@@ -159,6 +189,9 @@ func (pm *taskQueuePartitionManagerImpl) Stop(unloadCause unloadCause) {
159189 if pm .cancelNewMatcherSub != nil {
160190 pm .cancelNewMatcherSub ()
161191 }
192+ if pm .cancelAutoEnableSub != nil {
193+ pm .cancelAutoEnableSub ()
194+ }
162195
163196 // First, stop all queues to wrap up ongoing operations.
164197 for _ , vq := range pm .versionedQueues {
@@ -184,10 +217,6 @@ func (pm *taskQueuePartitionManagerImpl) MarkAlive() {
184217}
185218
186219func (pm * taskQueuePartitionManagerImpl ) WaitUntilInitialized (ctx context.Context ) error {
187- err := pm .userDataManager .WaitUntilInitialized (ctx )
188- if err != nil {
189- return err
190- }
191220 return pm .defaultQueue .WaitUntilInitialized (ctx )
192221}
193222
@@ -197,6 +226,21 @@ func (pm *taskQueuePartitionManagerImpl) AddTask(
197226) (buildId string , syncMatched bool , err error ) {
198227 var spoolQueue , syncMatchQueue physicalTaskQueueManager
199228 directive := params .taskInfo .GetVersionDirective ()
229+
230+ if pm .Partition ().IsRoot () && pm .config .AutoEnable && pm .fairnessState == persistencespb .TaskQueueTypeUserData_FAIRNESS_STATE_UNSPECIFIED {
231+ //TODO(moody): unsure about this check, what is the correct way to check to see if PriorityKey is set?
232+ //TODO(moody): This was originally discussed to be a separate API, but is just exposing this through the generic UpdateUserData sufficient?
233+ // what is the pro/cons of adding the new API and perhaps invoking that instead? We're going to unload either way...
234+ if params .taskInfo .Priority != nil && (params .taskInfo .Priority .FairnessKey != "" || params .taskInfo .Priority .PriorityKey != int32 (pm .config .DefaultPriorityKey )) {
235+ updateFn := func (old * persistencespb.TaskQueueUserData ) (* persistencespb.TaskQueueUserData , bool , error ) {
236+ new := common .CloneProto (old )
237+ perType := new .GetPerType ()[int32 (pm .Partition ().TaskType ())]
238+ perType .FairnessState = persistencespb .TaskQueueTypeUserData_FAIRNESS_STATE_V2
239+ return new , true , nil
240+ }
241+ pm .userDataManager .UpdateUserData (ctx , UserDataUpdateOptions {Source : "Matching auto enable" }, updateFn )
242+ }
243+ }
200244 // spoolQueue will be nil iff task is forwarded.
201245reredirectTask:
202246 spoolQueue , syncMatchQueue , _ , taskDispatchRevisionNumber , err := pm .getPhysicalQueuesForAdd (ctx , directive , params .forwardInfo , params .taskInfo .GetRunId (), params .taskInfo .GetWorkflowId (), false )
@@ -1303,7 +1347,17 @@ func (pm *taskQueuePartitionManagerImpl) getPerTypeUserData() (*persistencespb.T
13031347 return perType , userDataChanged , nil
13041348}
13051349
1306- func (pm * taskQueuePartitionManagerImpl ) userDataChanged () {
1350+ func (pm * taskQueuePartitionManagerImpl ) userDataChanged (old , new * persistencespb.VersionedTaskQueueUserData ) {
1351+ taskType := int32 (pm .Partition ().TaskType ())
1352+ //TODO(moody): this stinks, do we need this more verbose, is this interface bad?
1353+ //TODO(moody): we get calls into this callback quite a bit with data being nil(sampled from unit tests), do we want to go through the full update
1354+ // even in those cases? Should we pass the inner data and avoid a callback when that is nil? I am not totally sure about the implcations....
1355+ if old != nil && old .GetData () != nil && old .GetData ().GetPerType () != nil && new != nil && new .GetData () != nil && new .GetData ().GetPerType () != nil {
1356+ if old .GetData ().GetPerType ()[taskType ].FairnessState != new .GetData ().GetPerType ()[taskType ].FairnessState {
1357+ pm .unloadFromEngine (unloadCauseConfigChange )
1358+ return
1359+ }
1360+ }
13071361 // Update rateLimits if any change is userData.
13081362 pm .rateLimitManager .UserDataChanged ()
13091363
0 commit comments