Skip to content

Commit 417ff16

Browse files
thetumbledfengwenzhi
andauthored
add rate limit for zk read rate in gc. (#4645)
* add rate limit for gc. * fix checkstyle. * fix conf name and acqurie. * rename gcZkOpRateLimit to gcMetadataOpRateLimit. * rename gcZkOpRateLimit to gcMetadataOpRateLimit. * add return label. * add test code. * document conf. --------- Co-authored-by: fengwenzhi <[email protected]>
1 parent e80d031 commit 417ff16

File tree

5 files changed

+78
-0
lines changed

5 files changed

+78
-0
lines changed

bookkeeper-server/src/main/java/org/apache/bookkeeper/bookie/ScanAndCompareGarbageCollector.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static org.apache.bookkeeper.common.concurrent.FutureUtils.result;
2525

2626
import com.google.common.collect.Sets;
27+
import com.google.common.util.concurrent.RateLimiter;
2728
import java.io.IOException;
2829
import java.net.URI;
2930
import java.util.List;
@@ -84,6 +85,7 @@ public class ScanAndCompareGarbageCollector implements GarbageCollector {
8485
private int activeLedgerCounter;
8586
private StatsLogger statsLogger;
8687
private final int maxConcurrentRequests;
88+
private final RateLimiter gcMetadataOpRateLimiter;
8789

8890
public ScanAndCompareGarbageCollector(LedgerManager ledgerManager, CompactableLedgerStorage ledgerStorage,
8991
ServerConfiguration conf, StatsLogger statsLogger) throws IOException {
@@ -103,6 +105,7 @@ public ScanAndCompareGarbageCollector(LedgerManager ledgerManager, CompactableLe
103105
enableGcOverReplicatedLedger, gcOverReplicatedLedgerIntervalMillis, maxConcurrentRequests);
104106

105107
verifyMetadataOnGc = conf.getVerifyMetadataOnGC();
108+
this.gcMetadataOpRateLimiter = RateLimiter.create(conf.getGcMetadataOpRateLimit());
106109

107110
this.activeLedgerCounter = 0;
108111
}
@@ -153,6 +156,7 @@ public void gc(GarbageCleaner garbageCleaner) {
153156
Versioned<LedgerMetadata> metadata = null;
154157
while (!done) {
155158
start = end + 1;
159+
gcMetadataOpRateLimiter.acquire();
156160
if (ledgerRangeIterator.hasNext()) {
157161
LedgerRange lRange = ledgerRangeIterator.next();
158162
ledgersInMetadata = lRange.getLedgers();
@@ -175,6 +179,7 @@ public void gc(GarbageCleaner garbageCleaner) {
175179
metadata = null;
176180
int rc = BKException.Code.OK;
177181
try {
182+
gcMetadataOpRateLimiter.acquire();
178183
metadata = result(ledgerManager.readLedgerMetadata(bkLid), zkOpTimeoutMs,
179184
TimeUnit.MILLISECONDS);
180185
} catch (BKException | TimeoutException e) {
@@ -236,6 +241,7 @@ private Set<Long> removeOverReplicatedledgers(Set<Long> bkActiveledgers, final G
236241
// check ledger ensembles before creating lock nodes.
237242
// this is to reduce the number of lock node creations and deletions in ZK.
238243
// the ensemble check is done again after the lock node is created.
244+
gcMetadataOpRateLimiter.acquire();
239245
Versioned<LedgerMetadata> preCheckMetadata = ledgerManager.readLedgerMetadata(ledgerId).get();
240246
if (!isNotBookieIncludedInLedgerEnsembles(preCheckMetadata)) {
241247
latch.countDown();
@@ -261,6 +267,7 @@ private Set<Long> removeOverReplicatedledgers(Set<Long> bkActiveledgers, final G
261267
// current bookie again and, in that case, we cannot remove the ledger from local storage
262268
lum.acquireUnderreplicatedLedger(ledgerId);
263269
semaphore.acquire();
270+
gcMetadataOpRateLimiter.acquire();
264271
ledgerManager.readLedgerMetadata(ledgerId)
265272
.whenComplete((metadata, exception) -> {
266273
try {

bookkeeper-server/src/main/java/org/apache/bookkeeper/conf/ServerConfiguration.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ public class ServerConfiguration extends AbstractConfiguration<ServerConfigurati
114114
protected static final String GC_OVERREPLICATED_LEDGER_WAIT_TIME = "gcOverreplicatedLedgerWaitTime";
115115
protected static final String GC_OVERREPLICATED_LEDGER_MAX_CONCURRENT_REQUESTS =
116116
"gcOverreplicatedLedgerMaxConcurrentRequests";
117+
protected static final String GC_METADATA_OP_RATE_LIMIT = "gcMetadataOpRateLimit";
117118
protected static final String USE_TRANSACTIONAL_COMPACTION = "useTransactionalCompaction";
118119
protected static final String VERIFY_METADATA_ON_GC = "verifyMetadataOnGC";
119120
protected static final String GC_ENTRYLOGMETADATA_CACHE_ENABLED = "gcEntryLogMetadataCacheEnabled";
@@ -481,6 +482,24 @@ public ServerConfiguration setGcOverreplicatedLedgerMaxConcurrentRequests(
481482
return this;
482483
}
483484

485+
/**
486+
* Get the rate limit of metadata operations in garbage collection.
487+
* @return rate limit of metadata operations in garbage collection
488+
*/
489+
public int getGcMetadataOpRateLimit() {
490+
return this.getInt(GC_METADATA_OP_RATE_LIMIT, 1000);
491+
}
492+
493+
/**
494+
* Set the rate limit of metadata operations in garbage collection.
495+
* @param gcRateLimit
496+
* @return server configuration
497+
*/
498+
public ServerConfiguration setGcMetadataOpRateLimit(int gcRateLimit) {
499+
this.setProperty(GC_METADATA_OP_RATE_LIMIT, Integer.toString(gcRateLimit));
500+
return this;
501+
}
502+
484503
/**
485504
* Get whether to use transactional compaction and using a separate log for compaction or not.
486505
*

bookkeeper-server/src/test/java/org/apache/bookkeeper/meta/GcLedgersTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.Collections;
3737
import java.util.EnumSet;
3838
import java.util.HashSet;
39+
import java.util.Iterator;
3940
import java.util.LinkedList;
4041
import java.util.List;
4142
import java.util.Map;
@@ -342,6 +343,53 @@ public void clean(long ledgerId) {
342343
assertEquals("Should have cleaned first ledger" + first, (long) first, (long) cleaned.get(0));
343344
}
344345

346+
347+
/**
348+
* Verifies that the garbage collector respects the configured rate limit for metadata operations.
349+
* @throws Exception
350+
*/
351+
@Test
352+
public void testGcMetadataOpRateLimit() throws Exception {
353+
int numLedgers = 2000;
354+
int numRemovedLedgers = 800;
355+
final Set<Long> createdLedgers = new HashSet<Long>();
356+
createLedgers(numLedgers, createdLedgers);
357+
358+
ServerConfiguration conf = new ServerConfiguration(baseConf);
359+
int customRateLimit = 200;
360+
conf.setGcMetadataOpRateLimit(customRateLimit);
361+
// set true to verify metadata on gc
362+
conf.setVerifyMetadataOnGc(true);
363+
364+
final GarbageCollector garbageCollector = new ScanAndCompareGarbageCollector(
365+
getLedgerManager(), new MockLedgerStorage(), conf, NullStatsLogger.INSTANCE);
366+
367+
// delete created ledgers to simulate the garbage collection scenario
368+
Iterator<Long> createdLedgersIterator = createdLedgers.iterator();
369+
for (int i = 0; i < numRemovedLedgers && createdLedgersIterator.hasNext(); i++) {
370+
long ledgerId = createdLedgersIterator.next();
371+
try {
372+
removeLedger(ledgerId);
373+
} catch (Exception e) {
374+
LOG.error("Failed to remove ledger {}", ledgerId, e);
375+
}
376+
}
377+
378+
long startTime = System.currentTimeMillis();
379+
garbageCollector.gc(new GarbageCollector.GarbageCleaner() {
380+
@Override
381+
public void clean(long ledgerId) {
382+
}
383+
});
384+
long endTime = System.currentTimeMillis();
385+
long duration = endTime - startTime;
386+
long minExpectedTime = (numRemovedLedgers * 1000L) / customRateLimit;
387+
388+
LOG.info("GC operation with rate limit {} took {} ms, theoretical minimum time: {} ms",
389+
customRateLimit, duration, minExpectedTime);
390+
assertTrue("GC operation should be rate limited", duration >= minExpectedTime * 0.7);
391+
}
392+
345393
/*
346394
* in this scenario no ledger is created, so ledgeriterator's hasNext call would return false and next would be
347395
* null. GarbageCollector.gc is expected to behave normally

conf/bk_server.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,9 @@ ledgerDirectories=/tmp/bk-data
601601
# interval if there is enough disk capacity.
602602
# gcWaitTime=1000
603603

604+
# The rate limit of metadata operations in garbage collection.
605+
#gcMetadataOpRateLimit=1000
606+
604607
# How long the interval to trigger next garbage collection of overreplicated
605608
# ledgers, in milliseconds [Default: 1 day]. This should not be run very frequently
606609
# since we read the metadata for all the ledgers on the bookie from zk

site3/website/docs/reference/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ The table below lists parameters that you can set to configure bookies. All conf
191191
| Parameter | Description | Default
192192
| --------- | ----------- | ------- |
193193
| gcWaitTime | How long the interval to trigger next garbage collection, in milliseconds. Since garbage collection is running in background, too frequent gc will heart performance. It is better to give a higher number of gc interval if there is enough disk capacity. | 1000 |
194+
| gcMetadataOpRateLimit | Rate limit for metadata operations in garbage collection, in operations per second. This is used to limit the rate of metadata operations during garbage collection to avoid overwhelming the metadata service. | 1000 |
194195
| gcOverreplicatedLedgerWaitTime | How long the interval to trigger next garbage collection of overreplicated ledgers, in milliseconds. This should not be run very frequently since we read the metadata for all the ledgers on the bookie from zk. | 86400000 |
195196
| gcOverreplicatedLedgerMaxConcurrentRequests | Max number of concurrent requests in garbage collection of overreplicated ledgers. | 1000 |
196197
| isForceGCAllowWhenNoSpace | Whether force compaction is allowed when the disk is full or almost full. Forcing GC may get some space back, but may also fill up disk space more quickly. This is because new log files are created before GC, while old garbage log files are deleted after GC. | false |

0 commit comments

Comments
 (0)