-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API Proposal]: System.ClientModel long-running operation base type OperationResult
#106197
Comments
Generally looks reasonably, but I'm wondering about the IsCompleted property. What's the use case for that, and does the value change after the result has been constructed? e.g. is that actually going to make networking requests to determine whether the operation has completed? If it doesn't, then I worry it'll be confusing that IsCompleted isn't up-to-date. And if it does, then I worry that there's expensive work happening behind a property, possibly expensive asynchronous work. |
Also, for the usage example, what happens if instead of: VectorStoreClient client = new VectorStoreClient(new OpenAIClientOptions());
CreateVectorStoreOperation operation = await client.CreateVectorStoreAsync(waitUntilCompleted: false);
await operation.WaitForCompletionAsync();
VectorStore vectorStore = operation.Value; I end up writing: VectorStoreClient client = new VectorStoreClient(new OpenAIClientOptions());
CreateVectorStoreOperation operation = await client.CreateVectorStoreAsync(waitUntilCompleted: false);
VectorStore vectorStore = operation.Value; which is what I'd naturally be inclined to do. Does |
How does the operation know that it's completed? Is it push based (i.e. does the server tell the client "I'm done") or is it polling based? |
Yes, the value of
while (!operation.IsCompleted)
{
// delay custom interval
operation.UpdateStatus();
}
It does not.
It actually doesn't. We made the design decision in Azure clients to require a user to pass a value for this parameter because it was hard to pick a default that would work across all LRO cases, and we are planning to follow the same design for SCM-based clients. For example, some LROs can take up to 24 hours to complete, and we didn't want user code to block for that long without explicit opt-in to that. Other LROs can return in a few seconds, so we also didn't want to force callers to write extra lines of code to |
Thanks. IsCompleted is still troublesome to me. I'd expect to be able to poll it in a loop like: while (!IsCompleted)
{
DoOtherThings();
} but it sounds like that'll likely hang because I'm not calling am explicit update that doesn't exist on the base where IsCompleted is. Does IsCompleted need to exist on the base? Or should it be a method that also has an async counterpart that can actually poll the service if not completed? |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
So you're proposing something like this: while (!(await client.IsCompletedAsync())
{
DoOtherThings();
} That feels awkward. I'd probably invert the Boolean to avoid the parentheses: while (await client.IsPendingAync())
{
DoOtherThings();
} But I guess if your primary concern is that it's easy to write the sync loop and dead lock, then maybe we should detect that pattern in an analyzer. |
I agree that we may not want to make @stephentoub, is your concern primarily that the |
That's a key part of it.
Either moving IsCompleted to the derived types or moving UpdateStatus to the base type. If we believe that IsCompleted+UpdateStatus+Wait are sufficient for the 99% use cases, then moving UpdateStatus down seems reasonable, along with thoroughly documenting IsCompleted as being a snapshot answer that's only ever updated by a call to UpdateStatus/Wait. I'd also wonder at that point why IsCompleted needs to be abstract, rather than just having a protected setter; I think that also helps a bit to communicate that there's no meaningful work being done in it, whereas having it be abstract might hint that it's actually being implemented by the derived type to call out to the service. The other approach would be fine, too; if you need the derived type to be successful in performing updates in many cases, then just move IsCompleted to that derived type as well. |
Background and motivation
The System.ClientModel library provides building blocks for .NET clients that call cloud services. This addition supports client APIs for long-running operations, i.e. service operations where the service returns a response before the requested operation has completed. For such operations, SCM-based clients return a subclient derived from SCM's abstract
OperationResult
class, which allows users to monitor the operation's status and obtain any resulting value.The new
OperationResult
type is similar to the Operation and Operation<T> types in Azure.Core, but has fewer public APIs in ordre to support greater variation of implementation patterns in the third-party cloud service space. It serves as a base type for public operation subclients such as theCreateVectorStoreOperation
type in the .NET OpenAI client library. It provides APIs to wait for the operation to complete processing on the service, and a rehydration token that can be to "rehydrate" an operation in progress, e.g. to obtain the value computed by a long-running operation from a different process than the one that started the operation. Client subtypes add public properties such asValue
andStatus
as applicable to the operation implementation.API Proposal
API Usage
Example usage of derived
CreateVectorStoreOperation
type in OpenAI client:Alternative Designs
No response
Risks
No response
The text was updated successfully, but these errors were encountered: