Call the retry listener from inside another listener maintaining with the whole retry Logic - spring-kafka

We are using spring-kafka-2.2.8.RELEASE. I have an specific situation where I need help. I have 4 topics topic, retryTopic, successTopic and errorTopic. If topic fails, should be redirected to retryTopic where the 3 attempts to retry will be made. If those attempts fails, must redirect to errorTopic. In case of sucess on both topic and retryTopic, should be redirected to the sucessTopic. This situation is already implemented based on the question How to retry with spring kafka version 2..2.
But now, I have a new situation where I need to call the retryTopic listener from inside the topic listener based on a business logic error without an Exception been thrown(it already calls the retryTopic when an exception is thrown and it must remain with this behavior). And I also need to know on which retry attempt number the retryTopic is been called as a paramater of the listener bellow.
#KafkaListener(id = "so60172304.2", topics = "retryTopic")
public void listen2(String in) {
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
#Override
public Void doWithRetry(RetryContext retryContext) throws RuntimeException {
// Can I get the retry count here? It didn't work
Integer count =RetrySynchronizationManager.getContext().getRetryCount());
return this.doWithRetry(retryContext);
}
});
}

There is no reason you can't call one listener from another (but you won't get retries unless you call it using a RetryTemplate in the first method).
If you use a RetryTemplate configured on the container factory to do the retries (rather than adding a BackOff to the SeektoCurrentErrorHandler in versions 2.3.x and higher), you can obtain the retry count (starting at zero) like this...
#KafkaListener(id = "so60172304.2", topics = "retryTopic")
public void listen2(String in) {
int retryCount = RetrySynchronizationManager.getContext().getRetryCount();
...
}
getContext() will return null if you call this directly from the first method (unless you wrap the call in a RetryTemplate.execute()).
In 2.5.x a delivery attempt header will be available (optionally) even if using the SeektoCurrentErrorHandler with a BackOff instead of using a RetryTemplate in the container factory.

Related

How to manual commit do not recorverd offset already sent DLT through CommonErrorHandler

A simple example is currently being made through the spring kafka.
If an exception occurs at the service layer, I want to commit the original offset after trying to retry and loading it into the dead letter queue.
However, the dead letter queue is loaded properly, but the original message remains in the kafka because the commit is not processed.
To show you my code, it is as follows.
KafkaConfig.java
...
#Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
factory.setCommonErrorHandler(kafkaListenerErrorHandler());
factory.getContainerProperties().setAckMode(AckMode.MANUAL_IMMEDIATE);
return factory;
}
private CommonErrorHandler kafkaListenerErrorHandler() {
DefaultErrorHandler defaultErrorHandler = new DefaultErrorHandler(
new DeadLetterPublishingRecoverer(template, DEAD_TOPIC_DESTINATION_RESOLVER),
new FixedBackOff(1000, 3));
defaultErrorHandler.setCommitRecovered(true);
defaultErrorHandler.setAckAfterHandle(true);
defaultErrorHandler.setResetStateOnRecoveryFailure(false);
return defaultErrorHandler;
}
...
KafkaListener.java
...
#KafkaListener(topics = TOPIC_NAME, containerFactory = "kafkaListenerContainerFactory", groupId = "stock-adjustment-0")
public void subscribe(final String message, Acknowledgment ack) throws IOException {
log.info(String.format("Message Received : [%s]", message));
StockAdjustment stockAdjustment = StockAdjustment.deserializeJSON(message);
if(stockService.isAlreadyProcessedOrderId(stockAdjustment.getOrderId())) {
log.info(String.format("AlreadyProcessedOrderId : [%s]", stockAdjustment.getOrderId()));
} else {
if(stockAdjustment.getAdjustmentType().equals("REDUCE")) {
stockService.decreaseStock(stockAdjustment);
}
}
ack.acknowledge(); // <<< does not work!
}
...
Stockservice.java
...
if(stockAdjustment.getQty() > stock.getAvailableStockQty()) {
throw new RuntimeException(String.format("Stock decreased Request [decreasedQty: %s][availableQty : %s]", stockAdjustment.getQty(), stock.getAvailableStockQty()));
}
...
At this time, when RuntimeException occur in the service layer as above, the DLT is issued through an CommonErrorhandler according to the Kafka setting.
However, after issuing the DLT, the original message remains in Kafka, so there is a need for a solution.
I looked it up and found that the setting I wrote is processed through SeekUtils.seekOrRecover(), and if it is not processed even if the maximum number of attempts is not processed, an exception occurs and the original offset is rolled back without processing a commit.
According to the document, it seems that the AfterRollbackProcessor handles rollback if it fails with the default value, but I don't know how to write the code to commit even if it fails.
EDITED
The above code and settings work normally.
I thought the consumer lag would occur, but when I judged the actual logic code(SeekUtils.seekOrRecover()) and checked the offset commit and lag, I confirmed that it works normally.
I think it was caused by my mistake.
Records are never removed (until they expire), the consumer's committed offset is updated.
Use kafka-consumer-groups.sh to describe the group to see the committed offset for the failed record that was sent to the DLT.

How to retry failed ConsumerRecord in reactor-kafka

I am trying on reactor-kafka for consuming messages. Everything else work fine, but I want to add a retry(2) for failing messages. spring-kafka already retries failed record 3 times by default, I want to achieve the same using reactor-kafka.
I am using spring-kafka as a wrapper for reactive-kafka. Below is my consumer template:
reactiveKafkaConsumerTemplate
.receiveAutoAck()
.map(ConsumerRecord::value)
.flatMap(this::consumeWithRetry)
.onErrorContinue((error, value)->log.error("something bad happened while consuming : {}", error.getMessage()))
.retryWhen(Retry.backoff(30, Duration.of(10, ChronoUnit.SECONDS)))
.subscribe();
Let us consider the consume method is as follows
public Mono<Void> consume(MessageRecord message){
return Mono.error(new RuntimeException("test retry"); //sample error scenario
}
I am using the following logic to retry the consume method on failure.
public Mono<Void> consumeWithRetry(MessageRecord message){
return consume(message)
.retry(2);
}
I want to retry consuming the message if the current consumer record fails with exception. I have tried to wrap the consume method with another retry(3) but that does not serve the purpose. The last retryWhen is only for retrying subscription on kafka rebalances.
#simon-baslé #gary-russell
Previously while retrying I was using the below approach:
public Mono<Void> consumeWithRetry(MessageRecord message){
return consume(message)
.retry(2);
}
But it was not retrying. After adding Mono.defer, the above code works and adds required retry.
public Mono<Void> consumeWithRetry(MessageRecord message){
return Mono.defer(()->consume(message))
.retry(2);
}

.net transient database context being disposed prematurely

I am moving an asp.net mvc5 application using EF6 to asp.net core MVC 3.0 using EF Core.
In my mvc5 application I have some administrative operation that modify the database and take a long time, so I use a pattern when I create a new DBContext that is not the one that is associated with the request context and then run the task in the background using Task.Run. This has been working fine for years.
In converting to .net core it was unclear how to create a new DBContext in the way that I was doing it in my old codebase. It seems like I should be able to create a Transient DBContext in these cases and all should be fine.
So I created a subclass of MyDbContext called MyTransientDbContex and in my Configure class I added this service:
services.AddDbContext<MyTransientDbContex>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("MyContextConnection")),
ServiceLifetime.Transient, ServiceLifetime.Transient);
In my controller I inject the context in the action that needs the transient service and spawn a thread to do something with it:
public ActionResult Update([FromServices] MyTransientContext context) {
Task.Run(() =>
{
try {
// Do some long running operation with context
}
Catch (Exception e) {
// Report Exception
}
finally {
context.Dispose();
}
}
return RedirectToAction("Status");
}
I would not expect my transient context to be disposed until the finally block. But I am getting this exception when attempting to access the context on the background thread:
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'MyTransientContext'.'
And indeed the _disposed flag is set to true on the context object.
I put a breakpoint on the constructer for MyTransientContext and "Made an Object ID" of the this pointer so that I could track the object. This transient object is being created and is the same one that is inject into my controller action. It's also the same object that I'm trying to reference when the exception is thrown.
I tried setting a data breakpoint on the _disposed member in order to get a callstack on when disposed is being set to true, but the breakpoint won't bind.
I also tried overriding the Dispose method on MyTransientContext, and it isn't called until my explicit dispose in the finally block, which is after the exception is thrown and caught.
I feel like I'm missing something fundamental here. Isn't this what the transient services are for? What would dispose a Transient service?
One last detail - MyTransientContext is derived from MyContext, which is in turn derived from IdentityDbContext (Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContex)
Edit: The reason that I went down the path of using a Transient was because of this ef core document page: https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext. It states that "...any code that explicitly executes multiple threads in parallel should ensure that DbContext instances aren't ever accessed concurrently. Using dependency injection, this can be achieved by either registering the context as scoped and creating scopes (using IServiceScopeFactory) for each thread, or by registering the DbContext as transient (using the overload of AddDbContext which takes a ServiceLifetime parameter)."
As xabikos pointed out, this seems to be overriden by the scoping of the asp.net DI system, where it looks like anything created by that system is scoped to the request context, including Transient objects. Can someone point out where that's documented so that I can better understand how to work with the limitations?
f you want manage the lifetime of service, you can instantiate it manually (or use a factory) :
public ActionResult Update()
{
Task.Run(() =>
{
using(var context = new MyTransientContext(...))
{
try
{
// Do some long running operation with context
}
catch (Exception e)
{
// Report Exception
}
}
}
return RedirectToAction("Status");
}
Or you can use IServiceProvider to get and manage a service :
public class MyController
{
private IServiceProvider _services;
public MyController(IServiceProvider services)
{
_services = services;
}
public ActionResult Update()
{
var context = (MyTransientContext)_services.GetService(typeof(MyTransientContext));
Task.Run(() =>
{
using (context)
{
try
{
// Do some long running operation with context
}
catch (Exception e)
{
// Report Exception
}
}
}
return RedirectToAction("Status");
}
}
You mixed the concepts of transient objects that are created by internal DI container asp.net core provides.
You configure the MyTransientContext to be transient in the internal DI system. This practically means that every time a scope is created then a new instance is returned. For asp.net application this scope matches an HTTP request. When the requests ends then all the objects are disposed if applicable.
Now in your code, that is a synchronous action method you spawn a Task with Task.Run. This is an async operation and you don't await for this. Practically during execution this will be started but not wait to finish, the redirect will happen and the request will end. At this point if you try to use the injected instance you will get the exception.
If you would like to solve this you need change to an async action and await on the Task.Run. And most likely you don't need to spawn a new Task. But you need to understand that this is not probably the best way as it will need for the long operation to finish before the redirect takes place.
An alternative to this would be to use a messaging mechanism, and send a message that triggers this operation. And you have another component, like worker service that listens for those messages and process them.

Actually what is the init parameter in servlet?

Friends tell me what is the core meaning of init parameter in case of a servlet.
I know that how to initialize it in a web.xml but I don't know what is the actual purpose of it why it is required? Please tell me with a good example.
The Javadoc says: "A convenience method which can be overridden so that there's no need to call super.init(config)."
The init method's main purpose is to allow customization while you are initializing the servlet.
The simplest implementation is when you don't want to do any customization according to your application you can always call super.init method.
To understand meaning of what different init params can be there and how init method is useful:
Imagine a system Of BookManagement system, here for adding books and removing books from db you will be needing Database connection over which you can access the data. Now as Servlet's init method is called for the first request and database connection also needs be created only once(or n number of time if doing connection pooling) then initializing the database connection is something that you should do in init method.
A code snippet from Softlab example , let's assume that getInitParameter method reads the databaseUrl and other properties from web.xml
public class DBServlet ... {
Connection connection = null;
public void init() throws ServletException {
// Open a database connection to prepare for requests
try {
databaseUrl = getInitParameter("databaseUrl");
... // get user and password parameters the same way
connection = DriverManager.getConnection(databaseUrl,
user, password);
} catch(Exception e) {
throw new UnavailableException (this,
"Could not open a connection to the database");
}
}
...
}
One more example of counting the number of time servlet was accessed: https://docstore.mik.ua/orelly/java-ent/servlet/ch03_03.htm
So in Summary: To do customization like read the initial values of variable or to initialize resources(like db connection) you can use init method.
Below is the source code of init methods :
public void init(ServletConfig config)throws ServletException
{
this.config = config;
int();
}
public void init() throws ServletException;
It is recommended to override to init() method, not init(ServletConfig).
When overriding init(ServletConfig), the first thing that must be done is to call:
super.init(config);
If you do this then calling directly to getServletContext() in your method will no longer result in an NPE.

DEADLOCK, EJB 3.1 with asynchronous Method and TimerService in Singleton

In my Singleton-EJB i start a TimerService every 2 minutes. When a client access the test method
sometimes the application runs into a deadlock. The problem is, the test method calls a asynchronous method inside the EJB (see Method determineABC). The deadlock happens when the scheduleMethod tries to create a single action timer and therefore tries to acquire a lock (because hte timer callback method is annotated with LOCK.WRITE). At the same time we are already in the determineABC Method which tries to invoke the asynchronous method asynchMethod. Maybe the call of ejbLocal.asynchMethod(...); also tries to acquire a lock. Anyway here i run into a deadlock, because the asynchronous method is never called. So what is the problem?
Here is a source code snippet:
#Singleton
#Startup
#TransactionManagement(TransactionManagementType.CONTAINER)
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class XEJB implements XEJBLocal {
#javax.annotation.Resource(name = "x/XEJB/TimeService")
private TimerService timerService;
#javax.annotation.Resource
private SessionContext ctx;
#Schedule(minute = "*/2", hour = "*", persistent = false)
#Lock(LockType.READ)
private void scheduleMethod() {
// Create Single Action Timer
timerService.createSingleActionTimer(new Date(), new TimerConfig(null, false));
}
#Timeout
#Lock(LockType.WRITE)
private void timer(Timer timer) {
// Do something
}
#Override
#Lock(LockType.READ)
public B test(...) {
return determineABC(...);
}
#Lock(LockType.READ)
private B determineABC(...) {
XEJBLocal ejb= (XEJBLocal) ctx.getBusinessObject(ctx.getInvokedBusinessInterface());
Future<ArrayList> result = null;
result = ejb.asynchMethod(...);
result.get(4, TimeUnit.MINUTES); // Sometimes runs into a DEADLOCK
...
}
#Asynchronous
#Override
#Lock(LockType.READ)
public Future<ArrayList> asynchMethod(...) {
...
return new AsyncResult<ArrayList>(abcList);
}
The Deadlock also happens when i only use the #Schedule Method and no TimerService...
The DeadLock also happens when i do not use a Future Object but void as return type of the asynchronous Method.
When the timeout Exception is thrown the deadlock is solved. When i annotate the timer method with #AccessTimeout(2000) and this time is up the asynchronous method is called and therefore the deadlock is also solved.
When i use Locktype.READ for the timer Method no Deadlock happens. But why? What does the asychronous method call?
READ locks have to wait for WRITE locks to finish before they start their work. When timer() is working all your other invokations, even to READ methods, are going to wait. Are you sure the timeout happens in result.get(4, TimeUnit.MINUTES);?
I think you may be have access timeouts in test() invokation, way before reaching result.get(4, TimeUnit.MINUTES);.

Resources