# Perigee Utility Class

## Block Until

This handy method takes a CRON string and is an easy way of creating an awaitable task. It returns True if the await finished without being cancelled by the optional token.

```csharp
//Async wait until the 0th second of every 5th minute
await PerigeeUtil.BlockUntil("0 */5 * * * *");

//Or for non async methods
PerigeeUtil.BlockUntil("0 */5 * * * *").GetAwaiter().GetResult();

//Block until the next 5 minute mark, or 10 seconds. Whichever comes first
CancellationTokenSource CTSource = new CancellationTokenSource();
CTSource.CancelAfter(TimeSpan.FromSeconds(10));
var done = PerigeeUtil.BlockUntil("*/5 * * * *", null, CTSource.Token).GetAwaiter().GetResult();
```

## Retry and Rety Async

One of the utility class methods is a <mark style="color:blue;">**`Retry`**</mark>and <mark style="color:blue;">**`RetryAsync`**</mark>. They provide an easy ability to retry <mark style="color:red;">**on exception thrown**</mark> with a delay.&#x20;

The response from these classes is a Tuple where&#x20;

* <mark style="color:blue;">**`Item1`**</mark> is a `boolean` indicating if it was a success(true), or failed the maximum number of times (false).
* <mark style="color:blue;">**`Item2`**</mark> is the exception thrown if it was not a succeed

```csharp
PerigeeApplication.ApplicationNoInit("CancelTokens", (c) => {

    c.AddRecurring("Recurring", (ct, l) => {

        //This will be tried 5 times, with 10 seconds between retries. 
        Tuple<bool, Exception> Retry = PerigeeUtil.Retry(5, (i) => {

            bool Success = false;
            if (Success == false)
            {
                throw new Exception("Do or do not, there is a retry");
            }

        }, 10000);

        if (!Retry.Item1)
        {
            l.LogError(Retry.Item2, "Retries exceeded");
        }

    
    });
});
```

## RetryExponentialBackoff

Another retry strategy is the Exponential Backoff method. Unlike a constant delay between retries, this method progressively increases the delay (incorporating jitter) to prevent overwhelming a remote system with requests.&#x20;

You can define both a base delay (in milliseconds) and a maximum delay (in milliseconds) to ensure the delay between requests scales appropriately.

```csharp
PerigeeApplication.ApplicationNoInit("Backoff", (c) => {

    c.AddRecurring("Exponential", (ct, l) => {

        //Log out the backoff delays, logging them is an easy way to test and see what times are going to be used.
        l.LogInformation("Backoff times: {time}", string.Join(", ", PerigeeUtil.GetExponentialBackoffDelays(20, 1000, 1800000)));

        // Use the .RetryExponential to follow the same times as shown above, with a small jitter to each delay
        PerigeeUtil.RetryExponentialBackoff(20, (i) => {

            l.LogInformation("Retry number {i}", i);
            throw new Exception("Throwing to cause another retry");

        }, 1000, 1800000);

    });
    
});

/*
Backoff times: 956, 2102, 4469, 8470, 17127, 31337, 57203, 152968, 234956, 575255, 901233, 1645927, 1800000, 1800000, 1800000, 1800000, 1800000, 1800000, 1800000, 1800000
*/
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.perigee.software/core-modules/utility-classes/perigee-utility-class.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
