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

com.alibaba.hologres.client.Subscribe Maven / Gradle / Ivy

There is a newer version: 2.6.0
Show newest version
/*
 * Copyright (c) 2022. Alibaba Group Holding Limited
 */

package com.alibaba.hologres.client;

import com.alibaba.hologres.client.exception.ExceptionCode;
import com.alibaba.hologres.client.exception.HoloClientException;
import com.alibaba.hologres.client.impl.binlog.BinlogOffset;
import com.alibaba.hologres.client.utils.IdentifierUtil;

import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableMap;
import java.util.Set;
import java.util.TreeMap;

/**
 * 消费binlog的请求.
 */
public class Subscribe {
	private final String tableName;
	private final String slotName;
	//两个builder会确保offsetMap和startTime
	private final Map offsetMap;
	private final String binlogReadStartTime;

	// tableName是分区父表时,分区子表的消费请求
	private final NavigableMap partitionToSubscribeMap;

	// STATIC模式, 指定希望消费的分区的分区值,字符串数组
	private String[] partitionValues;

	private String[] logicalPartitionColumnNames;
	private String[][] logicalPartitionValues;

	protected Subscribe(String tableName, String slotName, Map offsetMap, String binlogReadStartTime) {
		this(tableName, slotName, offsetMap, binlogReadStartTime, new TreeMap<>(), new String[0]);
	}

	protected Subscribe(String tableName, String slotName, Map offsetMap, String binlogReadStartTime,
						NavigableMap partitionToSubscribeMap, String[] partitionValues) {
		this.tableName = tableName;
		this.slotName = slotName;
		this.offsetMap = offsetMap;
		this.binlogReadStartTime = binlogReadStartTime;
		this.partitionToSubscribeMap = partitionToSubscribeMap;
		this.partitionValues = partitionValues;
	}

	protected Subscribe(String tableName, String slotName, Map offsetMap, String binlogReadStartTime,
						NavigableMap partitionToSubscribeMap, String[] logicalPartitionNames, String[][] logicalPartitionValues) {
		this.tableName = tableName;
		this.slotName = slotName;
		this.offsetMap = offsetMap;
		this.binlogReadStartTime = binlogReadStartTime;
		this.partitionToSubscribeMap = partitionToSubscribeMap;
		this.logicalPartitionColumnNames = logicalPartitionNames;
		this.logicalPartitionValues = logicalPartitionValues;
	}

	public String getTableName() {
		return tableName;
	}

	public String getSlotName() {
		return slotName;
	}

	public Map getOffsetMap() {
		return offsetMap;
	}

	public String getBinlogReadStartTime() {
		return binlogReadStartTime;
	}

	public NavigableMap getPartitionToSubscribeMap() {
		return partitionToSubscribeMap;
	}

	public String[] getPartitionValuesToSubscribe() {
		return partitionValues;
	}

	public void setLogicalPartitionValuesToSubscribe(String[][] logicalPartitionValues) {
		this.logicalPartitionValues = logicalPartitionValues;
	}

	// Encode the logical_partition_values 2d array into a string.
	// The first level of the array represents  each row and is delimited by colons.
	// The second level represents each column in the same row and is delimited by commas.
	public String encodeLogicalPartitionValuesToSubscribe() throws HoloClientException {
		if (logicalPartitionValues == null) {
			return null;
		}
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < logicalPartitionValues.length; i++) {
			if (logicalPartitionValues[0].length != logicalPartitionValues[i].length) {
				throw new HoloClientException(ExceptionCode.INVALID_REQUEST, "Each row in partition-values-to-read should have the same column number");
			}
			for (int j = 0; j < logicalPartitionValues[i].length; j++) {
				if (j > 0) {
					builder.append(",");
				}
				builder.append(IdentifierUtil.quoteIdentifier(logicalPartitionValues[i][j], true, true));
			}
			if (i < logicalPartitionValues.length - 1) {
				builder.append(";");
			}
		}
		return builder.toString();
	}

	public void setLogicalPartitionColumnNamesToSubscribe(String[] logicalPartitionColumnNames) {
		this.logicalPartitionColumnNames = logicalPartitionColumnNames;
	}

	// Encode the logical_partition_column_names array into a string delimited by commas.
	public String encodeLogicalPartitionColumnNamesToSubscribe() {
		if (logicalPartitionColumnNames == null) {
			return null;
		}
		StringBuilder builder = new StringBuilder();
		// Iterating through the array and building the quoted column names
		for (int i = 0; i < logicalPartitionColumnNames.length; i++) {
			if (i > 0) {
				builder.append(",");
			}
			builder.append(IdentifierUtil.quoteIdentifier(logicalPartitionColumnNames[i], true, true));  // Assuming quoteIdentifier is a method in the same class
		}
		return builder.toString();
	}

	@Override
	public String toString() {
		return "Subscribe{" +
				"tableName='" + tableName + '\'' +
				", slotName='" + slotName + '\'' +
				", offsetMap=" + offsetMap +
				", binlogReadStartTime='" + binlogReadStartTime + '\'' +
				", partitionToSubscribeMap=" + partitionToSubscribeMap +
				", logicalPartitionColumnNames=" + logicalPartitionColumnNames +
				", logicalPartitionValues=" + logicalPartitionValues +
				'}';
	}

	public static OffsetBuilder newOffsetBuilder(String tableName) {
		return new OffsetBuilder(tableName);
	}

	public static OffsetBuilder newOffsetBuilder(String tableName, String slotName) {
		return new OffsetBuilder(tableName, slotName);
	}

	public static StartTimeBuilder newStartTimeBuilder(String tableName, String slotName) {
		return new StartTimeBuilder(tableName, slotName);
	}

	public static StartTimeBuilder newStartTimeBuilder(String tableName) {
		return new StartTimeBuilder(tableName);
	}

	/**
	 * Builder.
	 */
	public abstract static class Builder {
		protected final String tableName;
		protected final String slotName;

		public Builder(String tableName) {
			if (tableName == null) {
				throw new InvalidParameterException("tableName must be not null");
			}
			this.tableName = tableName;
			this.slotName = null;
		}

		public Builder(String tableName, String slotName) {
			if (tableName == null) {
				throw new InvalidParameterException("tableName must be not null");
			}
			this.tableName = tableName;
			this.slotName = slotName;
		}
	}

	/**
	 * 指定shard时用的.
	 */
	public static class OffsetBuilder extends Builder {
		private Map offsetMap;
		private Map partitionToBuilderMap;
		private List partitionValuesToSubscribe = new ArrayList<>();
		private String[] logicalPartitionNamesToSubscribe;
		private String[][] logicalPartitionValuesToSubscribe;

		public OffsetBuilder(String tableName, String slotName) {
			super(tableName, slotName);
		}

		public OffsetBuilder(String tableName) {
			super(tableName);
		}

		/**
		 * 设定每个shardId的offset.
		 *
		 * @param shardId
		 * @param offset
		 * @return
		 */
		public OffsetBuilder addShardStartOffset(int shardId, BinlogOffset offset) {
			if (offsetMap == null) {
				offsetMap = new HashMap<>();
			}
			offsetMap.putIfAbsent(shardId, offset);
			return this;
		}

		/**
		 * 为一组shard设置offset.
		 * 	一般用于批量设置启动时间.
		 *
		 * @param shardIds
		 * @param offset
		 * @return
		 */
		public OffsetBuilder addShardsStartOffset(Set shardIds, BinlogOffset offset) {
			shardIds.forEach(shardId -> addShardStartOffset(shardId, offset));
			return this;
		}

		public OffsetBuilder addShardStartOffsetForPartition(String partitionName, int shardId, BinlogOffset offset) {
			if (partitionToBuilderMap == null) {
				partitionToBuilderMap = new HashMap<>();
			}
			OffsetBuilder partitionBuilder = partitionToBuilderMap.computeIfAbsent(partitionName, k -> new OffsetBuilder(partitionName));
			partitionBuilder.addShardStartOffset(shardId, offset);
			return this;
		}

		public OffsetBuilder addShardsStartOffsetForPartition(String partitionName, Set shardIds, BinlogOffset offset) {
			if (partitionToBuilderMap == null) {
				partitionToBuilderMap = new HashMap<>();
			}
			OffsetBuilder partitionBuilder = partitionToBuilderMap.computeIfAbsent(partitionName, k -> new OffsetBuilder(partitionName));
			partitionBuilder.addShardsStartOffset(shardIds, offset);
			return this;
		}

		public OffsetBuilder addPartitionValuesToSubscribe(String[] partitionValues) {
			partitionValuesToSubscribe.addAll(Arrays.asList(partitionValues));
			return this;
		}

		// Since logical partition table supports multiple partition columns, we need to pass in
		// a 2D array.
		public OffsetBuilder addLogicalPartitionValuesToSubscribe(String[][] logicalPartitionValues) {
			logicalPartitionValuesToSubscribe = logicalPartitionValues;
			return this;
		}

		public OffsetBuilder addLogicalPartitionNamesToSubscribe(String[] logicalPartitionNames) {
			logicalPartitionNamesToSubscribe = logicalPartitionNames;
			return this;
		}

		public Subscribe build() {
			if (offsetMap == null) {
				throw new InvalidParameterException("must call addShardStartOffset before build");
			}
			NavigableMap partitionToSubscribeMap = new TreeMap<>();
			if (partitionToBuilderMap != null) {
				partitionToBuilderMap.forEach((partition, subscribe) -> partitionToSubscribeMap.put(partition, subscribe.build()));
			}
			if (!partitionValuesToSubscribe.isEmpty()) {
				// Initialize Subscribe for physical partition table.
				return new Subscribe(tableName, slotName, offsetMap, null, partitionToSubscribeMap, partitionValuesToSubscribe.toArray(new String[0]));
			} else {
				// Initialize Subscribe for logical partition table.
				return new Subscribe(tableName, slotName, offsetMap, null, partitionToSubscribeMap, logicalPartitionNamesToSubscribe, logicalPartitionValuesToSubscribe);
			}
		}
	}

	/**
	 * 指定时间时用的, 不能指定shard, 运行时会默认给表的所有shard指定相同的启动时间.
	 */
	public static class StartTimeBuilder extends Builder {
		private String binlogReadStartTime;
		private List partitionValuesToSubscribe = new ArrayList<>();
		private String[] logicalPartitionNamesToSubscribe;
		private String[][] logicalPartitionValuesToSubscribe;

		public StartTimeBuilder(String tableName, String slotName) {
			super(tableName, slotName);
		}

		public StartTimeBuilder(String tableName) {
			super(tableName);
		}

		public StartTimeBuilder setBinlogReadStartTime(String binlogReadStartTime) {
			this.binlogReadStartTime = binlogReadStartTime;
			return this;
		}

		public StartTimeBuilder addPartitionValuesToSubscribe(String[] partitionValues) {
			partitionValuesToSubscribe.addAll(Arrays.asList(partitionValues));
			return this;
		}

		// Since logical partition table supports multiple partition columns, we need to pass in
		// a 2D array.
		public StartTimeBuilder addLogicalPartitionValuesToSubscribe(String[][] logicalPartitionValues) {
			logicalPartitionValuesToSubscribe = logicalPartitionValues;
			return this;
		}

		public StartTimeBuilder addLogicalPartitionNamesToSubscribe(String[] logicalPartitionNames) {
			logicalPartitionNamesToSubscribe = logicalPartitionNames;
			return this;
		}

		public Subscribe build() {
			if (!partitionValuesToSubscribe.isEmpty()) {
				// Initialize Subscribe for physical partition table.
				return new Subscribe(tableName, slotName, null, binlogReadStartTime, new TreeMap<>(), partitionValuesToSubscribe.toArray(new String[0]));
			} else {
				// Initialize Subscribe for logical partition table.
				return new Subscribe(tableName, slotName, null, binlogReadStartTime, new TreeMap<>(), logicalPartitionNamesToSubscribe, logicalPartitionValuesToSubscribe);
			}
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy