-
Notifications
You must be signed in to change notification settings - Fork 287
🐛(backend) race condition create doc #633
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
Tests for Documents API endpoint in impress's core app: children create | ||
""" | ||
|
||
from concurrent.futures import ThreadPoolExecutor | ||
from uuid import uuid4 | ||
|
||
import pytest | ||
|
@@ -249,3 +250,41 @@ def test_api_documents_children_create_force_id_existing(): | |
assert response.json() == { | ||
"id": ["A document with this ID already exists. You cannot override it."] | ||
} | ||
|
||
|
||
@pytest.mark.django_db(transaction=True) | ||
def test_api_documents_create_document_children_race_condition(): | ||
""" | ||
It should be possible to create several documents at the same time | ||
without causing any race conditions or data integrity issues. | ||
""" | ||
|
||
user = factories.UserFactory() | ||
|
||
client = APIClient() | ||
client.force_login(user) | ||
|
||
document = factories.DocumentFactory() | ||
|
||
factories.UserDocumentAccessFactory(user=user, document=document, role="owner") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are not testing concurrency on children creation. But on parent creation. Children will be created on different parent so they have a different path, so you will never have a unique constraint error in you test. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, look at here: https://github.com/suitenumerique/docs/pull/633/files#diff-719c99bace85294f27e3909b1067f53da483436c448af38d93a7856fb5ae36faR290 If you remove my patch on the children viewset, this test gives an error. |
||
|
||
def create_document(): | ||
return client.post( | ||
f"/api/v1.0/documents/{document.id}/children/", | ||
{ | ||
"title": "my child", | ||
}, | ||
) | ||
|
||
with ThreadPoolExecutor(max_workers=2) as executor: | ||
future1 = executor.submit(create_document) | ||
future2 = executor.submit(create_document) | ||
|
||
response1 = future1.result() | ||
response2 = future2.result() | ||
|
||
assert response1.status_code == 201 | ||
assert response2.status_code == 201 | ||
|
||
document.refresh_from_db() | ||
assert document.numchild == 2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
Tests for Documents API endpoint in impress's core app: create | ||
""" | ||
|
||
from concurrent.futures import ThreadPoolExecutor | ||
from uuid import uuid4 | ||
|
||
import pytest | ||
|
@@ -51,6 +52,36 @@ def test_api_documents_create_authenticated_success(): | |
assert document.accesses.filter(role="owner", user=user).exists() | ||
|
||
|
||
@pytest.mark.django_db(transaction=True) | ||
def test_api_documents_create_document_race_condition(): | ||
""" | ||
It should be possible to create several documents at the same time | ||
without causing any race conditions or data integrity issues. | ||
""" | ||
|
||
def create_document(title): | ||
user = factories.UserFactory() | ||
client = APIClient() | ||
client.force_login(user) | ||
return client.post( | ||
"/api/v1.0/documents/", | ||
{ | ||
"title": title, | ||
}, | ||
format="json", | ||
) | ||
|
||
with ThreadPoolExecutor(max_workers=2) as executor: | ||
future1 = executor.submit(create_document, "my document 1") | ||
future2 = executor.submit(create_document, "my document 2") | ||
|
||
response1 = future1.result() | ||
response2 = future2.result() | ||
|
||
assert response1.status_code == 201 | ||
assert response2.status_code == 201 | ||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add test for the children use case as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes we are working on it but it's much more difficult to reproduce There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
def test_api_documents_create_authenticated_title_null(): | ||
"""It should be possible to create several documents with a null title.""" | ||
user = factories.UserFactory() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should add a comment explaining this mode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should be done when creating children as well? See children method on the viewset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed with lunika, I don't think the lock should be managed by the viewset, is should be at the model/manager level.
And as you mentioned, the same could happen to children/siblings, it should be covered also.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We did try to add it in the model, see here, but it brings instability in our tests with the factory, a fix brings another instability, and so on.
This naive way is much more stable.

Most of my flakiness is gone as well: