5

I have a recursive script that hits a System.Management.Automation.ScriptCallDepthException exception.

What is the call depth limit in Sitecore PowerShell and can it be overridden?

Note: Yes, I can probably refactor to avoid recursion.

EDIT: For a little clarity, it's a script to copy items from one site to another, maintaining "localized" links based on the two sites having parallel structures. The outline looks like this in pseudocode:

Queue1 = System.Collections.Generic.Queue<Sitecore.Item>
Queue2 = System.Collections.Generic.Queue<Sitecore.Item>

Function HandleQueues()
    If Queue1 > 0
        ProcessQueue1
        HandleQueues
    Else If Queue2 > 0
        ProcessQueue2
        HandleQueues
    End if
Return

Function ProcessQueue1()
    Do while Queue1 > 0
        Pop Top item
        Do stuff with it
        Add it to Queue2
    Loop
Return

Function ProcessQueue2()
    Pop Top item
    Do stuff with it
    Find if it has links to anything that needs to go into Queue1 and add them
Return

Obviously there's plenty of scope for possible infinite loops, but I've handled pretty much all I can think of, and going through the script output I can't see any repetition. I'm pretty sure the code is logically sound, if a little brutal with building a call stack, so the question remains a technical one.

  • I think that you've got a recursion that cannot find an exit point. Is it possible? – Alan Płócieniak Feb 19 '18 at 16:05
  • I don't think so, and I don't see any repetition in the script output. It runs fine for a small amount of items. The recursive function handles two queues - each of which can add items to the other, but I've closed the possible infinite loop between them. – James Walford Feb 19 '18 at 16:13
  • And you're certain, adding items to either queue does not reset the enumerator? – Mark Cassidy Feb 19 '18 at 17:29
  • Yes. The recursive function doesn't enumerate - just checks queue A and queue B to see if they have items in, then calls functions to process the queues (the first is enumerated, the second pops one item). After checking which queue to process it calls itself. If both queues are empty it exits. It works perfectly on small sets. – James Walford Feb 20 '18 at 18:13
6

There are two points to consider.

Call Depth Limit

Windows Powershell may have a call depth limit of 100. Consider changing your code to use tail-recursion trampolines as pointed out in the article instead.

Not this kind of trampoline:

imgur

This kind of trampoline:

$stack = New-Object System.Collections.Stack

#Load first level
Get-ChildItem -Path "master:" -ID "{0DE95AE4-41AB-4D01-9EB0-67441B7C2450}" | ForEach-Object { $stack.Push($_) }

Write-Host "Beginning processing of stack."
while($stack.Length -and ($item = $stack.Pop())) {
    Write-Host "Processing item $($item.ID) - $($item.Name)"
    Get-ChildItem -Path "master:" -ID $item.ID | ForEach-Object { $stack.Push($_) }
}

Write-Host "Completed processing stack."

Results

Recurse Depth for Get-ChildItem

In version 4.7 of SPE (came out sooner) you can set the depth limit while calling Get-ChildItem.

Get-ChildItem -Path "master:\content\home" -Recurse -Depth 1

Screenshot

Note: Updated statement above regarding tail recursion based on the comment below and the fact the linked article says to...I must not have read it properly the first time.

  • That's useful, but it's not in a Get-Item call, unfortunately. – James Walford Feb 20 '18 at 18:15
  • Yes, but Get-Item does not perform a recursive call either. Perhaps you can update the question with a sample script to help clarify what script is revealing the issue. – Michael West Feb 20 '18 at 18:31
  • Sorry, I maent Get-ChildItem, not Get-Item. But yes, I'll try and provide a bit more meat on the bones of the question. It's certainly not an issue with any commands or such, and I'm 99% sure I don't have an infinite loop. It's purely my code structure hitting a limit. – James Walford Feb 20 '18 at 18:40
  • I've fixed my code now, but just to note that PowerShell doesn't support tail recursion optimisation (this would allow simply moving the recursive call to the last line of the function). I used a very simple trampoline pattern outlined in the linked article: The once-was-recursive function now returns true if it wants to be called again, and a simple do while loop calls it until it returns false. – James Walford Feb 23 '18 at 9:59
  • Nice update! Hoping for Microsoft to launch Marsupial View Controller pattern soon. – James Walford Feb 26 '18 at 11:48

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.