enne87
enne87

Reputation: 2299

Atomic insertion in Flask

I have a problem to achieve the following in Flask and SQLAlchemy: I have a kind of blog application written in Flask where a user can write textblocks. So I have a user model and a textblock model and the textblocks need to be kind of unique. Textblocks can be written and sent from two different client applications and that can happpen at the same time. I need to make the insertion and processing of the timeblock atomic so that the same timeblock is not inserted and processed at the same time by the two client-applications. The models:

class TextblockModel(db.Model):
    __tablename__ = "textblock"

    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
    text = db.Column(db.Text, default="", nullable=False)

    user = db.relationship("User", backref=db.backref("texts", lazy=True))

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    email = db.Column(db.String(100), unique=True, nullable=False)
    password = db.Column(db.String(255))

For each of the two client applications, I have one post endpoint:

def post1(self):
        blocks = insert_block_into_db(request.json, current_user, CLIENTAPP1)
        process_blocks(blocks)

def post2(self):
        blocks = insert_block_into_db(request.json, current_user, CLIENTAPP2)
        process_blocks(blocks)

I need to make the insertion and processing of textblocks atomic. So something like this:

def post1(self):
    with lock:
        blocks = insert_block_into_db(request.json, current_user, WEBAPP1)
        process_blocks(blocks)

def post2(self):
    with lock:
        blocks = insert_block_into_db(request.json, current_user, WEBAPP2)
        process_blocks(blocks)

But I think a plain Python lock doesn't work since WSGI spawns multiple processes for request-handling. Further, I don't need to lock everything but only the rows that are related to the current user. Does anyone have an idea how I could solve that?

Upvotes: 0

Views: 1730

Answers (1)

NMO
NMO

Reputation: 766

If you are using gunicorn, then you can set preload_app = True in your gunicorn.conf. This ensures that a lock is shared across workers (i.e. processes).

A more webserver agnostic solution would be to use NamedAtomicLock. This is a great package that creates an operating system global lock. So a lock can be shared across processes. A lock creates an empty folder in a specified directory which is used as shared resource. Since creating and deleting direcories is an atomic operation according to the POSIX standard, this mechanism allows to lock across processes. I am not sure if it works on non-UNIX machines, e.g. Windows.

Upvotes: 1

Related Questions