fix(bot): 동시성 중복 INSERT 시 ER_DUP_ENTRY 에러 무시 처리
YouTube/X 봇의 영상 저장 트랜잭션에서 UNIQUE 제약 위반 발생 시 크래시 대신 null을 반환하여 gracefully 무시하도록 변경. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3feb23f67f
commit
f8acb5450f
2 changed files with 59 additions and 46 deletions
|
|
@ -103,22 +103,28 @@ async function xBotPlugin(fastify, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 트랜잭션으로 INSERT 작업 수행
|
// 트랜잭션으로 INSERT 작업 수행
|
||||||
return withTransaction(fastify.db, async (connection) => {
|
try {
|
||||||
// schedules 테이블에 저장
|
return await withTransaction(fastify.db, async (connection) => {
|
||||||
const [result] = await connection.query(
|
// schedules 테이블에 저장
|
||||||
'INSERT INTO schedules (category_id, title, date, time) VALUES (?, ?, ?, ?)',
|
const [result] = await connection.query(
|
||||||
[YOUTUBE_CATEGORY_ID, video.title, video.date, video.time]
|
'INSERT INTO schedules (category_id, title, date, time) VALUES (?, ?, ?, ?)',
|
||||||
);
|
[YOUTUBE_CATEGORY_ID, video.title, video.date, video.time]
|
||||||
const scheduleId = result.insertId;
|
);
|
||||||
|
const scheduleId = result.insertId;
|
||||||
|
|
||||||
// schedule_youtube 테이블에 저장
|
// schedule_youtube 테이블에 저장
|
||||||
await connection.query(
|
await connection.query(
|
||||||
'INSERT INTO schedule_youtube (schedule_id, video_id, video_type, channel_id, channel_name) VALUES (?, ?, ?, ?, ?)',
|
'INSERT INTO schedule_youtube (schedule_id, video_id, video_type, channel_id, channel_name) VALUES (?, ?, ?, ?, ?)',
|
||||||
[scheduleId, video.videoId, video.videoType, video.channelId, video.channelTitle]
|
[scheduleId, video.videoId, video.videoType, video.channelId, video.channelTitle]
|
||||||
);
|
);
|
||||||
|
|
||||||
return scheduleId;
|
return scheduleId;
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// UNIQUE 제약 위반 (동시성 중복) → 무시
|
||||||
|
if (err.code === 'ER_DUP_ENTRY') return null;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -276,42 +276,49 @@ async function youtubeBotPlugin(fastify) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 트랜잭션으로 INSERT 작업 수행
|
// 트랜잭션으로 INSERT 작업 수행
|
||||||
const scheduleId = await withTransaction(fastify.db, async (connection) => {
|
let scheduleId;
|
||||||
// schedules 테이블에 저장
|
try {
|
||||||
const [result] = await connection.query(
|
scheduleId = await withTransaction(fastify.db, async (connection) => {
|
||||||
'INSERT INTO schedules (category_id, title, date, time) VALUES (?, ?, ?, ?)',
|
// schedules 테이블에 저장
|
||||||
[YOUTUBE_CATEGORY_ID, video.title, video.date, video.time]
|
const [result] = await connection.query(
|
||||||
);
|
'INSERT INTO schedules (category_id, title, date, time) VALUES (?, ?, ?, ?)',
|
||||||
const newScheduleId = result.insertId;
|
[YOUTUBE_CATEGORY_ID, video.title, video.date, video.time]
|
||||||
|
);
|
||||||
|
const newScheduleId = result.insertId;
|
||||||
|
|
||||||
// schedule_youtube 테이블에 저장
|
// schedule_youtube 테이블에 저장
|
||||||
await connection.query(
|
await connection.query(
|
||||||
'INSERT INTO schedule_youtube (schedule_id, video_id, video_type, channel_id, channel_name) VALUES (?, ?, ?, ?, ?)',
|
'INSERT INTO schedule_youtube (schedule_id, video_id, video_type, channel_id, channel_name) VALUES (?, ?, ?, ?, ?)',
|
||||||
[newScheduleId, video.videoId, video.videoType, video.channelId, bot.channelName]
|
[newScheduleId, video.videoId, video.videoType, video.channelId, bot.channelName]
|
||||||
);
|
);
|
||||||
|
|
||||||
// 멤버 연결 (커스텀 설정)
|
// 멤버 연결 (커스텀 설정)
|
||||||
const hasDefaultMembers = bot.defaultMemberIds && bot.defaultMemberIds.length > 0;
|
const hasDefaultMembers = bot.defaultMemberIds && bot.defaultMemberIds.length > 0;
|
||||||
if (hasDefaultMembers || bot.extractMembersFromDesc) {
|
if (hasDefaultMembers || bot.extractMembersFromDesc) {
|
||||||
const memberIds = [];
|
const memberIds = [];
|
||||||
if (hasDefaultMembers) {
|
if (hasDefaultMembers) {
|
||||||
memberIds.push(...bot.defaultMemberIds);
|
memberIds.push(...bot.defaultMemberIds);
|
||||||
|
}
|
||||||
|
if (nameMap) {
|
||||||
|
memberIds.push(...extractMemberIds(video.description, nameMap));
|
||||||
|
}
|
||||||
|
if (memberIds.length > 0) {
|
||||||
|
const uniqueIds = [...new Set(memberIds)];
|
||||||
|
const values = uniqueIds.map(id => [newScheduleId, id]);
|
||||||
|
await connection.query(
|
||||||
|
'INSERT INTO schedule_members (schedule_id, member_id) VALUES ?',
|
||||||
|
[values]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (nameMap) {
|
|
||||||
memberIds.push(...extractMemberIds(video.description, nameMap));
|
|
||||||
}
|
|
||||||
if (memberIds.length > 0) {
|
|
||||||
const uniqueIds = [...new Set(memberIds)];
|
|
||||||
const values = uniqueIds.map(id => [newScheduleId, id]);
|
|
||||||
await connection.query(
|
|
||||||
'INSERT INTO schedule_members (schedule_id, member_id) VALUES ?',
|
|
||||||
[values]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newScheduleId;
|
return newScheduleId;
|
||||||
});
|
});
|
||||||
|
} catch (err) {
|
||||||
|
// UNIQUE 제약 위반 (동시성 중복) → 무시
|
||||||
|
if (err.code === 'ER_DUP_ENTRY') return null;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
// 새 영상 추가 후 다음 주 예정 일정 생성 (쇼츠 제외)
|
// 새 영상 추가 후 다음 주 예정 일정 생성 (쇼츠 제외)
|
||||||
if (autoScheduleNext && isVideoType && scheduleId) {
|
if (autoScheduleNext && isVideoType && scheduleId) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue