Skip to content

Commit f4d7374

Browse files
committed
Add multicast and reduce flakiness
1 parent 146f17b commit f4d7374

File tree

4 files changed

+74
-8
lines changed

4 files changed

+74
-8
lines changed

spring/spring-boot-session-replication-spring-session-hazelcast/src/main/java/com/hazelcast/guide/config/SessionConfiguration.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ public HazelcastInstance hazelcastInstance() {
4343
Config config = new Config();
4444
config.setClusterName("spring-session-cluster");
4545

46+
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(true);
47+
4648
// Add this attribute to be able to query sessions by their PRINCIPAL_NAME_ATTRIBUTE's
4749
AttributeConfig attributeConfig = new AttributeConfig()
4850
.setName(HazelcastIndexedSessionRepository.PRINCIPAL_NAME_ATTRIBUTE)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.hazelcast.guide.controller;
2+
3+
import com.hazelcast.core.HazelcastInstance;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RestController;
6+
7+
/**
8+
* This controller is optional; it's used only to check cluster size to check if cluster already have all nodes connected
9+
* in the tests.
10+
*/
11+
@RestController
12+
public class ClusterController {
13+
14+
private final HazelcastInstance hazelcastInstance;
15+
16+
public ClusterController(HazelcastInstance hazelcastInstance) {
17+
this.hazelcastInstance = hazelcastInstance;
18+
}
19+
20+
@GetMapping("/clusterSize")
21+
public int getClusterSize() {
22+
return hazelcastInstance.getCluster().getMembers().size();
23+
}
24+
}

spring/spring-boot-session-replication-spring-session-hazelcast/src/main/java/com/hazelcast/guide/controller/SessionController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.hazelcast.guide.controller;
22

33
import jakarta.servlet.http.HttpServletRequest;
4+
import jakarta.servlet.http.HttpServletResponse;
45
import jakarta.servlet.http.HttpSession;
56
import org.springframework.http.MediaType;
67
import org.springframework.session.FindByIndexNameSessionRepository;
@@ -41,7 +42,7 @@ public SessionController(FindByIndexNameSessionRepository<?> sessionRepository)
4142
*
4243
*/
4344
@GetMapping("/create")
44-
public String createSession(@RequestParam("principal") String principal, HttpServletRequest request) {
45+
public String createSession(@RequestParam("principal") String principal, HttpServletRequest request, HttpServletResponse response) {
4546
HttpSession session = request.getSession(false);
4647
if (session == null) {
4748
session = request.getSession();

spring/spring-boot-session-replication-spring-session-hazelcast/src/test/java/com/hazelcast/guide/HazelcastSpringSessionApplicationTests.java

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66
import org.springframework.boot.builder.SpringApplicationBuilder;
77
import org.springframework.http.HttpEntity;
88
import org.springframework.http.HttpHeaders;
9-
import org.springframework.http.HttpMethod;
109
import org.springframework.http.ResponseEntity;
1110
import org.springframework.web.client.RestTemplate;
1211
import org.springframework.web.util.UriComponentsBuilder;
1312

13+
import java.util.Base64;
1414
import java.util.Collections;
1515
import java.util.Map;
16+
import java.util.concurrent.TimeUnit;
1617

17-
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
import static java.nio.charset.StandardCharsets.UTF_8;
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.springframework.http.HttpMethod.GET;
1821

22+
@SuppressWarnings("DataFlowIssue")
1923
class HazelcastSpringSessionApplicationTests {
2024

2125
private static final Logger logger = LoggerFactory.getLogger(HazelcastSpringSessionApplicationTests.class);
@@ -29,16 +33,21 @@ void contextLoads() {
2933
String port2 = startApplication();
3034
Map<String, String> principalMap = Collections.singletonMap("principal", "hazelcast2020");
3135

36+
waitForCluster(port1);
37+
3238
// when
3339
ResponseEntity<?> response1 = makeRequest(port1, "create", null, principalMap);
3440
String sessionCookie1 = extractSessionCookie(response1);
41+
logger.info("First request headers: {}", response1.getHeaders());
42+
logger.info("First request body: {}", response1.getBody().toString());
43+
logger.info("First session cookie: {}", sessionCookie1);
3544

3645
// then
3746
ResponseEntity<?> response2 = makeRequest(port2, "create", sessionCookie1, principalMap);
3847
String body = response2.getBody().toString();
3948
logger.info("Body contains: {}", body);
40-
assertTrue(body.contains("Session already exists"));
4149

50+
assertThat(body).contains("Session already exists");
4251
}
4352

4453
private static String startApplication() {
@@ -53,25 +62,55 @@ private String extractSessionCookie(ResponseEntity<?> response) {
5362
return response.getHeaders().get("Set-Cookie").stream()
5463
.filter(s -> s.contains(COOKIE_NAME))
5564
.map(s -> s.substring(COOKIE_NAME.length() + 1))
65+
.map(s -> s.contains(";") ? s.substring(0, s.indexOf(";")) : s)
66+
.map(s -> new String(Base64.getDecoder().decode(s)))
5667
.findFirst().orElse(null);
5768
}
5869

59-
private static ResponseEntity<?> makeRequest(String port, String endpoint, String sessionCookie, Map<String, String> parameters) {
70+
@SuppressWarnings("SameParameterValue")
71+
private static ResponseEntity<?> makeRequest(String port, String endpoint, String sessionCookie, Map<String, String> parameters) {
6072
String url = "http://localhost:" + port + "/" + endpoint;
6173

6274
// Header
6375
HttpHeaders headers = new HttpHeaders();
6476
if (sessionCookie != null) {
65-
headers.add("Cookie", COOKIE_NAME + "=" + sessionCookie);
77+
headers.add("Cookie", COOKIE_NAME + "=" +
78+
Base64.getEncoder().encodeToString(sessionCookie.getBytes(UTF_8)));
6679
}
6780

6881
// Query parameters
69-
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
82+
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
7083
parameters.forEach(builder::queryParam);
7184

7285
// Request
7386
RestTemplate restTemplate = new RestTemplate();
74-
return restTemplate.exchange(builder.toUriString(), HttpMethod.GET, new HttpEntity<>(headers), String.class);
87+
return restTemplate.exchange(builder.toUriString(), GET, new HttpEntity<>(headers), String.class);
88+
}
89+
90+
public static void waitForCluster(String port) {
91+
String url = "http://localhost:" + port + "/clusterSize";
92+
93+
var restTemplate = new RestTemplate();
94+
var requestEntity = new HttpEntity<>(new HttpHeaders());
95+
96+
long now = System.currentTimeMillis();
97+
long timeout = TimeUnit.MINUTES.toMillis(5);
98+
int attempts = 0;
99+
while (System.currentTimeMillis() - now < timeout) {
100+
logger.info("attempt no {}, remaining time {}", attempts + 1, System.currentTimeMillis() - now);
101+
ResponseEntity<Integer> clusterSize = restTemplate.exchange(url, GET, requestEntity, Integer.class);
102+
if (clusterSize.getBody() == 2) {
103+
return;
104+
}
105+
attempts++;
106+
logger.info("Waiting for cluster size, attempt no {} failed", attempts);
107+
try {
108+
Thread.sleep(1000);
109+
} catch (InterruptedException e) {
110+
throw new RuntimeException(e);
111+
}
112+
}
113+
throw new IllegalStateException("Cluster has incorrect size");
75114
}
76115

77116
}

0 commit comments

Comments
 (0)