-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
288 additions
and
279 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +0,0 @@ | ||
# - | ||
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,165 @@ | ||
const { flushAttendCache } = require('./attendCaches'); | ||
const Attend = require('../models/attend'); | ||
// 동시성 문제를 해결하기위해 한 번에 하나의 비동기 작업만 해당 자원에 접근 가능 | ||
// 작업이 끝나기 전까지 다른 작업이 실행되지 않도록 보장 | ||
const { Mutex } = require('async-mutex'); | ||
|
||
const tokenCache = { | ||
const TTL = 600000; // 10분 (600,000 밀리초) | ||
|
||
// 출석에 대한 토큰이자 캐시 객체(서버 당 하나의 객체만 필요하므로 객체로 선언) | ||
const AttendanceTokenCache = { | ||
sessionId: null, | ||
attendIdx: null, | ||
expireAt: null, | ||
attendIdx: null, | ||
code: null, | ||
}; | ||
const TTL = 600000; // 10분 (600,000 밀리초) | ||
cacheTimeout: null, | ||
attendCache: [], | ||
mutex: new Mutex(), | ||
|
||
// Token 삭제 함수 | ||
async function deleteToken(sessionId) { | ||
await flushAttendCache(sessionId); | ||
tokenCache.sessionId = null; | ||
tokenCache.attendIdx = null; | ||
tokenCache.expireAt = null; | ||
tokenCache.code = null; | ||
} | ||
|
||
// 토큰 생성 함수 | ||
function createToken(sessionId, attendIdx) { | ||
if (tokenCache.sessionId) { | ||
console.log(`이미 출석 체크 중`); | ||
return null; // 이미 존재하는 경우 false 반환 | ||
} | ||
// 캐시 리셋 함수 | ||
resetCache() { | ||
this.sessionId = null; | ||
this.expireAt = null; | ||
this.attendIdx = null; | ||
this.code = null; | ||
this.attendCache = []; | ||
|
||
const expireAt = Date.now() + TTL; | ||
const code = String(Math.floor(Math.random() * 10000)).padStart(4, '0'); | ||
tokenCache.sessionId = sessionId; | ||
tokenCache.attendIdx = attendIdx; | ||
tokenCache.expireAt = expireAt; | ||
tokenCache.code = code; | ||
|
||
// 시간 지나면 사라짐 | ||
setTimeout(async () => { | ||
if (tokenCache.sessionId == sessionId && tokenCache.expireAt <= Date.now()) { | ||
await deleteToken(sessionId); | ||
if (this.cacheTimeout) { | ||
clearTimeout(this.cacheTimeout); | ||
this.cacheTimeout = null; | ||
} | ||
}, TTL); | ||
}, | ||
|
||
return tokenCache; // 새로 설정된 경우 토큰 반환 | ||
} | ||
// 캐시 삭제 후 데이터 베이스에 전달 함수 | ||
async flushCache() { | ||
if (this.attendCache.length > 0) { | ||
const bulkOps = this.attendCache.map(attend => ({ | ||
updateOne: { | ||
filter: { user: attend.user, session: this.sessionId }, | ||
update: { $set: { attendList: attend.attendList } }, | ||
upsert: true | ||
} | ||
})); | ||
|
||
// 토큰 재시작 함수 | ||
function restartToken(sessionId) { | ||
if (tokenCache.sessionId !== sessionId) { | ||
console.log(`세션 ID가 일치하지 않습니다.`); | ||
return null; // 세션 ID가 일치하지 않는 경우 null 반환 | ||
} | ||
try { | ||
const result = await Attend.bulkWrite(bulkOps); | ||
console.log('데이터베이스에 출석 정보 저장 성공:', result); | ||
} catch (error) { | ||
console.error('데이터베이스에 출석 정보 저장 실패:', error); | ||
} | ||
} | ||
|
||
this.resetCache(); | ||
}, | ||
|
||
// 토큰 생성 후 토큰 반환 함수 | ||
async setToken(sessionId, attendIdx, attends) { | ||
const release = await this.mutex.acquire(); // Mutex 잠금 | ||
try { | ||
if (this.sessionId) { | ||
console.log('이미 출석 체크 중입니다.'); | ||
return null; | ||
} | ||
|
||
this.expireAt = Date.now() + TTL; | ||
this.code = String(Math.floor(Math.random() * 10000)).padStart(4, '0'); | ||
this.sessionId = sessionId; | ||
this.attendIdx = attendIdx; | ||
this.attendCache = []; | ||
|
||
const token = tokenCache; | ||
token.code = String(Math.floor(Math.random() * 10000)).padStart(4, '0'); | ||
token.expireAt = Date.now() + TTL; | ||
// 매개변수로 attend배열을 받아옴 | ||
attends.forEach(attend => { | ||
// attend중 idx가 맞은 거만 가져옴 | ||
let attendAtIdx = attend.attendList.find(item => item.attendIdx == attendIdx); | ||
if (!attendAtIdx) { // 이게 없다면 생성 후 캐시에 저장 | ||
attendAtIdx = { attendIdx, status: false }; | ||
attend.attendList.push(attendAtIdx); | ||
} else { | ||
// 있다면 false로 바꿔 | ||
attendAtIdx.status = false; | ||
} | ||
// 캐시에 저장 (save를 하지 않으므로 디비에 영향 x) | ||
this.attendCache.push(attend); | ||
}); | ||
|
||
return token; | ||
} | ||
this.cacheTimeout = setTimeout(async () => { | ||
if (this.expireAt <= Date.now()) { | ||
await this.flushCache(); | ||
} | ||
}, TTL); | ||
|
||
// Token 조회 함수 | ||
function getToken(sessionId) { | ||
if (tokenCache.sessionId == sessionId) { | ||
return tokenCache; | ||
} else { | ||
return { | ||
sessionId: this.sessionId, | ||
attendIdx: this.attendIdx, | ||
expireAt: this.expireAt, | ||
code: this.code | ||
}; | ||
// 임계 구역(이 위 코드는 이 작업이 끝난 이후에 다른 작업 가능) | ||
} finally { | ||
release(); | ||
} | ||
}, | ||
|
||
// 토큰 재시작 후 토큰 반환 함수 | ||
async restartToken() { | ||
const release = await this.mutex.acquire(); // Mutex 잠금 | ||
try { | ||
if (!this.sessionId) { | ||
console.log('토큰이 없습니다. 먼저 토큰을 생성하세요.'); | ||
return null; | ||
} | ||
|
||
this.code = String(Math.floor(Math.random() * 10000)).padStart(4, '0'); | ||
this.expireAt = Date.now() + TTL; | ||
|
||
if (this.cacheTimeout) { | ||
clearTimeout(this.cacheTimeout); | ||
} | ||
|
||
this.cacheTimeout = setTimeout(async () => { | ||
if (this.expireAt <= Date.now()) { | ||
await this.flushCache(); | ||
} | ||
}, TTL); | ||
|
||
this.attendCache.forEach(attend => { | ||
const attendCheck = attend.attendList.find(item => item.attendIdx == this.attendIdx); | ||
if (attendCheck) { | ||
attendCheck.status = false; | ||
} else { | ||
attend.attendList.push({ attendIdx: this.attendIdx, status: false }); | ||
} | ||
}); | ||
|
||
return { | ||
sessionId: this.sessionId, | ||
attendIdx: this.attendIdx, | ||
expireAt: this.expireAt, | ||
code: this.code | ||
}; | ||
|
||
// 임계 구역(이 위 코드는 이 작업이 끝난 이후에 다른 작업 가능) | ||
} finally { | ||
release(); | ||
} | ||
}, | ||
|
||
// 현재 토큰 정보 반환 함수 | ||
nowToken() { | ||
if (this.sessionId) { | ||
return { | ||
sessionId: this.sessionId, | ||
attendIdx: this.attendIdx, | ||
expireAt: this.expireAt, | ||
code: this.code | ||
}; | ||
} | ||
return null; | ||
}, | ||
|
||
// 현재 출석 캐시 반환 함수 | ||
attendsCache() { | ||
return this.attendCache; | ||
} | ||
} | ||
}; | ||
|
||
module.exports = { createToken, getToken, restartToken, deleteToken }; | ||
module.exports = AttendanceTokenCache; |
Oops, something went wrong.