TransactionSynchronizationManager and Detection Of Transaction Rollback - spring-transactions

I am adding some code that executes in the beforeCommit() and beforeCompletion() methods of a TransactionSycnhoniztion in Spring. I need to be able to detect in a transaction is active and marked for rollback before executing my code..
public void beforeCompletion() {
if (transaction inactive and not rolledback)
doit();
}
How do I detect if transaction is active and not rolled back?. I see the method isActualTransactionActive() but can see no way to access the transaction or determine if rolled back. (unless those methods arent called if the transaction is rolled back)

I was looking through the Spring source code and AbstractPlatformTransactionManager has methods for processCommit() and processRollback().....
processCommit() calls triggerBeforeCommit() and processRollback does not.
So the answer is beforeCommit() isnt called when a rollback happens....
and beforeCompletion is called in both cases but passes status in the method argument.

Related

how to cleanly shutdown high-concurrency Jms.messageDrivenChannelAdapter?

When I try to shutdown my spring-integration process, the flow using an inbound Jms.messageDrivenChannelAdapter throws the following error message:
"org.springframework.jms.listener.DefaultMessageListenerContainer - Rejecting received message because of the listener container been stopeed in the meantime"
my inbound adapter is defined as follows:
Jms.messageDrivenChannelAdapter(
Jms.container(jmsConnectionFactory, destinationName)
.concurrency(highConcurrency)
.get()
)
I believe that my problem is that the default "receiveTimeout" on my jms container is too small and that I need to increase that value to cater for my "high-concurrency" (right ?), as "receiveTimeout" seems to be the only value the container "doShutdown" method cares about.
Now, the sourceCode for the receiveTimeout property says "this value needs to be smaller than the transaction timeout". Also the spring-integration doco regarding inbound jms adapters says "if you want the entire flow to be transactional [...] consider using a jms-message-driven-channel-adapter with acknowledge set to transacted (the default)", which seems to imply that the jms adapter is transactional by default.
Hence, my main question is: even though I'm not using any explicit transaction manager, do I need to not only explicitely set "receiveTimeout" on my container but also "transactionTimeout" with transactionTimeout > receiveTimeout ?
Thanks a lot in advance for your expertise and your time.
Best Regards
That is not "throws". That is just warn:
protected void doExecuteListener(Session session, Message message) throws JMSException {
if (!isAcceptMessagesWhileStopping() && !isRunning()) {
if (logger.isWarnEnabled()) {
logger.warn("Rejecting received message because of the listener container " +
"having been stopped in the meantime: " + message);
}
rollbackIfNecessary(session);
throw new MessageRejectedWhileStoppingException();
}
And pay attention to that rollbackIfNecessary(session);. So, even if the received message slips somehow into this listener function, the whole environment makes it sure that the state is not broken and the data is not lost - the session is rolled back.
The transactionTimeout does not make sense if you don't use a transactionManager. Spring Integration makes it transacted exactly for the use-case we see around that warn log.

How to execute stored procedure

I know this is not recommended way by Acumatica, but we don't have other option than to use stored procedure. I have created a new processing screen to execute stored procedure but am facing time out exception.
My code sample is below:
using (new PXConnectionScope())
{
using (PXTransactionScope ts = new PXTransactionScope())
{
PXDatabase.Execute("MYSTOREDPROCEDURE", pars.ToArray());
ts.Complete();
}
}
Try executing long running code in PXLongOperation context. I assume these establishes a connection with periodic ping to avoid time-out while waiting for data to arrive.
PXLongOperation.StartOperation(Base, delegate()
{
// Code executed in long operation context
});
If your code is executed from the context of a processing delegate I think it should be already wrapped in a long operation though. Otherwise long operation should be used inside an action event handler.
A last recourse would be to increase time-out in the web.config file.
Use of stored procedure is a concern mainly for SAAS hosting and obtaining an Acumatica ISV Certification. There's likely no official support for it but I doubt it's gonna go away.

Can the Azure Service Bus be delayed before retrying a message?

The Azure Service Bus supports a built-in retry mechanism which makes an abandoned message immediately visible for another read attempt. I'm trying to use this mechanism to handle some transient errors, but the message is made available immediately after being abandoned.
What I would like to do is make the message invisible for a period of time after it is abandoned, preferably based on an exponentially incrementing policy.
I've tried to set the ScheduledEnqueueTimeUtc property when abandoning the message, but it doesn't seem to have an effect:
var messagingFactory = MessagingFactory.CreateFromConnectionString(...);
var receiver = messagingFactory.CreateMessageReceiver("test-queue");
receiver.OnMessageAsync(async brokeredMessage =>
{
await brokeredMessage.AbandonAsync(
new Dictionary<string, object>
{
{ "ScheduledEnqueueTimeUtc", DateTime.UtcNow.AddSeconds(30) }
});
}
});
I've considered not abandoning the message at all and just letting the lock expire, but this would require having some way to influence how the MessageReceiver specifies the lock duration on a message, and I can't find anything in the API to let me change this value. In addition, it wouldn't be possible to read the delivery count of the message (and therefore make a decision for how long to wait for the next retry) until after the lock is already required.
Can the retry policy in the Message Bus be influenced in some way, or can a delay be artificially introduced in some other way?
Careful here because I think you are confusing the retry feature with the automatic Complete/Abandon mechanism for the OnMessage event-driven message handling. The built in retry mechanism comes into play when a call to the Service Bus fails. For example, if you call to set a message as complete and that fails, then the retry mechanism would kick in. If you are processing a message an exception occurs in your own code that will NOT trigger a retry through the retry feature. Your question doesn't get explicit on if the error is from your code or when attempting to contact the service bus.
If you are indeed after modifying the retry policy that occurs when an error occurs attempting to communicate with the service bus you can modify the RetryPolicy that is set on the MessageReciver itself. There is an RetryExponitial which is used by default, as well as an abstract RetryPolicy you can create your own from.
What I think you are after is more control over what happens when you get an exception doing your processing, and you want to push off working on that message. There are a few options:
When you create your message handler you can set up OnMessageOptions. One of the properties is "AutoComplete". By default this is set to true, which means as soon as processing for the message is completed the Complete method is called automatically. If an exception occurs then abandon is automatically called, which is what you are seeing. By setting the AutoComplete to false you required to call Complete on your own from within the message handler. Failing to do so will cause the message lock to eventually run out, which is one of the behaviors you are looking for.
So, you could write your handler so that if an exception occurs during your processing you simply do not call Complete. The message would then remain on the queue until it's lock runs out and then would become available again. The standard dead lettering mechanism applies and after x number of tries it will be put into the deadletter queue automatically.
A caution of handling this way is that any type of exception will be treated this way. You really need to think about what types of exceptions are doing this and if you really want to push off processing or not. For example, if you are calling a third party system during your processing and it gives you an exception you know is transient, great. If, however, it gives you an error that you know will be a big problem then you may decide to do something else in the system besides just bailing on the message.
You could also look at the "Defer" method. This method actually will then not allow that message to be processed off the queue unless it is specifically pulled by its sequence number. You're code would have to remember the sequence number value and pull it. This isn't quite what you described though.
Another option is you can move away from the OnMessage, Event-driven style of processing messages. While this is very helpful you don't get a lot of control over things. Instead hook up your own processing loop and handle the abandon/complete on your own. You'll also need to deal some of the threading/concurrent call management that the OnMessage pattern gives you. This can be more work but you have the ultimate in flexibility.
Finally, I believe the reason the call you made to AbandonAsync passing the properties you wanted to modify didn't work is that those properties are referring to Metadata properties on the method, not standard properties on BrokeredMessage.
I actually asked this same question last year (implementation aside) with the three approaches I could think of looking at the API. #ClemensVasters, who works on the SB team, responded that using Defer with some kind of re-receive is really the only way to control this precisely.
You can read my comment to his answer for a specific approach to doing it where I suggest using a secondary queue to store messages that indicate which primary messages have been deferred and need to be re-received from the main queue. Then you can control how long you wait by setting the ScheduledEnqueueTimeUtc on those secondary messages to control exactly how long you wait before you retry.
I ran into a similar issue where our order picking system is legacy and goes into maintenance mode each night.
Using the ideas in this article(https://markheath.net/post/defer-processing-azure-service-bus-message) I created a custom property to track how many times a message has been resubmitted and manually dead lettering the message after 10 tries. If the message is under 10 retries it clones the message increments the custom property and sets the en queue of the new message.
using Microsoft.Azure.ServiceBus;
public PickQueue()
{
queueClient = new QueueClient(QUEUE_CONN_STRING, QUEUE_NAME);
}
public async Task QueueMessageAsync(int OrderId)
{
string body = JsonConvert.SerializeObject(OrderId);
var message = new Message(Encoding.UTF8.GetBytes(body));
await queueClient.SendAsync(message);
}
public async Task ReQueueMessageAsync(Message message, DateTime utcEnqueueTime)
{
int resubmitCount = (int)(message.UserProperties["ResubmitCount"] ?? 0) + 1;
if (resubmitCount > 10)
{
await queueClient.DeadLetterAsync(message.SystemProperties.LockToken);
}
else
{
Message clone = message.Clone();
clone.UserProperties["ResubmitCount"] = ++resubmitCount;
await queueClient.ScheduleMessageAsync(message, utcEnqueueTime);
}
}
This question asks how to implement exponential backoff in Azure Functions. If you do not want to use the built-in RetryPolicy (only available when autoComplete = false), here's the solution I've been using:
public static async Task ExceptionHandler(IMessageSession MessageSession, string LockToken, int DeliveryCount)
{
if (DeliveryCount < Globals.MaxDeliveryCount)
{
var DelaySeconds = Math.Pow(Globals.ExponentialBackoff, DeliveryCount);
await Task.Delay(TimeSpan.FromSeconds(DelaySeconds));
await MessageSession.AbandonAsync(LockToken);
}
else
{
await MessageSession.DeadLetterAsync(LockToken);
}
}

Does EF5 Auto rollback when a save failed

This is my first time using EF.
I would like to know if EF 5 will auto rollsback on error when saving. I created a test app and it seems like it does but I'm not sure if its a setting.
Here is my code
public Test()
{
InitializeComponent();
//Gets Gets unit of work for a specific context
var s=DAL.DALHelper.GetUnitOfWork();
var categoryRepo=s.GetRepository<Category>();
var onlyRecord = s.GetRepository<Category>().GetById(3);
onlyRecord.CategoryDescription = "Test2222";
Category catToAdd=new Category();
catToAdd.CategoryDescription="Test3";
catToAdd.CategoryName="Toys";
//This will break due to a constraint
categoryRepo.Add(catToAdd);
s.Save();
}
I have seen lots of code on the web that shows running code within a transaction, so I'm a little skeptical on the auto rollback.
There is an implicit transaction for a call to DbContext.SaveChanges(), so any operations performed during a single DbContext.SaveChanges() call that throws an exception will roll back. If you call DbContext.SaveChanges() twice and the second call results in an exception, the changes from the first SaveChanges() call will not be rolled back

Fault Tolerance & state management in Domain Services

In my domain, services are used to coordinate higher level behavior that involve multiple aggregates and/or other services. Take for instance an order management system which needs to perform the following steps when canceling an order:
Change the state of the Order to "Canceled"
Reverse any pending credit card transaction
Add an audit entry containing an explanation why the order was canceled
Persist changes to the Order to the data store
Raise the OrderCanceledEvent (as a message)
Coding this is pretty straight-forward except there are a few additional concerns I have to implement:
I cannot perform any of the actions if the Order is not in a
"cancelable" state
I cannot perform any of the actions if the current user does not have
permission to cancel orders
If I cannot reverse the credit card transaction then the entire
operation should fail and the Order should remain in its original
state
Failing to add the audit entry does not abort the operation
Failing to persist the Order should abort the operation and the Order
should remain in its original state
The operation is only successful if steps 1, 2, and 4 succeed. As a result, I cannot have these steps implemented as event handlers.
Putting aside any issues with the persistance mechanism in use (it is what it is), can you help me understand how best to implement my service so that it handles validation, errors and managing the state of the Order properly?
(I should note that I am not using Event Sourcing, so don't let the OrderCanceledEvent throw you.)
One way to solve this with event handlers is with the use of a saga. The workflow would be as follows:
Upon receiving CancelOrder command, a OrderCancellationSaga is started, placing the order into a Cancelling state.
Upon confirmation of refund from payment gateway, the saga is completed and the order is placed into a cancelled state and persisted. At this point, within the same transaction, the OrderCancelled event is raised.
If the interaction with the payment gateway fails or is declined, the order can be reverted into the prior state or placed into some sort of error state.
In this scenario auditing can occur at any stage. Also, permissions and whether the order can be cancelled in the first place should be verified before starting the saga or as a first step in starting the saga.
Rough sample with C# and NServiceBus sagas:
class OrderOrderCancellationSaga : Saga<OrderCancellationSagaData>
,IAmStartedBy<CancelOrderCommand>,
,IHandle<PaymentGatewayInteractionFailedEvent>
{
public OrderService OrderService { get; set; }
public PaymentGateway PaymentGateway { get; set; }
// correlate saga messages by order ID
public override void ConfigureHowToFindSaga()
{
ConfigureMapping<PaymentGatewayInteractionFailedEvent>(x => x.OrderId, x =>x.OrderId);
ConfigureMapping<RefundCompletedEvent>(x => x.OrderId, x => x.OrderId);
}
// start cancellation process
public void Handle(CancelOrderCommand message)
{
// check if cancellation is authorized and valid
// ....
// can save prior state here, if needed
this.Data.OrderId = message.OrderId;
this.Data.State = "Cancelling";
this.Bus.Send(new RefundOrderCommand(...));
}
public void Handle(RefundCompletedEvent message)
{
this.Data.State = "Cancelled";
this.OrderService.CompleteCancellation(...);
MarkAsComplete();
}
// this handler can be hosted on a different endpoint.
public void Handle(RefundOrderCommand message)
{
try
{
this.PaymentGateway.Refund(...
}
catch(Exception ex)
{
this.Bus.Reply(new PaymentGatewayInteractionFailedEventmessage(...));
}
}
// this handler can be used to revert whole operation.
public void Handle(PaymentGatewayInteractionFailedEvent message)
{
// or revert to prior state.
this.Data.Status = "Cancellation Failed";
// call any application services needed.
// finishes saga, deleting state
MarkAsComplete();
}
}

Resources