2010-09-10 42 views
3

我面臨着一個非常奇怪的問題,即使我刪除了一些行,我也可以在同一個事務中再次取回它們。我在apache和mod_wsgi下運行這個,數據庫是mysql。django刪除行存在?

編輯: 我創建了一個示例應用程序來測試它,以便我可以確定我的代碼都不是罪魁禍首。

我創建了一個與testapp以下代碼

models.py

import uuid 
from django.db import models 

class TestTable(models.Model): 
    id = models.CharField(max_length=36, primary_key=True) 
    name = models.CharField(max_length=50) 

    @classmethod 
    def get_row(cls, name): 
     return TestTable(id=str(uuid.uuid4()), name=name) 

    def __unicode__(self): 
     return u"%s[%s]"%(self.name, self.id) 

views.py

import traceback 
import time 
from django.db import transaction 
from django.http import HttpResponse 

from testapp.models import TestTable 

@transaction.commit_manually 
def test_view(request): 
    time.sleep(1) 
    out = [] 
    try: 
     # delete 3 rows 
     for row in TestTable.objects.all()[:3]: 
      ID=row.id 
      out.append("deleting %s"%row) 
      row.delete() 
      # check fi really deleted 
      try: 
       TestTable.objects.get(id=ID) 
       out.append("row not deleted?") 
      except TestTable.DoesNotExist,e: 
       out.append("row deleted.") 

     # create 5 rows 
     for i in range(5): 
      row = TestTable.get_row("row %s"%i) 
      row.save() 

    except Exception,e: 
     out.append("Error:%s"%traceback.format_exc()) 
     transaction.rollback() 
    else: 
     transaction.commit() 

    return HttpResponse('\n'.join(out), 'text/text') 

urls.py

from django.conf.urls.defaults import * 

urlpatterns = patterns('testapp.views', (r'^test_bug$', 'test_view') 

TestScript

import urllib2 
from multiprocessing import Process 

def get_data(): 
    r = urllib2.urlopen("http://localhost:81/timeapp/test/test_bug") 
    print "---------" 
    print r.read() 

if __name__ == "__main__": 
    for i in range(2): 
     p = Process(target=get_data) 
     p.start() 

輸出:

$ python test.py 
--------- 
deleting row 1[3ad3a82e-830f-4540-8148-88479175ed5e] 
row deleted. 
deleting row 0[544462d1-8588-4a8c-a809-16a060054479] 
row deleted. 
deleting row 3[55d422f3-6c39-4c26-943a-1b4db498bf25] 
row deleted. 
--------- 
deleting row 1[3ad3a82e-830f-4540-8148-88479175ed5e] 
row not deleted? 
deleting row 0[544462d1-8588-4a8c-a809-16a060054479] 
row not deleted? 
deleting row 3[55d422f3-6c39-4c26-943a-1b4db498bf25] 
row not deleted? 

所以我的問題是怎麼來刪除的行是TestTable.objects.get再次檢索,也就算我多睡覺在第二次調用,以便第一次調用可以提交代碼我仍然會在第二次調用中刪除行。

+0

我已經運行你的代碼,無法重現錯誤。我用MySQL(使用InnoDB引擎)和PostgreSQL嘗試了它,並且行總是被刪除。有可能你有一個錯誤的庫版本。我一直使用的版本是:'Django == 1.2.3'和'MySQL-python == 1.2.3'以及'Python 2.6'和'MySQL 14.14(5.5.2-m2)'。您也可以嘗試檢查問題是否也發生在PostgreSQL上。 – 2010-09-17 08:53:54

+0

@Aram Dulyan,感謝您檢查了這一點,我希望你在apache上嘗試過,而不是django devserver,我使用mysql和innodb,並且我始終如一地獲取這個「行未刪除?」。 – 2010-09-17 11:29:01

+0

既然你正在一個事務中工作,那麼刪除只會在你實際提交後纔會發生? – 2010-09-20 21:12:18

回答

3

你的問題讓我着迷,所以我花了不少時間研究它,唯一的結論,我可以拿出的是,它在任何蟒蛇,MySQL或MySQL的本身就是一個善意的錯誤。下面是我的嘗試:

我要指出,我改變了代碼稍微使代替:

try: 
    TestTable.objects.get(id=ID) 
    out.append("row not deleted?") 
except TestTable.DoesNotExist,e: 
    out.append("row deleted.") 

我:

c = TestTable.objects.filter(id=ID).count() 
if c: 
    out.append("row not deleted?") 
else: 
    out.append("row deleted.") 

這使得它稍微容易調試,並沒有不影響問題的表現。

首先,Django的緩存不應該怪在這裏。查詢以獲得數發行,因爲可以在MySQL日誌中可以看出(1和2表示得到由兩個獨立的併發連接):在獲取的

1 Query SET NAMES utf8 
2 Query SET NAMES utf8 
2 Query set autocommit=0 
1 Query set autocommit=0 
1 Query SELECT `testapp_testtable`.`id`, `testapp_testtable`.`name` FROM `testapp_testtable` LIMIT 3 
2 Query SELECT `testapp_testtable`.`id`, `testapp_testtable`.`name` FROM `testapp_testtable` LIMIT 3 
2 Query DELETE FROM `testapp_testtable` WHERE `id` IN ('32f027ff-c798-410b-8621-c2d47e2cfa7c') 
1 Query DELETE FROM `testapp_testtable` WHERE `id` IN ('32f027ff-c798-410b-8621-c2d47e2cfa7c') 
2 Query SELECT COUNT(*) FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '32f027ff-c798-410b-8621-c2d47e2cfa7c' 
2 Query DELETE FROM `testapp_testtable` WHERE `id` IN ('3f693297-9993-4162-98c4-a9ca68232c75') 
2 Query SELECT COUNT(*) FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '3f693297-9993-4162-98c4-a9ca68232c75' 
2 Query DELETE FROM `testapp_testtable` WHERE `id` IN ('96f9a1f7-c818-4528-858f-4e85a93de5c3') 
2 Query SELECT COUNT(*) FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '96f9a1f7-c818-4528-858f-4e85a93de5c3' 
2 Query SELECT (1) AS `a` FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '035c90ba-82a6-4bdc-afe1-318382563017' LIMIT 1 
2 Query INSERT INTO `testapp_testtable` (`id`, `name`) VALUES ('035c90ba-82a6-4bdc-afe1-318382563017', 'row 0') 
2 Query SELECT (1) AS `a` FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '15393978-4200-4b98-98e6-73636c39dd1c' LIMIT 1 
2 Query INSERT INTO `testapp_testtable` (`id`, `name`) VALUES ('15393978-4200-4b98-98e6-73636c39dd1c', 'row 1') 
2 Query SELECT (1) AS `a` FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '22459ba2-18d5-4175-ac6b-2377ba63ecc7' LIMIT 1 
2 Query INSERT INTO `testapp_testtable` (`id`, `name`) VALUES ('22459ba2-18d5-4175-ac6b-2377ba63ecc7', 'row 2') 
2 Query commit 
2 Quit 
1 Query SELECT COUNT(*) FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '32f027ff-c798-410b-8621-c2d47e2cfa7c' 
1 Query DELETE FROM `testapp_testtable` WHERE `id` IN ('3f693297-9993-4162-98c4-a9ca68232c75') 
1 Query SELECT COUNT(*) FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '3f693297-9993-4162-98c4-a9ca68232c75' 
1 Query DELETE FROM `testapp_testtable` WHERE `id` IN ('96f9a1f7-c818-4528-858f-4e85a93de5c3') 
1 Query SELECT COUNT(*) FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '96f9a1f7-c818-4528-858f-4e85a93de5c3' 
1 Query SELECT (1) AS `a` FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '6dc6e901-bebe-4f3b-98d1-c8c4a90d06df' LIMIT 1 
1 Query INSERT INTO `testapp_testtable` (`id`, `name`) VALUES ('6dc6e901-bebe-4f3b-98d1-c8c4a90d06df', 'row 0') 
1 Query SELECT (1) AS `a` FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = 'c335ccad-31c6-4ddd-bccd-578435cd6e7b' LIMIT 1 
1 Query INSERT INTO `testapp_testtable` (`id`, `name`) VALUES ('c335ccad-31c6-4ddd-bccd-578435cd6e7b', 'row 1') 
1 Query SELECT (1) AS `a` FROM `testapp_testtable` WHERE `testapp_testtable`.`id` = '2c507629-a87e-48ec-b80d-2f758cd16c44' LIMIT 1 
1 Query INSERT INTO `testapp_testtable` (`id`, `name`) VALUES ('2c507629-a87e-48ec-b80d-2f758cd16c44', 'row 2') 
1 Query commit 
1 Quit 

和當然,任何後續嘗試在會話關閉後計數顯示該行實際上已被刪除。此外,在django.db.models.sql.query中記錄收到的SQL結果顯示緊接在DELETE語句之後的SELECT COUNT語句事實上在上述日誌的後半部分中返回1,而不是預期的0。我對此沒有任何解釋。

據我所知,只有兩個選項可用於獲得您想要的功能。我已經驗證他們兩人的工作:

  • 在Apache的conf,設置MaxClientsThreadsPerChild爲1(不是一個非常實用的選擇)。
  • 使用PostgreSQL(我會推薦給任何使用MySQL的人)。
+0

之後獲得相同的行,並且僅在事務中發生,可能是mysql事務的一些錯誤,你在哪裏看到查詢返回的結果,在你的日誌中我只看到查詢 – 2010-09-22 16:13:14

+0

爲了得到結果,我在'django.db.models.sql.query.Query中添加了一行。get_aggregation()'在'result = query.get_compiler(using).execute_sql(SINGLE)'這一行之後,它會將'result'變量記錄到一個文件中。該文件最終看起來像'000111'。 – 2010-09-22 23:30:08

0

我懷疑你的問題是你認爲的問題。請注意,第二次沒有打印異常。

問題是,您正在捕獲所有異常,而不是您處理的'TimeCardDetail.DoesNotExist'異常。這在發生意外事件時掩蓋了真正的問題。用特定的例外替換catch-all'Exception',然後看看會發生什麼。

+0

在我的真實代碼中,我沒有捕獲異常,無論如何它打印「刪除的行存在?這意味着在創建已刪除的行時不會出現異常,只是爲了確保我嘗試更改爲TimeCardDetail.DoesNotExist,但行爲仍然相同,如何在r.delete – 2010-09-10 07:53:23

0

您可能正在處理對緩存對象的查找。另外,如果你正在處理緩存對象,它們可能只會顯示你的apache設置,因爲請求是由兩個不同的進程並行處理的。嘗試將apache工作進程的數量減少到1,並查看該行爲是否與在dev服務器中運行時相同(./manage.py runserver)。

您還可以嘗試添加一些時間戳和正在使用的SQL轉儲。在settings.py中設置DEBUG = True,然後你can look at your raw sql queries

# views.py 
from django.db import connection 

def test_view(request): 
    connection.queries = [] 
    start_time = time.time() 
    out = [] 
    out.append("%09.6f" % (time.time() % 100)) # something like 13.45678 
    time.sleep(1) 
    [...] 
     # delete 3 rows 
      [...] 
      out.append("deleting %s"%row) 
      out.append("%09.6f" % (time.time() % 100)) 
      [...] 
      out.append("%d queries after the last delete" %d len(connection.queries)) 
     # create 5 rows 
    [...] 
    out.append("%09.6f total time spent" % (time.time() - start_time)) 
    out.append("%d queries TOTAL" %d len(connection.queries)) 
    # dump the actual queries if you are still digging. 
    for q in connection.queries: 
     out.append("%s\n----" % q) 
1

它看起來好像你有一個在djangoproject.com報道的this ticket變體。

+0

是的看起來相似,但它說固定,我仍然面臨問題 – 2010-09-24 09:00:10