Skip to content

Commit 1a0a0b7

Browse files
authored
detect blocking call in query handler (#227)
* detect blocking call in query handler * fix lint * use more descriptive error message
1 parent 368b3cf commit 1a0a0b7

File tree

3 files changed

+20
-8
lines changed

3 files changed

+20
-8
lines changed

internal_worker.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -783,10 +783,7 @@ func (th *hostEnvImpl) decode(data []byte, to []interface{}) error {
783783
}
784784
}
785785

786-
if err := encoder.Unmarshal(data, to); err != nil {
787-
return err
788-
}
789-
return nil
786+
return encoder.Unmarshal(data, to)
790787
}
791788

792789
// encode multiple arguments(arguments to a function).

internal_workflow.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import (
4040

4141
const (
4242
defaultSignalChannelSize = 100000 // really large buffering size(100K)
43+
44+
panicIllegalAccessCoroutinueState = "getState: illegal access from outside of workflow context"
4345
)
4446

4547
type (
@@ -461,7 +463,11 @@ func getState(ctx Context) *coroutineState {
461463
if s == nil {
462464
panic("getState: not workflow context")
463465
}
464-
return s.(*coroutineState)
466+
state := s.(*coroutineState)
467+
if !state.dispatcher.executing {
468+
panic(panicIllegalAccessCoroutinueState)
469+
}
470+
return state
465471
}
466472

467473
func (c *channelImpl) Receive(ctx Context, valuePtr interface{}) (more bool) {
@@ -1122,7 +1128,14 @@ func (h *queryHandler) execute(input []byte) (result []byte, err error) {
11221128
defer func() {
11231129
if p := recover(); p != nil {
11241130
result = nil
1125-
err = fmt.Errorf("handler for query type %s failed: %v", h.queryType, p)
1131+
st := getStackTraceRaw("query handler [panic]:", 7, 0)
1132+
if p == panicIllegalAccessCoroutinueState {
1133+
// query handler code try to access workflow functions outside of workflow context, make error message
1134+
// more descriptive and clear.
1135+
p = "query handler must not use cadence context to do things like cadence.NewChannel(), " +
1136+
"cadence.Go() or to call any workflow blocking functions like Channel.Get() or Future.Get()"
1137+
}
1138+
err = fmt.Errorf("query handler panic: %v, stack trace: %v", p, st)
11261139
}
11271140
}()
11281141

workflow.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,10 @@ func GetVersion(ctx Context, changeID string, minSupported, maxSupported Version
633633
// All the input parameter must be serializable. You should call cadence.SetQueryHandler() at the beginning of the workflow
634634
// code. When client calls Client.QueryWorkflow() to cadence server, a task will be generated on server that will be dispatched
635635
// to a workflow worker, which will replay the history events and then execute a query handler based on the query type.
636-
// The query handler will be invoked out of the context of the workflow, meaning that the handler must not use cadence
637-
// context to call any workflow blocking functions like Channel.Get() or Future.Get().
636+
// The query handler will be invoked out of the context of the workflow, meaning that the handler code must not use cadence
637+
// context to do things like cadence.NewChannel(), cadence.Go() or to call any workflow blocking functions like
638+
// Channel.Get() or Future.Get(). Trying to do so in query handler code will fail the query and client will receive
639+
// QueryFailedError.
638640
// Example of workflow code that support query type "current_state":
639641
// func MyWorkflow(ctx cadence.Context, input string) error {
640642
// currentState := "started" // this could be any serializable struct

0 commit comments

Comments
 (0)