-
Notifications
You must be signed in to change notification settings - Fork 3.4k
HBASE-24877 Add option to avoid aborting RS process upon uncaught exc… #2255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
5a0adaf
3fb4dce
8af3041
84994f9
e716047
2fcf714
16db51e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,6 +35,7 @@ | |
| import java.util.concurrent.PriorityBlockingQueue; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.TimeoutException; | ||
| import java.util.concurrent.atomic.AtomicBoolean; | ||
| import java.util.concurrent.atomic.AtomicLong; | ||
| import java.util.function.Predicate; | ||
| import org.apache.commons.lang3.StringUtils; | ||
|
|
@@ -119,6 +120,17 @@ public class ReplicationSource implements ReplicationSourceInterface { | |
| private int logQueueWarnThreshold; | ||
| // ReplicationEndpoint which will handle the actual replication | ||
| private volatile ReplicationEndpoint replicationEndpoint; | ||
| //Flag that signalizes uncaught error happening while starting up the source | ||
| // and a retry should be attempted | ||
| private final AtomicBoolean retryStartup = new AtomicBoolean(false); | ||
| //This is needed for the startup loop to identify when there's already | ||
| // an initialization happening (but not finished yet), | ||
| // so that it doesn't try submit another initialize thread. | ||
| // NOTE: this should only be set to false at the end of initialize method, prior to return. | ||
| private final AtomicBoolean startupOngoing = new AtomicBoolean(false); | ||
|
|
||
| private boolean abortOnError; | ||
|
|
||
|
|
||
| /** | ||
| * A filter (or a chain of filters) for WAL entries; filters out edits. | ||
|
|
@@ -217,6 +229,10 @@ public void init(Configuration conf, FileSystem fs, ReplicationSourceManager man | |
| this.throttler = new ReplicationThrottler((double) currentBandwidth / 10.0); | ||
| this.totalBufferUsed = manager.getTotalBufferUsed(); | ||
| this.walFileLengthProvider = walFileLengthProvider; | ||
|
|
||
| this.abortOnError = this.conf.getBoolean("replication.source.regionserver.abort", | ||
| true); | ||
|
|
||
| LOG.info("queueId={}, ReplicationSource: {}, currentBandwidth={}", queueId, | ||
| replicationPeer.getId(), this.currentBandwidth); | ||
| } | ||
|
|
@@ -373,7 +389,21 @@ private void tryStartNewShipper(String walGroupId, PriorityBlockingQueue<Path> q | |
| Threads.setDaemonThreadRunning( | ||
| walReader, Thread.currentThread().getName() | ||
| + ".replicationSource.wal-reader." + walGroupId + "," + queueId, | ||
| this::uncaughtException); | ||
| (t,e) -> { | ||
| this.uncaughtException(t,e); | ||
| while (true) { | ||
| try { | ||
| LOG.info("Refreshing replication sources now due " + | ||
| "to previous error on reader thread."); | ||
| this.manager.refreshSources(this.getPeerId()); | ||
| break; | ||
| } catch (IOException e1) { | ||
| LOG.error("Replication sources refresh failed.", e1); | ||
| sleepForRetries("Sleeping before try refreshing sources again", | ||
| maxRetriesMultiplier); | ||
| } | ||
| } | ||
| }); | ||
| worker.setWALReader(walReader); | ||
| worker.startup(this::uncaughtException); | ||
| return worker; | ||
|
|
@@ -454,7 +484,9 @@ protected final void uncaughtException(Thread t, Throwable e) { | |
| RSRpcServices.exitIfOOME(e); | ||
| LOG.error("Unexpected exception in {} currentPath={}", | ||
| t.getName(), getCurrentPath(), e); | ||
| server.abort("Unexpected exception in " + t.getName(), e); | ||
| if(abortOnError){ | ||
| server.abort("Unexpected exception in " + t.getName(), e); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -549,6 +581,7 @@ private void initialize() { | |
| } | ||
|
|
||
| if (!this.isSourceActive()) { | ||
| this.startupOngoing.set(false); | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -571,6 +604,7 @@ private void initialize() { | |
| } | ||
|
|
||
| if(!this.isSourceActive()) { | ||
| this.startupOngoing.set(false); | ||
| return; | ||
| } | ||
| LOG.info("{} Source: {}, is now replicating from cluster: {}; to peer cluster: {};", | ||
|
|
@@ -583,16 +617,27 @@ private void initialize() { | |
| PriorityBlockingQueue<Path> queue = entry.getValue(); | ||
| tryStartNewShipper(walGroupId, queue); | ||
| } | ||
| this.startupOngoing.set(false); | ||
| } | ||
|
|
||
| @Override | ||
| public void startup() { | ||
| // mark we are running now | ||
| this.sourceRunning = true; | ||
| initThread = new Thread(this::initialize); | ||
| Threads.setDaemonThreadRunning(initThread, | ||
| Thread.currentThread().getName() + ".replicationSource," + this.queueId, | ||
| this::uncaughtException); | ||
| this.retryStartup.set(true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This flag is only used in this method? Let's use a local var instead of a class member field?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, originally I was also referring it on uncaughtException, but it was not actually needed. |
||
| do { | ||
| if(retryStartup.get()) { | ||
| retryStartup.set(false); | ||
| startupOngoing.set(true); | ||
|
wchevreuil marked this conversation as resolved.
|
||
| initThread = new Thread(this::initialize); | ||
| Threads.setDaemonThreadRunning(initThread, | ||
| Thread.currentThread().getName() + ".replicationSource," + this.queueId, | ||
| (t,e) -> { | ||
| uncaughtException(t, e); | ||
| retryStartup.set(true); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I hope if we encounter uncaughtException here, we want to retry the loop again.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, we want to keep trying until we succeed.
The only cases
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah right, it is not set to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed the nits and added some comments to the two flags. |
||
| }); | ||
| } | ||
| } while (startupOngoing.get()); | ||
| } | ||
|
|
||
| @Override | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -290,7 +290,22 @@ private boolean updateLogPosition(WALEntryBatch batch) { | |
| public void startup(UncaughtExceptionHandler handler) { | ||
| String name = Thread.currentThread().getName(); | ||
| Threads.setDaemonThreadRunning(this, | ||
| name + ".replicationSource.shipper" + walGroupId + "," + source.getQueueId(), handler); | ||
| name + ".replicationSource.shipper" + walGroupId + "," + source.getQueueId(), | ||
| (t,e) -> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, the code is almost the same... Then I think we could move the logic into uncaughtException method? If abortOnError is true, we about, otherwise we will try to refresh the source.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved it to uncaughtException. This is true when handling potential errors on both the reader and shipper threads, but uncaughtException is also used when handling issues from ReplicationSource.initialize method, which might blow before reader/shipper is even started. So needed to add extra check in uncaughtException to decide when to invoke refreshSources. |
||
| handler.uncaughtException(t, e); | ||
| while (true) { | ||
| try { | ||
| LOG.info("Refreshing replication sources now due " + | ||
| "to previous error on shipper thread."); | ||
| this.source.getSourceManager().refreshSources(this.source.getPeerId()); | ||
| break; | ||
| } catch (IOException e1) { | ||
| LOG.error("Replication sources refresh failed.", e1); | ||
| sleepForRetries("Sleeping before try refreshing sources again", | ||
| sleepForRetries, 1, maxRetriesMultiplier); | ||
| } | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| Path getCurrentPath() { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So here it is for wal reader. I think refreshSources and retry is an acceptable way. Then let's just test the abortOnError flag here? If it is true, we will call uncaughtException, otherwise we will try to refresh the replication source.