1818package kafka .server
1919
2020import com .yammer .metrics .core .Meter
21- import io .aiven .inkless .control_plane .FindBatchRequest
21+ import io .aiven .inkless .control_plane .FindBatchResponse
2222import kafka .utils .Logging
2323
2424import java .util .concurrent .TimeUnit
@@ -60,6 +60,7 @@ class DelayedFetch(
6060 minBytes : Option [Int ] = None ,
6161 responseCallback : Seq [(TopicIdPartition , FetchPartitionData )] => Unit ,
6262) extends DelayedOperation (maxWaitMs.getOrElse(params.maxWaitMs)) with Logging {
63+ var maybeBatchCoordinates : Option [Map [TopicIdPartition , FindBatchResponse ]] = None
6364
6465 override def toString : String = {
6566 s " DelayedFetch(params= $params" +
@@ -153,7 +154,15 @@ class DelayedFetch(
153154 }
154155 }
155156
156- tryCompleteDiskless(disklessFetchPartitionStatus) match {
157+ // adjust the max bytes for diskless fetches based on the percentage of diskless partitions
158+ // Complete the classic fetches first
159+ val classicRequestsSize = classicFetchPartitionStatus.size.toFloat
160+ val disklessRequestsSize = disklessFetchPartitionStatus.size.toFloat
161+ val totalRequestsSize = classicRequestsSize + disklessRequestsSize
162+ val disklessPercentage = disklessRequestsSize / totalRequestsSize
163+ val disklessParams = replicaManager.fetchParamsWithNewMaxBytes(params, disklessPercentage)
164+
165+ tryCompleteDiskless(disklessFetchPartitionStatus, disklessParams.maxBytes) match {
157166 case Some (disklessAccumulatedSize) => accumulatedSize += disklessAccumulatedSize
158167 case None => forceComplete()
159168 }
@@ -174,53 +183,55 @@ class DelayedFetch(
174183 * Case D: The fetch offset is equal to the end offset, meaning that we have reached the end of the log
175184 * Upon completion, should return whatever data is available for each valid partition
176185 */
177- private def tryCompleteDiskless (fetchPartitionStatus : Seq [(TopicIdPartition , FetchPartitionStatus )]): Option [Long ] = {
186+ private def tryCompleteDiskless (
187+ fetchPartitionStatus : Seq [(TopicIdPartition , FetchPartitionStatus )],
188+ disklessMaxBytes : Int
189+ ): Option [Long ] = {
178190 var accumulatedSize = 0L
179191 val fetchPartitionStatusMap = fetchPartitionStatus.toMap
180- val requests = fetchPartitionStatus.map { case (topicIdPartition, fetchStatus) =>
181- new FindBatchRequest (topicIdPartition, fetchStatus.startOffsetMetadata.messageOffset, fetchStatus.fetchInfo.maxBytes)
182- }
183- if (requests.isEmpty) return Some (0 )
184192
185- val response = try {
186- replicaManager.findDisklessBatches(requests, Int . MaxValue )
193+ maybeBatchCoordinates = try {
194+ Some ( replicaManager.findDisklessBatches(fetchPartitionStatus, disklessMaxBytes) )
187195 } catch {
188196 case e : Throwable =>
189197 error(" Error while trying to find diskless batches on delayed fetch." , e)
190198 return None // Case C
191199 }
192200
193- response.get.asScala.foreach { r =>
194- r.errors() match {
195- case Errors .NONE =>
196- if (r.batches().size() > 0 ) {
197- // Gather topic id partition from first batch. Same for all batches in the response.
198- val topicIdPartition = r.batches().get(0 ).metadata().topicIdPartition()
199- val endOffset = r.highWatermark()
200-
201- val fetchPartitionStatus = fetchPartitionStatusMap.get(topicIdPartition)
202- if (fetchPartitionStatus.isEmpty) {
203- warn(s " Fetch partition status for $topicIdPartition not found in delayed fetch $this. " )
204- return None // Case C
205- }
206-
207- val fetchOffset = fetchPartitionStatus.get.startOffsetMetadata
208- // If the fetch offset is greater than the end offset, it means that the log has been truncated
209- // If it is equal to the end offset, it means that we have reached the end of the log
210- // If the fetch offset is less than the end offset, we can accumulate the size of the batches
211- if (fetchOffset.messageOffset > endOffset) {
212- // Truncation happened
213- debug(s " Satisfying fetch $this since it is fetching later segments of partition $topicIdPartition. " )
214- return None // Case A
215- } else if (fetchOffset.messageOffset < endOffset) {
216- val bytesAvailable = r.estimatedByteSize(fetchOffset.messageOffset)
217- accumulatedSize += bytesAvailable // Case B: accumulate the size of the batches
218- } // Case D: same as fetchOffset == endOffset, no new data available
201+ maybeBatchCoordinates match {
202+ case Some (exists) =>
203+ exists.values.foreach { r =>
204+ r.errors() match {
205+ case Errors .NONE =>
206+ if (r.batches().size() > 0 ) {
207+ // Gather topic id partition from first batch. Same for all batches in the response.
208+ val topicIdPartition = r.batches().get(0 ).metadata().topicIdPartition()
209+ val endOffset = r.highWatermark()
210+
211+ val fetchPartitionStatus = fetchPartitionStatusMap.get(topicIdPartition)
212+ if (fetchPartitionStatus.isEmpty) {
213+ warn(s " Fetch partition status for $topicIdPartition not found in delayed fetch $this. " )
214+ return None // Case C
215+ }
216+
217+ val fetchOffset = fetchPartitionStatus.get.startOffsetMetadata
218+ // If the fetch offset is greater than the end offset, it means that the log has been truncated
219+ // If it is equal to the end offset, it means that we have reached the end of the log
220+ // If the fetch offset is less than the end offset, we can accumulate the size of the batches
221+ if (fetchOffset.messageOffset > endOffset) {
222+ // Truncation happened
223+ debug(s " Satisfying fetch $this since it is fetching later segments of partition $topicIdPartition. " )
224+ return None // Case A
225+ } else if (fetchOffset.messageOffset < endOffset) {
226+ val bytesAvailable = r.estimatedByteSize(fetchOffset.messageOffset)
227+ accumulatedSize += bytesAvailable // Case B: accumulate the size of the batches
228+ } // Case D: same as fetchOffset == endOffset, no new data available
229+ }
230+ case _ => return None // Case C
219231 }
220- case _ => return None // Case C
221- }
232+ }
233+ case None => // Case D
222234 }
223-
224235 Some (accumulatedSize)
225236 }
226237
@@ -272,13 +283,16 @@ class DelayedFetch(
272283
273284 if (disklessRequestsSize > 0 ) {
274285 // Classic fetches are complete, now handle diskless fetches
275- // adjust the max bytes for diskless fetches based on the percentage of diskless partitions
276- val disklessPercentage = disklessRequestsSize / totalRequestsSize
277- val disklessParams = replicaManager.fetchParamsWithNewMaxBytes(params, disklessPercentage)
278286 val disklessFetchInfos = disklessFetchPartitionStatus.map { case (tp, status) =>
279287 tp -> status.fetchInfo
280288 }
281- val disklessFetchResponseFuture = replicaManager.fetchDisklessMessages(disklessParams, disklessFetchInfos)
289+ val batchCoordinates = maybeBatchCoordinates match {
290+ case Some (batchCoordinates) => batchCoordinates
291+ case None =>
292+ responseCallback(Seq .empty)
293+ return
294+ }
295+ val disklessFetchResponseFuture = replicaManager.fetchDisklessMessages(batchCoordinates, disklessFetchInfos)
282296
283297 // Combine the classic fetch results with the diskless fetch results
284298 disklessFetchResponseFuture.whenComplete { case (disklessFetchPartitionData, _) =>
0 commit comments