Ok, so I've done a little bit of basic profiling: "5eShapedCompanion 1488618858086 DEBUG : Campaign size: 73323"
"1488618858177 Getting attributes for initialize.sheetOpened"
"1488618858579 Got attributes for initialize.sheetOpened"
"1488618858580 Processing start for initialize.sheetOpened"
"1488618858581 Getting repeating sections for initialize.generateSkills"
"1488618858582 Getting attributes for abilities.update"
"1488618858583 Getting repeating sections for abilityChecks.updateMacro"
"1488618858583 Getting attributes for abilityChecks.updateInitiative"
"1488618858584 Getting attributes for savingThrows.updateMacro"
"1488618858584 Processing end for initialize.sheetOpened"
"1488618858585 Setting attrs for initialize.sheetOpened"
"1488618858596 Got repeating sections for initialize.generateSkills"
"1488618858596 Getting attrs for repeating sections for initialize.generateSkills"
"1488618858604 Got attributes for abilities.update"
"1488618858610 Processing start for abilities.update"
"1488618858613 Processing end for abilities.update"
"1488618858613 Setting attrs for abilities.update"
"1488618858616 Got repeating sections for abilityChecks.updateMacro"
"1488618858616 Getting attrs for repeating sections for abilityChecks.updateMacro"
"1488618858625 Got attributes for abilityChecks.updateInitiative"
"1488618858625 Processing start for abilityChecks.updateInitiative"
"1488618858626 Processing end for abilityChecks.updateInitiative"
"1488618858626 Setting attrs for abilityChecks.updateInitiative"
"1488618858633 Got attributes for savingThrows.updateMacro"
"1488618858634 Processing start for savingThrows.updateMacro"
"1488618858636 Processing end for savingThrows.updateMacro"
"1488618858636 Setting attrs for savingThrows.updateMacro"
"1488618858662 Finished setting attributes for initialize.sheetOpened"
"1488618858670 Got attributes for repeating sections initialize.generateSkills"
"1488618858670 Processing start for repeating sections initialize.generateSkills"
"1488618858673 Processing end for repeating sections initialize.generateSkills"
"1488618858673 Setting attrs for initialize.generateSkills"
"1488618858876 Finished setting attributes for abilities.update"
"1488618858888 Got attributes for repeating sections abilityChecks.updateMacro"
"1488618858888 Processing start for repeating sections abilityChecks.updateMacro"
"1488618858889 Processing end for repeating sections abilityChecks.updateMacro"
"1488618858889 Setting attrs for abilityChecks.updateMacro"
"1488618858912 Finished setting attributes for abilityChecks.updateInitiative"
"1488618861755 Finished setting attributes for savingThrows.updateMacro"
"1488618862092 Set attributes for repeating sections initialize.generateSkills"
"1488618862093 Getting repeating sections for abilityChecks.updateSkill"
"1488618862115 Set attributes for repeating sections abilityChecks.updateMacro"
"1488618866487 Got repeating sections for abilityChecks.updateSkill"
"1488618866487 Getting attrs for repeating sections for abilityChecks.updateSkill"
"1488618866670 Got attributes for repeating sections abilityChecks.updateSkill"
"1488618866674 Processing start for repeating sections abilityChecks.updateSkill"
"1488618866676 Processing end for repeating sections abilityChecks.updateSkill"
"1488618866676 Setting attrs for abilityChecks.updateSkill"
"1488618867021 Set attributes for repeating sections abilityChecks.updateSkill"
"1488618867021 Getting repeating sections for abilityChecks.updateMacro"
"1488618871126 Got repeating sections for abilityChecks.updateMacro"
"1488618871126 Getting attrs for repeating sections for abilityChecks.updateMacro"
"1488618871157 Got attributes for repeating sections abilityChecks.updateMacro"
"1488618871158 Processing start for repeating sections abilityChecks.updateMacro"
"1488618871159 Processing end for repeating sections abilityChecks.updateMacro"
"1488618871159 Setting attrs for abilityChecks.updateMacro"
"1488618871337 Set attributes for repeating sections abilityChecks.updateMacro" For reference, the relevant part of the function that kicks this all of is: this.generateSkills();
abilities.updateAll();
abilityChecks.updateMacro();
abilityChecks.updateInitiative();
savingThrows.updateMacro(); generateSkills has a setAttrs callback at the end of it, which calls abilityChecks.updateSkill(). That should be enough to explain the sequencing of the above trace. The first line is output immediately before I set the attribute that triggers the subsequent sheetworker activity. There's a delay of 91ms before the first sheetworker activity, which is a bit on the high side, but not disastrous. What comes next, though, looks like a problem. It takes 402ms to return a simple getAttrs call (requesting 13 attributes, none of which exist yet, I don't believe). Interestingly, the next getAttrs calls all return quite quickly. Is the initial delay the caching of attributes etc happening lazily? If so, when exactly does this happen? Is it a per-campaign thing, or a per-character thing? Happy to introduce a delay to work around it, unless perhaps you can factor this into your timeout calculations for onSheetWorkerCompleted as well? There's then another big delay (200ms) before "Finished setting attributes for abilities.update", and then a HUGE, nearly 3 second delay before "Finished setting attributes for savingThrows.updateMacro". The original setAttrs call for that happened at 58636, and the callback comes at 61755 - which is 3119ms later! As we look further through the trace, there other huge gaps of several seconds. All of the sheet logic/processing happens between the "processing start" and "processing end" lines - and as you can see, this is never more than a few milliseconds. The delays appear to be on stuff returning from the server still. There doesn't seem to be any particular pattern to which calls take lots of time. For example, by the end of the trace, the only thread still going on is abilityChecks.updateMacro). There's a >4s delay (!) simply requesting repeating sections using getSectionIDs with nothing else going on. That's crazy! But other getSectionId calls return in a few tens of ms, so it doesn't look like a systematic problem with that call. Judging by the trace alone, I'd have to guess that either (1) there's a server under unreasonable load somewhere in the system that is just grinding to a halt every few calls and causing everything to back up or (2) there's some sort of expensive, intermittent process that is kicking in on the API sandbox and making everything else wait every so often - perhaps something to do with cache management given that this seems worse for large campaigns? As usual, I'm stabbing in the dark without darkvision here; realistically if we want to solve this properly I think it's going to involve you guys introducing some profiling at your end, because it looks like there are still some very large delays going on there somewhere. Without wanting to teach my grandmother to suck eggs, it's been my experience that attempting to fix performance problems by tweaking the "obvious culprits" without actually profiling the system is almost always a waste of energy; it usually turns out that it's the thing that you least expected that's blocking everything else. Putting the logging in is dull, I know, but it's less dull than playing whack-a-mole with a performance bug that rumbles on for months and months. I'm also aware that a lot of energy has gone into this feature now, which is pro-only and has largely been driven by a few script/sheet authors. Nonetheless, as I said before, a lot of other users do get value out of these scripts/sheets + I'm confident that many of these performance improvements will have an impact on everyone's Roll20 experience - and, in the long run, your hosting bills! So I hope that it will continue to seem worthwhile trying to nail this stuff down! :-) Cheers,