@@ -47,6 +47,9 @@ public final class QueryCircuitBreaker {
4747 private static final Logger LOGGER = LoggerFactory .getLogger (QueryCircuitBreaker .class );
4848 private static final long DEFAULT_RECOVERY_COOLDOWN_MS = 1000 ;
4949 private static final long DEFAULT_GC_MONITOR_POLL_MS = 250 ;
50+ private static final long CHECKPOINT_GC_BASE_INTERVAL_MS = 1000 ;
51+ private static final int CHECKPOINT_GC_MAX_INTERVAL_STEPS = 10 ;
52+ private static final long CHECKPOINT_GC_RESET_AFTER_MS = 30_000 ;
5053 private static final long HIGH_MEMORY_GC_INTERVAL_MS = 5000 ;
5154 private static final long CRITICAL_MEMORY_GC_INTERVAL_MS = 1000 ;
5255 private static final long THROTTLE_WARNING_INTERVAL_MS = 60_000 ;
@@ -74,6 +77,8 @@ public final class QueryCircuitBreaker {
7477 private volatile long lastCancelAt = Long .MIN_VALUE ;
7578 private volatile QueryPressureState lastMonitorMemoryState = QueryPressureState .NORMAL ;
7679 private volatile long lastMonitorGcAt = Long .MIN_VALUE ;
80+ private long lastCheckpointGcAt = Long .MIN_VALUE ;
81+ private int checkpointGcIntervalStep ;
7782
7883 public static QueryCircuitBreaker getInstance () {
7984 return INSTANCE ;
@@ -180,15 +185,18 @@ public void checkpoint(QueryCircuitBreakerHandle handle, String operator) throws
180185 QueryPressureMonitor .Snapshot snapshot = pressureMonitor .sample ();
181186 QueryPressureState state = refreshState ("checkpoint:" + operator , configuration , snapshot );
182187 warnAboutRunningQueryThrottlingIfNeeded (state , snapshot );
188+ if (shouldRequestCheckpointGc (state , configuration , snapshot )) {
189+ maybeRunCheckpointGc ();
190+ }
183191 if (state == QueryPressureState .CRITICAL ) {
184- System .gc ();
185192 cancelOneHeavyQueryIfNeeded (configuration , snapshot , "critical-checkpoint:" + operator );
186193 }
187194 if (handle .isCancelRequested ()) {
188195 throw CircuitBreakerException .cancelled (handle .getCancellationState (), configuration .getRetryAfterSeconds (),
189196 handle .getCancellationReason ());
190197 }
191- if (state .throttlesRunningQueries () && configuration .getCheckpointDelayMs () > 0 && (throttleCount ++)%256 ==0 ) {
198+ if (state .throttlesRunningQueries () && configuration .getCheckpointDelayMs () > 0
199+ && (throttleCount ++) % 256 == 0 ) {
192200 delay (configuration .getCheckpointDelayMs (), state , configuration , true );
193201 }
194202 }
@@ -262,9 +270,7 @@ private QueryPressureState refreshState(String reason, Configuration configurati
262270 LOGGER .info (
263271 "Query circuit breaker transition previous={} current={} freeMb={} rollingGcMs={} reason={}" ,
264272 previous , next , snapshot .getFreeMemoryMb (), snapshot .getRollingGcMs (), reason );
265- if (currentState == QueryPressureState .HIGH
266- || (currentState == QueryPressureState .WARN && determineFreeMemoryState (configuration ,
267- snapshot .getFreeMemoryMb ()) == QueryPressureState .WARN )) {
273+ if (!isCheckpointReason (reason ) && shouldRequestTransitionGc (currentState , configuration , snapshot )) {
268274 System .gc ();
269275 }
270276 }
@@ -305,6 +311,48 @@ private boolean throttlesNewQueriesAtEntry(QueryPressureState state, Configurati
305311 || state .rejectsNewQueries ();
306312 }
307313
314+ private boolean isCheckpointReason (String reason ) {
315+ return reason .startsWith ("checkpoint:" );
316+ }
317+
318+ private boolean shouldRequestTransitionGc (QueryPressureState state , Configuration configuration ,
319+ QueryPressureMonitor .Snapshot snapshot ) {
320+ return state == QueryPressureState .HIGH
321+ || (state == QueryPressureState .WARN
322+ && determineFreeMemoryState (configuration ,
323+ snapshot .getFreeMemoryMb ()) == QueryPressureState .WARN );
324+ }
325+
326+ private boolean shouldRequestCheckpointGc (QueryPressureState state , Configuration configuration ,
327+ QueryPressureMonitor .Snapshot snapshot ) {
328+ return state == QueryPressureState .CRITICAL || shouldRequestTransitionGc (state , configuration , snapshot );
329+ }
330+
331+ private void maybeRunCheckpointGc () {
332+ long now = clock .getAsLong ();
333+ if (!shouldRunCheckpointGc (now )) {
334+ return ;
335+ }
336+ gcInvoker .runGc ();
337+ }
338+
339+ private synchronized boolean shouldRunCheckpointGc (long now ) {
340+ if (lastCheckpointGcAt == Long .MIN_VALUE || now - lastCheckpointGcAt > CHECKPOINT_GC_RESET_AFTER_MS ) {
341+ lastCheckpointGcAt = now ;
342+ checkpointGcIntervalStep = 1 ;
343+ return true ;
344+ }
345+
346+ long requiredDelayMs = checkpointGcIntervalStep * CHECKPOINT_GC_BASE_INTERVAL_MS ;
347+ if (now - lastCheckpointGcAt < requiredDelayMs ) {
348+ return false ;
349+ }
350+
351+ lastCheckpointGcAt = now ;
352+ checkpointGcIntervalStep = Math .min (checkpointGcIntervalStep + 1 , CHECKPOINT_GC_MAX_INTERVAL_STEPS );
353+ return true ;
354+ }
355+
308356 private boolean shouldLogThrottleWarning (AtomicLong lastWarningAt ) {
309357 long now = clock .getAsLong ();
310358 while (true ) {
@@ -728,7 +776,7 @@ private Configuration(boolean enabled, int warnGcMs, int highGcMs, int criticalG
728776 }
729777
730778 static Configuration fromSystemProperties () {
731- return new Configuration (Boolean .parseBoolean (System .getProperty (ENABLED_PROPERTY , "true " )),
779+ return new Configuration (Boolean .parseBoolean (System .getProperty (ENABLED_PROPERTY , "false " )),
732780 getIntProperty (WARN_GC_MS_PROPERTY , 400 ),
733781 getIntProperty (HIGH_GC_MS_PROPERTY , 600 ),
734782 getIntProperty (CRITICAL_GC_MS_PROPERTY , 800 ),
0 commit comments