임시 데이터베이스 적용을 위해 미리 결정한 체크리스트는 다음과 같습니다.
insert
)되는 데이터는 메모리에 모아두었다가 한 번에 DB에 bulk insert
를 진행하기update
) 시 메모리에서 직접 갱신하며, 최종적으로 갱신된 내용만 DB에 update
를 진행하기
빈번한 DB 접근을 줄이고 성능을 향상시키기delete
) 요청이 들어온 데이터가 메모리에 담겨져 있는 경우에는, 해당 데이터에 접근할 수 없도록 막기fs.appendFile()
을 활용하여 CSV 파일에 데이터를 저장하기
데이터를 DB에 전송한 후와 서버가 재시작됐을 때는 CSV 파일을 비우기Docker
를 적용하였기 때문에, CSV 파일은 Docker 컨테이너 내부
가 아닌 Docker Volume
을 활용하여 클라우드 서버에 저장하기임시 데이터베이스를 관리하기 위해, TemporaryDatabaseService
클래스를 구현하였습니다.
@Injectable()
export class TemporaryDatabaseService {
private database: Map<string, Map<string, Map<string, DataType>>> = new Map();
private readonly FOLDER_NAME = CSV_FOLDER;
constructor(
private readonly prismaMysql: PrismaServiceMySQL,
private readonly prismaMongoDB: PrismaServiceMongoDB,
) {
this.init();
}
async init() {
this.initializeDatabase();
await this.readDataFromFiles();
await this.executeBulkOperations();
}
...
}
init()
함수가 실행 됩니다. init()
함수에는 다음과 같은 함수들이 포함되어 있습니다.private initializeDatabase() {
const services = [
'USER_TB',
'PROFILE_TB',
'SPACE_TB',
'BoardCollection',
'PROFILE_SPACE_TB',
'REFRESH_TOKEN_TB',
'INVITE_CODE_TB',
];
const operations = ['insert', 'update', 'delete'];
services.forEach((service) => {
const serviceMap = new Map();
this.database.set(service, serviceMap);
operations.forEach((operation) => {
serviceMap.set(operation, new Map());
});
});
}
initializeDatabase
메서드를 통해 초기에 각 서비스별로 insert
, update
, delete
명령을 수행할 해시맵을 생성합니다. 이를 통해 메모리 상에서 빠른 데이터 접근을 가능하게 하였습니다.private async readDataFromFiles() {
const files = await fs.readdir(this.FOLDER_NAME);
return Promise.all(
files
.filter((file) => file.endsWith('.csv'))
.map((file) => this.readDataFromFile(file)),
);
}
private async readDataFromFile(file: string) {
const [service, commandWithExtension] = file.split('-');
const command = commandWithExtension.replace('.csv', '');
const fileData = await fs.readFile(join(this.FOLDER_NAME, file), 'utf8');
fileData.split('\\n').forEach((line) => {
if (line.trim() !== '') {
const [uniqueKey, ...dataParts] = line.split(',');
const data = dataParts.join(',');
this.database
.get(service)
.get(command)
.set(uniqueKey, JSON.parse(data));
}
});
}
readDataFromFiles
및 readDataFromFile
메서드를 통해 CSV 파일로부터 데이터를 읽어와 해시맵에 저장합니다.@Cron('0 */10 * * * *')
async executeBulkOperations() {
for (const service of this.database.keys()) {
const serviceMap = this.database.get(service);
const prisma =
service === 'BoardCollection' ? this.prismaMongoDB : this.prismaMysql;
await this.performInsert(service, serviceMap.get('insert'), prisma);
await this.performUpdate(service, serviceMap.get('update'), prisma);
await this.performDelete(service, serviceMap.get('delete'), prisma);
}
}