I am dispatching a message from the service bus publisher, & essentially doing a db polling for 5 seconds.
var message = new Message(Encoding.UTF8.GetBytes(object));
message.SessionId = sessionId;
return client.SendAsync(message);
On the consumer side, I am receiving the message and doing some processing which may or may not take more than 5 seconds. We can call CompleteAsync(), AbadonAsync() to close that session.
Can we cancel from the publisher client?
Essentially, I want something like such
queueClient.publish(message); // publish message to queue
var tokenSource - new CancellationToken();
var task = Task.Run(()=>{
...
}, tokenSource.Token);
if(!task.Wait(5000, tokenSource.Token))
{
queueClient.Cancel(); // <-----Cancel/Abadon processing in service bus consumer.
}
You can try configuring the timeout for the queue client
MessagingFactorySettings factorySettings = new MessagingFactorySettings()
{
TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider("[keyName]", "[sharedAccessKey]"),
OperationTimeout = TimeSpan.FromSeconds(5),
};
MessagingFactory messagingFactory = MessagingFactory.Create(serviceBusUri, factorySettings);
QueueClient Client = messagingFactory.CreateQueueClient("[QueueName]");
Related
I created a bot with a command, that allows the user to configure some sort of 'feed' to their channel.
This feed is supposed to send a message, save guild, channel and message id. And in a stand-alone update cycle, try to update the message with new information.
This all works fairly well, as long as it is within the same session.
Say the bot losses it's connection due to a discord outage, and re-connects x amount of time later, the bot no longer seems to be able to find, and thus update the message anymore.
In particular, it seems to be unable to retrieve the message by id
var message = await channel.GetMessageAsync(playtimeFeed.MessageId) as SocketUserMessage;
It's worth to note that I make use of _settings which is persisted in json format, and is loaded again upon bot reboot.
I also confirmed that the message still exists in the server at the channel, with the same message id. And that the bot has permissions to view the message history of the channel.
Thus my question, how come the GetMessageAsync is unable to retrieve a previously posted message after reconnecting?
Initialy invoked command
public async Task BindPlaytimeFeedAsync(ICommandContext context)
{
var builder = await _scumService.GetTop25PlaytimeByDate(new DateTime(), DateTime.Now);
var message = await context.Channel.SendMessageAsync(null, false, builder.Build());
_settings.PlaytimeFeed = new MessageInfo()
{
GuildId = context.Guild.Id,
ChannelId = context.Channel.Id,
MessageId = message.Id,
};
var ptFeedMessage = await context.Channel.SendMessageAsync("Playtime feed is now bound to this channel (this message self-destructs in 5 seconds)");
await Task.Delay(5000);
await ptFeedMessage.DeleteAsync();
}
The refresh interval of the feed is defined alongside the bot itself using a timer as seen below.
...
_client = new DiscordSocketClient(
new DiscordSocketConfig
{
LogLevel = LogSeverity.Verbose,
AlwaysDownloadUsers = true, // Start the cache off with updated information.
MessageCacheSize = 1000
}
);
_service = ConfigureServices();
_feedInterval = new Timer(async (e) =>
{
Console.WriteLine("doing feed stuff");
await HandleFeedsAsync();
}, null, 15000, 300000);
CmdHandler = new CommandHandler(_service, state);
...
private async Task HandleFeedsAsync()
{
var botSettings = _service.GetService<ISettings>() as BotSettings;
await HandleKdFeedAsync(botSettings.KdFeed);
await HandlePlaytimeFeedAsync(botSettings.PlaytimeFeed);
await HandleWeeklyPlaytimeFeed(botSettings.WeeklyPlaytimeFeed);
await HandleAdminFeed(botSettings);
}
And ultimately the message is overwritten using the below snippet.
private async Task HandlePlaytimeFeedAsync(MessageInfo playtimeFeed)
{
if (playtimeFeed == null)
return;
var scumService = _service.GetService<ScumService>();
var guild = _client.GetGuild(playtimeFeed.GuildId);
var channel = guild.GetTextChannel(playtimeFeed.ChannelId);
var message = await channel.GetMessageAsync(playtimeFeed.MessageId) as SocketUserMessage;
if (message == null)
return;
var builder = await scumService.GetTop25PlaytimeByDate(new DateTime(), DateTime.Now);
await message.ModifyAsync(prop =>
{
prop.Embed = builder.Build();
});
}
var message = await channel.GetMessageAsync(playtimeFeed.MessageId) as SocketUserMessage;
The GetMessageAsync method attempts to retrieve a message from cache as a SocketUserMessage, if however the message is not found in cache, a rest request is performed which would return a RestUserMessge. By performing a soft cast on the result of GetMessageAsync, you can get null if/when a RestUserMessage is returned.
When the possibility exists that the message you are dealing with can be either a Socket entity or Rest entity, simply use the interface to interact with it -- IUserMessage.
I've an Azure Web Job with the following initialization code
class Program
{
static void Main(string[] args)
{
IHostBuilder builder = new HostBuilder()
.ConfigureWebJobs(b =>
{
b.AddServiceBus((opt) =>
{
opt.ConnectionString = "Connection string";
opt.MessageHandlerOptions = new MessageHandlerOptions((ex) =>
{
return Task.Run(() =>
{
// logging the error message
});
})
{
MaxAutoRenewDuration = new TimeSpan(0, 0, 5, 0),
MaxConcurrentCalls = 1
};
});
})
.UseConsoleLifetime();
IHost host = builder.Build();
using (host)
{
host.Run();
}
}
}
The Service Bus queue is configured to have a Lock Duration of 5 minutes, that is the maximum time that Azure allows.
The message processing can take more than 30 minutes and the lock renew mechanism works correctly.
When the process ends correctly, an exception is thrown The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue, or was received by a different receiver instance and the message goes back to the queue again.
When you call messsage.Complete() (or CompleteAsync()) then you should instantiate an MessageHandlerOptions object, set AutoComplete to false, and pass it into your message handler registration.
new MessageHandlerOptions(OnException)
{
AutoComplete = false,
MaxConcurrentCalls = MaxConcurrentCalls, // 1
MaxAutoRenewDuration = MaxAutoRenewDuration // 2 hrs
}
For more details, you could refer to this article.
I created consumer and producer using MassTransit:
var busControl = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(messageBrokerUri, h =>
{
h.Username(communicationConfiguration.MessageBrokerUsername);
h.Password(communicationConfiguration.MessageBrokerPassword);
});
cfg.ReceiveEndpoint(host, communicationConfiguration.FileResultAggregationServiceAddress, ec =>
{
ec.Consumer(serviceCollection.GetRequiredService<IFileResultAggregationConsumer>);
});
});
busControl.Start();
My question is: if the consumer consume message1 and in the middle i will shut down the consumer service then rabbitMQ will not get an ack. after 2 minutes the consumer will be alive.
What will happen to message1?
If the message will gone, how can i use Masstransit to make the message not disappear
I have a service fabric service which i call like this from another service :
var checkerProxy = new ServiceProxyFactory<ICheck>(uri);
var checker = checkerProxy.CreateSingletonServiceProxy();
success = await checker.CheckMe();
I want to abort the call if it takes longer than a timeout.
How do I set timeout for a remoting call in service fabric ?
Edit 1 : note i can do something like this :
success = checker.CheckMe().Wait(TimeSpan.FromSeconds(10));
but this will not abort the remoting call, but only stop waiting for the completion of the task upon a timeout and i do not have the return value.
You can set timeout in proxy using FabricTransportRemotingSettings :
FabricTransportRemotingSettings settings = new FabricTransportRemotingSettings();
settings.OperationTimeout = TimeSpan.FromMinutes(5);
return new ServiceProxyFactory(
(h) =>
{
return new FabricTransportServiceRemotingClientFactory(settings);
});
I'm using Azure Service Bus as transport for MassTransit and I'm trying to send message(command) to queue:
var sendEndpoint = await busControl.GetSendEndpoint(sericeUri);
sendEndpoint.Send<ISimpleRequest>(new SimpleRequest(customerId));
Also I try to connect to this queue by two consumers:
var _busControl = Bus.Factory.CreateUsingAzureServiceBus(cfg =>
{
var host = cfg.Host("...", h =>
{
h.OperationTimeout = TimeSpan.FromMinutes(1);
});
cfg.ReceiveEndpoint(host, "queueName",
e => { e.Consumer<RequestConsumer>(); });
cfg.UseServiceBusMessageScheduler();
});
The same code with same queue name for second consumer.
After I send message only one consumer get the response. So how I can config this to work with two or more consumers?
If you want to get two copies of the message, one for each consumer, you should use two separate queues and Publish the message. In this case, MassTransit will send it to the topic, and each queue will receive a copy forwarded from the topic.