2012-06-08 27 views
10

Tôi đang làm việc trong một ứng dụng web Django cần truy vấn cơ sở dữ liệu PostgreSQL. Khi triển khai đồng thời sử dụng giao diện Python threading, tôi nhận được lỗi DoesNotExist cho các mục được truy vấn. Tất nhiên, những lỗi này không xảy ra khi thực hiện truy vấn tuần tự.Lỗi cơ sở dữ liệu ở Django khi sử dụng luồng

Hãy để tôi thể hiện một thử nghiệm đơn vị mà tôi đã viết để chứng minh hành vi bất ngờ:

class ThreadingTest(TestCase): 
    fixtures = ['demo_city',] 

    def test_sequential_requests(self): 
     """ 
     A very simple request to database, made sequentially. 

     A fixture for the cities has been loaded above. It is supposed to be 
     six cities in the testing database now. We will made a request for 
     each one of the cities sequentially. 
     """ 
     for number in range(1, 7): 
      c = City.objects.get(pk=number) 
      self.assertEqual(c.pk, number) 

    def test_threaded_requests(self): 
     """ 
     Now, to test the threaded behavior, we will spawn a thread for 
     retrieving each city from the database. 
     """ 

     threads = [] 
     cities = [] 

     def do_requests(number): 
      cities.append(City.objects.get(pk=number)) 

     [threads.append(threading.Thread(target=do_requests, args=(n,))) for n in range(1, 7)] 

     [t.start() for t in threads] 
     [t.join() for t in threads] 

     self.assertNotEqual(cities, []) 

Như bạn thấy, thử nghiệm đầu tiên thực hiện một số yêu cầu cơ sở dữ liệu liên tục, được thực sự làm việc với không có vấn đề. Tuy nhiên, thử nghiệm thứ hai thực hiện chính xác các yêu cầu tương tự nhưng mỗi yêu cầu được sinh ra trong một luồng. Điều này thực sự không thành công, trả về một ngoại lệ DoesNotExist.

Kết quả của việc thực hiện các đơn vị kiểm tra này là như thế này:

test_sequential_requests (cesta.core.tests.threadbase.ThreadingTest) ... ok 
test_threaded_requests (cesta.core.tests.threadbase.ThreadingTest) ... 

Exception in thread Thread-1: 
Traceback (most recent call last): 
    File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner 
    self.run() 
    File "/usr/lib/python2.6/threading.py", line 484, in run 
    self.__target(*self.__args, **self.__kwargs) 
    File "/home/jose/Work/cesta/trunk/src/cesta/core/tests/threadbase.py", line 45, in do_requests 
    cities.append(City.objects.get(pk=number)) 
    File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/manager.py", line 132, in get 
    return self.get_query_set().get(*args, **kwargs) 
    File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/query.py", line 349, in get 
    % self.model._meta.object_name) 
DoesNotExist: City matching query does not exist. 

... đề khác trả về một kết quả tương tự ...

Exception in thread Thread-6: 
Traceback (most recent call last): 
    File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner 
    self.run() 
    File "/usr/lib/python2.6/threading.py", line 484, in run 
    self.__target(*self.__args, **self.__kwargs) 
    File "/home/jose/Work/cesta/trunk/src/cesta/core/tests/threadbase.py", line 45, in do_requests 
    cities.append(City.objects.get(pk=number)) 
    File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/manager.py", line 132, in get 
    return self.get_query_set().get(*args, **kwargs) 
    File "/home/jose/Work/cesta/trunk/parts/django/django/db/models/query.py", line 349, in get 
    % self.model._meta.object_name) 
DoesNotExist: City matching query does not exist. 


FAIL 

====================================================================== 
FAIL: test_threaded_requests (cesta.core.tests.threadbase.ThreadingTest) 
---------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/home/jose/Work/cesta/trunk/src/cesta/core/tests/threadbase.py", line 52, in test_threaded_requests 
    self.assertNotEqual(cities, []) 
AssertionError: [] == [] 

---------------------------------------------------------------------- 
Ran 2 tests in 0.278s 

FAILED (failures=1) 
Destroying test database for alias 'default' ('test_cesta')... 

Hãy nhớ rằng tất cả điều này là xảy ra trong cơ sở dữ liệu PostgreSQL, được cho là luồng an toàn, không phải với SQLite hoặc similars. Kiểm tra đã được chạy bằng cách sử dụng PostgreSQL cũng.

Tại thời điểm này, tôi hoàn toàn bị mất về những gì có thể không thành công. Bất kỳ ý tưởng hoặc đề nghị?

Cảm ơn!

EDIT: Tôi đã viết một chế độ xem nhỏ chỉ để kiểm tra xem nó có hoạt động không. Đây là mã của quan điểm:.

def get_cities(request): 
    queue = Queue.Queue() 

    def get_async_cities(q, n): 
     city = City.objects.get(pk=n) 
     q.put(city) 

    threads = [threading.Thread(target=get_async_cities, args=(queue, number)) for number in range(1, 5)] 

    [t.start() for t in threads] 
    [t.join() for t in threads] 

    cities = list() 

    while not queue.empty(): 
     cities.append(queue.get()) 

    return render_to_response('async/cities.html', {'cities': cities}, 
     context_instance=RequestContext(request)) 

(Xin vui lòng, không đưa vào tài khoản các sự điên rồ của văn bản cho logic ứng dụng bên trong mã xem Hãy nhớ rằng đây chỉ là một bằng chứng của khái niệm và sẽ không bao giờ trong ứng dụng thực tế)

Kết quả là mã đang hoạt động tốt, các yêu cầu được thực hiện thành công trong chuỗi và cuối cùng hiển thị các thành phố sau khi gọi URL của nó.

Vì vậy, tôi nghĩ việc tạo truy vấn bằng cách sử dụng chuỗi sẽ chỉ là vấn đề khi bạn cần kiểm tra mã. Trong sản xuất, nó sẽ làm việc mà không có bất kỳ vấn đề.

Bất kỳ đề xuất hữu ích nào để kiểm tra loại mã này thành công?

+0

Bạn có chắc chắn là vật cố được nhập không? Bạn có thể dán nhật ký ghi: "Lịch thi đấu demo_city được xử lý" hay thứ gì đó như thế ...... Ah, nevermind .. chưa đọc hết câu hỏi .. – Tisho

Trả lời

2

Điều này có vẻ như đây là vấn đề với giao dịch. Nếu bạn đang tạo các phần tử trong yêu cầu hiện tại (hoặc kiểm tra), chúng hầu như chắc chắn trong một giao dịch không được cam kết không thể truy cập được từ kết nối riêng biệt trong luồng khác. Bạn có thể cần phải manage your transctions manually để có được điều này để làm việc.

+0

Thực ra, các truy vấn không sửa đổi bất kỳ dữ liệu nào, nhưng chỉ lấy nó từ cơ sở dữ liệu. Vì vậy, tôi không nhận được điểm trong chú thích của bạn. Dù sao, tôi đã thay đổi yêu cầu trong hàm 'do_requests' bằng truy vấn SQL thô và kết quả giống nhau. –

+0

https://docs.djangoproject.com/en/1.8/topics/db/transactions/ Liên kết trong câu trả lời không hoạt động nữa bởi vì nó dành cho v1.4. – Heliodor

10

Hãy thử sử dụng TransactionTestCase:

class ThreadingTest(TransactionTestCase): 

TestCase giữ dữ liệu trong bộ nhớ và không đưa ra một cam kết cơ sở dữ liệu. Có lẽ các chủ đề đang cố gắng kết nối trực tiếp với DB, trong khi dữ liệu chưa được cam kết ở đó.Seedescription đây: https://docs.djangoproject.com/en/dev/topics/testing/?from=olddocs#django.test.TransactionTestCase

TransactionTestCase và TestCase là giống hệt nhau ngoại trừ cách trong đó cơ sở dữ liệu được thiết lập lại để một nhà nước biết và khả năng cho mã kiểm tra để kiểm tra tác động của cam kết và rollback. A TransactionTestCase đặt lại cơ sở dữ liệu trước khi thử nghiệm chạy bằng cách cắt ngắn tất cả các bảng và tải lại dữ liệu ban đầu. A TransactionTestCase có thể gọi cam kết và rollback và quan sát các hiệu ứng của các cuộc gọi này trên cơ sở dữ liệu.

+0

đây là cách tiếp cận –

0

trở thành rõ ràng hơn từ phần này của tài liệu

class LiveServerTestCase(TransactionTestCase): 
    """ 
    ... 
    Note that it inherits from TransactionTestCase instead of TestCase because 
    the threads do not share the same transactions (unless if using in-memory 
    sqlite) and each thread needs to commit all their transactions so that the 
    other thread can see the changes. 
    """ 

Bây giờ, giao dịch chưa được cam kết bên trong một TestCase, vì thế mà thay đổi này là không hiển thị cho các chủ đề khác.

Các vấn đề liên quan