Skip to content

Commit a01ab50

Browse files
authored
fixes memory leak when default rsocket tracing propagation is enabled (#2262)
* fixes memory leak when default rsocket tracing propagation is enabled Signed-off-by: OlegDokuka <[email protected]> * fixes styles Signed-off-by: OlegDokuka <[email protected]> --------- Signed-off-by: OlegDokuka <[email protected]>
1 parent f4dc2ce commit a01ab50

File tree

2 files changed

+91
-9
lines changed

2 files changed

+91
-9
lines changed

spring-cloud-sleuth-instrumentation/src/main/java/org/springframework/cloud/sleuth/instrument/rsocket/TracingRequesterRSocketProxy.java

+17-9
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,12 @@
2121
import java.util.function.Function;
2222

2323
import io.netty.buffer.ByteBuf;
24+
import io.netty.buffer.ByteBufAllocator;
2425
import io.netty.buffer.CompositeByteBuf;
2526
import io.rsocket.Payload;
2627
import io.rsocket.RSocket;
2728
import io.rsocket.frame.FrameType;
29+
import io.rsocket.metadata.CompositeMetadataCodec;
2830
import io.rsocket.metadata.RoutingMetadata;
2931
import io.rsocket.metadata.TracingMetadataCodec;
3032
import io.rsocket.metadata.WellKnownMimeType;
@@ -104,34 +106,40 @@ <T> Mono<T> setSpan(Function<Payload, Mono<T>> input, Payload payload, FrameType
104106
log.debug("Extracted result from context or thread local " + span);
105107
}
106108
final Payload newPayload = PayloadUtils.cleanTracingMetadata(payload, new HashSet<>(propagator.fields()));
107-
TraceContext traceContext = span.context();
109+
final TraceContext traceContext = span.context();
110+
final CompositeByteBuf metadata = (CompositeByteBuf) newPayload.metadata();
108111
if (this.isZipkinPropagationEnabled) {
109-
injectDefaultZipkinRSocketHeaders(newPayload, traceContext);
112+
injectDefaultZipkinRSocketHeaders(metadata, traceContext);
110113
}
111-
this.propagator.inject(traceContext, (CompositeByteBuf) newPayload.metadata(), this.setter);
114+
this.propagator.inject(traceContext, metadata, this.setter);
112115
return input.apply(newPayload).doOnError(span::error).doFinally(signalType -> span.end());
113116
});
114117
}
115118

116-
private void injectDefaultZipkinRSocketHeaders(Payload newPayload, TraceContext traceContext) {
119+
void injectDefaultZipkinRSocketHeaders(CompositeByteBuf metadata, TraceContext traceContext) {
117120
TracingMetadataCodec.Flags flags = traceContext.sampled() == null ? TracingMetadataCodec.Flags.UNDECIDED
118121
: traceContext.sampled() ? TracingMetadataCodec.Flags.SAMPLE : TracingMetadataCodec.Flags.NOT_SAMPLE;
119122
String traceId = traceContext.traceId();
120123
long[] traceIds = EncodingUtils.fromString(traceId);
121124
long[] spanId = EncodingUtils.fromString(traceContext.spanId());
122125
long[] parentSpanId = EncodingUtils.fromString(traceContext.parentId());
123126
boolean isTraceId128Bit = traceIds.length == 2;
127+
128+
final ByteBufAllocator allocator = metadata.alloc();
124129
if (isTraceId128Bit) {
125-
TracingMetadataCodec.encode128(newPayload.metadata().alloc(), traceIds[0], traceIds[1], spanId[0],
126-
EncodingUtils.fromString(traceContext.parentId())[0], flags);
130+
CompositeMetadataCodec.encodeAndAddMetadata(metadata, allocator,
131+
WellKnownMimeType.MESSAGE_RSOCKET_TRACING_ZIPKIN,
132+
TracingMetadataCodec.encode128(allocator, traceIds[0], traceIds[1], spanId[0],
133+
EncodingUtils.fromString(traceContext.parentId())[0], flags));
127134
}
128135
else {
129-
TracingMetadataCodec.encode64(newPayload.metadata().alloc(), traceIds[0], spanId[0], parentSpanId[0],
130-
flags);
136+
CompositeMetadataCodec.encodeAndAddMetadata(metadata, allocator,
137+
WellKnownMimeType.MESSAGE_RSOCKET_TRACING_ZIPKIN,
138+
TracingMetadataCodec.encode64(allocator, traceIds[0], spanId[0], parentSpanId[0], flags));
131139
}
132140
}
133141

134-
private Span.Builder spanBuilder(ContextView contextView) {
142+
Span.Builder spanBuilder(ContextView contextView) {
135143
Span.Builder spanBuilder = this.tracer.spanBuilder();
136144
if (contextView.hasKey(TraceContext.class)) {
137145
spanBuilder = spanBuilder.setParent(contextView.get(TraceContext.class));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright 2013-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.sleuth.instrument.rsocket;
18+
19+
import io.netty.buffer.ByteBuf;
20+
import io.netty.buffer.ByteBufAllocator;
21+
import io.netty.buffer.CompositeByteBuf;
22+
import org.assertj.core.api.Assertions;
23+
import org.junit.jupiter.api.Test;
24+
25+
import org.springframework.cloud.sleuth.TraceContext;
26+
27+
public class TracingRequesterRSocketProxyTest {
28+
29+
@Test
30+
public void checkNoLeaksOnDefaultTracingHeaders() {
31+
final TracingRequesterRSocketProxy tracingRequesterRSocketProxy = new TracingRequesterRSocketProxy(null, null,
32+
null, null, true);
33+
34+
final ByteBufAllocator allocator = ByteBufAllocator.DEFAULT;
35+
final CompositeByteBuf metadata = allocator.compositeBuffer();
36+
tracingRequesterRSocketProxy.injectDefaultZipkinRSocketHeaders(metadata, new TraceContext() {
37+
@Override
38+
public String traceId() {
39+
return "0000000000000002";
40+
}
41+
42+
@Override
43+
public String parentId() {
44+
return null;
45+
}
46+
47+
@Override
48+
public String spanId() {
49+
return "0000000000000001";
50+
}
51+
52+
@Override
53+
public Boolean sampled() {
54+
return null;
55+
}
56+
});
57+
58+
// should add 2 components which are headers and body
59+
Assertions.assertThat(metadata.numComponents()).isEqualTo(2);
60+
Assertions.assertThat(metadata.refCnt()).isEqualTo(1);
61+
62+
final ByteBuf c1 = metadata.internalComponent(0);
63+
final ByteBuf c2 = metadata.internalComponent(1);
64+
65+
Assertions.assertThat(c1.refCnt()).isEqualTo(1);
66+
Assertions.assertThat(c2.refCnt()).isEqualTo(1);
67+
68+
Assertions.assertThat(metadata.release()).isTrue();
69+
70+
Assertions.assertThat(c1.refCnt()).isEqualTo(0);
71+
Assertions.assertThat(c2.refCnt()).isEqualTo(0);
72+
}
73+
74+
}

0 commit comments

Comments
 (0)