forked from erp-dev/erp
fux: mdy_plate_order sync pk conflict
This commit is contained in:
@@ -4,6 +4,8 @@ from datetime import datetime
|
||||
import time
|
||||
from typing import Literal
|
||||
|
||||
from django.db import connection
|
||||
from django.db.utils import IntegrityError
|
||||
from django.utils import timezone
|
||||
|
||||
from api_v1 import models as api_models
|
||||
@@ -19,6 +21,42 @@ from flower.utils.mingdaoyun.relations import (
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_MDY_PLATE_ORDER_STAGING_PK_CONSTRAINT = "api_mdy_plate_order_staging_pkey"
|
||||
|
||||
|
||||
def _reset_mdy_plate_order_staging_id_sequence() -> None:
|
||||
"""修复 PostgreSQL 自增序列落后导致的主键重复(duplicate key violates ..._pkey)。
|
||||
|
||||
常见场景:数据库 restore/copy 后,sequence 未随数据同步到最新值。
|
||||
"""
|
||||
table_name = api_models.MDYPlateOrderStaging._meta.db_table # 'api_mdy_plate_order_staging'
|
||||
pk_column = api_models.MDYPlateOrderStaging._meta.pk.column # 'id'
|
||||
|
||||
with connection.cursor() as cursor:
|
||||
cursor.execute(f"SELECT COALESCE(MAX({pk_column}), 1) FROM {table_name}")
|
||||
max_id = cursor.fetchone()[0] or 1
|
||||
|
||||
cursor.execute("SELECT pg_get_serial_sequence(%s, %s)", [table_name, pk_column])
|
||||
seq_name = cursor.fetchone()[0]
|
||||
if not seq_name:
|
||||
logger.warning("未找到 %s.%s 的 serial sequence,跳过 setval", table_name, pk_column)
|
||||
return
|
||||
|
||||
# is_called=true => nextval 会返回 max_id+1
|
||||
cursor.execute("SELECT setval(%s, %s, true)", [seq_name, max_id])
|
||||
logger.warning(
|
||||
"已重置 sequence=%s 到 max(id)=%s(下次插入将从 %s 开始)",
|
||||
seq_name,
|
||||
max_id,
|
||||
max_id + 1,
|
||||
)
|
||||
|
||||
|
||||
def _is_pk_duplicate_error(exc: Exception) -> bool:
|
||||
"""判断是否为主键约束冲突(多见于 sequence 未对齐)。"""
|
||||
msg = str(exc) or ""
|
||||
return _MDY_PLATE_ORDER_STAGING_PK_CONSTRAINT in msg
|
||||
|
||||
|
||||
def _parse_mdy_datetime(value: str | None):
|
||||
if not value:
|
||||
@@ -196,15 +234,33 @@ def sync_mdy_plate_orders_to_staging(
|
||||
if not rowid:
|
||||
continue
|
||||
|
||||
api_models.MDYPlateOrderStaging.objects.update_or_create(
|
||||
mdy_rowid=rowid,
|
||||
defaults={
|
||||
"ctime": _parse_mdy_datetime(row.get("ctime")),
|
||||
"utime": _parse_mdy_datetime(row.get("utime")),
|
||||
"raw": row,
|
||||
"related": related_map.get(rowid, []),
|
||||
},
|
||||
)
|
||||
defaults = {
|
||||
"ctime": _parse_mdy_datetime(row.get("ctime")),
|
||||
"utime": _parse_mdy_datetime(row.get("utime")),
|
||||
"raw": row,
|
||||
"related": related_map.get(rowid, []),
|
||||
}
|
||||
|
||||
try:
|
||||
api_models.MDYPlateOrderStaging.objects.update_or_create(
|
||||
mdy_rowid=rowid,
|
||||
defaults=defaults,
|
||||
)
|
||||
except IntegrityError as exc:
|
||||
# 仅对“主键重复(通常为 sequence 未对齐)”做一次自愈重试。
|
||||
if _is_pk_duplicate_error(exc):
|
||||
logger.warning(
|
||||
"检测到暂存表主键冲突,尝试重置序列后重试一次:rowid=%s err=%s",
|
||||
rowid,
|
||||
exc,
|
||||
)
|
||||
_reset_mdy_plate_order_staging_id_sequence()
|
||||
api_models.MDYPlateOrderStaging.objects.update_or_create(
|
||||
mdy_rowid=rowid,
|
||||
defaults=defaults,
|
||||
)
|
||||
else:
|
||||
raise
|
||||
synced_rows += 1
|
||||
|
||||
record_ctime = _parse_mdy_datetime(row.get("ctime"))
|
||||
|
||||
Reference in New Issue
Block a user