You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 5 Next »

Authors: Henry Cai, Thomas Thornton

Status

Current state: "Under Discussion"

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).

Motivation

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.

Proposed Changes

Know the location of remote log segment file

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:

  1. __remote_log_metaddata topic is a congested topic in tiered storage which contains metadata for all the tiered storage enabled topic partitions and the client also needs to be aware that each metadata needs to go through several state changes.  The topic includes the metadata history of all the remote log segment from day 1 even if the segment file itself is already removed from tiered storage; There are lot of reading/filtering and caching needs to happen on the consumer side;
  2. With many programming languages to support on the consumer side and given the trend of moving complex consumer logic to the broker side, we feel it is more natural to have the broker to keep track of the remote tiered storage segment file and exchange the info with the consumer.  This is similar to the design choice (whether consumer or the broker keep tracks of preferred read replica) during KIP-392: Allow consumers to fetch from closest replica design where it also favors to have the broker exchange the preferred read replica information with the consumer.

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.

Authorization check

The broker will still perform the standard authorization check against the fetch request.

Fetch and Read Remote Log segment file

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 follow-up KIP since this work is dependent on the programming language choice on the consumer side.

Handle Transaction Messages

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:

  1. LSO (Last Stable Offset) marks the end of the last committed transaction.  The client should not see messages beyond LSO.  When a leader broker generates FetchResponse normally it will make sure it will not send the messages beyond LSO.  When the consumer is reading directly from server logs from remote storage, it needs to stop at the LSO boundary. Fortunately LSO field is present in FetchResponseData.PartitionData so the consumer can use this information to stop the message consumption;
  2. Aborted Transactions contains the message offsets for all the messages in an aborted transaction, the consumer client will need to filter out those messages before it can be seen by the user;  There are two possible implementation ideas (and we favor option b: broker retrieve the aborted transaction content):
    1. AbortedTransactions are stored in TransactionIndex file and working together with ProducerSnapshot index file, both indexes are stored on broker side as well as on remote tiered storage side.  The leader broker can return the location of those two index files back to the consumer client and consumer will download both of them from remote storage to local and then do the filtering accordingly;
    2. Broker can retrieve the aborted transaction from remote tiered storage and then set it in FetchResponse.PartitionData structure.  Although this is an extra downloading on broker side but since this is a small index file normally (since transaction abort should be a rare case), downloading that index should not cause too much performance issue on the broker side and this can greatly reduce the complexity of consumer code;

Handle Queue

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.

Consumer read a subset of messages

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:

  1. When the leader broker respond FetchResponse back to the consumer client, it can also add the location of OffsetIndex file (which is stored on remote tiered storage) and have client download the index file and do the necessary index search;
  2. The broker will download and read the OffsetIndex file and sets the corresponding begin/end byte position in the file back to the consumer client;  This way the client only needs to download the data log segment within a small range marked by begin/end file position;  Since the OffsetIndex file is small comparing to the log segment file and this approach can simplify the consumer side logic, we prefer this option;

Version compatibility check with broker

Over the time he 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.

Public Interfaces

Protocol Changes

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.

 


FetchRequest
{
  "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.


FetchResponse
{
  "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.

Rejected Alternatives

  1. Having 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;  We don’t recommend this approach because:
    1. __remote_log_metaddata topic is a congested topic in tiered storage which contains metadata for all the tiered storage enabled topic partitions and each metadata needs to go through several state changes.  The topic includes the metadata history of all the remote log segment from day 1 even if the segment file itself is already removed from tiered storage; There are lot of reading/filtering and caching needs to happen on the consumer side;
    2. With many programming languages to support on the consumer side and given the trend of moving complex consumer logic to the broker side, we feel it is more natural to have the broker to keep track of the remote tiered storage segment file and exchange the info with the consumer.  This is similar to the design choice (whether consumer or the broker keep tracks of preferred read replica) during KIP-392: Allow consumers to fetch from closest replica design where it also favors to have the broker exchange the preferred read replica information with the consumer.
  2. Launch new brokers to serve the large backfill read use cases.  When the consumer asks the leader broker to serve FetchRequest, the leader broker will respond with those new brokers' ids in FetchResponse so the consumer will subsequently asks those new brokers to serve FetchRequest.  This is very similar to how leader broker responded with preferredReadReplica in KIP-392.  Those new brokers can still download log segments from remote storage before they can serve FetchRequest.  In this approach, the network latency/cost is not avoided, but we can keep the current running brokers not being affected by reads from large backfill consumers.  This approach has minimal code changes needed on the consumer side, but it has the following drawbacks:
    1.  In order not to affect the performance of the current running brokers, we need to group the brokers into different pools (a pool to serve the normal producing/consumer use cases, a pool to serve the backfill or other maintenance tasks), this is a new concept for Kafka community;
    2. The user needs to manually start/stop those new backfill-dedicated brokers before/after backfill task;
    3. In order to quickly bootstrap those new brokers to ready-to-serve state, we need KIP-1023: Follower Fetch from tiered offset to be implemented first.  With KIP-1023, the new brokers only needs to replicate a few minutes of data from leader broker to be considered in-sync (the rest of the data can be downloaded later lazily from tiered storage);
    4. Network cost/latency related to 2-hop data transfer (from remote storage to broker, from broker to consumer) is still there.
  • No labels