feat: base 优化safe_get_or_create2
This commit is contained in:
parent
53a56ace1f
commit
7e09b872cf
|
|
@ -8,6 +8,7 @@ from django.db import IntegrityError
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from rest_framework.exceptions import ParseError
|
from rest_framework.exceptions import ParseError
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
from django.db import transaction, connection
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
# 自定义软删除查询基类
|
# 自定义软删除查询基类
|
||||||
|
|
@ -115,25 +116,39 @@ class BaseModel(models.Model):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def safe_get_or_create(cls, defaults=None, **kwargs):
|
def safe_get_or_create(cls, defaults=None, **kwargs):
|
||||||
|
"""
|
||||||
|
多进程/多服务器安全的 get_or_create
|
||||||
|
- 数据库唯一约束不够时,用 Redis 锁防止重复创建
|
||||||
|
- 在事务中使用 select_for_update
|
||||||
|
"""
|
||||||
defaults = defaults or {}
|
defaults = defaults or {}
|
||||||
|
create_kwargs = {**kwargs, **defaults}
|
||||||
|
|
||||||
for i in range(3):
|
for attempt in range(3):
|
||||||
try:
|
try:
|
||||||
obj = cls.objects.create(**kwargs, **defaults)
|
if connection.in_atomic_block:
|
||||||
return obj, True
|
# 在事务中,先锁定再获取
|
||||||
except IntegrityError:
|
|
||||||
try:
|
try:
|
||||||
# 有别的进程抢先创建了,直接 get
|
obj = cls.objects.select_for_update().get(**kwargs)
|
||||||
obj = cls.objects.get(**kwargs)
|
|
||||||
return obj, False
|
return obj, False
|
||||||
except cls.DoesNotExist:
|
except cls.DoesNotExist:
|
||||||
# 极端并发情况下 create 和 get 都失败,稍微等一下重试
|
obj = cls(**create_kwargs)
|
||||||
time.sleep(0.001 * (i + 1)) # 微等一下避免竞争
|
obj.save()
|
||||||
continue
|
return obj, True
|
||||||
|
else:
|
||||||
|
# 非事务,使用分布式锁
|
||||||
|
sorted_kwargs = dict(sorted(create_kwargs.items()))
|
||||||
|
lock_hash = hashlib.md5(str(sorted_kwargs).encode()).hexdigest()
|
||||||
|
lock_key = f"safe_get_or_create:{cls.__name__}:{lock_hash}"
|
||||||
|
|
||||||
# 最终重试失败(极罕见)
|
with cache.lock(lock_key, timeout=10):
|
||||||
obj = cls.objects.get(**kwargs)
|
return cls.objects.get_or_create(**kwargs, defaults=defaults)
|
||||||
return obj, False
|
|
||||||
|
except IntegrityError:
|
||||||
|
# 唯一约束冲突,重试
|
||||||
|
if attempt == 2:
|
||||||
|
raise
|
||||||
|
time.sleep(0.1 * (attempt + 1))
|
||||||
|
|
||||||
|
|
||||||
def handle_parent(self):
|
def handle_parent(self):
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue