Goal:
Show the message "show error" after 3 attempt.
Problem:
What part of the code do not work in order to achieve the goal.
using Polly;
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsolePollyTest2
{
class Program
{
static void Main(string[] args)
{
var maxRetryAttempts = 3;
var pauseBetweenFailures = TimeSpan.FromSeconds(2);
Policy
.Handle<HttpRequestException>()
.Or<TaskCanceledException>()
.WaitAndRetryAsync(maxRetryAttempts, i => pauseBetweenFailures)
.ExecuteAsync(PersistApplicationData2)
.ContinueWith(x =>
{
if (x.Exception != null)
{
Console.WriteLine("show error");
}
});
}
private static async Task PersistApplicationData2()
{
int ddfd = 3;
var df = ddfd / 0;
Console.WriteLine("Show data");
await Task.FromResult<object>(null);
}
}
}
Thank you!
Await your task to be run first also your policy says in case of HttpRequestException or TaskCanceledException retry policy will work and your method has no any exception of those.
If you want to test retry policy you can do some thing like this:
public static async Task Main(string[] args)
{
var maxRetryAttempts = 3;
var pauseBetweenFailures = TimeSpan.FromSeconds(2);
await Policy
.Handle<HttpRequestException>()
.Or<Exception>() //if any exception raised will try agian
.Or<TaskCanceledException>()
.WaitAndRetryAsync(maxRetryAttempts, i => pauseBetweenFailures)
.ExecuteAsync( PersistApplicationData2)
.ContinueWith(x =>
{
if (x.Exception != null)
{
Console.WriteLine("show error");
}
//success
}, scheduler: TaskScheduler.Default);
}
Related
I've implemented a socket connection module according to the instructions here
https://github.com/endel/NativeWebSocket
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NativeWebSocket;
public class Connection : MonoBehaviour
{
WebSocket websocket;
public bool SpamSend;
public float spamEvery = 3f;
public string uri = "ws://localhost:2567";
[TextArea] public string message;
private string CurrectData;
// Start is called before the first frame update
async void Start()
{
CurrectData = message;
websocket = new WebSocket(uri);
InitilizeWebSocket();
}
async void InitilizeWebSocket()
{
websocket.OnOpen += () =>
{
Debug.Log("Connection open!");
};
websocket.OnError += (e) =>
{
Debug.Log("Error! " + e);
};
websocket.OnClose += (e) =>
{
Debug.Log("Connection closed!");
};
websocket.OnMessage += (bytes) =>
{
Debug.Log("OnMessage!");
//Debug.Log(bytes);
// getting the message as a string
var message = System.Text.Encoding.UTF8.GetString(bytes);
Debug.Log("OnMessage! " + message);
};
if(SpamSend)
// Keep sending messages at every 0.3s
InvokeRepeating("SendWebSocketMessage", 0.0f, spamEvery);
// waiting for messages
await websocket.Connect();
}
void Update()
{
#if !UNITY_WEBGL || UNITY_EDITOR
websocket.DispatchMessageQueue();
#endif
}
async void SendWebSocketMessage()
{
if (websocket.State == WebSocketState.Open)
{
// Sending bytes
// await websocket.Send(new byte[] { 10, 20, 30 });
// Sending plain text
await websocket.SendText(CurrectData);
CancelInvoke("SendWebSocketMessage");
}
}
private async void OnApplicationQuit()
{
await websocket.Close();
}
}
you may now notice the oddity of "invokeRepeating" and CancelInvoke.
this is where I've encountered a problem.
when I tried to just Invoke, I received no response from the server as if it was never sent.
nor when I tried a coroutine - with or without waitForSeconds.
nor when I simply tried SendWebSocketMessage().
What did I miss that only the invokeRepeating made it through?
I'm trying to register consumers but no success using mass transit.
I registered MT using Autofac using module approach.
Firstly - I created some simple message:
public class SimpleMessage
{
public string msg { get; set; }
}
and I've managed to send them into queue:
var endpointTest = await _busControl.GetSendEndpoint(new Uri("queue:queueTest"));
await endpointTest.Send(new SimpleMessage
{
msg = "test"
});
Then I created a consumer:
public class SimpleMessageConsumer : IConsumer<SimpleMessage>
{
private readonly ILogger _logger;
public SimpleMessageConsumer(ILogger logger)
{
_logger = logger;
}
public async Task Consume(ConsumeContext<SimpleMessage> context)
{
_logger.Info($"got msg from queue: {context.Message}");
}
}
But it won't run when the message appeared in the queue. My configuration is:
public class BusModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<BusSettings>().As<IBusSettings>();
builder.AddMassTransit(cfg =>
{
cfg.AddConsumer<SimpleMessageConsumer, SimpleMessageConsumerDefinition>();
cfg.Builder.Register(context =>
{
var busSettings = context.Resolve<IBusSettings>();
var logger = context.Resolve < ILogger >();
var busControl = Bus.Factory.CreateUsingRabbitMq(bus =>
{
bus.AutoDelete = busSettings.AutoDelete;
bus.Durable = busSettings.Durable;
bus.Exclusive = busSettings.Exclusive;
bus.ExchangeType = busSettings.Type;
//bus.UseNServiceBusJsonSerializer();
bus.Host(busSettings.HostAddress, busSettings.Port, busSettings.VirtualHost, null, h =>
{
h.Username(busSettings.Username);
h.Password(busSettings.Password);
});
bus.ReceiveEndpoint("queueTest", ec =>
{
ec.Consumer(() => new SimpleMessageConsumer(logger));
});
});
return busControl;
}).SingleInstance().As<IBusControl>().As<IBus>();
});
}
}
in program.cs
I have:
services.AddMassTransitHostedService();
and
containerBuilder.RegisterModule<BusModule>();
Such I mentioned - sending a msg to queue works but consumer wasn't running.
Can you help me what did I do wrong? how should I fix the configuration? in order to activate the consumer?
I've updated your configuration to work properly, using the actual bus configuration methods instead of mixing the two solutions:
public class BusModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<BusSettings>().As<IBusSettings>();
builder.AddMassTransit(cfg =>
{
cfg.AddConsumer<SimpleMessageConsumer, SimpleMessageConsumerDefinition>();
cfg.UsingRabbitMq((context,cfg) =>
{
var busSettings = context.GetRequiredService<IBusSettings>();
var logger = context.GetRequiredService<ILogger>();
//bus.UseNServiceBusJsonSerializer();
bus.Host(busSettings.HostAddress, busSettings.Port, busSettings.VirtualHost, null, h =>
{
h.Username(busSettings.Username);
h.Password(busSettings.Password);
});
bus.ReceiveEndpoint("queueTest", ec =>
{
// i'm guessing these apply to the receive endpoint, not the bus endpoint
ec.AutoDelete = busSettings.AutoDelete;
ec.Durable = busSettings.Durable;
ec.Exclusive = busSettings.Exclusive;
ec.ExchangeType = busSettings.Type;
ec.ConfigureConsumer<SimpleMessageConsumer>(context);
});
});
});
}
}
I want to read data from database. For this I create a query and queryhandler classes
QueryHandler
public class OrderGetQueryHandler: IQueryHandler<OrderGetQuery, OrderDTO>
{
private readonly GoodWillWebDbContext _context;
private readonly IQueryDispatcher _queryDispatcher;
public OrderGetQueryHandler(GoodWillWebDbContext context, IQueryDispatcher queryDispatcher)
{
_context = context;
_queryDispatcher = queryDispatcher;
}
private bool CheckPartnerBlock(BlockTypes blockType, decimal debtOverdue, bool payOff)
{
if (blockType == BlockTypes.Block)
return true;
if (blockType == BlockTypes.NotBlock)
return false;
if (blockType == BlockTypes.PreliminaryPayment)
return payOff;
return debtOverdue <= 0;
}
public async Task<OrderDTO> HandleAsync(OrderGetQuery query)
{
var order = await _context.Orders.FindAsync(query.OrderID);
if (order != null)
{
var getCustomerTask = _context.Partners.FindAsync(order.CustomerID).AsTask();
var getCuratorTask = _context.Users.FindAsync(order.CuratorID).AsTask();
var getPaymentTask = _context.Payments.OrderByDescending(x => x.PaymentID).FirstOrDefaultAsync(x => x.CustomerID == order.CustomerID);
var getOrderLinesTask =
_queryDispatcher.HandleAsync<OrderLinesGetQuery, OrderLineDTO[]>(
new OrderLinesGetQuery(query.OrderID));
await Task.WhenAll(getCustomerTask, getCuratorTask, getOrderLinesTask, getPaymentTask);
var priceRange = await _context.PriceRanges.FindAsync(getCustomerTask.Result.PriceRangeID);
return new OrderDTO
(
order.OrderID,
getCustomerTask.Result.Name,
getOrderLinesTask.Result,
order.CustomerID,
order.OrderStateID,
order.CanDelete,
order.CreationDate,
getPaymentTask.Result.DebtBank,
getPaymentTask.Result.DebtOverdue,
this.CheckPartnerBlock(getCustomerTask.Result.BlockTypeID, getPaymentTask.Result.DebtOverdue, order.PayOff),
priceRange.Name,
order.ReservationDate,
Mapper.Convert<DeliveryInfoDTO, BaseEntities.Entities.Sales.Order>(order)
);
}
throw new NullReferenceException();
}
}
this queryhandler i use in ASP.NET WEB Application. My startup class is
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
string connection = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<GoodWillWebDbContext>(options =>
options.UseSqlServer(connection), ServiceLifetime.Transient);
services.AddScoped<IQueryHandler<OrdersGetQuery, BaseEntities.DTO.Sales.Order.OrderDTO[]>, OrdersGetQueryHandler>();
services.AddScoped<IQueryHandler<OrderGetQuery, Sales.Queries.DTO.Order.OrderDTO>, OrderGetQueryHandler>();
services.AddScoped<ICommandDispatcher, CommandDispatcher>();
services.AddScoped<IQueryDispatcher, QueryDispatcher>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
I set ServiceLifetime.Transient for my context, but I still get an exception: InvalidOperationException A second operation started on this context before a previous operation completed.
What's wrong?
It seems you're running multiple operations on the context without waiting for the previous ones to end, which EF doesn't like:
var getCustomerTask = _context.Partners.FindAsync(order.CustomerID).AsTask();
var getCuratorTask = _context.Users.FindAsync(order.CuratorID).AsTask();
var getPaymentTask = _context.Payments.OrderByDescending(x => x.PaymentID).FirstOrDefaultAsync(x => x.CustomerID == order.CustomerID);
Either make these call sync or use the await keyword.
I'm new to Polly and I'm trying to apply the Retry policy, so that I can have it manually handling the retry connection in case of IBMMQ connection issue.
Please, consider the following code:
public class ReconnectException : Exception
{
}
public class QueueMonitor : IObservable<Message>, IDisposable
{
private readonly MQQueue mqQueue;
private readonly MQQueueManager queueManager;
private readonly string queueName;
private IDisposable timer;
private readonly object lockObj = new object();
private bool isChecking;
private readonly TimeSpan checkingFrequency;
private readonly List<IObserver<Message>> observers;
private TimeSpan reconnectInterval;
private readonly IScheduler scheduler;
private readonly int maxReconnectCount;
private static readonly ILog Logger = LogProvider.For<AonQueueManager>();
private readonly Policy pollyPolicy;
public QueueMonitor(IConfiguration configuration, string queueName, IScheduler scheduler = null)
{
this.queueManager = QueueFactory.GetIstance(configuration);
this.queueName = queueName;
this.scheduler = scheduler ?? Scheduler.Default;
checkingFrequency = configuration.GetValue("checkingFrequency", new TimeSpan(0, 0, 5));
reconnectInterval = configuration.GetValue("reconnectInterval", new TimeSpan(0, 0, 5));
maxReconnectCount = configuration.GetValue("maxReconnectCount", 3);
observers = new List<IObserver<Message>>();
pollyPolicy = Policy.Handle<ReconnectException>().WaitAndRetry(maxReconnectCount, _ => TimeSpan.FromSeconds(2));
mqQueue = queueManager.AccessQueue(queueName,
MQC.MQOO_INPUT_AS_Q_DEF // open queue for input
+ MQC.MQOO_FAIL_IF_QUIESCING); // but not if MQM stopping
}
public void Start()
{
var x = pollyPolicy.ExecuteAndCapture(CreateTimer);
}
private void CreateTimer()
{
Logger.DebugFormat("Repeating timer started, checking frequency: {checkingFrequency}", checkingFrequency);
timer = Observable.Interval(checkingFrequency, scheduler).Subscribe(_ =>
{
lock (lockObj)
{
if (isChecking) return;
Logger.Log(LogLevel.Debug, () => "Listening on queues for new messages");
isChecking = true;
var mqMsg = new MQMessage();
var mqGetMsgOpts = new MQGetMessageOptions { WaitInterval = checkingFrequency.Milliseconds };
// 15 second limit for waiting
mqGetMsgOpts.Options |= MQC.MQGMO_WAIT | MQC.MQGMO_FAIL_IF_QUIESCING |
MQC.MQCNO_RECONNECT_Q_MGR | MQC.MQOO_INPUT_AS_Q_DEF;
try
{
mqQueue.Get(mqMsg, mqGetMsgOpts);
if (mqMsg.Format.CompareTo(MQC.MQFMT_STRING) == 0)
{
var text = mqMsg.ReadString(mqMsg.MessageLength);
Logger.Debug($"Message received : [{text}]");
Message message = new Message { Content = text };
foreach (var observer in observers)
observer.OnNext(message);
}
else
{
Logger.Warn("Non-text message");
}
}
catch (MQException ex)
{
if (ex.Message == MQC.MQRC_NO_MSG_AVAILABLE.ToString())
{
Logger.Trace("No messages available");
//nothing to do, emtpy queue
}
else if (ex.Message == MQC.MQRC_CONNECTION_BROKEN.ToString())
{
Logger.ErrorException("MQ Exception, trying to recconect", ex);
throw new ReconnectException();
}
}
finally
{
isChecking = false;
}
}
});
}
public IDisposable Subscribe(IObserver<Message> observer)
{
if (!observers.Contains(observer))
observers.Add(observer);
return new Unsubscriber(observers, observer);
}
public void Dispose()
{
((IDisposable)mqQueue)?.Dispose();
((IDisposable)queueManager)?.Dispose();
timer?.Dispose();
}
}
public class Unsubscriber : IDisposable
{
private readonly List<IObserver<Message>> observers;
private readonly IObserver<Message> observer;
public Unsubscriber(List<IObserver<Message>> observers, IObserver<Message> observer)
{
this.observers = observers;
this.observer = observer;
}
public void Dispose()
{
if (observer != null) observers.Remove(observer);
}
}
The problem I've is that when an exception is thrown inside the lamda ( throw new ReconnectException();), Polly doesn't catch it (and I understand why, since it's on another thread) and the application quits since it's on a different thread.
This code is a part of a library,so I don't know that if in every project the Global exceptions are correctly handed.
How do I get it "catched" by the Polly's code?
Thanks in advance
The code posted in the question applies the policy only to the act of creating the timer (the execution of CreateTimer()), not to the code executed by the timer (the lambda inside the .(Subscribe(_ => { }) call).
This is the same as the behaviour if the call to CreateTimer() was surrounded by a try { } catch { }. The catch would only cover the act of executing the CreateTimer() method, the creation of the timer.
For the Polly policy to govern exceptions thrown within the lambda, it needs to be applied within the lambda, to the relevant block/group of statements which are expected to throw the exception.
For example, you might code:
pollyPolicy.ExecuteAndCapture(() => mqQueue.Get(mqMsg, mqGetMsgOpts));
(with a policy configured to govern the particular MQException/s you want to handle).
Or you can apply the policy to a wider group of statements - just as with a try { } clause.
pollyPolicy.ExecuteAndCapture(() =>
{
// ...
mqQueue.Get(mqMsg, mqGetMsgOpts));
// ...
}
I am trying to implement Npgsql in our DAL and running into issues under heavy load. the following sample application is a decent representation of just a simple query that under heavy load, throws a 'A command is already in progress' exception. I am assuming this is due to the lack of MARS support so I also tried creating a connection each time with a using statement around each command only to have the performance become unusable. I checked that the username is indexed so that shouldn't be an issue.
Not sure what I am doing wrong here but I need some advice on how to get this performing well.
OS: Docker Container: microsoft/dotnet:2.1.301-sdk
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace npgsqlTest
{
class Program
{
static async Task Main(string[] args)
{
DAL dal = new DAL();
dal.Prepare();
var tasks = dal.Users.Select(async user =>
{
Console.WriteLine(await dal.RunTest(user));
});
await Task.WhenAll(tasks);
}
}
public class DAL
{
private static string _ConnectionString;
private NpgsqlConnection _Connection;
public List<string> Users { get; set; } = new List<string>();
public DAL()
{
_ConnectionString = $"Host=192.168.1.1;Username=admin;Port=5432;Password=password;Database=BigDB;";
_Connection = new NpgsqlConnection(_ConnectionString);
_Connection.Open();
}
public void Prepare()
{
string query = "SELECT username FROM usertable;";
using (var cmd = new NpgsqlCommand(query, _Connection))
{
var reader = cmd.ExecuteReader();
using (reader)
{
while (reader.Read())
{
Users.Add(reader[0].ToString());
}
}
}
}
public async Task<string> RunTest(string user)
{
var parameters = new Dictionary<string, Object> { { "username", user } };
var query = $"SELECT name FROM usertable WHERE username = (#username);";
var reader = await QueryAsync(query, parameters);
using (reader)
{
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var name = reader["name"];
if (!(hash is DBNull))
return (string)name;
}
}
}
return String.Empty;
}
public async Task<DbDataReader> QueryAsync(string query, Dictionary<string, Object> parameters)
{
using (var cmd = new NpgsqlCommand(query, _Connection))
{
foreach (var parameter in parameters)
{
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value == null ? DBNull.Value : parameter.Value);
}
cmd.Prepare();
return await cmd.ExecuteReaderAsync();
}
}
}
}