Not Getting any result in Powershell, Suspecting async issue - c#

Created a PowerShell Cmdlet command in C# and am trying to get the output after passing parameters. The function runs perfectly if I run it as a console application and I get the desired result,But when I am trying to invoke the method from PowerShell, I am not getting any records/results. I suspect there is a async issue which is causing the code to complete, but I am unable to figure it out.
During Debugging I get the status as RanToCompletion in ProcessRecord() Method. Will appreciate some advice on what's wrong in the code below.
protected override void ProcessRecord()
{
var changedFeed = ProcessChangedRecords();
changedFeed.Wait();
}
private async Task ProcessChangedRecords()
{
DaysDuration = DaysDuration * -1;
DateTime particularPointInTime = DateTime.Now.AddDays(DaysDuration).ToUniversalTime();
CosmosClient Client = new CosmosClient("AccountEndpoint = https://test.documents.azure.com:443/;AccountKey=ouJH1chKC5npTfK5y9NUEOA==;");
if (DaysDuration == 0)
{
particularPointInTime = DateTime.Now.ToUniversalTime();
}
var database = Client.GetDatabase("databasename");
var container = database.GetContainer("containername");
var leaseContainer = database.GetContainer("leases");
var cfp = container.GetChangeFeedProcessorBuilder<Document>(ChangeFeedProcessorName, ProcessChanges)
.WithLeaseContainer(leaseContainer)
.WithInstanceName(changeFeedInstanceName)
.WithStartTime(particularPointInTime)
.Build();
await cfp.StartAsync();
await Task.Delay(5000);
await cfp.StopAsync();
}
public async Task ProcessChanges(IReadOnlyCollection<Document> docs, CancellationToken cancellationToken)
{
foreach (var doc in docs)
{
WriteObject(doc, true);
await Task.Delay(1000);
}
}
Just to be more precise, I am trying to use changefeed of CosmosDb and get the modified documents from a particular day. On running the above command, I can see the Lease instance getting created in lease container but there is no output in powershell window

Related

Using return value from awaited method

I'm trying to understand async methods and await, I've got this simple example:
using (var client = new AmazonSQSClient())
{
var sqsRequest = new SendMessageRequest()
{
MessageBody = JsonConvert.SerializeObject(callProcessingRequest),
QueueUrl = "https://sqs.eu-west-2.amazonaws.com/*****6014/W*****g"
};
LoggingHelper.Log(LoggingLevel.INFO, "Calling SQS", context);
var sqsResponse = await client.SendMessageAsync(sqsRequest);
LoggingHelper.Log(LoggingLevel.DEBUG,
JsonConvert.SerializeObject(sqsResponse), context)
}
When I run this, the logging of sqsResponse never happens, however if I change
var sqsResponse = await client.SendMessageAsync(sqsRequest);
to
var sqsResponse = client.SendMessageAsync(sqsRequest).Result;
Then it works as expected.
With it being an async method I guess I should be using await with it, but not sure why it's not working.
EDIT: Whole method as requested. Adding .ConfigureAwait(false) didn't help.
public static async Task ProcessOutstandingDialplanItems()
{
var context = new Context() { CurrentHandler = "PBXCallbacksLambdaFunction" };
var callProcessingRequest = new CallProcessingRequest()
{
context = context,
MethodToInvoke = "ProcessOutstandingDialPlanItems"
};
try
{
using (var client = new AmazonSQSClient())
{
var sqsRequest = new SendMessageRequest()
{
MessageBody = JsonConvert.SerializeObject(callProcessingRequest),
QueueUrl = "https://sqs.eu-west-2.amazonaws.com/XXX6014/WXXXg"
};
LambdaLogger.Log("Calling SQS");
var sqsResponse = await client.SendMessageAsync(sqsRequest)
.ConfigureAwait(false);
//var sqsResponse = client.SendMessageAsync(sqsRequest).Result;
LambdaLogger.Log(JsonConvert.SerializeObject(sqsResponse));
}
}
catch (Exception x)
{
LambdaLogger.Log(x.Message);
}
}
From AWS Logging .NET:
AWS Lambda
These packages batch logging messages in a queue and send messages to CloudWatch Logs using a background thread. The use of the background thread means that the messages are not guaranteed to be delivered when used in AWS Lambda. The reason is because the background thread will be frozen once a Lambda event is processed and may not ever be unfrozen if more Lambda events are not received for some time.
When using Lambda it is recommended to use either the ILambdaContext.Logger.LogLine or the Amazon.Lambda.Logging.AspNetCore package.
My guess would be that you are already using Result or Wait (or something which is calling your code) in one of the method before in application which has SynchronizationContext (classic ASP.NET, UI apps like WPF, WinForms, etc.). That is a "good" way to end up in deadlock. One thing you can try - adding ConfigureAwait(false) to the call:
var sqsResponse = await client.SendMessageAsync(sqsRequest).ConfigureAwait(false);
But much better course of action - Don't Block on Async Code.

EF 6 - Consume stored procedure and call ToListAsync()

I am trying to call a stored procedure via EF 6. Using ToList() works as expected and a list of entities is returned. However, ToListAsync() does NOT seem to return data. In sql Profiler, I can see the stored procedure being executed against the database. However, the break point after the line for ToListAsync() does not get hit. I never see data get returned.
code below
public async Task<List<MyEntityObject>> GetStoredProcedureData() {
List<MyEntityObject> MyEntityObjects;
using (var dbContext = new DbContext())
{
var MyEntityObjectsQry = dbContext.Database.SqlQuery<MyEntityObject>("dbo.GetStoredProcedureData");
MyEntityObjects =await MyEntityObjectsQry.ToListAsync();
}
return MyEntityObjects;
}
I figured this out. I was not consuming the method correctly. I was using a windows service. Plus, I could not add async to the Main method.
static void Main(string[] args)
{
// async cannot be added to Main
// await CallStoredProcedure(); will not work here
Task<bool> task = Task.Run<bool>(async () => await CallStoredProcedure());
var test = task.Result;
}
static async Task<bool> CallStoredProcedure()
{
var dataService = new MyDataService();
// just call my method with await
var details = await dataService.GetStoredProcedureData();
Console.WriteLine("found {0} items", details.Count);
return true;
}
The get method will be like
var myEntityObjectsQry = await dbContext.Database.ToListAsync();
More references here:
https://www.entityframeworktutorial.net/entityframework6/async-query-and-save.aspx

How to correctly close the EventHubReceiver when working with Azure IoT in C#?

I am writing an application that should be able to read and display IoT data. The basic functionality works for me with this code (I removed some checks etc so that the code would the shorter):
public void Run()
{
_eventHubClient = EventHubClient.CreateFromConnectionString(ConnectionString, "messages/events");
var partitions = _eventHubClient.GetRuntimeInformation().PartitionIds;
cts = new CancellationTokenSource();
var tasks = partitions.Select(partition => ReceiveMessagesFromDeviceAsync(partition, cts.Token));
Task.WaitAll(tasks.ToArray());
}
public void Cancel()
{
cts.Cancel();
}
private async Task ReceiveMessagesFromDeviceAsync(string partition, CancellationToken cancellationToken)
{
var eventHubReceiver = _eventHubClient.GetDefaultConsumerGroup().CreateReceiver(partition, DateTime.UtcNow);
while (true)
{
if (cancellationToken.IsCancellationRequested)
{
break;
}
var eventData = await eventHubReceiver.ReceiveAsync(new TimeSpan(0,0,1));
var data = Encoding.UTF8.GetString(eventData.GetBytes());
Console.WriteLine("Message received at {2}. Partition: {0} Data: '{1}'", partition, data, eventData.EnqueuedTimeUtc);
}
}
My problem is that I need to be able to stop and restart the connection again. Everything works okay until the moment when I start it for the 6th time, then I get the "QuotaExceededException": "Exceeded the maximum number of allowed receivers per partition in a consumer group which is 5". I have googled the exception and I understand the problem, what I don't know is how to correctly close the previous receivers after I close a connection, so that I could open it again later. I have tried calling
eventHubReceiver.Close()
in the Cancel() method but it didn't seem to help.
I would be very grateful for any hints on how to solve this, thanks.

Calling AWS RDS CreateDBSnapshotAsync Asynchronously "Set It And Forget It"

In an AWS Lambda function, I would like to be able to call a component to create a RDS DB Snapshot. There is an async method on the client named CreateDBSnapshotAsync. But, because this is AWS Lambda, I only have 5 minutes to complete the task. So, if I await it, the AWS Lambda function will timeout. And, apparently when it times out, the call is cancelled and then the snapshot is not completed.
Is there some way I can make the call in a COMPLETELY asynchronously way so that once I invoke it, it will complete no matter if my Lambda function times out or not?
In other words, I don't care about the result, I just want to invoke the process and move on, a "set it and forget it" mentality.
My call (without the await, obviously) is as below
using (var rdsClient = new AmazonRDSClient())
{
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(new CreateDBSnapshotRequest($"MySnapShot", instanceId));
}
As requested, here's the full method:
public async Task<CloudFormationResponse> MigrateDatabase(CloudFormationRequest request, ILambdaContext context)
{
LambdaLogger.Log($"{nameof(MigrateDatabase)} invoked: " + JsonConvert.SerializeObject(request));
if (request.RequestType != "Delete")
{
try
{
var migrations = this.Context.Database.GetPendingMigrations().OrderBy(b=>b).ToList();
for (int i = 0; i < migrations.Count(); i++)
{
string thisMigration = migrations [i];
this.ApplyMigrationInternal(thisMigration);
}
this.TakeSnapshotAsync(context,migrations.Last());
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
}
catch (Exception e)
{
LambdaLogger.Log(e.ToString());
if (e.InnerException != null) LambdaLogger.Log(e.InnerException.ToString());
return await CloudFormationResponse.CompleteCloudFormationResponse(e, request, context);
}
}
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
}
internal void TakeSnapshotAsync(ILambdaContext context, string migration)
{
var instanceId = this.GetEnvironmentVariable(nameof(DBInstance));
using (var rdsClient = new AmazonRDSClient())
{
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(new CreateDBSnapshotRequest($"{instanceId}{migration.Replace('_','-')}", instanceId));
while (context.RemainingTime > TimeSpan.FromSeconds(15))
{
Thread.Sleep(15000);
}
}
}
First refactor that sub function to use proper async syntax along with the use of Task.WhenAny.
internal async Task TakeSnapshotAsync(ILambdaContext context, string migration) {
var instanceId = this.GetEnvironmentVariable(nameof(DBInstance));
//don't wrap in using block or it will be disposed before you are done with it.
var rdsClient = new AmazonRDSClient();
var request = new CreateDBSnapshotRequest($"{instanceId}{migration.Replace('_','-')}", instanceId);
//don't await this long running task
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(request);
Task delay = Task.Run(async () => {
while (context.RemainingTime > TimeSpan.FromSeconds(15)) {
await Task.Delay(15000); //Don't mix Thread.Sleep. use Task.Delay and await it.
}
}
// The call returns as soon as the first operation completes,
// even if the others are still running.
await Task.WhenAny(response, delay);
}
So if the RemainingTime runs out, it will break out of the call even if the snap shot task is still running so that the request does not time out.
Now you should be able to await the snapshot while there is still time available in the context
public async Task<CloudFormationResponse> MigrateDatabase(CloudFormationRequest request, ILambdaContext context) {
LambdaLogger.Log($"{nameof(MigrateDatabase)} invoked: " + JsonConvert.SerializeObject(request));
if (request.RequestType != "Delete") {
try {
var migrations = this.Context.Database.GetPendingMigrations().OrderBy(b=>b).ToList();
for (int i = 0; i < migrations.Count(); i++) {
string thisMigration = migrations [i];
this.ApplyMigrationInternal(thisMigration);
}
await this.TakeSnapshotAsync(context, migrations.Last());
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
} catch (Exception e) {
LambdaLogger.Log(e.ToString());
if (e.InnerException != null) LambdaLogger.Log(e.InnerException.ToString());
return await CloudFormationResponse.CompleteCloudFormationResponse(e, request, context);
}
}
return await CloudFormationResponse.CompleteCloudFormationResponse(null, request, context);
}
This should also allow for any exceptions thrown by the RDS client to be caught by the currently executing thread. Which should help with troubleshooting any exception messages.
Some interesting information from documentation.
Using Async in C# Functions with AWS Lambda
If you know your Lambda function will require a long-running process, such as uploading large files to Amazon S3 or reading a large stream of records from DynamoDB, you can take advantage of the async/await pattern. When you use this signature, Lambda executes the function synchronously and waits for the function to return a response or for execution to time out.
From docs about timeouts
Function Settings
...
Timeout – The amount of time that Lambda allows a function to run before stopping it. The default is 3 seconds. The maximum allowed value is 900 seconds.
If getting a HTTP timeout then shorten the delay but leave the long running task. You still use the Task.WhenAny to give the long running task an opportunity to finish first even if that is not the expectation.
internal async Task TakeSnapshotAsync(ILambdaContext context, string migration) {
var instanceId = this.GetEnvironmentVariable(nameof(DBInstance));
//don't wrap in using block or it will be disposed before you are done with it.
var rdsClient = new AmazonRDSClient();
var request = new CreateDBSnapshotRequest($"{instanceId}{migration.Replace('_','-')}", instanceId);
//don't await this long running task
Task<CreateDBSnapshotResponse> response = rdsClient.CreateDBSnapshotAsync(request);
Task delay = Task.Delay(TimeSpan.FromSeconds(2.5));
// The call returns as soon as the first operation completes,
// even if the others are still running.
await Task.WhenAny(response, delay);
}

BackgroundTask doesn't fire

My background task registers but never fires. I have tried to delete the whole project to erase all tasks, changed the name on the TaskBuilder class, and used different conditions. But nothing seems to work. I sometimes get an error that says it can't show me the error.
Here do I build it:
public async void RegisterBackgroundTask()
{
var taskRegistered = false;
var TaskName = "TimeTriggeredTask";
foreach (var task in BackgroundTaskRegistration.AllTasks)
{
if (task.Value.Name == TaskName)
{
taskRegistered = true;
break;
}
}
var tommorowMidnight = DateTime.Today.AddDays(1);
var timeTilMidnight = tommorowMidnight - DateTime.Now;
var minutesTilMidnight = (uint)timeTilMidnight.TotalMinutes;
if (!taskRegistered)
{
var task = RegisterBackgroundTask("TaskBuilderClass",
"TimeTriggeredTask",
new TimeTrigger(minutesTilMidnight, false),
new SystemCondition(SystemConditionType.InternetAvailable));
await task;
CheckPremieres();
}
}
Builder method:
public static async Task<BackgroundTaskRegistration> RegisterBackgroundTask(String taskEntryPoint, String name, IBackgroundTrigger trigger, IBackgroundCondition condition)
{
await BackgroundExecutionManager.RequestAccessAsync();
var builder = new BackgroundTaskBuilder();
builder.Name = name;
builder.TaskEntryPoint = taskEntryPoint;
builder.SetTrigger(trigger);
builder.AddCondition(condition);
BackgroundTaskRegistration task = builder.Register();
//
// Remove previous completion status from local settings.
//
var settings = ApplicationData.Current.LocalSettings;
settings.Values.Remove(name);
return task;
}
This is the task builder class which I also added to the manifest:
public sealed class TaskBuilderClass : IBackgroundTask
{
//
// The Run method is the entry point of a background task.
//
public void Run(IBackgroundTaskInstance taskInstance)
{
//
// Query BackgroundWorkCost
// Guidance: If BackgroundWorkCost is high, then perform only the minimum amount
// of work in the background task and return immediately.
//
var cost = BackgroundWorkCost.CurrentBackgroundWorkCost;
var settings = ApplicationData.Current.LocalSettings;
settings.Values["BackgroundWorkCost"] = cost.ToString();
App.nHandler.CheckPremieres();
}
}
I'm pretty sure the task needs to be in its own Windows Runtime Component; I've always done it like this. The Microsoft sample on GitHub also has the tasks in a separate project.
Try doing that. And don't forget to reference the newly created prject from your application. That's the thing that I most always forget.
Once you do that, I also suggest you first trigger the task from Visual Studio Debug Location toolbar, just to make sure everything is configured correctly. It should appear in the dropdown and should work correctly from here, otherwise it won't work when scheduled either.

Categories