001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.hbase.replication;
020
021import java.util.Collection;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.List;
025import java.util.Map;
026import java.util.Set;
027import java.util.TreeMap;
028import org.apache.hadoop.hbase.TableName;
029import org.apache.hadoop.hbase.util.Bytes;
030import org.apache.yetus.audience.InterfaceAudience;
031
032/**
033 * A configuration for the replication peer cluster.
034 */
035@InterfaceAudience.Public
036public class ReplicationPeerConfig {
037
038  private String clusterKey;
039  private String replicationEndpointImpl;
040  private final Map<byte[], byte[]> peerData;
041  private final Map<String, String> configuration;
042  private Map<TableName, ? extends Collection<String>> tableCFsMap = null;
043  private Set<String> namespaces = null;
044  // Default value is true, means replicate all user tables to peer cluster.
045  private boolean replicateAllUserTables = true;
046  private Map<TableName, ? extends Collection<String>> excludeTableCFsMap = null;
047  private Set<String> excludeNamespaces = null;
048  private long bandwidth = 0;
049  private final boolean serial;
050
051  private ReplicationPeerConfig(ReplicationPeerConfigBuilderImpl builder) {
052    this.clusterKey = builder.clusterKey;
053    this.replicationEndpointImpl = builder.replicationEndpointImpl;
054    this.peerData = Collections.unmodifiableMap(builder.peerData);
055    this.configuration = Collections.unmodifiableMap(builder.configuration);
056    this.tableCFsMap =
057        builder.tableCFsMap != null ? unmodifiableTableCFsMap(builder.tableCFsMap) : null;
058    this.namespaces =
059        builder.namespaces != null ? Collections.unmodifiableSet(builder.namespaces) : null;
060    this.replicateAllUserTables = builder.replicateAllUserTables;
061    this.excludeTableCFsMap =
062        builder.excludeTableCFsMap != null ? unmodifiableTableCFsMap(builder.excludeTableCFsMap)
063            : null;
064    this.excludeNamespaces =
065        builder.excludeNamespaces != null ? Collections.unmodifiableSet(builder.excludeNamespaces)
066            : null;
067    this.bandwidth = builder.bandwidth;
068    this.serial = builder.serial;
069  }
070
071  private Map<TableName, List<String>>
072      unmodifiableTableCFsMap(Map<TableName, List<String>> tableCFsMap) {
073    Map<TableName, List<String>> newTableCFsMap = new HashMap<>();
074    tableCFsMap.forEach((table, cfs) -> newTableCFsMap.put(table,
075      cfs != null ? Collections.unmodifiableList(cfs) : null));
076    return Collections.unmodifiableMap(newTableCFsMap);
077  }
078
079  /**
080   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
081   *             {@link ReplicationPeerConfigBuilder} to create new ReplicationPeerConfig.
082   */
083  @Deprecated
084  public ReplicationPeerConfig() {
085    this.peerData = new TreeMap<>(Bytes.BYTES_COMPARATOR);
086    this.configuration = new HashMap<>(0);
087    this.serial = false;
088  }
089
090  /**
091   * Set the clusterKey which is the concatenation of the slave cluster's:
092   * hbase.zookeeper.quorum:hbase.zookeeper.property.clientPort:zookeeper.znode.parent
093   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
094   *             {@link ReplicationPeerConfigBuilder#setClusterKey(String)} instead.
095   */
096  @Deprecated
097  public ReplicationPeerConfig setClusterKey(String clusterKey) {
098    this.clusterKey = clusterKey;
099    return this;
100  }
101
102  /**
103   * Sets the ReplicationEndpoint plugin class for this peer.
104   * @param replicationEndpointImpl a class implementing ReplicationEndpoint
105   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
106   *             {@link ReplicationPeerConfigBuilder#setReplicationEndpointImpl(String)} instead.
107   */
108  @Deprecated
109  public ReplicationPeerConfig setReplicationEndpointImpl(String replicationEndpointImpl) {
110    this.replicationEndpointImpl = replicationEndpointImpl;
111    return this;
112  }
113
114  public String getClusterKey() {
115    return clusterKey;
116  }
117
118  public String getReplicationEndpointImpl() {
119    return replicationEndpointImpl;
120  }
121
122  public Map<byte[], byte[]> getPeerData() {
123    return peerData;
124  }
125
126  public Map<String, String> getConfiguration() {
127    return configuration;
128  }
129
130  public Map<TableName, List<String>> getTableCFsMap() {
131    return (Map<TableName, List<String>>) tableCFsMap;
132  }
133
134  /**
135   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
136   *             {@link ReplicationPeerConfigBuilder#setTableCFsMap(Map)} instead.
137   */
138  @Deprecated
139  public ReplicationPeerConfig setTableCFsMap(Map<TableName,
140                                              ? extends Collection<String>> tableCFsMap) {
141    this.tableCFsMap = tableCFsMap;
142    return this;
143  }
144
145  public Set<String> getNamespaces() {
146    return this.namespaces;
147  }
148
149  /**
150   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
151   *             {@link ReplicationPeerConfigBuilder#setNamespaces(Set)} instead.
152   */
153  @Deprecated
154  public ReplicationPeerConfig setNamespaces(Set<String> namespaces) {
155    this.namespaces = namespaces;
156    return this;
157  }
158
159  public long getBandwidth() {
160    return this.bandwidth;
161  }
162
163  /**
164   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
165   *             {@link ReplicationPeerConfigBuilder#setBandwidth(long)} instead.
166   */
167  @Deprecated
168  public ReplicationPeerConfig setBandwidth(long bandwidth) {
169    this.bandwidth = bandwidth;
170    return this;
171  }
172
173  public boolean replicateAllUserTables() {
174    return this.replicateAllUserTables;
175  }
176
177  /**
178   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
179   *             {@link ReplicationPeerConfigBuilder#setReplicateAllUserTables(boolean)} instead.
180   */
181  @Deprecated
182  public ReplicationPeerConfig setReplicateAllUserTables(boolean replicateAllUserTables) {
183    this.replicateAllUserTables = replicateAllUserTables;
184    return this;
185  }
186
187  public Map<TableName, List<String>> getExcludeTableCFsMap() {
188    return (Map<TableName, List<String>>) excludeTableCFsMap;
189  }
190
191  /**
192   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
193   *             {@link ReplicationPeerConfigBuilder#setExcludeTableCFsMap(Map)} instead.
194   */
195  @Deprecated
196  public ReplicationPeerConfig setExcludeTableCFsMap(Map<TableName,
197                                              ? extends Collection<String>> tableCFsMap) {
198    this.excludeTableCFsMap = tableCFsMap;
199    return this;
200  }
201
202  public Set<String> getExcludeNamespaces() {
203    return this.excludeNamespaces;
204  }
205
206  /**
207   * @deprecated as release of 2.0.0, and it will be removed in 3.0.0. Use
208   *             {@link ReplicationPeerConfigBuilder#setExcludeNamespaces(Set)} instead.
209   */
210  @Deprecated
211  public ReplicationPeerConfig setExcludeNamespaces(Set<String> namespaces) {
212    this.excludeNamespaces = namespaces;
213    return this;
214  }
215
216  public static ReplicationPeerConfigBuilder newBuilder() {
217    return new ReplicationPeerConfigBuilderImpl();
218  }
219
220  public boolean isSerial() {
221    return serial;
222  }
223
224  public static ReplicationPeerConfigBuilder newBuilder(ReplicationPeerConfig peerConfig) {
225    ReplicationPeerConfigBuilderImpl builder = new ReplicationPeerConfigBuilderImpl();
226    builder.setClusterKey(peerConfig.getClusterKey())
227      .setReplicationEndpointImpl(peerConfig.getReplicationEndpointImpl())
228      .putAllPeerData(peerConfig.getPeerData()).putAllConfiguration(peerConfig.getConfiguration())
229      .setTableCFsMap(peerConfig.getTableCFsMap()).setNamespaces(peerConfig.getNamespaces())
230      .setReplicateAllUserTables(peerConfig.replicateAllUserTables())
231      .setExcludeTableCFsMap(peerConfig.getExcludeTableCFsMap())
232      .setExcludeNamespaces(peerConfig.getExcludeNamespaces())
233      .setBandwidth(peerConfig.getBandwidth()).setSerial(peerConfig.isSerial());
234    return builder;
235  }
236
237  static class ReplicationPeerConfigBuilderImpl implements ReplicationPeerConfigBuilder {
238
239    private String clusterKey;
240
241    private String replicationEndpointImpl;
242
243    private Map<byte[], byte[]> peerData = new TreeMap<>(Bytes.BYTES_COMPARATOR);
244
245    private Map<String, String> configuration = new HashMap<>();
246
247    private Map<TableName, List<String>> tableCFsMap = null;
248
249    private Set<String> namespaces = null;
250
251    // Default value is true, means replicate all user tables to peer cluster.
252    private boolean replicateAllUserTables = true;
253
254    private Map<TableName, List<String>> excludeTableCFsMap = null;
255
256    private Set<String> excludeNamespaces = null;
257
258    private long bandwidth = 0;
259
260    private boolean serial = false;
261
262    @Override
263    public ReplicationPeerConfigBuilder setClusterKey(String clusterKey) {
264      this.clusterKey = clusterKey;
265      return this;
266    }
267
268    @Override
269    public ReplicationPeerConfigBuilder setReplicationEndpointImpl(String replicationEndpointImpl) {
270      this.replicationEndpointImpl = replicationEndpointImpl;
271      return this;
272    }
273
274    @Override
275    public ReplicationPeerConfigBuilder putConfiguration(String key, String value) {
276      this.configuration.put(key, value);
277      return this;
278    }
279
280    @Override
281    public ReplicationPeerConfigBuilder putPeerData(byte[] key, byte[] value) {
282      this.peerData.put(key, value);
283      return this;
284    }
285
286    @Override
287    public ReplicationPeerConfigBuilder
288        setTableCFsMap(Map<TableName, List<String>> tableCFsMap) {
289      this.tableCFsMap = tableCFsMap;
290      return this;
291    }
292
293    @Override
294    public ReplicationPeerConfigBuilder setNamespaces(Set<String> namespaces) {
295      this.namespaces = namespaces;
296      return this;
297    }
298
299    @Override
300    public ReplicationPeerConfigBuilder setReplicateAllUserTables(boolean replicateAllUserTables) {
301      this.replicateAllUserTables = replicateAllUserTables;
302      return this;
303    }
304
305    @Override
306    public ReplicationPeerConfigBuilder
307        setExcludeTableCFsMap(Map<TableName, List<String>> excludeTableCFsMap) {
308      this.excludeTableCFsMap = excludeTableCFsMap;
309      return this;
310    }
311
312    @Override
313    public ReplicationPeerConfigBuilder setExcludeNamespaces(Set<String> excludeNamespaces) {
314      this.excludeNamespaces = excludeNamespaces;
315      return this;
316    }
317
318    @Override
319    public ReplicationPeerConfigBuilder setBandwidth(long bandwidth) {
320      this.bandwidth = bandwidth;
321      return this;
322    }
323
324    @Override
325    public ReplicationPeerConfigBuilder setSerial(boolean serial) {
326      this.serial = serial;
327      return this;
328    }
329
330    @Override
331    public ReplicationPeerConfig build() {
332      // It would be nice to validate the configuration, but we have to work with "old" data
333      // from ZK which makes it much more difficult.
334      return new ReplicationPeerConfig(this);
335    }
336  }
337
338  @Override
339  public String toString() {
340    StringBuilder builder = new StringBuilder("clusterKey=").append(clusterKey).append(",");
341    builder.append("replicationEndpointImpl=").append(replicationEndpointImpl).append(",");
342    builder.append("replicateAllUserTables=").append(replicateAllUserTables).append(",");
343    if (replicateAllUserTables) {
344      if (excludeNamespaces != null) {
345        builder.append("excludeNamespaces=").append(excludeNamespaces.toString()).append(",");
346      }
347      if (excludeTableCFsMap != null) {
348        builder.append("excludeTableCFsMap=").append(excludeTableCFsMap.toString()).append(",");
349      }
350    } else {
351      if (namespaces != null) {
352        builder.append("namespaces=").append(namespaces.toString()).append(",");
353      }
354      if (tableCFsMap != null) {
355        builder.append("tableCFs=").append(tableCFsMap.toString()).append(",");
356      }
357    }
358    builder.append("bandwidth=").append(bandwidth).append(",");
359    builder.append("serial=").append(serial);
360    return builder.toString();
361  }
362
363  /**
364   * Decide whether the table need replicate to the peer cluster
365   * @param table name of the table
366   * @return true if the table need replicate to the peer cluster
367   */
368  public boolean needToReplicate(TableName table) {
369    String namespace = table.getNamespaceAsString();
370    if (replicateAllUserTables) {
371      // replicate all user tables, but filter by exclude namespaces and table-cfs config
372      if (excludeNamespaces != null && excludeNamespaces.contains(namespace)) {
373        return false;
374      }
375      // trap here, must check existence first since HashMap allows null value.
376      if (excludeTableCFsMap == null || !excludeTableCFsMap.containsKey(table)) {
377        return true;
378      }
379      Collection<String> cfs = excludeTableCFsMap.get(table);
380      // if cfs is null or empty then we can make sure that we do not need to replicate this table,
381      // otherwise, we may still need to replicate the table but filter out some families.
382      return cfs != null && !cfs.isEmpty();
383    } else {
384      // Not replicate all user tables, so filter by namespaces and table-cfs config
385      if (namespaces == null && tableCFsMap == null) {
386        return false;
387      }
388      // First filter by namespaces config
389      // If table's namespace in peer config, all the tables data are applicable for replication
390      if (namespaces != null && namespaces.contains(namespace)) {
391        return true;
392      }
393      return tableCFsMap != null && tableCFsMap.containsKey(table);
394    }
395  }
396}