@@ -19,6 +19,12 @@ type PolicyConfig = { isolate: bool; cutoff: CutoffConfig option; limit: Bulkhea
1919
2020type GovernorState = { circuitState : string option ; bulkheadAvailable : int option ; queueAvailable : int option }
2121
22+ [<NoComparison; NoEquality>]
23+ [<RequireQualifiedAccess>]
24+ type TaskOrAsync < 'T > =
25+ | Task of ( CancellationToken -> Task < 'T >)
26+ | Async of ( Async < 'T >)
27+
2228/// Translates a PolicyConfig's rules to a Polly IAsyncPolicy instance that gets held in the ActionPolicy
2329type Governor ( log : Serilog.ILogger , buildFailurePolicy : unit -> Polly.PolicyBuilder , serviceName : string , callName : string , policyName : string , config : PolicyConfig ) =
2430 let logBreach sla interval = log |> Events.Log.cutoffSlaBreached ( serviceName, callName, policyName) sla interval
@@ -115,9 +121,14 @@ type Governor(log: Serilog.ILogger, buildFailurePolicy: unit -> Polly.PolicyBuil
115121 Some () // Compiler gets too clever if we never return Some
116122
117123 /// Execute and/or log failures regarding invocation of a function with the relevant policy applied
118- member __.Execute ( inner : Async < 'a >) : Async < 'a > =
124+ member __.Execute ( inner : TaskOrAsync < 'a >) : Async < 'a > =
119125 match asyncPolicy with
120- | None -> inner
126+ | None ->
127+ match inner with
128+ | TaskOrAsync.Async a -> a
129+ | TaskOrAsync.Task factory -> async {
130+ let! ct = Async.CancellationToken
131+ return ! factory ct |> Async.AwaitTaskCorrect }
121132 | Some polly -> async {
122133 let mutable wasFull = false
123134 // NB This logging is on a best-effort basis - obviously the guard here has an implied race condition
@@ -154,7 +165,10 @@ type Governor(log: Serilog.ILogger, buildFailurePolicy: unit -> Polly.PolicyBuil
154165
155166 // sic - cancellation of the inner computation needs to be controlled by Polly's chain of handlers
156167 // for example, if a cutoff is configured, it's Polly that will be requesting the cancellation
157- Async.StartAsTask( inner, cancellationToken= pollyCt)
168+ match inner with
169+ | TaskOrAsync.Async a -> Async.StartAsTask( a, cancellationToken= pollyCt)
170+ | TaskOrAsync.Task factory -> factory pollyCt
171+
158172 let execute = async {
159173 let! ct = Async.CancellationToken // Grab async cancellation token of this `Execute` call, so cancellation gets propagated into the Polly [wrap]
160174 try return ! polly.ExecuteAsync( startInnerTask, ct) |> Async.AwaitTaskCorrect
@@ -209,9 +223,13 @@ type CallPolicy<'TConfig when 'TConfig: equality> (makeGoverner : CallConfig<'TC
209223 member __.Policy = cfg.policy
210224 member __.Config = cfg.config
211225
212- /// Execute the call, apply the policy rules
226+ /// Execute an inner Async computation, applying the policy rules
213227 member __.Execute ( inner : Async < 't >) =
214- governor.Execute inner
228+ governor.Execute ( TaskOrAsync.Async inner)
229+
230+ /// Execute an inner Task-Method, applying the policy rules
231+ member __.ExecuteTask < 'T >( inner : CancellationToken -> Task < 'T >) =
232+ governor.Execute ( TaskOrAsync.Task inner)
215233
216234 /// Facilitates dumping for diagnostics
217235 member __.InternalState =
0 commit comments