AVOID THESE! - Follow The Quartz NET Listener Best Practices
August 7, 2024
• 1,309 views
Quartz NET has listeners that unlock a TON of observability.
BUT BEWARE!
It's super easy to completely bork your precious scheduled jobs. And nobody wants that after you put all the effort into leveraging a powerful system like Quartz.
So what can you do?
Check out these best practices for working with Quartz NET listeners to avoid these common problems.
View Transcript
quartz.net is a job scheduling framework that we have access to when we're building C applications but there are some best practices that we want to follow when using more advanced features like listeners hi my name is Nick centino and I'm a principal software engineering manager at Microsoft in previous videos I talked about using listeners of different kinds and you can check that out here if you haven't seen it already in that video we just look through some basic functionality doing some console WR lines and checking out the different methods that we had access to however in this video I want to talk about some best practice is that the courts.net team has published in their documentation and that way you can do a better job building listeners that sounds interesting remember to subscribe to the channel and check out that pined comment for my courses
on dome Trin but with that said let's jump over to visual studio and check out some best practices for listeners all right the first thing that I want to focus on when we're talking about listeners is the concept that they mention in the documentation which is to make them lightweight they should feel fast they shouldn't be doing something that isn't very complicated and the reason for that is because it's going to influence the job that you're going to be executing right so if I go to modify some of the listeners that we have here and just to kind of Step through them very quickly if I scroll up a little bit you can see that I have this custom job listener so this has methods uh that will get executed specifically around the job execution and down here is the trigger listener right so on
screen what's being highlighted uh these methods are specifically for the trigger that's triggering the job we have these different methods that we can hook into do different things and if we were to simulate what something that would take longer we might be able to see something kind of interesting with the behavior of the jobs the reason that this is important is because when you're scheduling jobs and you want them to fire at certain times or at certain intervals that can get all sorts of messed up if your uh different listeners are taking way longer than you might expect right so what I want to do is try simulating what happens if we wait a little bit too long in these different cases right and when I say too long I don't mean that we're going to have some condition that's going to mess it all
up for us but I want to simulate waiting in different areas and we can see the side effect of what that looks like for our job to explain what I mean by that if I scroll up a little bit and we look at the trigger that we have here we can see that we have this job trigger this is going to say starting at you can see on line 49 in 5 seconds from now okay so the goal will be when this application starts up you can see a little bit lower that I'm scheduling the job right here on line 75 so the application starts up and in 5 seconds from now once it's started we should be able to see that our job runs it's just going to print to the console but what happens if we wait too long inside of these different
listeners what's the effect of that right so scrolling back down in the previous video that I linked that earlier you should be able to see if I find it on my screen we have this job to be executed this listener method will fire before the job's U body the primary body for the job gets run so if we were to wait too long here that's going to defer the execution but the other thing is this trigger fired part right this also runs in fact before that other method we just saw so let's go ahead and do this I'm going to go say um this is a sync right so let me go make this method a sync specifically and I will do an await task. delay and then we can wait some period of time right I want to simulate this so that I can
talk it's running so we'll do from seconds and maybe we'll do another 5 Seconds here okay and then I'm going to write to the console co-pilot anything creative your fired after delay excellent that's good enough for me and then we don't need that return at the end we're going to simulate this trigger fired delay and then I'm going to also just copy this and put it up to job to be executed okay so same idea make this a sync and then I'm going to put this here before we move on this is just a reminder that I do have courses available on dome train if you want level up in your C programming if you head over to dome train you can see that I have a course bundle that has my getting started and deep dive courses on C between the two of these
that's 11 hours of programming in the C language taking you from absolutely no programming experience to being able to build basic applications you'll learn everything about variables Loops a bit of async programming and object-oriented programming as well make sure to check it out this will say job maybe we'll see if co-pilot comes up with something more uh creative cuz I'm terrible at naming things and I love co-pilot for there we go that's good better than I could probably do on the spot so these two sleeps or these delays right we're going to simulate these happening before the job gets to be run so I'm going to press play this should wait basically a total of 10 seconds before we should have seen the job actually right to the console so let's go try it and see okay so the application has started up trigger fired
now we're waiting that 5 Seconds there it goes okay so the job to be executed another 5 Seconds remember the job should have run already by now and then we end up seeing this is the dependency uh and then the merge job data this is from an earlier video so the merge job data is the message from the trigger so this is where the job was supposed to essentially execute right away but we had a total of 10 seconds of delay before this was fired so if you truly wanted to make sure that things were firing at the appropriate time you really want to reduce the amount of time that you're spending in these listeners so that's one of the first best practices that comes from the Court's documentation around leveraging listeners you can have the same effect right if you end up putting this
type of delay inside of the other listener methods you are going to be deferring execution of other things of course the code that you're running in this uh in these listeners it's not instantaneous that's just reality right any line of code you write is not going to be instantaneous but you don't want to spend too much time because you will have an effect on the things that you're running that is pro tip number one from the courts documentation but the other thing that they talk about is not just delays but what happens if you throw exceptions if we have these listeners and they do something bad what's going to happen to your job so I'm going to get rid of these delays and maybe a good thing to do is let's get this back to returning a completed task and I'm going to copy that
line I'm going to go ahead and put that back down here as well putting these back to normal okay this is how they started off now there's no delays we're just going to throw exceptions so on the trigger fired let's go ahead and throw an exception here so we will do throw new invalid operation exception oh no so we will throw that and we don't need this here what's going to happen when we fire the trigger and instead The Listener here is going to throw an exception right so if we go ahead and run this we'll see the effect so after 5 Seconds we should get an exception there it is there's the debugger hitting it I'm going to press F5 and what happens to our job right the job didn't run so you can bake in error handling for your courts jobs but the
point here is that we aren't able to do what we expected because our listener is throwing an exception so you have to be very careful if you are are going to be having more complex code running inside of these different listeners whether it's the job listener or in this case this is going to be the trigger listener this can have a very negative side effect on the jobs you're trying to schedule so what does it say in here it says unable to notify trigger listeners while firing trigger so in fact if there was this chain of execution for triggers and listeners in this particular case it's going to break everything Downstream there's an exception thrown those other listeners can't run right so trigger and job will not be fired important to note so what do they recommend doing well they essentially say that you should
have a try catch around basically everything so this entire block should be in a TR catch that way you can do some appropriate error handling make sure that you're not letting exceptions bubble outside of this personally if I were building an API like this if I knew that I had things where people could accidentally be throwing exceptions if I had the design decision to say okay up to every implementor to go put a try catch in place instead I would probably design it this is just my personal preference to be able to say okay you can do whatever you want here if you throw an exception we're going to go do something about it and allow the other things to execute or make it so that there is a result pattern so I can understand in their case they are catching an exception the whole
application didn't get torn down right they are catching an exception at some level and then logging out information to say that things will not continue to execute so they are doing that to some degree but they do still expect you to have your own try catch in here so that the flow of execution can continue one thing that you can do is like they said put a TR catcher on this whole block and this is the same for every listener method by the way not just for trigger fired so I'm not going to walk through them all individually to explain it just using this one as an example but instead of putting a TR catch around here for me what I've liked to do is I built a little helper method and I've called it safely and basically if we just look at it down
here I just have a couple of variations of this and in my own projects I I actually have something that's like this and I'll explain it very briefly and it's built in a result pattern as well so instead of putting a TR catch block around things and like I've mentioned my implementation of this in my personal projects is a little bit more advanced I I basically build a logger into it I have my own result type I'm using the very popular one of type for this example here but it just has a TR catch in it in this very simple example you know it's not doing anything fancy but what it is doing for us is that it's making it look like that we have a result pattern so I'm going to show you very quickly how I like to use this this variation below
is just not returning a uh type right so this one is a funk and this one is a funk with a task instead of a task T and the idea is that you either return an exception when there's an error or you return the result of the thing that you were executing so because there is no return type what we can do instead is do something like this and co-pilot was very helpful here we need an await oops and there's too much stuff going on co-pilot you were close but you did too much so this idea is basically going to allow us to say anything we run it's just like a TR catch I just prefer the syntax and The Styling this way so I can say um right and then if I wanted to uh let's say error is not right you can put
your own loging Telemetry stuff here this is my Approach like I said it's very much like having a TR catch but I build my own helper methods like this to allow uh different syntax I just prefer it I just wanted to kind of throw it into this to make it a little bit more interesting and I also mentioned that in mine this kind of logging thing that's done here I end up building logging in Telemetry into this helper method itself so just a different thing to think about but the whole point of this one was to call out this best practice they recommend which is to not be throwing exceptions so what we covered in this video was two best practices to use for listeners the first one was don't take too long in these listener methods you can see the impact of that pushing
out the job being run if you wanted it scheduled for 5 seconds or some time it will defer the execution and the second part was that you don't want these listener methods to throw exceptions because they will break behavior of things that are Downstream so they do recommend you put a try catch in place or like I showed you very briefly here that I have my own helper methods that basically do a try catch under the hood I hope that you found that helpful quartz is a super powerful Library there's lots of things to hook into lots of flexibility and control so I highly recommend you give it a try and let me know what you think in the comments if you're going to be using courts in the applications You're Building thanks so much for watching I'll see you next time
Frequently Asked Questions
What are the best practices for using listeners in quartz.net?
The best practices for using listeners in quartz.net include making them lightweight to ensure they execute quickly and do not introduce delays in job scheduling. Additionally, it's crucial to avoid throwing exceptions in listener methods, as this can disrupt the execution of downstream jobs. I recommend wrapping your listener code in try-catch blocks to handle any potential exceptions gracefully.
How can delays in listener methods affect job execution?
Delays in listener methods can significantly impact job execution timing. If a listener takes too long to execute, it can defer the job that is supposed to run, leading to unexpected behavior. For example, if a job is scheduled to run in 5 seconds but the listener takes 10 seconds to complete, the job won't execute on time. It's essential to keep listener methods efficient to maintain the intended job schedule.
What should I do if my listener methods might throw exceptions?
If your listener methods might throw exceptions, it's important to implement error handling. I recommend wrapping your listener code in try-catch blocks to manage exceptions effectively. This way, you can prevent exceptions from disrupting the execution of other jobs and ensure that your application continues to run smoothly.
These FAQs were generated by AI from the video transcript.