Table of Contents

Notifications

Notifications are events which are raised when something happens in DynamicWeb platform. You can then subscribe to these events using a notification subscriber, and thereby affect how the system behaves when a specific event happens, or e.g. notify an external system about the change.

In general, when you want to implement a Notification Subscriber, you need to create a new class, which inherits from NotificationSubscriber. This NotificationSubscriber class has this overridable method OnNotify, which is called every time an event is fired.

To limit the notification subscriber to a specific event, you need to decorate the class with the Subscribe attribute, where the specific event name is passed to the attribute.

using Dynamicweb.Extensibility.Notifications;

namespace Examples
{
    [Subscribe(Dynamicweb.Notifications.Standard.Page.OnBeforeSave)]
    public class BasicNotificationSample : NotificationSubscriber
    {
        public override void OnNotify(string notification, NotificationArgs args)
        {
            base.OnNotify(notification, args);
        }
    }
}

Notifications are raised by all parts of the DynamicWeb system - here are links to the most relevant namespaces:

Suppressing notifications

When creating a notification subscriber, inside that notification subscriber you can run code that will trigger new notifications.

That is not always desired and notifications should then be suppressed. This can be done by using a NotificationContext around the executing code.

using (var context = new NotificationContext(NotificationContext.NotificationState.SuppressNotifications))
{
    //run code in a context that suppresses notifications
}

Consider a notification that fires when an object is being changed - here a page. In the notification subscriber, the code will change the object being send in, here the page being changed.

This subscriber then saves the page again, and that will re-trigger the page saved notification causing a problem.

[Subscribe(Dynamicweb.Notifications.Standard.Page.Saved)]
public class PageSavedObserver : NotificationSubscriber
{
    public override void OnNotify(string notification, NotificationArgs args)
    {
        if (args == null)
            return;

        var pageArgs = (Dynamicweb.Notifications.Standard.Page.PageNotificationArgs)args;
        var page = pageArgs.Target;
        page.MetaTitle = "Page title change";
        Services.Pages.SavePage(page);
    }
}

To avoid this problem, notifications can be suppressed inside the notification subscriber itself:

[Subscribe(Dynamicweb.Notifications.Standard.Page.Saved)]
public class PageSavedObserver : NotificationSubscriber
{
    public override void OnNotify(string notification, NotificationArgs args)
    {
        if (args == null)
            return;

        using (var context = new NotificationContext(NotificationContext.NotificationState.SuppressNotifications))
        {
            var pageArgs = (Dynamicweb.Notifications.Standard.Page.PageNotificationArgs)args;
            var page = pageArgs.Target;
            page.MetaTitle = "Page title change";
            Services.Pages.SavePage(page);
        }
    }
}

Performance Considerations for Notification Subscribers

Ensure that notification subscribers contribute effectively to your DynamicWeb solution without negatively impacting system performance. Proper optimization of these subscribers is particularly important for high-traffic areas and critical business processes where user experience and transaction reliability are paramount.

When implementing notification subscribers, it is important to consider their impact on the overall performance of the DynamicWeb solution, e.g.:

  • Notifications can be fired multiple times during various stages of processing, including within loops, page rendering, and critical operations such as adding products to the cart or completing orders.
  • Poorly optimized code in subscribers can lead to degraded performance, slow page loads, or, in extreme cases, hanging user interfaces.

Here are several key performance considerations and best practices for developers to keep in mind when working with notification subscribers:

Minimize Processing Time

  • Avoid long-running operations within the notification subscriber's execution. Heavy computations or database queries may significantly slow down the system, especially if the subscriber is triggered frequently.
  • If you need to perform time-consuming tasks such as data synchronization with an external system, consider queuing the task and handling it asynchronously instead of blocking the main process.

Conditional Execution

  • Implement checks to ensure your notification handler runs only when absolutely necessary. For instance, use conditional logic to filter out irrelevant cases or events that do not need your handler's attention.

Debounce High-Frequency Events

  • Some notifications may be fired multiple times in rapid succession due to their context. Implement debouncing or throttling mechanisms where applicable to reduce redundant invocations.

Resource Management

  • Properly manage database connections, file handles, or any other resources opened within your subscriber. Ensure they are released or disposed of promptly to prevent memory leaks or resource exhaustion.
  • Use caching strategies to avoid fetching the same data repeatedly during notifications that execute closely together.

Asynchronous Processing and Background Tasks

  • For non-critical actions that do not need immediate completion, consider offloading work to background jobs or asynchronous operations using available task schedulers or message queues.
  • Be aware that handling asynchronous tasks within a web request flow can lead to complexities if the results are expected synchronously, so tailor the approach based on the nature of the work being done.

Test Under Load

  • Perform load testing to simulate scenarios where the notification subscribers are triggered concurrently or frequently to identify potential performance bottlenecks.
  • Analyze metrics such as response times and system resource utilization to understand the impact of your subscriber logic under stress.

Monitoring and Logging

  • Add logging to your notification subscribers but avoid excessive or detailed logging during high-frequency events, as this can add significant overhead.
  • Consider leveraging performance monitoring tools to detect and diagnose slowdowns caused by subscribers.

Example: Performance in notifications

Here's an example illustrating the usage of CheckoutDoneOrderIsComplete with comments highlighting performance considerations within the OnNotify method:

using Dynamicweb.Extensibility.Notifications;
using Dynamicweb.Ecommerce.Orders;

[Dynamicweb.Extensibility.Notifications.Subscribe(Dynamicweb.Ecommerce.Notifications.Ecommerce.Cart.CheckoutDoneOrderIsComplete)]
      public class EcomCartCheckoutDoneOrderIsCompleteObserver : Dynamicweb.Extensibility.Notifications.NotificationSubscriber
{
    public override void OnNotify(string notification, NotificationArgs args)
    {
        var orderCompleteArgs = args as Dynamicweb.Notifications.Ecommerce.CheckoutDoneOrderIsCompleteArgs;
        if (orderCompleteArgs == null || orderCompleteArgs.Order == null)
            return;

        Order order = orderCompleteArgs.Order;

        // Performance consideration: Ensure that any heavy processing logic is minimized
        // This notification is fired when an order is marked complete, which can be a
        // critical step in checkout workflows. Introducing slow logic here can delay
        // customer receipt issuance, and user experience might be impacted.

        // Example of good practice: Use asynchronous task if heavy integration with other systems is required
        // Avoiding blocking tasks on critical path
        Task.Run(() => 
        {
            // Simulating integration with an external system asynchronously
            ExternalSystemIntegration(order);
        });

        // Example of bad practice: Blocking call or heavy computation inside the notification
        // This would make the checkout process slower for users
        // ProcessHeavyLogic(order); 
    }

    private void ExternalSystemIntegration(Order order)
    {
        // Simulate integration logic, e.g., sending data to a third-party API
        // Long-running operations should not block the main thread when processing orders
        System.Threading.Thread.Sleep(5000); // This is a placeholder for demonstration and should not be used
    }

    // private void ProcessHeavyLogic(Order order)
    // {
    //     // Heavy, blocking operations would go here
    //     // Example: Running data aggregation or intensive calculations
    // }
}

Comments

  • Blocking Calls: The OnNotify method responds to CheckoutDoneOrderIsComplete, which is triggered after an order completes. This is often a critical juncture in checkout processes, where user experience is important (e.g., order confirmation). Blocking the main thread by performing heavy computation or synchronous API calls could lead to delayed page responses or hanging cart receipts for the user.
  • Asynchronous Tasks: Offloading heavy tasks to background threads using asynchronous processing ensures that the main checkout flow remains responsive. In this example, data integration with an external system is performed asynchronously using Task.Run().
  • Avoiding Heavy Computation: Avoid placing time-consuming logic directly inside the notification handler to minimize potential slowdowns in checkout processes.
To top