88 "go.temporal.io/api/workflowservice/v1"
99 "go.temporal.io/server/chasm"
1010 "go.temporal.io/server/chasm/lib/activity/gen/activitypb/v1"
11- "go.temporal.io/server/common/persistence/transitionhistory"
1211)
1312
1413type handler struct {
@@ -62,8 +61,9 @@ func (h *handler) PollActivityExecution(
6261 ctx context.Context ,
6362 req * activitypb.PollActivityExecutionRequest ,
6463) (* activitypb.PollActivityExecutionResponse , error ) {
65- switch req .GetFrontendRequest ().GetWaitPolicy ().(type ) {
66- case nil :
64+
65+ waitPolicy := req .GetFrontendRequest ().GetWaitPolicy ()
66+ if waitPolicy == nil {
6767 return chasm .ReadComponent (
6868 ctx ,
6969 chasm.NewComponentRef [* Activity ](chasm.EntityKey {
@@ -75,42 +75,66 @@ func (h *handler) PollActivityExecution(
7575 nil ,
7676 nil ,
7777 )
78+ }
79+
80+ var response * activitypb.PollActivityExecutionResponse
81+ var newRef []byte
82+ var err error
83+
84+ switch waitPolicy .(type ) {
7885 case * workflowservice.PollActivityExecutionRequest_WaitAnyStateChange :
79- return pollActivityExecutionWaitAnyStateChange (ctx , req )
86+ response , newRef , err = pollActivityExecutionWaitAnyStateChange (ctx , req )
8087 case * workflowservice.PollActivityExecutionRequest_WaitCompletion :
81- return pollActivityExecutionWaitCompletion (ctx , req )
88+ response , newRef , err = pollActivityExecutionWaitCompletion (ctx , req )
8289 default :
83- return nil , fmt .Errorf ("unexpected wait policy type: %T" , req .GetFrontendRequest ().GetWaitPolicy ())
90+ return nil , fmt .Errorf ("unexpected wait policy type: %T" , waitPolicy )
91+ }
92+ if err != nil {
93+ return nil , err
8494 }
95+ response .GetFrontendResponse ().StateChangeLongPollToken = newRef
96+ return response , nil
8597}
8698
8799// pollActivityExecutionWaitAnyStateChange waits until the activity state has advanced beyond that
88- // specified by the submitted token. If no token was submitted, it returns the current state
89- // without waiting.
90- func pollActivityExecutionWaitAnyStateChange (
91- ctx context.Context ,
92- req * activitypb.PollActivityExecutionRequest ,
93- ) (* activitypb.PollActivityExecutionResponse , error ) {
94- request := req .GetFrontendRequest ()
95- waitPolicy := request .GetWaitPolicy ().(* workflowservice.PollActivityExecutionRequest_WaitAnyStateChange )
96- refBytesFromToken := waitPolicy .WaitAnyStateChange .GetLongPollToken ()
100+ // specified by the submitted token. If no token was submitted, it returns the current state without
101+ // waiting.
102+ func pollActivityExecutionWaitAnyStateChange (ctx context.Context , req * activitypb.PollActivityExecutionRequest ) (* activitypb.PollActivityExecutionResponse , []byte , error ) {
103+
104+ // TODO(dan): it is not guaranteed that the response data will differ from that received on a
105+ // previous call. However, we don't want the server to say "there's been a change" while
106+ // returning data in which the change is not apparent.
107+
108+ refBytesFromToken := req .GetFrontendRequest ().
109+ GetWaitPolicy ().(* workflowservice.PollActivityExecutionRequest_WaitAnyStateChange ).
110+ WaitAnyStateChange .GetLongPollToken ()
97111
98112 var lastSeenRef chasm.ComponentRef
99113 if refBytesFromToken != nil {
100114 var err error
101115 lastSeenRef , err = chasm .DeserializeComponentRef (refBytesFromToken )
102116 if err != nil {
103- return nil , serviceerror .NewInvalidArgument ("invalid long poll token" )
117+ return nil , nil , serviceerror .NewInvalidArgument ("invalid long poll token" )
118+ }
119+ if lastSeenRef .NamespaceID != req .GetNamespaceId () ||
120+ lastSeenRef .BusinessID != req .GetFrontendRequest ().GetActivityId () ||
121+ lastSeenRef .EntityID != req .GetFrontendRequest ().GetRunId () {
122+ // token is inconsistent with request
123+ return nil , nil , serviceerror .NewInvalidArgument ("invalid long poll token" )
104124 }
105125 } else {
106126 lastSeenRef = chasm.NewComponentRef [* Activity ](chasm.EntityKey {
107127 NamespaceID : req .GetNamespaceId (),
108- BusinessID : request .GetActivityId (),
109- EntityID : request .GetRunId (),
128+ BusinessID : req . GetFrontendRequest () .GetActivityId (),
129+ EntityID : req . GetFrontendRequest () .GetRunId (),
110130 })
111131 }
112132
113- response , ref , err := chasm .PollComponent (
133+ // PollComponent will return an error if lastSeenRef is not consistent with the entity
134+ // transition history on this shard, or if the state on this shard is behind the ref after a
135+ // reload.
136+ // TODO(dan): retryability of these errors
137+ return chasm .PollComponent (
114138 ctx ,
115139 lastSeenRef ,
116140 func (
@@ -120,17 +144,35 @@ func pollActivityExecutionWaitAnyStateChange(
120144 ) (* activitypb.PollActivityExecutionResponse , bool , error ) {
121145 // TODO(dan): we're walking the tree to construct a ref when all we want here is the
122146 // root/entity VT. Would it make sense for Context to provide access to root node?
123- refBytes , err := ctx .Ref (a )
147+ currentRefBytes , err := ctx .Ref (a )
124148 if err != nil {
125149 return nil , false , err
126150 }
127-
128- ref , err := chasm .DeserializeComponentRef (refBytes )
151+ currentRef , err := chasm .DeserializeComponentRef (currentRefBytes )
129152 if err != nil {
130153 return nil , false , err
131154 }
132155
133- switch transitionhistory .Compare (lastSeenRef .GetEntityLastUpdateVersionedTransition (), ref .GetEntityLastUpdateVersionedTransition ()) {
156+ if lastSeenRef .EntityID != currentRef .EntityID {
157+ // The runID from the token doesn't match this shard's state. We return immediately,
158+ // on the basis that this constitutes a state change. If the runID from the token is
159+ // ahead of this shard's state then this will be detected by shard ownership or
160+ // staleness checks and the caller will receive an error. Therefore we can assume
161+ // that the runID from the token is behind the shard state and that it's appropriate
162+ // to report a state change to the caller.
163+ response , err := a .buildPollActivityExecutionResponse (ctx , req )
164+ if err != nil {
165+ return nil , true , err
166+ }
167+ return response , true , nil
168+ }
169+
170+ // TODO(dan): is this leaking too much detail about VTs?
171+ refComparison , err := chasm .CompareComponentRefs (& lastSeenRef , & currentRef )
172+ if err != nil {
173+ return nil , false , err
174+ }
175+ switch refComparison {
134176 case - 1 :
135177 // state has advanced beyond last seen: this is what we're waiting for
136178 response , err := a .buildPollActivityExecutionResponse (ctx , req )
@@ -142,27 +184,21 @@ func pollActivityExecutionWaitAnyStateChange(
142184 // state is same as last seen: keep waiting
143185 return nil , false , nil
144186 case 1 :
145- // TODO(dan): error code?
187+ // Impossible: PollComponent guarantees that at this point, current VT >= lastSeen VT.
146188 return nil , false , serviceerror .NewFailedPrecondition ("submitted long-poll token represents a state beyond current" )
147189 default :
190+ // Impossible
148191 return nil , false , serviceerror .NewInternal ("unexpected transition history comparison result" )
149192 }
150193 },
151194 req ,
152195 )
153- if err != nil {
154- return nil , err
155- }
156- response .GetFrontendResponse ().StateChangeLongPollToken = ref
157- return response , nil
158196}
159197
160- func pollActivityExecutionWaitCompletion (
161- ctx context.Context ,
162- req * activitypb.PollActivityExecutionRequest ,
163- ) (* activitypb.PollActivityExecutionResponse , error ) {
198+ // pollActivityExecutionWaitCompletion waits until the activity is completed.
199+ func pollActivityExecutionWaitCompletion (ctx context.Context , req * activitypb.PollActivityExecutionRequest ) (* activitypb.PollActivityExecutionResponse , []byte , error ) {
164200 // TODO(dan): untested
165- response , ref , err := chasm .PollComponent (
201+ return chasm .PollComponent (
166202 ctx ,
167203 chasm.NewComponentRef [* Activity ](chasm.EntityKey {
168204 NamespaceID : req .GetNamespaceId (),
@@ -186,9 +222,4 @@ func pollActivityExecutionWaitCompletion(
186222 },
187223 req ,
188224 )
189- if err != nil {
190- return nil , err
191- }
192- response .GetFrontendResponse ().StateChangeLongPollToken = ref
193- return response , nil
194225}
0 commit comments