Skip to content

Query error automatically discards the transaction #105

@danielmai

Description

@danielmai

This is a behavior change from dgo v1.0 to dgo v2. In dgo v2.1.0 a query error discards the current transaction, so future queries using the same txn are not allowed. This used to work in dgo v1.

Steps to reproduce

This is reproducible with the following sample code that runs these two queries in the same txn:

  1. {me(){}me(){}}: The first query returns a query error. In this case, Duplicate aliases not allowed: me.
  2. {me(){}}: This is a valid query that I expect should succeed.

dgo/v2:

package main

import (
	"context"
	"flag"
	"fmt"
	"log"

	"github.com/dgraph-io/dgo/v2"
	"github.com/dgraph-io/dgo/v2/protos/api"
	"google.golang.org/grpc"
)

var (
	addr = flag.String("addr", "localhost:9180", "Dgraph Alpha address.")

	ctx = context.Background()

	query1 = `{me(){}me(){}}`
	query2 = `{me(){}}`
)

func main() {
	flag.Parse()
	conn, err := grpc.Dial(*addr, grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))

	txn := dg.NewTxn()

	resp, err := txn.Query(ctx, query1)
	if err != nil {
		log.Printf("Query 1 error: %v\n", err)
	} else {
		log.Printf("Response: %v\n", string(resp.Json))
	}

	resp, err = txn.Query(ctx, query2)
	if err != nil {
		log.Printf("Query 2 error: %v\n", err)
	} else {
		log.Printf("Response: %v\n", string(resp.Json))
	}

	txn.Discard(ctx)

	fmt.Println("Done.")
}

Output:

2019/11/15 11:43:12 Query 1 error: rpc error: code = Unknown desc = Duplicate aliases not allowed: me
2019/11/15 11:43:12 Query 2 error: Transaction has already been committed or discarded
Done.

Expected behavior

In dgo v1 this behaves differently. We can use the same example code but change the import paths to use dgo v1.0.0:

dgo (v1):

package main

import (
	"context"
	"flag"
	"fmt"
	"log"

	"github.com/dgraph-io/dgo"
	"github.com/dgraph-io/dgo/protos/api"
	"google.golang.org/grpc"
)

var (
	ctx  = context.Background()
	addr = flag.String("addr", "localhost:9180", "Dgraph Alpha address.")

	query1 = `{me(){}me(){}}`
	query2 = `{me(){}}`
)

func main() {
	flag.Parse()
	conn, err := grpc.Dial(*addr, grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	dg := dgo.NewDgraphClient(api.NewDgraphClient(conn))

	txn := dg.NewTxn()

	resp, err := txn.Query(ctx, query1)
	if err != nil {
		log.Printf("Query 1 error: %v\n", err)
	} else {
		log.Printf("Response: %v\n", string(resp.Json))
	}

	resp, err = txn.Query(ctx, query2)
	if err != nil {
		log.Printf("Query 2 error: %v\n", err)
	} else {
		log.Printf("Response: %v\n", string(resp.Json))
	}

	txn.Discard(ctx)

	fmt.Println("Done.")
}

Output:

2019/11/15 11:45:15 Query 1 error: rpc error: code = Unknown desc = Duplicate aliases not allowed: me
2019/11/15 11:45:15 Response: {"me":[]}
Done.

Additional notes

In dgo v2 the Query and QueryWithVars methods internally call Do. And Do calls txn.Discard if a query returns an error.

https://github.com/dgraph-io/dgo/blob/3efa60e5593b4dc7dafc9b2cd1d5408bfcdebbfd/txn.go#L159

Discarding txn after an error is only documented for the Mutate method in both dgo v1 and dgo v2:

If the mutation fails, then the transaction is discarded and all future operations on it will fail.

But discarding the transaction after a query error was not and is not the documented behavior for the Query methods in dgo.

Metadata

Metadata

Assignees

Labels

kind/bugSomething isn't workingstatus/acceptedWe were able to reproduce the issue and accept to work on it.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions