Node.js에서 읽기 전용 PostgreSQL 복제본으로 쿼리 라우팅
일반적인 부하 분산 요구 사항은 모든 "논리적"읽기 전용 쿼리를 읽기 전용 인스턴스로 라우팅하는 것입니다. 이 요구 사항은 두 가지 방법으로 구현할 수 있습니다.
옵션 1: 두 개의 서로 다른 클라이언트
첫 번째 옵션이 가장 명시적이므로 선호됩니다. 그러나 구현하는 데 가장 많은 오버헤드가 있습니다. 기존 코드베이스에 이를 추가하면 수천 줄의 코드를 수정해야 할 수 있습니다. 이 접근 방식의 다른 단점은 읽기 전용 쿼리가 읽기-쓰기 데이터베이스 클라이언트를 사용할 때 간과하기 쉽다는 것입니다. 이것이 주요 문제는 아니지만 그 반대가 참이라면 좋을 것입니다. 쿼리에 대해 잘못된 연결을 사용하는 경우 경고를 받게 됩니다. 쿼리를 라우팅하는 미들웨어를 사용하면 가능할 수 있습니다.
옵션 2: 미들웨어
두 번째 옵션은 쿼리 자체와 쿼리가 시작된 컨텍스트를 기반으로 쿼리를 라우팅합니다. 쿼리를 읽기 전용 인스턴스로 안전하게 라우팅하려면 다음이 참이어야 합니다.
처음 두 요구 사항은 구현하기가 상대적으로 간단합니다. 세 번째는 우리가 컨벤션을 도입할 것을 요구합니다.
휘발성 함수 처리
PostgreSQL의 휘발성 함수는 부작용(예: 데이터 쓰기)이 있거나 단일 테이블 스캔 내에서도 출력을 변경할 수 있는 함수(예: random())입니다. 우리의 맥락에서 휘발성 기능의 첫 번째 부분만 관련이 있습니다. 그러나 쿼리를 보는 것만으로는 휘발성인지 여부를 알 수 있는 방법이 없습니다.
SELECT foo() # Is foo() volatile?
따라서 이러한 쿼리를 읽기 전용 라우팅에 대해 안전하지 않은 것으로 표시하기 위한 규칙이 필요합니다. 이를 수행하는 방법은 여러 가지가 있지만 쿼리를 휘발성으로 표시하는 매직 주석이라는 매우 간단한 접근 방식을 선택했습니다. 쿼리(주석)의 아무 곳에나
@volatile
를 추가하여 부작용이 있음을 나타내고 미들웨어가 해당 키워드를 확인하도록 합니다.여기에서 역으로 수행하고
@readOnly
키워드를 사용하여 읽기 전용 인스턴스로 라우팅하기에 안전한 쿼리를 표시하는 규칙을 사용할 수도 있습니다. 이렇게 하면 다른 키워드에 대한 쿼리를 모두 확인하지 않고 엔지니어가 계측하는 것에 전적으로 의존할 수 있습니다. 그러나 이 접근 방식은 #1 옵션과 동일한 단점이 있습니다. 기회를 놓치기 쉽고 모든 쿼리를 하나씩 편집해야 합니다.Slonik을 사용하여 쿼리 라우팅 구현
우리는 프로젝트에서 Slonik PostgreSQL 클라이언트를 사용하고 있으며 운 좋게도 Slonik에는 3가지 요구 사항을 모두 구현하는 데 사용할 수 있는
beforePoolConnection
middleware이 있습니다. 즉, 쿼리에 연결이 할당되기 직전에 beforePoolConnection
가 호출됩니다. 세 가지 조건이 모두 충족되면 두 번째 읽기 전용 풀을 만들고 해당 읽기 전용 풀로 쿼리를 라우팅하기만 하면 됩니다. 코드는 매우 간단합니다.const readOnlyPool = await createPool('postgres://read-only');
const pool = await createPool('postgres://main', {
interceptors: [
{
beforePoolConnection: (connectionContext) => {
if (!connectionContext.query?.sql.trim().toUpperCase().startsWith('SELECT ')) {
// Returning null falls back to using the DatabasePool from which the query originates.
return null;
}
// This is a convention for the edge-cases where a SELECT query includes a volatile function.
// Adding a @volatile comment anywhere into the query bypasses the read-only route, e.g.
// sql`
// # @volatile
// SELECT write_log()
// `
if (connectionContext.query?.sql.includes('@volatile')) {
return null;
}
// Returning an instance of DatabasePool will attempt to run the query using the other connection pool.
// Note that all other interceptors of the pool that the query originated from are short-circuited.
return readOnlyPool;
}
}
]
});
// This query will use `postgres://read-only` connection.
pool.query(sql`SELECT 1`);
// This query will use `postgres://main` connection.
pool.query(sql`UPDATE 1`);
위의 코드에 대해 명확히 할 가치가 있는 몇 가지 사항은 다음과 같습니다.
SELECT INTO
를 처리하지 않습니다. 사용 사례에 필요하지 않다는 것을 알고 있으므로 생략했습니다. SELECT INTO
를 사용하는 경우 쿼리에 INTO
키워드가 포함되어 있는지 확인하는 간단한 수정이 될 수 있습니다. pool#connect()
또는 pool#transaction()
, then
connectionContext.query is
null`을 사용하여 연결을 시작하는 경우 즉, 기본 풀을 사용하도록 폴백한다는 점을 강조할 필요가 있습니다. 그리고 그게 다야! 짧고 달다. 이제 기본 인스턴스의 로드를 줄이고 서비스를 더 잘 확장할 수 있는 efficient query routing mechanism이 있습니다.
Reference
이 문제에 관하여(Node.js에서 읽기 전용 PostgreSQL 복제본으로 쿼리 라우팅), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/gajus/routing-queries-to-read-only-postgresql-replica-in-nodejs-5eo1텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)