Skip to content

Commit 56379a3

Browse files
Lazily initialize TransportActionStats in RequestHandlerRegistry (#117435)
Same as #114107 but for the transport side. We are consuming more than 0.5M for these stat instances and a large fraction if not the majority will go unused in many use-cases, given how expensive stats are in terms of volatile operations the overhead of an added `getAcquire` is trivial in the absence of writes. Outside of reducing the prod footprint this is also kind of nice in x-pack internal cluster tests in particular where it saves quite a bit of heap.
1 parent bde7bff commit 56379a3

File tree

4 files changed

+47
-6
lines changed

4 files changed

+47
-6
lines changed

server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import org.elasticsearch.node.Node;
4545
import org.elasticsearch.node.NodeValidationException;
4646
import org.elasticsearch.plugins.PluginsLoader;
47+
import org.elasticsearch.rest.MethodHandlers;
48+
import org.elasticsearch.transport.RequestHandlerRegistry;
4749

4850
import java.io.IOException;
4951
import java.io.InputStream;
@@ -201,7 +203,11 @@ private static void initPhase2(Bootstrap bootstrap) throws IOException {
201203
SubscribableListener.class,
202204
RunOnce.class,
203205
// We eagerly initialize to work around log4j permissions & JDK-8309727
204-
VectorUtil.class
206+
VectorUtil.class,
207+
// RequestHandlerRegistry and MethodHandlers classes do nontrivial static initialization which should always succeed but load
208+
// it now (before SM) to be sure
209+
RequestHandlerRegistry.class,
210+
MethodHandlers.class
205211
);
206212

207213
// load the plugin Java modules and layers now for use in entitlements

server/src/main/java/org/elasticsearch/rest/MethodHandlers.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
/**
2323
* Encapsulate multiple handlers for the same path, allowing different handlers for different HTTP verbs and versions.
2424
*/
25-
final class MethodHandlers {
25+
public final class MethodHandlers {
2626

2727
private final String path;
2828
private final Map<RestRequest.Method, Map<RestApiVersion, RestHandler>> methodHandlers;
2929

3030
@SuppressWarnings("unused") // only accessed via #STATS_TRACKER_HANDLE, lazy initialized because instances consume non-trivial heap
31-
private volatile HttpRouteStatsTracker statsTracker;
31+
private HttpRouteStatsTracker statsTracker;
3232

3333
private static final VarHandle STATS_TRACKER_HANDLE;
3434

server/src/main/java/org/elasticsearch/transport/RequestHandlerRegistry.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import org.elasticsearch.telemetry.tracing.Tracer;
2020

2121
import java.io.IOException;
22+
import java.lang.invoke.MethodHandles;
23+
import java.lang.invoke.VarHandle;
2224
import java.util.concurrent.Executor;
2325

2426
import static org.elasticsearch.core.Releasables.assertOnce;
@@ -33,7 +35,19 @@ public class RequestHandlerRegistry<Request extends TransportRequest> implements
3335
private final TaskManager taskManager;
3436
private final Tracer tracer;
3537
private final Writeable.Reader<Request> requestReader;
36-
private final TransportActionStatsTracker statsTracker = new TransportActionStatsTracker();
38+
@SuppressWarnings("unused") // only accessed via #STATS_TRACKER_HANDLE, lazy initialized because instances consume non-trivial heap
39+
private TransportActionStatsTracker statsTracker;
40+
41+
private static final VarHandle STATS_TRACKER_HANDLE;
42+
43+
static {
44+
try {
45+
STATS_TRACKER_HANDLE = MethodHandles.lookup()
46+
.findVarHandle(RequestHandlerRegistry.class, "statsTracker", TransportActionStatsTracker.class);
47+
} catch (Exception e) {
48+
throw new ExceptionInInitializerError(e);
49+
}
50+
}
3751

3852
public RequestHandlerRegistry(
3953
String action,
@@ -118,15 +132,34 @@ public static <R extends TransportRequest> RequestHandlerRegistry<R> replaceHand
118132
}
119133

120134
public void addRequestStats(int messageSize) {
121-
statsTracker.addRequestStats(messageSize);
135+
statsTracker().addRequestStats(messageSize);
122136
}
123137

124138
@Override
125139
public void addResponseStats(int messageSize) {
126-
statsTracker.addResponseStats(messageSize);
140+
statsTracker().addResponseStats(messageSize);
127141
}
128142

129143
public TransportActionStats getStats() {
144+
var statsTracker = existingStatsTracker();
145+
if (statsTracker == null) {
146+
return TransportActionStats.EMPTY;
147+
}
130148
return statsTracker.getStats();
131149
}
150+
151+
private TransportActionStatsTracker statsTracker() {
152+
var tracker = existingStatsTracker();
153+
if (tracker == null) {
154+
var newTracker = new TransportActionStatsTracker();
155+
if ((tracker = (TransportActionStatsTracker) STATS_TRACKER_HANDLE.compareAndExchange(this, null, newTracker)) == null) {
156+
tracker = newTracker;
157+
}
158+
}
159+
return tracker;
160+
}
161+
162+
private TransportActionStatsTracker existingStatsTracker() {
163+
return (TransportActionStatsTracker) STATS_TRACKER_HANDLE.getAcquire(this);
164+
}
132165
}

server/src/main/java/org/elasticsearch/transport/TransportActionStats.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ public record TransportActionStats(
2727
long[] responseSizeHistogram
2828
) implements Writeable, ToXContentObject {
2929

30+
public static final TransportActionStats EMPTY = new TransportActionStats(0, 0, new long[0], 0, 0, new long[0]);
31+
3032
public TransportActionStats(StreamInput in) throws IOException {
3133
this(in.readVLong(), in.readVLong(), in.readVLongArray(), in.readVLong(), in.readVLong(), in.readVLongArray());
3234
}

0 commit comments

Comments
 (0)