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.executor;
019
020import io.opentelemetry.api.trace.Span;
021import io.opentelemetry.context.Context;
022import io.opentelemetry.context.Scope;
023import java.io.IOException;
024import java.util.concurrent.atomic.AtomicLong;
025import org.apache.hadoop.hbase.Server;
026import org.apache.hadoop.hbase.trace.TraceUtil;
027import org.apache.yetus.audience.InterfaceAudience;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031/**
032 * Abstract base class for all HBase event handlers. Subclasses should implement the
033 * {@link #process()} and {@link #prepare()} methods. Subclasses should also do all necessary checks
034 * up in their prepare() if possible -- check table exists, is disabled, etc. -- so they fail fast
035 * rather than later when process is running. Do it this way because process be invoked directly but
036 * event handlers are also run in an executor context -- i.e. asynchronously -- and in this case,
037 * exceptions thrown at process time will not be seen by the invoker, not till we implement a
038 * call-back mechanism so the client can pick them up later.
039 * <p>
040 * Event handlers have an {@link EventType}. {@link EventType} is a list of ALL handler event types.
041 * We need to keep a full list in one place -- and as enums is a good shorthand for an
042 * implemenations -- because event handlers can be passed to executors when they are to be run
043 * asynchronously. The hbase executor, see ExecutorService, has a switch for passing event type to
044 * executor.
045 * <p>
046 * @see ExecutorService
047 */
048@InterfaceAudience.Private
049public abstract class EventHandler implements Runnable, Comparable<EventHandler> {
050  private static final Logger LOG = LoggerFactory.getLogger(EventHandler.class);
051
052  // type of event this object represents
053  protected EventType eventType;
054
055  protected Server server;
056
057  // sequence id generator for default FIFO ordering of events
058  protected static final AtomicLong seqids = new AtomicLong(0);
059
060  // sequence id for this event
061  private final long seqid;
062
063  // Time to wait for events to happen, should be kept short
064  protected int waitingTimeForEvents;
065
066  private final Span parent;
067
068  /**
069   * Default base class constructor.
070   */
071  public EventHandler(Server server, EventType eventType) {
072    this.parent = Span.current();
073    this.server = server;
074    this.eventType = eventType;
075    seqid = seqids.incrementAndGet();
076    if (server != null) {
077      this.waitingTimeForEvents =
078        server.getConfiguration().getInt("hbase.master.event.waiting.time", 1000);
079    }
080  }
081
082  /**
083   * Event handlers should do all the necessary checks in this method (rather than in the
084   * constructor, or in process()) so that the caller, which is mostly executed in the ipc context
085   * can fail fast. Process is executed async from the client ipc, so this method gives a quick
086   * chance to do some basic checks. Should be called after constructing the EventHandler, and
087   * before process().
088   * @return the instance of this class
089   * @throws Exception when something goes wrong
090   */
091  public EventHandler prepare() throws Exception {
092    return this;
093  }
094
095  @Override
096  public void run() {
097    Span span = TraceUtil.getGlobalTracer().spanBuilder(getClass().getSimpleName())
098      .setParent(Context.current().with(parent)).startSpan();
099    try (Scope scope = span.makeCurrent()) {
100      process();
101    } catch (Throwable t) {
102      handleException(t);
103    } finally {
104      span.end();
105    }
106  }
107
108  /**
109   * This method is the main processing loop to be implemented by the various subclasses.
110   */
111  public abstract void process() throws IOException;
112
113  /**
114   * Return the event type
115   * @return The event type.
116   */
117  public EventType getEventType() {
118    return this.eventType;
119  }
120
121  /**
122   * Get the priority level for this handler instance. This uses natural ordering so lower numbers
123   * are higher priority.
124   * <p>
125   * Lowest priority is Integer.MAX_VALUE. Highest priority is 0.
126   * <p>
127   * Subclasses should override this method to allow prioritizing handlers.
128   * <p>
129   * Handlers with the same priority are handled in FIFO order.
130   * <p>
131   * @return Integer.MAX_VALUE by default, override to set higher priorities
132   */
133  public int getPriority() {
134    return Integer.MAX_VALUE;
135  }
136
137  /** Returns This events' sequence id. */
138  public long getSeqid() {
139    return this.seqid;
140  }
141
142  /**
143   * Default prioritized runnable comparator which implements a FIFO ordering.
144   * <p>
145   * Subclasses should not override this. Instead, if they want to implement priority beyond FIFO,
146   * they should override {@link #getPriority()}.
147   */
148  @Override
149  public int compareTo(EventHandler o) {
150    if (o == null) {
151      return 1;
152    }
153    if (getPriority() != o.getPriority()) {
154      return (getPriority() < o.getPriority()) ? -1 : 1;
155    }
156    return (this.seqid < o.seqid) ? -1 : 1;
157  }
158
159  @Override
160  public String toString() {
161    return "Event #" + getSeqid() + " of type " + eventType + " (" + getInformativeName() + ")";
162  }
163
164  /**
165   * Event implementations should override thie class to provide an informative name about what
166   * event they are handling. For example, event-specific information such as which region or server
167   * is being processed should be included if possible.
168   */
169  public String getInformativeName() {
170    return this.getClass().toString();
171  }
172
173  /**
174   * Event exception handler, may be overridden
175   * @param t Throwable object
176   */
177  protected void handleException(Throwable t) {
178    String msg = "Caught throwable while processing event " + eventType;
179    LOG.error(msg, t);
180    if (server != null && (t instanceof Error || t instanceof RuntimeException)) {
181      server.abort(msg, t);
182    }
183  }
184}