-
Notifications
You must be signed in to change notification settings - Fork 238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Issue #196 - Add support for specifying CalculationMode in developer API #208
Conversation
Thanks for this! Curious, what was your use case / motivation for this? |
…o be calculated via developer API Due to having depdencies on fflib, DLRS should be installed as a package. In order to invoke forced realtime calculation, expose global developer API.
Hi Andrew - I just updated the pull request per your guidance in #209. The short version of the use case is that we are processing large quantities of records in our API transactions (e.g. more than 200 of a single SObject type) and this results in validations on SObjects failing in certain cases (e.g. SUM(Child.Qty) == Parent.TotalQty) because all records are not yet complete. Part of the solution to solve for this involves deferring the rollups from occurring until all DML operations are complete. Doing this provides two benefits:
More details on the background of the use case can be found at http://salesforce.stackexchange.com/questions/81350/validate-data-as-unit-of-work/81712#81712. (See the post from me at the bottom from 6/29 @ 22:45Z). Coupled with the changes proposed in apex-enterprise-patterns/fflib-apex-common#62, I'm able to defer all rollups from firing within the trigger context and instead keep track of all SObjects involved in the Execution Context and then force rollups to occur after all DML is complete. The flip side to this is that I still need rollups to fire in real-time within trigger context when the operation is not coming from an API interaction (e.g. standard page layout). This is ultimately the goal of the original issue I posted (making calc mode a multi-picklist). The solution in this PR is really a "compromise" to what I'd proposed the real enhancement be. Unfortunately I'd want to come up to speed more on DLRS library to evaluate the potential impacts before making that change and would need more time to complete. Assuming you feel that making calc mode a multi-picklist makes sense, I'll try to work towards making that change in the near future (or if someone else has the time :)). |
I'm hoping to keep Realtime mode focused on the dynamically deployed triggers, e.g. the triggerHandler entry point. The Developer mode on the DLRS definition is what i really intended for this kind of use case. I think whats driven you here and your other PR is that really the dlrs.RollupService.rollup method didn't do what you want. Would focusing on adding new dlrs.RollupService.rollup method that works exactly like trigerHandler work for you here? |
If I'm understanding where you are going with having a dlrs.RollupService.rollup method that works exactly like triggerHandler I think you're saying that it would do the update itself rather than return the result for the caller to be updated and also do things like handling different SObject types provided in the parameter list? If I'm misunderstanding let me know. Assuming that's the case, in my scenario it unfortunately wouldn't provide what I need. In short, I need to be able to run a rollup in both "Realtime" and "Developer" mode. Here's an example: A) User goes to a standard layout to update an Account. When they click save the rollup would fire immediately using trigger context (Realtime) Hope this makes sense, sorry if I'm misunderstanding what you were thinking with dlrs.RollupService.rollup. |
Just read #165 per your comment in #215, I think I see where you are going now - sorry for delayed reaction ;) Here's my current understanding and how it would apply to the scenario described in my previous post. Add an API that behaves like triggerHandler that would handle multiple SObject types, detect changes from old/new and perform the actual update. Essentially it's the same as triggerHandler except it gets its data from parameters instead of Trigger context. For my scenario, all my rollups would be marked Developer and instead of calling triggerHandler in my trigger I would call the new developer API in both scenario "A" and "B". Correct? If so, I think this would work well and a great solution to what I'm after. The only other thing I would need is to make sure that the new developer API can "force" an update to occur. In other words, skip any "change detection" and just always force the rollup to occur. The reason for this is that within a "transaction" an SObject can be updated multiple times and outside of a trigger context, old/new isn't viable to rely on. For this reason, the new developer api could have two flavors one that just takes the SObject list and one that takes SOBject lists for both old/new. Now that I think about it more, I could actually further optimize my side if this was exposed to take advantage of change tracking by tracking the original "old" from trigger. Sidebar thought, just thinking through how I'd approach if this was available. Let me know if I'm understanding what you were driving at correctly and your thoughts. Thanks! |
I put together a very rough skeleton of what I think you're thinking. This by no means is intended to be "PR" ready, just a baseline to help the discussion along. See RollupService.cls at https://github.com/jondavis9898/declarative-lookup-rollup-summaries/blob/3e99e897dbe75b69f1b12ac323a0a0c90b84e579/rolluptool/src/classes/RollupService.cls. |
Yes if its this one...
Though i would pass on CalculationMode.Developer to it, as per the current rollup global static List rollup(List childRecords). |
Hi Andrew - Yes, the api that you referenced is the new one that I was thinking. Not sure I follow what you mean by "pass on CalculationMode.Developer" to it? Do you mean have it accept CalculationMode as a parameter or only specifiy Developer when it calls handleRollups or something else? If the former, I can't add CalculationMode to the method signature without changing RollupSummaries class becuase CalculationMode is public not global. If the later, is there a reason that you don't think Scheduled should be included? Or if neither of those and you meant something totally different, let me know :) The full skeleton is in commit jondavis9898@d71861a. In there I mocked up the following changes:
Essentially, calling triggerHandler or new developer API would result in exactly the same functionality, the only difference would be "where" it gets childRecords & existingRecords from. Let me know your thoughts and if you think there is anything inside of handleRollups that might cause a regression when invoked via Developer API directly. I'm still coming up to speed on the library so don't want to overlook anything. Thanks! |
Yes this is pretty close, sorry ment to say 'Pass only CalculationMode.Developer to it', so yes i ment 'only specifiy Developer when it calls handleRollups'. |
Thanks for clarifying Andrew. Is there a reason why you don't think Scheduled should be included? In the declaritive case, Realtime and Scheduled are included. If I'm understanding your original intent based on my proposed addition, it's to be able to invoke identical functionality but in a non-declarative manner (e.g. not have to deploy the trigger that is required in Realtime mode). Along those lines, it's essentially just moving the call to triggerHandler in to another area in the code (e.g. inside of an SObjectDomain class for example). In this case, I would think including Scheduled would be preferred so that the net result is exactly the same as using triggerHandler via declaritive mode. Very possible I'm not fully understanding the nuances of Scheduled. If you could share why you think Scheduled should not be included I'd appreciate it. Thanks! |
I'm basically wanting to keep the rollups defined by admins separate from those defined by developers to be used by the API. This way things don't end up getting processed twice if a developer ends up calling the API in the same execution context as one of the standard DLRS triggers on a child object. This essentially means Developer mode is always realtime, the thinking is if a developer thought they needed to do the query in batch they would do so in their batch context. |
Ah, gotcha, that makes sense, thanks for the clarification. Based on your familiarity, can you see a use case where via the Developer API, there would be a need to have an async/scheduled solution (e.g. avoid governor limits, etc.)? If no current use case, possibly we just cross that bridge if/when it ever comes up? |
Yeah, probably be another API to push a schedule item. |
See Issue SFDO-Community#196 and PR SFDO-Community#208 for background
Closing this as PR #221 replaces and targets approach discussed. |
This is not the original desired result of the issue which would be to completely separate the "When" (calculationmode) from the "What" (rollup definition). After reviewing the code, a change of that nature would be fairly invasive and honestly and unfortunately, take more time than I have available right now.
As a compromise, I've exposed a public method that allows the modes to be specified with the previously existing global overload maintaining backwards compat and only executing Developer mode rollups. Since CalculationMode is a public enum in a public class, I was unable to make the new rollup overload global so calling this method does require that the code be installed and not the package. An unfortunate side-effect.
I think the end goal here should be to separate the "when" and "what" concerns but hopefully this is acceptable approach for the time being.