Skip to content

Commit 0b77ff1

Browse files
johntomcat7408-cmykxxubai
authored andcommitted
[AMORO-4303][AMS] Stabilize optimizer heartbeat tests (#4307)
(cherry picked from commit 4953d9c)
1 parent 7495046 commit 0b77ff1

3 files changed

Lines changed: 66 additions & 24 deletions

File tree

amoro-ams/src/main/java/org/apache/amoro/server/DefaultOptimizingService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,9 @@ public void run() {
591591
}
592592
} catch (InterruptedException ignored) {
593593
} catch (Throwable t) {
594-
LOG.error("{} has encountered a problem.", this.getClass().getSimpleName(), t);
594+
if (!stopped) {
595+
LOG.error("{} has encountered a problem.", this.getClass().getSimpleName(), t);
596+
}
595597
}
596598
}
597599
}

amoro-ams/src/test/java/org/apache/amoro/server/AMSServiceTestBase.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,24 @@
3333
import java.time.Duration;
3434

3535
public abstract class AMSServiceTestBase extends AMSManagerTestBase {
36+
// Normal optimizer tests may block in pollTask for 3 seconds. Keep the heartbeat timeout well
37+
// outside that window so CI scheduling delays do not turn unrelated tests into expiry tests.
38+
private static final Duration DEFAULT_OPTIMIZER_HEARTBEAT_TIMEOUT = Duration.ofSeconds(10);
39+
3640
private static DefaultTableService TABLE_SERVICE = null;
3741
private static DefaultOptimizingService OPTIMIZING_SERVICE = null;
3842
private static ProcessService PROCESS_SERVICE = null;
3943

4044
@BeforeClass
4145
public static void initTableService() {
46+
initTableService(DEFAULT_OPTIMIZER_HEARTBEAT_TIMEOUT);
47+
}
48+
49+
protected static void initTableService(Duration optimizerHeartbeatTimeout) {
4250
DefaultTableRuntimeFactory runtimeFactory = new DefaultTableRuntimeFactory();
4351
try {
4452
Configurations configurations = new Configurations();
45-
configurations.set(AmoroManagementConf.OPTIMIZER_HB_TIMEOUT, Duration.ofMillis(800L));
53+
configurations.set(AmoroManagementConf.OPTIMIZER_HB_TIMEOUT, optimizerHeartbeatTimeout);
4654
configurations.set(
4755
AmoroManagementConf.OPTIMIZER_TASK_EXECUTE_TIMEOUT, Duration.ofMillis(30000L));
4856
// must stay above OPTIMIZER_POLLING_TIMEOUT (3s): a blocking pollTask waiting out its full
@@ -78,6 +86,8 @@ public static void initTableService() {
7886
@AfterClass
7987
public static void disposeTableService() {
8088
TABLE_SERVICE.dispose();
89+
OPTIMIZING_SERVICE.dispose();
90+
PROCESS_SERVICE.dispose();
8191
MetricManager.dispose();
8292
EventsManager.dispose();
8393
}

amoro-ams/src/test/java/org/apache/amoro/server/TestDefaultOptimizingService.java

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,22 @@
7070
import org.junit.runner.RunWith;
7171
import org.junit.runners.Parameterized;
7272

73+
import java.time.Duration;
7374
import java.util.ArrayList;
7475
import java.util.List;
7576
import java.util.Map;
7677

7778
@RunWith(Parameterized.class)
7879
public class TestDefaultOptimizingService extends AMSTableTestBase {
7980

81+
private static final Duration EXPIRATION_TEST_HEARTBEAT_TIMEOUT = Duration.ofMillis(800);
82+
private static final long OPTIMIZER_HEARTBEAT_INTERVAL_MS = 100;
83+
private static final long ASYNC_WAIT_TIMEOUT_MS = 10000;
84+
8085
private final int THREAD_ID = 0;
8186
private String token;
8287
private Toucher toucher;
88+
private boolean customHeartbeatTimeout;
8389

8490
@Parameterized.Parameters(name = "{0}, {1}")
8591
public static Object[] parameters() {
@@ -121,6 +127,12 @@ public void clear() {
121127
dropDatabase();
122128
} catch (Exception e) {
123129
// ignore
130+
} finally {
131+
if (customHeartbeatTimeout) {
132+
disposeTableService();
133+
initTableService();
134+
customHeartbeatTimeout = false;
135+
}
124136
}
125137
}
126138

@@ -255,20 +267,22 @@ public void testTouch() throws InterruptedException {
255267

256268
@Test
257269
public void testTouchTimeout() throws InterruptedException {
270+
rebootWithHeartbeatTimeout(EXPIRATION_TEST_HEARTBEAT_TIMEOUT);
258271
OptimizingTask task = optimizingService().pollTask(token, THREAD_ID);
259272
Assertions.assertNotNull(task);
273+
String expiredToken = token;
260274
toucher.stop();
261275
toucher = null;
262-
Thread.sleep(1000);
263-
Assertions.assertThrows(PluginRetryAuthException.class, () -> optimizingService().touch(token));
276+
waitForOptimizerExpiration(expiredToken, ASYNC_WAIT_TIMEOUT_MS);
264277
Assertions.assertThrows(
265-
PluginRetryAuthException.class, () -> optimizingService().pollTask(token, THREAD_ID));
278+
PluginRetryAuthException.class, () -> optimizingService().touch(expiredToken));
279+
Assertions.assertThrows(
280+
PluginRetryAuthException.class,
281+
() -> optimizingService().pollTask(expiredToken, THREAD_ID));
266282
// After optimizer expires, its tasks are immediately reset to PLANNED
267283
// because unregister happens before task scan in OptimizerKeeper
268-
assertTaskStatus(TaskRuntime.Status.PLANNED);
269-
token = optimizingService().authenticate(buildRegisterInfo());
284+
waitForTaskStatus(TaskRuntime.Status.PLANNED, ASYNC_WAIT_TIMEOUT_MS);
270285
toucher = new Toucher();
271-
Thread.sleep(1000);
272286
assertTaskStatus(TaskRuntime.Status.PLANNED);
273287
OptimizingTask task2 = optimizingService().pollTask(token, THREAD_ID);
274288
Assertions.assertEquals(task2.getTaskId(), task.getTaskId());
@@ -283,11 +297,10 @@ public void testTouchTimeout() throws InterruptedException {
283297
public void testRebootAndPoll() throws InterruptedException {
284298
OptimizingTask task = optimizingService().pollTask(token, THREAD_ID);
285299
Assertions.assertNotNull(task);
286-
reboot();
300+
rebootWithHeartbeatTimeout(EXPIRATION_TEST_HEARTBEAT_TIMEOUT);
287301

288302
// wait for last optimizer expiring
289-
Thread.sleep(1000);
290-
assertTaskStatus(TaskRuntime.Status.PLANNED);
303+
waitForTaskStatus(TaskRuntime.Status.PLANNED, ASYNC_WAIT_TIMEOUT_MS);
291304
OptimizingTask task2 = optimizingService().pollTask(token, THREAD_ID);
292305
Assertions.assertNotNull(task2);
293306
Assertions.assertEquals(task2.getTaskId(), task.getTaskId());
@@ -329,10 +342,10 @@ public void testAckTimeoutResetThenLateAckRejected() throws InterruptedException
329342
Assertions.assertNotNull(task);
330343
assertTaskStatus(TaskRuntime.Status.SCHEDULED); // polled but NOT acked
331344

332-
// the optimizer stays alive (Toucher touches every 300ms), so waiting past the ack timeout hits
345+
// the optimizer stays alive, so waiting past the ack timeout hits
333346
// the SCHEDULED + ackTimeout branch rather than the optimizer-expired branch: the keeper resets
334347
// the task out from under the live optimizer
335-
waitForTaskStatus(TaskRuntime.Status.PLANNED, 10000);
348+
waitForTaskStatus(TaskRuntime.Status.PLANNED, 20000);
336349

337350
// the delayed ack arrives for the now-reset task -> rejected, exactly like the issue
338351
Assertions.assertThrows(
@@ -351,11 +364,8 @@ public void testExecuteTaskTimeOutAndRetry() throws InterruptedException {
351364
optimizingService().listTasks(defaultResourceGroup().getName()).get(0);
352365
assertTaskStatus(TaskRuntime.Status.ACKED);
353366

354-
// In this test, OPTIMIZER_TASK_EXECUTE_TIMEOUT is set to 30 seconds, so after waiting 45
355-
// seconds the task will be considered suspended and retried
356-
Thread.sleep(45000);
357-
358-
assertTaskStatus(TaskRuntime.Status.PLANNED);
367+
// In this test, OPTIMIZER_TASK_EXECUTE_TIMEOUT is set to 30 seconds.
368+
waitForTaskStatus(TaskRuntime.Status.PLANNED, 60000);
359369
OptimizingTask task2 = optimizingService().pollTask(token, THREAD_ID);
360370
Assertions.assertNotNull(task2);
361371
Assertions.assertEquals(task2.getTaskId(), task.getTaskId());
@@ -706,7 +716,9 @@ public void testHandleConfigChangedGroupNotExist() {
706716
private OptimizerRegisterInfo buildRegisterInfo() {
707717
OptimizerRegisterInfo registerInfo = new OptimizerRegisterInfo();
708718
Map<String, String> registerProperties = Maps.newHashMap();
709-
registerProperties.put(OptimizerProperties.OPTIMIZER_HEART_BEAT_INTERVAL, "100");
719+
registerProperties.put(
720+
OptimizerProperties.OPTIMIZER_HEART_BEAT_INTERVAL,
721+
String.valueOf(OPTIMIZER_HEARTBEAT_INTERVAL_MS));
710722
registerInfo.setProperties(registerProperties);
711723
registerInfo.setThreadCount(1);
712724
registerInfo.setMemoryMb(1024);
@@ -785,6 +797,23 @@ private void waitForTaskStatus(TaskRuntime.Status expectedStatus, long timeoutMs
785797
assertTaskStatus(expectedStatus);
786798
}
787799

800+
private void waitForOptimizerExpiration(String optimizerToken, long timeoutMs)
801+
throws InterruptedException {
802+
long deadline = System.currentTimeMillis() + timeoutMs;
803+
while (System.currentTimeMillis() < deadline) {
804+
boolean optimizerExists =
805+
optimizerManager().listOptimizers().stream()
806+
.anyMatch(optimizer -> optimizerToken.equals(optimizer.getToken()));
807+
boolean optimizerAuthenticated =
808+
optimizingService().getTotalQuota(defaultResourceGroup().getName()) > 0;
809+
if (!optimizerExists && !optimizerAuthenticated) {
810+
return;
811+
}
812+
Thread.sleep(100);
813+
}
814+
Assertions.fail("Optimizer did not expire within " + timeoutMs + " ms");
815+
}
816+
788817
private void assertTaskCompleted(TaskRuntime<?> taskRuntime) {
789818
if (taskRuntime != null) {
790819
Assertions.assertEquals(TaskRuntime.Status.SUCCESS, taskRuntime.getStatus());
@@ -798,17 +827,18 @@ private void assertTaskCompleted(TaskRuntime<?> taskRuntime) {
798827
}
799828

800829
protected void reload() {
801-
disposeTableService();
802830
toucher.suspend();
831+
disposeTableService();
803832
initTableService();
804833
toucher.goOn();
805834
}
806835

807-
protected void reboot() throws InterruptedException {
808-
disposeTableService();
836+
protected void rebootWithHeartbeatTimeout(Duration heartbeatTimeout) throws InterruptedException {
809837
toucher.stop();
810838
toucher = null;
811-
initTableService();
839+
disposeTableService();
840+
customHeartbeatTimeout = true;
841+
initTableService(heartbeatTimeout);
812842
toucher = new Toucher();
813843
}
814844

@@ -855,7 +885,7 @@ public synchronized void goOn() {
855885
public void run() {
856886
while (!stop) {
857887
try {
858-
Thread.sleep(300);
888+
Thread.sleep(OPTIMIZER_HEARTBEAT_INTERVAL_MS);
859889
synchronized (this) {
860890
if (!suspend) {
861891
optimizingService().touch(token);

0 commit comments

Comments
 (0)