Skip to content

Commit acab6f7

Browse files
committed
HBASE-30238 Prevent duplicate replicated bulk loads across retries
Split replicated bulkload event tracking behind an interface and keep the ZooKeeper implementation responsible for marker lifecycle. Mark loaded table events done immediately and keep failed table events in progress to avoid retrying already loaded HFiles.
1 parent 364a785 commit acab6f7

10 files changed

Lines changed: 649 additions & 400 deletions

File tree

hbase-common/src/main/resources/hbase-default.xml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,6 +1867,79 @@ possible configurations would overwhelm and obscure the important.
18671867
default of 10 will rarely need to be changed.
18681868
</description>
18691869
</property>
1870+
<property>
1871+
<name>hbase.replication.bulkload.copy.bandwidth.mb</name>
1872+
<value>0</value>
1873+
<description>
1874+
The maximum aggregate bandwidth, in megabytes per second, that a sink RegionServer uses while
1875+
copying HFiles from the source cluster for replicated bulk loads. 0 means no limit. This value
1876+
can be changed dynamically through configuration reload on the sink. If this is set too low,
1877+
the HFile copy can exceed the replication RPC timeout and cause the source to retry the same
1878+
batch.
1879+
</description>
1880+
</property>
1881+
<property>
1882+
<name>hbase.replication.bulkload.event.tracker.znode</name>
1883+
<value>bulkload-events</value>
1884+
<description>
1885+
The ZooKeeper znode name under the HBase replication znode used to coordinate replicated
1886+
bulk load events in the sink cluster. RegionServers create short-lived in-progress markers
1887+
and completed-event markers under this znode so that retries of the same replicated bulk
1888+
load WAL event are not executed more than once across different target RegionServers.
1889+
</description>
1890+
</property>
1891+
<property>
1892+
<name>hbase.replication.bulkload.event.bucket.width.ms</name>
1893+
<value>3600000</value>
1894+
<description>
1895+
The time width, in milliseconds, of each bucket used when storing replicated bulk load event
1896+
markers. The event write time is divided by this value to choose a bucket. Larger values
1897+
reduce the number of ZooKeeper bucket znodes but make each bucket contain more completed
1898+
event markers for the cleaner to scan. Smaller values create more bucket znodes but make
1899+
each bucket smaller.
1900+
</description>
1901+
</property>
1902+
<property>
1903+
<name>hbase.replication.bulkload.event.wait.timeout.ms</name>
1904+
<value>60000</value>
1905+
<description>
1906+
The maximum time, in milliseconds, that a sink RegionServer waits while another RegionServer
1907+
is processing the same replicated bulk load event. If the event is marked done before this
1908+
timeout, the retry is treated as already completed and skipped. If the timeout expires while
1909+
the in-progress marker still exists, replication fails the batch so the source can retry
1910+
later.
1911+
</description>
1912+
</property>
1913+
<property>
1914+
<name>hbase.replication.bulkload.event.wait.interval.ms</name>
1915+
<value>1000</value>
1916+
<description>
1917+
The sleep interval, in milliseconds, between checks while waiting for another sink
1918+
RegionServer to finish the same replicated bulk load event. Smaller values detect completion
1919+
sooner but issue more ZooKeeper checks. Larger values reduce ZooKeeper polling at the cost
1920+
of slower retry progress.
1921+
</description>
1922+
</property>
1923+
<property>
1924+
<name>hbase.master.cleaner.replication.bulkload.event.period.ms</name>
1925+
<value>600000</value>
1926+
<description>
1927+
The period, in milliseconds, at which the HBase Master runs the replicated bulk load event
1928+
marker cleaner. The cleaner removes expired completed-event markers after they are older
1929+
than hbase.replication.bulkload.event.done.ttl.ms and are no longer protected by a matching
1930+
in-progress marker.
1931+
</description>
1932+
</property>
1933+
<property>
1934+
<name>hbase.replication.bulkload.event.done.ttl.ms</name>
1935+
<value>86400000</value>
1936+
<description>
1937+
The minimum age, in milliseconds, before a completed replicated bulk load event marker can be
1938+
removed by the Master cleaner. This value should be long enough to cover expected replication
1939+
retry delays; if it is too small, a late retry may not find the completed marker and can
1940+
execute the same bulk load again. Larger values retain more ZooKeeper marker znodes.
1941+
</description>
1942+
</property>
18701943
<!-- Static Web User Filter properties. -->
18711944
<property>
18721945
<name>hbase.http.staticuser.user</name>

hbase-server/src/main/java/org/apache/hadoop/hbase/master/cleaner/ReplicationBulkLoadEventCleaner.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,15 @@
1717
*/
1818
package org.apache.hadoop.hbase.master.cleaner;
1919

20+
import java.io.IOException;
2021
import java.util.concurrent.TimeUnit;
2122
import org.apache.hadoop.conf.Configuration;
2223
import org.apache.hadoop.hbase.ScheduledChore;
2324
import org.apache.hadoop.hbase.Stoppable;
2425
import org.apache.hadoop.hbase.replication.regionserver.ReplicationBulkLoadEventTracker;
26+
import org.apache.hadoop.hbase.replication.regionserver.ZKReplicationBulkLoadEventTracker;
2527
import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
2628
import org.apache.yetus.audience.InterfaceAudience;
27-
import org.apache.zookeeper.KeeperException;
2829
import org.slf4j.Logger;
2930
import org.slf4j.LoggerFactory;
3031

@@ -48,7 +49,7 @@ public class ReplicationBulkLoadEventCleaner extends ScheduledChore {
4849
public ReplicationBulkLoadEventCleaner(Configuration conf, Stoppable stopper, ZKWatcher zkw) {
4950
super("ReplicationBulkLoadEventCleaner", stopper,
5051
conf.getInt(PERIOD_MS_KEY, PERIOD_MS_DEFAULT));
51-
this.tracker = new ReplicationBulkLoadEventTracker(conf, zkw);
52+
this.tracker = new ZKReplicationBulkLoadEventTracker(conf, zkw);
5253
this.doneTtlMs = conf.getLong(DONE_TTL_MS_KEY, DONE_TTL_MS_DEFAULT);
5354
}
5455

@@ -59,12 +60,8 @@ protected void chore() {
5960
if (deleted > 0) {
6061
LOG.info("Cleaned {} replicated bulkload event marker(s)", deleted);
6162
}
62-
} catch (KeeperException e) {
63+
} catch (IOException e) {
6364
LOG.warn("Failed to clean replicated bulkload event markers", e);
6465
}
6566
}
66-
67-
public void choreForTesting() {
68-
chore();
69-
}
7067
}

hbase-server/src/main/java/org/apache/hadoop/hbase/replication/regionserver/HFileReplicator.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,17 @@ public class HFileReplicator implements Closeable {
9191
private static final String UNDERSCORE = "_";
9292
private final static FsPermission PERM_ALL_ACCESS = FsPermission.valueOf("-rwxrwxrwx");
9393
private static final int COPY_BUFFER_SIZE = 65536;
94-
// null means no throttling
94+
private static final BulkLoadTableLoadListener NO_OP_TABLE_LOAD_LISTENER =
95+
new BulkLoadTableLoadListener() {
96+
@Override
97+
public void tableLoaded(String tableName) {
98+
}
99+
100+
@Override
101+
public void tableLoadFailed(String tableName) {
102+
}
103+
};
104+
// Double.MAX_VALUE means no throttling.
95105
private volatile RateLimiter rateLimiter;
96106

97107
private Configuration sourceClusterConf;
@@ -152,7 +162,17 @@ public void close() throws IOException {
152162
}
153163
}
154164

165+
interface BulkLoadTableLoadListener {
166+
void tableLoaded(String tableName) throws IOException;
167+
168+
void tableLoadFailed(String tableName);
169+
}
170+
155171
public Void replicate() throws IOException {
172+
return replicate(NO_OP_TABLE_LOAD_LISTENER);
173+
}
174+
175+
Void replicate(BulkLoadTableLoadListener tableLoadListener) throws IOException {
156176
// Copy all the hfiles to the local file system
157177
Map<String, Path> tableStagingDirsMap = copyHFilesToStagingDir();
158178

@@ -174,10 +194,16 @@ public Void replicate() throws IOException {
174194
}
175195
fsDelegationToken.acquireDelegationToken(sinkFs);
176196
try {
177-
doBulkLoad(conf, tableName, stagingDir, queue, maxRetries);
197+
try {
198+
doBulkLoad(conf, tableName, stagingDir, queue, maxRetries);
199+
} catch (IOException e) {
200+
tableLoadListener.tableLoadFailed(tableNameString);
201+
throw e;
202+
}
178203
} finally {
179204
cleanup(stagingDir);
180205
}
206+
tableLoadListener.tableLoaded(tableNameString);
181207
}
182208
return null;
183209
}

0 commit comments

Comments
 (0)