BeanieODM / beanie

Asynchronous Python ODM for MongoDB
http://beanie-odm.dev/
Apache License 2.0
1.94k stars 203 forks source link

QUESTION: Is events + method an atomic action, or do we need to do verbose transaction management? #297

Closed edgarfelizmenio closed 1 year ago

edgarfelizmenio commented 2 years ago

Hi,

We are trying to implement an "accession number" field called acc in our Beanie Documents. Since acc is a counter that follows a specific format, we decided to make a new field instead of using MongoDB's ObjectID.

Implementation-wise, we are still deciding which approach to follow, since we are not entirely sure with Beanie's behaviour. Here is an example to illustrate:

next_accession_number = 1

def get_accession_number():
    return next_accession_number

def increment_accession_number():
    next_accession_number += 1

class Record(Document):
    title: str
    acc: int

The Record object is the Beanie Document that we are saving to the database, while next_acc_number is the next accession number that will be assigned to a record, and is incremented whenever a new Document is saved to the database. We are avoiding to use Pydantic's default_factory since we only want to assign the accession number by the time the record will be saved to the database.

Currently here are our ideas:

Assign and increment as a before insert event:

class Record(Document):
    title: str
    acc: int

    @before_event(Insert)
    def assign_accession_number(self):
        self.acc = get_accession_number()
        increment_accession_number()

The problem that we see here is when the Insert operation fails for whatever reason, the accession number has already incremented when it shouldn't.

Assign accession number as a before insert event, increment accession number as an after insert event:

class Record(Document):
    title: str
    acc: int

    @before_event(Insert)
    def assign_accession_number(self):
        self.acc = get_accession_number()

    @after_event(Insert)
    def_update_accession_number(self):
        increment_accession_number()

Here, my concern is more on concurrency. Is it possible that two different records have the same accession number assigned (e.g., before_event on second record is executed before the after_event of the first record)? I have checked the source code on how the events are executed. Since the wrapper function is async, can we make an atomic operation (before, action, after) if we simply use await(e.g., await record.save()) or does our use case call for the use of transactions (i.e., no events, enclose assign-save-increment in a transaction using the AsyncIOMotorClient)?

rgajason commented 2 years ago

Regarding concurrency, the Revisions feature may help: https://roman-right.github.io/beanie/tutorial/revision/

github-actions[bot] commented 1 year ago

This issue is stale because it has been open 30 days with no activity.

github-actions[bot] commented 1 year ago

This issue was closed because it has been stalled for 14 days with no activity.