Something wrong with upsert

Hi,

This is my User type:

<User.username>: string @index(hash, trigram) @upsert .
<User.full_name>: string @index(term) @lang .
<User.email>: string @index(hash, trigram) @upsert .
<User.joined_on>: datetime @index(month) .
<User.last_modified>: datetime .
<User.memberships>: [uid] @count @reverse .
<User.api_key_sets>: uid @reverse .

type <User> {
	User.username
	User.full_name
	User.email
	User.joined_on
	User.last_modified
	User.memberships
	User.api_key_sets
}

I insert the following data using ratel:

{
    set{
      # Users
      _:or <dgraph.type> "User" .
      _:or <User.username>  "spinelsun" .
      _:or <User.email> "my.mail@gmail.com" .
      _:or <User.joined_on> "2020-11-22" .
      _:or <User.full_name> "Or Chen" .
    }
}

It works as expected on the first time I clicked run - a new node of type user with all the data is created.
The problem is when I hit run again another node is created even though I have the @upsert directive on User.email and User.username predicates.
according to the documentation:

When committing transactions involving predicates with the @upsert directive, Dgraph checks index keys for conflicts, helping to enforce uniqueness constraints when running concurrent upserts.

What am I doing wrong?

Thanks,
Spinelsun

Hi @spinelsun
If you start two transactions concurrently and try to commit together, the @upsert directive will come into action and abort one of the transactions. The @upsert directive does not get activated when you commit transactions via Ratel in a back-to-back, non-overlapping fashion.
See @dmai’s note in this ticket: Complete working example of an upsert operation - #2 by dmai
Also, please review this video.

1 Like

Thanks

If using an upsert block, is the directive unnecessary? Or should the directive still be placed on the fields to protect against concurrent upsert blocks?

I see this in the docs for upsert block which makes it seem like the directive is unnecessary if using the upsert block, but confirmation would be nice :slight_smile:

Upsert operations are intended to be run concurrently, as per the needs of the application. As such, it’s possible that two concurrently running operations could try to add the same node at the same time. For example, both try to add a user with the same email address. If they do, then one of the transactions will fail with an error indicating that the transaction was aborted.

Hi @seanlaff,
Yes, while using the upsert block, @upsert directive is not necessary.
The video shared above does not use an upsert block to demonstrate @upsert directive.

1 Like