Post-commit hooks
Introduction
Post-commit hooks is a way to guarantee that some code executes after committing a transaction. This functionality is, for example, useful if you want to send out confirmation emails after you've created orders in the database:
With this, you're guaranteed to send out a confirmation email after someone creates a new order. The hook is triggered no matter what app creates the order, as long as the app is in the same database, the app with the hooks runs, and the app has registered the hooks.
The fact that post-commit hooks execute after the transaction differentiates it from normal commit hooks where the hook executes as a part of the transaction.
API
There are three different post-commit hooks:
Each event uses the standard EventHandler<T>
delegate, where T
is ulong
. The ID provided to the hook is the unique object ID of the triggering object. The sender is an object
that you can cast to a Task
that represents the asynchronous operation for the transaction that triggered the hook. Read more about how you can use this with a custom scheduler in the post-commit hooks with a custom scheduler section.
The DatabaseClass
in the code sample above is the database class that should trigger the hook when an instance from the class is inserted, updated, or deleted.
Invocation
Starcounter invokes the correct hooks when an instance of the specified class is inserted, updated, or deleted. This statement assumes that the hook is registered and that the app with the hook is running.
Triggering hooks
For example, say that we have these four post-commit hooks:
To trigger each of these, we write something like this:
Notice how each of these operations is in a separate transaction. If we put them in the same transaction, the result is different:
This code only triggers the AfterCommitInsert
hook for the Order
class. The reason for this is that the hooks are triggered based on the final result of the transaction, not the individual operations themselves. The only state change after the transaction was that the app created an Order
since the transaction created and then immediately deleted the Person
.
Post-commit hooks trigger multiple times if there are several operations in one transaction:
Infinite loops
If you invoke a post-commit hook from inside a commit hook, it runs in an infinite loop because you're scheduling a task from inside that task:
Invocation timing
Also, post-commit hooks are not guaranteed to execute right after the transaction. It's possible that another transaction executes before executing the hook. Thus, you can't assume that the database state that existed after the transaction that triggered the hook is still the same:
If another thread deletes the person between the time the transaction completes and before the hook is invoked, the object will no longer exist.
Durability
Post-commit hooks execute after the transaction is committed to memory but before Starcounter flushes the commit to the transaction log. That the post-commit hooks execute before the transaction log flush means that the transaction is not guaranteed to be durable in the hook:
If the transaction with the new order has not been flushed to the transaction log where the power outage happens, then the customer gets the confirmation email but the order has not been stored in the database.
You can guarantee that the transaction is durable by executing the hook after the task with ContinueWith
:
Registering hooks
You can almost register hooks anywhere. A common practice is to have them in a specific class with a Register
method and invoke this method in the entry point:
When you register post-commit hooks, avoid two things: registering the hooks in a transaction or in another hook. The reason for this is that the transaction may have to restart if there's a conflict which would register the hook several times. If you register it in another hook the inner hook will be registered every time the outer hook is called. If you register a hook many times, it's called as many times as its registered:
Advanced: post-commit hooks with a custom scheduler
Starcounter uses its default scheduling mechanism to schedule tasks that the post-commit hooks execute in. Currently, that's an instance of DbTaskScheduler
. A complementary API can provide a custom scheduler when you register post-commit hooks, forcing Starcounter to utilize that instead of the built-in default. The example below shows a custom task scheduler that extends the Starcounter database task scheduler, executing a callback every time the hook in which it is installed in is queued:
Corresponding APIs are available for all other hooks:
Hook<DatabaseClass>.OnAfterCommitInsert
Hook<DatabaseClass>.OnAfterCommitUpdate
Hook<DatabaseClass>.OnAfterCommitDelete
Last updated