Authors: Thomas Thornton, Henry Cai
Current state: Under Discussion
Depends On: KIP-1248
Discussion thread: [DISCUSS] KIP-1254: Kafka Consumer Support for Remote Tiered Storage Fetch
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-1248 introduces protocol changes to allow brokers to allow consumers to read remote log segments directly, reducing network bandwidth/cost and disk IOPs.
However, the broker-side protocol changes are only one half of the solution. Consumer clients must be able to:
This KIP defines the general consumer client interface, to be used in the respective consumer-client languages.
The core record parsing logic exists in the clients module:
Class | Location | Purpose |
RemoteLogInputStream | Iterates RecordBatch from InputStream | |
MemoryRecords | Wraps ByteBuffer as usable records | |
CompletedFetch | Transaction filtering for read_committed | |
DefaultRecordBatch | Batch parsing (v2+ format) |
Consumer parsing flow:
This is the same parsing flow the broker uses internally.
Optimization for partial reads: To avoid downloading entire segments (possibly multiple GBs):
This approach keeps complexity on the broker side and simplifies the consumer.
The Kafka consumer has two runtime modes: AsyncKafkaConsumer (the new threading model with separate application and network threads) and ClassicKafkaConsumer (the legacy single-threaded model). Both modes will support direct remote storage fetches with the same RemoteStorageFetchManager plugin, but the internal flow differs slightly. We describe each below.
Within the network thread:

The legacy path follows similar logic:

Raw segments may contain messages from aborted transactions. The handling depends on isolation level:
We favor the broker providing transaction info rather than having consumers download transaction index files, as:
Status: Not supported in this KIP. Planned for a subsequent KIP.
Share groups require the broker to mediate record distribution via SharePartition.acquire(). Direct consumer fetch is feasible in a future KIP where the broker uses OffsetIndex to determine byte ranges, returns offset metadata and segment location (instead of records), and consumers fetch record bytes directly from remote tiered storage. The transaction filtering logic in this KIP would extend to that design.
The consumer falls back to broker-mediated fetch when remote fetch fails:
Condition | Behavior |
Remote fetch timeout exceeded (fetch.remote.read.timeout.ms exceeded) | Re-issue FetchRequest with RemoteLogSegmentLocationRequested=false |
Connection timeout (fetch.remote.connect.timeout.ms exceeded) | Re-issue FetchRequest with RemoteLogSegmentLocationRequested=false |
Authentication failure | Re-issue FetchRequest with RemoteLogSegmentLocationRequested=false |
RemoteStorageFetcher not configured | Never set RemoteLogSegmentLocationRequested=true |
To handle storage format evolution, consumers include SupportedStorageFormatVersions in FetchRequest:
This also enables Kafka-API-compatible vendors with proprietary storage formats to participate (e.g. Apache Pulsar, WarpStreams, AutoMQ)
The security model is implementation-specific to each RemoteStorageFetcher plugin. We offer some thoughts on the different approaches, but this entirely depends on specific cloud-provider and is not specified in this KIP:
Presigned URLs (Preferred):
Local Credentials:
A pluggable, cloud-provider agnostic interface for fetching data from remote storage:
/**
* Interface for fetching log segment data directly from remote tiered storage.
* Implementations are cloud-provider specific (e.g., S3, GCS, Azure Blob)
*/
public interface RemoteStorageFetcher {
/** Fetch log segment data from remote storage
* @param remoteLogSegmentMetadata metadata about the remote log segment
* @param startPosition start byte position in the segment (inclusive)
* @param endPosition end byte position in the segment (exclusive) or -1 to read to end of segment
* @return InputStream of the requested log segment data
* @throws RemoteStorageException if there are errors fetching the segment
*/
InputStream fetchLogSegment(RemoteLogSegmentMetadata remoteLogSegmentMetadata,
int startPosition,
int endPosition) throws RemoteStorageException;
} |
Field | Type | Description |
RemoteLogSegmentLocationRequested | boolean | Whether consumer requests remote segment location |
SupportedStorageFormatVersions | []string | Storage format versions the consumer can parse |
Field | Type | Description |
RemoteLogSegmentId | UUID | Unique identifier of the remote segment |
RemoteLogSegmentCustomMetadata | bytes | Provider-specific metadata |
Note: existing abortedTransactions field will be populated by brokers for remote segments.
Config | Type | Default | Description |
fetch.remote.enabled | boolean | false | Controls whether consumers set RemoteLogSegmentLocationRequested=true |
remote.storage.fetcher.class | string | null | Implementation class for RemoteStorageFetcher interface |
remote.storage.fetcher.class.path | string | null | Classpath for loading RemoteStorageFetcher implementation |
int | 1000 | Connection timeout for remote storage. Triggers fallback on timeout | |
int | 5000 | Read timeout for remote storage. Triggers fallback on timeout |
New consumer metrics will be added following the existing kafka.consumer:type=consumer-fetch-manager-metrics naming convention including:
Detailed metric definitions will be finalized during implementation.
Class | Change Type | Description |
RemoteStorageFetcher | New interface | Cloud-agnostic interface for fetching from remote storage |
RemoteStorageNetworkClient | New class | Routes requests to remote storage, converts responses |
SubscriptionState | Modified | Add RemoteLogSegmentId, RemoteLogSegmentCustomMetadata fields |
UnsentRequest | Modified | Add remote segment location fields |
FetchCollector | Modified | Extract remote segment location from FetchResponse |
Scenario | Behavior |
New brokers, old consumers | Consumer never sets RemoteLogSegmentLocationRequested=true |
Old brokers, new consumers | Broker ignore the field, behaves as before |
Format mismatch | Broker checks SupportedStorageFormatVersions, falls back if incompatible |
Disabling fetch.remote.enabled immediately returns to typical broker fetches. No data loss or protocol issues occur.
Alternative | Reason for Rejection |
Broker-side filtering | Defeats the purpose of reducing broker load |
Reuse RemoteStorageManager | Too bulky for read-only client use, violates interface segregation |
Consumer downloads index files | Adds complexity; broker can provide needed info more efficiently |