question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

FEATURE: Policy Wrap ( / pipeline)

See original GitHub issue

Proposal: Policy Wrap (was: Pipeline)

Purpose

To provide the ability to chain a series of policies together.

Scope

  • A meta-policy.
  • All sync and async variants
  • When result-returning policies placed in a wrap, all policies must return the same TResult
  • Or void-returning (Action-executing) policies may be wrapped; all polices in the wrap must return void.
  • Individual policies should be able to be used in more than one wrap.

Original proposal: Policy Pipeline

The roadmap originally proposed the following syntax:

// Possible syntax
Policy
  .Pipeline(params IEnumerable[] policies)
  .Execute(...) // Executes the action through the pipeline of policies, each policy wrapping the next.  

Some issues were:

  • The term pipeline very much connotes a one-way flow. In exception-handling this precisely isn’t the case: exceptions thrown would then be passed ‘backwards back through the pipeline’ (? - ugly broken metaphor).
  • The syntax isn’t conducive to flexibly composing pipelines by functional composition.

Revised proposal: Policy Wrap

The revision proposes the name Policy Wrap:

  • The term wrap more clearly connotes outer and inner layers, and the idea of wrapping a function with another (what is actually happenning).
  • It’s the classic ‘onion-layers’ metaphor, with outer policies executing next-inner policies, executing next-inner, until getting to execute the user delegate (at the ‘core’ of the onion).
  • Easier to grok what happens with exceptions: they get thrown back outwards through the layers.
  • The term wrap plays better with a flexibly-composed, functionally-composed policy - see below.

Syntax proposal

Both static and instance configuration methods are now proposed:

// Static configuration syntax as before:
PolicyWrap policyWrap = Policy.Wrap(fallback, cache, retry, circuitBreaker, timeout, throttle); 
policyWrap.Execute(myAction);

// Instance syntax
Policy retry = ...       // some retry policy
Policy breaker = ...     // some breaker
Policy timeout = ...     // some timeout

retry.Wrap(breaker).Execute(myAction); // Inline wrap usage showing simple functional composition

retry.Wrap(breaker).Wrap(throttle).Execute(myAction); // Inline wrap usage chaining three policies
retry.Wrap(breaker.Wrap(throttle)).Execute(myAction); // (functionally equivalent to preceding line)

// Possible use of instance syntax configuring a policy for later use
PolicyWrap myWrap = fallback.Wrap(cache).Wrap(retry).Wrap(breaker).Wrap(timeout).Wrap(throttle);
PolicyWrap myWrap = fallback.Wrap(cache.Wrap(retry.Wrap(breaker.Wrap(timeout.Wrap(throttle))))); // (functionally equivalent to preceding)


Value of functional composition

With the original ‘one-shot’ static syntax, it was not intuitive how to create a pipeline that was a slight variation on another. It could lead to questions about ‘how to [consistently] insert something into the middle of an IEnumerable/ICollection’. Mutable pipelines also potentially have issues in multi-threaded, concurrent scenarios.

The instance syntax of .Wrap(...) allows more flexible functional-composition of a PolicyWrap:

Policy myWrap = ...            // some existing wrap or policy - maybe even passed in to this code from elsewhere
bool useCache = ...            // some value, maybe passed in
CachePolicy cachePolicy = ...  // from somewhere
myWrap = useCache ? cachePolicy.Wrap(myWrap) : myWrap;
// etc

The functional-composition paradigm matches LINQ and Rx: successively composing functional transformations on the function-so-far, before actual execution.

Operation

  • Executes the supplied user delegate through the layers or wrap: the outermost policy executes the next inner, which executes the next inner, etc, until the innermost policy executes the user delegate.
  • Exceptions bubble back outwards (until handled) through the layers.
  • The Context instance carries Key information unifying the call: see Keys proposal #139 for details.
  • Care needed around implementations for ExecuteAndCapture(). Probably only the outermost policy in the wrap should ExecuteAndCapture(); others should Execute() -and-throw.

Naming

Any alternative naming suggestions?

Comments?

Comments? Alternative suggestions? Extra considerations to bear in mind?

Have scenarios to share where you’d use this? (could inform development)

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

2reactions
reisenbergercommented, Jul 12, 2016

@juancarrey , re:

ExecuteAndCapture actually was done by the innermost policy, and returned backwards. [should it be] option at builder ?

My thought so far was that the capture of ExecuteAndCapture() doesn’t want to be done by any inner policy: if an inner execution faults, you want that fault thrown back on to the next-outer policy (not neutralised-by-Capture), as the next outer policy might deal with it. For example, in the sample wrap in the proposal (fallback, cache, retry, circuitBreaker, timeout, throttle), if the throttle or timeout rejects execution, you may well want the circuitbreaker to register that (configurable by which exceptions the breaker handles), you may want to retry after a delay (ditto), and you very likely want the fallback to detect that failure and provide the fallback substitute. That was the (not accidental) logic in supplying fallback, cache, retry, circuitBreaker, timeout, throttle as the example.

My assumption so far about the ExecuteAndCapture() feature has been that its purpose is (after the policy or wrap exhausts all other options…) to capture any final unhandled fault into an ‘outcome’ class, rather than have the caller have to place an extra try / catch round the execution (hence, again, only applying to the outermost call). Is this sounding sensible to other people?? (Keen to hear if I missing something about how ExecuteAndCapture() might operate at inner levels!..)


If the question is about how to capture ‘what went on [what exception was maybe thrown] at the inner levels’, then my thinking so far is that the complexity dictates we need to look to another mechanism: either onSomething delegates like onBreak and onRetry in the current policies (great for logging); and/or capturing through observing events emitted from all policies in the wrap, as first mooted here. Something like an IEnumerable<Exception> or IEnumerable<DelegateResult<TResult>> on PolicyResult wouldn’t (could it?) capture enough detail about on which inner policy the exception occurred on (and might contain lots of repeats if the same exception was thrown outwards through several levels?).

Thoughts, anyone?

0reactions
reisenbergercommented, Sep 4, 2016

An initial implementation of PolicyWrap can now be seen in the v5.0-alpha branch.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Using Split feature flags with Bitbucket pipelines
Feature flags enable developers to control the execution of deployed code. This can make responding to buggy deployments faster, reducing impacts on users....
Read more >
Scripted pipeline: wrap stage - jenkins
I am not familiar with the metaclass concept but I think that a simple solution to your problem is to define a wrapped...
Read more >
Frequently asked questions for wrap - Power Apps
Wrap for Power Apps uses Azure DevOps build pipelines to build mobile apps. Customer assets may be exposed in the build pipeline to...
Read more >
jenkins - Can I wrap a whole stage of a declarative pipeline ...
to a Jenkinsfile. myStage() is defined in a shared library and contains a whole stage. Is this possible? I read that I can...
Read more >
Composite Wrap for Non-Leaking Pipeline Defects
Composite wrap is a permanent, cost-effective pipeline repair technology, suitable for non-leaking defects such as pits, dents, gouges, and external ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found