CREATE SUBSCRIPTION

CREATE SUBSCRIPTION

출판(publisher) 쪽 데이터가 변할 때마다 구독(subscriber) 쪽에 그대로 반영되면 좋겠다, 하는 게 논리 복제의 시작이에요. CREATE SUBSCRIPTION은 그런 논리 복제 구독을 새로 추가하는 명령이에요. 구독을 만든 사용자가 구독의 소유자가 되고, 구독 이름은 현재 데이터베이스에 있는 기존 구독 이름과 달라야 해요.

구독은 출판자에 대한 복제 연결을 나타내요. 그래서 로컬 카탈로그에 정의를 추가하는 것 외에도, 이 명령은 보통 출판자 쪽에 복제 슬롯을 만들어 줘요. 이 명령이 실행된 트랜잭션이 커밋될 때 새 구독의 데이터를 복제할 논리 복제 워커가 시작되는데, 구독이 처음부터 비활성화되어 있지 않다면 말이죠.

구독을 만들려면 pg_create_subscription 역할의 권한과, 현재 데이터베이스에 대한 CREATE 권한이 필요해요. 구독과 논리 복제 전반에 대한 더 자세한 내용은 29.2절과 29장에서 볼 수 있어요.

출처: PostgreSQL 문서

본문

Synopsis

CREATE SUBSCRIPTION subscription_name
    CONNECTION 'conninfo'
    PUBLICATION publication_name [, ...]
    [ WITH ( subscription_parameter [= value] [, ... ] ) ]

Description

CREATE SUBSCRIPTION은 새로운 논리 복제 구독을 추가해요. 구독을 만든 사용자가 구독의 소유자가 되고, 구독 이름은 현재 데이터베이스의 기존 구독 이름과 달라야 해요.

구독은 출판자로의 복제 연결을 나타내요. 그래서 로컬 카탈로그에 정의를 추가하는 것 외에도, 이 명령은 보통 출판자에 복제 슬롯을 만들죠.

이 명령이 실행된 트랜잭션의 커밋 시점에 새 구독의 데이터를 복제할 논리 복제 워커가 시작돼요. 단, 구독이 처음부터 비활성화되어 있지 않은 경우에 한해요.

구독을 만들려면 pg_create_subscription 역할의 권한과 현재 데이터베이스에 대한 CREATE 권한이 필요해요.

구독과 논리 복제 전반에 대한 추가 정보는 29.2절과 29장을 참고하세요.

Parameters

subscription_name

새 구독의 이름이에요.

CONNECTION 'conninfo'

출판자 데이터베이스에 어떻게 연결할지 정의하는 libpq 연결 문자열이에요. 자세한 내용은 32.1.1절을 참고하세요.

PUBLICATION publication_name [, ...]

구독할 출판자 쪽 출판(publication)의 이름들이에요.

WITH ( subscription_parameter [= value] [, ... ] )

이 절은 구독의 옵션 매개변수를 지정해요.

다음 매개변수들은 구독 생성 시 어떤 일이 일어날지를 제어해요.

connect (boolean)

CREATE SUBSCRIPTION 명령이 출판자에 연결할지 여부를 지정해요. 기본값은 true예요. false로 설정하면 create_slot, enabled, copy_data 값이 모두 false로 강제돼요. (connectfalse로 두면서 create_slot, enabled, copy_datatrue로 둘 수는 없어요.)

이 옵션이 false면 연결을 만들지 않으므로 어떤 테이블도 구독되지 않아요. 복제를 시작하려면 복제 슬롯을 수동으로 만들고, 필요하면 장애 조치를 활성화하고, 구독을 활성화하고, 구독을 새로 고침해야 해요. 예시는 29.2.3절을 참고하세요.

create_slot (boolean)

출판자에 복제 슬롯을 만들지 여부를 지정해요. 기본값은 true예요.

false로 설정하면 출판자의 슬롯을 다른 방식으로 직접 만들어야 해요. 예시는 29.2.3절을 참고하세요.

enabled (boolean)

구독이 활발하게 복제를 수행할지, 아니면 설정만 해 두고 아직 시작하지 않을지 지정해요. 기본값은 true예요.

slot_name (string)

사용할 출판자의 복제 슬롯 이름이에요. 기본값은 구독 이름을 슬롯 이름으로 사용하는 거예요.

slot_nameNONE으로 설정하면 해당 구독과 연결된 복제 슬롯이 없어요. 이런 구독은 enabledcreate_slot 모두 false여야 해요. 복제 슬롯을 나중에 수동으로 만들 계획일 때 이걸 써요. 예시는 29.2.3절을 참고하세요.

slot_name을 유효한 이름으로, create_slotfalse로 설정한 경우, 그 이름의 슬롯의 failover 속성 값이 구독에 지정된 failover 매개변수와 다를 수 있어요. 슬롯의 failover 속성이 구독의 해당 매개변수와 일치하는지(반대 방향도) 항상 확인하세요. 그렇지 않으면 출판자의 슬롯이 이 구독 옵션과 다르게 동작할 수 있어요. 예를 들어 구독의 failover 옵션이 비활성화되어 있어도 출판자의 슬롯이 스탠바이에 동기화되거나, 구독의 failover 옵션이 활성화되어 있어도 동기화가 비활성화될 수 있어요.

다음 매개변수들은 생성된 후 구독의 복제 동작을 제어해요.

binary (boolean)

구독이 출판자에게 데이터를 텍스트가 아니라 이진 형식으로 보내 달라고 요청할지 지정해요. 기본값은 false예요. 초기 테이블 동기화 복사(copy_data 참고)도 같은 형식을 사용해요. 이진 형식은 텍스트 형식보다 빠를 수 있지만, 머신 아키텍처와 PostgreSQL 버전 간 이식성은 떨어져요. 이진 형식은 데이터 타입에 매우 의존적이어서, 예를 들어 텍스트 형식에서는 잘 동작하더라도 smallint 컬럼에서 integer 컬럼으로 복사하는 건 허용되지 않아요. 이 옵션을 켜도 이진 송·수신 함수가 있는 데이터 타입만 이진으로 전송돼요. 초기 동기화는 모든 데이터 타입이 이진 송·수신 함수를 가져야 하고, 그렇지 않으면 동기화가 실패한다는 점을 유의하세요(송·수신 함수에 대한 자세한 내용은 CREATE TYPE 참고).

버전 간 복제를 할 때, 출판자에는 어떤 데이터 타입의 이진 송신 함수가 있지만 구독자에는 그 타입의 이진 수신 함수가 없을 수 있어요. 그런 경우 데이터 전송이 실패하므로 binary 옵션을 쓸 수 없어요.

출판자가 16 이전 버전이라면 binary = true여도 초기 테이블 동기화는 텍스트 형식을 사용해요.

copy_data (boolean)

복제가 시작될 때 구독 중인 출판에 이미 있던 데이터를 복사할지 지정해요. 기본값은 true예요.

출판에 WHERE 절이 있으면 어떤 데이터가 복사될지에 영향을 줘요. 자세한 내용은 Notes를 참고하세요.

copy_data = trueorigin 매개변수와 어떻게 상호작용하는지는 Notes를 참고하세요.

streaming (enum)

이 구독에 대해 진행 중인 트랜잭션의 스트리밍을 활성화할지 지정해요. 기본값은 parallel로, 가능하면 들어오는 변경 사항을 병렬 적용 워커 중 하나가 직접 적용한다는 뜻이에요. 병렬 적용 워커에 스트리밍 트랜잭션을 처리할 여유가 없으면 변경 사항을 임시 파일에 쓰고 트랜잭션이 커밋된 뒤에 적용해요. 병렬 적용 워커에서 오류가 발생하면 서버 로그에 원격 트랜잭션의 종료(finish) LSN이 기록되지 않을 수 있다는 점을 유의하세요.

주의: 출판자와 구독자의 스키마가 다르면 교착 상태(deadlock) 위험이 있어요. 다만 그런 경우는 드물어요. 적용 워커는 이런 트랜잭션을 자동으로 재시도하도록 갖춰져 있어요.

on으로 설정하면 들어오는 변경 사항을 임시 파일에 쓰고, 출판자에서 트랜잭션이 커밋되고 구독자가 받은 뒤에만 적용해요.

off로 설정하면 모든 트랜잭션을 출판자에서 완전히 디코딩한 다음에야 구독자에게 통째로 보내요.

synchronous_commit (enum)

이 매개변수의 값은 이 구독의 적용 워커 프로세스 안에서 synchronous_commit 설정을 덮어써요. 기본값은 off예요.

논리 복제에는 off를 쓰는 게 안전해요. 동기화가 빠져서 구독자가 트랜잭션을 잃으면 데이터가 출판자에서 다시 보내지기 때문이에요.

동기식 논리 복제를 할 때는 다른 설정이 더 적합할 수 있어요. 논리 복제 워커는 쓰기·플러시 위치를 출판자에 보고하고, 동기식 복제를 쓸 때 출판자는 실제 플러시를 기다려요. 즉 구독을 동기식 복제에 쓸 때 구독자의 synchronous_commitoff로 설정하면 출판자의 COMMIT 지연이 커질 수 있어요. 이 시나리오에서는 synchronous_commitlocal 이상으로 두는 게 유리할 수 있어요.

two_phase (boolean)

이 구독에 대해 2단계 커밋을 활성화할지 지정해요. 기본값은 false예요.

2단계 커밋이 활성화되면 준비된(prepared) 트랜잭션이 PREPARE TRANSACTION 시점에 구독자로 보내지고, 구독자에서도 2단계 트랜잭션으로 처리돼요. 그렇지 않으면 준비된 트랜잭션은 커밋될 때만 구독자로 보내지고 즉시 처리돼요.

2단계 커밋 구현은 복제가 초기 테이블 동기화 단계를 성공적으로 끝내야 해요. 그래서 구독에 two_phase가 활성화되어 있어도 초기화 단계가 끝나기 전까지 내부 2단계 상태는 임시로 “pending”으로 남아요. 실제 2단계 상태는 pg_subscriptionsubtwophasestate 컬럼을 보면 알 수 있어요.

disable_on_error (boolean)

출판자로부터 데이터 복제 중 구독 워커가 오류를 감지하면 구독을 자동으로 비활성화할지 지정해요. 기본값은 false예요.

password_required (boolean)

true로 설정하면 이 구독 때문에 이루어지는 출판자 연결은 비밀번호 인증을 써야 하고, 비밀번호를 연결 문자열의 일부로 지정해야 해요. 이 설정은 구독이 슈퍼유저 소유일 때는 무시돼요. 기본값은 true예요. 슈퍼유저만 이 값을 false로 설정할 수 있어요.

run_as_owner (boolean)

true면 모든 복제 동작이 구독 소유자로 수행돼요. false면 복제 워커가 각 테이블의 동작을 그 테이블의 소유자로 수행해요. 후자의 구성이 일반적으로 훨씬 안전해요. 자세한 내용은 29.11절을 참고하세요. 기본값은 false예요.

origin (string)

구독이 출판자에게 origin 없는 변경 사항만 보내 달라고 할지, origin에 상관없이 보내 달라고 할지 지정해요. originnone으로 설정하면 origin 없는 변경 사항만 요청하는 거예요. originany로 설정하면 출판자가 origin에 상관없이 변경 사항을 보내요. 기본값은 any예요.

copy_data = trueorigin 매개변수와 어떻게 상호작용하는지는 Notes를 참고하세요.

failover (boolean)

구독과 연결된 복제 슬롯을 스탠바이에 동기화하도록 활성화해서, 장애 조치 후 새 프라이머리에서 논리 복제를 재개할 수 있게 할지 지정해요. 기본값은 false예요.

boolean 타입의 매개변수를 지정할 때는 = value 부분을 생략할 수 있는데, TRUE를 지정한 것과 같아요.

Notes

구독과 출판 인스턴스 사이의 접근 제어를 구성하는 방법은 29.11절을 참고하세요.

복제 슬롯을 만들 때(기본 동작)는 CREATE SUBSCRIPTION을 트랜잭션 블록 안에서 실행할 수 없어요.

같은 데이터베이스 클러스터에 연결하는 구독(예: 같은 클러스터의 데이터베이스 사이를 복제하거나 같은 데이터베이스 안에서 복제)을 만드는 것은, 복제 슬롯이 같은 명령의 일부로 만들어지지 않을 때만 성공해요. 그렇지 않으면 CREATE SUBSCRIPTION 호출이 멈춰 버려요. 이걸 동작시키려면 복제 슬롯을 별도로 만들고(플러그인 이름 pgoutput을 가진 pg_create_logical_replication_slot 함수 사용) create_slot = false 매개변수로 구독을 만들어요. 예시는 29.2.3절을 참고하세요. 이는 구현 제한이라 향후 릴리스에서 풀릴 수 있어요.

출판에 있는 어떤 테이블에 WHERE 절이 있으면, 그 expressionfalseNULL로 평가되는 행은 출판되지 않아요. 구독에 같은 테이블이 서로 다른 WHERE 절로 출판된 여러 출판이 있으면, 그 출판 동작을 가리키는 표현식 중 하나라도 만족하면 그 행이 출판돼요. 서로 다른 WHERE 절의 경우, 어떤 출판에(그 출판 동작을 가리키는) WHERE 절이 없거나 그 출판이 FOR ALL TABLES나 FOR TABLES IN SCHEMA로 선언되어 있으면, 다른 표현식의 정의와 무관하게 행이 항상 출판돼요. 구독자가 15 이전 버전이라면 초기 데이터 동기화 단계에서 행 필터링은 무시돼요. 이 경우 사용자는 이후 필터링과 호환되지 않는 초기 복사 데이터를 삭제하는 것을 고려해야 해요. 초기 데이터 동기화는 기존 테이블 데이터를 복사할 때 출판의 publish 매개변수를 고려하지 않기 때문에, DML로는 복제되지 않을 행이 복사될 수 있어요. 예시는 29.2.2절을 참고하세요.

같은 테이블이 서로 다른 컬럼 목록으로 출판된 여러 출판을 가진 구독은 지원되지 않아요.

존재하지 않는 출판을 지정하는 것도 허용돼요. 나중에 그 출판을 추가할 수 있도록 하기 위해서죠. 즉 pg_subscription에 존재하지 않는 출판이 들어갈 수 있어요.

copy_data = trueorigin = NONE 조합을 쓰면 초기 동기화 테이블 데이터가 출판자에서 직접 복사되므로, 그 데이터의 진정한 origin을 알 수 없어요. 출판자에도 구독이 있다면 복사된 테이블 데이터가 더 상류에서 온 것일 수 있어요. 이 시나리오는 감지되어 사용자에게 경고(WARNING)가 기록되지만, 그 경고는 잠재적 문제를 알리는 표시일 뿐이에요. 복사된 데이터의 origin이 정말 원하는 대로인지를 확인할 책임은 사용자에게 있어요.

출판자에 생성된 다른 구독 때문에 본래 아닌 origin을 가질 수 있는 테이블을 찾으려면 이런 SQL 쿼리를 써 볼 수 있어요.

# substitute <pub-names> below with your publication name(s) to be queried
SELECT DISTINCT PT.schemaname, PT.tablename
FROM pg_publication_tables PT
     JOIN pg_class C ON (C.relname = PT.tablename)
     JOIN pg_namespace N ON (N.nspname = PT.schemaname),
     pg_subscription_rel PS
WHERE C.relnamespace = N.oid AND
      (PS.srrelid = C.oid OR
      C.oid IN (SELECT relid FROM pg_partition_ancestors(PS.srrelid) UNION
                SELECT relid FROM pg_partition_tree(PS.srrelid))) AND
      PT.pubname IN (<pub-names>);

Examples

mypublicationinsert_only 출판의 테이블을 복제하고, 커밋 즉시 복제를 시작하는 원격 서버에 대한 구독을 만들어 볼게요.

CREATE SUBSCRIPTION mysub
         CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
        PUBLICATION mypublication, insert_only;

insert_only 출판의 테이블을 복제하지만, 나중에 활성화할 때까지는 복제를 시작하지 않는 구독을 만들어 볼게요.

CREATE SUBSCRIPTION mysub
         CONNECTION 'host=192.168.1.50 port=5432 user=foo dbname=foodb'
        PUBLICATION insert_only
               WITH (enabled = false);

Compatibility

CREATE SUBSCRIPTION은 PostgreSQL 확장 기능이에요.

더 알아보기 (Learn more)