use of org.apache.pulsar.common.api.proto.PulsarApi.CommandSubscribe in project incubator-pulsar by apache.
the class PersistentTopicTest method testFailoverSubscription.
@Test
public void testFailoverSubscription() throws Exception {
PersistentTopic topic1 = new PersistentTopic(successTopicName, ledgerMock, brokerService);
CommandSubscribe cmd1 = CommandSubscribe.newBuilder().setConsumerId(1).setTopic(successTopicName).setSubscription(successSubName).setRequestId(1).setSubType(SubType.Failover).build();
// 1. Subscribe with non partition topic
Future<Consumer> f1 = topic1.subscribe(serverCnx, cmd1.getSubscription(), cmd1.getConsumerId(), cmd1.getSubType(), 0, cmd1.getConsumerName(), cmd1.getDurable(), null, Collections.emptyMap(), cmd1.getReadCompacted(), InitialPosition.Latest);
f1.get();
// 2. Subscribe with partition topic
PersistentTopic topic2 = new PersistentTopic(successPartitionTopicName, ledgerMock, brokerService);
CommandSubscribe cmd2 = CommandSubscribe.newBuilder().setConsumerId(1).setConsumerName("C1").setTopic(successPartitionTopicName).setSubscription(successSubName).setRequestId(1).setSubType(SubType.Failover).build();
Future<Consumer> f2 = topic2.subscribe(serverCnx, cmd2.getSubscription(), cmd2.getConsumerId(), cmd2.getSubType(), 0, cmd2.getConsumerName(), cmd2.getDurable(), null, Collections.emptyMap(), cmd2.getReadCompacted(), InitialPosition.Latest);
f2.get();
// 3. Subscribe and create second consumer
CommandSubscribe cmd3 = CommandSubscribe.newBuilder().setConsumerId(2).setConsumerName("C2").setTopic(successPartitionTopicName).setSubscription(successSubName).setRequestId(1).setSubType(SubType.Failover).build();
Future<Consumer> f3 = topic2.subscribe(serverCnx, cmd3.getSubscription(), cmd3.getConsumerId(), cmd3.getSubType(), 0, cmd3.getConsumerName(), cmd3.getDurable(), null, Collections.emptyMap(), cmd3.getReadCompacted(), InitialPosition.Latest);
f3.get();
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerId(), 1);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerName(), "C1");
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(1).consumerId(), 2);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(1).consumerName(), "C2");
// 4. Subscribe and create third duplicate consumer
CommandSubscribe cmd4 = CommandSubscribe.newBuilder().setConsumerId(3).setConsumerName("C1").setTopic(successPartitionTopicName).setSubscription(successSubName).setRequestId(1).setSubType(SubType.Failover).build();
Future<Consumer> f4 = topic2.subscribe(serverCnx, cmd4.getSubscription(), cmd4.getConsumerId(), cmd4.getSubType(), 0, cmd4.getConsumerName(), cmd4.getDurable(), null, Collections.emptyMap(), cmd4.getReadCompacted(), InitialPosition.Latest);
f4.get();
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerId(), 1);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerName(), "C1");
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(1).consumerId(), 3);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(1).consumerName(), "C1");
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(2).consumerId(), 2);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(2).consumerName(), "C2");
// 5. Subscribe on partition topic with existing consumer id and different sub type
CommandSubscribe cmd5 = CommandSubscribe.newBuilder().setConsumerId(2).setConsumerName("C1").setTopic(successPartitionTopicName).setSubscription(successSubName).setRequestId(1).setSubType(SubType.Exclusive).build();
Future<Consumer> f5 = topic2.subscribe(serverCnx, cmd5.getSubscription(), cmd5.getConsumerId(), cmd5.getSubType(), 0, cmd5.getConsumerName(), cmd5.getDurable(), null, Collections.emptyMap(), cmd5.getReadCompacted(), InitialPosition.Latest);
try {
f5.get();
fail("should fail with exception");
} catch (ExecutionException ee) {
// Expected
assertTrue(ee.getCause() instanceof BrokerServiceException.SubscriptionBusyException);
}
// 6. Subscribe on partition topic with different sub name, type and different consumer id
CommandSubscribe cmd6 = CommandSubscribe.newBuilder().setConsumerId(4).setConsumerName("C3").setTopic(successPartitionTopicName).setSubscription(successSubName2).setRequestId(1).setSubType(SubType.Exclusive).build();
Future<Consumer> f6 = topic2.subscribe(serverCnx, cmd6.getSubscription(), cmd6.getConsumerId(), cmd6.getSubType(), 0, cmd6.getConsumerName(), cmd6.getDurable(), null, Collections.emptyMap(), cmd6.getReadCompacted(), InitialPosition.Latest);
f6.get();
// 7. unsubscribe exclusive sub
Future<Void> f7 = topic2.unsubscribe(successSubName2);
f7.get();
assertNull(topic2.getSubscription(successSubName2));
// 8. unsubscribe active consumer from shared sub.
PersistentSubscription sub = topic2.getSubscription(successSubName);
Consumer cons = sub.getDispatcher().getConsumers().get(0);
sub.removeConsumer(cons);
// Verify second consumer become active
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerId(), 3);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerName(), "C1");
// 9. unsubscribe active consumer from shared sub.
cons = sub.getDispatcher().getConsumers().get(0);
sub.removeConsumer(cons);
// Verify second consumer become active
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerId(), 2);
assertEquals(topic2.getSubscription(successSubName).getDispatcher().getConsumers().get(0).consumerName(), "C2");
// 10. unsubscribe shared sub
Future<Void> f8 = topic2.unsubscribe(successSubName);
f8.get();
assertNull(topic2.getSubscription(successSubName));
}
use of org.apache.pulsar.common.api.proto.PulsarApi.CommandSubscribe in project incubator-pulsar by apache.
the class PersistentTopicTest method testSubscribeFail.
@Test
public void testSubscribeFail() throws Exception {
PersistentTopic topic = new PersistentTopic(successTopicName, ledgerMock, brokerService);
// Empty subscription name
CommandSubscribe cmd = CommandSubscribe.newBuilder().setConsumerId(1).setTopic(successTopicName).setSubscription("").setRequestId(1).setSubType(SubType.Exclusive).build();
Future<Consumer> f1 = topic.subscribe(serverCnx, cmd.getSubscription(), cmd.getConsumerId(), cmd.getSubType(), 0, cmd.getConsumerName(), cmd.getDurable(), null, Collections.emptyMap(), cmd.getReadCompacted(), InitialPosition.Latest);
try {
f1.get();
fail("should fail with exception");
} catch (ExecutionException ee) {
// Expected
assertTrue(ee.getCause() instanceof BrokerServiceException.NamingException);
}
}
use of org.apache.pulsar.common.api.proto.PulsarApi.CommandSubscribe in project incubator-pulsar by apache.
the class PersistentTopicTest method testSubscribeUnsubscribe.
@Test
public void testSubscribeUnsubscribe() throws Exception {
PersistentTopic topic = new PersistentTopic(successTopicName, ledgerMock, brokerService);
CommandSubscribe cmd = CommandSubscribe.newBuilder().setConsumerId(1).setTopic(successTopicName).setSubscription(successSubName).setRequestId(1).setSubType(SubType.Exclusive).build();
// 1. simple subscribe
Future<Consumer> f1 = topic.subscribe(serverCnx, cmd.getSubscription(), cmd.getConsumerId(), cmd.getSubType(), 0, cmd.getConsumerName(), cmd.getDurable(), null, Collections.emptyMap(), cmd.getReadCompacted(), InitialPosition.Latest);
f1.get();
// 2. duplicate subscribe
Future<Consumer> f2 = topic.subscribe(serverCnx, cmd.getSubscription(), cmd.getConsumerId(), cmd.getSubType(), 0, cmd.getConsumerName(), cmd.getDurable(), null, Collections.emptyMap(), cmd.getReadCompacted(), InitialPosition.Latest);
try {
f2.get();
fail("should fail with exception");
} catch (ExecutionException ee) {
// Expected
assertTrue(ee.getCause() instanceof BrokerServiceException.ConsumerBusyException);
}
// 3. simple unsubscribe
Future<Void> f3 = topic.unsubscribe(successSubName);
f3.get();
assertNull(topic.getSubscription(successSubName));
}
use of org.apache.pulsar.common.api.proto.PulsarApi.CommandSubscribe in project incubator-pulsar by apache.
the class ServerCnx method handleSubscribe.
@Override
protected void handleSubscribe(final CommandSubscribe subscribe) {
checkArgument(state == State.Connected);
final long requestId = subscribe.getRequestId();
final long consumerId = subscribe.getConsumerId();
TopicName topicName = validateTopicName(subscribe.getTopic(), requestId, subscribe);
if (topicName == null) {
return;
}
if (invalidOriginalPrincipal(originalPrincipal)) {
final String msg = "Valid Proxy Client role should be provided while subscribing ";
log.warn("[{}] {} with role {} and proxyClientAuthRole {} on topic {}", remoteAddress, msg, authRole, originalPrincipal, topicName);
ctx.writeAndFlush(Commands.newError(requestId, ServerError.AuthorizationError, msg));
return;
}
final String subscriptionName = subscribe.getSubscription();
final SubType subType = subscribe.getSubType();
final String consumerName = subscribe.getConsumerName();
final boolean isDurable = subscribe.getDurable();
final MessageIdImpl startMessageId = subscribe.hasStartMessageId() ? new BatchMessageIdImpl(subscribe.getStartMessageId().getLedgerId(), subscribe.getStartMessageId().getEntryId(), subscribe.getStartMessageId().getPartition(), subscribe.getStartMessageId().getBatchIndex()) : null;
final String subscription = subscribe.getSubscription();
final int priorityLevel = subscribe.hasPriorityLevel() ? subscribe.getPriorityLevel() : 0;
final boolean readCompacted = subscribe.getReadCompacted();
final Map<String, String> metadata = CommandUtils.metadataFromCommand(subscribe);
final InitialPosition initialPosition = subscribe.getInitialPosition();
CompletableFuture<Boolean> isProxyAuthorizedFuture;
if (service.isAuthorizationEnabled() && originalPrincipal != null) {
isProxyAuthorizedFuture = service.getAuthorizationService().canConsumeAsync(topicName, authRole, authenticationData, subscribe.getSubscription());
} else {
isProxyAuthorizedFuture = CompletableFuture.completedFuture(true);
}
isProxyAuthorizedFuture.thenApply(isProxyAuthorized -> {
if (isProxyAuthorized) {
CompletableFuture<Boolean> authorizationFuture;
if (service.isAuthorizationEnabled()) {
authorizationFuture = service.getAuthorizationService().canConsumeAsync(topicName, originalPrincipal != null ? originalPrincipal : authRole, authenticationData, subscription);
} else {
authorizationFuture = CompletableFuture.completedFuture(true);
}
authorizationFuture.thenApply(isAuthorized -> {
if (isAuthorized) {
if (log.isDebugEnabled()) {
log.debug("[{}] Client is authorized to subscribe with role {}", remoteAddress, authRole);
}
log.info("[{}] Subscribing on topic {} / {}", remoteAddress, topicName, subscriptionName);
try {
Metadata.validateMetadata(metadata);
} catch (IllegalArgumentException iae) {
final String msg = iae.getMessage();
ctx.writeAndFlush(Commands.newError(requestId, ServerError.MetadataError, msg));
return null;
}
CompletableFuture<Consumer> consumerFuture = new CompletableFuture<>();
CompletableFuture<Consumer> existingConsumerFuture = consumers.putIfAbsent(consumerId, consumerFuture);
if (existingConsumerFuture != null) {
if (existingConsumerFuture.isDone() && !existingConsumerFuture.isCompletedExceptionally()) {
Consumer consumer = existingConsumerFuture.getNow(null);
log.info("[{}] Consumer with the same id is already created: {}", remoteAddress, consumer);
ctx.writeAndFlush(Commands.newSuccess(requestId));
return null;
} else {
// There was an early request to create a consumer with same consumerId. This can happen
// when
// client timeout is lower the broker timeouts. We need to wait until the previous
// consumer
// creation request either complete or fails.
log.warn("[{}][{}][{}] Consumer is already present on the connection", remoteAddress, topicName, subscriptionName);
ServerError error = !existingConsumerFuture.isDone() ? ServerError.ServiceNotReady : getErrorCode(existingConsumerFuture);
ctx.writeAndFlush(Commands.newError(requestId, error, "Consumer is already present on the connection"));
return null;
}
}
service.getTopic(topicName.toString()).thenCompose(topic -> topic.subscribe(ServerCnx.this, subscriptionName, consumerId, subType, priorityLevel, consumerName, isDurable, startMessageId, metadata, readCompacted, initialPosition)).thenAccept(consumer -> {
if (consumerFuture.complete(consumer)) {
log.info("[{}] Created subscription on topic {} / {}", remoteAddress, topicName, subscriptionName);
ctx.writeAndFlush(Commands.newSuccess(requestId), ctx.voidPromise());
} else {
// The consumer future was completed before by a close command
try {
consumer.close();
log.info("[{}] Cleared consumer created after timeout on client side {}", remoteAddress, consumer);
} catch (BrokerServiceException e) {
log.warn("[{}] Error closing consumer created after timeout on client side {}: {}", remoteAddress, consumer, e.getMessage());
}
consumers.remove(consumerId, consumerFuture);
}
}).exceptionally(exception -> {
if (exception.getCause() instanceof ConsumerBusyException) {
if (log.isDebugEnabled()) {
log.debug("[{}][{}][{}] Failed to create consumer because exclusive consumer is already connected: {}", remoteAddress, topicName, subscriptionName, exception.getCause().getMessage());
}
} else {
log.warn("[{}][{}][{}] Failed to create consumer: {}", remoteAddress, topicName, subscriptionName, exception.getCause().getMessage(), exception);
}
// back to client, only if not completed already.
if (consumerFuture.completeExceptionally(exception)) {
ctx.writeAndFlush(Commands.newError(requestId, BrokerServiceException.getClientErrorCode(exception.getCause()), exception.getCause().getMessage()));
}
consumers.remove(consumerId, consumerFuture);
return null;
});
} else {
String msg = "Client is not authorized to subscribe";
log.warn("[{}] {} with role {}", remoteAddress, msg, authRole);
ctx.writeAndFlush(Commands.newError(requestId, ServerError.AuthorizationError, msg));
}
return null;
}).exceptionally(e -> {
String msg = String.format("[%s] %s with role %s", remoteAddress, e.getMessage(), authRole);
log.warn(msg);
ctx.writeAndFlush(Commands.newError(requestId, ServerError.AuthorizationError, e.getMessage()));
return null;
});
} else {
final String msg = "Proxy Client is not authorized to subscribe";
log.warn("[{}] {} with role {} on topic {}", remoteAddress, msg, authRole, topicName);
ctx.writeAndFlush(Commands.newError(requestId, ServerError.AuthorizationError, msg));
}
return null;
}).exceptionally(ex -> {
String msg = String.format("[%s] %s with role %s", remoteAddress, ex.getMessage(), authRole);
if (ex.getCause() instanceof PulsarServerException) {
log.info(msg);
} else {
log.warn(msg);
}
ctx.writeAndFlush(Commands.newError(requestId, ServerError.AuthorizationError, ex.getMessage()));
return null;
});
}
Aggregations