question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Is there a race condition when allocating stock?

See original GitHub issue

there are several methods in the partner.StockRecord model that look a bit like this:

def allocate(self, amount, reference='', status=''):
        """
        Convenience method for ring-fencing money against this source
        """
        self.amount_allocated += amount
        self.save()

I didn’t dig too deep but I didn’t find any use of select_for_update in the source so I think even knowing that everything’s wrapped in a per-request transaction there’s a possible race condition here?

Should the methods do something like this to be safer?

def allocate(self, amount, reference='', status=''):
        """
        Convenience method for ring-fencing money against this source
        """
        self.__class__.objects.select_for_update().filter(pk=self.pk).update(
            amount_allocated=F('amount_allocated') + amount)

Issue Analytics

  • State:closed
  • Created 8 years ago
  • Comments:11 (11 by maintainers)

github_iconTop GitHub Comments

1reaction
anentropiccommented, Oct 4, 2016

coming back on my earlier comments, I think select_for_update is not necessary when doing an atomic update, you would only use it where you are first selecting, then modifying the instance, then saving

you could manually send those signals for the affected rows, e.g.

from django.db import router
from django.db.models import signals

def allocate(self, quantity):
    """
    Record a stock allocation.

    This normally happens when a product is bought at checkout.  When the
    product is actually shipped, then we 'consume' the allocation.
    """

    self.__class__.objects.filter(pk=self.pk).update(
        num_allocated=Case(
            When(num_allocated__isnull=True,
                 then=(0 + quantity)),
            default=F('num_allocated') + quantity
        ),
    )
    signals.post_save.send(
        sender=self.__class__,
        instance=self,
        created=False,
        raw=False,
        using=router.db_for_write(self.__class__, instance=self)
    )
allocate.alters_data = True
0reactions
sasha0commented, Apr 27, 2017

Fixed in #2281.

Read more comments on GitHub >

github_iconTop Results From Across the Web

6.3. Race Conditions and Critical Sections
A race condition is the general term for a bug that arises from the nondeterministic timing of execution. If the threads are scheduled...
Read more >
Concurrency and Race Conditions - O'Reilly
This sequence of events is a demonstration of a race condition. Race conditions are a result of uncontrolled access to shared data. When...
Read more >
Where's the Race Condition? - by Jim Cownie - CPU fun
The race condition that we are trying to avoid occurs when the owner thread is incrementing the base to claim the final iteration,...
Read more >
Race condition - Wikipedia
Race conditions can occur especially in logic circuits, multithreaded, or distributed software programs.
Read more >
What is a Race Condition? - TechTarget
A race condition is an undesirable situation that occurs when a device or system attempts to perform two or more operations at the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found