我已经实现了一个基于Django和Django REST框架的API,发现在高度并发的情况下工作时会遇到一些问题。
因此,我有兴趣编写一些测试代码以重现该问题,并改进代码以使其稍后能够安全地线程化。
我已经尝试过before_after
库,如果代码的繁忙部分不是函数或者是函数但包含参数,则很难使用。
我也尝试过使用ThreadPoolExecutor
来生成赛车条件,但是会引发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.
我的实现是这样的:
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)
print('done', count_done())
self.john.wallet.refresh_from_db()
self.jane.wallet.refresh_from_db()
self.assertEqual(self.john.wallet.balance, john_balance + 1)
self.assertEqual(self.jane.wallet.balance, jane_balance - 1)
任何建议都值得赞赏。预先感谢。