use of com.yahoo.pulsar.common.policies.data.RetentionPolicies in project pulsar by yahoo.
the class Namespaces method setRetention.
@POST
@Path("/{property}/{cluster}/{namespace}/retention")
@ApiOperation(value = " Set retention configuration on a namespace.")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"), @ApiResponse(code = 404, message = "Namespace does not exist"), @ApiResponse(code = 409, message = "Concurrent modification"), @ApiResponse(code = 412, message = "Retention Quota must exceed backlog quota") })
public void setRetention(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace, RetentionPolicies retention) {
validatePoliciesReadOnlyAccess();
try {
Stat nodeStat = new Stat();
final String path = path("policies", property, cluster, namespace);
byte[] content = globalZk().getData(path, null, nodeStat);
Policies policies = jsonMapper().readValue(content, Policies.class);
if (!checkQuotas(policies, retention)) {
log.warn("[{}] Failed to update retention configuration for namespace {}/{}/{}: conflicts with backlog quota", clientAppId(), property, cluster, namespace);
throw new RestException(Status.PRECONDITION_FAILED, "Retention Quota must exceed configured backlog quota for namespace.");
}
policies.retention_policies = retention;
globalZk().setData(path, jsonMapper().writeValueAsBytes(policies), nodeStat.getVersion());
policiesCache().invalidate(path("policies", property, cluster, namespace));
log.info("[{}] Successfully updated retention configuration: namespace={}/{}/{}, map={}", clientAppId(), property, cluster, namespace, jsonMapper().writeValueAsString(policies.retention_policies));
} catch (KeeperException.NoNodeException e) {
log.warn("[{}] Failed to update retention configuration for namespace {}/{}/{}: does not exist", clientAppId(), property, cluster, namespace);
throw new RestException(Status.NOT_FOUND, "Namespace does not exist");
} catch (KeeperException.BadVersionException e) {
log.warn("[{}] Failed to update retention configuration for namespace {}/{}/{}: concurrent modification", clientAppId(), property, cluster, namespace);
throw new RestException(Status.CONFLICT, "Concurrent modification");
} catch (RestException pfe) {
throw pfe;
} catch (Exception e) {
log.error("[{}] Failed to update retention configuration for namespace {}/{}/{}", clientAppId(), property, cluster, namespace, e);
throw new RestException(e);
}
}
use of com.yahoo.pulsar.common.policies.data.RetentionPolicies in project pulsar by yahoo.
the class Namespaces method setBacklogQuota.
@POST
@Path("/{property}/{cluster}/{namespace}/backlogQuota")
@ApiOperation(value = " Set a backlog quota for all the destinations on a namespace.")
@ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"), @ApiResponse(code = 404, message = "Namespace does not exist"), @ApiResponse(code = 409, message = "Concurrent modification"), @ApiResponse(code = 412, message = "Specified backlog quota exceeds retention quota. Increase retention quota and retry request") })
public void setBacklogQuota(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace, @QueryParam("backlogQuotaType") BacklogQuotaType backlogQuotaType, BacklogQuota backlogQuota) {
validateAdminAccessOnProperty(property);
validatePoliciesReadOnlyAccess();
if (backlogQuotaType == null) {
backlogQuotaType = BacklogQuotaType.destination_storage;
}
try {
Stat nodeStat = new Stat();
final String path = path("policies", property, cluster, namespace);
byte[] content = globalZk().getData(path, null, nodeStat);
Policies policies = jsonMapper().readValue(content, Policies.class);
RetentionPolicies r = policies.retention_policies;
if (r != null) {
Policies p = new Policies();
p.backlog_quota_map.put(backlogQuotaType, backlogQuota);
if (!checkQuotas(p, r)) {
log.warn("[{}] Failed to update backlog configuration for namespace {}/{}/{}: conflicts with retention quota", clientAppId(), property, cluster, namespace);
throw new RestException(Status.PRECONDITION_FAILED, "Backlog Quota exceeds configured retention quota for namespace. Please increase retention quota and retry");
}
}
policies.backlog_quota_map.put(backlogQuotaType, backlogQuota);
globalZk().setData(path, jsonMapper().writeValueAsBytes(policies), nodeStat.getVersion());
policiesCache().invalidate(path("policies", property, cluster, namespace));
log.info("[{}] Successfully updated backlog quota map: namespace={}/{}/{}, map={}", clientAppId(), property, cluster, namespace, jsonMapper().writeValueAsString(policies.backlog_quota_map));
} catch (KeeperException.NoNodeException e) {
log.warn("[{}] Failed to update backlog quota map for namespace {}/{}/{}: does not exist", clientAppId(), property, cluster, namespace);
throw new RestException(Status.NOT_FOUND, "Namespace does not exist");
} catch (KeeperException.BadVersionException e) {
log.warn("[{}] Failed to update backlog quota map for namespace {}/{}/{}: concurrent modification", clientAppId(), property, cluster, namespace);
throw new RestException(Status.CONFLICT, "Concurrent modification");
} catch (RestException pfe) {
throw pfe;
} catch (Exception e) {
log.error("[{}] Failed to update backlog quota map for namespace {}/{}/{}", clientAppId(), property, cluster, namespace, e);
throw new RestException(e);
}
}
use of com.yahoo.pulsar.common.policies.data.RetentionPolicies in project pulsar by yahoo.
the class BrokerClientIntegrationTest method testResetCursor.
@Test(timeOut = 10000, dataProvider = "subType")
public void testResetCursor(SubscriptionType subType) throws Exception {
final RetentionPolicies policy = new RetentionPolicies(60, 52 * 1024);
final DestinationName destName = DestinationName.get("persistent://my-property/use/my-ns/unacked-topic");
final int warmup = 20;
final int testSize = 150;
final List<Message> received = new ArrayList<Message>();
final ConsumerConfiguration consConfig = new ConsumerConfiguration();
final String subsId = "sub";
final NavigableMap<Long, TimestampEntryCount> publishTimeIdMap = new ConcurrentSkipListMap<>();
consConfig.setSubscriptionType(subType);
consConfig.setMessageListener((MessageListener) (Consumer consumer, Message msg) -> {
try {
synchronized (received) {
received.add(msg);
}
consumer.acknowledge(msg);
long publishTime = ((MessageImpl) msg).getPublishTime();
log.info(" publish time is " + publishTime + "," + msg.getMessageId());
TimestampEntryCount timestampEntryCount = publishTimeIdMap.computeIfAbsent(publishTime, (k) -> new TimestampEntryCount(publishTime));
timestampEntryCount.incrementAndGet();
} catch (final PulsarClientException e) {
log.warn("Failed to ack!");
}
});
admin.namespaces().setRetention(destName.getNamespace(), policy);
Consumer consumer = pulsarClient.subscribe(destName.toString(), subsId, consConfig);
final Producer producer = pulsarClient.createProducer(destName.toString());
log.info("warm up started for " + destName.toString());
// send warmup msgs
byte[] msgBytes = new byte[1000];
for (Integer i = 0; i < warmup; i++) {
producer.send(msgBytes);
}
log.info("warm up finished.");
// sleep to ensure receiving of msgs
for (int n = 0; n < 10 && received.size() < warmup; n++) {
Thread.sleep(100);
}
// validate received msgs
Assert.assertEquals(received.size(), warmup);
received.clear();
// publish testSize num of msgs
log.info("Sending more messages.");
for (Integer n = 0; n < testSize; n++) {
producer.send(msgBytes);
Thread.sleep(1);
}
log.info("Sending more messages done.");
Thread.sleep(3000);
long begints = publishTimeIdMap.firstEntry().getKey();
long endts = publishTimeIdMap.lastEntry().getKey();
// find reset timestamp
long timestamp = (endts - begints) / 2 + begints;
timestamp = publishTimeIdMap.floorKey(timestamp);
NavigableMap<Long, TimestampEntryCount> expectedMessages = new ConcurrentSkipListMap<>();
expectedMessages.putAll(publishTimeIdMap.tailMap(timestamp, true));
received.clear();
log.info("reset cursor to " + timestamp + " for topic " + destName.toString() + " for subs " + subsId);
log.info("issuing admin operation on " + admin.getServiceUrl().toString());
List<String> subList = admin.persistentTopics().getSubscriptions(destName.toString());
for (String subs : subList) {
log.info("got sub " + subs);
}
publishTimeIdMap.clear();
// reset the cursor to this timestamp
Assert.assertTrue(subList.contains(subsId));
admin.persistentTopics().resetCursor(destName.toString(), subsId, timestamp);
consumer = pulsarClient.subscribe(destName.toString(), subsId, consConfig);
Thread.sleep(3000);
int totalExpected = 0;
for (TimestampEntryCount tec : expectedMessages.values()) {
totalExpected += tec.numMessages;
}
// validate that replay happens after the timestamp
Assert.assertTrue(publishTimeIdMap.firstEntry().getKey() >= timestamp);
consumer.close();
producer.close();
// validate that expected and received counts match
int totalReceived = 0;
for (TimestampEntryCount tec : publishTimeIdMap.values()) {
totalReceived += tec.numMessages;
}
Assert.assertEquals(totalReceived, totalExpected, "did not receive all messages on replay after reset");
}
use of com.yahoo.pulsar.common.policies.data.RetentionPolicies in project pulsar by yahoo.
the class AdminApiTest method persistentTopicsInvalidCursorReset.
@Test
public void persistentTopicsInvalidCursorReset() throws Exception {
admin.namespaces().setRetention("prop-xyz/use/ns1", new RetentionPolicies(10, 10));
assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
String topicName = "persistent://prop-xyz/use/ns1/invalidcursorreset";
// Force to create a destination
publishMessagesOnPersistentTopic(topicName, 0);
assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList(topicName));
// create consumer and subscription
URL pulsarUrl = new URL("http://127.0.0.1" + ":" + BROKER_WEBSERVICE_PORT);
ClientConfiguration clientConf = new ClientConfiguration();
clientConf.setStatsInterval(0, TimeUnit.SECONDS);
PulsarClient client = PulsarClient.create(pulsarUrl.toString(), clientConf);
ConsumerConfiguration conf = new ConsumerConfiguration();
conf.setSubscriptionType(SubscriptionType.Exclusive);
Consumer consumer = client.subscribe(topicName, "my-sub", conf);
assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
publishMessagesOnPersistentTopic(topicName, 10);
List<Message> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
assertEquals(messages.size(), 10);
for (int i = 0; i < 10; i++) {
Message message = consumer.receive();
consumer.acknowledge(message);
}
// use invalid timestamp
try {
admin.persistentTopics().resetCursor(topicName, "my-sub", System.currentTimeMillis() - 190000);
} catch (Exception e) {
// fail the test
throw e;
}
admin.persistentTopics().resetCursor(topicName, "my-sub", System.currentTimeMillis() + 90000);
consumer = client.subscribe(topicName, "my-sub", conf);
consumer.close();
client.close();
admin.persistentTopics().deleteSubscription(topicName, "my-sub");
assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
admin.persistentTopics().delete(topicName);
}
use of com.yahoo.pulsar.common.policies.data.RetentionPolicies in project pulsar by yahoo.
the class AdminApiTest method persistentTopicsCursorReset.
@Test(dataProvider = "topicName")
public void persistentTopicsCursorReset(String topicName) throws Exception {
admin.namespaces().setRetention("prop-xyz/use/ns1", new RetentionPolicies(10, 10));
assertEquals(admin.persistentTopics().getList("prop-xyz/use/ns1"), Lists.newArrayList());
topicName = "persistent://prop-xyz/use/ns1/" + topicName;
// create consumer and subscription
ConsumerConfiguration conf = new ConsumerConfiguration();
conf.setSubscriptionType(SubscriptionType.Exclusive);
Consumer consumer = pulsarClient.subscribe(topicName, "my-sub", conf);
assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList("my-sub"));
publishMessagesOnPersistentTopic(topicName, 5, 0);
// Allow at least 1ms for messages to have different timestamps
Thread.sleep(1);
long messageTimestamp = System.currentTimeMillis();
publishMessagesOnPersistentTopic(topicName, 5, 5);
List<Message> messages = admin.persistentTopics().peekMessages(topicName, "my-sub", 10);
assertEquals(messages.size(), 10);
for (int i = 0; i < 10; i++) {
Message message = consumer.receive();
consumer.acknowledge(message);
}
// messages should still be available due to retention
admin.persistentTopics().resetCursor(topicName, "my-sub", messageTimestamp);
int receivedAfterReset = 0;
for (int i = 4; i < 10; i++) {
Message message = consumer.receive();
consumer.acknowledge(message);
++receivedAfterReset;
String expected = "message-" + i;
assertEquals(message.getData(), expected.getBytes());
}
assertEquals(receivedAfterReset, 6);
consumer.close();
admin.persistentTopics().deleteSubscription(topicName, "my-sub");
assertEquals(admin.persistentTopics().getSubscriptions(topicName), Lists.newArrayList());
admin.persistentTopics().delete(topicName);
}
Aggregations