Note This section discusses some Sentinel feature development Best Practices

This page documents and describes some of the Best Practices established by the Sentinel development team.

For each guideline this page discusses why the practice should be followed, and whether there are situations when it may not be appropriate to follow the guideline. While there are existing features that do not follow all these guidelines, new features should be written to follow the instructions on this page.

Effective Tracing

Trace messages serve a number of purposes. Trace messages at fine, finer and finest should be used when diagnosing a problem. Trace messages at warning and severe should be used to record information related to a situation that will cause unexpected behaviour when it happens. info trace level is generally not useful in sentinel features.

It is important to put effort into designing the tracing a features does so:

  • there is useful information available for troubleshooting

  • unexpected behaviour does not cause a snowballing number of warning and severe trace messages that impact the stability of the platform.

Rule Rationale Exceptions

Always guard trace statements.

if (tracer.isXEnabled()) {
    ...
}

This pattern avoids unnecessary overhead caused when evaluating the argument to the tracer (eg string concatenation), when the trace message would not be written.

The pattern should be followed in all cases for consistency, even when there is no overhead required to evaluate the argument of tracer methods.

Tracer messages written at fine level should be simple Strings.

This allows fine tracing to be used with minimal performance impact

Always follow this rule.

Features should trace FEATURENAME has started and FEATURENAME has finished at fine level on entering and exiting feature control.

Allows for simple, consistent search in log files to follow control flow.

Always follow this rule.

Features should minimise using warning or severe trace levels

This avoids the possibility of log spam on production systems, which could compound failures (for example during severe load)

Generally avoid these trace levels in features. Use statistics instead.

Features should rarely use info logging

Logging of feature execution should be done at fine/finer/finest.

Using info might be defensible in cases that won’t spam the logs in repeated failures, and isn’t handled better by some other mechanism.

Note

Java uses eager evaluation - all arguments to a method are evaluated before the method itself is executed. This includes calls to tracers. Remember that the toString() implementation of objects being traced (such as events) may be computationally expensive.

Using Statistics

Statistics associate counters with actions a feature performs, such rejecting a session, or unexpected situations, such as an external system not being available. Platform administrators can use statistics to track stability and performance; for example, noting how long an external component takes to answer a request or how many retries a feature has had to make.

Rule Rationale Exceptions

Feature statistics should represent problem domain related occurrences that reflect the behaviour of the feature.

Statistics provide a low impact method to monitor the behaviour of Sentinel.

Always follow this recommendation

Don’t replicate feature statistics that are already collected by the Sentinel core.

The sentinel core already collects statistics when a feature runs, and related to the outcome (failing to start, issued a warning and so on).

Always follow this recommendation

Consider defining threshold alarms

A threshold alarm is a custom alarm that is created by the platform administrator. Threshold alarms are raised or cleared automatically based rules that are based on statistics.

Using the Feature API

Rule Rationale Exceptions

One of …​

  • featureEndpoint.featureHasFinished()

  • featureEndpoint.featureIsWaiting()

must be called when a feature’s main control block completes.

If the feature is waiting for an asynchronous non-sip event, featureIsWaiting() must be called. Otherwise, featureHasFinished() must be called.

The sentinel run-time requires features to signal the outcome of being invoked, so it can update its own state and continue running other features.

Always follow this rule.

For SIP-only features, or features with no asynchronous calls, trace "FEATURENAME has finished" and call featureHasFinished() in a finally block around the feature logic.

featureCannotStart() should be called if the feature is unable to begin execution - for example if config cannot be loaded

The Sentinel run-time collects statistics related to feature failures that can be monitored.

Always follow this rule.

featureFailedToExecute() should be called if the feature starts, but is unable to complete execution due to an internal (relative to the sentinel system) error.

The Sentinel run-time collects statistics that can be monitored by the platform operator

The decision about whether to call featureFailedToExecute or whether to increment a feature statistic and trace the error can be a grey area. In general, if the problem is internal to the system - eg a call to getLegManager() returns null, or a feature is triggered on an unexpected object type - featureFailedToExecute() should be called. If the problem is external - eg a received SIP message cannot be decoded - then a feature statistic should be defined for this case.

featureIssuedWarning() should be used in the same way as featureFailedToExecute(), but for situations where the feature can proceed with some form of call processing

The Sentinel run-time collects statistics that can be monitored by the platform operator

In general, if the problem is internal to the system - eg a timer cannot be set - featureFailedToExecute() should be called. If the problem is external - eg a message sent to an external system timed out - then a feature statistic should be defined for this case.

Dependencies and Annotations

Rule Rationale Exceptions

Don’t hard-code name/vendor/version values.

If the version of a dependency changes, you have to change it in all places it is used.

Always follow this rule.

Add dependencies to the slee-component ivy conf

The sentinel build system inspects resolved dependencies at build time and, where possible, sets variables that can be used during annotation processing.

Note

hint - use ant -v to see the variables available.

Always follow this rule.

For example …​

Ivy dependency
<dependency org="${sdk.ivy.org}"
            name="volte-example-pojo-feature-library"
            rev="latest.${ivy.status}"
            branch="${branch.name}"
            conf="self, provisioning -> api; api; slee-component; slee-binding"/>
Annotation in a feature
@LibraryReferences(
    libraryRefs = {
        @LibraryReference(
            library = @ComponentId(name = "@volte-example-pojo-feature-library.name@",
                                 vendor = "@volte-example-pojo-feature-library.vendor@",
                                version = "@volte-example-pojo-feature-library.version@"))
    }
)
Output from the build (ant -v)
...
[oc:finddepcomponentinfo] Processing module: volte-example-pojo-feature-library
[oc:finddepcomponentinfo] Adding property: name=volte-example-pojo-feature-library.name, value=volte-example-pojo-feature-library
[oc:finddepcomponentinfo] Adding property: name=volte-example-pojo-feature-library.vendor, value=OpenCloud
[oc:finddepcomponentinfo] Adding property: name=volte-example-pojo-feature-library.version, value=2.8.0
...
Generated fragment of the deployment descriptor
...
    <library-ref>
        <library-name>volte-example-pojo-feature-library</library-name>
        <library-vendor>OpenCloud</library-vendor>
        <library-version>2.8.0</library-version>
    </library-ref>
...

Minimise the number of dependencies in ivy.xml files

Ivy is a transitive package manager. Make use of ivy and only include the direct dependencies your components need and let ivy transitively resolve the rest.

Follow this rule unless you need fine grained control of transitive dependencies, for example to exclude a component.

Using Cassandra CQL

Rule Rationale Exceptions

Prepared statements should used in preference to executing raw strings when using the cassandraCQLProvider

Efficiency and security

There may be some situations where prepared statements are not practical or efficient, and in those cases raw strings can be used

Using SLEE Profiles

Rule Rationale Exceptions

Each profile specification should include a profile management class that extends BaseProfileAbstractClass

  • Avoid run-time errors related to invalid configuration state.

  • Provides a single point for validation logic used in both REM and the Rhino console

  • BaseProfileAbstractClass has many useful utility methods for validating attributes of profiles and implements profileVerify() from the javax.slee.Profile interface.

Always follow this rule.

Note See Chapter 10 Profiles and Profile Specifications of the SLEE specification.

Using SIP

Rule Rationale Exceptions

Prefer using sip header names from predefined classes, eg javax.sip.header.RouteHeader to redefining strings

Avoids typos and regional spelling differences

Whenever importing the relevant libraries is not feasible

Feature Configuration

Rule Rationale Exceptions

Feature configuration should be provisioned using the @Provisioning annotation.

These annotations are used to generate the REST API and web UI for the feature.

If configuration profiles are shared between multiple features, only one feature should be responsible for provisioning.

Session State

Rule Rationale Exceptions

the @InitialValueField annotation should be used for session state variables where the default value is unacceptable

Proper initialisation of session state fields means that less validation logic is needed, spread throughout the features that use the session state fields.

Use this pattern whenever:

  • a complex type in session state should be initialized to something other than null

  • the default value of standard Java type is not appropriate for the field.

Using the SIP Leg Manager

Rule Rationale Exceptions

Don’t look up legs by name - use the ACI instead

  • Avoid potential duplicate leg names when replacing a leg with the same name eg EarlyMediaAnnouncements

  • legNames are useful for tracing, but in general the ACI should be stored on fsm endpoints or a session state field

Previous page Next page