mercredi 24 avril 2019

What Is the Best Way to Test Racing Condition of Django Model Data Operation?

I've implemented an API based on Django and Django REST framework and found it had some problems when it's work under great concurrency.

So I was interested in write some Testing code to reproducing the problem, and improve the code to get it to thread safe later.

I've tried the library before_after, and it's quite hard to use if the busy part of the code is not a function or is a function but contains arguments.

I've also tried using ThreadPoolExecutor to generate a racing condition, but it raises django.db.utils.DatabaseError: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 4328317000 and this is thread id 4690485864.

My implementation is like this:

class TestOrderViewSet(APILiveServerTestCase, AuthRequestMixin):

    def test_replace_order_thread_safety(self):
        john_balance = self.john.wallet.balance
        jane_balance = self.jane.wallet.balance
        order_user_pairs = [(mommy.make_recipe('payments.tests.jane_order'), self.jane)]
        for i in range(10):
            order_user_pairs.append((mommy.make_recipe('payments.tests.jane_order'), self.jane))
            order_user_pairs.append((mommy.make_recipe('payments.tests.john_order'), self.john))
        random.shuffle(order_user_pairs)
        print(order_user_pairs)
        self.assertGreaterEqual(Order.objects.count(), 20)

        def replace_order(order, user, i):
            print(i, order.id)
            response = self.auth_post('order', {'pk': order.id}, user)
            print(i, user.wallet.balance)
            self.assertEqual(response.status_code, 200)
            user.wallet.refresh_from_db()
            print(i, user.wallet.balance)

        def count_done():
            return sum(int(r.done()) for r in results)

        with ThreadPoolExecutor(max_workers=4) as e:
            results = []
            for i in range(10):
                r = e.submit(replace_order, *order_user_pairs[i], i)
                results.append(r)
                r = e.submit(replace_order, *order_user_pairs[i], i)
                results.append(r)

            print('done', count_done())
            # e.shutdown(wait=True)
        sum(int(r.done()) for r in results)
        print('done', count_done())


Any kind of suggestions is appreciated. Thanks in advance.

Aucun commentaire:

Enregistrer un commentaire