Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TransactionSerializationError in UPDATE concurrent queries #7342

Open
MrFoxPro opened this issue May 13, 2024 · 10 comments
Open

TransactionSerializationError in UPDATE concurrent queries #7342

MrFoxPro opened this issue May 13, 2024 · 10 comments

Comments

@MrFoxPro
Copy link

MrFoxPro commented May 13, 2024

  • EdgeDB Version: 4.2, 4.6, 4.7, 5.3
  • EdgeDB CLI Version: 5.1.0+7c5764f
  • OS Version: GNU/Linux 6.8.9 x64_86

Steps to Reproduce:

module default {
  type A {
    required value: int64 { default := 0; }
  }
}
insert A {} 

Then, open two bash instances and run queries:

for i in {1..100}; do edgedb query "select(update A set { value := .value + 1 }).value"; done

Observe errors:

errors.mp4

After end of loops, A.value will not reach 200.
I got this also in JS and Rust client.

Compared to PostgresQL:

psql.mp4
@MrFoxPro MrFoxPro changed the title TransactionSerializationError for select (update ...) queries run concurrently TransactionSerializationError for select (update ...) concurrent queries May 13, 2024
@MrFoxPro MrFoxPro changed the title TransactionSerializationError for select (update ...) concurrent queries TransactionSerializationError in UPDATE concurrent queries May 14, 2024
@MrFoxPro
Copy link
Author

I workaround this by using RwLock on client, so I can have select/insert/delete concurrent queries and sequential update queries. But I guess EdgeDB should handle it itself, as underlying postgres does?

@msullivan
Copy link
Member

I think part of the reason for the difference with postgres is that we are using postgres's SERIALIZABLE isolation level, which is stricter than the default of READ COMMITTED.

When I try it with postgres natively using SERIALIZABLE or REPEATABLE READ, I get the same results.

Another part of this is that the CLI doesn't retry on fails, though I think all of our language clients do.

@MrFoxPro
Copy link
Author

MrFoxPro commented May 21, 2024

Another part of this is that the CLI doesn't retry on fails, though I think all of our language clients do.

My observation is completely opposite. I was getting this error on NodeJS separate workers and in multi-threaded Rust app too. I believe you're confusing it with TransactionConflictError https://github.com/edgedb/edgedb-rust/blob/093729876737738a559e35c2ec3f0ae27efd2f69/edgedb-tokio/src/options.rs#L26

Anyway, do you think retrying is correct way to workaround this? First time I tried to implement retries, and then I compared to RwLock implementation. I observed better results with manual locking on UPDATE operations. I guess receiving error and retrying multiple times is more expensive than synchronous locking.

@msullivan
Copy link
Member

Hmmmm, I had thought that TransactionSerializationError was marked as SHOULD_RETRY also, but it is not: https://github.com/edgedb/edgedb/blob/b9b4177d8c0080235e46b7e825384224fbe5923c/edb/api/errors.txt#L128C18-L128C47
Maybe we need to change that

Doing synchronous locking should work great if you only have one client, but it doesn't help you in a distributed situation.

@elprans
Copy link
Member

elprans commented May 21, 2024

SHOULD_RETRY should be inherited, and it's set on the parent TransactionConflictError. The bindings might not be reflecting that faithfully, though.

@msullivan
Copy link
Member

Oh, good point. It looks like the rust bindings do reflect that faithfully: https://github.com/edgedb/edgedb-rust/blob/093729876737738a559e35c2ec3f0ae27efd2f69/edgedb-errors/src/kinds.rs#L139.

This sort of counter workload is not a great fit for SERIALIZED/REPEATABLE READ, perhaps.

@1st1
Copy link
Member

1st1 commented May 22, 2024

Oh, good point. It looks like the rust bindings do reflect that faithfully:

@msullivan Can you double check js/py/go bindings?

@MrFoxPro
Copy link
Author

MrFoxPro commented May 22, 2024

Is it possible to configure EdgeDB to work with postgres in READ COMMITTED mode? What are benefits/drawbacks, since I'm not planning to distrubute my db across servers so far

@MrFoxPro
Copy link
Author

I changed global and database default_transaction_isolation to 'read committed' but TransactionSerializationError still happens

@msullivan
Copy link
Member

EdgeDB sets the mode in the connection settings when it connects to postgres, I think.
Changing it to read committed there does fix this problem, though I don't think there is a way to do it as a user.
Which is fine, because a bunch of weird stuff can happen in that mode in edgedb. (Exclusive constraints not enforced properly, for one)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants