4

I have gone through the documentation and read the examples and use cases. However, I am still struggling to understand how Finalizer add value and what can we achieve with it that was not achievable before, for example, by using a simply try-catch-finally statement.

Example: in the same way that we attach a finalizer to the job execution, and we check the result whenever it is finished, we could execute the ParentJobResult.SUCCESS logic at the end of the execution logic or the ParentJobResult.UNHANDLED_EXCEPTION in the catch block if anything is thrown.

Is the only purpose to be able to catch any exception that is not catcheable, such as assertions or TOO_MANY_SOQL_QUERIES?

1 Answer 1

6

Yes, the primary purpose of Finalizer is to permit recovery from LimitException. However, this is huge. If a Queueable crashes from a LimitException, it can't retry itself, the context data may not have been logged to a Debug Log, and all anyone can see is a simple "Status: Failed" message.

With this new feature, you can do things like: send an email notification of what happened, save detailed logs of what the execution state was when the process crashed, attempt to restart the Queueable in case it was a transient error, perform callouts to other systems after performing DML in the Queueable (remember, callout after DML is illegal).

It also allows you to write cleaner code. If you haven't read my answer about the Pokemon Catch, take a moment. I suggest that developers avoid catching Exception as a general rule. Most developers abuse Exception, and therefore it should be avoided. Catching Exception directly suggests that the developer has no idea what type of errors are possible in a given area.

As a competent developer, one should be able to anticipate which exceptions are possible and avoid them with simple if statements. Catching only exceptions that can be caught displays confidence and competency, and specific exceptions are more explanative/self-documenting than handling the generic Exception. The try-catch should ideally be as close to the problematic code as possible.

Forcing yourself to think about what type of exceptions can occur, and catching only those exceptions, will help you become a better developer. In fact, most of my code doesn't even have a try-catch handler at all; if it does, it is never around an entire method, but usually just a couple of lines of code, and usually catches a specific type.

Given two choices, either handling all exceptions generically, or handling specific exceptions selectively and reporting unexpected exceptions in a separate transaction, it should be clear which I'd choose to do. Also, as another benefit, you can conceptually write one Finalizer that could work for many different Queueables, providing a standard interface for exception handling and reporting, as well as reducing duplicated code.

So, rather than thinking of this as a simple tool that helps with LimitException, think of this as a way to write more robust code that runs more efficiently when things are running smooth, and has more capability when things go unexpectedly wrong.

Finally, keep in mind that finalizers also run when the job succeeds. This is also an excellent opportunity to log data, make any remaining callouts, or whatever additional post-transaction logic you'd like to do that cannot be done in finally block.

9
  • The exception piece is why I think Java wins over Apex: you can explicitly declare the thrown exceptions for a given method and the caller is obliged to understand this. Compile time checking is performed against this info.
    – Phil W
    Commented Apr 6, 2022 at 1:11
  • 2
    We have used finalizer to adjust the actual "size" of the data set the queueable processes when it hits limits (adapting from a configured size) to ensure the data does eventually get processed without violating limits. We also provide this info as feedback to the admin so they can adjust the configured size if this is happening a lot, enabling more optimal use of async limits.
    – Phil W
    Commented Apr 6, 2022 at 1:16
  • @PhilW I like the idea of forced exception handling as Java, but I would hate this in Apex, since my general stance is that devs should be able to avoid most Exceptions in Apex, so forced handling would be redundant. I'd rather have the compiler implement an Optional type, any variable not declared as Optional must be initialized before use or throw a compile error. Parameters not marked Optional can't be passed an Optional variable.
    – sfdcfox
    Commented Apr 6, 2022 at 2:20
  • 1
    @PhilW And that auto-adjust feature is exactly where I was going with this. Detecting limit problems and adjusting on the fly is really handy, esp. for an ISV, because you never know how much limits the org is already using ahead of time.
    – sfdcfox
    Commented Apr 6, 2022 at 2:22
  • @PhilW Actually, thinking about it, I meant Nillable<T>, not Optional<T>. I've seen this in some languages, and it would make sense in Apex. Using this would allow static compile-time guarantees that nulls are not present, and this is the by far the most commonly thrown exception. If we could somehow avoid nulls entirely, I'd be okay with forcing handling exceptions at the compiler level. This would clean up so much code.
    – sfdcfox
    Commented Apr 6, 2022 at 2:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .