1. 問題情況:on_commit() 會不會執行得太快?
最近在 Django 專案中使用 transaction.on_commit()
設定
讓 Celery 任務在交易成功後執行。
但是,查看日誌時,發現了一個奇怪的現象。
即使尚未完成像
post.categories.add(...)
的操作,
在on_commit()
裡的 Celery 任務卻已經執行了。
2. 查看代碼…
def view():
post = Post.objects.create(...)
def post_process():
with transaction.atomic():
post.categories.add(...)
post.tags.add(...)
def run_async():
translate_post.delay(post.id)
transaction.on_commit(run_async)
threading.Thread(target=post_process).start()
起初看似沒問題…
問題在於這個 threading.Thread()
的使用。
3. 原因:交易是執行緒本地的
Django 的交易依賴於當前執行緒。
也就是說, transaction.atomic()
和 on_commit()
所應用的僅限於當前執行緒打開的交易。
在新的執行緒中打開的
transaction.atomic()
,
與主交易毫無關聯。
4. 最終發生了什麼
- Celery 任務雖然在“交易提交之後”執行,但
- 這個交易只是在子執行緒中的小交易,
post.categories.add(...)
等操作仍在進行中
換句話說,Celery 的執行比預期要快,
因此產生了參考不完整數據的問題。
5. 解決方法
只需這樣修改即可:
def view():
post = Post.objects.create(...)
with transaction.atomic():
post.categories.add(...)
post.tags.add(...)
def run_async():
translate_post.delay(post.id)
transaction.on_commit(run_async)
🚫 threading.Thread(...)
✅ 在主執行緒中註冊 on_commit()
6. 總結
- Django 的交易是 執行緒本地的
on_commit()
也僅在當前執行緒的交易提交之後執行- 在其他執行緒中註冊,僅響應該執行緒的交易
這次經驗讓我獲得了強有力的教訓。
在使用交易 + Celery + 執行緒時,
我深刻體會到必須小心。
Add a New Comment