1

I'm facing GC overhead limit exceeded. I'm trying to run a heavy query(with multiple joins) and store the result in report.

Below is the query:

select o.ID_KEY as orderId, o.ORDER_NUMBER as orderNumber, a.ADL_ACCOUNT as adlAccount, o.BASL_ACCOUNT as baslAccount,
             a.FULL_NAME as customerName,
             a.MOBILE_NO as mobileNumber,
                         CASE
                              WHEN dr_a.STATUS_CODE = '200'
                              THEN 'Y'
                              ELSE 'N'
                              END         adlPaymentPosting,
                         CASE
                             WHEN dr_b.STATUS_CODE = '200'
                                 THEN 'Y'
                             ELSE 'N'
                             END         baslPaymentPosting,
                         CASE
                             WHEN o.ORDER_STATUS IN ('INSTALLATION_COMPLETED', 'COMPLETED')
                                 THEN 'Y'
                             ELSE 'N'
                             END         adlChargePosting,
                         CASE
                             WHEN o.ORDER_STATUS IN
                                  ('ATOM_SUCCESS', 'INSTALLATION_SCHEDULED', 'INSTALLATION_STARTED', 'INSTALLATION_COMPLETED',
                                   'COMPLETED')
                                 THEN 'Y'
                             ELSE 'N'
                             END         baslChargePosting,
                         CASE
                             WHEN o.PAYMENT_STATUS = 'COMPLETED'
                                 THEN 'Y'
                             ELSE 'N'
                             END         paymentStatus,
                         o.ORDER_STATUS as orderStatus,
                         o.AMOUNT     as paymentAmount,
                         accAdd.CITY   as orderCity,
                         pd.RESPONSE_CODE as TransactionId,
                         o.SUBSCRIPTION_COST as adlAmount,
                         o.INSTALLATION_COST + o.HARDWARE_COST as baslAmount,
                         ad.FIRST_NAME || ' ' || ad.LAST_NAME as agentName,
                        
                         ad.MOBILE_NO as agentMobileNumber,
                    
                         pd.payment_gateway as PaymentGateway, 
             o.CREATED_ON as orderDate,
             ct.CIRCLE_NAME as circle,
             NVL(q111,0) as total111,
             NVL(q112,0) as total112,
             NVL(q113,0) as total113, 
             NVL(q111,0) +  NVL(q112,0) + NVL(q113,0) as total, 
             (SELECT TYPE FROM ABC WHERE ID_KEY = (SELECT PLAN_ID FROM XYZ WHERE ORDER_ID = o.ID_KEY AND ROWNUM=1)) as type 
                         
                  from ORDERS o
                      INNER JOIN
                          ACCOUNT a
                              on o.ACCOUNT_ID = a.ID_KEY
                      LEFT OUTER JOIN
                          DCAP_RESPONSE dr_a
                              on o.ID_KEY = dr_a.ORDER_ID and dr_a.REQ_TYPE = 'ADL' AND dr_a.STATUS_CODE = (SELECT STATUS_CODE FROM DCAP_RESPONSE WHERE ID_KEY = (SELECT MAX(ID_KEY) FROM DCAP_RESPONSE) AND ID_KEY = o.ID_KEY)
                      LEFT OUTER JOIN
                          DCAP_RESPONSE dr_b
                              on o.ID_KEY = dr_b.ORDER_ID and dr_b.REQ_TYPE = 'BASL' AND dr_a.STATUS_CODE = (SELECT STATUS_CODE FROM DCAP_RESPONSE WHERE ID_KEY = (SELECT MAX(ID_KEY) FROM DCAP_RESPONSE) AND ID_KEY = o.ID_KEY)
                      LEFT OUTER JOIN 
                          payment_details pd
                              on o.ID_KEY = pd.ORDER_ID
                     LEFT OUTER JOIN
                         ACCOUNT_ADDRESS accAdd
                             on o.SHIPPING_ADDRESS_ID = accAdd.ID_KEY
                     LEFT OUTER JOIN 
                         agent_orders ao
                             on o.ID_KEY = ao.ORDER_ID
                     LEFT OUTER JOIN 
                         agent_details ad
                         on ad.AGENT_ID = ao.AGENT_ID
                     LEFT OUTER JOIN 
                         cities ct
                     on accAdd.CITY = ct.NAME
                 LEFT OUTER JOIN ( 
                         SELECT ORDER_ID,
                         MAX(CASE WHEN PRODUCT_ID =111 THEN QUANTITY END) AS q111,
                         MAX(CASE WHEN PRODUCT_ID =112 THEN QUANTITY END) AS q112,
                         MAX(CASE WHEN PRODUCT_ID =113 THEN QUANTITY END) AS q113
                         FROM ORDER_HARDWARE_ITEM 
                         GROUP BY  ORDER_ID) oh 
                         on oh.ORDER_ID = o.ID_KEY
                  WHERE (o.LAST_UPDATED_ON BETWEEN (SELECT TRUNC((SYSDATE-1), 'month') from dual) AND (TRUNC(SYSDATE)-1/86400)) AND (o.ORDER_TYPE = 'BUY_PRODUCT')
                

Below is the code for storing in excel:

public <T> void writeToExcel(String fileName, List<T> data, String sheetName, boolean addExtraMethods) {
    OutputStream fos = null;
    XSSFWorkbook workbook = null;
    FileInputStream inputStream = null;
    try {
        final String currentDate = simpleDateFormat.format(new Date());
        File file = new File(currentDate + fileName + ".xlsx");
        if (file.exists()) {
            inputStream = new FileInputStream(file);
            workbook = new XSSFWorkbook(inputStream);
        } else
            workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet(sheetName);
        try {
            int rowCount = 0;
            int columnCount = 0;
            Row row = sheet.createRow(rowCount++);
            CellStyle headerCellStyle = reportUtil.getHeaderStyle(sheet);
            List<String> fieldNames = getFieldNamesForClass(data.get(0).getClass());
            for (String header : fieldNames) {

                Cell cell = row.createCell(columnCount);
                cell.setCellStyle(headerCellStyle);
                cell.setCellValue(header);
                sheet.autoSizeColumn(columnCount++);

            }

            Class<? extends Object> classz = data.get(0).getClass();

            for (T t : data) {
                row = sheet.createRow(rowCount++);
                columnCount = 0;
                for (String fieldName : fieldNames) {
                    Cell rowCell = row.createCell(columnCount);

                    Method method = null;
                    try {
                        method = classz.getMethod("get" + capitalize(fieldName));
                        Object value = method.invoke(t, (Object[]) null);
                        if (value != null) {
                            if (value instanceof String) {
                                rowCell.setCellValue((String) value);
                            } else if (value instanceof Long) {
                                rowCell.setCellValue((Long) value);
                            } else if (value instanceof Integer) {
                                rowCell.setCellValue((Integer) value);
                            } else if (value instanceof Double) {
                                rowCell.setCellValue((Double) value);
                            }
                        }
                        columnCount++;
                    } catch (NoSuchMethodException nme) {
                        method = classz.getMethod("get" + fieldName);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }


        fos = new FileOutputStream(currentDate + fileName + ".xlsx");
        workbook.write(fos);
        fos.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (fos != null) {
                fos.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if (workbook != null) {
                workbook.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

// retrieve field names from an interface, ignoring getDecorateClass, getTarget getTargetClass methods,etc
private static List<String> getFieldNamesForClass(Class<?> clazz) throws Exception {
    List<String> fieldNames = new ArrayList<String>();
    Method[] methods = OrdersDataReportDTO.class.getMethods();

    Arrays.sort(methods, (o1, o2) -> {
        boolean isCheck = o1.isAnnotationPresent(Sequence.class);
        Sequence or1 = o1.getAnnotation(Sequence.class);
        Sequence or2 = o2.getAnnotation(Sequence.class);
        // nulls last
        if (or1 != null && or2 != null) {
            return or1.value() - or2.value();
        } else if (or1 != null && or2 == null) {
            return -1;
        } else if (or1 == null && or2 != null) {
            return 1;
        }
        return o1.getName().compareTo(o2.getName());
    });
    for (Method method : methods) {
        if (method.getName().startsWith("get") && !method.getName().equals("getDecoratedClass")
                && !method.getName().equals("getTarget") && !method.getName().equals("getTargetClass"))
            fieldNames.add(lowerFirst(method.getName().substring(3)));
    }
    return fieldNames;
}

// lower the first letter of the field name
private static String lowerFirst(String s) {
    if (s.length() == 0)
        return s;
    return s.substring(0, 1).toLowerCase() + s.substring(1);
}

// capitalize the first letter of the field name
private static String capitalize(String s) {
    if (s.length() == 0)
        return s;
    return s.substring(0, 1).toUpperCase() + s.substring(1);
}

These are error logs:

    2022-07-18 | 20:22:38.203 |  |  | scheduling-1                        | ERROR | org.springframework.scheduling.support.TaskUtils$LoggingErrorHandler                                 | Unexpected error occurred in scheduled task
java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.StringBuilder.toString(StringBuilder.java:412)
    at org.apache.poi.ss.util.CellReference.formatAsString(CellReference.java:521)
    at org.apache.poi.ss.util.CellReference.formatAsString(CellReference.java:495)
    at org.apache.poi.xssf.usermodel.XSSFCell.setCellNum(XSSFCell.java:897)
    at org.apache.poi.xssf.usermodel.XSSFRow.createCell(XSSFRow.java:226)
    at org.apache.poi.xssf.usermodel.XSSFRow.createCell(XSSFRow.java:200)
    at org.apache.poi.xssf.usermodel.XSSFRow.createCell(XSSFRow.java:47)
    at com.airtel.surveillance.analytics.utils.ExcelUtil.writeToExcel(ExcelUtil.java:64)

I've tried increasing memory:

FROM openjdk:8
ADD target/surveillance-analytics-*.jar surveillance-analytics.jar
EXPOSE 8092
RUN mkdir -p /opt/ssl
COPY srvlnc_prod.cer /opt/ssl/.
RUN keytool -keystore /usr/local/openjdk-8/jre/lib/security/cacerts -storepass changeit -noprompt -trustcacerts -importcert -alias srvlnc_prod_ssl -file /opt/ssl/srvlnc_prod.cer
ENTRYPOINT ["java", "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCGroupMemoryLimitForHeap", "-XX:MaxRAMFraction=2", "-XshowSettings:vm", "-jar", "app.jar"]

I've tried quite a few things, adding MaxRAMFraction, increasing memory on jenkins job:

docker create -m 4G --cpus=4 -e "CONFIG_PROFILE=sit"

Either thread goes to starvation or it throws GC overhead. Logs for thread starvation:

Thread starvation or clock leap detected (housekeeper delta=51s742ms299µs124ns)

Starvation doesn't end after 51s.

Memory consumption: Memory consumption

tez
  • 119
  • 1
  • 1
  • 14
  • 2
    The XSSF API is very memory consuming. When you just want to write the data, you may try [the streaming version of the API, SXSSF](https://poi.apache.org/apidocs/dev/?org/apache/poi/xssf/streaming/package-summary.html). – Holger Jul 21 '22 at 06:48
  • @Holger Thank you for the suggestion. I'll definitely use this next time. We've optimised the query to process data batch wise. – tez Jul 21 '22 at 08:28
  • Have you considered reading data in batches and write off the batch in a streaming way within the report? – Cristian Balint Aug 04 '22 at 07:34

0 Answers0