uralbash / sqlalchemy_mptt

SQLAlchemy nested sets mixin (MPTT)
http://sqlalchemy-mptt.readthedocs.io
MIT License
196 stars 32 forks source link

The problem with duplicate nodes in same tree #80

Open savao opened 2 years ago

savao commented 2 years ago

On our project, with numerous requests to api in a short time, they are processed in different threads. When a user requests the creation of several instances at once in a table that is inherited from MPTT, this occurs in such a way that the front-end sends several different requests to the back-end in a very short time, each of which calls the code for creating one instance of this table. These requests are processed in different threads. But an unpleasant situation arises, when processing in different threads, instances are assigned the same tree and the same left and right numbers for different table rows. As a result, it turns out that the tree has in the same node several different values ​​for this node.

Please help me to fix this problem. How we can deal with this problem?

models.py:

import sqlalchemy as sa
from sqlalchemy.orm import relationship
from sqlalchemy_mptt import BaseNestedSets

class UserFile(BaseNestedSets):
__tablename__ = "user_files"

id = sa.Column(sa.Integer, primary_key=True)
is_file: bool = sa.Column(sa.Boolean, default=False)
name: str = sa.Column(sa.String(100))
real_name: Optional[str] = sa.Column(sa.String(50))
size: int = sa.Column(sa.Integer, default=0)
downloaded_at: datetime = sa.Column(sa.DateTime, default=datetime.utcnow, nullable=False)
author_id = sa.Column("author_id", sa.ForeignKey("users.id", ondelete="CASCADE"), nullable=True)
author = relationship("User", foreign_keys=[self.author_id])

def __repr__(self) -> str:
    return self.name

schemas.py:

@spec_definition
class UserFileSchema(ModelSchema):
class Meta:
    model = UserFile
    fields = ["id", "name", "is_file", "size", "downloaded_at"]

views.py:

@api.resource("/user/<int:user_id>/files/root/add_file")
class UserFilesAddRootFileResource(SchemaResource):
    def post(self, user_id: int) -> dict:
        content = request.files.get("file")
        item = add_file(user_id, content, filename=content.filename)
        return UserFileSchema().dump(item)

user_files.py:

def add_file(user_id: int, content: FileStorage, filename: str) -> UserFile:
    size = file_size(content)
    final_name = storage.save(None, content, True)
    return add_user_file(
        user_id=user_id,
        name=filename,
        is_file=True,
        real_name=final_name,
        size=size,
    )

def add_user_file(
    user_id: int,
    name: str,
    real_name: Optional[str] = None,
    size: int = 0,
) -> UserFile:
    base_name = name
    if is_file:
        base_name, ext = storage.splitext(base_name)
    result_name = name
    item_id = None
item = UserFile(
    name=result_name,
    real_name=real_name,
    is_file=is_file,
    size=size,
    )
item.save()
return item

I am omitting the definition of some functions from the storage module, which are currently just a layer for working with the os.path.splitext(name) function or writing a file to disk, and they are not relevant to this problem.

I also wanted to ask why some tree nodes can have negative right and left numbers?

We use in our project Flask, SQLAlchemy, sqlalchemy-mptt==0.2.5