优化
This commit is contained in:
+125
-3
@@ -1,10 +1,13 @@
|
||||
package com.baiyue.eventbridge.service;
|
||||
|
||||
import com.baiyue.common.event.EventTypes;
|
||||
import com.baiyue.eventbridge.config.EventDispatcherProperties;
|
||||
import com.baiyue.eventbridge.domain.BridgeDeliveryAttempt;
|
||||
import com.baiyue.eventbridge.domain.BridgeInbox;
|
||||
import com.baiyue.eventbridge.mapper.BridgeDeliveryAttemptMapper;
|
||||
import com.baiyue.eventbridge.mapper.BridgeInboxMapper;
|
||||
import com.baiyue.eventbridge.metrics.EventBridgeMetrics;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
@@ -12,6 +15,7 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -21,10 +25,14 @@ import static org.mockito.Mockito.*;
|
||||
class OverseasEventDispatcherTest {
|
||||
private final BridgeInboxMapper inboxMapper = mock(BridgeInboxMapper.class);
|
||||
private final BridgeDeliveryAttemptMapper attemptMapper = mock(BridgeDeliveryAttemptMapper.class);
|
||||
private final LastlegEventClient eventClient = mock(LastlegEventClient.class);
|
||||
private final FirstlegEventClient firstlegEventClient = mock(FirstlegEventClient.class);
|
||||
private final com.baiyue.eventbridge.service.LastlegEventClient eventClient =
|
||||
mock(com.baiyue.eventbridge.service.LastlegEventClient.class);
|
||||
private final com.baiyue.eventbridge.service.FirstlegEventClient firstlegEventClient =
|
||||
mock(com.baiyue.eventbridge.service.FirstlegEventClient.class);
|
||||
private final SecurityProjectionEventClient securityProjectionEventClient = mock(SecurityProjectionEventClient.class);
|
||||
private OverseasEventDispatcher dispatcher;
|
||||
private BridgeInbox event;
|
||||
private SimpleMeterRegistry registry;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
@@ -33,16 +41,21 @@ class OverseasEventDispatcherTest {
|
||||
Duration.ofSeconds(30), Duration.ofSeconds(5), Duration.ofMinutes(1),
|
||||
Duration.ofSeconds(3), Duration.ofSeconds(10));
|
||||
dispatcher = new OverseasEventDispatcher();
|
||||
registry = new SimpleMeterRegistry();
|
||||
ReflectionTestUtils.setField(dispatcher, "inboxMapper", inboxMapper);
|
||||
ReflectionTestUtils.setField(dispatcher, "attemptMapper", attemptMapper);
|
||||
ReflectionTestUtils.setField(dispatcher, "eventClient", eventClient);
|
||||
ReflectionTestUtils.setField(dispatcher, "firstlegEventClient", firstlegEventClient);
|
||||
ReflectionTestUtils.setField(dispatcher, "securityProjectionEventClient", securityProjectionEventClient);
|
||||
ReflectionTestUtils.setField(dispatcher, "properties", properties);
|
||||
ReflectionTestUtils.setField(dispatcher, "metrics", new EventBridgeMetrics(registry));
|
||||
event = new BridgeInbox();
|
||||
event.setEventId("event-1");
|
||||
event.setEventType(EventTypes.FIRST_LEG_LOADING_PLAN_COMPLETED);
|
||||
event.setSourceRegion("CN");
|
||||
event.setTargetRegion("OVERSEAS");
|
||||
event.setRetryCount(0);
|
||||
event.setReceivedAt(LocalDateTime.now().minusSeconds(5));
|
||||
when(inboxMapper.claim(eq("event-1"), anyString(), anyLong())).thenReturn(1);
|
||||
when(inboxMapper.selectById("event-1")).thenReturn(event);
|
||||
when(inboxMapper.nextAttemptNo("event-1")).thenReturn(1);
|
||||
@@ -56,27 +69,50 @@ class OverseasEventDispatcherTest {
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verify(inboxMapper).markProcessed(eq("event-1"), anyString());
|
||||
verify(eventClient).deliver(event);
|
||||
verifyNoInteractions(securityProjectionEventClient);
|
||||
assertThat(updatedAttempt().getStatus()).isEqualTo("DELIVERED");
|
||||
assertThat(transportCount("DELIVERED")).isEqualTo(1.0);
|
||||
assertThat(registry.get(EventBridgeMetrics.DELIVERY_LATENCY_METRIC).timer().count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedDeliveryIsScheduledForRetry() throws Exception {
|
||||
when(eventClient.deliver(event)).thenThrow(new IllegalStateException("network down"));
|
||||
when(inboxMapper.markFailed(eq("event-1"), anyString(), eq(3), anyLong(), eq("network down")))
|
||||
.thenReturn(1);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verify(inboxMapper).markFailed(eq("event-1"), anyString(), eq(3), anyLong(), eq("network down"));
|
||||
verify(inboxMapper).markFailed(eq("event-1"), anyString(), eq(3), eq(5L), eq("network down"));
|
||||
assertThat(updatedAttempt().getStatus()).isEqualTo("RETRY");
|
||||
assertThat(transportCount("RETRY")).isEqualTo(1.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void finalFailureIsMovedToDeadState() throws Exception {
|
||||
event.setRetryCount(2);
|
||||
when(eventClient.deliver(event)).thenThrow(new IllegalStateException("still down"));
|
||||
when(inboxMapper.markFailed(eq("event-1"), anyString(), eq(3), anyLong(), eq("still down")))
|
||||
.thenReturn(1);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verify(inboxMapper).markFailed(eq("event-1"), anyString(), eq(3), eq(20L), eq("still down"));
|
||||
assertThat(updatedAttempt().getStatus()).isEqualTo("DEAD");
|
||||
assertThat(transportCount("DEAD")).isEqualTo(1.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void exponentialBackoffIsCappedAtConfiguredMaximum() throws Exception {
|
||||
event.setRetryCount(4);
|
||||
when(eventClient.deliver(event)).thenThrow(new IllegalStateException("still down"));
|
||||
when(inboxMapper.markFailed(eq("event-1"), anyString(), eq(3), anyLong(), eq("still down")))
|
||||
.thenReturn(1);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verify(inboxMapper).markFailed(eq("event-1"), anyString(), eq(3), eq(60L), eq("still down"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -92,6 +128,7 @@ class OverseasEventDispatcherTest {
|
||||
|
||||
@Test
|
||||
void cnTargetIsDeliveredToFirstleg() throws Exception {
|
||||
event.setEventType(EventTypes.OVERSEAS_CONTAINER_UNLOADED);
|
||||
event.setSourceRegion("OVERSEAS");
|
||||
event.setTargetRegion("CN");
|
||||
when(firstlegEventClient.deliver(event)).thenReturn("200");
|
||||
@@ -101,9 +138,87 @@ class OverseasEventDispatcherTest {
|
||||
|
||||
verify(firstlegEventClient).deliver(event);
|
||||
verifyNoInteractions(eventClient);
|
||||
verifyNoInteractions(securityProjectionEventClient);
|
||||
assertThat(updatedAttempt().getStatus()).isEqualTo("DELIVERED");
|
||||
}
|
||||
|
||||
@Test
|
||||
void iamSecurityEventsAreDeliveredToSecurityProjectionAndNeverLastleg() throws Exception {
|
||||
for (String eventType : List.of(EventTypes.IAM_USER_STATUS_CHANGED,
|
||||
EventTypes.IAM_TENANT_STATUS_CHANGED, EventTypes.IAM_PERMISSION_VERSION_CHANGED)) {
|
||||
reset(inboxMapper, attemptMapper, eventClient, firstlegEventClient, securityProjectionEventClient);
|
||||
event.setEventType(eventType);
|
||||
when(inboxMapper.claim(eq("event-1"), anyString(), anyLong())).thenReturn(1);
|
||||
when(inboxMapper.selectById("event-1")).thenReturn(event);
|
||||
when(inboxMapper.nextAttemptNo("event-1")).thenReturn(1);
|
||||
when(inboxMapper.markProcessed(eq("event-1"), anyString())).thenReturn(1);
|
||||
when(securityProjectionEventClient.deliver(event)).thenReturn("200");
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verify(securityProjectionEventClient).deliver(event);
|
||||
verifyNoInteractions(eventClient);
|
||||
verifyNoInteractions(firstlegEventClient);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unsupportedTargetRegionIsNeverMisroutedToLastleg() {
|
||||
event.setTargetRegion("UNKNOWN");
|
||||
when(inboxMapper.markFailed(eq("event-1"), anyString(), eq(3), anyLong(), contains("不支持")))
|
||||
.thenReturn(1);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verifyNoInteractions(eventClient);
|
||||
verifyNoInteractions(firstlegEventClient);
|
||||
verifyNoInteractions(securityProjectionEventClient);
|
||||
assertThat(updatedAttempt().getStatus()).isEqualTo("RETRY");
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingEventTypeFailsWithExplicitDiagnosticAndIsNeverDelivered() {
|
||||
event.setEventType(null);
|
||||
when(inboxMapper.markFailed(eq("event-1"), anyString(), eq(3), anyLong(),
|
||||
eq("事件类型不能为空,无法选择下游消费者"))).thenReturn(1);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verifyNoInteractions(eventClient);
|
||||
verifyNoInteractions(firstlegEventClient);
|
||||
verifyNoInteractions(securityProjectionEventClient);
|
||||
BridgeDeliveryAttempt attempt = updatedAttempt();
|
||||
assertThat(attempt.getStatus()).isEqualTo("RETRY");
|
||||
assertThat(attempt.getErrorMessage()).isEqualTo("事件类型不能为空,无法选择下游消费者");
|
||||
}
|
||||
|
||||
@Test
|
||||
void acknowledgedDeliveryWithLostLeaseIsNotRecordedAsRetryOrDead() throws Exception {
|
||||
when(eventClient.deliver(event)).thenReturn("200");
|
||||
when(inboxMapper.markProcessed(eq("event-1"), anyString())).thenReturn(0);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
verify(inboxMapper, never()).markFailed(anyString(), anyString(), anyInt(), anyLong(), anyString());
|
||||
BridgeDeliveryAttempt attempt = updatedAttempt();
|
||||
assertThat(attempt.getStatus()).isEqualTo("ACKNOWLEDGED_LEASE_LOST");
|
||||
assertThat(attempt.getResponseCode()).isEqualTo("200");
|
||||
assertThat(attempt.getErrorMessage()).contains("租约已失效");
|
||||
assertThat(transportCount("ACKNOWLEDGED_LEASE_LOST")).isEqualTo(1.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void failedDeliveryAfterLeaseTakeoverDoesNotOverwriteNewOwnerState() throws Exception {
|
||||
when(eventClient.deliver(event)).thenThrow(new IllegalStateException("network down"));
|
||||
when(inboxMapper.markFailed(eq("event-1"), anyString(), eq(3), anyLong(), eq("network down")))
|
||||
.thenReturn(0);
|
||||
|
||||
dispatcher.dispatchOne("event-1");
|
||||
|
||||
assertThat(updatedAttempt().getStatus()).isEqualTo("FAILED_LEASE_LOST");
|
||||
assertThat(transportCount("FAILED_LEASE_LOST")).isEqualTo(1.0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void downstream503RetriesThenDeliversWithDiagnosticAttemptsInBothDirections() throws Exception {
|
||||
for (String target : List.of("OVERSEAS", "CN")) {
|
||||
@@ -162,4 +277,11 @@ class OverseasEventDispatcherTest {
|
||||
verify(attemptMapper).updateById(captor.capture());
|
||||
return captor.getValue();
|
||||
}
|
||||
|
||||
private double transportCount(String result) {
|
||||
return registry.get(EventBridgeMetrics.TRANSPORT_METRIC)
|
||||
.tags("target_region", event.getTargetRegion(),
|
||||
"event_type", event.getEventType(), "result", result)
|
||||
.counter().count();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user