1

I'm trying to execute a PowerShell script which retirves Exchange Online mailboxes. When running the script separately in PowerShell prompt and running the script via JAVA class file (in eclipse), the time taken is 5 minutes. But when invoking the script inside our application which is multi threaded JVM environment with huge heap memory, it takes almost double the time.

We are using ProcessBuilder to invoke the script and read the InuptStream. Only the script execution time is higher and parsing time is fast enough in JAVA side. If any extra info needed, feel free to comment.

I'm quite new to JAVA ProcessBuilder. So, still trying to understand some concepts.

I tried to trobleshoot in our environemnt but the issue seems to be of PowerShell side. I seacrhed everywhere about this but couldn't find any.

Edit: We are fetching mailboxes and processing to sort top 10 mailboxes based on usage and top inactive mailboxes in desired format. Please find the PowerShell snippet requested below:

Import-Module ExchangeOnlineManagement -ErrorAction Stop -WarningAction SilentlyContinue
Connect-ExchangeOnline -ShowBanner:$false -Credential $credential -ErrorAction Stop -WarningAction SilentlyContinue
$mailboxes = $null
if ($metricsList -contains "InactiveMailboxes" -or $metricsList -contains "TopMailboxes") {
$mailboxes = Get-EXOMailbox -ResultSize Unlimited -WarningAction SilentlyContinue | Where-Object { (!$_.Name.startswith("SystemMailbox")) -and (!$_.Name.startswith("FederatedEmail")) -and (!$_.Name.startswith("DiscoverySearchMailbox")) } | select -expand userprincipalname | Get-EXOMailboxStatistics -Properties DisplayName,ItemCount,TotalItemSize,LastLogonTime -WarningAction SilentlyContinue}

$inactivemailbox = $mailboxes | Where-Object { $_.LastLogonTime -ne $null } | Where-Object { $_.LastLogonTime -lt (Get-Date).AddDays(-30) } | Sort-Object LastLogonTime -Descending | Select-Object -First 100 | Select-Object DisplayName,LastLogonTime | ConvertTo-Csv -Delimiter "|" -NoTypeInformation | ForEach-Object { $_.Replace('"','') } | Select-Object -Skip 1

$topmailbox = $mailboxes | Sort-Object TotalItemSize -Descending | Select-Object -First 10 | Select-Object DisplayName,@{ Name = "TotalItemSizeMB"; expression = { [math]::Round(($_.TotalItemSize.ToString().split("(")[1].split(" ")[0].Replace(",","") / 1MB),2) } },ItemCount | ConvertTo-Csv -Delimiter "|" -NoTypeInformation | ForEach-Object { $_.Replace('"','') } | Select-Object -Skip 1

Java Code snippet: We are framing the script execution for ProcessBuilder like below for security reasons:

String POWERSHELL_PATH = System.getenv("SystemRoot") + File.separator + "System32" + File.separator + "WindowsPowerShell" + File.separator + "v1.0" + File.separator + "powershell.exe";
ProcessBuilder pb = new ProcessBuilder(POWERSHELL_PATH);
List args = [-ExecutionPolicy, Bypass, -NoLogo, -NonInteractive, -NoProfile, -WindowStyle, Hidden, "&{&, 'Script_With_Path', 'Comma separated args' }"]; // Sample values
pb.command().addAll(args);

Reading the InputStream as below:

BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
while((str = br.readLine()) != null) {
    if(str.indexOf("Script execution time was exceeded")!=-1) {
        outputBuff.append("Error # Script execution time was exceeded for the host"); // NO I18N
        br.close();
        return;
    }
    outputBuff.append(str);
    outputBuff.append("\n"); // NO I18N
}
Meiruko
  • 11
  • 3
  • ProcessBuilder just launches external processes, it shouldn't have a major impact on how those processes execute, since it uses the exact same mechanism that the rest of the OS uses (fork/exec or whatever the Windows equivalent is). One thing that could influence your script is how much memory and/or cpu time is available while your Java application is running. Is your application doing any heavy work *while* waiting for the script to execute? How much memory is available and how much is used by your JVM? – Joachim Sauer Feb 02 '23 at 10:47
  • Yes, the application will be doing heavy work all the time. Maximum heap memory available for JVM is 2093 MB (May increase during usage) but under normal conditions with less work, the JVM will be consuming around 800MB with 350 threads. Will this information suffice? – Meiruko Feb 02 '23 at 12:00
  • Check with Task manager memory. You may have an object that is not being disposed. If memory is constantly increasing that an object is not being disposed or the application is using more memory as time goes on. – jdweng Feb 02 '23 at 12:12
  • The above info is from the task manager only. Memory will increase during usage and will decrease after it. But the thing is PowerShell runs as a separate service when invoked from JVM in task manager. So, why is it running so slow? – Meiruko Feb 02 '23 at 12:17
  • 2
    [This question](https://stackoverflow.com/q/74362226/2711488) seems to have a similar or even the same issue, a program running slower with `ProcessBuilder` than stand-alone. But again we have not enough information, i.e. code to reproduce the issue. At the moment, it’s not clear whether the issue is connected to the way you use the API (e.g. reading the pipe) or whether there’s a deeper problem with the API itself. – Holger Feb 02 '23 at 13:52
  • Please add the basic powershell script too, or a simplified version. If you're just getting mailbox properties, include about how many mailboxes you process too. When running via processBuilder, are you launching multiple instances of powershell? Exchange Online has some tight throttling and concurrency limits per-user and per-tenant. – Cpt.Whale Feb 02 '23 at 17:39
  • Have added code snippets as requested. Please look into it. @Cpt.Whale – Meiruko Feb 03 '23 at 04:46
  • Do you ever have more than one instance of powershell running? You may want to `Disconnect-ExchangeOnline` between. The majority of the performance there is going to be just the response time from EXO which can be pretty inconsistent, and always going to be pretty slow for all mailboxes. Since you're doing all mailboxes anyways, the ms graph reports might be better, `/reports/getMailboxUsageDetail(period='D7')` only takes a second or two for example for a few hundred mailboxes – Cpt.Whale Feb 03 '23 at 15:31
  • We do use the `/reports/getMailboxUsageDetail(period='D7')` API but it is archived data. For live data, we are using PowerShell. As you suggested we are not using `Disconnect-ExchangeOnline` command. However, I think the lag is not due to this as I have tested the same code in a separate JAVA file but the time lag was not there. The delay is only when executing this via our JVM environment with multiple threads even though memory allocation is sufficient and similar to standalone PowerShell console. – Meiruko Feb 06 '23 at 05:25
  • In my own PB-PWSH tests I noticed two ways that speed was improved: Use a more recent version of Powershell than v1.0, and trim excessive stdout. If writing huge reports, perhaps try adding `| Out-File -FilePath reportXYZ.csv` in appropriate places to generate directly to file within Powershell, and scan in Java afterwards. However I didn't see much difference between PB-PWSH vs PWSH. – DuncG Feb 07 '23 at 16:13
  • `v1.0` is the folder name where `PowerShell.exe` is present. The Output will be around 50 lines only. Performance should not differ for 50 lines. Tried the `Out-File` method also, couldn't find any difference. – Meiruko Feb 08 '23 at 05:22
  • @Meiruko Are you running multiple EXO queries -at the same time- in different threads? Exchange online will suspend your connections if you open too many. Are you using a current version of the `ExchangeOnlineManagement` module? – Cpt.Whale Feb 08 '23 at 14:49
  • @Cpt.Whale we are not running multiple queries at the same time. Exchange Online will allow only upto three simultaneous connections. We have suggested the below command for EXO for users. `Install-Module -Name ExchangeOnlineManagement -RequiredVersion 2.0.5` – Meiruko Feb 09 '23 at 04:30

0 Answers0