Article
· 21 hr ago 9m read

Analyzing IRIS ^PERFMON Runtime Performance Using the Java Native SDK

Technical Documentation — Quarkus IRIS Monitor System

1. Purpose and Scope

This module enables integration between Quarkus-based Java applications and InterSystems IRIS’s native performance monitoring capabilities.
It allows a developer to annotate methods with @PerfmonReport, which triggers IRIS’s ^PERFMON routines automatically around method execution, generating performance reports without manual intervention.


2. System Components

2.1 Annotation: @PerfmonReport

  • Defined as a CDI InterceptorBinding.
  • Can be applied to methods or classes.
  • Directs the framework to wrap method execution with IRIS monitoring logic.

2.2 Interceptor: PerfmonReportInterceptor

  • Intercepts calls to annotated methods.
  • Execution flow:

    1. Log start event (LOG.infof("INIT: …"))
    2. Call monitorSystem.startPerfmon()
    3. Proceed with context.proceed()
    4. In a finally block:
    • Call monitorSystem.generateReportPerfmon(...)
    • Call monitorSystem.stopPerfmon()
    • Log end event with execution time
  • Ensures that monitoring is always ended, even if an exception is thrown.

2.3 DAO Bean: MonitorSystem

  • CDI bean annotated with @ApplicationScoped.
  • Holds a single instance of IRIS initialized at startup.
  • Configuration injected via @ConfigProperty (JDBC URL, user, password).
  • Uses DriverManager.getConnection(...) to obtain a raw IRISConnection.
  • Contains methods:

    • startPerfmon()
    • generateReportPerfmon(String reportName)
    • stopPerfmon()
  • Each calls the appropriate ObjectScript methods in iris.src.dc.AdapterPerfmonProc via iris.classMethodVoid(...).

2.4 ObjectScript Adapter: iris.src.dc.AdapterPerfmonProc

  • Defines the routines that encapsulate ^PERFMON logic:

    Class iris.src.dc.AdapterPerfmonProc Extends %RegisteredObject
    {
        ClassMethod start() As %Status
        {
            Set namespace = $NAMESPACE
            zn "%SYS"
            set status = $$Stop^PERFMON()
            set status = $$Start^PERFMON()
            zn namespace
            return status
        }
    
        ClassMethod generateReport(nameReport As %String = "report.txt") As %Status
        {
            Set namespace = $NAMESPACE
            zn "%SYS"
            Set tempDirectory = ##class(%SYS.System).TempDirectory()
            set status = $$Report^PERFMON("R","R","P", tempDirectory_"/"_nameReport)
            zn namespace
    
            return status
        }
    
        ClassMethod stop() As %Status
        {
            Set namespace = $NAMESPACE
            zn "%SYS"
            Set status = $$Stop^PERFMON()
            zn namespace
    
            return status
        }
    }
    
  • Operates in namespace %SYS to access ^PERFMON routines, then returns to the original namespace.


3. Execution Flow

  1. A request enters the Quarkus application.
  2. The CDI interceptor detects the @PerfmonReport annotation and intercepts the method call.
  3. monitorSystem.startPerfmon() is invoked, triggering IRIS ^PERFMON monitoring.
  4. The business method executes normally (data access, transformations, logic, etc.).
  5. After the method returns or throws an exception, the interceptor ensures:
    • monitorSystem.generateReportPerfmon(...) is called to create a .txt performance report.
    • monitorSystem.stopPerfmon() is executed to stop the monitoring session.
    • The total Java-side execution time is logged using Logger.infof(...).
  6. The generated report file is stored in the IRIS temporary directory, typically: /usr/irissys/mgr/Temp/

    • The file name follows the pattern:
      <ClassName><MethodName><timestamp>.txt

4. Technical Challenges and Solutions

Challenge Solution
ClassCastException when using pooled JDBC connections Use DriverManager.getConnection(...) to obtain a native IRISConnection instead of the pooled ConnectionWrapper.
Overhead from repeatedly opening connections Maintain a single IRIS instance within an @ApplicationScoped bean, initialized via @PostConstruct.
Ensuring ^PERFMON is always stopped even on exceptions Use try-finally in the interceptor to call stopPerfmon() and generateReportPerfmon().
Configuration portability Inject connection settings (jdbc.url, username, password) using @ConfigProperty and application.properties.
Managing concurrent monitor sessions Avoid annotating highly concurrent endpoints. Future versions may implement session-level isolation.

5. Use Cases and Benefits

  • Enables real-time visibility into IRIS runtime activity from Java code.
  • Simplifies performance analysis and query optimization for developers.
  • Useful for benchmarking, profiling, and system regression testing.
  • Can serve as a lightweight performance audit trail for critical operations.

6. Practical Example of Use

The complete source code and deployment setup are available at:


6.1 Overview

The application runs a Quarkus server connected to an InterSystems IRIS instance configured with the FHIRSERVER namespace.
The ORM layer is implemented using Hibernate ORM with PanacheRepository, allowing direct mapping between Java entities and IRIS database classes.

When the application starts (via docker-compose up), it brings up:
- The IRIS container, hosting the FHIR data model and ObjectScript routines (including AdapterPerfmonProc);
- The Quarkus container, exposing REST endpoints and connecting to IRIS via the native JDBC driver.


6.2 REST Endpoint

A REST resource exposes a simple endpoint to retrieve patient information:

@Path("/patient")
public class PatientResource {

    @Inject
    PatientService patientService;

    @GET
    @Path("/info")
    @Produces(MediaType.APPLICATION_JSON)
    public PatientInfoDTO searchPatientInfo(@QueryParam("key") String key) {
        return patientService.patientGetInfo(key);
    }
}

This endpoint accepts a query parameter (key) that identifies the patient resource within the FHIR data repository.


### 6.3 Service Layer with @PerfmonReport

The PatientService class contains the business logic to retrieve and compose patient information.
It is annotated with @PerfmonReport, which means each request to /patient/info triggers IRIS performance monitoring:

@ApplicationScoped
public class PatientService {

    @Inject
    PatientRepository patientRepository;

    @PerfmonReport
    public PatientInfoDTO patientGetInfo(String patientKey) {

        Optional<Patient> patientOpt = patientRepository.find("key", patientKey).firstResultOptional();
        Patient patient = patientOpt.orElseThrow(() -> new IllegalArgumentException("Patient not found"));

        PatientInfoDTO dto = new PatientInfoDTO();
        dto.setKey(patient.key);
        dto.setName(patient.name);
        dto.setAddress(patient.address);
        dto.setBirthDate(patient.birthDate != null ? patient.birthDate.toString() : null);
        dto.setGender(patient.gender);
        dto.setMedications(patientRepository.findMedicationTextByPatient(patientKey));
        dto.setConditions(patientRepository.findConditionsByPatient(patientKey));
        dto.setAllergies(patientRepository.findAllergyByPatient(patientKey));

        return dto;
    }
}

6.4 Execution Flow

A request is made to: GET /patient/info?key=Patient/4

Quarkus routes the request to PatientResource.searchPatientInfo().

The CDI interceptor detects the @PerfmonReport annotation in PatientService.patientGetInfo().

Before executing the service logic:

  • The interceptor invokes MonitorSystem.startPerfmon(), which calls the IRIS class iris.src.dc.AdapterPerfmonProc.start().

  • The method executes business logic, querying patient data using Hibernate PanacheRepository mappings.

After the method completes:

  • MonitorSystem.generateReportPerfmon() is called to create a performance report.

  • MonitorSystem.stopPerfmon() stops the IRIS performance monitor.

A .txt report is generated under: usr/irissys/mgr/Temp/

Example filename: PatientService_patientGetInfo_20251005_161906.txt

6.5 Result

The generated report contains detailed IRIS runtime statistics, for example:

                         Routine Activity by Routine

Started: 10/11/2025 05:07:30PM                    Collected: 10/11/2025 05:07:31PM

Routine Name                        RtnLines  % Lines   RtnLoads  RtnFetch  Line/Load Directory
----------------------------------- --------- --------- --------- --------- --------- ---------
Other                                     0.0       0.0       0.0       0.0         0
PERFMON                                  44.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%occLibrary                         3415047.0      34.1   48278.0       0.0      70.7 /usr/irissys/mgr/irislib/
iris.src.dc.AdapterPerfmonProc.1          7.0       0.0       2.0       0.0       3.5 /usr/irissys/mgr/FHIRSERVER/
%occName                            5079994.0      50.7       0.0       0.0         0 /usr/irissys/mgr/irislib/
%apiDDL2                            1078497.0      10.8   63358.0       0.0      17.0 /usr/irissys/mgr/irislib/
%SQL.FeatureGetter.1                 446710.0       4.5   66939.0       0.0       6.7 /usr/irissys/mgr/irislib/
%SYS.WorkQueueMgr                       365.0       0.0       1.0       0.0     365.0 /usr/irissys/mgr/
%CSP.Daemon.1                            16.0       0.0       1.0       0.0      16.0 /usr/irissys/mgr/irislib/
%SYS.TokenAuth.1                         14.0       0.0       5.0       0.0       2.8 /usr/irissys/mgr/
%Library.PosixTime.1                      2.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislib/
%SYS.sqlcq.uEXTg3QR7a7I7Osf9e8Bz...      52.0       0.0       1.0       0.0      52.0 /usr/irissys/mgr/
%SYS.SQLSRV                              16.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%apiOBJ                                 756.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislib/
FT.Collector.1                            0.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
SYS.Monitor.FeatureTrackerSensor.1        0.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%SYS.Monitor.Control.1                    0.0       0.0       0.0       0.0         0 /usr/irissys/mgr/
%SYS.DBSRV.1                            252.0       0.0       4.0       0.0      63.0 /usr/irissys/mgr/
%sqlcq.FHIRSERVER.cls12.1                19.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%sqlcq.FHIRSERVER.cls13.1                74.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%sqlcq.FHIRSERVER.cls14.1                74.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%sqlcq.FHIRSERVER.cls15.1                52.0       0.0       0.0       0.0         0 /usr/irissys/mgr/irislocaldata/
%SYS.System.1                             1.0       0.0       0.0       0.0         0 /usr/irissys/mgr/

This data provides precise insight into what routines were executed internally by IRIS during that REST call — including SQL compilation, execution, and FHIR data access.

Insight: The %sqlcq.FHIRSERVER.* routines capture all SQL cache queries executed by Quarkus within the method. Monitoring these routines allows developers to analyze query execution, understand code behavior, and identify potential performance bottlenecks. This makes them a powerful tool for development and debugging of FHIR-related operations.

image

6.6 Summary

This example demonstrates how a standard Quarkus service can transparently leverage IRIS native monitoring tools using the @PerfmonReport annotation.
It combines:

  • CDI interceptors (Quarkus)

  • Hibernate PanacheRepositories (ORM)

  • IRIS native ObjectScript routines (^PERFMON)

The result is a fully automated and reproducible performance profiling mechanism that can be applied to any service method in the application.

Discussion (0)1
Log in or sign up to continue