Search in sources :

Example 6 with PersistentSubscription

use of com.yahoo.pulsar.broker.service.persistent.PersistentSubscription in project pulsar by yahoo.

the class PersistentTopicE2ETest method testMessageReplay.

/**
     * Verify: 
     * 1. Broker should not replay already acknowledged messages 
     * 2. Dispatcher should not stuck while dispatching new messages due to previous-replay 
     * of invalid/already-acked messages
     * 
     * @throws Exception
     */
@Test
public void testMessageReplay() throws Exception {
    final String topicName = "persistent://prop/use/ns-abc/topic2";
    final String subName = "sub2";
    Message msg;
    int totalMessages = 10;
    int replayIndex = totalMessages / 2;
    ConsumerConfiguration conf = new ConsumerConfiguration();
    conf.setSubscriptionType(SubscriptionType.Shared);
    conf.setReceiverQueueSize(1);
    Consumer consumer = pulsarClient.subscribe(topicName, subName, conf);
    Producer producer = pulsarClient.createProducer(topicName);
    PersistentTopic topicRef = (PersistentTopic) pulsar.getBrokerService().getTopicReference(topicName);
    assertNotNull(topicRef);
    PersistentSubscription subRef = topicRef.getPersistentSubscription(subName);
    PersistentDispatcherMultipleConsumers dispatcher = (PersistentDispatcherMultipleConsumers) subRef.getDispatcher();
    Field replayMap = PersistentDispatcherMultipleConsumers.class.getDeclaredField("messagesToReplay");
    replayMap.setAccessible(true);
    TreeSet<PositionImpl> messagesToReplay = Sets.newTreeSet();
    assertNotNull(subRef);
    // (1) Produce messages
    for (int i = 0; i < totalMessages; i++) {
        String message = "my-message-" + i;
        producer.send(message.getBytes());
    }
    MessageIdImpl firstAckedMsg = null;
    // (2) Consume and ack messages except first message
    for (int i = 0; i < totalMessages; i++) {
        msg = consumer.receive();
        consumer.acknowledge(msg);
        MessageIdImpl msgId = (MessageIdImpl) msg.getMessageId();
        if (i == 0) {
            firstAckedMsg = msgId;
        }
        if (i < replayIndex) {
            // (3) accumulate acked messages for replay
            messagesToReplay.add(new PositionImpl(msgId.getLedgerId(), msgId.getEntryId()));
        }
    }
    // (4) redelivery : should redeliver only unacked messages
    Thread.sleep(1000);
    replayMap.set(dispatcher, messagesToReplay);
    // (a) redelivery with all acked-message should clear messageReply bucket
    dispatcher.redeliverUnacknowledgedMessages(dispatcher.getConsumers().get(0));
    assertEquals(messagesToReplay.size(), 0);
    // (b) fill messageReplyBucket with already acked entry again: and try to publish new msg and read it
    messagesToReplay.add(new PositionImpl(firstAckedMsg.getLedgerId(), firstAckedMsg.getEntryId()));
    replayMap.set(dispatcher, messagesToReplay);
    // send new message
    final String testMsg = "testMsg";
    producer.send(testMsg.getBytes());
    // consumer should be able to receive only new message and not the
    dispatcher.consumerFlow(dispatcher.getConsumers().get(0), 1);
    msg = consumer.receive(1, TimeUnit.SECONDS);
    assertNotNull(msg);
    assertEquals(msg.getData(), testMsg.getBytes());
    consumer.close();
    producer.close();
}
Also used : Message(com.yahoo.pulsar.client.api.Message) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) MessageIdImpl(com.yahoo.pulsar.client.impl.MessageIdImpl) PersistentSubscription(com.yahoo.pulsar.broker.service.persistent.PersistentSubscription) Field(java.lang.reflect.Field) Consumer(com.yahoo.pulsar.client.api.Consumer) Producer(com.yahoo.pulsar.client.api.Producer) PersistentDispatcherMultipleConsumers(com.yahoo.pulsar.broker.service.persistent.PersistentDispatcherMultipleConsumers) PersistentTopic(com.yahoo.pulsar.broker.service.persistent.PersistentTopic) ConsumerConfiguration(com.yahoo.pulsar.client.api.ConsumerConfiguration) Test(org.testng.annotations.Test)

Example 7 with PersistentSubscription

use of com.yahoo.pulsar.broker.service.persistent.PersistentSubscription in project pulsar by yahoo.

the class PersistentDispatcherFailoverConsumerTest method testAddRemoveConsumer.

@Test
public void testAddRemoveConsumer() throws Exception {
    log.info("--- Starting PersistentDispatcherFailoverConsumerTest::testAddConsumer ---");
    PersistentTopic topic = new PersistentTopic(successTopicName, ledgerMock, brokerService);
    PersistentSubscription sub = new PersistentSubscription(topic, cursorMock);
    int partitionIndex = 0;
    PersistentDispatcherSingleActiveConsumer pdfc = new PersistentDispatcherSingleActiveConsumer(cursorMock, SubType.Failover, partitionIndex, topic);
    // 1. Verify no consumers connected
    assertFalse(pdfc.isConsumerConnected());
    // 2. Add consumer
    Consumer consumer1 = new Consumer(sub, SubType.Exclusive, 1, /* consumer id */
    0, "Cons1", /* consumer name */
    50000, serverCnx, "myrole-1");
    pdfc.addConsumer(consumer1);
    List<Consumer> consumers = pdfc.getConsumers();
    assertTrue(consumers.get(0).consumerName() == consumer1.consumerName());
    assertEquals(1, consumers.size());
    // 3. Add again, duplicate allowed
    pdfc.addConsumer(consumer1);
    consumers = pdfc.getConsumers();
    assertTrue(consumers.get(0).consumerName() == consumer1.consumerName());
    assertEquals(2, consumers.size());
    // 4. Verify active consumer
    assertTrue(pdfc.getActiveConsumer().consumerName() == consumer1.consumerName());
    // 5. Add another consumer which does not change active consumer
    Consumer consumer2 = new Consumer(sub, SubType.Exclusive, 2, /* consumer id */
    0, "Cons2", /* consumer name */
    50000, serverCnx, "myrole-1");
    pdfc.addConsumer(consumer2);
    consumers = pdfc.getConsumers();
    assertTrue(pdfc.getActiveConsumer().consumerName() == consumer1.consumerName());
    assertEquals(3, consumers.size());
    // 6. Add a consumer which changes active consumer
    Consumer consumer0 = new Consumer(sub, SubType.Exclusive, 0, /* consumer id */
    0, "Cons0", /* consumer name */
    50000, serverCnx, "myrole-1");
    pdfc.addConsumer(consumer0);
    consumers = pdfc.getConsumers();
    assertTrue(pdfc.getActiveConsumer().consumerName() == consumer0.consumerName());
    assertEquals(4, consumers.size());
    // 7. Remove last consumer
    pdfc.removeConsumer(consumer2);
    consumers = pdfc.getConsumers();
    assertTrue(pdfc.getActiveConsumer().consumerName() == consumer0.consumerName());
    assertEquals(3, consumers.size());
    // 8. Verify if we can unsubscribe when more than one consumer is connected
    assertFalse(pdfc.canUnsubscribe(consumer0));
    // 9. Remove active consumer
    pdfc.removeConsumer(consumer0);
    consumers = pdfc.getConsumers();
    assertTrue(pdfc.getActiveConsumer().consumerName() == consumer1.consumerName());
    assertEquals(2, consumers.size());
    // 10. Attempt to remove already removed consumer
    String cause = "";
    try {
        pdfc.removeConsumer(consumer0);
    } catch (Exception e) {
        cause = e.getMessage();
    }
    assertEquals(cause, "Consumer was not connected");
    // 11. Remove active consumer
    pdfc.removeConsumer(consumer1);
    consumers = pdfc.getConsumers();
    assertTrue(pdfc.getActiveConsumer().consumerName() == consumer1.consumerName());
    assertEquals(1, consumers.size());
    // 11. With only one consumer, unsubscribe is allowed
    assertTrue(pdfc.canUnsubscribe(consumer1));
}
Also used : Consumer(com.yahoo.pulsar.broker.service.Consumer) PersistentDispatcherSingleActiveConsumer(com.yahoo.pulsar.broker.service.persistent.PersistentDispatcherSingleActiveConsumer) PersistentTopic(com.yahoo.pulsar.broker.service.persistent.PersistentTopic) PersistentSubscription(com.yahoo.pulsar.broker.service.persistent.PersistentSubscription) PersistentDispatcherSingleActiveConsumer(com.yahoo.pulsar.broker.service.persistent.PersistentDispatcherSingleActiveConsumer) ManagedLedgerException(org.apache.bookkeeper.mledger.ManagedLedgerException) Test(org.testng.annotations.Test)

Example 8 with PersistentSubscription

use of com.yahoo.pulsar.broker.service.persistent.PersistentSubscription in project pulsar by yahoo.

the class PersistentTopics method resetCursor.

@POST
@Path("/{property}/{cluster}/{namespace}/{destination}/subscription/{subName}/resetcursor/{timestamp}")
@ApiOperation(value = "Reset subscription to message position closest to absolute timestamp (in ms).", notes = "There should not be any active consumers on the subscription.")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"), @ApiResponse(code = 404, message = "Topic does not exist"), @ApiResponse(code = 405, message = "Not supported for global topics"), @ApiResponse(code = 412, message = "Subscription has active consumers") })
public void resetCursor(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace, @PathParam("destination") @Encoded String destination, @PathParam("subName") String subName, @PathParam("timestamp") long timestamp, @QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
    destination = decode(destination);
    DestinationName dn = DestinationName.get(domain(), property, cluster, namespace, destination);
    PartitionedTopicMetadata partitionMetadata = getPartitionedTopicMetadata(property, cluster, namespace, destination, authoritative);
    if (partitionMetadata.partitions > 0) {
        int numParts = partitionMetadata.partitions;
        int numPartException = 0;
        Exception partitionException = null;
        try {
            for (int i = 0; i < numParts; i++) {
                pulsar().getAdminClient().persistentTopics().resetCursor(dn.getPartition(i).toString(), subName, timestamp);
            }
        } catch (PreconditionFailedException pfe) {
            // throw the last exception if all partitions get this error
            // any other exception on partition is reported back to user
            ++numPartException;
            partitionException = pfe;
        } catch (Exception e) {
            log.warn("[{}] [{}] Failed to reset cursor on subscription {} to time {}", clientAppId(), dn, subName, timestamp, e);
            throw new RestException(e);
        }
        // report an error to user if unable to reset for all partitions
        if (numPartException == numParts) {
            log.warn("[{}] [{}] Failed to reset cursor on subscription {} to time {}", clientAppId(), dn, subName, timestamp, partitionException);
            throw new RestException(Status.PRECONDITION_FAILED, partitionException.getMessage());
        } else if (numPartException > 0 && log.isDebugEnabled()) {
            log.debug("[{}][{}] partial errors for reset cursor on subscription {} to time {} - ", clientAppId(), destination, subName, timestamp, partitionException);
        }
    } else {
        validateAdminOperationOnDestination(dn, authoritative);
        log.info("[{}][{}] received reset cursor on subscription {} to time {}", clientAppId(), destination, subName, timestamp);
        PersistentTopic topic = getTopicReference(dn);
        try {
            PersistentSubscription sub = topic.getPersistentSubscription(subName);
            checkNotNull(sub);
            sub.resetCursor(timestamp).get();
            log.info("[{}][{}] reset cursor on subscription {} to time {}", clientAppId(), dn, subName, timestamp);
        } catch (Exception e) {
            Throwable t = e.getCause();
            log.warn("[{}] [{}] Failed to reset cursor on subscription {} to time {}", clientAppId(), dn, subName, timestamp, e);
            if (e instanceof NullPointerException) {
                throw new RestException(Status.NOT_FOUND, "Subscription not found");
            } else if (e instanceof NotAllowedException) {
                throw new RestException(Status.METHOD_NOT_ALLOWED, e.getMessage());
            } else if (t instanceof SubscriptionBusyException) {
                throw new RestException(Status.PRECONDITION_FAILED, "Subscription has active connected consumers");
            } else if (t instanceof SubscriptionInvalidCursorPosition) {
                throw new RestException(Status.PRECONDITION_FAILED, "Unable to find position for timestamp specified -" + t.getMessage());
            } else {
                throw new RestException(e);
            }
        }
    }
}
Also used : NotAllowedException(com.yahoo.pulsar.broker.service.BrokerServiceException.NotAllowedException) RestException(com.yahoo.pulsar.broker.web.RestException) PersistentSubscription(com.yahoo.pulsar.broker.service.persistent.PersistentSubscription) RestException(com.yahoo.pulsar.broker.web.RestException) TopicBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.TopicBusyException) WebApplicationException(javax.ws.rs.WebApplicationException) PulsarClientException(com.yahoo.pulsar.client.api.PulsarClientException) PreconditionFailedException(com.yahoo.pulsar.client.admin.PulsarAdminException.PreconditionFailedException) SubscriptionBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionBusyException) NotFoundException(com.yahoo.pulsar.client.admin.PulsarAdminException.NotFoundException) NotAllowedException(com.yahoo.pulsar.broker.service.BrokerServiceException.NotAllowedException) KeeperException(org.apache.zookeeper.KeeperException) IOException(java.io.IOException) SubscriptionInvalidCursorPosition(com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionInvalidCursorPosition) PersistentTopic(com.yahoo.pulsar.broker.service.persistent.PersistentTopic) DestinationName(com.yahoo.pulsar.common.naming.DestinationName) PreconditionFailedException(com.yahoo.pulsar.client.admin.PulsarAdminException.PreconditionFailedException) SubscriptionBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionBusyException) PartitionedTopicMetadata(com.yahoo.pulsar.common.partition.PartitionedTopicMetadata) Path(javax.ws.rs.Path) POST(javax.ws.rs.POST) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Example 9 with PersistentSubscription

use of com.yahoo.pulsar.broker.service.persistent.PersistentSubscription in project pulsar by yahoo.

the class PersistentTopics method peekNthMessage.

@GET
@Path("/{property}/{cluster}/{namespace}/{destination}/subscription/{subName}/position/{messagePosition}")
@ApiOperation(value = "Peek nth message on a topic subscription.")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"), @ApiResponse(code = 404, message = "Topic, subscription or the message position does not exist") })
public Response peekNthMessage(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace, @PathParam("destination") @Encoded String destination, @PathParam("subName") String subName, @PathParam("messagePosition") int messagePosition, @QueryParam("authoritative") @DefaultValue("false") boolean authoritative) {
    destination = decode(destination);
    DestinationName dn = DestinationName.get(domain(), property, cluster, namespace, destination);
    PartitionedTopicMetadata partitionMetadata = getPartitionedTopicMetadata(property, cluster, namespace, destination, authoritative);
    if (partitionMetadata.partitions > 0) {
        throw new RestException(Status.METHOD_NOT_ALLOWED, "Peek messages on a partitioned topic is not allowed");
    }
    validateAdminOperationOnDestination(dn, authoritative);
    PersistentTopic topic = getTopicReference(dn);
    PersistentReplicator repl = null;
    PersistentSubscription sub = null;
    Entry entry = null;
    if (subName.startsWith(topic.replicatorPrefix)) {
        repl = getReplicatorReference(subName, topic);
    } else {
        sub = getSubscriptionReference(subName, topic);
    }
    try {
        if (subName.startsWith(topic.replicatorPrefix)) {
            entry = repl.peekNthMessage(messagePosition).get();
        } else {
            entry = sub.peekNthMessage(messagePosition).get();
        }
        checkNotNull(entry);
        PositionImpl pos = (PositionImpl) entry.getPosition();
        ByteBuf metadataAndPayload = entry.getDataBuffer();
        // moves the readerIndex to the payload
        MessageMetadata metadata = Commands.parseMessageMetadata(metadataAndPayload);
        ResponseBuilder responseBuilder = Response.ok();
        responseBuilder.header("X-Pulsar-Message-ID", pos.toString());
        for (KeyValue keyValue : metadata.getPropertiesList()) {
            responseBuilder.header("X-Pulsar-PROPERTY-" + keyValue.getKey(), keyValue.getValue());
        }
        if (metadata.hasPublishTime()) {
            responseBuilder.header("X-Pulsar-publish-time", DATE_FORMAT.format(Instant.ofEpochMilli(metadata.getPublishTime())));
        }
        // Decode if needed
        CompressionCodec codec = CompressionCodecProvider.getCompressionCodec(metadata.getCompression());
        ByteBuf uncompressedPayload = codec.decode(metadataAndPayload, metadata.getUncompressedSize());
        // Copy into a heap buffer for output stream compatibility
        ByteBuf data = PooledByteBufAllocator.DEFAULT.heapBuffer(uncompressedPayload.readableBytes(), uncompressedPayload.readableBytes());
        data.writeBytes(uncompressedPayload);
        uncompressedPayload.release();
        StreamingOutput stream = new StreamingOutput() {

            @Override
            public void write(OutputStream output) throws IOException, WebApplicationException {
                output.write(data.array(), data.arrayOffset(), data.readableBytes());
                data.release();
            }
        };
        return responseBuilder.entity(stream).build();
    } catch (NullPointerException npe) {
        throw new RestException(Status.NOT_FOUND, "Message not found");
    } catch (Exception exception) {
        log.error("[{}] Failed to get message at position {} from {} {}", clientAppId(), messagePosition, dn, subName, exception);
        throw new RestException(exception);
    } finally {
        if (entry != null) {
            entry.release();
        }
    }
}
Also used : PersistentReplicator(com.yahoo.pulsar.broker.service.persistent.PersistentReplicator) KeyValue(com.yahoo.pulsar.common.api.proto.PulsarApi.KeyValue) PositionImpl(org.apache.bookkeeper.mledger.impl.PositionImpl) OutputStream(java.io.OutputStream) RestException(com.yahoo.pulsar.broker.web.RestException) StreamingOutput(javax.ws.rs.core.StreamingOutput) ByteBuf(io.netty.buffer.ByteBuf) PersistentSubscription(com.yahoo.pulsar.broker.service.persistent.PersistentSubscription) RestException(com.yahoo.pulsar.broker.web.RestException) TopicBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.TopicBusyException) WebApplicationException(javax.ws.rs.WebApplicationException) PulsarClientException(com.yahoo.pulsar.client.api.PulsarClientException) PreconditionFailedException(com.yahoo.pulsar.client.admin.PulsarAdminException.PreconditionFailedException) SubscriptionBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionBusyException) NotFoundException(com.yahoo.pulsar.client.admin.PulsarAdminException.NotFoundException) NotAllowedException(com.yahoo.pulsar.broker.service.BrokerServiceException.NotAllowedException) KeeperException(org.apache.zookeeper.KeeperException) IOException(java.io.IOException) Entry(org.apache.bookkeeper.mledger.Entry) MessageMetadata(com.yahoo.pulsar.common.api.proto.PulsarApi.MessageMetadata) PersistentTopic(com.yahoo.pulsar.broker.service.persistent.PersistentTopic) DestinationName(com.yahoo.pulsar.common.naming.DestinationName) CompressionCodec(com.yahoo.pulsar.common.compression.CompressionCodec) ResponseBuilder(javax.ws.rs.core.Response.ResponseBuilder) PartitionedTopicMetadata(com.yahoo.pulsar.common.partition.PartitionedTopicMetadata) Path(javax.ws.rs.Path) GET(javax.ws.rs.GET) ApiOperation(io.swagger.annotations.ApiOperation) ApiResponses(io.swagger.annotations.ApiResponses)

Example 10 with PersistentSubscription

use of com.yahoo.pulsar.broker.service.persistent.PersistentSubscription in project pulsar by yahoo.

the class Namespaces method unsubscribe.

private void unsubscribe(NamespaceName nsName, String bundleRange, String subscription) {
    try {
        List<PersistentTopic> topicList = pulsar().getBrokerService().getAllTopicsFromNamespaceBundle(nsName.toString(), nsName.toString() + "/" + bundleRange);
        List<CompletableFuture<Void>> futures = Lists.newArrayList();
        if (subscription.startsWith(pulsar().getConfiguration().getReplicatorPrefix())) {
            throw new RestException(Status.PRECONDITION_FAILED, "Cannot unsubscribe a replication cursor");
        } else {
            for (PersistentTopic topic : topicList) {
                PersistentSubscription sub = topic.getPersistentSubscription(subscription);
                if (sub != null) {
                    futures.add(sub.delete());
                }
            }
        }
        FutureUtil.waitForAll(futures).get();
    } catch (RestException re) {
        throw re;
    } catch (Exception e) {
        log.error("[{}] Failed to unsubscribe {} for namespace {}/{}", clientAppId(), subscription, nsName.toString(), bundleRange, e);
        if (e.getCause() instanceof SubscriptionBusyException) {
            throw new RestException(Status.PRECONDITION_FAILED, "Subscription has active connected consumers");
        }
        throw new RestException(e.getCause());
    }
}
Also used : CompletableFuture(java.util.concurrent.CompletableFuture) PersistentTopic(com.yahoo.pulsar.broker.service.persistent.PersistentTopic) RestException(com.yahoo.pulsar.broker.web.RestException) SubscriptionBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionBusyException) PersistentSubscription(com.yahoo.pulsar.broker.service.persistent.PersistentSubscription) RestException(com.yahoo.pulsar.broker.web.RestException) WebApplicationException(javax.ws.rs.WebApplicationException) SubscriptionBusyException(com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionBusyException) KeeperException(org.apache.zookeeper.KeeperException) PulsarAdminException(com.yahoo.pulsar.client.admin.PulsarAdminException) NoNodeException(org.apache.zookeeper.KeeperException.NoNodeException) PulsarServerException(com.yahoo.pulsar.broker.PulsarServerException)

Aggregations

PersistentSubscription (com.yahoo.pulsar.broker.service.persistent.PersistentSubscription)29 PersistentTopic (com.yahoo.pulsar.broker.service.persistent.PersistentTopic)29 Test (org.testng.annotations.Test)16 ConsumerConfiguration (com.yahoo.pulsar.client.api.ConsumerConfiguration)12 PulsarClientException (com.yahoo.pulsar.client.api.PulsarClientException)12 Consumer (com.yahoo.pulsar.client.api.Consumer)11 Producer (com.yahoo.pulsar.client.api.Producer)10 Message (com.yahoo.pulsar.client.api.Message)8 SubscriptionBusyException (com.yahoo.pulsar.broker.service.BrokerServiceException.SubscriptionBusyException)7 RestException (com.yahoo.pulsar.broker.web.RestException)7 WebApplicationException (javax.ws.rs.WebApplicationException)7 KeeperException (org.apache.zookeeper.KeeperException)7 NotAllowedException (com.yahoo.pulsar.broker.service.BrokerServiceException.NotAllowedException)6 TopicBusyException (com.yahoo.pulsar.broker.service.BrokerServiceException.TopicBusyException)6 PersistentDispatcherSingleActiveConsumer (com.yahoo.pulsar.broker.service.persistent.PersistentDispatcherSingleActiveConsumer)6 NotFoundException (com.yahoo.pulsar.client.admin.PulsarAdminException.NotFoundException)6 PreconditionFailedException (com.yahoo.pulsar.client.admin.PulsarAdminException.PreconditionFailedException)6 DestinationName (com.yahoo.pulsar.common.naming.DestinationName)6 PartitionedTopicMetadata (com.yahoo.pulsar.common.partition.PartitionedTopicMetadata)6 IOException (java.io.IOException)6