Hi there,
There’s a good chance I’m not using upsert correctly since what I am trying to do should be trivial but I’m struggling a lot with making it work.
I Want to Do
I want to make sure a user cannot be created twice. Using an “email” predicate is the unique constraint. In the SQL world, this is much easier by just declaring that a column is unique. In Dgraph, it seems that the only way to do this is through a conditional if.
However, even with the conditional-if, it seems to not work when I hammer it with a few goroutines that try to create a user with the same email. It ends up with duplicate users.
What I Did
Concurrently created the same users over and over again using the upsert mechanism:
type service { *dgo.Dgraph }
func (s *service) CreateUser(ctx context.Context, email, hashedPassword string) error {
mut := fmt.Sprintf(`
_:user <dgraph.type> "User" .
_:user <email> %q .
_:user <hashedPassword> %q .
`, email, hashedPassword)
query := fmt.Sprintf(`query {
v as var(func: eq(email, %q))
}`, email)
resp, err := s.NewTxn().Do(ctx, &api.Request{
Query: query,
Mutations: []*api.Mutation{{
Cond: `@if(eq(len(v), 0))`,
SetNquads: []byte(mut),
}},
CommitNow: true,
})
if err != nil {
return err
}
if len(resp.Uids) > 1 {
return fmt.Errorf("unexpected uid results length: %d", len(resp.Uids))
}
if len(resp.Uids) == 0 {
return fmt.Errorf("user already exists")
}
return nil
}
// elsewhere
func testConcurrentCreate(t *testing.T, s *service) {
email := "me@gmail.com"
errCh := make(chan error, 5)
for i := 0; i < 5; i++ {
go func() {
errCh <- s.CreateUser(ctx, email, "123")
}()
}
var errCount int
for i := 0; i < 5; i++ {
err := <-errCh
if err == nil {
continue
}
if err.Error() != "user already exists" {
t.Fatal(err)
}
errCount++
}
if errCount != 4 {
t.Fatalf("expected 4 duplicate entry errors but got %d", errCount)
}
}
What I expected
I expected only 1 user would ever be created. But instead, I end up with a few duplicate users on every run.
Dgraph Metadata
dgraph version
docker run --rm -it -p 8080:8080 -p 9080:9080 -p 8000:8000 dgraph/standalone:v20.11.1
PASTE THE RESULTS OF dgraph version
HERE.
dgraph version
[Decoder]: Using assembly version of decoder
Page Size: 4096
Dgraph version : v20.11.1
Dgraph codename : tchalla-1
Dgraph SHA-256 : cefdcc880c0607a92a1d8d3ba0beb015459ebe216e79fdad613eb0d00d09f134
Commit SHA-1 : 7153d13fe
Commit timestamp : 2021-01-28 15:59:35 +0530
Branch : HEAD
Go version : go1.15.5
jemalloc enabled : true
For Dgraph official documentation, visit https://dgraph.io/docs/.
For discussions about Dgraph , visit http://discuss.dgraph.io.
Licensed variously under the Apache Public License 2.0 and Dgraph Community License.
Copyright 2015-2020 Dgraph Labs, Inc.
Thank you!