Authors: Henry Cai, Thomas Thornton
Current state: "Under Discussion"
Related KIP: KIP-1254
Discussion thread: here
JIRA: here [Change the link from KAFKA-1 to your own ticket]
Please keep the discussion on the mailing list rather than commenting on the wiki (wiki discussions get unwieldy fast).
KIP-405 enables Kafka brokers to upload log segments to remote tiered storage, this opens the door to many new use cases, one of them is having consumers to fetch from the tiered storage directly.
Currently, when a new consumer or a fallen-off consumer requires fetching messages from a while ago, and those messages are no longer present in the Kafka broker's local storage, the broker must download the message from the remote tiered storage and subsequently transfer the data back to the consumer. This process involves two data transfers, resulting in increased network latency and transfer costs. Critically, when a broker downloads the data segment from remote storage and stores it locally, it imposes additional IOPS on the network and local disk capacity. Furthermore, this action negatively impacts the page cache established for messages closer to the end of the message queue.
With the data segment file already present on remote tiered storage, there is no reason why the consumer cannot directly fetch them from remote storage without affecting the broker performance. To address this gap, we propose here to allow the consumers to fetch from the remote tiered storage.
This feature will also be highly beneficial for serving large backfill requests from consumer clients from data analytics and streaming applications such as Pinot or Flink.
This KIP is focused on the design on broker side to support remote tiered storage fetch from the consumer, for consumer side design please refer to KIP-1254.
In order to support fetching from the remote tiered storage, the consumer needs to be notified about the location of the remote log segment file for the fetch offset. This can be done either by (1) the consumer subscribes the tiered storage metadata topic: __remote_log_metadata, filters to the partition it interests in, parses each metadata message and tracks the state change; (2) have the leader broker notifies the consumer about the location of the remote log segment file during the consumer FetchRequest/Response message exchange. We recommend the second option: have the leader broker notifies the consumer about the location.
The reason we favor the broker/consumer location exchange is because:
We propose to extend the Fetch API to convey the remote log segment location information.
The consumer client will initiate the FetchRequest with an extra boolean parameter: RemoteLogSegmentLocationRequested which indicates the client is able to read from remote storage and is interested to get the location of remote log segment file on remote storage. When the leader broker receives FetchRequest from the consumer and it finds out it no longer has the requested fetch offset in the local storage, it can respond with an empty Records and with the extra information about the location of the remote log segment. This is very similar to how KIP-392 responds with empty Records and preferred read replica in FetchResponse to the consumer.
The broker will perform the standard access and authorization check against the fetch request based on Kafka Access Control configuration.
However since the consumer can later read the object in Object Store from the location information in FetchResponse, some organizations might have concerns on the access in Object Store for the consumer. Most of the Object Store has its own ACL configurations (for example S3's IAM role/policy configuration) to manage specific client access. For the organizations who wants a simple way to provide another layer of protection, the broker can protect the location information with a signing mechanism provided by the cloud provider (for example, presigned URL in S3 or SAS token with Azure), this way the client will only be able to access a specific object in object store within a limited time.
If we choose this design, we will add a method getSignedRemoteLogSegmentLocation(RemoteLogSegmentId, CustomMetadata) in RemoteStorageManager interface to retrieved the signed url for the segment location from the cloud provider and add a field SignedRemoteLogSegmentLocation in the FetchResponse to return to the consumer.
Once the consumer is aware of the location of the remote log segment file, it can initiate the read from remote tiered storage and parses the content in log segment file and converts the message into ConsumerRecord objects; We will address them in the separate KIP since this work is dependent on the programming language choice on the consumer side. (Note we have published KIP-1254 for the enhancement needed on Java consumer side.)
We plan to support transaction messages in the phase II of this project so we didn’t include transactional message support structure as mandatory fields in this KIP. Nonetheless here is the idea on how to support transaction messages:
If the consumer is reading with ReadUncommitted isolation level, no special filter is needed since it should be able to read all messages from server log.
If the consumer is reading with ReadCommitted isolation level, for messages involved in transactions, two special logic are needed on the consumer side to process based on transaction boundary:
We are not sure whether queue (shared group) is already supported on tiered storage, but if we need to support it here are some thoughts:
The exchange of messages is now between ShareConsumer and broker through ShareFetchRequest/ShareFetchResponse. We can replicate the design from Consumer/Broker through FetchRequest/FetchResponse into the ShareFetch message flow.
However, the broker is also acting as share partition leader and needs to manage and distribute the inflight messages (the messages between SPSO and SPEO) to share consumers. If we need to maintain this logic, we would need to nominate one of the consumers to act as share partition leader.
Sometimes the consumer might only want to read a subset of messages within an offset range. Kafka log segment file can be very big (on the order of GB), having the consumer read the whole GB long segment and then find the offset range might not be performant. We would need the OffsetIndex file to help us to quickly narrow down the search. There are also two options to expose the offset index to the consumer with the second option as the preference:
Over the time the broker might evolve the storage format of the log files which makes the earlier version of the client not able to read the log file anymore. To protect this kind of version incompatibility issue, we can introduce another field in FetchRequest: SupportedStorageFormatVersions, the consumer will fill in this field with the server side storage format versions it understands (we can mark the current storage format as ApachKafkaV1), the server can fallback to the old behavior if it thinks the client version is too low. And we mark this field as list type to contain a list of versions the client supports, so in the case when the client is newer while the server is older (forward-compatibility case), the server can still respond with the new behavior if server’s old version is still in the list of versions client understands.
This new field in FetchRequest can also handle the case that some Kafka-API-compabile vendors (e.g. Apache Pulsar, WarpStreams, AutoMQ) use their own proprietary storage format.
We will extend the FetchRequest API by adding a new field RemoteLogSegmentLocationRequested to indicate the client has the capability to read from remote storage and is interested in getting the location of remote log segment file on remote storage.
{
"validVersions": "0-18",
"fields": [
{ "name": "RackId", ... }
{ "name": "RemoteLogSegmentLocationRequested", "type": "bool", "versions": "18+", "default": "false", "ignorable": true,
"about": "Indicate whether the client is able to read from remote storage and interested in getting the location of remote log segment file"}
]
}
|
We will extend the FetchResponse API in order to convey the location of the remote log segment file to the consumer client.
{
"validVersions": "0-18",
"fields": [
{ "name": "Responses", "type": "[]FetchableTopicResponse", "versions": "0+",
{ "name": "Partitions", "type": "[]PartitionData", "versions": "0+",
{ "name": "PreferredReadReplica", ... },
{ "name": "RemoteLogSegmentId", "type": "uuid", "versions": "18+", "ignorable": true,
"about": "The uuid of the remote log segment id"},
{ "name": "RemoteLogSegmentCustomMetadata", "type": "bytes", "versions": "18+", "ignorable": true,
"about": "The byte[] of the custom metadata of the remote log segment metadata"},
]}
]},
]
}
|
Two new fields are added to FetchResponse.Partitions message structure:
These two fields should be sufficient to resolve the physical file path on remote tiered storage.
Compatibility, Deprecation, and Migration Plan
The change is backwards compatible with previous versions. The new parameter RemoteLogSegmentLocationRequested in consumer FetchRequest is false by default to not to change the FetchResponse behavior. And If the receiving broker does not support this feature, it will resort to the old behavior of fetching old messages from remote tiered storage and sending them back to the consumer client.