Skip to content

Commit

Permalink
Merge pull request #185 from RaniSputnik/context-deadline
Browse files Browse the repository at this point in the history
Respect Context Deadline
  • Loading branch information
chris-ramon committed Feb 19, 2017
2 parents 22050ee + e95c164 commit a77f106
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 29 deletions.
90 changes: 61 additions & 29 deletions executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,40 +24,72 @@ type ExecuteParams struct {
}

func Execute(p ExecuteParams) (result *Result) {
result = &Result{}

exeContext, err := buildExecutionContext(BuildExecutionCtxParams{
Schema: p.Schema,
Root: p.Root,
AST: p.AST,
OperationName: p.OperationName,
Args: p.Args,
Errors: nil,
Result: result,
Context: p.Context,
})

if err != nil {
result.Errors = append(result.Errors, gqlerrors.FormatError(err))
return
// Use background context if no context was provided
ctx := p.Context
if ctx == nil {
ctx = context.Background()
}

defer func() {
if r := recover(); r != nil {
var err error
if r, ok := r.(error); ok {
err = gqlerrors.FormatError(r)
resultChannel := make(chan *Result)

go func(out chan<- *Result, done <-chan struct{}) {
result := &Result{}

exeContext, err := buildExecutionContext(BuildExecutionCtxParams{
Schema: p.Schema,
Root: p.Root,
AST: p.AST,
OperationName: p.OperationName,
Args: p.Args,
Errors: nil,
Result: result,
Context: p.Context,
})

if err != nil {
result.Errors = append(result.Errors, gqlerrors.FormatError(err))
select {
case out <- result:
case <-done:
}
exeContext.Errors = append(exeContext.Errors, gqlerrors.FormatError(err))
result.Errors = exeContext.Errors
return
}
}()

return executeOperation(ExecuteOperationParams{
ExecutionContext: exeContext,
Root: p.Root,
Operation: exeContext.Operation,
})
defer func() {
if r := recover(); r != nil {
var err error
if r, ok := r.(error); ok {
err = gqlerrors.FormatError(r)
}
exeContext.Errors = append(exeContext.Errors, gqlerrors.FormatError(err))
result.Errors = exeContext.Errors
select {
case out <- result:
case <-done:
}
}
}()

result = executeOperation(ExecuteOperationParams{
ExecutionContext: exeContext,
Root: p.Root,
Operation: exeContext.Operation,
})
select {
case out <- result:
case <-done:
}

}(resultChannel, ctx.Done())

select {
case <-ctx.Done():
result = &Result{}
result.Errors = append(result.Errors, gqlerrors.FormatError(ctx.Err()))
case r := <-resultChannel:
result = r
}
return
}

type BuildExecutionCtxParams struct {
Expand Down
54 changes: 54 additions & 0 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"reflect"
"testing"
"time"

"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/gqlerrors"
Expand Down Expand Up @@ -1639,3 +1640,56 @@ func TestGraphqlTag(t *testing.T) {
t.Fatalf("unexpected result, got: %+v, expected: %+v", expectedData, result.Data)
}
}

func TestContextDeadline(t *testing.T) {
timeout := time.Millisecond * time.Duration(100)
acceptableDelay := time.Millisecond * time.Duration(10)
expectedErrors := []gqlerrors.FormattedError{
{
Message: context.DeadlineExceeded.Error(),
Locations: []location.SourceLocation{},
},
}

// Query type includes a field that won't resolve within the deadline
var queryType = graphql.NewObject(
graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"hello": &graphql.Field{
Type: graphql.String,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
time.Sleep(2 * time.Second)
return "world", nil
},
},
},
})
schema, err := graphql.NewSchema(graphql.SchemaConfig{
Query: queryType,
})
if err != nil {
t.Fatalf("unexpected error, got: %v", err)
}

ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()

startTime := time.Now()
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: "{hello}",
Context: ctx,
})
duration := time.Since(startTime)

if duration > timeout+acceptableDelay {
t.Fatalf("graphql.Do completed in %s, should have completed in %s", duration, timeout)
}
if !result.HasErrors() || len(result.Errors) == 0 {
t.Fatalf("Result should include errors when deadline is exceeded")
}
if !reflect.DeepEqual(expectedErrors, result.Errors) {
t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expectedErrors, result.Errors))
}
}

0 comments on commit a77f106

Please sign in to comment.