Develop > Developer Reference > Using APIs > Universal CMDB API > High Availability Notification Service

High Availability Notification Service

Overview

The High Availability Notification Service keeps the notification listener alive within a UCMDB cluster. When a UCMDB instance that has a listener registered to it goes down, the listener is passed to another instance inside the cluster, and the client continues to receive notifications. This new notification service is available in the client SDK, and it is an optimization of the old notification process which was bound to the topology query service. Before the implementation of this new process, when a UCMDB instance failed, the registered listener client failed with it.

A UCMDB instance does not know when it becomes unresponsive. This is why the new notification system belongs to the UCMDB client API which runs outside the UCMDB cluster. In this way, the notification system can ping the UCMDB servers and check for their status.

Inside a UCMDB cluster exists only one writer and many readers. This is a commonly found setup in the IT world. Only one server can do changes (write) to the database while the other servers can just read from it. If the writer goes down, then a reader takes its responsibilities and becomes a writer.

Since the model changes happen only on the writer, the notification listener is also bound to the writer. If the writer goes down, until a reader takes its responsibilities no model changes happen. However, the following two situations can occur when the notification system will not deliver the changes to the registered listener:

  • Some changes are made, and the writer goes down in the meantime. In this case, UCMDB is unable to send the changes to the listener.
  • The writer goes down, and a new writer is chosen. While the listener registers to the new writer, some model changes can happen.

    Note The registration to the new writer is delayed because the registration system runs outside of UCMDB cluster and it takes a little while until it finds the new writer in the cluster.

The above situations can occur only during a specific time frame which starts when the last model changes are received, and ends when the listener has been registered to a new writer. The changes that occur in this time frame can be received by using the HistoryNotification parameter of the onSuccessfullySwitch() method.

Note The changes received by using the HistoryNotification parameter of the onSuccessfullySwitch() method can also include the last model changes that were already received by the listener.

Caution The new notification service uses common components with the old one, so you cannot use both notification services in the same time.

Environments with Load Balancer

The typical configuration for a Suite environment has a load balancer present. All the requests sent to any of the UCMDB instances that are configured to work behind a load balancer will pass through the load balancer. This is why a direct access way must be opened, and a mapping should be done between each UCMDB instance and each access point. Authorization objects must be configured to make requests to these access points.

The High Availability Notification Service is based on the old notification system, so before starting to use the High Availability Notification Service, you must check if the old notification service works with all the UCMDB instances within the cluster. To test the old notification system, use the TopologyQueryService#registerModelListener method.

The HighAvailabilityNotificationService Interface

A new interface was added to the UCMDB client called HighAvailabilityNotificationService.

For being able to switch between the UCMDB instances within the cluster, the HighAvailabilityNotificationService interface needs to know the details about every instance in the cluster. Therefore, the getAuthorizationManager() method was created which returns another interface called HighAvailabilityAuthorizationManager.

The HighAvailabilityAuthorizationManager interface contains two methods:

  • getAuthorizationFactory(). Returns the high availability authorization factory that allows the user to create authorization objects using the createAuthorizationObject() method.
  • addAuthorizationObject(). Adds the previously created authorization objects to the HighAvailabilityAuthorizationManager interface, which will further be used to authenticate to the UCMDB instance.

    To configure an authorization object, specify the following:

    • host name. UCMDB Server name
    • protocol. UCMDB Server protocol
    • port. UCMDB Server port
    • client context. Client context name
    • customer context. Customer context name
    • user name. User's user name
    • password. User's password
    • user repository. User's repository

Note An authorization object must be created and added to the HighAvailabilityAuthorizationManager interface for each UCMDB instance in the cluster. The authorization objects can be added dynamically to the HighAvailabilityAuthorizationManager interface along with any UCMDB instance that is added in the HA cluster.

To receive the notifications, you need to register to certain model events. In the old notification service, the registration process required three parameters:

  • A notification filter to know which kind of notifications to deliver
  • A notification listener to know where to deliver the notifications
  • (Optional) A notification fuse listener which limits the number of waiting notifications to be delivered to the notification listener

To actually register a listener for model changes in the high availability notification service, a new HighAvailabilityModelListener must be instantiated. HighAvailabilityModelListener is an abstract class providing two abstract methods that must be overridden and four normal methods which may be overridden in case of necessity.

The new registration process has been enhanced by optimizing the notification listener which now also delivers lifecycle events of the new notification system. These events can be listened by overriding the following methods:

  • onChange(). Receives a notification when a change is made to a CI or to a relation according to the configured ModelEventsFilter.

    Parameter: ModelChange

    By using the ModelChange parameter, the following types of changes can be retrieved: added CIs, updated CIs, removed CIs, added relations, updated relations, and removed relations.

    For example, the following configured ModelEventsFilter returns the name and someProperty attributes changes that happened to the specified configured CI Type (nt).

    	final TopologyQueryService topologyQueryService = ucmdbService.getTopologyQueryService();
            final ModelEventsFilter modelEventsFilter;
            modelEventsFilter = topologyQueryService.getFactory().createModelEventsFilter();
            modelEventsFilter.notifyOnUpdate().ofType("nt").property("name");
            modelEventsFilter.notifyOnCreate().ofType("nt").property("SomeProperty");
  • onSuccessfullySwitch(). It is called when a new writer is found and the listener registers to it.

    This method is called when a registration switch was done. This happens when the UCMDB writer is down and the notification system found another writer for registration. It gives user the possibility to receive history changes from the database, if one of the following situations occurs:

    • Some changes are made, and the writer goes down in the meantime. In this case, UCMDB is unable to send the changes to the listener.
    • The writer goes down, and a new writer is chosen. While the listener registers to the new writer, some model changes can happen.

      Note  

      • The registration to the new writer is delayed because the registration system runs outside of UCMDB cluster and it takes a little while until it finds the new writer in the cluster.
      • The changes that are retrieved from history and not by the listener are not filtered according to the attributes that were configured in the ModelEventsFilter. Only the CI Types are taken into account.

        A large amount of data can be expected when many attributes changes happen in one of the situations described above.

    Parameter: HistoryNotification

    To receive the changes made while the listener registered to the new writer, you can use the getHistoryNotification() method.

  • onSwitchingWriter(). (Optional) Switches to another UCMDB writer in case of failure.

    This method is called when the writer is down and the registration of the listener will be passed to another writer.

    Parameter:

    The authorization object that represents the UCMDB writer that has failed.

  • onFindingWriter(). (Optional) Searches for a UCMDB writer inside the cluster

    This method is called when the notification system is searching for a writer. Each time an authorization object is created and added to the HighAvailabilityAuthorizationManager interface, this method searches within the created objects for a writer, and it stops only when a writer is found.

    Parameter:

    False if no writer was found, and True if a writer was found. If the result is True, then this method will stop being called until the next cycle of high availability notification service.

  • onRegisteringToWriter(). (Optional) Registers to UCMDB writer for retrieving events

    This method is called when the registration is done on a specific writer.

    Parameter:

    A registration ID that can be used for debugging purposes.

  • shouldStopNotification(). (Optional) Stops the notification system.

    This method is called every time the current notification system pings the current writer for its status.

    Parameter: none. This method does not use a parameter; instead, it allows you to stop the entire notification system for a specific Model Listener by returning True or False.

    Return value:

    By default, this method has no impact on the notification system. Anyway, if you want to stop the notification system in some cases, you can override the method and return the boolean value True.

Note If long processing tasks are included in any of the methods described above (except onChange()), it is recommended to use a new thread to avoid delaying the notification system.

Example: How to properly configure the High Availability Notification Service

This is just an example to help you understand how to use the high availability notification service.

In this example, we want to retrieve all the model changes that happened to the Windows CI Type inside a UCMDB cluster.

We have the following values:

  • UCMDB Instance 1

    • authorization object: UCMDBServer1
    • host name: myhost1.mycompany.net

  • UCMDB Instance 2

    • authorization object: UCMDBServer2
    • host name: myhost2.mycompany.net

  • UCMDB Instance 3

    • authorization object: UCMDBServer3
    • host name: myhost3.mycompany.net

In this example, the following values are the same for all three UCMDB instances:

  • protocol: https
  • port: 8443
  • client context: "integration user"
  • customer context: "Default Client"

  • user name: "admin"

  • password: "Admin123."

  • user repository: "UCMDB"

We perform the following steps:

  1. Find the unique name of the Windows CI Type. To do this, we go to UCMDB > Modeling > CI Type Manager > CI Types pane, search for Windows, and then open the Details tab. In the Name field we find the unique name of the Windows CI Type. In our case, the unique name of the Windows CI Type is nt.

  2. Download UCMDB Local Client from the UCMDB splash screen (access UCMDB Server version 10.33, or 2018.08 or later using the following URL: https://<server name or IP address>.<domain name>:8443), and then create a new project.
  3. Connect to one of the UCMDB instances to have access to the new HighAvailabilityNotificationService interface. We use the following connection method:

        public static UcmdbService initializeConnection(String host) {
            String protocol = "https";
    
            int port = 8443;
            UcmdbServiceProvider provider = null;
            try {
                provider = UcmdbServiceFactory.getServiceProvider(protocol, host, port);
                Credentials credentials = provider.createCredentials("admin", "Admin123.","UCMDB");
                CustomerContext customerContext = provider.createCustomerContext("Default Client");
                ClientContext apiClientCotnext = provider.createClientContext("integration user");
                return provider.connect(customerContext, credentials, apiClientCotnext);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
  4. Use the HighAvailabilityNotificationService interface from the UCMDB service.

  5. Register for model changes to a cluster:

    1. Provide information about each UCMDB instance to the notification system.

         UcmdbService ucmdbService = initializeConnection("myhost2.mycompany.net");
              HighAvailabilityNotificationService highAvailabilityNotificationService = ucmdbService.getHighAvailabilityNotificationService();
      
              HighAvailabilityAuthorizationManager authorizationManagement = highAvailabilityNotificationService.getAuthorizationManager();
      
              HighAvailabilityAuthorizationFactory authorizationFactory = authorizationManagement.getAuthorizationFactory();
              HighAvailabilityAuthorizationObject  UCMDBServer1 = authorizationFactory.createAuthorizationObject();
              HighAvailabilityAuthorizationObject  UCMDBServer2 = authorizationFactory.createAuthorizationObject();
              HighAvailabilityAuthorizationObject  UCMDBServer3 = authorizationFactory.createAuthorizationObject();
      
               UCMDBServer1.setHost("myhost1.mycompany.net").setProtocol("https").setPort(8443).setClientContext("integration user").
                      setCustomerContext("Default Client").setUserName("admin").setPassword("Admin123.").setUserRepository("UCMDB");
               UCMDBServer2.setHost("myhost2.mycompany.net").setProtocol("https").setPort(8443).setClientContext("integration user").
                      setCustomerContext("Default Client").setUserName("admin").setPassword("Admin123.").setUserRepository("UCMDB");
               UCMDBServer3.setHost("myhost3.mycompany.net").setProtocol("https").setPort(8443).setClientContext("integration user").
                      setCustomerContext("Default Client").setUserName("admin").setPassword("Admin123.").setUserRepository("UCMDB");
      
              authorizationManagement.addAuthorizationObject(UCMDBServer1);
              authorizationManagement.addAuthorizationObject(UCMDBServer2);
              authorizationManagement.addAuthorizationObject(UCMDBServer3);
    2. Configure the model notification filter.

      	final TopologyQueryService topologyQueryService = ucmdbService.getTopologyQueryService();
              final ModelEventsFilter modelEventsFilter;
              modelEventsFilter = topologyQueryService.getFactory().createModelEventsFilter();
              modelEventsFilter.notifyOnUpdate().ofType("nt").property("name");
              modelEventsFilter.notifyOnCreate().ofType("nt").property("SomeProperty");
    3. Register a new model listener using one of methods provided by the notification service. In this example we do not extend the HighAvailabilityModelListener to a new class template and instantiate it. Instead, we use the inline declaration to provide the first parameter which must be of type HighAvailabilityModelListener, and the modelEventsFilter configured above, as a second parameter.

                 highAvailabilityNotificationService.registerModelListener(new HighAvailabilityModelListener() {
                  @Override
                  public void onChange(ModelChange modelChange) {
                      System.out.println("\n" +
                              "\nOnChange 1");
                      System.out.println(modelChange.getAddedCIs());
                      System.out.println(modelChange.getUpdatedCIs());
                      System.out.println(modelChange.getRemovedCIs());
                      System.out.println(modelChange.getAddedRelations());
                      System.out.println(modelChange.getUpdatedRelations());
                      System.out.println(modelChange.getRemovedRelations());
                      System.out.println("\n");
                  }
      
                  @Override
                  public void onSuccessfullySwitch(HistoryNotification s) {
                      System.out.println("Writer switch");
                      Map<UcmdbId, HistoryChanges> historyNotification = s.getHistoryNotification();
                      Collection<HistoryChanges> values = historyNotification.values();
                      for(HistoryChanges changes:values){
                          System.out.println(changes.getDataId());
                          System.out.println(changes.getChanges().stream().map(HistoryChange::getData).collect(Collectors.toList()));
                      }
                      System.out.println("\n");
                  }
                  @Override
                  public void onFindingWriter(boolean found) {
                      System.out.println("Writer found "+found);
                  }
      
                  @Override
                  public void onRegisteringToWriter(String registeredListenerId) {
                      System.out.println("Registered listener "+registeredListenerId);
                  }
      
                  @Override
                  public void onSwitchingWriter(HighAvailabilityAuthorizationObject currentlyUsedAuthorizationObject) {
                      System.out.println("Switching writer "+currentlyUsedAuthorizationObject.getHost());
                  }
      
                  @Override
                  public boolean shouldStopNotification() {
                      return true;
                  }
              },modelEventsFilter);