Skip to content

Commit 7108a81

Browse files
Optimize slicing ReleasableBytesReference to avoid needless retention of unused pages
We can make slicing things like search hits where we cut many small buffers out of a large composite reference a lot more efficient in some cases by making the slice of a releasable reference itself releasable. This fixes the current situation where we could have composite buffer of many MB that is made up of e.g. 16k chunks but that we would retain in full if we were to slice even a single byte out of it in any position.
1 parent 7402dfd commit 7108a81

File tree

2 files changed

+18
-5
lines changed

2 files changed

+18
-5
lines changed

server/src/main/java/org/elasticsearch/common/bytes/ReleasableBytesReference.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,19 @@ public ReleasableBytesReference retain() {
7676
return this;
7777
}
7878

79+
/**
80+
* Same as {@link #slice} except that the slice is not guaranteed to share the same underlying reference count as this instance.
81+
* This method is equivalent to calling {@code .slice(from, length).retain()} but might be more efficient through the avoidance of
82+
* retaining unnecessary buffers.
83+
*/
7984
public ReleasableBytesReference retainedSlice(int from, int length) {
8085
if (from == 0 && length() == length) {
8186
return retain();
8287
}
8388
final BytesReference slice = delegate.slice(from, length);
89+
if (slice instanceof ReleasableBytesReference releasable) {
90+
return releasable.retain();
91+
}
8492
refCounted.incRef();
8593
return new ReleasableBytesReference(slice, refCounted);
8694
}
@@ -131,10 +139,17 @@ public int length() {
131139
return delegate.length();
132140
}
133141

142+
/**
143+
* {@inheritDoc}
144+
*
145+
* The returned bytes reference will share the reference count of this instance and as such any ref-counting operations on the return
146+
* are shared with this instance and vice versa. Using {@link #retainedSlice(int, int)} might be more efficient in situations where the
147+
* return of this method is subsequently retained by increasing its ref-count.
148+
*/
134149
@Override
135-
public BytesReference slice(int from, int length) {
150+
public ReleasableBytesReference slice(int from, int length) {
136151
assert hasReferences();
137-
return delegate.slice(from, length);
152+
return new ReleasableBytesReference(delegate.slice(from, length), refCounted);
138153
}
139154

140155
@Override

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,7 @@ public int internalDecode(ReleasableBytesReference reference, CheckedConsumer<Ob
121121
bytesConsumedThisDecode += maxBytesToConsume;
122122
bytesConsumed += maxBytesToConsume;
123123
if (maxBytesToConsume == remainingToConsume) {
124-
try (ReleasableBytesReference retained = reference.retainedSlice(0, maxBytesToConsume)) {
125-
fragmentConsumer.accept(retained);
126-
}
124+
fragmentConsumer.accept(reference.slice(0, maxBytesToConsume));
127125
} else {
128126
fragmentConsumer.accept(reference);
129127
}

0 commit comments

Comments
 (0)