@@ -23,6 +23,12 @@ type PolicyConfig =
2323
2424type GovernorState = { circuitState : string option ; bulkheadAvailable : int option ; queueAvailable : int option }
2525
26+ [<NoComparison; NoEquality>]
27+ [<RequireQualifiedAccess>]
28+ type TaskOrAsync < 'T > =
29+ | Task of ( CancellationToken -> Task < 'T >)
30+ | Async of ( Async < 'T >)
31+
2632/// Translates a PolicyConfig's rules to a Polly IAsyncPolicy instance that gets held in the ActionPolicy
2733type Governor ( log : Serilog.ILogger , buildFailurePolicy : unit -> Polly.PolicyBuilder , serviceName : string , callName : string , policyName : string , config : PolicyConfig ) =
2834 let logBreach sla interval = log |> Events.Log.cutoffSlaBreached ( serviceName, callName, policyName) sla interval
@@ -123,11 +129,15 @@ type Governor(log: Serilog.ILogger, buildFailurePolicy: unit -> Polly.PolicyBuil
123129 Some () // Compiler gets too clever if we never return Some
124130
125131 /// Execute and/or log failures regarding invocation of a function with the relevant policy applied
126- member __.Execute ( inner : Async < 'a >) : Async < 'a > =
132+ member __.Execute ( inner : TaskOrAsync < 'a >) : Async < 'a > =
127133 match asyncPolicy with
128134 | None ->
129135 log.Debug( " Policy Execute Raw {service:l}-{call:l}" , serviceName, callName)
130- inner
136+ match inner with
137+ | TaskOrAsync.Async a -> a
138+ | TaskOrAsync.Task factory -> async {
139+ let! ct = Async.CancellationToken
140+ return ! factory ct |> Async.AwaitTaskCorrect }
131141 | Some polly -> async {
132142 let mutable wasFull = false
133143 // NB This logging is on a best-effort basis - obviously the guard here has an implied race condition
@@ -164,7 +174,10 @@ type Governor(log: Serilog.ILogger, buildFailurePolicy: unit -> Polly.PolicyBuil
164174
165175 // sic - cancellation of the inner computation needs to be controlled by Polly's chain of handlers
166176 // for example, if a cutoff is configured, it's Polly that will be requesting the cancellation
167- Async.StartAsTask( inner, cancellationToken= pollyCt)
177+ match inner with
178+ | TaskOrAsync.Async a -> Async.StartAsTask( a, cancellationToken= pollyCt)
179+ | TaskOrAsync.Task factory -> factory pollyCt
180+
168181 let execute = async {
169182 let! ct = Async.CancellationToken // Grab async cancellation token of this `Execute` call, so cancellation gets propagated into the Polly [wrap]
170183 log.Debug( " Policy Execute Inner {service:l}-{call:l}" , serviceName, callName)
@@ -222,9 +235,13 @@ type CallPolicy<'TConfig when 'TConfig: equality> (makeGoverner : CallConfig<'TC
222235 member __.Policy = cfg.policy
223236 member __.Config = cfg.config
224237
225- /// Execute the call, apply the policy rules
238+ /// Execute an inner Async computation, applying the policy rules
226239 member __.Execute ( inner : Async < 't >) =
227- governor.Execute inner
240+ governor.Execute ( TaskOrAsync.Async inner)
241+
242+ /// Execute an inner Task-Method, applying the policy rules
243+ member __.ExecuteTask < 'T >( inner : CancellationToken -> Task < 'T >) =
244+ governor.Execute ( TaskOrAsync.Task inner)
228245
229246 /// Facilitates dumping for diagnostics
230247 member __.InternalState =
0 commit comments