I have created a very simple WebJob with a TimerTrigger, for example;
static async Task Main()
{
var builder = new HostBuilder();
builder.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices();
b.AddTimers();
});
builder.ConfigureLogging((context, b) =>
{
b.AddConsole();
});
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
}
public class Functions
{
public static void ProcessTimerMessage([TimerTrigger("*/30 * * * * *", RunOnStartup = true)] TimerInfo timerInfo, ILogger logger)
{
logger.LogInformation("I am here");
}
}
When I run this in Azure it never finishes. I was expecting this to complete on each run and then start again on the next trigger. Instead it never stops running:
The TimerTrigger for WebJobs runs as a singleton instance at startup and will run continuously, internally keeping track of the interval it runs at (based on the provide cron pattern). If any single invocation takes longer than the interval, then it will contiune processing the work it's doing and skip the overlapping invocation until the current work is complete.
You can read the details here:
https://github.com/Azure/azure-webjobs-sdk-extensions/wiki/TimerTrigger
Related
Does SWF natively supports manual pause and resume workflow functionality in java framework? If not is there any way to achieve to achieve that semantics?
Edit: I implemented following example, seems to be working with initial testing. Is there anything which can break with this. My workflow is going to be long running (~3-5 hours) with same activity being multiple times with different params.
import com.amazonaws.services.simpleworkflow.flow.annotations.Asynchronous;
import com.amazonaws.services.simpleworkflow.flow.core.Promise;
import com.amazonaws.services.simpleworkflow.flow.core.Settable;
public class GreeterWorkflowImpl implements GreeterWorkflow {
private GreeterActivitiesClient operations = new GreeterActivitiesClientImpl();
Settable<Void> paused = new Settable<>();
public void greet() {
Promise<String> fs = getGreeting(0, operations.getName());
print(fs);
}
#Asynchronous
private Promise<String> getGreeting(int count, Promise<String> name)
{
if (count > 10)
return name;
return getGreeting(count, name, paused);
}
#Asynchronous
private Promise<String> getGreeting(int count, Promise<String> name, Settable<Void> paused) {
Promise<String> returnString = operations.getGreeting(name.get());
return getGreeting(count + 1, returnString);
}
#Asynchronous
private void print(Promise<String> finalString)
{
System.out.println("Final String is " + finalString.get());
}
// #Signal method
#Override
public void pause() {
paused = new Settable<>();
}
// #Signal method
#Override
public void resume() {
paused.set(null);
}
}
In case you get multiple signals for resume, you will be setting the paused settable again (which is already ready) so you might end up with unhandled IllegalStateException
I am trying to trigger azure webjob using azure service bus queue which is FIFO. but following error is occured. is there way to fix it ?
It is not possible for an entity that requires sessions to create a
non-sessionful message receiver.
TrackingId:af15f75d-3d5f-423e-92c5-e22b222d25e6_G9_B1,
SystemTracker:testsbqueue:Queue:testsbqueuexxx
static void Main()
{
var serviceBusConfi = new ServiceBusConfiguration
{
ConnectionString= "Endpoint="",
};
var config = new JobHostConfiguration();
if (config.IsDevelopment)
{
config.UseDevelopmentSettings();
}
config.UseServiceBus(serviceBusConfi);
ServicePointManager.DefaultConnectionLimit = Int32.MaxValue;
var host = new JobHost(config);
// The following code ensures that the WebJob will be running continuously
host.RunAndBlock();
}
Function.cs
public class Functions
{
// This function will get triggered/executed when a new message is written
// on an Azure Queue called queue.
public static void ProcessQueueMessage([ServiceBusTrigger("testsbqueuexxx") ] string message, TextWriter log)
{
log.WriteLine($"[WebJobNotificationProcessor-]-{message}");
}
}
I'm struggling to read any variables from my appsettings.json file in an Azure Webjob SDK 3.0 project.
I am using:
Microsoft.Azure.WebJobs.Core version="3.0.6"
Microsoft.Azure.WebJobs.Extensions version="3.0.2"
Microsoft.Extensions.Configuration version="2.2.0"
I've tried the following:
System.Configuration.ConfigurationManager.AppSettings["ApiUrl"]
but this returns null and fails to read. I've checked the documentation and other questions on SO, but currently it's like finding a needle in a haystack.
Program.cs
static async Task Main(string[] args)
{
var builder = new HostBuilder()
.ConfigureWebJobs(b =>
{
b.AddAzureStorageCoreServices()
.AddAzureStorage()
.AddTimers();
b.UseHostId(Environment.UserName.ToLowerInvariant());
})
.ConfigureAppConfiguration((b, c)=>
{
var env = b.HostingEnvironment;
c.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
c.AddCommandLine(args);
c.AddEnvironmentVariables();
c.Build();
})
.ConfigureLogging((context, b) =>
{
b.SetMinimumLevel(LogLevel.Debug);
b.AddConsole();
string appInsightsKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(appInsightsKey))
{
b.AddApplicationInsights(o => o.InstrumentationKey = appInsightsKey);
}
})
.UseConsoleLifetime();
var host = builder.Build();
using (host)
{
await host.RunAsync();
}
}
Functions.cs
public static async Task ProcessAsync([TimerTrigger("0 */3 * * * *")] TimerInfo timerInfo, ILogger log)
{
//I want to read my appsettings.json here!
}
appsettings.json example:
{
"AzureWebJobsDashboard": "################",
"AzureWebJobsStorage": "################",
"ApiUrl": "#############",
"APPINSIGHTS_INSTRUMENTATIONKEY": "############"
}
IGNORE THIS - STILL not working
Just figured it out! Initialising this at the start of my Functions class fixed it for me:
private readonly IConfiguration _configuration;
public Functions(IConfiguration configuration)
{
_configuration = configuration;
}
public static async Task ProcessAsync([TimerTrigger("0 */3 * * * *")] TimerInfo timerInfo, ILogger log)
{
var conf = _configuration["ApiUrl"];
}
This works, but causes the WebJob to run as one function. Each run should be broken out as each individual run, under the WebJob function parent in the logs. Incredibly frustrating.
If anybody can help, please let me know.
Is it possible to run alexa skill locally with ngrok instead AWS? I built a skill in AWS Lambda but I would rather use my own server. What can I do to run Alexa locally?
I tried https://github.com/alexa-js/alexa-app-server but it makes any sense because I would need to rewrite my whole code :( The better solution is http://docs.bespoken.tools/en/latest/tutorials/tutorial_lambda_nodejs/ but it isn't the best. It just works only for a wellcome intent and freezes after that :(
Terminal Logs from bespken command:
BST: v0.9.35 Node: v7.8.0
Your URL for Alexa Skill configuration:
https://proxy.bespoken.tools?node-id=33efccba-2246-477f-bbb8-2e1e510cce9d
INFO 2017-04-25T20:27:20.628Z Connected - proxy.bespoken.tools:5000
INFO 2017-04-25T20:27:26.812Z RequestReceived: POST /?node-id=33efccba-2246-477f-bbb8-2e1e510cce9d ID: 1493152039146
INFO 2017-04-25T20:27:26.815Z Forwarding localhost:10000
Current hour: 24
Warning: Application ID is not set
INFO 2017-04-25T20:27:27.939Z ResponseReceived ID: 1493152039146
INFO 2017-04-25T20:28:10.755Z RequestReceived: POST /?node-id=33efccba-2246-477f-bbb8-2e1e510cce9d ID: 1493152078963
INFO 2017-04-25T20:28:10.756Z Forwarding localhost:10000
Warning: Application ID is not set
INFO 2017-04-25T20:28:11.157Z ResponseReceived ID: 1493152078963
INFO 2017-04-25T20:28:51.073Z RequestReceived: POST /?node-id=33efccba-2246-477f-bbb8-2e1e510cce9d ID: 1493152113739
INFO 2017-04-25T20:28:51.073Z Forwarding localhost:10000
Warning: Application ID is not set
INFO 2017-04-25T20:28:51.995Z ResponseReceived ID: 1493152113739
Yes, there are several solutions for running your node lambda locally. I've been using node-lambda, for example. Like most solutions it is oriented to users who want to test locally and then easily deploy to AWS Lambda.
If you want to run them yourself, I would note that MS and IBM have made their implementations of lambda open-source (here's MS's and IBM's). I haven't actually tried it myself, and I would note that with AWS, GCP, and Azure all providing Lambda services for node the market for these is healthy and the lock-in is minimal so I feel less need to be able to run it myself than for something like Dynamo.
But I also recommend that you continue to pursue BST. I'm using some of my own pieces for testing my skills because I got started before I heard of their stuff, but what I have tried of their's (BSTAlexa) is very useful and I see that they provide some of the other pieces you need for easy and effective testing of your skill.
Here's some sample code that you can use to easily run a Lambda locally, call this file AlexaLambda.js:
const log = require('console');
var AWS = require('aws-sdk');
AWS.config.region = "us-east-1";
AWS.config.update({
accessKeyId: "----",
secretAccessKey: "----",
});
/**
* Wraps the actual underlying Alexa lambda initialization in a
* Promise. Injects test mocks where appropriate.
*/
var initializerPromise = new Promise(function(fulfill, reject) {
// Mock out certain imports here if you want but not necessary
/*
var Module = require('module');
var originalRequire = Module.prototype.require;
Module.prototype.require = function() {
if ((arguments[0] == 'S3FeedService') ||
(arguments[0] == './lib/S3FeedService')) {
return MockS3Service;
} else if ((arguments[0] == 'WebsocketService') ||
(arguments[0] == './lib/WebsocketService')) {
return WSMockService;
} else if ((arguments[0] == 'SQSService') ||
(arguments[0] == './lib/SQSService')) {
return SQSMockService;
} else {
return originalRequire.apply(this, arguments);
}
};*/
// Import your actual lambda here.
var lambda = require('../src/index.js');
fulfill(lambda);
});
/**
* The Alexa Lambda context object which is called upon completion
* of lambda execution. Also wraps the callback which contains the
* test assertion code of the caller.
* #param callback - must be of the form function(error, result) {};
* #returns
*/
function Context(callback) {
this.clientContext = {"env": {}};
this.callback = callback;
}
Context.prototype.done = function(error, result) {
if (typeof error != "undefined" && error) {
this.callback(error, null);
} else {
this.callback(null, result);
}
}
Context.prototype.succeed = function(result) {
this.callback(null, result);
}
Context.prototype.fail = function(error) {
this.callback(error, null);
}
/**
* The AlexaLambda object that's exposed for test cases.
* #returns
*/
function AlexaLambda() {
}
/**
* Executes the lambda function, provided an inputEvent and a
* callback.
* #param inputEvent - the input event that includes the intent.
* #param callback - called upon completion of lambda execution.
*/
AlexaLambda.prototype.execute = function(inputEvent, callback) {
initializerPromise.then(function(lambda) {
var context = new Context(callback);
lambda.handler(inputEvent, context);
});
}
/**
* Export the lambda class, importers instantiate via new AlexaLambda();
*/
module.exports = AlexaLambda;
Then you can use this 'AlexaLambda' in your tests like so (in my case, I'm using Mocha):
var AlexaLambda = require('./AlexaLambda');
var Event = require('./Event'); // My 'fake' Event class
describe("Guest User Test", function() {
var alexaLambda = new AlexaLambda();
it("Alexa, open/launch 60db", function(done) {
var event = Event.createLaunchEvent();
alexaLambda.execute(event, function(error, result) {
validateYourResultHere();
done();
})
});
Then it's just a matter of running your test via whatever framework you're using.
You can test your alexa skill locally by following the following tutorial:
How to test Alexa locally
I have this simple Saga in Rebus:
public void MySaga : Saga<MySagaData>
IAmInitiatedBy<Event1>
IHandleMessages<Event2>
{
private IBus bus;
private ILog logger;
public MySaga(IBus bus, ILog logger)
{
if (bus == null) throw new ArgumentNullException("bus");
if (logger == null) throw new ArgumentNullException("logger");
this.bus = bus;
this.logger = logger;
}
protected override void CorrelateMessages(ICorrelationConfig<MySagaData> config)
{
config.Correlate<Event>(m => m.MyObjectId.Id, s => s.Id);
config.Correlate<Event>(m => m.MyObjectId.Id, s => s.Id);
}
public Task Handle(Event1 message)
{
return Task.Run(() =>
{
this.Data.Id = message.MyObjectId.Id;
this.Data.State = MyEnumSagaData.Step1;
var cmd = new ResponseCommandToEvent1(message.MyObjectId);
bus.Send(cmd);
});
}
public Task Handle(Event2 message)
{
return Task.Run(() =>
{
this.Data.State = MyEnumSagaData.Step2;
var cmd = new ResponseCommandToEvent2(message.MyObjectId);
bus.Send(cmd);
});
}
}
and thanks to the kind mookid8000 I can test the saga using FakeBus and a SagaFixture:
[TestInitialize]
public void TestInitialize()
{
var log = new Mock<ILog>();
bus = new FakeBus();
fixture = SagaFixture.For<MySaga>(() => new MySaga(bus, log.Object));
idTest = new MyObjectId(Guid.Parse("1B2E7286-97E5-4978-B5B0-D288D71AD670"));
}
[TestMethod]
public void TestIAmInitiatedBy()
{
evt = new Event1(idTest);
fixture.Deliver(evt);
var testableFixture = fixture.Data.OfType<MySagaData>().First();
Assert.AreEqual(MyEnumSagaData.Step1, testableFixture.State);
// ... more asserts
}
[TestMethod]
public void TestIHandleMessages()
{
evt = new Event2(idTest);
fixture.Deliver(evt);
var testableFixture = fixture.Data.OfType<MySagaData>().First();
Assert.AreEqual(MyEnumSagaData.Step2, testableFixture.State);
// ... more asserts
}
[TestCleanup]
public void TestCleanup()
{
fixture.Dispose();
bus.Dispose();
}
The first test method that check IAmInitiatedBy is correctly executed and no error is thrown, while the second test fail. It looks like a correlation issues since fixture.Data contains no elements and in fixture.LogEvents contains as last elements this error: Could not find existing saga data for message Event2/b91d161b-eb1b-419d-9576-2c13cd9d9c51.
What is this GUID? Is completly different from the one I defined in the unit test? Any ideas? Is legal what I'm tryng to test (since I'm using an in-memory bus)?
This line is bad: this.Data.Id = message.MyObjectId.Id. If you checked the value of Data.Id before you overwrote it, you would have noticed that the property already had a value.
You do not assign the saga ID - Rebus does that. And you should leave that property alone :)
Regarding your error - when Rebus wants to log information about a specific message, it logs a short name for the type and the message ID, i.e. the value of the automatically-assigned rbs2-msg-id header. In other words: It's not the value of the property m.MyObjectId.Id, you're seeing, it's the message ID.
Since the saga fixture is re-initialized for every test run, and you only deliver an Event2 to it (which is not allowed to initiate a new instance), the saga will not be hit.