All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.shardingsphere.data.pipeline.opengauss.ingest.OpenGaussPositionInitializer Maven / Gradle / Ivy

There is a newer version: 5.3.3.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.shardingsphere.data.pipeline.opengauss.ingest;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.shardingsphere.data.pipeline.opengauss.ingest.wal.decode.OpenGaussLogSequenceNumber;
import org.apache.shardingsphere.data.pipeline.postgresql.ingest.wal.WALPosition;
import org.apache.shardingsphere.data.pipeline.spi.ingest.position.PositionInitializer;
import org.opengauss.replication.LogSequenceNumber;

import javax.sql.DataSource;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * OpenGauss WAL position initializer.
 */
// TODO reuse PostgreSQLPositionInitializer
@Slf4j
public final class OpenGaussPositionInitializer implements PositionInitializer {
    
    private static final String SLOT_NAME_PREFIX = "pipeline";
    
    private static final String DECODE_PLUGIN = "mppdb_decoding";
    
    private static final String DUPLICATE_OBJECT_ERROR_CODE = "42710";
    
    @Override
    public WALPosition init(final DataSource dataSource, final String slotNameSuffix) throws SQLException {
        try (Connection connection = dataSource.getConnection()) {
            createSlotIfNotExist(connection, slotNameSuffix);
            return getWalPosition(connection);
        }
    }
    
    @Override
    public WALPosition init(final String data) {
        return new WALPosition(new OpenGaussLogSequenceNumber(LogSequenceNumber.valueOf(Long.parseLong(data))));
    }
    
    /**
     * Create logical replication slot if it does not exist.
     *
     * @param connection connection
     * @throws SQLException SQL exception
     */
    private void createSlotIfNotExist(final Connection connection, final String slotNameSuffix) throws SQLException {
        String slotName = getUniqueSlotName(connection, slotNameSuffix);
        if (!isSlotExist(connection, slotName)) {
            createSlotBySQL(connection, slotName);
        }
    }
    
    private boolean isSlotExist(final Connection connection, final String slotName) throws SQLException {
        String sql = "SELECT slot_name FROM pg_replication_slots WHERE slot_name=? AND plugin=?";
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.setString(1, slotName);
            preparedStatement.setString(2, DECODE_PLUGIN);
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                return resultSet.next();
            }
        }
    }
    
    private void createSlotBySQL(final Connection connection, final String slotName) throws SQLException {
        String sql = String.format("SELECT * FROM pg_create_logical_replication_slot('%s', '%s')", slotName, DECODE_PLUGIN);
        try (PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
            preparedStatement.execute();
        } catch (final SQLException ex) {
            if (!DUPLICATE_OBJECT_ERROR_CODE.equals(ex.getSQLState())) {
                throw ex;
            }
        }
    }
    
    private WALPosition getWalPosition(final Connection connection) throws SQLException {
        try (
                PreparedStatement preparedStatement = connection.prepareStatement("SELECT PG_CURRENT_XLOG_LOCATION()");
                ResultSet resultSet = preparedStatement.executeQuery()) {
            resultSet.next();
            return new WALPosition(new OpenGaussLogSequenceNumber(LogSequenceNumber.valueOf(resultSet.getString(1))));
        }
    }
    
    @Override
    public void destroy(final DataSource dataSource, final String slotNameSuffix) throws SQLException {
        try (Connection connection = dataSource.getConnection()) {
            dropSlotIfExist(connection, slotNameSuffix);
        }
    }
    
    private void dropSlotIfExist(final Connection connection, final String slotNameSuffix) throws SQLException {
        String slotName = getUniqueSlotName(connection, slotNameSuffix);
        if (!isSlotExist(connection, slotName)) {
            log.info("dropSlotIfExist, slot not exist, ignore, slotName={}", slotName);
            return;
        }
        String sql = String.format("select * from pg_drop_replication_slot('%s')", slotName);
        try (CallableStatement callableStatement = connection.prepareCall(sql)) {
            callableStatement.execute();
        }
    }
    
    /**
     * Get the unique slot name by connection.
     *
     * @param connection connection
     * @param slotNameSuffix slot name suffix
     * @return the unique name by connection
     * @throws SQLException failed when getCatalog
     */
    public static String getUniqueSlotName(final Connection connection, final String slotNameSuffix) throws SQLException {
        // same as PostgreSQL, but length over 64 will throw an exception directly
        String slotName = DigestUtils.md5Hex(String.join("_", connection.getCatalog(), slotNameSuffix).getBytes());
        return String.format("%s_%s", SLOT_NAME_PREFIX, slotName);
    }
    
    @Override
    public String getType() {
        return "openGauss";
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy