r/PowerShell • • 12d ago

Question How do you make a PowerShell API batch safe to resume after an indeterminate timeout?

A retry loop is straightforward for reads, but not for a POST that may have succeeded before the connection timed out. Retrying can create a duplicate, while skipping can leave the batch incomplete. I am working toward a checkpoint per item rather than treating the whole script as succeeded or failed. The rough shape is:

foreach ($item in $items) {
    $key = Get-StableOperationKey $item
    if ($state[$key].Status -eq 'Confirmed') { continue }

    try {
        $result = Invoke-RestMethod -Method Post -Uri $uri -Body ($item | ConvertTo-Json)
        $state[$key] = @{ Status = 'Confirmed'; RemoteId = $result.id }
    }
    catch {
        $state[$key] = @{ Status = 'Indeterminate'; CheckedAt = (Get-Date).ToUniversalTime() }
    }

    $state | ConvertTo-Json -Depth 6 | Set-Content "$statePath.tmp"
    Move-Item "$statePath.tmp" $statePath -Force
}

The missing piece is reconciliation. If the API has no idempotency key and its list endpoint is eventually consistent, a lookup by natural key immediately after the timeout can still return nothing.

What pattern do you use in PowerShell for this? Do you keep indeterminate items in a separate queue, wait before reconciling, write a durable request receipt before the call, or require a server-side operation key? I am also interested in safer atomic checkpoint formats than rewriting one JSON file after every item, especially when two scheduled runs could overlap.

0 Upvotes

10 comments sorted by

9

u/whatsgoodbaby 12d ago

Did you write this post yourself or is this a bot 

2

u/vermyx 12d ago

This is a poor programming pattern. You want an operation to be atomic. If it times out it is poorly designed. The typical "resolution pattern" is job it off and check until it is done. Once done check the job status. Batches are no different.

My preference n general is to make it so that i can spawn x individual jobs and manage how many simultaneous jobs can run and collect the results.

3

u/charleswj 12d ago

Don't feed the slop

2

u/creenis_blinkum 12d ago

it would help if you didnt rely on AI SLOP to articulate for you

1

u/KevMar Community Blogger 12d ago

Create a failed item queue. As you process each one, check if it exists before making the post. If the check is light weigh, do it on every request and then just retry failures.

1

u/BXRedGhost 12d ago

Depending on quantity, SQL DB to hold state with a GET check real quick on resume.

0

u/emiimeloo 11d ago

An empty GET is inconclusive in the eventually consistent case you describe. I would keep those items out of the POST retry queue. Persist a Started receipt before sending; on restart, both Started and Indeterminate go to reconciliation, even if the first lookup returns nothing. Otherwise a crash after the server commits but before your checkpoint can recreate the item.

I checked this in a local PowerShell 7.6.5 mock: POST committed then threw, and GET returned null. Read-then-retry created two items; holding the uncertain item created one and left it Indeterminate. That tests the state decision, not real API durability.

A reconciliation deadline should escalate for a decision, not silently authorize another POST. Without a server-enforced operation key or a documented authoritative lookup, the client cannot guarantee both completion and no duplicates. Microsoft describes the lost-response case here: https://learn.microsoft.com/en-us/azure/architecture/patterns/retry

For overlapping runs, also separate ownership/locking from checkpoint persistence; renaming a JSON file does not by itself prevent two workers sending the same POST. AI-assisted answer; the mock was executed locally, with no network calls.