Skip to content

Commit f4843bf

Browse files
authored
Merge pull request #125 from zalando-nakadi/backport-features-from-v20
Backport features from v20
2 parents 2f52dd4 + 22c01b6 commit f4843bf

File tree

15 files changed

+221
-49
lines changed

15 files changed

+221
-49
lines changed

CODEOWNERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# The two maintainers are code owners for everything.
2+
* @bgehrels @epaul

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,21 @@ The example above uses `com.jayway.jsonpath:json-path:jar:2.2.0` to parse and te
317317

318318
Note that you should disable the scheduled event transmission for the test (e.g. by setting `nakadi-producer.scheduled-transmission-enabled:false`), as that might interfere with the manual transmission and the clearing in the test setup, leading to events from one test showing up in the next test, depending on timing issues.
319319

320+
### Customizing event locks
321+
322+
* **lock-duration**: The selected events are locked before transmission. If the transmission fails the events stay locked
323+
until the lock expires. The default is currently 600 seconds but might change in future releases.
324+
325+
* **lock-duration-buffer**: Since clocks never work exactly synchronous and sending events also takes some time, a safety
326+
buffer is included. During the last x seconds before the expiration of the lock the events are not considered for
327+
transmission. The default is currently 60 seconds but might change in future releases.
328+
329+
```yaml
330+
nakadi-producer:
331+
lock-duration: 600
332+
lock-duration-buffer: 60
333+
```
334+
320335
## Contributing
321336

322337
We welcome contributions. Please have a look at our [contribution guidelines](CONTRIBUTING.md).

nakadi-producer-spring-boot-starter/pom.xml

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<parent>
1111
<groupId>org.zalando</groupId>
1212
<artifactId>nakadi-producer-reactor</artifactId>
13-
<version>4.3.0</version>
13+
<version>4.4.0</version>
1414
</parent>
1515

1616
<artifactId>nakadi-producer-spring-boot-starter</artifactId>
@@ -22,7 +22,6 @@
2222
<maven.compiler.source>1.8</maven.compiler.source>
2323
<maven.compiler.target>1.8</maven.compiler.target>
2424
<java.version>1.8</java.version>
25-
<zalando-swagger-codegen-maven-plugin.version>0.4.24</zalando-swagger-codegen-maven-plugin.version>
2625
</properties>
2726

2827
<dependencies>
@@ -53,13 +52,13 @@
5352
<dependency>
5453
<groupId>org.zalando</groupId>
5554
<artifactId>tracer-core</artifactId>
56-
<version>0.11.2</version>
55+
<version>0.17.1</version>
5756
<optional>true</optional>
5857
</dependency>
5958
<dependency>
6059
<groupId>org.zalando.stups</groupId>
6160
<artifactId>tokens</artifactId>
62-
<version>0.10.0</version>
61+
<version>0.12.2</version>
6362
<optional>true</optional>
6463
</dependency>
6564
<dependency>
@@ -98,9 +97,10 @@
9897
<scope>test</scope>
9998
</dependency>
10099
<dependency>
100+
<!-- needed for the optional spring actuator http endpoints -->
101101
<groupId>org.springframework.boot</groupId>
102102
<artifactId>spring-boot-starter-web</artifactId>
103-
<scope>test</scope>
103+
<optional>true</optional>
104104
</dependency>
105105
</dependencies>
106106

@@ -124,7 +124,6 @@
124124
<plugin>
125125
<groupId>org.apache.maven.plugins</groupId>
126126
<artifactId>maven-source-plugin</artifactId>
127-
<version>2.2.1</version>
128127
<executions>
129128
<execution>
130129
<id>attach-sources</id>
@@ -137,7 +136,6 @@
137136
<plugin>
138137
<groupId>org.apache.maven.plugins</groupId>
139138
<artifactId>maven-javadoc-plugin</artifactId>
140-
<version>2.9.1</version>
141139
<configuration>
142140
<additionalparam>-Xdoclint:none</additionalparam>
143141
</configuration>
@@ -153,7 +151,7 @@
153151
<plugin>
154152
<groupId>org.apache.maven.plugins</groupId>
155153
<artifactId>maven-gpg-plugin</artifactId>
156-
<version>1.5</version>
154+
<version>1.6</version>
157155
<executions>
158156
<execution>
159157
<id>sign-artifacts</id>
@@ -167,7 +165,7 @@
167165
<plugin>
168166
<groupId>org.sonatype.plugins</groupId>
169167
<artifactId>nexus-staging-maven-plugin</artifactId>
170-
<version>1.6.7</version>
168+
<version>1.6.8</version>
171169
<extensions>true</extensions>
172170
<configuration>
173171
<serverId>ossrh</serverId>

nakadi-producer-spring-boot-starter/src/main/java/org/zalando/nakadiproducer/NakadiProducerAutoConfiguration.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,8 @@ static class ManagementEndpointConfiguration {
116116
@Bean
117117
@ConditionalOnMissingBean
118118
public SnapshotEventCreationEndpoint snapshotEventCreationEndpoint(
119-
SnapshotCreationService snapshotCreationService) {
120-
return new SnapshotEventCreationEndpoint(snapshotCreationService);
119+
SnapshotCreationService snapshotCreationService, FlowIdComponent flowIdComponent) {
120+
return new SnapshotEventCreationEndpoint(snapshotCreationService, flowIdComponent);
121121
}
122122

123123
@Bean
@@ -197,11 +197,16 @@ public EventTransmissionScheduler eventTransmissionScheduler(EventTransmitter ev
197197
return new EventTransmissionScheduler(eventTransmitter, scheduledTransmissionEnabled);
198198
}
199199

200-
@Bean
201-
public EventTransmissionService eventTransmissionService(EventLogRepository eventLogRepository,
202-
NakadiPublishingClient nakadiPublishingClient, ObjectMapper objectMapper) {
203-
return new EventTransmissionService(eventLogRepository, nakadiPublishingClient, objectMapper);
204-
}
200+
@Bean
201+
public EventTransmissionService eventTransmissionService(
202+
EventLogRepository eventLogRepository,
203+
NakadiPublishingClient nakadiPublishingClient,
204+
ObjectMapper objectMapper,
205+
@Value("${nakadi-producer.lock-duration:600}") int lockDuration,
206+
@Value("${nakadi-producer.lock-duration-buffer:60}") int lockDurationBuffer) {
207+
return new EventTransmissionService(
208+
eventLogRepository, nakadiPublishingClient, objectMapper, lockDuration, lockDurationBuffer);
209+
}
205210

206211
@Bean
207212
public FlywayMigrator flywayMigrator() {

nakadi-producer-spring-boot-starter/src/main/java/org/zalando/nakadiproducer/flowid/NoopFlowIdComponent.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ public class NoopFlowIdComponent implements FlowIdComponent {
77

88
@Override
99
public String getXFlowIdValue() {
10-
log.debug("No bean of class FlowIdComponent was found. Returning null.");
1110
return null;
1211
}
12+
13+
@Override
14+
public void startTraceIfNoneExists() {
15+
}
1316
}

nakadi-producer-spring-boot-starter/src/main/java/org/zalando/nakadiproducer/flowid/TracerFlowIdComponent.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,21 @@ public String getXFlowIdValue() {
3737
}
3838
return null;
3939
}
40+
41+
@Override
42+
public void startTraceIfNoneExists() {
43+
if (tracer != null) {
44+
try {
45+
tracer.get(X_FLOW_ID).getValue();
46+
} catch (IllegalArgumentException e) {
47+
log.warn("No trace was configured for the name {}. Returning null. " +
48+
"To configure Tracer provide an application property: " +
49+
"tracer.traces.X-Flow-ID=flow-id", X_FLOW_ID);
50+
} catch (IllegalStateException e) {
51+
tracer.start();
52+
}
53+
} else {
54+
log.warn("No bean of class Tracer was found.");
55+
}
56+
}
4057
}

nakadi-producer-spring-boot-starter/src/main/java/org/zalando/nakadiproducer/snapshots/impl/SnapshotEventCreationEndpoint.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,17 @@
77

88
import lombok.AllArgsConstructor;
99
import lombok.Getter;
10+
import org.zalando.nakadiproducer.flowid.FlowIdComponent;
1011

1112
@ConfigurationProperties("endpoints.snapshot-event-creation")
1213
public class SnapshotEventCreationEndpoint extends AbstractEndpoint<SnapshotEventCreationEndpoint.SnapshotReport> {
1314
private final SnapshotCreationService snapshotCreationService;
15+
private final FlowIdComponent flowIdComponent;
1416

15-
public SnapshotEventCreationEndpoint(SnapshotCreationService snapshotCreationService) {
17+
public SnapshotEventCreationEndpoint(SnapshotCreationService snapshotCreationService, FlowIdComponent flowIdComponent) {
1618
super("snapshot_event_creation", true, true);
1719
this.snapshotCreationService = snapshotCreationService;
20+
this.flowIdComponent = flowIdComponent;
1821
}
1922

2023
@Override
@@ -23,6 +26,7 @@ public SnapshotReport invoke() {
2326
}
2427

2528
public void invoke(String eventType, String filter) {
29+
flowIdComponent.startTraceIfNoneExists();
2630
snapshotCreationService.createSnapshotEvents(eventType, filter);
2731
}
2832

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.zalando.nakadiproducer;
2+
3+
import org.junit.After;
4+
import org.junit.Before;
5+
import org.junit.Test;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.boot.test.context.SpringBootTest;
8+
import org.springframework.transaction.annotation.Transactional;
9+
import org.zalando.nakadiproducer.eventlog.EventLogWriter;
10+
import org.zalando.nakadiproducer.eventlog.impl.EventLog;
11+
import org.zalando.nakadiproducer.transmission.MockNakadiPublishingClient;
12+
import org.zalando.nakadiproducer.transmission.impl.EventTransmissionService;
13+
import org.zalando.nakadiproducer.transmission.impl.EventTransmitter;
14+
import org.zalando.nakadiproducer.util.Fixture;
15+
16+
import java.time.Clock;
17+
import java.time.Instant;
18+
import java.time.ZoneId;
19+
import java.util.Collection;
20+
21+
import static java.time.temporal.ChronoUnit.SECONDS;
22+
import static org.hamcrest.MatcherAssert.assertThat;
23+
import static org.hamcrest.Matchers.empty;
24+
import static org.hamcrest.Matchers.is;
25+
26+
@Transactional
27+
@SpringBootTest(properties = {
28+
"nakadi-producer.scheduled-transmission-enabled:false",
29+
"nakadi-producer.lock-duration:300",
30+
"nakadi-producer.lock-duration-buffer:30"})
31+
public class LockTimeoutIT extends BaseMockedExternalCommunicationIT {
32+
private static final String MY_EVENT_TYPE = "myEventType";
33+
34+
@Autowired
35+
private EventLogWriter eventLogWriter;
36+
37+
@Autowired
38+
private EventTransmitter eventTransmitter;
39+
40+
@Autowired
41+
private EventTransmissionService eventTransmissionService;
42+
43+
@Autowired
44+
private MockNakadiPublishingClient nakadiClient;
45+
46+
@Before
47+
@After
48+
public void clearNakadiEvents() {
49+
mockServiceClock(Instant.now());
50+
eventTransmitter.sendEvents();
51+
nakadiClient.clearSentEvents();
52+
}
53+
54+
@Test
55+
public void testLockedUntil() {
56+
eventLogWriter.fireBusinessEvent(MY_EVENT_TYPE, Fixture.mockPayload(1, "code123"));
57+
58+
Instant timeOfInitialLock = Instant.now();
59+
mockServiceClock(timeOfInitialLock);
60+
61+
assertThat(eventTransmissionService.lockSomeEvents().size(), is(1));
62+
assertThat(eventTransmissionService.lockSomeEvents(), empty());
63+
64+
// lock is still valid
65+
mockServiceClock(timeOfInitialLock.plus(300 - 5, SECONDS));
66+
assertThat(eventTransmissionService.lockSomeEvents(), empty());
67+
68+
// lock is expired
69+
mockServiceClock(timeOfInitialLock.plus(300 + 5, SECONDS));
70+
assertThat(eventTransmissionService.lockSomeEvents().size(), is(1));
71+
}
72+
73+
@Test
74+
public void testLockNearlyExpired() {
75+
eventLogWriter.fireBusinessEvent(MY_EVENT_TYPE, Fixture.mockPayload(1, "code456"));
76+
Instant timeOfInitialLock = Instant.now();
77+
78+
Collection<EventLog> lockedEvent = eventTransmissionService.lockSomeEvents();
79+
80+
// event will not be sent, because the event-lock is "nearlyExpired"
81+
mockServiceClock(timeOfInitialLock.plus(300 - 30 + 5, SECONDS));
82+
eventTransmissionService.sendEvents(lockedEvent);
83+
assertThat(nakadiClient.getSentEvents(MY_EVENT_TYPE), empty());
84+
}
85+
86+
private void mockServiceClock(Instant ins) {
87+
eventTransmissionService.overrideClock(Clock.fixed(ins, ZoneId.systemDefault()));
88+
}
89+
}

nakadi-producer-spring-boot-starter/src/test/java/org/zalando/nakadiproducer/flowid/TracerFlowIdComponentTest.java

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package org.zalando.nakadiproducer.flowid;
22

33
import static org.junit.Assert.assertThat;
4+
import static org.mockito.Mockito.never;
5+
import static org.mockito.Mockito.verify;
6+
import static org.mockito.Mockito.when;
47

58
import org.hamcrest.Matchers;
69
import org.junit.Test;
710
import org.junit.runner.RunWith;
811
import org.mockito.Answers;
912
import org.mockito.Mock;
10-
import org.mockito.Mockito;
1113
import org.mockito.runners.MockitoJUnitRunner;
12-
import org.zalando.nakadiproducer.flowid.TracerFlowIdComponent;
1314
import org.zalando.tracer.Tracer;
1415

1516
@RunWith(MockitoJUnitRunner.class)
@@ -21,10 +22,40 @@ public class TracerFlowIdComponentTest {
2122
@Test
2223
public void makeSureItWorks() {
2324
TracerFlowIdComponent flowIdComponent = new TracerFlowIdComponent(tracer);
24-
Mockito.when(tracer.get("X-Flow-ID").getValue()).thenReturn("A_FUNKY_VALUE");
25+
when(tracer.get("X-Flow-ID").getValue()).thenReturn("A_FUNKY_VALUE");
2526

2627
assertThat(flowIdComponent.getXFlowIdKey(), Matchers.equalTo("X-Flow-ID"));
2728
assertThat(flowIdComponent.getXFlowIdValue(), Matchers.equalTo("A_FUNKY_VALUE"));
2829
}
2930

31+
@Test
32+
public void makeSureTraceWillBeStartedIfNoneHasBeenStartedBefore() {
33+
TracerFlowIdComponent flowIdComponent = new TracerFlowIdComponent(tracer);
34+
when(tracer.get("X-Flow-ID").getValue()).thenThrow(new IllegalStateException());
35+
36+
flowIdComponent.startTraceIfNoneExists();
37+
38+
verify(tracer).start();
39+
}
40+
41+
@Test
42+
public void wontFailIfTraceHasNotBeenConfiguredInStartTrace() {
43+
TracerFlowIdComponent flowIdComponent = new TracerFlowIdComponent(tracer);
44+
when(tracer.get("X-Flow-ID")).thenThrow(new IllegalArgumentException());
45+
46+
flowIdComponent.startTraceIfNoneExists();
47+
48+
// then no exception is thrown
49+
}
50+
51+
@Test
52+
public void makeSureTraceWillNotStartedIfOneExists() {
53+
TracerFlowIdComponent flowIdComponent = new TracerFlowIdComponent(tracer);
54+
when(tracer.get("X-Flow-ID").getValue()).thenReturn("A_FUNKY_VALUE");
55+
56+
flowIdComponent.startTraceIfNoneExists();
57+
58+
verify(tracer, never()).start();
59+
}
60+
3061
}

0 commit comments

Comments
 (0)