Skip to content

Instantly share code, notes, and snippets.

@joheredi
Last active August 9, 2023 19:50
Show Gist options
  • Save joheredi/09b81c192e8c7c4ee3581ac0b9f76c12 to your computer and use it in GitHub Desktop.
Save joheredi/09b81c192e8c7c4ee3581ac0b9f76c12 to your computer and use it in GitHub Desktop.
Lro Redesign

Designing Long Running Operations in Client Libraries

Motivation for Change in LRO

Current Status: At present, our Long Running Operations (LRO) handling is designed such that every LRO invocation invariably returns a poller. The current design flow requires users to await the LRO to obtain this poller.

Identified Challenge: The act of 'awaiting' typically signals to developers an expectation of finality, i.e., the culmination of an asynchronous process and receipt of the anticipated result. In the current LRO design, however, awaiting does not yield the final result. Instead, it returns the poller, which can then be used to track the operation's status. This deviation from the conventional 'await' paradigm has caused confusion among our users. They often mistakenly believe that by awaiting, they're obtaining the operation's final outcome, not an intermediary tool like the poller.

Need for Change: To maintain a consistent and intuitive user experience, it's crucial that our system's behavior aligns with established developer expectations. The 'await' paradigm is well-understood in the developer community as a means to retrieve final results from asynchronous operations. By deviating from this norm, we risk alienating or bewildering our users, leading to potential inefficiencies or mistakes in their development processes.

An ideal solution would seamlessly marry the need for long-running operation tracking (via pollers) with the established conventions of asynchronous programming. The goal is to ensure clarity, reduce the learning curve, and elevate the overall developer experience.

Option 1: Implicit Final Result

Automatically complete the LRO and return the final result.

const result = await client.someLro();
console.log(result.name);

Pros:

  • Simplified API: Removes the need for users to manage LROs.
  • Direct for quick operations.

Cons:

  • Obscures service intention: Users may unintentionally wait for long durations.
  • Inconsistent: Not applicable to all LROs, requiring TypeSpec annotations.
  • Removes the option for users who want to manually handle LROs.

Option 2: Explicit Wait Suffix

Provide two separate functions, one returning a poller, the other waiting for LRO completion.

const poller = client.someLro();
const result = await client.someLroAndWait();
console.log(result.name);

Pros:

  • Versatility: Provides both manual and automatic LRO handling.
  • Familiar pattern for existing Management Plane users.

Cons:

  • Risk of bloated API: Many redundant functions for extended operations.
  • Requires TypeSpec support to apply selectively.
  • Potentially confusing with two methods for similar tasks.

Option 3: Behavior Based on Invocation

A single function behaves differently based on how it's invoked.

const poller = client.someLro();
const result = await client.someLro();
console.log(result.name);

Pros:

  • Streamlined API: Single function caters to both use cases.
  • Clear semantics: Awaiting fetches the result, not awaiting provides a poller.
  • Universal application: No extra annotations or deviations in function behavior.
  • No TypeSpec changes needed.

Cons:

  • Risky: Users might unknowingly await long LROs.

Option 4: Always Return a Poller

Only return a poller. Users must explicitly call a method to await the result.

const poller = client.someLro();
const result = await poller.pollUntilDone();
console.log(result.name);

Pros:

  • Clear service intent: Users are always aware they're working with LROs.
  • Reduces unintended long waits: Explicit call to wait for completion.
  • Clean API: Consistency across implementations.

Cons:

  • Learning curve: May be complex for users unfamiliar with pollers and LROs.

Recommendation for Long Running Operations in Client Libraries: Option 3

Recommended Approach: Adopt a behavior-based invocation method for handling Long Running Operations (LROs). This means that the function's behavior will vary depending on its invocation - directly returning a poller when not awaited, and returning the final result when awaited.

Rationale:

  1. User Experience & Simplicity: This approach provides users with a seamless and intuitive experience. The dual behavior caters to both quick operations (where direct results are desired) and longer tasks where users might want to handle LROs manually, all without changing the method's name or signature.

  2. Clean API Design: Offering this dual functionality within a single method avoids API bloat, leading to a more streamlined and coherent library.

  3. Consistency: With a single method handling both scenarios, users don't have to grapple with deciding between different functions or methods for different tasks.

Acknowledgment of Risks:

We recognize that this approach carries an inherent risk: users might unknowingly invoke a wait on lengthy LROs, leading to unintended delays. This scenario could arise especially if users aren't aware of the method's dual behavior.

Mitigation Strategies:

  1. Comprehensive Documentation: We will ensure that our documentation clearly explains the dual behavior of the method. Users will be presented with easy-to-understand examples that highlight both usages, enabling them to make informed decisions.

  2. IntelliSense Integration: By integrating with IntelliSense, we'll provide real-time guidance to users as they code. This can serve as an immediate reminder of the method's behavior, reducing the chances of misuse.

  3. Logging: We will integrate logging mechanisms to provide feedback during the method's invocation. For instance, when a user awaits an LRO, a log message can indicate the start of the operation and provide periodic updates. This active feedback will not only keep users informed but also give them an opportunity to adjust if they mistakenly awaited a lengthy LRO.

  4. Samples & Tutorials: To further assist our users, we'll create a range of samples and tutorials. These resources will not only showcase the correct usage but also provide context on when to use each behavior, ensuring users are well-equipped to handle LROs effectively.

Conclusion:

While every approach has its pros and cons, Option 3 offers a balanced blend of simplicity, flexibility, and intuitiveness. By acknowledging and actively mitigating the associated risks, we believe this option will offer our users the best experience while maintaining the integrity and efficiency of our client libraries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment