Skip to content

Commit 75ee7ae

Browse files
authored
sidecar: try to reconnect if channel closed (#1180)
Since the write is inline in user threads the write can get interruped leading to the channel being closed. Before all subsequent writes would fail, now it will detect a closed channel and attempt to reconnect.
1 parent 795c22a commit 75ee7ae

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

spectator-reg-sidecar/src/main/java/com/netflix/spectator/sidecar/UdpWriter.java

+26-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2022 Netflix, Inc.
2+
* Copyright 2014-2025 Netflix, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,27 +15,48 @@
1515
*/
1616
package com.netflix.spectator.sidecar;
1717

18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
1821
import java.io.IOException;
1922
import java.net.SocketAddress;
2023
import java.nio.ByteBuffer;
24+
import java.nio.channels.ClosedChannelException;
2125
import java.nio.channels.DatagramChannel;
2226
import java.nio.charset.StandardCharsets;
2327

2428
/** Writer that outputs data to UDP socket. */
2529
final class UdpWriter extends SidecarWriter {
2630

27-
private final DatagramChannel channel;
31+
private static final Logger LOGGER = LoggerFactory.getLogger(UdpWriter.class);
32+
33+
private final SocketAddress address;
34+
private DatagramChannel channel;
2835

2936
/** Create a new instance. */
3037
UdpWriter(String location, SocketAddress address) throws IOException {
3138
super(location);
32-
this.channel = DatagramChannel.open();
33-
this.channel.connect(address);
39+
this.address = address;
40+
connect();
41+
}
42+
43+
private void connect() throws IOException {
44+
channel = DatagramChannel.open();
45+
channel.connect(address);
3446
}
3547

3648
@Override public void writeImpl(String line) throws IOException {
3749
ByteBuffer buffer = ByteBuffer.wrap(line.getBytes(StandardCharsets.UTF_8));
38-
channel.write(buffer);
50+
try {
51+
channel.write(buffer);
52+
} catch (ClosedChannelException e) {
53+
try {
54+
connect();
55+
} catch (IOException ex) {
56+
LOGGER.warn("channel closed, failed to reconnect", ex);
57+
}
58+
throw e;
59+
}
3960
}
4061

4162
@Override public void close() throws IOException {

spectator-reg-sidecar/src/test/java/com/netflix/spectator/sidecar/UdpWriterTest.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2022 Netflix, Inc.
2+
* Copyright 2014-2025 Netflix, Inc.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -39,6 +39,23 @@ public void udp() throws IOException {
3939
}
4040
}
4141

42+
@Test
43+
public void udpReconnectIfClosed() throws IOException {
44+
try (UdpServer server = new UdpServer()) {
45+
try (SidecarWriter w = SidecarWriter.create(server.address())) {
46+
// Used to simulate close from something like an interrupt. The next write
47+
// will fail and it should try to reconnect.
48+
w.close();
49+
w.write("1");
50+
51+
w.write("2");
52+
Assertions.assertEquals("2", server.read());
53+
w.write("3");
54+
Assertions.assertEquals("3", server.read());
55+
}
56+
}
57+
}
58+
4259
// Disabled because it can have issues on CI
4360
@Test
4461
@Disabled

0 commit comments

Comments
 (0)