-
Notifications
You must be signed in to change notification settings - Fork 15.1k
MINOR: Remove Struct from Request/Response classes #2513
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
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 |
|---|---|---|
|
|
@@ -200,11 +200,11 @@ public void onSuccess(ClientResponse resp) { | |
| long fetchOffset = request.fetchData().get(partition).offset; | ||
| FetchResponse.PartitionData fetchData = entry.getValue(); | ||
| completedFetches.add(new CompletedFetch(partition, fetchOffset, fetchData, metricAggregator, | ||
| request.version())); | ||
| resp.requestHeader().apiVersion())); | ||
| } | ||
|
|
||
| sensors.fetchLatency.record(resp.requestLatencyMs()); | ||
| sensors.fetchThrottleTimeSensor.record(response.getThrottleTime()); | ||
| sensors.fetchThrottleTimeSensor.record(response.throttleTimeMs()); | ||
| } | ||
|
|
||
| @Override | ||
|
|
@@ -603,13 +603,12 @@ public void onFailure(RuntimeException e) { | |
| * @return A response which can be polled to obtain the corresponding timestamps and offsets. | ||
| */ | ||
| private RequestFuture<Map<TopicPartition, OffsetData>> sendListOffsetRequest(final Node node, | ||
| final Map<TopicPartition, Long> timestampsToSearch, | ||
| boolean requireTimestamp) { | ||
| ListOffsetRequest.Builder builder = new ListOffsetRequest.Builder().setTargetTimes(timestampsToSearch); | ||
|
|
||
| // If we need a timestamp in the response, the minimum RPC version we can send is v1. | ||
| // Otherwise, v0 is OK. | ||
| builder.setMinVersion(requireTimestamp ? (short) 1 : (short) 0); | ||
| final Map<TopicPartition, Long> timestampsToSearch, | ||
| boolean requireTimestamp) { | ||
| // If we need a timestamp in the response, the minimum RPC version we can send is v1. Otherwise, v0 is OK. | ||
| short minVersion = requireTimestamp ? (short) 1 : (short) 0; | ||
|
Contributor
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. We don't have to do this here, but as I'm reading this, I wonder why we can't push this logic into the builder. For example, we can add a builder method
Contributor
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. Sounds like a good idea
Member
Author
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'll swap the |
||
| ListOffsetRequest.Builder builder = ListOffsetRequest.Builder.forConsumer(minVersion) | ||
| .setTargetTimes(timestampsToSearch); | ||
|
|
||
| log.trace("Sending ListOffsetRequest {} to broker {}", builder, node); | ||
| return client.send(node, builder) | ||
|
|
@@ -733,7 +732,7 @@ private Map<Node, FetchRequest.Builder> createFetchRequests() { | |
| Map<Node, FetchRequest.Builder> requests = new HashMap<>(); | ||
| for (Map.Entry<Node, LinkedHashMap<TopicPartition, FetchRequest.PartitionData>> entry : fetchable.entrySet()) { | ||
| Node node = entry.getKey(); | ||
| FetchRequest.Builder fetch = new FetchRequest.Builder(this.maxWaitMs, this.minBytes, entry.getValue()). | ||
| FetchRequest.Builder fetch = FetchRequest.Builder.forConsumer(this.maxWaitMs, this.minBytes, entry.getValue()). | ||
| setMaxBytes(this.maxBytes); | ||
| requests.put(node, fetch); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,40 +25,39 @@ | |
| import java.nio.ByteBuffer; | ||
|
|
||
| public abstract class AbstractRequest extends AbstractRequestResponse { | ||
| private final short version; | ||
|
|
||
| public static abstract class Builder<T extends AbstractRequest> { | ||
| private final ApiKeys apiKey; | ||
| private short version; | ||
| private final Short desiredVersion; | ||
|
|
||
| public Builder(ApiKeys apiKey) { | ||
| this(apiKey, null); | ||
| } | ||
|
|
||
| public Builder(ApiKeys apiKey, Short desiredVersion) { | ||
| this.apiKey = apiKey; | ||
| this.version = ProtoUtils.latestVersion(apiKey.id); | ||
| this.desiredVersion = desiredVersion; | ||
| } | ||
|
|
||
| public ApiKeys apiKey() { | ||
| return apiKey; | ||
| } | ||
|
|
||
| public Builder<T> setVersion(short version) { | ||
| this.version = version; | ||
| return this; | ||
| public short desiredOrLatestVersion() { | ||
| return desiredVersion == null ? ProtoUtils.latestVersion(apiKey.id) : desiredVersion; | ||
|
Contributor
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. Minor suggestion: maybe we can just add a
Member
Author
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. That's a good idea, here you go: https://github.com/apache/kafka/pull/2580/files |
||
| } | ||
|
|
||
| public short version() { | ||
| return version; | ||
| public T build() { | ||
| return build(desiredOrLatestVersion()); | ||
| } | ||
|
|
||
| public abstract T build(); | ||
| public abstract T build(short version); | ||
| } | ||
|
|
||
| public AbstractRequest(Struct struct, short version) { | ||
| super(struct); | ||
| this.version = version; | ||
| } | ||
| private final short version; | ||
|
|
||
| public Send toSend(String destination, RequestHeader header) { | ||
| return new NetworkSend(destination, serialize(header, this)); | ||
| public AbstractRequest(short version) { | ||
| this.version = version; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -68,6 +67,19 @@ public short version() { | |
| return version; | ||
| } | ||
|
|
||
| public Send toSend(String destination, RequestHeader header) { | ||
| return new NetworkSend(destination, serialize(header)); | ||
| } | ||
|
|
||
| /** | ||
| * Use with care, typically {@link #toSend(String, RequestHeader)} should be used instead. | ||
| */ | ||
| public ByteBuffer serialize(RequestHeader header) { | ||
| return serialize(header.toStruct(), toStruct()); | ||
| } | ||
|
|
||
| protected abstract Struct toStruct(); | ||
|
|
||
| /** | ||
| * Get an error response for a request | ||
| */ | ||
|
|
@@ -76,54 +88,78 @@ public short version() { | |
| /** | ||
| * Factory method for getting a request object based on ApiKey ID and a buffer | ||
| */ | ||
| public static AbstractRequest getRequest(int requestId, short versionId, ByteBuffer buffer) { | ||
| public static RequestAndSize getRequest(int requestId, short version, ByteBuffer buffer) { | ||
|
Contributor
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 guess another option is to add the request size as a field in
Member
Author
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 had that it that way first, but it's a bit weird because we don't always need the size and we can't compute it unless the
Contributor
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. Another alternative is to add the size to
Member
Author
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 don't think that works because we often don't have (and don't need) a size. We had discussed renaming
Contributor
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. Makes sense. We can always rename the class if we need to add some more info in there. |
||
| ApiKeys apiKey = ApiKeys.forId(requestId); | ||
| Struct struct = ProtoUtils.parseRequest(apiKey.id, version, buffer); | ||
| AbstractRequest request; | ||
| switch (apiKey) { | ||
| case PRODUCE: | ||
| return ProduceRequest.parse(buffer, versionId); | ||
| request = new ProduceRequest(struct, version); | ||
| break; | ||
| case FETCH: | ||
| return FetchRequest.parse(buffer, versionId); | ||
| request = new FetchRequest(struct, version); | ||
| break; | ||
| case LIST_OFFSETS: | ||
| return ListOffsetRequest.parse(buffer, versionId); | ||
| request = new ListOffsetRequest(struct, version); | ||
| break; | ||
| case METADATA: | ||
| return MetadataRequest.parse(buffer, versionId); | ||
| request = new MetadataRequest(struct, version); | ||
| break; | ||
| case OFFSET_COMMIT: | ||
| return OffsetCommitRequest.parse(buffer, versionId); | ||
| request = new OffsetCommitRequest(struct, version); | ||
| break; | ||
| case OFFSET_FETCH: | ||
| return OffsetFetchRequest.parse(buffer, versionId); | ||
| request = new OffsetFetchRequest(struct, version); | ||
| break; | ||
| case GROUP_COORDINATOR: | ||
| return GroupCoordinatorRequest.parse(buffer, versionId); | ||
| request = new GroupCoordinatorRequest(struct, version); | ||
| break; | ||
| case JOIN_GROUP: | ||
| return JoinGroupRequest.parse(buffer, versionId); | ||
| request = new JoinGroupRequest(struct, version); | ||
| break; | ||
| case HEARTBEAT: | ||
| return HeartbeatRequest.parse(buffer, versionId); | ||
| request = new HeartbeatRequest(struct, version); | ||
| break; | ||
| case LEAVE_GROUP: | ||
| return LeaveGroupRequest.parse(buffer, versionId); | ||
| request = new LeaveGroupRequest(struct, version); | ||
| break; | ||
| case SYNC_GROUP: | ||
| return SyncGroupRequest.parse(buffer, versionId); | ||
| request = new SyncGroupRequest(struct, version); | ||
| break; | ||
| case STOP_REPLICA: | ||
| return StopReplicaRequest.parse(buffer, versionId); | ||
| request = new StopReplicaRequest(struct, version); | ||
| break; | ||
| case CONTROLLED_SHUTDOWN_KEY: | ||
| return ControlledShutdownRequest.parse(buffer, versionId); | ||
| request = new ControlledShutdownRequest(struct, version); | ||
| break; | ||
| case UPDATE_METADATA_KEY: | ||
| return UpdateMetadataRequest.parse(buffer, versionId); | ||
| request = new UpdateMetadataRequest(struct, version); | ||
| break; | ||
| case LEADER_AND_ISR: | ||
| return LeaderAndIsrRequest.parse(buffer, versionId); | ||
| request = new LeaderAndIsrRequest(struct, version); | ||
| break; | ||
| case DESCRIBE_GROUPS: | ||
| return DescribeGroupsRequest.parse(buffer, versionId); | ||
| request = new DescribeGroupsRequest(struct, version); | ||
| break; | ||
| case LIST_GROUPS: | ||
| return ListGroupsRequest.parse(buffer, versionId); | ||
| request = new ListGroupsRequest(struct, version); | ||
| break; | ||
| case SASL_HANDSHAKE: | ||
| return SaslHandshakeRequest.parse(buffer, versionId); | ||
| request = new SaslHandshakeRequest(struct, version); | ||
| break; | ||
| case API_VERSIONS: | ||
| return ApiVersionsRequest.parse(buffer, versionId); | ||
| request = new ApiVersionsRequest(struct, version); | ||
| break; | ||
| case CREATE_TOPICS: | ||
| return CreateTopicsRequest.parse(buffer, versionId); | ||
| request = new CreateTopicsRequest(struct, version); | ||
| break; | ||
| case DELETE_TOPICS: | ||
| return DeleteTopicsRequest.parse(buffer, versionId); | ||
| request = new DeleteTopicsRequest(struct, version); | ||
| break; | ||
| default: | ||
| throw new AssertionError(String.format("ApiKey %s is not currently handled in `getRequest`, the " + | ||
| "code should be updated to do so.", apiKey)); | ||
| } | ||
| return new RequestAndSize(request, struct.sizeOf()); | ||
| } | ||
| } | ||
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.
The awkwardness is that the request objects themselves are kind of a pointless intermediate stage between the builders and the transformation to the
Send(for the client anyway). I guess this will be resolved when the builders go away (in effect, the request classes become the builders). I'd probably be inclined to do it in one shot, but up to you. One challenge is dealing with the fact that the request version must be known when the server receives it, but unknown at the time the client creates it.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.
Yes, I agree that the builders should go away. Let's do that in a follow-up PR though. :)
The request version thing isn't an issue because it's in the
headerfield as well, I think.