1919
2020import java .util .List ;
2121import java .util .Set ;
22+ import java .util .function .BooleanSupplier ;
23+ import java .util .function .LongSupplier ;
2224
2325import org .apache .hugegraph .config .HugeConfig ;
2426import org .apache .hugegraph .config .ServerOptions ;
2527import org .apache .hugegraph .define .WorkLoad ;
2628import org .apache .hugegraph .util .Bytes ;
2729import org .apache .hugegraph .util .E ;
30+ import org .apache .hugegraph .util .Log ;
31+ import org .slf4j .Logger ;
2832
2933import com .google .common .collect .ImmutableSet ;
3034import com .google .common .util .concurrent .RateLimiter ;
4347@ PreMatching
4448public class LoadDetectFilter implements ContainerRequestFilter {
4549
50+ private static final Logger LOG = Log .logger (LoadDetectFilter .class );
51+
4652 private static final Set <String > WHITE_API_LIST = ImmutableSet .of (
4753 "" ,
4854 "apis" ,
@@ -54,10 +60,44 @@ public class LoadDetectFilter implements ContainerRequestFilter {
5460 private static final RateLimiter GC_RATE_LIMITER =
5561 RateLimiter .create (1.0 / 30 );
5662
63+ // Log at most 1 request per second to avoid too many logs when server is under heavy load
64+ private static final RateLimiter BUSY_REJECT_LOG_RATE_LIMITER =
65+ RateLimiter .create (1.0 );
66+ private static final RateLimiter MEMORY_REJECT_LOG_RATE_LIMITER =
67+ RateLimiter .create (1.0 );
68+
5769 @ Context
5870 private jakarta .inject .Provider <HugeConfig > configProvider ;
5971 @ Context
6072 private jakarta .inject .Provider <WorkLoad > loadProvider ;
73+ private BooleanSupplier gcTrigger = LoadDetectFilter ::triggerGcIfNeeded ;
74+ private BooleanSupplier busyRejectLogPermit =
75+ BUSY_REJECT_LOG_RATE_LIMITER ::tryAcquire ;
76+ private BooleanSupplier memoryRejectLogPermit =
77+ MEMORY_REJECT_LOG_RATE_LIMITER ::tryAcquire ;
78+ private LongSupplier freeMemorySupplier = LoadDetectFilter ::currentFreeMemoryInMB ;
79+
80+ public static boolean isWhiteAPI (ContainerRequestContext context ) {
81+ List <PathSegment > segments = context .getUriInfo ().getPathSegments ();
82+ E .checkArgument (!segments .isEmpty (), "Invalid request uri '%s'" ,
83+ context .getUriInfo ().getPath ());
84+ String rootPath = segments .get (0 ).getPath ();
85+ return WHITE_API_LIST .contains (rootPath );
86+ }
87+
88+ private static boolean triggerGcIfNeeded () {
89+ if (GC_RATE_LIMITER .tryAcquire (1 )) {
90+ System .gc ();
91+ return true ;
92+ }
93+ return false ;
94+ }
95+
96+ private static long currentFreeMemoryInMB () {
97+ long allocatedMem = Runtime .getRuntime ().totalMemory () -
98+ Runtime .getRuntime ().freeMemory ();
99+ return (Runtime .getRuntime ().maxMemory () - allocatedMem ) / Bytes .MB ;
100+ }
61101
62102 @ Override
63103 public void filter (ContainerRequestContext context ) {
@@ -70,20 +110,50 @@ public void filter(ContainerRequestContext context) {
70110 int maxWorkerThreads = config .get (ServerOptions .MAX_WORKER_THREADS );
71111 WorkLoad load = this .loadProvider .get ();
72112 // There will be a thread doesn't work, dedicated to statistics
73- if (load .incrementAndGet () >= maxWorkerThreads ) {
113+ int currentLoad = load .incrementAndGet ();
114+ if (currentLoad >= maxWorkerThreads ) {
115+ if (this .busyRejectLogPermit .getAsBoolean ()) {
116+ LOG .warn ("Rejected request due to high worker load, method={}, path={}, " +
117+ "currentLoad={}, maxWorkerThreads={}" ,
118+ context .getMethod (), context .getUriInfo ().getPath (),
119+ currentLoad , maxWorkerThreads );
120+ }
74121 throw new ServiceUnavailableException (String .format (
75122 "The server is too busy to process the request, " +
76123 "you can config %s to adjust it or try again later" ,
77124 ServerOptions .MAX_WORKER_THREADS .name ()));
78125 }
79126
80127 long minFreeMemory = config .get (ServerOptions .MIN_FREE_MEMORY );
81- long allocatedMem = Runtime .getRuntime ().totalMemory () -
82- Runtime .getRuntime ().freeMemory ();
83- long presumableFreeMem = (Runtime .getRuntime ().maxMemory () -
84- allocatedMem ) / Bytes .MB ;
128+ long presumableFreeMem = this .freeMemorySupplier .getAsLong ();
85129 if (presumableFreeMem < minFreeMemory ) {
86- gcIfNeeded ();
130+ boolean gcTriggered = this .gcTrigger .getAsBoolean ();
131+ if (gcTriggered ) {
132+ long recheckedFreeMem = this .freeMemorySupplier .getAsLong ();
133+ if (recheckedFreeMem >= minFreeMemory ) {
134+ if (this .memoryRejectLogPermit .getAsBoolean ()) {
135+ LOG .warn ("Low free memory recovered after GC, method={}, path={}, " +
136+ "presumableFreeMemMB={}, recheckedFreeMemMB={}, " +
137+ "minFreeMemoryMB={}" ,
138+ context .getMethod (), context .getUriInfo ().getPath (),
139+ presumableFreeMem , recheckedFreeMem , minFreeMemory );
140+ }
141+ return ;
142+ }
143+ if (this .memoryRejectLogPermit .getAsBoolean ()) {
144+ LOG .warn ("Rejected request due to low free memory after GC, " +
145+ "method={}, path={}, presumableFreeMemMB={}, " +
146+ "recheckedFreeMemMB={}, minFreeMemoryMB={}" ,
147+ context .getMethod (), context .getUriInfo ().getPath (),
148+ presumableFreeMem , recheckedFreeMem , minFreeMemory );
149+ }
150+ presumableFreeMem = recheckedFreeMem ;
151+ } else if (this .memoryRejectLogPermit .getAsBoolean ()) {
152+ LOG .warn ("Rejected request due to low free memory, method={}, path={}, " +
153+ "presumableFreeMemMB={}, minFreeMemoryMB={}" ,
154+ context .getMethod (), context .getUriInfo ().getPath (),
155+ presumableFreeMem , minFreeMemory );
156+ }
87157 throw new ServiceUnavailableException (String .format (
88158 "The server available memory %s(MB) is below than " +
89159 "threshold %s(MB) and can't process the request, " +
@@ -92,18 +162,4 @@ public void filter(ContainerRequestContext context) {
92162 ServerOptions .MIN_FREE_MEMORY .name ()));
93163 }
94164 }
95-
96- public static boolean isWhiteAPI (ContainerRequestContext context ) {
97- List <PathSegment > segments = context .getUriInfo ().getPathSegments ();
98- E .checkArgument (!segments .isEmpty (), "Invalid request uri '%s'" ,
99- context .getUriInfo ().getPath ());
100- String rootPath = segments .get (0 ).getPath ();
101- return WHITE_API_LIST .contains (rootPath );
102- }
103-
104- private static void gcIfNeeded () {
105- if (GC_RATE_LIMITER .tryAcquire (1 )) {
106- System .gc ();
107- }
108- }
109165}
0 commit comments