카테고리 없음
[SocketIO] MongoDB Change Stream 중복 이벤트 문제 해결 과정
걍작
2025. 6. 24. 15:19
1. 문제 상황
- MongoDB Change Stream을 사용해서 devices 컬렉션의 변경을 감지하고,
관리자(admin)에게 실시간으로 socketlist 변경을 알리고 싶었음. - 하지만, DB에 변경이 일어날 때마다 Change Stream 이벤트 로그가 여러 번(2번 이상) 출력되어 혼란이 있었음.
2. 원인 분석
- 서버 단독 실행으로 changeStream 등록이 이중으로 되었거나, 서버 실행이 중복되는 등의 문제가 아님을 확인.
- 이벤트 등록 로그와 이벤트 실행 로그를 통해 saveSocketListOrder() 함수에서 newOrder 배열의 각 _id에 대해 updateOne을 반복 실행이 이루어진다는 것을 발견.
- MongoDB Change Stream은 실제로 값이 바뀌는 document마다 이벤트를 발생시킴(6개의 리스트여도 2개만 변경되는 겨우 2번 실행 >> 이것 때문에 테스틑 2개만 나와서 예상을 못하고 방황함).
- 즉, 여러 개의 document가 실제로 변경되면 그 수만큼 이벤트(로그)가 발생하는 것이 정상임.
RUN() 함수{
... 중략 ...
db = atlas_client.db(dbname); // db에 연결
console.log(`Atlas MongoDB connected successfully!`);
//changeStream을 사용하여 devices 컬렉션의 변경 사항을 감지하고, 관리자 네임스페이스에 이벤트를 전송
console.log('Change Stream 생성!');
const changeStream = db.collection('devices').watch();
changeStream.on('change', () => {
console.log('Change Stream 이벤트 발생!');
adminNamespace.emit('socketlist-changed');
console.log(`>> server(${process.env.PORT}) : devices collection changed, notifying admin namespace`);
});
... 중략 ...
}
>>CONSOLE LOG
Atlas MongoDB connected successfully!
MongoDB Adapter connected successfully!
process.env.PORT: undefined
HTTP Server mode is running on port 3000
Admin socket connected: lR4gWDCKDSycoGfeAAAB Account ID: your-account-id
admin socket event: save-socket-order
C:\Users\user\Dropbox\github\websocket-server /saveSocketListOrder()>>
New order: [
'684943ff060b6e0685cbc1ce',
'683d48a69fbbc5f2c640f58b',
'684a8aa7a14c86aaa07c9127',
'683d50b97073568ada79662b',
'683d4bef94154f1d636e80d6',
'683d50df10e27ec77720480a'
]
Change Stream 이벤트 발생!
>> server(undefined) : devices collection changed, notifying admin namespace
Change Stream 이벤트 발생!
>> server(undefined) : devices collection changed, notifying admin namespace
3. 해결 흐름
- Change Stream이 중복 생성된 것이 아니라,
여러 document가 실제로 변경될 때마다 이벤트가 발생하는 것이 원인임을 확인. - 실제로 값이 바뀌는 document 수만큼 로그가 찍히는 것이 정상 동작임을 이해함.
4. 최종 해결 방법
- debounce/throttle (지연/합치기) 기법을 사용해 짧은 시간 동안 여러 번 발생하는 이벤트를 모아서 한 번만 처리.( db 에서 실제 작동은 동일하게 변경 건 갯수만큼 동작 )
//changeStream을 사용하여 devices 컬렉션의 변경 사항을 감지하고, 관리자 네임스페이스에 이벤트를 전송
console.log('Change Stream 생성!');
let changeStreamTimeout = null;
const changeStream = db.collection('devices').watch();
changeStream.on('change', () => {
if (changeStreamTimeout) clearTimeout(changeStreamTimeout);
changeStreamTimeout = setTimeout(() => {
adminNamespace.emit('socketlist-changed');
console.log(`>> server(${process.env.PORT}) : devices collection changed, notifying admin namespace`);
}, 300); // 0.3초 동안 이벤트가 또 오면 마지막에 한 번만 실행
});