/* * Copyright (c) 2016, PostgreSQL Global Development Group * See the LICENSE file in the project root for more information. */ package org.postgresql.core.v3.replication; import org.postgresql.copy.CopyDual; import org.postgresql.core.PGStream; import org.postgresql.core.QueryExecutor; import org.postgresql.core.ReplicationProtocol; import org.postgresql.replication.PGReplicationStream; import org.postgresql.replication.ReplicationType; import org.postgresql.replication.fluent.CommonOptions; import org.postgresql.replication.fluent.logical.LogicalReplicationOptions; import org.postgresql.replication.fluent.physical.PhysicalReplicationOptions; import org.postgresql.util.GT; import org.postgresql.util.PSQLException; import org.postgresql.util.PSQLState; import java.io.IOException; import java.sql.SQLException; import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; public class V3ReplicationProtocol implements ReplicationProtocol { private static final Logger LOGGER = Logger.getLogger(V3ReplicationProtocol.class.getName()); private final QueryExecutor queryExecutor; private final PGStream pgStream; public V3ReplicationProtocol(QueryExecutor queryExecutor, PGStream pgStream) { this.queryExecutor = queryExecutor; this.pgStream = pgStream; } public PGReplicationStream startLogical(LogicalReplicationOptions options) throws SQLException { String query = createStartLogicalQuery(options); return initializeReplication(query, options, ReplicationType.LOGICAL); } public PGReplicationStream startPhysical(PhysicalReplicationOptions options) throws SQLException { String query = createStartPhysicalQuery(options); return initializeReplication(query, options, ReplicationType.PHYSICAL); } private PGReplicationStream initializeReplication(String query, CommonOptions options, ReplicationType replicationType) throws SQLException { LOGGER.log(Level.FINEST, " FE=> StartReplication(query: {0})", query); configureSocketTimeout(options); CopyDual copyDual = (CopyDual) queryExecutor.startCopy(query, true); return new V3PGReplicationStream( copyDual, options.getStartLSNPosition(), options.getStatusInterval(), replicationType ); } /** * START_REPLICATION [SLOT slot_name] [PHYSICAL] XXX/XXX */ private String createStartPhysicalQuery(PhysicalReplicationOptions options) { StringBuilder builder = new StringBuilder(); builder.append("START_REPLICATION"); if (options.getSlotName() != null) { builder.append(" SLOT ").append(options.getSlotName()); } builder.append(" PHYSICAL ").append(options.getStartLSNPosition().asString()); return builder.toString(); } /** * START_REPLICATION SLOT slot_name LOGICAL XXX/XXX [ ( option_name [option_value] [, ... ] ) ] */ private String createStartLogicalQuery(LogicalReplicationOptions options) { StringBuilder builder = new StringBuilder(); builder.append("START_REPLICATION SLOT ") .append(options.getSlotName()) .append(" LOGICAL ") .append(options.getStartLSNPosition().asString()); Properties slotOptions = options.getSlotOptions(); if (slotOptions.isEmpty()) { return builder.toString(); } //todo replace on java 8 builder.append(" ("); boolean isFirst = true; for (String name : slotOptions.stringPropertyNames()) { if (isFirst) { isFirst = false; } else { builder.append(", "); } builder.append('\"').append(name).append('\"').append(" ") .append('\'').append(slotOptions.getProperty(name)).append('\''); } builder.append(")"); return builder.toString(); } private void configureSocketTimeout(CommonOptions options) throws PSQLException { if (options.getStatusInterval() == 0) { return; } try { int previousTimeOut = pgStream.getSocket().getSoTimeout(); int minimalTimeOut; if (previousTimeOut > 0) { minimalTimeOut = Math.min(previousTimeOut, options.getStatusInterval()); } else { minimalTimeOut = options.getStatusInterval(); } pgStream.getSocket().setSoTimeout(minimalTimeOut); } catch (IOException ioe) { throw new PSQLException(GT.tr("The connection attempt failed."), PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe); } } }