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 */
018package org.apache.hadoop.hbase.replication;
019
020import java.io.IOException;
021import java.util.List;
022import java.util.UUID;
023import java.util.concurrent.TimeUnit;
024import java.util.concurrent.TimeoutException;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.fs.FileSystem;
027import org.apache.hadoop.hbase.Abortable;
028import org.apache.hadoop.hbase.HBaseInterfaceAudience;
029import org.apache.hadoop.hbase.Server;
030import org.apache.hadoop.hbase.TableDescriptors;
031import org.apache.hadoop.hbase.replication.regionserver.MetricsSource;
032import org.apache.hadoop.hbase.replication.regionserver.ReplicationSourceInterface;
033import org.apache.hadoop.hbase.wal.WAL.Entry;
034import org.apache.yetus.audience.InterfaceAudience;
035
036/**
037 * ReplicationEndpoint is a plugin which implements replication to other HBase clusters, or other
038 * systems. ReplicationEndpoint implementation can be specified at the peer creation time by
039 * specifying it in the {@link ReplicationPeerConfig}. A ReplicationEndpoint is run in a thread in
040 * each region server in the same process.
041 * <p>
042 * ReplicationEndpoint is closely tied to ReplicationSource in a producer-consumer relation.
043 * ReplicationSource is an HBase-private class which tails the logs and manages the queue of logs
044 * plus management and persistence of all the state for replication. ReplicationEndpoint on the
045 * other hand is responsible for doing the actual shipping and persisting of the WAL entries in the
046 * other cluster.
047 */
048@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.REPLICATION)
049public interface ReplicationEndpoint extends ReplicationPeerConfigListener {
050  // TODO: This class needs doc. Has a Context and a ReplicationContext. Then has #start, #stop.
051  // How they relate? Do we #start before #init(Context)? We fail fast if you don't?
052
053  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.REPLICATION)
054  class Context {
055    private final ReplicationSourceInterface replicationSource;
056    private final Server server;
057    private final Configuration localConf;
058    private final Configuration conf;
059    private final FileSystem fs;
060    private final TableDescriptors tableDescriptors;
061    private final ReplicationPeer replicationPeer;
062    private final String peerId;
063    private final UUID clusterId;
064    private final MetricsSource metrics;
065    private final Abortable abortable;
066
067    @InterfaceAudience.Private
068    public Context(final ReplicationSourceInterface replicationSource, final Server server,
069      final Configuration localConf, final Configuration conf, final FileSystem fs,
070      final String peerId, final UUID clusterId, final ReplicationPeer replicationPeer,
071      final MetricsSource metrics, final TableDescriptors tableDescriptors,
072      final Abortable abortable) {
073      this.replicationSource = replicationSource;
074      this.server = server;
075      this.localConf = localConf;
076      this.conf = conf;
077      this.fs = fs;
078      this.clusterId = clusterId;
079      this.peerId = peerId;
080      this.replicationPeer = replicationPeer;
081      this.metrics = metrics;
082      this.tableDescriptors = tableDescriptors;
083      this.abortable = abortable;
084    }
085
086    public ReplicationSourceInterface getReplicationSource() {
087      return replicationSource;
088    }
089
090    public Server getServer() {
091      return server;
092    }
093
094    public Configuration getConfiguration() {
095      return conf;
096    }
097
098    public Configuration getLocalConfiguration() {
099      return localConf;
100    }
101
102    public FileSystem getFilesystem() {
103      return fs;
104    }
105
106    public UUID getClusterId() {
107      return clusterId;
108    }
109
110    public String getPeerId() {
111      return peerId;
112    }
113
114    public ReplicationPeerConfig getPeerConfig() {
115      return replicationPeer.getPeerConfig();
116    }
117
118    public ReplicationPeer getReplicationPeer() {
119      return replicationPeer;
120    }
121
122    public MetricsSource getMetrics() {
123      return metrics;
124    }
125
126    public TableDescriptors getTableDescriptors() {
127      return tableDescriptors;
128    }
129
130    public Abortable getAbortable() {
131      return abortable;
132    }
133  }
134
135  /**
136   * Initialize the replication endpoint with the given context.
137   * @param context replication context
138   * @throws IOException error occur when initialize the endpoint.
139   */
140  void init(Context context) throws IOException;
141
142  /**
143   * Whether or not, the replication endpoint can replicate to it's source cluster with the same
144   * UUID
145   */
146  boolean canReplicateToSameCluster();
147
148  /**
149   * Returns a UUID of the provided peer id. Every HBase cluster instance has a persisted associated
150   * UUID. If the replication is not performed to an actual HBase cluster (but some other system),
151   * the UUID returned has to uniquely identify the connected target system.
152   * @return a UUID or null if the peer cluster does not exist or is not connected.
153   */
154  UUID getPeerUUID();
155
156  /**
157   * Returns a WALEntryFilter to use for filtering out WALEntries from the log. Replication
158   * infrastructure will call this filter before sending the edits to shipEdits().
159   * @return a {@link WALEntryFilter} or null.
160   */
161  WALEntryFilter getWALEntryfilter();
162
163  /**
164   * A context for {@link ReplicationEndpoint#replicate(ReplicateContext)} method.
165   */
166  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.REPLICATION)
167  static class ReplicateContext {
168    List<Entry> entries;
169    int size;
170    String walGroupId;
171    int timeout;
172
173    @InterfaceAudience.Private
174    public ReplicateContext() {
175    }
176
177    public ReplicateContext setEntries(List<Entry> entries) {
178      this.entries = entries;
179      return this;
180    }
181
182    public ReplicateContext setSize(int size) {
183      this.size = size;
184      return this;
185    }
186
187    public ReplicateContext setWalGroupId(String walGroupId) {
188      this.walGroupId = walGroupId;
189      return this;
190    }
191
192    public List<Entry> getEntries() {
193      return entries;
194    }
195
196    public int getSize() {
197      return size;
198    }
199
200    public String getWalGroupId() {
201      return walGroupId;
202    }
203
204    public void setTimeout(int timeout) {
205      this.timeout = timeout;
206    }
207
208    public int getTimeout() {
209      return this.timeout;
210    }
211  }
212
213  /**
214   * Replicate the given set of entries (in the context) to the other cluster. Can block until all
215   * the given entries are replicated. Upon this method is returned, all entries that were passed in
216   * the context are assumed to be persisted in the target cluster.
217   * @param replicateContext a context where WAL entries and other parameters can be obtained.
218   */
219  boolean replicate(ReplicateContext replicateContext);
220
221  // The below methods are inspired by Guava Service. See
222  // https://github.com/google/guava/wiki/ServiceExplained for overview of Guava Service.
223  // Below we implement a subset only with different names on some methods so we can implement
224  // the below internally using Guava (without exposing our implementation to
225  // ReplicationEndpoint implementors.
226
227  /**
228   * Returns {@code true} if this service is RUNNING.
229   */
230  boolean isRunning();
231
232  /** Returns Return {@code true} is this service is STARTING (but not yet RUNNING). */
233  boolean isStarting();
234
235  /**
236   * Initiates service startup and returns immediately. A stopped service may not be restarted.
237   * Equivalent of startAsync call in Guava Service.
238   * @throws IllegalStateException if the service is not new, if it has been run already.
239   */
240  void start();
241
242  /**
243   * Waits for the {@link ReplicationEndpoint} to be up and running.
244   * @throws IllegalStateException if the service reaches a state from which it is not possible to
245   *                               enter the (internal) running state. e.g. if the state is
246   *                               terminated when this method is called then this will throw an
247   *                               IllegalStateException.
248   */
249  void awaitRunning();
250
251  /**
252   * Waits for the {@link ReplicationEndpoint} to to be up and running for no more than the given
253   * time.
254   * @param timeout the maximum time to wait
255   * @param unit    the time unit of the timeout argument
256   * @throws TimeoutException      if the service has not reached the given state within the
257   *                               deadline
258   * @throws IllegalStateException if the service reaches a state from which it is not possible to
259   *                               enter the (internal) running state. e.g. if the state is
260   *                               terminated when this method is called then this will throw an
261   *                               IllegalStateException.
262   */
263  void awaitRunning(long timeout, TimeUnit unit) throws TimeoutException;
264
265  /**
266   * If the service is starting or running, this initiates service shutdown and returns immediately.
267   * If the service has already been stopped, this method returns immediately without taking action.
268   * Equivalent of stopAsync call in Guava Service.
269   */
270  void stop();
271
272  /**
273   * Waits for the {@link ReplicationEndpoint} to reach the terminated (internal) state.
274   * @throws IllegalStateException if the service FAILED.
275   */
276  void awaitTerminated();
277
278  /**
279   * Waits for the {@link ReplicationEndpoint} to reach a terminal state for no more than the given
280   * time.
281   * @param timeout the maximum time to wait
282   * @param unit    the time unit of the timeout argument
283   * @throws TimeoutException      if the service has not reached the given state within the
284   *                               deadline
285   * @throws IllegalStateException if the service FAILED.
286   */
287  void awaitTerminated(long timeout, TimeUnit unit) throws TimeoutException;
288
289  /**
290   * Returns the {@link Throwable} that caused this service to fail.
291   * @throws IllegalStateException if this service's state isn't FAILED.
292   */
293  Throwable failureCause();
294
295  /**
296   * Hook invoked before persisting replication offsets. Eg: Buffered endpoints can flush/close WALs
297   * here.
298   */
299  default void beforePersistingReplicationOffset() throws IOException {
300  }
301}