성능과 확장
2026-10-27, 단일 Go 서버에서 WebSpace 인증 WebSocket 연결 10,000개와 이벤트 전달 100,000건을 확인했습니다. SQLite3 혼합 부하에서도 연결 10,000개를 유지한 채 쓰기 1,000건과 읽기 10,000건을 모두 완료했습니다.
검증 결과와 회차별 측정 데이터(JSON)를 내려받을 수 있습니다. Workspace 채팅과는 별도로 WebSpace 전용 실시간 경로를 시험한 결과입니다.
시험 환경과 설정
| 항목 | 시험 조건 |
|---|---|
| 시험일 | 2026-10-27, 한국 표준시 |
| 서버 | Intel Core i9-14900K, 논리 CPU 32개, RAM 약 62.6 GiB, Go 프로세스 1개 |
| 저장소 | NVMe SSD(SHPP41-2000GM), WSL2 가상 디스크(ext4), Ubuntu 26.04 |
| 부하 발생기 | 같은 장비의 Node 프로세스 4개, 루프백 IP 4개, 인증 계정 1개 |
| 통신 | 로컬 루프백, 외부망·TLS·운영 프록시 제외 |
| SQLite3 | WAL, synchronous=NORMAL, busy timeout 5초, 읽기 연결 최대 8개, DB별 단일 쓰기 큐 |
| WebSocket 한도 | 서버 전체·DB별 각각 12,000개, IP별 10,000개, 연결별 초당 메시지 10,000건 |
| HTTP 요청 한도 | 읽기·변경·핸드셰이크 각각 IP당 초당 20,000건, 버스트 30,000건 |
별도 시험용 개발 빌드에서 라이선스 등급 검사를 우회했습니다. 인증, 권한과 Origin 검사는 일반 WebSpace 경로에서 수행했습니다. DB는 디스크의 ext4에 저장했으며 메모리 파일시스템(tmpfs)은 사용하지 않았습니다. 부하 발생기에서 읽기 요청 속도나 동시 쓰기 수를 제한하지 않았습니다.
HTTP 한도는 서버 설정의 webspace.rateLimits.read, webspace.rateLimits.mutation, webspace.rateLimits.handshake 아래 requestsPerSecond와 burst로 지정하고 재시작해 적용했습니다. 시험값은 기본값과 다릅니다. 실시간 연결 한도는 서비스 및 용량에서 관리합니다.
5,000개에서 10,000개까지의 연결과 전달
각 연결이 이벤트 10건을 받도록 설정하고 규모별로 3회씩 측정했습니다. 모든 회차에서 이벤트 누락, 중복과 다른 방으로의 전달은 0건이었습니다.
| 인증 연결 | 회차별 전달 성공 / 예상 | 전체 연결 준비 시간 | 이벤트 전달 p95 |
|---|---|---|---|
| 5,000개 | 50,000 / 50,000건 | 1.25초 | 110 ms |
| 7,500개 | 75,000 / 75,000건 | 1.77초 | 183 ms |
| 10,000개 | 100,000 / 100,000건 | 2.66초 | 312 ms |
시간은 3회 측정값의 중앙값입니다. 전체 연결 준비 시간에는 부하 발생기 시작, 순차적인 연결 증가, 인증과 구독 완료가 포함됩니다. 전달 p95는 발신 시각부터 각 이벤트를 수신할 때까지 걸린 시간의 95백분위이며, 회차별 p95의 중앙값을 표시했습니다. 연결당 이벤트 10건을 모두 받는 데 걸린 시간을 사용한 9월 시험과는 지연 시간의 정의가 다릅니다.
SQLite3 쓰기 1,000건과 읽기 10,000건
인증 연결 10,000개를 유지하면서 공동 자원에 대한 상태 변경 1,000건과 HTTP 조회 10,000건을 함께 실행했습니다. 쓰기는 DB별 큐에서 직렬 처리합니다.
| HTTP 읽기 동시 요청 설정 | 반복 | 쓰기 성공 | 읽기 성공 | 혼합 부하 완료 시간 |
|---|---|---|---|---|
| 128개 | 3회 | 회차별 1,000 / 1,000건 | 회차별 10,000 / 10,000건 | 중앙값 2.69초 |
| 10,000개 | 1회 | 1,000 / 1,000건 | 10,000 / 10,000건 | 3.11초 |
완료 시간은 연결 준비 후 읽기와 쓰기를 시작한 시점부터 모두 끝날 때까지입니다. 동시 요청 10,000개 설정은 클라이언트가 요청을 발행하는 조건이며, SQLite 쿼리 10,000개를 동시에 실행한다는 뜻은 아닙니다. 읽기 연결 풀은 최대 8개입니다.
모든 읽기는 HTTP 200으로 응답했고 HTTP 429와 쓰기 오류는 없었습니다. 동시 요청 10,000개 시험의 쓰기 p95는 858 ms, 읽기 p95는 약 2.88초였습니다. 네 시험 DB 모두 고유 작업 1,000건, 자원 값 100,000→99,000, 버전 1,000, 보상 수량 1,000을 확인했고 SQLite integrity_check는 모두 ok였습니다.
연결 유지, 방 분리와 재접속
10,000개 연결에서 이벤트 10건씩 12회 전송하고 회차 사이에 5초씩 대기한 시험은 약 67.27초 동안 1,200,000건을 모두 전달했습니다. 누락과 중복은 없었습니다. 연결 100개를 강제 종료한 뒤 재접속하고, 후속 이벤트 10,000건도 모두 받았습니다.
별도 시험에서는 100개 방에 연결을 100개씩 나눴습니다. 초기 이벤트 100,000건을 모두 전달했고 다른 방으로 전달된 이벤트는 없었습니다. 20초 대기와 연결 100개 재접속 후의 전달도 통과했습니다. 전체 15개 시험에서 서버 RSS 최고 관측값은 약 1.50 GiB였습니다. 시험 종료 후 남은 연결은 0개였습니다.
지연 시간과 저장소 경로의 영향
연결 유지 시험에서는 전체 구독 영역의 토큰을 다시 검증하는 주기와 겹칠 때 회차별 전달 p95가 약 1.91~2.31초까지 늘어났습니다. 따라서 짧은 전달 시험의 312 ms를 지속적인 지연 시간 보장으로 해석할 수 없습니다.
WSL의 Windows 공유 파일시스템(9p)을 사용한 앞선 시험에서는 동시 쓰기 1,000건 중 414건만 완료되고 나머지는 5초 쓰기 대기·트랜잭션 제한 안에 끝나지 못했습니다. 위 성공 결과는 동일한 NVMe SSD의 WSL ext4 가상 디스크에서 얻었습니다. 저장소 접근 방식에 따라 결과가 달라질 수 있습니다.
기본 읽기 제한인 IP당 초당 500건·버스트 1,200건으로 실행했을 때는 읽기 37건이 HTTP 429로 응답했습니다. 위 최종 시험은 요청을 늦추는 대신 HTTP 한도를 높여 다시 측정한 결과입니다.
서비스 규모에 맞는 구성
| 작업 형태 | 검토할 구성 |
|---|---|
| 단일 서버, 사내 도구, 큐로 처리 가능한 쓰기 | SQLite3와 실제 읽기·쓰기 비율로 부하 시험 |
| 여러 방·자원에 지속적인 쓰기 집중 | PostgreSQL 또는 MySQL/MariaDB 검토 |
| 다중 서버와 복제 | 외부 DB와 서버 간 이벤트 전달 구조 검토 |
| 실시간 전달 지연 | 방 크기, 메시지 크기, 팬아웃과 인증 재검증 비용 점검 |
큐는 동시 쓰기의 순서를 정하지만 처리량을 무제한으로 늘리지는 않습니다. 이번 결과의 연결 수는 동일 계정의 인증 소켓 수이며, 서로 다른 사용자 10,000명의 전체 업무나 장시간 지속 부하를 검증한 수치는 아닙니다. 운영 환경의 TLS, 프록시, 외부망과 실제 작업 비율을 포함해 추가 측정해야 합니다.
운영 중에는 운영 현황에서 요청, 오류, 응답 시간과 DB 상태를 확인할 수 있습니다. 전체 사용자 흐름은 실시간 오목 게임 만들기의 샘플로 확인합니다.