-
Notifications
You must be signed in to change notification settings - Fork 25.2k
Consolidate DelayableWriteable #55932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
8dc10cd
0bb27c0
9e72338
0c0924a
80f97aa
cc9f81e
7a3fa7a
0bf6aa8
5138660
ceace45
02a0358
022ae29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,13 +25,26 @@ | |
import org.elasticsearch.common.bytes.BytesReference; | ||
|
||
import java.io.IOException; | ||
import java.util.function.Supplier; | ||
|
||
/** | ||
* A holder for {@link Writeable}s that can delays reading the underlying | ||
* {@linkplain Writeable} when it is read from a remote node. | ||
* A holder for {@link Writeable}s that delays reading the underlying object | ||
* on the receiving end. To be used for objects whose deserialized | ||
* representation is inefficient to keep in memory compared to their | ||
* corresponding serialized representation. | ||
* The node that produces the {@link Writeable} calls {@link #referencing(Writeable)} | ||
* to create a {@link DelayableWriteable} that serializes the inner object | ||
* first to a buffer and writes the content of the buffer to the {@link StreamOutput}. | ||
* The receiver node calls {@link #delayed(Reader, StreamInput)} to create a | ||
* {@link DelayableWriteable} that reads the buffer from the @link {@link StreamInput} | ||
* but delays creating the actual object by calling {@link #expand()} when needed. | ||
* Multiple {@link DelayableWriteable}s coming from different nodes may be buffered | ||
* on the receiver end, which may hold a mix of {@link DelayableWriteable}s that were | ||
* produced locally (hence expanded) as well as received form another node (hence subject | ||
* to delayed expansion). When such objects are buffered for some time it may be desirable | ||
* to force their buffering in serialized format by calling | ||
* {@link #asSerialized(Reader, NamedWriteableRegistry)}. | ||
*/ | ||
public abstract class DelayableWriteable<T extends Writeable> implements Supplier<T>, Writeable { | ||
public abstract class DelayableWriteable<T extends Writeable> implements Writeable { | ||
/** | ||
* Build a {@linkplain DelayableWriteable} that wraps an existing object | ||
* but is serialized so that deserializing it can be delayed. | ||
|
@@ -42,7 +55,7 @@ public static <T extends Writeable> DelayableWriteable<T> referencing(T referenc | |
/** | ||
* Build a {@linkplain DelayableWriteable} that copies a buffer from | ||
* the provided {@linkplain StreamInput} and deserializes the buffer | ||
* when {@link Supplier#get()} is called. | ||
* when {@link #expand()} is called. | ||
*/ | ||
public static <T extends Writeable> DelayableWriteable<T> delayed(Writeable.Reader<T> reader, StreamInput in) throws IOException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we rename this to readDelayedWriteable ? also depending on the rename proposed above... |
||
return new Serialized<>(reader, in.getVersion(), in.namedWriteableRegistry(), in.readBytesReference()); | ||
|
@@ -56,16 +69,21 @@ private DelayableWriteable() {} | |
*/ | ||
public abstract Serialized<T> asSerialized(Writeable.Reader<T> reader, NamedWriteableRegistry registry); | ||
|
||
/** | ||
* Expands the inner {@link Writeable} to its original representation and returns it | ||
*/ | ||
public abstract T expand(); | ||
|
||
/** | ||
* {@code true} if the {@linkplain Writeable} is being stored in | ||
* serialized form, {@code false} otherwise. | ||
*/ | ||
abstract boolean isSerialized(); | ||
|
||
private static class Referencing<T extends Writeable> extends DelayableWriteable<T> { | ||
private T reference; | ||
private final T reference; | ||
|
||
Referencing(T reference) { | ||
private Referencing(T reference) { | ||
this.reference = reference; | ||
} | ||
|
||
|
@@ -75,17 +93,19 @@ public void writeTo(StreamOutput out) throws IOException { | |
} | ||
|
||
@Override | ||
public T get() { | ||
public T expand() { | ||
return reference; | ||
} | ||
|
||
@Override | ||
public Serialized<T> asSerialized(Reader<T> reader, NamedWriteableRegistry registry) { | ||
BytesStreamOutput buffer; | ||
try { | ||
return new Serialized<T>(reader, Version.CURRENT, registry, writeToBuffer(Version.CURRENT).bytes()); | ||
buffer = writeToBuffer(Version.CURRENT); | ||
} catch (IOException e) { | ||
throw new RuntimeException("unexpected error expanding aggregations", e); | ||
throw new RuntimeException("unexpected error writing writeable to buffer", e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for this change! |
||
} | ||
return new Serialized<>(reader, Version.CURRENT, registry, buffer.bytes()); | ||
} | ||
|
||
@Override | ||
|
@@ -111,8 +131,8 @@ public static class Serialized<T extends Writeable> extends DelayableWriteable<T | |
private final NamedWriteableRegistry registry; | ||
private final BytesReference serialized; | ||
|
||
Serialized(Writeable.Reader<T> reader, Version serializedAtVersion, | ||
NamedWriteableRegistry registry, BytesReference serialized) throws IOException { | ||
private Serialized(Writeable.Reader<T> reader, Version serializedAtVersion, | ||
NamedWriteableRegistry registry, BytesReference serialized) { | ||
this.reader = reader; | ||
this.serializedAtVersion = serializedAtVersion; | ||
this.registry = registry; | ||
|
@@ -136,20 +156,20 @@ public void writeTo(StreamOutput out) throws IOException { | |
* differences in the wire protocol. This ain't efficient but | ||
* it should be quite rare. | ||
*/ | ||
referencing(get()).writeTo(out); | ||
referencing(expand()).writeTo(out); | ||
} | ||
} | ||
|
||
@Override | ||
public T get() { | ||
public T expand() { | ||
try { | ||
try (StreamInput in = registry == null ? | ||
serialized.streamInput() : new NamedWriteableAwareStreamInput(serialized.streamInput(), registry)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering, is it an option to keep a reference to the original stream input so that we do not need this if and we can avoid exposing the new namedWriteableRegistry getter in StreamInput? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we could keep a reference to the original I wonder if keeping a reference to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yea it did sound risky to me as well, and what we do instead looks fine to me. |
||
in.setVersion(serializedAtVersion); | ||
return reader.read(in); | ||
} | ||
} catch (IOException e) { | ||
throw new RuntimeException("unexpected error expanding aggregations", e); | ||
throw new RuntimeException("unexpected error expanding serialized delayed writeable", e); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -379,18 +379,16 @@ public void onPartialReduce(List<SearchShard> shards, TotalHits totalHits, | |
reducedAggs = () -> null; | ||
} else { | ||
/* | ||
* Keep a reference to the serialiazed form of the partially | ||
* Keep a reference to the serialized form of the partially | ||
* reduced aggs and reduce it on the fly when someone asks | ||
* for it. This will produce right-ish aggs. Much more right | ||
* than if you don't do the final reduce. Its important that | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI @nik9000 I removed the uncertainty around producing right-ish aggs. I find that if certain aggs don't work it's a bug that we should fix like we did with scripted metric. The approach here around partial aggs is the correct one hence we should not have doubts about it :) |
||
* we wait until someone needs the result so we don't perform | ||
* the final reduce only to throw it away. And it is important | ||
* that we kep the reference to the serialized aggrgations | ||
* because the SearchPhaseController *already* has that | ||
* reference so we're not creating more garbage. | ||
* for it. It's important that we wait until someone needs | ||
* the result so we don't perform the final reduce only to | ||
* throw it away. And it is important that we keep the reference | ||
* to the serialized aggregations because SearchPhaseController | ||
* *already* has that reference so we're not creating more garbage. | ||
*/ | ||
reducedAggs = () -> | ||
InternalAggregations.topLevelReduce(singletonList(aggregations.get()), aggReduceContextSupplier.get()); | ||
InternalAggregations.topLevelReduce(singletonList(aggregations.expand()), aggReduceContextSupplier.get()); | ||
} | ||
searchResponse.get().updatePartialResponse(shards.size(), totalHits, reducedAggs, reducePhase); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was wondering, is it just DelayedWriteable or DelayedReadWriteable ? Because it's not that you can choose to delay its reading, it's the only way that it works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess so. I figured
Writeable
was the name for things that we threw across the wire and calling itDelayed
was enough to imply that it delayed reading because that was sort of the only thing you could delay.At this point, given how we use it, it might be more correct to call it
PotentiallySerializedWriteable
. But that is kind of mouthful. I think the whole "Delayed" bit of the name was perfect for the first PR that built it but it is less and less perfect now.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but why Delayed VS Delayable for instance?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the
referencing
implementation isn't delayed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find that that is a lie :) because writeTo will always do the two steps serialization that will allow for delaying the deserialization on the other side. The point is that referencing is only on the producer side hence you never read into referencing from the stream? Maybe a bit of a philosophical discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should rename it to something like
SerializedReference
orMaybeSerializedReference
so maybe its not an issue then. I get where you are coming from though. The name is icky now.