Skip to content

Commit a697291

Browse files
committed
Refactor IAsyncReplicator support to not require two constructors
1 parent a3287bf commit a697291

4 files changed

Lines changed: 48 additions & 40 deletions

File tree

src/Lucene.Net.Replicator/Http/HttpReplicator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace Lucene.Net.Replicator.Http
3232
/// <remarks>
3333
/// @lucene.experimental
3434
/// </remarks>
35-
public class HttpReplicator : HttpClientBase, IReplicator, IAsyncReplicator
35+
public class HttpReplicator : HttpClientBase, IAsyncReplicator
3636
{
3737
/// <summary>
3838
/// Creates a new <see cref="HttpReplicator"/> with the given host, port and path.

src/Lucene.Net.Replicator/IAsyncReplicator.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77
namespace Lucene.Net.Replicator
88
{
99
/// <summary>
10-
/// Async version of <see cref="IReplicator"/> for non-blocking replication operations.
10+
/// Async extension of <see cref="IReplicator"/> for non-blocking replication operations.
11+
/// Implementations provide both synchronous (inherited from <see cref="IReplicator"/>)
12+
/// and asynchronous methods.
1113
/// </summary>
12-
public interface IAsyncReplicator
14+
public interface IAsyncReplicator : IReplicator
1315
{
1416
/// <summary>
1517
/// Check whether the given version is up-to-date and returns a

src/Lucene.Net.Replicator/ReplicationClient.cs

Lines changed: 41 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ public override void Run()
129129
/// </summary>
130130
public const string INFO_STREAM_COMPONENT = "ReplicationThread";
131131

132-
private readonly IReplicator? replicator;
132+
private readonly IReplicator replicator;
133133
private readonly IAsyncReplicator? asyncReplicator;
134134
private readonly IReplicationHandler handler;
135135
private readonly ISourceDirectoryFactory factory;
@@ -147,26 +147,16 @@ public override void Run()
147147
/// <summary>
148148
/// Constructor.
149149
/// </summary>
150-
/// <param name="replicator">The <see cref="IReplicator"/> used for checking for updates</param>
150+
/// <param name="replicator">The <see cref="IReplicator"/> used for checking for updates.
151+
/// If the replicator also implements <see cref="IAsyncReplicator"/>, the async
152+
/// methods will use the native async implementation; otherwise, they will fall back
153+
/// to wrapping the synchronous methods.</param>
151154
/// <param name="handler">The <see cref="IReplicationHandler"/> notified when new revisions are ready</param>
152155
/// <param name="factory">The <see cref="ISourceDirectoryFactory"/> for returning a <see cref="Directory"/> for a given source and session</param>
153156
public ReplicationClient(IReplicator replicator, IReplicationHandler handler, ISourceDirectoryFactory factory)
154157
{
155158
this.replicator = replicator ?? throw new ArgumentNullException(nameof(replicator));
156-
this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
157-
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
158-
}
159-
160-
/// <summary>
161-
/// Constructor for async replicators.
162-
/// </summary>
163-
/// <param name="asyncReplicator"></param>
164-
/// <param name="handler"></param>
165-
/// <param name="factory"></param>
166-
/// <exception cref="ArgumentNullException"></exception>
167-
public ReplicationClient(IAsyncReplicator asyncReplicator, IReplicationHandler handler, ISourceDirectoryFactory factory)
168-
{
169-
this.asyncReplicator = asyncReplicator ?? throw new ArgumentNullException(nameof(asyncReplicator));
159+
this.asyncReplicator = replicator as IAsyncReplicator;
170160
this.handler = handler ?? throw new ArgumentNullException(nameof(handler));
171161
this.factory = factory ?? throw new ArgumentNullException(nameof(factory));
172162
}
@@ -184,8 +174,6 @@ private void CopyBytes(IndexOutput output, Stream input)
184174
/// <exception cref="IOException"></exception>
185175
private void DoUpdate()
186176
{
187-
if (replicator is null) throw new InvalidOperationException("Replicator not initialized.");
188-
189177
SessionToken? session = null;
190178
Dictionary<string, Directory> sourceDirectory = new Dictionary<string, Directory>();
191179
Dictionary<string, IList<string>> copiedFiles = new Dictionary<string, IList<string>>();
@@ -285,13 +273,13 @@ private void DoUpdate()
285273
}
286274

287275
/// <summary>
288-
/// Performs the async update logic, mirrors DoUpdate but uses IAsyncReplicator.
276+
/// Performs the async update logic. When the replicator implements
277+
/// <see cref="IAsyncReplicator"/>, native async methods are used;
278+
/// otherwise, the synchronous <see cref="IReplicator"/> methods are
279+
/// called and their results wrapped.
289280
/// </summary>
290281
private async Task DoUpdateAsync(CancellationToken cancellationToken)
291282
{
292-
if (asyncReplicator is null)
293-
throw new InvalidOperationException("AsyncReplicator not initialized.");
294-
295283
SessionToken? session = null;
296284
var sourceDirectory = new Dictionary<string, Directory>();
297285
var copiedFiles = new Dictionary<string, IList<string>>();
@@ -300,15 +288,17 @@ private async Task DoUpdateAsync(CancellationToken cancellationToken)
300288
try
301289
{
302290
string? version = handler.CurrentVersion;
303-
session = await asyncReplicator.CheckForUpdateAsync(version, cancellationToken).ConfigureAwait(false);
291+
292+
session = asyncReplicator is not null
293+
? await asyncReplicator.CheckForUpdateAsync(version, cancellationToken).ConfigureAwait(false)
294+
: replicator.CheckForUpdate(version);
304295

305296
WriteToInfoStream($"DoUpdateAsync(): handlerVersion={version} session={session}");
306297

307298
if (session is null)
308299
return;
309300

310301
IDictionary<string, IList<RevisionFile>> requiredFiles = RequiredFiles(session.SourceFiles);
311-
WriteToInfoStream($"DoUpdateAsync(): handlerVersion={version} session={session}");
312302

313303
foreach (var pair in requiredFiles)
314304
{
@@ -327,7 +317,10 @@ private async Task DoUpdateAsync(CancellationToken cancellationToken)
327317
IndexOutput? output = null;
328318
try
329319
{
330-
input = await asyncReplicator.ObtainFileAsync(session.Id!, source, file.FileName, cancellationToken).ConfigureAwait(false);
320+
input = asyncReplicator is not null
321+
? await asyncReplicator.ObtainFileAsync(session.Id!, source, file.FileName, cancellationToken).ConfigureAwait(false)
322+
: replicator.ObtainFile(session.Id!, source, file.FileName);
323+
331324
output = directory.CreateOutput(file.FileName, IOContext.DEFAULT);
332325

333326
int numBytes;
@@ -354,7 +347,10 @@ private async Task DoUpdateAsync(CancellationToken cancellationToken)
354347
{
355348
try
356349
{
357-
await asyncReplicator.ReleaseAsync(session.Id, cancellationToken).ConfigureAwait(false);
350+
if (asyncReplicator is not null)
351+
await asyncReplicator.ReleaseAsync(session.Id, cancellationToken).ConfigureAwait(false);
352+
else
353+
replicator.Release(session.Id);
358354
}
359355
finally
360356
{
@@ -367,18 +363,28 @@ private async Task DoUpdateAsync(CancellationToken cancellationToken)
367363
}
368364
}
369365

370-
if (notify && !disposed)
366+
// notify outside the try-finally above, so the session is released sooner.
367+
// the handler may take time to finish acting on the copied files, but the
368+
// session itself is no longer needed.
369+
try
371370
{
372-
handler.RevisionReady(session.Version, session.SourceFiles, Collections.AsReadOnly(copiedFiles), sourceDirectory);
371+
if (notify && !disposed)
372+
{
373+
// no use to notify if we are closed already
374+
// LUCENENET specific - pass the copiedFiles as read only
375+
handler.RevisionReady(session.Version, session.SourceFiles, Collections.AsReadOnly(copiedFiles), sourceDirectory);
376+
}
373377
}
378+
finally
379+
{
380+
IOUtils.Dispose(sourceDirectory.Values);
374381

375-
IOUtils.Dispose(sourceDirectory.Values);
376-
377-
// LUCENENET specific: removed redundant check. Code above either ensures it is not null (by returning early) or an exception is thrown.
378-
// if (session != null)
379-
// {
380-
factory.CleanupSession(session.Id);
381-
// }
382+
// LUCENENET specific: removed redundant check. Code above either ensures it is not null (by returning early) or an exception is thrown.
383+
// if (session != null)
384+
// {
385+
factory.CleanupSession(session.Id);
386+
// }
387+
}
382388
}
383389

384390
/// <summary>Throws <see cref="ObjectDisposedException"/> if the client has already been disposed.</summary>

src/Lucene.Net.Tests.Replicator/Http/HttpReplicatorTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public void TestBasic()
152152
[LuceneNetSpecific]
153153
public async Task TestBasicAsync()
154154
{
155-
IAsyncReplicator replicator = new HttpReplicator(host, port, ReplicationService.REPLICATION_CONTEXT + "/s1", server.CreateHandler());
155+
IReplicator replicator = new HttpReplicator(host, port, ReplicationService.REPLICATION_CONTEXT + "/s1", server.CreateHandler());
156156
ReplicationClient client = new ReplicationClient(replicator, new IndexReplicationHandler(handlerIndexDir, null),
157157
new PerSessionDirectoryFactory(clientWorkDir.FullName));
158158

@@ -208,7 +208,7 @@ public void TestServerErrors()
208208
public async Task TestServerErrorsAsync()
209209
{
210210
// tests the behaviour of the client when the server sends an error
211-
IAsyncReplicator replicator = new HttpReplicator(host, port, ReplicationService.REPLICATION_CONTEXT + "/s1", server.CreateHandler());
211+
IReplicator replicator = new HttpReplicator(host, port, ReplicationService.REPLICATION_CONTEXT + "/s1", server.CreateHandler());
212212
using ReplicationClient client = new ReplicationClient(replicator, new IndexReplicationHandler(handlerIndexDir, null),
213213
new PerSessionDirectoryFactory(clientWorkDir.FullName));
214214

0 commit comments

Comments
 (0)