Search in sources :

Example 11 with FetchResponseData

use of org.apache.kafka.common.message.FetchResponseData in project kafka by apache.

the class KafkaRaftClientTest method testPurgatoryFetchCompletedByFollowerTransition.

@Test
public void testPurgatoryFetchCompletedByFollowerTransition() throws Exception {
    int localId = 0;
    int voter1 = localId;
    int voter2 = localId + 1;
    int voter3 = localId + 2;
    int epoch = 5;
    Set<Integer> voters = Utils.mkSet(voter1, voter2, voter3);
    RaftClientTestContext context = RaftClientTestContext.initializeAsLeader(localId, voters, epoch);
    // Follower sends a fetch which cannot be satisfied immediately
    context.deliverRequest(context.fetchRequest(epoch, voter2, 1L, epoch, 500));
    context.client.poll();
    assertTrue(context.channel.drainSendQueue().stream().noneMatch(msg -> msg.data() instanceof FetchResponseData));
    // Now we get a BeginEpoch from the other voter and become a follower
    context.deliverRequest(context.beginEpochRequest(epoch + 1, voter3));
    context.pollUntilResponse();
    context.assertElectedLeader(epoch + 1, voter3);
    // We expect the BeginQuorumEpoch response and a failed Fetch response
    context.assertSentBeginQuorumEpochResponse(Errors.NONE, epoch + 1, OptionalInt.of(voter3));
    // The fetch should be satisfied immediately and return an error
    MemoryRecords fetchedRecords = context.assertSentFetchPartitionResponse(Errors.NOT_LEADER_OR_FOLLOWER, epoch + 1, OptionalInt.of(voter3));
    assertEquals(0, fetchedRecords.sizeInBytes());
}
Also used : Records(org.apache.kafka.common.record.Records) Assertions.assertThrows(org.junit.jupiter.api.Assertions.assertThrows) NotLeaderException(org.apache.kafka.raft.errors.NotLeaderException) Assertions.assertNotNull(org.junit.jupiter.api.Assertions.assertNotNull) Arrays(java.util.Arrays) Assertions.assertNotEquals(org.junit.jupiter.api.Assertions.assertNotEquals) Assertions.assertNull(org.junit.jupiter.api.Assertions.assertNull) TimeoutException(java.util.concurrent.TimeoutException) CompletableFuture(java.util.concurrent.CompletableFuture) DescribeQuorumRequest(org.apache.kafka.common.requests.DescribeQuorumRequest) ClusterAuthorizationException(org.apache.kafka.common.errors.ClusterAuthorizationException) OptionalInt(java.util.OptionalInt) ByteBuffer(java.nio.ByteBuffer) FetchResponseData(org.apache.kafka.common.message.FetchResponseData) Record(org.apache.kafka.common.record.Record) ArrayList(java.util.ArrayList) Collections.singletonList(java.util.Collections.singletonList) ReplicaState(org.apache.kafka.common.message.DescribeQuorumResponseData.ReplicaState) OptionalLong(java.util.OptionalLong) MemoryPool(org.apache.kafka.common.memory.MemoryPool) RecordBatchTooLargeException(org.apache.kafka.common.errors.RecordBatchTooLargeException) Assertions.assertFalse(org.junit.jupiter.api.Assertions.assertFalse) RecordBatch(org.apache.kafka.common.record.RecordBatch) MutableRecordBatch(org.apache.kafka.common.record.MutableRecordBatch) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Utils(org.apache.kafka.common.utils.Utils) EndQuorumEpochResponse(org.apache.kafka.common.requests.EndQuorumEpochResponse) DEFAULT_ELECTION_TIMEOUT_MS(org.apache.kafka.raft.RaftClientTestContext.Builder.DEFAULT_ELECTION_TIMEOUT_MS) TestUtils(org.apache.kafka.test.TestUtils) EndQuorumEpochResponseData(org.apache.kafka.common.message.EndQuorumEpochResponseData) BufferAllocationException(org.apache.kafka.raft.errors.BufferAllocationException) Set(java.util.Set) TestUtils.assertFutureThrows(org.apache.kafka.test.TestUtils.assertFutureThrows) IOException(java.io.IOException) Test(org.junit.jupiter.api.Test) Mockito(org.mockito.Mockito) MemoryRecords(org.apache.kafka.common.record.MemoryRecords) List(java.util.List) Metrics(org.apache.kafka.common.metrics.Metrics) Assertions.assertTrue(org.junit.jupiter.api.Assertions.assertTrue) BeginQuorumEpochResponseData(org.apache.kafka.common.message.BeginQuorumEpochResponseData) Errors(org.apache.kafka.common.protocol.Errors) Optional(java.util.Optional) KafkaMetric(org.apache.kafka.common.metrics.KafkaMetric) Collections(java.util.Collections) VoteResponseData(org.apache.kafka.common.message.VoteResponseData) FetchResponseData(org.apache.kafka.common.message.FetchResponseData) MemoryRecords(org.apache.kafka.common.record.MemoryRecords) Test(org.junit.jupiter.api.Test)

Example 12 with FetchResponseData

use of org.apache.kafka.common.message.FetchResponseData in project kafka by apache.

the class KafkaRaftClientTest method testFetchResponseIgnoredAfterBecomingFollowerOfDifferentLeader.

@Test
public void testFetchResponseIgnoredAfterBecomingFollowerOfDifferentLeader() throws Exception {
    int localId = 0;
    int voter1 = localId;
    int voter2 = localId + 1;
    int voter3 = localId + 2;
    int epoch = 5;
    // Start out with `voter2` as the leader
    Set<Integer> voters = Utils.mkSet(voter1, voter2, voter3);
    RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters).withElectedLeader(epoch, voter2).build();
    context.assertElectedLeader(epoch, voter2);
    // Wait until we have a Fetch inflight to the leader
    context.pollUntilRequest();
    int fetchCorrelationId = context.assertSentFetchRequest(epoch, 0L, 0);
    // Now receive a BeginEpoch from `voter3`
    context.deliverRequest(context.beginEpochRequest(epoch + 1, voter3));
    context.client.poll();
    context.assertElectedLeader(epoch + 1, voter3);
    // The fetch response from the old leader returns, but it should be ignored
    Records records = context.buildBatch(0L, 3, Arrays.asList("a", "b"));
    FetchResponseData response = context.fetchResponse(epoch, voter2, records, 0L, Errors.NONE);
    context.deliverResponse(fetchCorrelationId, voter2, response);
    context.client.poll();
    assertEquals(0, context.log.endOffset().offset);
    context.assertElectedLeader(epoch + 1, voter3);
}
Also used : FetchResponseData(org.apache.kafka.common.message.FetchResponseData) Records(org.apache.kafka.common.record.Records) MemoryRecords(org.apache.kafka.common.record.MemoryRecords) Test(org.junit.jupiter.api.Test)

Example 13 with FetchResponseData

use of org.apache.kafka.common.message.FetchResponseData in project kafka by apache.

the class KafkaRaftClientTest method testFollowerLogReconciliation.

@Test
public void testFollowerLogReconciliation() throws Exception {
    int localId = 0;
    int otherNodeId = 1;
    int epoch = 5;
    int lastEpoch = 3;
    Set<Integer> voters = Utils.mkSet(localId, otherNodeId);
    RaftClientTestContext context = new RaftClientTestContext.Builder(localId, voters).withElectedLeader(epoch, otherNodeId).appendToLog(lastEpoch, Arrays.asList("foo", "bar")).appendToLog(lastEpoch, Arrays.asList("baz")).build();
    context.assertElectedLeader(epoch, otherNodeId);
    assertEquals(3L, context.log.endOffset().offset);
    context.pollUntilRequest();
    int correlationId = context.assertSentFetchRequest(epoch, 3L, lastEpoch);
    FetchResponseData response = context.divergingFetchResponse(epoch, otherNodeId, 2L, lastEpoch, 1L);
    context.deliverResponse(correlationId, otherNodeId, response);
    // Poll again to complete truncation
    context.client.poll();
    assertEquals(2L, context.log.endOffset().offset);
    // Now we should be fetching
    context.client.poll();
    context.assertSentFetchRequest(epoch, 2L, lastEpoch);
}
Also used : FetchResponseData(org.apache.kafka.common.message.FetchResponseData) Test(org.junit.jupiter.api.Test)

Example 14 with FetchResponseData

use of org.apache.kafka.common.message.FetchResponseData in project kafka by apache.

the class KafkaRaftClient method handleFetchRequest.

/**
 * Handle a Fetch request. The fetch offset and last fetched epoch are always
 * validated against the current log. In the case that they do not match, the response will
 * indicate the diverging offset/epoch. A follower is expected to truncate its log in this
 * case and resend the fetch.
 *
 * This API may return the following errors:
 *
 * - {@link Errors#INCONSISTENT_CLUSTER_ID} if the cluster id is presented in request
 *     but different from this node
 * - {@link Errors#BROKER_NOT_AVAILABLE} if this node is currently shutting down
 * - {@link Errors#FENCED_LEADER_EPOCH} if the epoch is smaller than this node's epoch
 * - {@link Errors#INVALID_REQUEST} if the request epoch is larger than the leader's current epoch
 *     or if either the fetch offset or the last fetched epoch is invalid
 */
private CompletableFuture<FetchResponseData> handleFetchRequest(RaftRequest.Inbound requestMetadata, long currentTimeMs) {
    FetchRequestData request = (FetchRequestData) requestMetadata.data;
    if (!hasValidClusterId(request.clusterId())) {
        return completedFuture(new FetchResponseData().setErrorCode(Errors.INCONSISTENT_CLUSTER_ID.code()));
    }
    if (!hasValidTopicPartition(request, log.topicPartition(), log.topicId())) {
        // Until we support multi-raft, we treat topic partition mismatches as invalid requests
        return completedFuture(new FetchResponseData().setErrorCode(Errors.INVALID_REQUEST.code()));
    }
    // If the ID is valid, we can set the topic name.
    request.topics().get(0).setTopic(log.topicPartition().topic());
    FetchRequestData.FetchPartition fetchPartition = request.topics().get(0).partitions().get(0);
    if (request.maxWaitMs() < 0 || fetchPartition.fetchOffset() < 0 || fetchPartition.lastFetchedEpoch() < 0 || fetchPartition.lastFetchedEpoch() > fetchPartition.currentLeaderEpoch()) {
        return completedFuture(buildEmptyFetchResponse(Errors.INVALID_REQUEST, Optional.empty()));
    }
    FetchResponseData response = tryCompleteFetchRequest(request.replicaId(), fetchPartition, currentTimeMs);
    FetchResponseData.PartitionData partitionResponse = response.responses().get(0).partitions().get(0);
    if (partitionResponse.errorCode() != Errors.NONE.code() || FetchResponse.recordsSize(partitionResponse) > 0 || request.maxWaitMs() == 0) {
        return completedFuture(response);
    }
    CompletableFuture<Long> future = fetchPurgatory.await(fetchPartition.fetchOffset(), request.maxWaitMs());
    return future.handle((completionTimeMs, exception) -> {
        if (exception != null) {
            Throwable cause = exception instanceof ExecutionException ? exception.getCause() : exception;
            // If the fetch timed out in purgatory, it means no new data is available,
            // and we will complete the fetch successfully. Otherwise, if there was
            // any other error, we need to return it.
            Errors error = Errors.forException(cause);
            if (error != Errors.REQUEST_TIMED_OUT) {
                logger.debug("Failed to handle fetch from {} at {} due to {}", request.replicaId(), fetchPartition.fetchOffset(), error);
                return buildEmptyFetchResponse(error, Optional.empty());
            }
        }
        // FIXME: `completionTimeMs`, which can be null
        logger.trace("Completing delayed fetch from {} starting at offset {} at {}", request.replicaId(), fetchPartition.fetchOffset(), completionTimeMs);
        return tryCompleteFetchRequest(request.replicaId(), fetchPartition, time.milliseconds());
    });
}
Also used : Errors(org.apache.kafka.common.protocol.Errors) FetchResponseData(org.apache.kafka.common.message.FetchResponseData) FetchRequestData(org.apache.kafka.common.message.FetchRequestData) OptionalLong(java.util.OptionalLong) ExecutionException(java.util.concurrent.ExecutionException)

Aggregations

FetchResponseData (org.apache.kafka.common.message.FetchResponseData)14 MemoryRecords (org.apache.kafka.common.record.MemoryRecords)6 Test (org.junit.jupiter.api.Test)6 Records (org.apache.kafka.common.record.Records)5 Errors (org.apache.kafka.common.protocol.Errors)4 ByteBuffer (java.nio.ByteBuffer)3 OptionalLong (java.util.OptionalLong)3 ArrayList (java.util.ArrayList)2 Collections (java.util.Collections)2 List (java.util.List)2 Optional (java.util.Optional)2 OptionalInt (java.util.OptionalInt)2 FetchRequestData (org.apache.kafka.common.message.FetchRequestData)2 ByteBufferAccessor (org.apache.kafka.common.protocol.ByteBufferAccessor)2 ObjectSerializationCache (org.apache.kafka.common.protocol.ObjectSerializationCache)2 RecordBatch (org.apache.kafka.common.record.RecordBatch)2 Utils (org.apache.kafka.common.utils.Utils)2 IOException (java.io.IOException)1 Arrays (java.util.Arrays)1 Collections.singletonList (java.util.Collections.singletonList)1