High CPU Usage with GroupDocs.Watermark for Java on EC2

I am currently using the GroupDocs.Watermark for Java library in a Java 8 environment, and I have encountered an issue where the CPU usage exceeds 100% on our EC2 when invoking the watermarking service. Below is a brief overview of the code implementation:

@RequestMapping(value = "{fileSecKey}/link.do")
    public ModelAndView link(@PathVariable String fileSecKey
            , @RequestParam(value = "ubiYn", required = false, defaultValue = "N") String ubiYn
            , @RequestParam(value = "inline", required = false, defaultValue = "false") boolean inline
            , @RequestParam(value = "thumbYn", required = false, defaultValue = "N") String thumbYn
            , @RequestParam(value = "reqUsrId", required = false, defaultValue = "USR_ID") String reqUsrId
            , @RequestParam(value = "reqUsrNm", required = false, defaultValue = "USR_NM") String reqUsrNm
            , @RequestParam(value = "reqCorpNm", required = false, defaultValue = "CORP_NM") String reqCorpNm
            , @RequestParam(value = "reqBrchNm", required = false, defaultValue = "BRCH_NM") String reqBrchNm
            , @RequestParam(value = "watermarkYn", required = false, defaultValue = "N") String watermarkYn
            , @RequestParam(value = "useCookieYn", required = false, defaultValue = "N") String useCookieYn
            , HttpServletRequest request
            , HttpServletResponse response) throws Exception {

        String transactionAId = (String) request.getAttribute(TRANSACTION_ID);
        FileItemVO fileItem = fileMgrService.selectFileItemBySecKey(fileSecKey);
        log.info("fileUpload.link.do -info " + transactionAId + " fileSecKey :" + fileSecKey + " FileItemVO is null :" + (fileItem == null));

        String filePath = "";
        if (StringUtils.hasText(fileItem.getRemark())) {
            if (fileItem.getRemark().equals("sal") || fileItem.getRemark().equals("crm")) {
                //ubiReport๋กœ ๋งŒ๋“ค์–ด์ง„ PDF์ธ ๊ฒฝ์šฐ
                if (ubiYn.equals("Y")) {
                    filePath = UploadPathType.valueOf(fileItem.getRemark()).getUploadPath() + fileItem.getFileData() + "." + fileItem.getFileTp();
                } else {
                    filePath = UploadPathType.valueOf(fileItem.getRemark()).getUploadPath() + fileItem.getFileData();
                }
            } else {
                filePath = UploadPathType.valueOf(fileItem.getRemark()).getUploadPath() + fileItem.getFileData();
            }
        } else {
            filePath = fileItem.getFileData();
        }

        log.info("file requested : " + filePath);
        DownloadVO downloadVO = null;
        File file = new File(filePath);
        //ํŒŒ์ผ์ด ๋ฏธ์กด์žฌ ํ•˜๋ฉด(๋ฐ์ดํ„ฐ๋งŒ ์กด์žฌ)
        if (!file.exists()) {
            downloadVO = FileUtils.getWdmsImageResource(fileItem, resourceLoader, FileUtils.ResourceFilePathType.DEL_FILE_IMG_PATH);
        }else {
            //์ธ๋„ค์ผ ํ˜ธ์ถœ ์ด๋ฉด ์ธ๋„ค์ผ ์ด๋ฏธ์ง€ ๋Œ€์ƒ ํ™•์ธ ํ›„ ์š”์ฒญ
            if (ThumbnailGeneratorUtil.IS_OK_IMG_CONTENT_TYPE.contains(fileItem.getFileTp())//์ด๋ฏธ์ง€ ๋Œ€์ƒ
                    && "Y".equals(thumbYn)) {
                downloadVO = thumbnailGeneratorUtil.reImgThumbFile(fileItem, filePath, resourceLoader);
            } else {
            	// watermarked file outputFilePath
            	String outputFilePath = filePath;

            	// ์›Œํ„ฐ๋งˆํฌ ์—ฌ๋ถ€ํ™•์ธ
            	if("Y".equals(watermarkYn)) {           
            	    long startTime = System.currentTimeMillis(); // ์‹œ์ž‘ ์‹œ๊ฐ„ ๊ธฐ๋ก

            		// PDF ํŒŒ์ผ์ผ ๊ฒฝ์šฐ ์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€
            		if ("application/pdf".equalsIgnoreCase(fileItem.getFileTp())) {
            			outputFilePath = watermarkService.addWatermarkToPdf(filePath, fileItem, reqUsrNm, reqUsrId, reqCorpNm, reqBrchNm);
            		}            		
            		// IMAGE ํŒŒ์ผ์ผ ๊ฒฝ์šฐ ์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€
            		else if (fileItem.getFileTp().startsWith("image/")) {
            			outputFilePath = watermarkService.addWatermarkToImage(filePath, fileItem, reqUsrNm, reqUsrId, reqCorpNm, reqBrchNm);
            		}
            		
            	    long endTime = System.currentTimeMillis(); // ์ข…๋ฃŒ ์‹œ๊ฐ„ ๊ธฐ๋ก
            	    long duration = endTime - startTime; // ์‹คํ–‰ ์‹œ๊ฐ„ ๊ณ„์‚ฐ

            	    log.info("์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€ ์ž‘์—… ์‹คํ–‰ ์‹œ๊ฐ„: " + duration + "ms");
            		
//                // PPT ํŒŒ์ผ์ผ ๊ฒฝ์šฐ ์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€
//                if ("application/vnd.ms-powerpoint".equalsIgnoreCase(fileItem.getFileTp()) ||
//                	    "application/vnd.openxmlformats-officedocument.presentationml.presentation".equalsIgnoreCase(fileItem.getFileTp())) {
//                	outputFilePath = watermarkService.addWatermarkToPpt(filePath, fileItem, reqUsrNm, reqUsrId, reqCorpNm, reqBrchNm);
//	            	}
            	}
                
                //์ธ๋„ค์ผ ์š”์ฒญ์ด ์•„๋‹Œ ํŒŒ์ผ
                downloadVO = new DownloadVO();
                // ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•œ ํŒŒ์ผ์˜ ํฌ๊ธฐ๋ฅผ ์‹ ๋ขฐํ•  ์ˆ˜ ์—†์–ด ํ•ด๋‹น ํŒŒ์ผ์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ์ง์ ‘ ํ™•์ธํ•˜๋„๋ก ์ˆ˜์ •
                long fileSize = (new File(outputFilePath)).length();
                downloadVO.setFileName(fileItem.getFileNm());
                downloadVO.setFilePath(outputFilePath);
                downloadVO.setFileSize(fileSize);
                downloadVO.setContentType(fileItem.getFileTp());
            }
        }
        
        // ํŒŒ์ผ์ด ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋‹ค์šด๋กœ๋“œ ์ฒ˜๋ฆฌ
        if (downloadVO != null && "Y".equals(useCookieYn)) {
            // Set-Cookie ํ—ค๋” ์ถ”๊ฐ€
        	response.setHeader("Set-Cookie", "fileDownload=true; path=/; Domain="+getDomain()+"; SameSite=None; Secure");
        }

        ModelAndView mav = new ModelAndView("downloadView");
        mav.addObject("FILE_INFO", downloadVO);
        mav.addObject("INLINE", inline);
        response.setHeader("Content-Type", fileItem.getFileTp() + "; charset=UTF-8;");

        return mav;
    }
    public String addWatermarkToImage(String filePath, FileItemVO fileItem, String userName, String userId, String corpNm, String brchNm) {
        String outputFilePath = filePath;
        ImageLoadOptions loadOptions = new ImageLoadOptions();
        Watermarker watermarker = null;

        try {
            watermarker = new Watermarker(filePath, loadOptions);

            String dateFormat = new SimpleDateFormat("yy-MM-dd").format(new Date());
            String baseText = String.format("%s_%s_%s_%s", corpNm, brchNm, userName, dateFormat);

            TextWatermark footerTextWatermark = new TextWatermark(baseText, new Font("BMWTypeNext Kr TT Regular", 15));
            footerTextWatermark.setForegroundColor(Color.getGray());
            footerTextWatermark.setHorizontalAlignment(HorizontalAlignment.Left);
            footerTextWatermark.setVerticalAlignment(VerticalAlignment.Bottom);
            footerTextWatermark.setOpacity(0.8);

            watermarker.add(footerTextWatermark);

            // ํŒŒ์ผ ํ™•์žฅ์ž ์ถ”์ถœ
            String extension = "";
            int i = fileItem.getFileTp().lastIndexOf('/');
            if (i > 0) {
                extension = fileItem.getFileTp().substring(i + 1).toLowerCase();
            }

            outputFilePath = filePath + "_watermarked." + extension;
            watermarker.save(outputFilePath);

        } finally {
            if (watermarker != null) {
                watermarker.close();
            }
        }

        return outputFilePath;
    }

I would appreciate any guidance or recommendations you could provide to help reduce the CPU usage during the watermarking process. Are there any specific optimizations or configurations within the GroupDocs.Watermark library that could help mitigate this issue?

Thank you for your assistance.

1 Like

@Kim_KyungMok

To better assist you, could you please clarify a couple of points?

  1. Are you experiencing high CPU usage with specific files, or does it occur with all files when using the watermarking service?
  2. If itโ€™s with specific files, could you share those files with us? This will help us in diagnosing the issue more effectively.
  3. Do you have a valid GroupDocs.Watermark for Java license, and have you applied it in your project, or are you just evaluating the API in trial mode?
  4. Additionally, could you provide the details of your development environment on the EC2 instance? Specifically, EC2 instance type, and any relevant configurations.
  5. Please share the GroupDocs.Watermark for Java API version that you are using.
1 Like

@atir.tahir

  1. It is not just with specific files; the high CPU usage occurs generally. It seems to be a problem especially with files larger than a few MB.
  2. Since itโ€™s not specific files, sharing them might not be very meaningful.
  3. We have purchased the license and applied it to the project.
  4. The instance type is m5.xlarge, and the operating system is Linux. If you need any additional information, please let me know.
  5. We are using GroupDocs.Watermark for Java API version 24.11.

Thank you for your response. Please review and let us know your thoughts.

@Kim_KyungMok
We have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): WATERMARKJAVA-148

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

1 Like

Hi @Kim_KyungMok ,
Could you please confirm if the high CPU usage youโ€™re experiencing happens specifically when processing image files? I ask because your initial message also mentioned a method called addWatermarkToPdf, but the code you shared was only for addWatermarkToImage.

If the issue is isolated to image files, can you also confirm whether the high CPU usage occurs specifically during the execution of this line:

watermarker.add(footerTextWatermark);

This will help us ensure that the CPU load is directly related to the watermarking operation and not other parts of the code.

I performed a quick test by calling addWatermarkToImage in a loop on a JPG image of about 10MB in size and wasnโ€™t able to reproduce the high CPU usageโ€”watermarking was completed quite fast.

Looking forward to your confirmation so we can continue investigating further.

1 Like

Hello, @alexndr

It seems that the watermarker.add method is consuming a significant amount of CPU resources. Here is the CPU usage before and after the watermark addition:

  • Before the call: 1.7% CPU usage
  • After the call: 98.9% CPU usage

A single watermark addition operation causes a substantial increase in CPU usage, indicating that the image processing task is quite CPU-intensive.

We have observed similar CPU usage levels when adding watermarks to both PDFs using watermarker.add(textWatermark); and images with watermarker.add(footerTextWatermark);.

I would appreciate any guidance or suggestions you might have on how we could potentially parallelize these operations or reduce the load.

Additionally, I have attached the source code for adding a watermark to PDFs for your reference.

Thank you.

    @Override
    public String addWatermarkToPdf(String filePath, FileItemVO fileItem, String userName, String userId, String corpNm, String brchNm) {
        String outputFilePath = filePath;

        PdfLoadOptions loadOptions = new PdfLoadOptions();
        Watermarker watermarker = new Watermarker(filePath, loadOptions);

        try {
            // ์›Œํ„ฐ๋งˆํฌ ํ…์ŠคํŠธ ์„ค์ •
            String dateFormat = new SimpleDateFormat("yy-MM-dd").format(new Date());
            String baseText = String.format("%s_%s_%s_%s", corpNm, brchNm, userName, dateFormat);
            PdfArtifactWatermarkOptions textWatermarkOptions = new PdfArtifactWatermarkOptions();
            
            TextWatermark textWatermark = new TextWatermark(baseText, watermarkFont);
            textWatermark.setForegroundColor(Color.getGray());
            textWatermark.setOpacity(0.1);
            textWatermark.setRotateAngle(-10);

            // ํƒ€์ผ ์˜ต์…˜ ์„ค์ •
            TileOptions tileOptions = new TileOptions();
            tileOptions.setLineSpacing(new MeasureValue());
            tileOptions.setWatermarkSpacing(new MeasureValue());
            textWatermark.setTileOptions(tileOptions);

    	    long startTime = System.currentTimeMillis(); // ์‹œ์ž‘ ์‹œ๊ฐ„ ๊ธฐ๋ก
    	    // ์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€
    	    watermarker.add(textWatermark);

    	    long endTime = System.currentTimeMillis(); // ์ข…๋ฃŒ ์‹œ๊ฐ„ ๊ธฐ๋ก
    	    long duration = endTime - startTime; // ์‹คํ–‰ ์‹œ๊ฐ„ ๊ณ„์‚ฐ
            
            log.info("watermarker.add(textWatermark): " + duration + "ms");

            // ํŒŒ์ผ ํ™•์žฅ์ž ์ถ”์ถœ
            String extension = "";
            int i = fileItem.getFileTp().lastIndexOf('/');
            if (i > 0) {
                extension = fileItem.getFileTp().substring(i + 1).toLowerCase();
            }

            outputFilePath = filePath + "_watermarked_" + UUID.randomUUID().toString() + "." + extension;
            //outputFilePath = filePath + "_watermarked." + extension;
            watermarker.save(outputFilePath);
            
        } finally {
            watermarker.close();
        }

        return outputFilePath;
    }

    @Override
    public String addWatermarkToImage(String filePath, FileItemVO fileItem, String userName, String userId, String corpNm, String brchNm) {
        String outputFilePath = filePath;
        ImageLoadOptions loadOptions = new ImageLoadOptions();
        Watermarker watermarker = null;

        try {
            watermarker = new Watermarker(filePath, loadOptions);

            String dateFormat = new SimpleDateFormat("yy-MM-dd").format(new Date());
            String baseText = String.format("%s_%s_%s_%s", corpNm, brchNm, userName, dateFormat);

            TextWatermark footerTextWatermark = new TextWatermark(baseText, new Font("BMWTypeNext Kr TT Regular", 15));
            footerTextWatermark.setForegroundColor(Color.getGray());
            footerTextWatermark.setHorizontalAlignment(HorizontalAlignment.Left);
            footerTextWatermark.setVerticalAlignment(VerticalAlignment.Bottom);
            footerTextWatermark.setOpacity(0.8);

            
    	    long startTime = System.currentTimeMillis(); // ์‹œ์ž‘ ์‹œ๊ฐ„ ๊ธฐ๋ก

            watermarker.add(footerTextWatermark);
            
    	    long endTime = System.currentTimeMillis(); // ์ข…๋ฃŒ ์‹œ๊ฐ„ ๊ธฐ๋ก
    	    long duration = endTime - startTime; // ์‹คํ–‰ ์‹œ๊ฐ„ ๊ณ„์‚ฐ
            
            log.info("watermarker.add(footerTextWatermark) : " + duration + "ms");

            // ํŒŒ์ผ ํ™•์žฅ์ž ์ถ”์ถœ
            String extension = "";
            int i = fileItem.getFileTp().lastIndexOf('/');
            if (i > 0) {
                extension = fileItem.getFileTp().substring(i + 1).toLowerCase();
            }

            outputFilePath = filePath + "_watermarked_" + UUID.randomUUID().toString() + "." + extension;
            //outputFilePath = filePath + "_watermarked." + extension;
            watermarker.save(outputFilePath);

        } finally {
            if (watermarker != null) {
                watermarker.close();
            }
        }

        return outputFilePath;
    }

To further investigate the CPU resource issue, I upgraded the EC2 instance from m5.xlarge to m5.2xlarge. I prepared a 10MB file with a resolution of 6000x4000 and conducted several tests:

  • 10MB file with 6000x4000 resolution: CPU processing failed
  • 700KB file with 6000x4000 resolution (reduced quality): CPU processing failed
  • 4MB file with 3000x2000 resolution: Processing possible
  • 600KB file with 1200x800 resolution: Processing somewhat smooth

Based on these tests, it seems that a resolution of around 6000 results in processing failure, similar to my experience. Could you please confirm if this is expected behavior? Additionally, if there are any settings I might have configured incorrectly or options I could try, I would appreciate your guidance.

Thank you.

hi @Kim_KyungMok ,
Thank you for the detailed information.

Would it be possible for you to share the 10MB file with 6000x4000 resolution so we can try to reproduce the issue on our side under similar conditions?

As for the settings, based on the code sample you previously shared, everything looks correctโ€”there doesnโ€™t appear to be anything misconfigured.

Looking forward to your file so we can continue the investigation.

1 Like

hi, @alexndr

Most cloud sharing services are inaccessible at our company, so I am sharing the GitHub repository link with you. Please extract the zip file from there to review the test files.

Thank you.