I am building a continuous job that listens for Azure queue messages. Using the documentation I have something like this:
static async Task Main()
{
var builder = new HostBuilder();
builder.UseEnvironment("development");
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
public static void Run(
[QueueTrigger("myqueue-items")] string myQueueItem,
ILogger log)
{
...
}
I'm not a fan of the magic QueueTrigger attribute found at runtime. Is it possible to add a trigger function manually? I'm imagining something like:
static async Task Main()
{
var builder = new HostBuilder();
builder.UseEnvironment("development");
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddQueueListener("myqueue-items", Run); // no magic attributes, compiler error if Run function is deleted
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
public static void Run(
string myQueueItem,
ILogger log)
{
...
}
Yes, you can use manual triggers, you don’t need to use QueueTrigger attribute, but, as described in the document: To trigger a function manually, use the NoAutomaticTrigger attribute.
Therefore, I think whether you are using Automatic triggers or Manual triggers, you must add a trigger with attributes, which is necessary!
Related
I am currently facing a strange issue I am not able to reproduce locally, but happens in AWS ECS regularly, letting the application crash or run slow.
We have a spring boot application which extracts the tenant from the incoming GraphQL request and sets the tenant to a ThreadLocal instance.
To support DataLoader from GraphQL Java kickstart we populate the tenant to each child thread which will be used by the graphql dataloader. The tenant is mandatory to specify the database schema.
The executor
#Bean
#Override
public Executor getAsyncExecutor() {
log.info("Configuring async executor for multi tenancy...");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(15);
executor.setThreadNamePrefix("tenant-child-executor-");
// Important part: Set the MultiTenancyTaskDecorator to populate current tenant to child thread
executor.setTaskDecorator(new MultiTenancyAsyncTaskDecorator());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.setWaitForTasksToCompleteOnShutdown(true);
log.info("Executor configured successfully!");
executor.initialize();
return executor;
}
Task Decorator
#NonNull
#Override
public Runnable decorate(#NonNull Runnable runnable) {
if (Objects.isNull(CurrentTenantContext.getTenant())) {
log.warn("Current tenant is null while decorating a new thread!");
}
final TenantIdentifier parentThreadTenantIdentifier = Objects.isNull(CurrentTenantContext.getTenant()) ? TenantIdentifier.asSystem() : CurrentTenantContext.getTenant();
// Also need to get the MDC context map as it is bound to the current local thread
final Map<String, String> parentContextMap = MDC.getCopyOfContextMap();
final var requestAttributes = RequestContextHolder.getRequestAttributes();
return () -> {
try {
CurrentTenantContext.setTenant(TenantIdentifier.of(parentThreadTenantIdentifier.getTenantName()));
if (Objects.isNull(requestAttributes)) {
log.warn("RequestAttributes are not available!");
log.warn("Running on tenant: {}", parentThreadTenantIdentifier.getTenantName());
} else {
RequestContextHolder.setRequestAttributes(requestAttributes, true);
}
if (Objects.isNull(parentContextMap)) {
log.warn("Parent context map not available!");
log.warn("Running on tenant: {}", parentThreadTenantIdentifier.getTenantName());
} else {
MDC.setContextMap(parentContextMap);
}
runnable.run();
} finally {
// Will be executed after thread finished or on exception
RequestContextHolder.resetRequestAttributes();
CurrentTenantContext.clear();
MDC.clear();
}
};
}
Tenant Context
public class CurrentTenantContext {
private static final ThreadLocal<TenantIdentifier> currentTenant = new ThreadLocal<>();
private CurrentTenantContext() {
// Hide constructor to only provide static functionality
}
public static TenantIdentifier getTenant() {
return currentTenant.get();
}
public static String getTenantName() {
return getTenant().getTenantName();
}
public static void setTenant(TenantIdentifier tenant) {
currentTenant.set(tenant);
}
public static void clear() {
currentTenant.remove();
}
public static boolean isTenantSet() {
return Objects.nonNull(currentTenant.get());
}
}
Locally, this works like a charm. Even in a docker compose environment with limited resources (CPU and Mem) like in AWS. Even 100.000 requests (JMETER) everything works like expected.
On AWS we can easily let the application crash.
After one or two requests, containing some child objects to resolve by GraphQL, we see a thread spawning which seems to ignore or not go through the chain
Thread-110 | [sys ] | WARN | MultiTenancyAsyncTaskDecorator | Current tenant is null while decorating a new thread!
An interesting thing in this line is the name of the thread.
Each incoming request has the pattern http-nio-9100-exec-[N] and each child thread the pattern tenant-child-executor-[I] but this one has the pattern Thread-[Y].
Now I am wondering where this thread is coming from and why is it not reproducible locally.
I was able to find the solution to the problem.
I needed to change
private static final ThreadLocal<TenantIdentifier> currentTenant = new ThreadLocal<>();
to
private static final InheritableThreadLocal<TenantIdentifier> currentTenant = new InheritableThreadLocal<>();
But I don't know why it works with InheritableThreadLocal but not with ThreadLocal within the AWS environment.
Further, I wonder why this change was not necessary for local testing which works with both ways.
Maybe somebody can provide some ideas.
I expect that uploadImage method finishes once the file is uploaded to AWS, while scanFile method is still running asynchronously in the background;
#RestController
public class EmailController {
#PostMapping("/upload")
#ResponseStatus(HttpStatus.OK)
public void uploadImage(#RequestParam MultipartFile photos) {
awsAPIService.uploadImage(photos);
}
}
...
#Service
public class AwsAPIService {
public void uploadImage(MultipartFile file) {
try {
File fileToUpload = this.convertMultiPartToFile(file);
String fileName = this.generateFileName(file);
s3client.putObject(new PutObjectRequest(AWS_S3_QUARANTINE_BUCKET_NAME,fileName, fileToUpload));
fileToUpload.delete();
// start scan file
scanFile();
} ...
}
#Async
public void scanFile() {
log.info("Start scanning");
String queueUrl = sqs.getQueueUrl("bucket-antivirus").getQueueUrl();
List<Message> messages = sqs.receiveMessage(new ReceiveMessageRequest().withQueueUrl(queueUrl)
.withWaitTimeSeconds(20)).getMessages();
for (Message message : messages) {
// delete message
...
}
}
}
...
#EnableAsync
public class AppConfig {
#Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setMaxPoolSize(2);
taskExecutor.setQueueCapacity(200);
taskExecutor.afterPropertiesSet();
return taskExecutor;
}
}
But this seems still running synchronously. What is the problem here?
By default #Async and other Spring method-level annotations like #Transactional work only on the external, bean-to-bean method call. An internal method call from uploadImage() to scanFile() in the same bean won't trigger the proxy implementing the Spring behaviour. As per Spring docs:
In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. #PostConstruct.
You could configure AspectJ to enable annotations on internal method calls, but it's usually easier to refactor the code.
I have a class that starts a server:
public class SocketServer
{
private static IXSocketServerContainer server = null;
public SocketServer()
{
server = XSockets.Plugin.Framework.Composable
.GetExport<IXSocketServerContainer>();
}
public bool StartServers()
{
try
{
server.StartServers();
return true;
} catch
{
return false;
}
}
this class has a method:
public void SendEventMessageToAllClients(string message)
{
XSockets.Core.XSocket.Helpers.XSocketHelper
.SendToAll<MyController>(new MyController(), message, "events");
}
where MyController is my own controller, it is implemented and the server can find it and this method work.
Now I would like to expand the functionality with a new method that allows me to send an event to an specific client:
public void SendEventMessageToClient(string clientId, string message)
{
XSockets.Core.XSocket.Helpers.XSocketHelper
.SendTo<MyController>(new MyController(),
p => p.ClientId == clientId, message, "events");
}
Is this the right approach or am I doing something wrong?
Thanks!
I would not recomend that approach, I have not even tested if that actaully works.
You create a new controller every time just to be able to access the extension method.
I am guessing that since you have this on the class starting the server you only use this as a publisher?
If so the correct way would be to install the XSockets.Client package and use the client pool to publish messages: client pool documentation
Example with client pool
The nice thing about the client pool is that you do not need to create an instance every time. The pool will reuse your connection to the controller.
Using the clientpool (or a real client connection) will ensure that the message pass through the Pipeline and all interceptors if you have any. Using a controller instance directly will never reach the pipline, interceptors etc.
//Get a pool client
ClientPool poolClient =
XSockets.Client.ClientPool.GetInstance("ws://127.0.0.1:4502/MyController", "*");
Methods for sending a message to the controller.
public void SendEventMessageToClient(Guid clientId, string message)
{
poolClient.Send(new {clientId, message}, "SendEventMessageToClient");
}
public void SendEventMessageToAllClients(string message)
{
poolClient.Send(message, "SendEventMessageToAllClients");
}
The controller
public void SendEventMessageToClient(Guid clientId, string message)
{
this.SendTo(p => p.ClientId == clientId, message, "SendEventMessageToClient");
}
public void SendEventMessageToAllClients(string message)
{
this.SendToAll(message, "SendEventMessageToAllClients");
}
Example with instance of controller
If you decide to use the way you have done you should at least create on ONE instance of the controller to use the in the server class.
Important: Using a controller instance directly will never reach the pipline, interceptors etc.
//Be aware of the fact that this controller NEVER will have a connection.
//It can only send to others, it can never receive messages!
MyController c = new MyController();
//You should probably have a Guid here instead of string
//Also note that the client have to subscribe for "events" to get the message
public void SendEventMessageToClient(Guid clientId, string message)
{
this.SendTo(p => p.ClientId == clientId, message, "SendEventMessageToClient");
}
public void SendEventMessageToAllClients(string message)
{
this.SendToAll(message, "SendEventMessageToAllClients");
}
Since I do not know what you are trying to accomplish Im not sure this is the best way, but one of the merhods above should work.
EDIT: Also, in a real application you probably dont have access to the MyController class since it probably is in a separate assembly not being referenced at compile time. So then you approach will not even be possible and the way to go then is client or clientpool
/Uffe
Uffe, you're right an the ClientPool is the right option for me, I had problems running your code because some of the mappings proposed by you are not working, here is your proposed solution slightly modified to make it run:
//Get a pool client
ClientPool poolClient = XSockets.Client.ClientPool.GetInstance("ws://127.0.0.1:4502/MyController", "*");
Methods for sending a message to the controller. ITextArgs are needed in this case
public void SendEventMessageToClient(Guid clientId, string message)
{
ITextArgs textargs = new TextArgs(mess, "SendEventMessageToClient");
poolClient.Send(new {clientId = guid, message = "Hello to one client"}, "SendEventMessageToClient");
}
Here, I TextArgs is not needed, it can be used, but string works also properly. It seems that the conversion to ITextArgs works fine here.
public void SendEventMessageToAllClients(string message)
{
poolClient.Send("hello all", "SendEventMessageToAllClients");
}
The controller: Only ITextArgs messages are mapped. Using string will not work.
public void SendEventMessageToClient(Guid clientId, ITextArgs message)
{
c.SendTo(p => p.ClientId == clientId, message.data, "events");
}
public void SendEventMessageToAllClients(ITextArgs message)
{
c.SendToAll(message.data, "events");
}
Thanks you very much Uffe for your help!
I have a ViewModel which, in the constructor, makes a call to an async void method to add to a collection
public MyViewModel(ICommandHandler commandHandler)
{
_commandHandler = commandHandler;
SetupCollection();
}
private async void SetupCollection()
{
var commands = GetCommands();
foreach (var command in commands)
{
var response = await _commandHandler.ExecuteGetReply(command);
if (response != null)
Response.Add(response);
}
}
How exactly would I stub the _commandHandler.ExecuteGetReply() command to return a value?
Also, is it OK to have such a function in the constructor to do something like this? Or should this perhaps go within an... override void OnActivate() call (I'm using Caliburn Micro) ?
ICommandHandler.ExecuteGetReply appears to return a Task<Response> so you can do something like:
ICommand commandArg;
Response response;
stubHandler.Stub(h => h.ExecuteGetReply(commandArg)).Return(Task.FromResult(response));
I wouldn't call an async void method from your constructor however, since you will have no way of being notified when it has completed.
I am using MVVM pattern and silverlight 4.0 and Moq for testing.
In the view model constructor, am passing an IEventAggregator object. This object is used to subscribe to an event called SelectionChangedEvent.
In the test method I am doing like this:
this.selectedEvent = new Mock<SelectionChangedEvent>();
this.eventAggregator.Setup(x => x.GetEvent<SelectionChangedEvent>()).Returns(this.selectedEvent.Object);
var viewModel = new ViewModel(this.eventAggregator);
I want to test that the event is getting subscribed when the constructor is called.
How can I verify this?
[Disclaimer: I haven't been able to test this under Silverlight]
Here's a possible solution that basically executes a callback to set an external boolean when Subscribe is called on the event. See comments below though.
[Test]
public void Constructor_CallsSubscribeOnSelectionChangeEvent()
{
var subscribeCalled = false;
var selectedEvent = new Mock<SelectionChangedEvent>();
var eventAggregator = new Mock<IEventAggregator>();
selectedEvent
.Setup(x => x.Subscribe(
It.IsAny<Action<object>>(),
It.IsAny<ThreadOption>(),
It.IsAny<bool>(),
It.IsAny<Predicate<object>>()))
.Callback<Action<object>, ThreadOption, bool, Predicate<object>>
((action, option, f, pred) => { subscribeCalled = true; });
eventAggregator
.Setup(x => x.GetEvent<SelectionChangedEvent>()).Returns(selectedEvent.Object);
var viewModel = new ViewModel(eventAggregator.Object);
Assert.That(subscribeCalled, Is.EqualTo(true));
}
The above is pretty ugly, mostly due to the fact the the only mockable (virtual) overload of the Event's Subscribe method takes four arguments and that the argument types of Callback() can't be deduced automatically - which leads to lots of "extraneous" code.
An alternative would be to instead mock EventBase.InternalSubscribe, which only takes a single argument; but as that method is protected this approach has its own caveats.