-
Notifications
You must be signed in to change notification settings - Fork 652
Add client-side async API for replication (IAsyncReplicator, ReplicationClient) #1182
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
base: master
Are you sure you want to change the base?
Add client-side async API for replication (IAsyncReplicator, ReplicationClient) #1182
Conversation
paulirwin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! I have some changes that need to be made, and a larger one about a refactoring that is up for discussion. Please share your thoughts on the refactoring before embarking on it!
| /// <param name="handler"></param> | ||
| /// <param name="factory"></param> | ||
| /// <exception cref="ArgumentNullException"></exception> | ||
| public ReplicationClient(IAsyncReplicator asyncReplicator, IReplicationHandler handler, ISourceDirectoryFactory factory) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, this is a brittle design. It means that you have to know which constructor was used in order to know which methods to call and not get an exception at runtime. I think we should break this type apart.
Let me know your thoughts on this:
- Refactor out a common ReplicationClientBase abstract class with any common methods/fields
- Make this type inherit from the base class. Leave
// LUCENENET:comments where methods were refactored into the base class, to aid future porting efforts. Move the async methods into item 3 below. - Introduce a new AsyncReplicationClient that inherits from ReplicationClientBase, and only has the async methods. This way you can know that it will have an async replicator field.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it is unlikely a user will require both async and synchronous methods at the same time. And it would clarify the StartAsyncUpdateLoop vs StartUpdateThread() logic. Although, I think both of those could be potentially combined.
I am hesitant to get on board with having completely separate implementations, though. That is not typically how concrete components in the BCL evolve. Usually, the synchronous and asynchronous methods exist side by side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree with @NightOwl888 suggestion
I guess we should stick with one ReplicationClient that implements both IReplicator and IAsyncReplicator
initially maintaining or testing it quite seems tough
but i guess our future target is asyn only
So may be
what we can do rightnow
Mark sync APIs with [Obsolete("Prefer async methods to avoid deadlocks.")] to gently guide users toward async.
Long term: async becomes the default; sync kept only for backward compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, I’m also flexible — if you both feel strongly about splitting into separate sync/async clients right now to minimize maintenance and testing overhead, I can adapt to that approach too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mark sync APIs with [Obsolete("Prefer async methods to avoid deadlocks.")] to gently guide users toward async.
Long term: async becomes the default; sync kept only for backward compatibility.
No, synchronous programming isn't going away any time soon. It is just in the space of HTTP servers, that it is no longer considered a good practice. Also, keep in mind that when debugging we will be comparing our implementation against the synchronous Java code. So, we should try to minimize changes to these methods.
ReplicationClient is not sealed. It is designed to be inherited to provide additional functionality. So, in that regard, it is the abstraction and can provide the base implementation of both the synchronous and asynchronous sides. The question is since ReplicationClient can already be mocked by inheritance, do we need to add interfaces?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for clarifying! You’re right — marking sync methods [Obsolete] was premature. Sync APIs are still important for backward compatibility and debugging against Java. Since ReplicationClient can already be extended and mocked via inheritance, we don’t need separate interfaces for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is another problem with the current approach of having two constructors: the constructors are ambiguous if you try to pass in a HttpReplicator without casting it to either interface first:
Our options include:
- Accept that you have to cast this before passing it in. I think this is a poor design choice for other reasons mentioned above, but also having to impose this on users is not ideal. Note that it's also technically a breaking change as-is, since we'd require people to cast HttpReplicator.
- Remove the IAsyncReplicator interface and move its async methods into IReplicator (so that it would have both async and sync methods), making any custom implementations either implement the async methods or throw NotImplementedException. This would be a breaking change because you'd make custom implementations add these async methods.
- A slight alternative to option 2 but with the same effect: make IAsyncReplicator inherit from IReplicator, and have ReplicationClient take only an IAsyncReplicator (which would now provide both methods). This is a breaking change since users of ReplicationClient with custom IReplicators would have to make their custom replicator implement IAsyncReplicator as well.
- Remove synchronous replication support and lean in to async only. This would of course be a breaking change, but it would simplify the API.
- Split out an AsyncReplicationClient as discussed above. AsyncReplicationClient would take an IAsyncReplicator; ReplicationClient would take a Replicator. This is the only non-breaking way to do this.
@NightOwl888 thoughts? I could go for any of these except for option 1. I think if we're going to have any breaking changes we might as well do option 4 and just remove synchronous replication support to keep it simple, but I'm inclined to go for option 5 and just not have the breaking change.
| /// <param name="handler"></param> | ||
| /// <param name="factory"></param> | ||
| /// <exception cref="ArgumentNullException"></exception> | ||
| public ReplicationClient(IAsyncReplicator asyncReplicator, IReplicationHandler handler, ISourceDirectoryFactory factory) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose it is unlikely a user will require both async and synchronous methods at the same time. And it would clarify the StartAsyncUpdateLoop vs StartUpdateThread() logic. Although, I think both of those could be potentially combined.
I am hesitant to get on board with having completely separate implementations, though. That is not typically how concrete components in the BCL evolve. Usually, the synchronous and asynchronous methods exist side by side.
|
@paulirwin @NightOwl888 |
25f65ff to
8135524
Compare
|
@NightOwl888 Reason: The merge brought in changes from master that we do not want in this feature branch. All intended feature commits are still intact. This keeps the branch history clean and focused on the feature work. If any changes from master are important to include, please let me know, and we can merge them properly. |
|
I’m seeing the check-editorconfig CI fail due to a “final newline expected” error. Locally, git diff --check doesn’t show any issues, so I suspect it might be related to line endings (CRLF vs LF) or some subtle trailing whitespace. Could you please guide me on the safest way to fix this, such as how to identify files with extra trailing whitespace or missing newlines, so the CI passes without affecting other files? |
|
That error means that after all of the content in the file, there is no newline character. So, it just needs to be added. I know it is a bit confusing - remove all trailing whitespace except at the end of the file, add a line break. |
f80c97b to
2773b42
Compare
…lient-side async API, related to #XXXX)
…safe); fix all warnings and nullable issues
…REAM_CANCELLATIONTOKEN defines for .NET 8+ and fix all warnings
2773b42 to
c660b52
Compare
|
I have rebased this PR on latest master. Had to do #1221 to fix the build error due to an editorconfig-checker update. I'll look at resolving the remaining comments. |
…ad of OperationCanceledException
@paulirwin @NightOwl888
Fixes:
Fixes #1181
Summary of the changes:
Implemented
IAsyncReplicatorand async methods inReplicationClientfor non-blocking replication operations.Description
This PR introduces an async Task-based API for the replication client, allowing operations such as checking for updates, obtaining files, and releasing sessions to be executed asynchronously.
Key changes:
IAsyncReplicatorinterface.HttpReplicatorto implement async versions of the operations (CheckForUpdateAsync,ObtainFileAsync,ReleaseAsync,PublishAsync).HttpReplicatorto wrap HTTP requests usingHttpClient.ReplicationClientnow exposes async methods that call intoIAsyncReplicator.This avoids synchronous HTTP calls that could deadlock or cause performance issues, while keeping
IReplicationHandlersynchronous, as Lucene.NET APIs currently do not have async equivalents.Additional context:
This implementation has been tested and works successfully when using
UpdateNowAsyncin the GSoC project by referencing the Lucene.NET repository in the GSoC extensions project.