1

There are quite a number of posts I went through that talk about 'Apex CPU Time limit exceeded' issue. I am looking for some advice on how to work around this problem with my situation in mind.

We have a custom VF page with a custom controller that does a LOT of computations and displays a page/PDF with 6 pages of complex data based on ONE record/entity. This part works fine.

Now I need to generate ONE PDF for multiple records (stacked). So, for example, if the user chooses 5 records, the PDF must contain 30 pages of data.

To accomplish this, all I did was to write a for-loop on the method that works on one for as many records as selected. This is where I am hitting 'Apex CPU Time limit exceeded' issue.

The code is fairly complex (5 SOQL statements but a lot of loops, 7000 lines) and although we put in effort to optimize it, re-optimizing it could be a herculean task which won't be approved. Even if we were successful with a little more optimization, there is no guarantee that something that works for 5 records (= 30 pages) now wouldn't fail for 7 (= 42 pages).

Salesforce support won't increase our limits.

I understand that the CPU limit on asynchronous transactions is 6 times higher than synchronous. So I followed this blog post but it still doesn't work for us. Reason: Although I have a REST Consumer method that is declared as @future (CPU limit = 60,000 ms); it calls a global RESTful web service setup in Salesforce (@HttpPost) that is responsible for generating the PDF, and it appears like the latter is still slapped with 10,000 ms of CPU time! Neither the @future methods nor the Batch jobs support getContentAsPDF() method.

So: I would like to utilize the 60,000 ms CPU time in an asynchronous transaction to generate a massive VF PDF and email it out to a user. What are the possible ways to accomplish that? I am really out of ideas.

Thanks in advance.

  • If the number of records that can be selected is open ended then even if you did manage to find a way to have the 60 second limit applied some selections could exceed that. Also bear in mind the 5M byte limit per email attachment. If you want to stay within the platform compromising on the requirement - e.g. generating a separate PDF per record via a batchable - might be necessary. Otherwise the complexity of calling our to an external PDF service that you create in e.g. Heroku may be needed. – Keith C Jul 28 '15 at 20:27
  • Right now it hits the CPU limit after 4 records. I can convince the users to limit their entry to at most 10 records if need be (or 15 if it works). That should be well under 60,000ms asynchronous limit. But a separate PDF for each is not an option. My point is, I would like to use the full 60,000ms potential asynchronous CPU time, whatever number of records that supports. Because I know at that point that the platform just doesn't support it. Right now I dont know how to get past the 10,000ms limit and utilize the extra that asynchronous transactions offer. Do you? – Lightning Evangelist Jul 28 '15 at 20:39
  • 1
    No I don't, hence my comment. This eventually non-optional change is also rolling out which may have a positive or negative effect on your solution. – Keith C Jul 28 '15 at 20:48
3

So we have a PDF that generates 30-60 PDF's with statistical analysis and graphs. What we did was:

  1. Create a custom object to hold the static html data (you may not need to do this but we wanted a way to change the text without modifying the VF page
  2. A object to hold the calculated data and items needed to piece it all together
  3. Created a controller that pulled it all together.

It will take a bit of rethinking but if you can leverage an object to hold the calculations and break up the processing a bit it may help.

As for creating a PDF from a batch, check out BatchPDF on the app exchange.

  • You could also check into leveraging heroku to generate your pdf and return it as well.

Without reviewing you code / org / requirements it will be hard to give a more precise answer and you may want to reach out to a consulting partner...

  • What if someone changes the inputs? When/ how often do you update the saved data ? – Lightning Evangelist Jul 28 '15 at 17:53
  • 1
    It all depends on your specific situation. Out "text" inputs do not change, and if they do, the saved PDF's retain the old data as that is what our customers were given at the time. The calculated data in the middle object could also be transient and deleted after the pdf is created. Again, it all depends on your business requirements, object model, etc. This is not a small ask, especially without any code or insight into the process. – Eric Jul 28 '15 at 18:35
  • I looked into Heroku but it sounds like we need to rewrite the entire code in a different programming language and build web services to communicate between Heroku <-> Salesforce. That's too much just for this one feature I think. I will check out BatchPDF app, but it will be nice to make sure I have exhausted all my options before convincing the management to spend more money on this. I will wait for more responses before I look for pre-built apps that could solve this. Thank you for taking the time to answer :) – Lightning Evangelist Jul 28 '15 at 19:58
1

To deal with the getContentAsPdf() issue I once resorted to a very tedious workaround: utilize Send Email in Workflow to call up a VF email template whose controller can then generate the PDF. It turns out Apex can access VF page content when invoked as part of the Send Email action. To do this you will need:

  1. a Custom object (call it WF Trigger) which you can insert/update once data is ready for your VF page (when batch jobs have finished)
  2. a WF rule on add/edit of WF_Trigger__c that sends an email (recipient set to a dummy user)
  3. a VF Email Template whose controller calls getContentAsPdf()

Now to be honest I did this a long time ago. You may need to also use an attachment in your email, then have the attachment generated via a VF Component whose controller calls getContentAsPdf(). But hopefully you won't need all this extra stuff and #3 will work.

Btw my use-case involved uploading a report as CSV to an external server and/or emailing it to specific recipients, all within the Schedulable context. Obviously getContent() was not working until I had the WF in place.

  • This sounds more like an answer to 'how to make getContentAsPDF() method work within a Schedulable context' and not how to generate a 'massive' PDF in Salesforce without running into CPU time limits. In other words, 'How can we generate a PDF that takes somewhere between 10,000ms to 60,000ms of CPU time to finish'. Email, batch job, web services, ... doesn't matter how. I am open to doing anything. Just looking for a way. – Lightning Evangelist Jul 28 '15 at 19:51
  • Use batch jobs to do your time consuming logic (you can chain them in case you only want to take advantage of the higher timeout limit). Then on finish insert a new WF_Trigger__c record with your result in it. When WF runs, it sends an email, enabling you to call getContent(). Obviously the VF page you are trying to render as a large PDF would query for the result from the above custom record. – Mossi Jul 28 '15 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.