서버에 들어오는 엄청난 양의 이벤트를 초당 약 1000 개의 이벤트로 평균 처리해야하는 상황이 있습니다 (피크는 ~ 2000 일 수 있음).
문제
우리의 시스템은 Heroku에서 호스팅되며 최대 500 개의 DB 연결을 허용하는 비교적 비싼 Heroku Postgres DB 를 사용합니다. 연결 풀링을 사용하여 서버에서 DB로 연결합니다.
DB 연결 풀이 처리 할 수있는 것보다 빠른 이벤트
우리가 가진 문제는 이벤트가 연결 풀이 처리 할 수있는 것보다 빠르다는 것입니다. 하나의 연결이 서버에서 DB 로의 네트워크 왕복을 완료 할 때까지 n
추가 이벤트가 들어오는 것보다 풀로 다시 해제 될 수 있습니다 .
결국 이벤트가 누적되어 저장 대기 중이며 풀에 사용 가능한 연결이 없기 때문에 시간이 초과되고 전체 시스템이 작동하지 않습니다.
우리는 고객으로부터 더 느린 속도로 문제가되는 고주파 이벤트를 방출하여 긴급 상황을 해결했지만, 고주파 이벤트를 처리해야하는 경우이 시나리오를 처리하는 방법을 여전히 알고 싶습니다.
제약
다른 클라이언트가 동시에 이벤트를 읽고 싶을 수 있습니다
다른 클라이언트는 DB에 아직 저장되지 않은 경우에도 특정 키를 사용하여 모든 이벤트를 계속 읽도록 요청합니다.
클라이언트는 GET api/v1/events?clientId=1
아직 이벤트를 DB에 저장하지 않은 경우에도 클라이언트 1이 보낸 모든 이벤트를 쿼리 하고 가져올 수 있습니다 .
이를 처리하는 방법에 대한 "교실"예가 있습니까?
가능한 해결책
서버에서 이벤트를 대기열에 넣습니다.
서버에서 이벤트를 큐에 넣을 수 있습니다 (큐의 동시성이 최대 400이므로 연결 풀이 부족하지 않음).
이것은 나쁜 생각 입니다.
- 사용 가능한 서버 메모리를 소모합니다. 누적 된 대기열 이벤트는 대량의 RAM을 소비합니다.
- 서버는 24 시간마다 한 번씩 다시 시작됩니다 . 이것은 Heroku가 부과 한 하드 한계 입니다. 이벤트가 대기열에있는 동안 서버가 다시 시작되어 대기열에있는 이벤트가 유실됩니다.
- 서버에 상태를 도입하여 확장 성을 손상시킵니다. 다중 서버 설정이 있고 클라이언트가 대기열에있는 + 저장된 이벤트를 모두 읽으려는 경우 대기열에있는 이벤트가 어떤 서버에 있는지 알 수 없습니다.
별도의 메시지 대기열을 사용하십시오.
메시지 를 펌핑 하는 메시지 대기열 (예 : RabbitMQ ?)을 사용할 수 있다고 가정 하고 다른쪽에는 DB에 이벤트 저장 만 처리하는 다른 서버가 있습니다.
메시지 큐가 대기열에 넣은 이벤트 (아직 저장되지 않은) 쿼리를 허용하는지 확실하지 않으므로 다른 클라이언트가 다른 클라이언트의 메시지를 읽으려면 DB에서 저장된 메시지와 큐에서 보류중인 메시지를 가져올 수 있습니다. 다시 연결하여 읽기 요청 클라이언트로 다시 보낼 수 있습니다.
중앙 DB 코디네이터 서버로 메시지의 일부를 저장하는 여러 데이터베이스를 사용하여 관리
우리가 해결 한 또 다른 솔루션은 중앙의 "DB 코디네이터 /로드 밸런서"와 함께 여러 데이터베이스를 사용하는 것입니다. 이벤트를 수신하면이 코디네이터는 메시지를 작성할 데이터베이스 중 하나를 선택합니다. 이를 통해 여러 개의 Heroku 데이터베이스를 사용할 수 있으므로 연결 제한을 500 x 수의 데이터베이스로 높일 수 있습니다.
읽기 쿼리에서이 코디네이터는 SELECT
각 데이터베이스에 쿼리를 발행 하고 모든 결과를 병합 한 후 읽기를 요청한 클라이언트로 다시 보낼 수 있습니다.
이것은 나쁜 생각 입니다.
- 이 아이디어는 ... ahem .. over-engineering? 관리해야 할 악몽이 될 것입니다 (백업 등). 구축 및 유지 관리가 복잡하며 꼭 필요한 경우가 아니면 KISS 위반 처럼 들립니다 .
- 일관성을 희생 합니다. 이 아이디어를 사용하면 여러 DB에서 트랜잭션을 수행 할 수 있습니다.
ANALYZE
쿼리 자체를 실행 했지만 문제가되지 않습니다. 또한 연결 풀 가설을 테스트하기위한 프로토 타입을 작성했으며 이것이 실제로 문제인지 확인했습니다. 데이터베이스와 서버 자체는 다른 머신에 존재하므로 대기 시간이 길어집니다. 없습니다 배포에 대해 걱정되고, 절대적으로 필요가있는 경우가 아니면, 우리는 Heroku가 포기하지 않으려는 거대한 플러스 우리를 위해.
select null
500 개의 연결을 발행하십시오 . 연결 풀에 문제가 없다는 것을 알 수 있습니다.