1717
1818package org .apache .hugegraph .unit .api .filter ;
1919
20+ import java .io .Serializable ;
21+ import java .util .ArrayDeque ;
22+ import java .util .ArrayList ;
23+ import java .util .Deque ;
2024import java .util .List ;
2125import java .util .stream .Collectors ;
2226
2933import org .apache .hugegraph .testutil .Assert ;
3034import org .apache .hugegraph .testutil .Whitebox ;
3135import org .apache .hugegraph .unit .BaseUnitTest ;
36+ import org .apache .logging .log4j .Level ;
37+ import org .apache .logging .log4j .LogManager ;
38+ import org .apache .logging .log4j .core .Filter ;
39+ import org .apache .logging .log4j .core .Layout ;
40+ import org .apache .logging .log4j .core .LogEvent ;
41+ import org .apache .logging .log4j .core .Logger ;
42+ import org .apache .logging .log4j .core .appender .AbstractAppender ;
43+ import org .apache .logging .log4j .core .config .Property ;
44+ import org .junit .After ;
3245import org .junit .Before ;
3346import org .junit .Test ;
3447import org .mockito .Mockito ;
4154
4255public class LoadDetectFilterTest extends BaseUnitTest {
4356
57+ private static final Logger TEST_LOGGER =
58+ (Logger ) LogManager .getLogger (LoadDetectFilter .class );
59+
4460 private LoadDetectFilter loadDetectFilter ;
4561 private ContainerRequestContext requestContext ;
4662 private UriInfo uriInfo ;
4763 private WorkLoad workLoad ;
64+ private TestAppender testAppender ;
4865
4966 @ Before
5067 public void setup () {
5168 this .requestContext = Mockito .mock (ContainerRequestContext .class );
5269 this .uriInfo = Mockito .mock (UriInfo .class );
5370 this .workLoad = new WorkLoad ();
71+ this .testAppender = new TestAppender ();
72+ this .testAppender .start ();
73+ TEST_LOGGER .addAppender (this .testAppender );
5474
5575 Mockito .when (this .requestContext .getUriInfo ()).thenReturn (this .uriInfo );
5676 Mockito .when (this .requestContext .getMethod ()).thenReturn ("GET" );
5777
58- this .loadDetectFilter = new LoadDetectFilter ();
78+ this .loadDetectFilter = new TestLoadDetectFilter ();
5979 this .setLoadProvider (this .workLoad );
6080 this .setConfigProvider (createConfig (8 , 0 ));
6181 }
6282
83+ @ After
84+ public void teardown () {
85+ TEST_LOGGER .removeAppender (this .testAppender );
86+ this .testAppender .stop ();
87+ }
88+
6389 @ Test
6490 public void testFilter_WhiteListPathIgnored () {
6591 setupPath ("" , List .of ("" ));
@@ -68,6 +94,7 @@ public void testFilter_WhiteListPathIgnored() {
6894 this .loadDetectFilter .filter (this .requestContext );
6995
7096 Assert .assertEquals (1 , this .workLoad .get ().get ());
97+ Assert .assertTrue (this .testAppender .events ().isEmpty ());
7198 }
7299
73100 @ Test
@@ -85,13 +112,19 @@ public void testFilter_RejectsWhenWorkerLoadIsTooHigh() {
85112 exception .getMessage ());
86113 Assert .assertContains (ServerOptions .MAX_WORKER_THREADS .name (),
87114 exception .getMessage ());
115+ Assert .assertEquals (1 , this .testAppender .events ().size ());
116+ this .assertWarnLogContains ("Rejected request due to high worker load" );
117+ this .assertWarnLogContains ("method=GET" );
118+ this .assertWarnLogContains ("path=graphs/hugegraph/vertices" );
119+ this .assertWarnLogContains ("currentLoad=2" );
88120 }
89121
90122 @ Test
91123 public void testFilter_RejectsWhenFreeMemoryIsTooLow () {
92124 setupPath ("graphs/hugegraph/vertices" ,
93125 List .of ("graphs" , "hugegraph" , "vertices" ));
94126 this .setConfigProvider (createConfig (8 , Integer .MAX_VALUE ));
127+ this .setGcTriggered (false );
95128
96129 ServiceUnavailableException exception = (ServiceUnavailableException ) Assert .assertThrows (
97130 ServiceUnavailableException .class ,
@@ -101,6 +134,11 @@ public void testFilter_RejectsWhenFreeMemoryIsTooLow() {
101134 exception .getMessage ());
102135 Assert .assertContains (ServerOptions .MIN_FREE_MEMORY .name (),
103136 exception .getMessage ());
137+ Assert .assertEquals (1 , this .testAppender .events ().size ());
138+ this .assertWarnLogContains ("Rejected request due to low free memory" );
139+ this .assertWarnLogContains ("method=GET" );
140+ this .assertWarnLogContains ("path=graphs/hugegraph/vertices" );
141+ this .assertWarnLogContains ("gcTriggered=false" );
104142 }
105143
106144 @ Test
@@ -112,6 +150,26 @@ public void testFilter_AllowsRequestWhenLoadAndMemoryAreHealthy() {
112150 this .loadDetectFilter .filter (this .requestContext );
113151
114152 Assert .assertEquals (1 , this .workLoad .get ().get ());
153+ Assert .assertTrue (this .testAppender .events ().isEmpty ());
154+ }
155+
156+ @ Test
157+ public void testFilter_RejectLogIsRateLimited () {
158+ setupPath ("graphs/hugegraph/vertices" ,
159+ List .of ("graphs" , "hugegraph" , "vertices" ));
160+ this .setConfigProvider (createConfig (2 , 0 ));
161+ this .setAllowRejectLogs (true , false );
162+
163+ this .workLoad .incrementAndGet ();
164+ Assert .assertThrows (ServiceUnavailableException .class ,
165+ () -> this .loadDetectFilter .filter (this .requestContext ));
166+
167+ this .workLoad .get ().set (1 );
168+ Assert .assertThrows (ServiceUnavailableException .class ,
169+ () -> this .loadDetectFilter .filter (this .requestContext ));
170+
171+ Assert .assertEquals (1 , this .testAppender .events ().size ());
172+ this .assertWarnLogContains ("Rejected request due to high worker load" );
115173 }
116174
117175 private HugeConfig createConfig (int maxWorkerThreads , int minFreeMemory ) {
@@ -144,4 +202,71 @@ private void setConfigProvider(HugeConfig config) {
144202 Whitebox .setInternalState (this .loadDetectFilter , "configProvider" ,
145203 (Provider <HugeConfig >) () -> config );
146204 }
205+
206+ private void setGcTriggered (boolean gcTriggered ) {
207+ ((TestLoadDetectFilter ) this .loadDetectFilter ).gcTriggered (gcTriggered );
208+ }
209+
210+ private void setAllowRejectLogs (boolean ... allowedLogs ) {
211+ ((TestLoadDetectFilter ) this .loadDetectFilter ).allowRejectLogs (allowedLogs );
212+ }
213+
214+ private void assertWarnLogContains (String expectedContent ) {
215+ Assert .assertFalse (this .testAppender .events ().isEmpty ());
216+ LogEvent event = this .testAppender .events ().get (0 );
217+ Assert .assertEquals (Level .WARN , event .getLevel ());
218+ Assert .assertContains (expectedContent ,
219+ event .getMessage ().getFormattedMessage ());
220+ }
221+
222+ private static class TestLoadDetectFilter extends LoadDetectFilter {
223+
224+ private boolean gcTriggered ;
225+ private final Deque <Boolean > allowRejectLogs = new ArrayDeque <>();
226+
227+ public void gcTriggered (boolean gcTriggered ) {
228+ this .gcTriggered = gcTriggered ;
229+ }
230+
231+ public void allowRejectLogs (boolean ... allowedLogs ) {
232+ this .allowRejectLogs .clear ();
233+ for (boolean allowedLog : allowedLogs ) {
234+ this .allowRejectLogs .addLast (allowedLog );
235+ }
236+ }
237+
238+ @ Override
239+ protected boolean gcIfNeeded () {
240+ return this .gcTriggered ;
241+ }
242+
243+ @ Override
244+ protected boolean allowRejectLog () {
245+ if (this .allowRejectLogs .isEmpty ()) {
246+ return true ;
247+ }
248+ return this .allowRejectLogs .removeFirst ();
249+ }
250+ }
251+
252+ private static class TestAppender extends AbstractAppender {
253+
254+ private final List <LogEvent > events ;
255+
256+ protected TestAppender () {
257+ super ("LoadDetectFilterTestAppender" , (Filter ) null ,
258+ (Layout <? extends Serializable >) null , false ,
259+ Property .EMPTY_ARRAY );
260+ this .events = new ArrayList <>();
261+ }
262+
263+ @ Override
264+ public void append (LogEvent event ) {
265+ this .events .add (event .toImmutable ());
266+ }
267+
268+ public List <LogEvent > events () {
269+ return this .events ;
270+ }
271+ }
147272}
0 commit comments